diff -Nru l-smash-1.9.1/a52.c l-smash-2.3.0/a52.c --- l-smash-1.9.1/a52.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/a52.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,629 +0,0 @@ -/***************************************************************************** - * a52.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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 - -#include "box.h" - -static const char *bit_stream_mode[] = - { - "Main audio service: complete main (CM)", - "Main audio service: music and effects (ME)", - "Associated service: visually impaired (VI)", - "Associated service: hearing impaired (HI)", - "Associated service: dialogue (D)", - "Associated service: commentary (C)", - "Associated service: emergency (E)", - "Undefined service", - "Associated service: voice over (VO)", /* only if acmod == 0b001 */ - "Main audio service: karaoke" - }; - -/* For karaoke mode, C->M, S->V1, SL->V1 and SR->V2. */ -static const char *audio_coding_mode[] = - { - "1 + 1: Dual mono", - "1/0: C", - "2/0: L, R", - "3/0: L, C, R", - "2/1: L, R, S", - "3/1: L, C, R, S", - "2/2: L, R, SL, SR", - "3/2: L, C, R, SL, SR", - "Undefined audio coding mode", - "Undefined audio coding mode", - "2/0: L, R", - "3/0: L, M, R", - "2/1: L, R, V1", - "3/1: L, M, R, V1", - "2/2: L, R, V1, V2", - "3/2: L, M, R, V1, V2" - }; - -/*************************************************************************** - AC-3 tools - ETSI TS 102 366 V1.2.1 (2008-08) -***************************************************************************/ -#include "a52.h" - -#define AC3_SPECIFIC_BOX_LENGTH 11 - -uint8_t *lsmash_create_ac3_specific_info( lsmash_ac3_specific_parameters_t *param, uint32_t *data_length ) -{ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - lsmash_bits_init( &bits, &bs ); - uint8_t buffer[AC3_SPECIFIC_BOX_LENGTH] = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = AC3_SPECIFIC_BOX_LENGTH; - lsmash_bits_put( &bits, 32, AC3_SPECIFIC_BOX_LENGTH ); /* box size */ - lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DAC3.fourcc ); /* box type: 'dac3' */ - lsmash_bits_put( &bits, 2, param->fscod ); - lsmash_bits_put( &bits, 5, param->bsid ); - lsmash_bits_put( &bits, 3, param->bsmod ); - lsmash_bits_put( &bits, 3, param->acmod ); - lsmash_bits_put( &bits, 1, param->lfeon ); - lsmash_bits_put( &bits, 5, param->frmsizecod >> 1 ); - lsmash_bits_put( &bits, 5, 0 ); - uint8_t *data = lsmash_bits_export_data( &bits, data_length ); - lsmash_bits_empty( &bits ); - return data; -} - -int lsmash_setup_ac3_specific_parameters_from_syncframe( lsmash_ac3_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) -{ - if( !data || data_length < AC3_MIN_SYNCFRAME_LENGTH ) - return -1; - IF_A52_SYNCWORD( data ) - return -1; - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t buffer[AC3_MAX_SYNCFRAME_LENGTH] = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = AC3_MAX_SYNCFRAME_LENGTH; - ac3_info_t handler = { { 0 } }; - ac3_info_t *info = &handler; - memcpy( info->buffer, data, LSMASH_MIN( data_length, AC3_MAX_SYNCFRAME_LENGTH ) ); - info->bits = &bits; - lsmash_bits_init( &bits, &bs ); - if( ac3_parse_syncframe_header( info, info->buffer ) ) - return -1; - *param = info->dac3_param; - return 0; -} - -static int ac3_check_syncframe_header( lsmash_ac3_specific_parameters_t *param ) -{ - if( param->fscod == 0x3 ) - return -1; /* unknown Sample Rate Code */ - if( param->frmsizecod > 0x25 ) - return -1; /* unknown Frame Size Code */ - if( param->bsid >= 10 ) - return -1; /* might be EAC-3 */ - return 0; -} - -int ac3_parse_syncframe_header( ac3_info_t *info, uint8_t *data ) -{ - lsmash_bits_t *bits = info->bits; - if( lsmash_bits_import_data( bits, data, AC3_MIN_SYNCFRAME_LENGTH ) ) - return -1; - lsmash_ac3_specific_parameters_t *param = &info->dac3_param; - lsmash_bits_get( bits, 32 ); /* syncword + crc1 */ - param->fscod = lsmash_bits_get( bits, 2 ); - param->frmsizecod = lsmash_bits_get( bits, 6 ); - param->bsid = lsmash_bits_get( bits, 5 ); - param->bsmod = lsmash_bits_get( bits, 3 ); - param->acmod = lsmash_bits_get( bits, 3 ); - if( (param->acmod & 0x01) && (param->acmod != 0x01) ) - lsmash_bits_get( bits, 2 ); /* cmixlev */ - if( param->acmod & 0x04 ) - lsmash_bits_get( bits, 2 ); /* surmixlev */ - if( param->acmod == 0x02 ) - lsmash_bits_get( bits, 2 ); /* dsurmod */ - param->lfeon = lsmash_bits_get( bits, 1 ); - lsmash_bits_empty( bits ); - return ac3_check_syncframe_header( param ); -} - -int ac3_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < AC3_SPECIFIC_BOX_LENGTH ) - return -1; - lsmash_ac3_specific_parameters_t *param = (lsmash_ac3_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - param->fscod = (data[0] >> 6) & 0x03; /* XXxx xxxx xxxx xxxx xxxx xxxx */ - param->bsid = (data[0] >> 1) & 0x1F; /* xxXX XXXx xxxx xxxx xxxx xxxx */ - param->bsmod = ((data[0] & 0x01) << 2) | ((data[2] >> 6) & 0x03); /* xxxx xxxX XXxx xxxx xxxx xxxx */ - param->acmod = (data[1] >> 3) & 0x07; /* xxxx xxxx xxXX Xxxx xxxx xxxx */ - param->lfeon = (data[1] >> 2) & 0x01; /* xxxx xxxx xxxx xXxx xxxx xxxx */ - param->frmsizecod = ((data[1] & 0x03) << 3) | ((data[3] >> 5) & 0x07); /* xxxx xxxx xxxx xxXX XXXx xxxx */ - param->frmsizecod <<= 1; - return 0; -} - -int ac3_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: AC3 Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - if( box->size < AC3_SPECIFIC_BOX_LENGTH ) - return -1; - uint8_t *data = box->binary; - isom_skip_box_common( &data ); - uint8_t fscod = (data[0] >> 6) & 0x03; - uint8_t bsid = (data[0] >> 1) & 0x1F; - uint8_t bsmod = ((data[0] & 0x01) << 2) | ((data[1] >> 6) & 0x03); - uint8_t acmod = (data[1] >> 3) & 0x07; - uint8_t lfeon = (data[1] >> 2) & 0x01; - uint8_t bit_rate_code = ((data[1] & 0x03) << 3) | ((data[2] >> 5) & 0x07); - if( fscod != 0x03 ) - lsmash_ifprintf( fp, indent, "fscod = %"PRIu8" (%"PRIu32" Hz)\n", fscod, ac3_sample_rate_table[fscod] ); - else - lsmash_ifprintf( fp, indent, "fscod = 0x03 (reserved)\n" ); - lsmash_ifprintf( fp, indent, "bsid = %"PRIu8"\n", bsid ); - lsmash_ifprintf( fp, indent, "bsmod = %"PRIu8" (%s)\n", bsmod, bit_stream_mode[bsmod + (acmod == 0x01 ? 1 : acmod > 0x01 ? 2 : 0)] ); - lsmash_ifprintf( fp, indent, "acmod = %"PRIu8" (%s)\n", acmod, audio_coding_mode[acmod + (bsmod == 0x07 ? 8 : 0)] ); - lsmash_ifprintf( fp, indent, "lfeon = %s\n", lfeon ? "1 (LFE)" : "0" ); - static const uint32_t bit_rate[] = - { - 32, 40, 48, 56, 64, 80, 96, 112, 128, - 160, 192, 224, 256, 320, 384, 448, 512, 576, 640, - 0 /* undefined */ - }; - lsmash_ifprintf( fp, indent, "bit_rate_code = 0x%02"PRIx8" (%"PRIu32" kbit/s)\n", bit_rate_code, bit_rate[bit_rate_code] ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", data[2] & 0x1F ); - return 0; -} - -#undef AC3_SPECIFIC_BOX_LENGTH - -/*************************************************************************** - Enhanced AC-3 tools - ETSI TS 102 366 V1.2.1 (2008-08) -***************************************************************************/ - -uint8_t *lsmash_create_eac3_specific_info( lsmash_eac3_specific_parameters_t *param, uint32_t *data_length ) -{ -#define EAC3_SPECIFIC_BOX_MAX_LENGTH 42 - if( param->num_ind_sub > 7 ) - return NULL; - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - lsmash_bits_init( &bits, &bs ); - uint8_t buffer[EAC3_SPECIFIC_BOX_MAX_LENGTH] = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = EAC3_SPECIFIC_BOX_MAX_LENGTH; - lsmash_bits_put( &bits, 32, 0 ); /* box size */ - lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DEC3.fourcc ); /* box type: 'dec3' */ - lsmash_bits_put( &bits, 13, param->data_rate ); /* data_rate; setup by isom_update_bitrate_description */ - lsmash_bits_put( &bits, 3, param->num_ind_sub ); - /* Apparently, the condition of this loop defined in ETSI TS 102 366 V1.2.1 (2008-08) is wrong. */ - for( int i = 0; i <= param->num_ind_sub; i++ ) - { - lsmash_eac3_substream_info_t *independent_info = ¶m->independent_info[i]; - lsmash_bits_put( &bits, 2, independent_info->fscod ); - lsmash_bits_put( &bits, 5, independent_info->bsid ); - lsmash_bits_put( &bits, 5, independent_info->bsmod ); - lsmash_bits_put( &bits, 3, independent_info->acmod ); - lsmash_bits_put( &bits, 1, independent_info->lfeon ); - lsmash_bits_put( &bits, 3, 0 ); /* reserved */ - lsmash_bits_put( &bits, 4, independent_info->num_dep_sub ); - if( independent_info->num_dep_sub > 0 ) - lsmash_bits_put( &bits, 9, independent_info->chan_loc ); - else - lsmash_bits_put( &bits, 1, 0 ); /* reserved */ - } - uint8_t *data = lsmash_bits_export_data( &bits, data_length ); - lsmash_bits_empty( &bits ); - /* Update box size. */ - data[0] = ((*data_length) >> 24) & 0xff; - data[1] = ((*data_length) >> 16) & 0xff; - data[2] = ((*data_length) >> 8) & 0xff; - data[3] = (*data_length) & 0xff; - return data; -#undef EAC3_SPECIFIC_BOX_MAX_LENGTH -} - -/* Return -1 if incomplete Enhanced AC-3 sample is given. */ -int lsmash_setup_eac3_specific_parameters_from_frame( lsmash_eac3_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) -{ - if( !data || data_length < 5 ) - return -1; - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t buffer[EAC3_MAX_SYNCFRAME_LENGTH] = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = EAC3_MAX_SYNCFRAME_LENGTH; - eac3_info_t handler = { { 0 } }; - eac3_info_t *info = &handler; - uint32_t overall_wasted_data_length = 0; - info->buffer_pos = info->buffer; - info->buffer_end = info->buffer; - info->bits = &bits; - lsmash_bits_init( &bits, &bs ); - while( 1 ) - { - /* Check the remainder length of the input data. - * If there is enough length, then parse the syncframe in it. - * The length 5 is the required byte length to get frame size. */ - uint32_t remainder_length = info->buffer_end - info->buffer_pos; - if( !info->no_more_read && remainder_length < EAC3_MAX_SYNCFRAME_LENGTH ) - { - if( remainder_length ) - memmove( info->buffer, info->buffer_pos, remainder_length ); - uint32_t wasted_data_length = LSMASH_MIN( data_length, EAC3_MAX_SYNCFRAME_LENGTH ); - data_length -= wasted_data_length; - memcpy( info->buffer + remainder_length, data + overall_wasted_data_length, wasted_data_length ); - overall_wasted_data_length += wasted_data_length; - remainder_length += wasted_data_length; - info->buffer_pos = info->buffer; - info->buffer_end = info->buffer + remainder_length; - info->no_more_read = (data_length < 5); - } - if( remainder_length < 5 && info->no_more_read ) - goto setup_param; /* No more valid data. */ - /* Parse syncframe. */ - IF_A52_SYNCWORD( info->buffer_pos ) - goto setup_param; - info->frame_size = 0; - if( eac3_parse_syncframe( info, info->buffer_pos, LSMASH_MIN( remainder_length, EAC3_MAX_SYNCFRAME_LENGTH ) ) ) - goto setup_param; - if( remainder_length < info->frame_size ) - goto setup_param; - int independent = info->strmtyp != 0x1; - if( independent && info->substreamid == 0x0 ) - { - if( info->number_of_audio_blocks == 6 ) - { - /* Encountered the first syncframe of the next access unit. */ - info->number_of_audio_blocks = 0; - goto setup_param; - } - else if( info->number_of_audio_blocks > 6 ) - goto setup_param; - info->number_of_audio_blocks += eac3_audio_block_table[ info->numblkscod ]; - info->number_of_independent_substreams = 0; - } - else if( info->syncframe_count == 0 ) - /* The first syncframe in an AU must be independent and assigned substream ID 0. */ - return -2; - if( independent ) - info->independent_info[info->number_of_independent_substreams ++].num_dep_sub = 0; - else - ++ info->independent_info[info->number_of_independent_substreams - 1].num_dep_sub; - info->buffer_pos += info->frame_size; - ++ info->syncframe_count; - } -setup_param: - if( info->number_of_independent_substreams == 0 || info->number_of_independent_substreams > 8 ) - return -1; - if( !info->dec3_param_initialized ) - eac3_update_specific_param( info ); - *param = info->dec3_param; - return info->number_of_audio_blocks == 6 ? 0 : -1; -} - -uint16_t lsmash_eac3_get_chan_loc_from_chanmap( uint16_t chanmap ) -{ - return ((chanmap & 0x7f8) >> 2) | ((chanmap & 0x2) >> 1); -} - -static int eac3_check_syncframe_header( eac3_info_t *info ) -{ - if( info->strmtyp == 0x3 ) - return -1; /* unknown Stream type */ - lsmash_eac3_substream_info_t *substream_info; - if( info->strmtyp != 0x1 ) - substream_info = &info->independent_info[ info->current_independent_substream_id ]; - else - substream_info = &info->dependent_info; - if( substream_info->fscod == 0x3 && substream_info->fscod2 == 0x3 ) - return -1; /* unknown Sample Rate Code */ - if( substream_info->bsid < 10 || substream_info->bsid > 16 ) - return -1; /* not EAC-3 */ - return 0; -} - -int eac3_parse_syncframe( eac3_info_t *info, uint8_t *data, uint32_t data_length ) -{ - lsmash_bits_t *bits = info->bits; - if( lsmash_bits_import_data( bits, data, data_length ) ) - return -1; - lsmash_bits_get( bits, 16 ); /* syncword (16) */ - info->strmtyp = lsmash_bits_get( bits, 2 ); /* strmtyp (2) */ - info->substreamid = lsmash_bits_get( bits, 3 ); /* substreamid (3) */ - lsmash_eac3_substream_info_t *substream_info; - if( info->strmtyp != 0x1 ) - { - if( info->substreamid == 0x0 && info->number_of_independent_substreams ) - eac3_update_specific_param( info ); - info->current_independent_substream_id = info->substreamid; - substream_info = &info->independent_info[ info->current_independent_substream_id ]; - substream_info->chan_loc = 0; - } - else - substream_info = &info->dependent_info; - info->frame_size = 2 * (lsmash_bits_get( bits, 11 ) + 1); /* frmsiz (11) */ - substream_info->fscod = lsmash_bits_get( bits, 2 ); /* fscod (2) */ - if( substream_info->fscod == 0x3 ) - { - substream_info->fscod2 = lsmash_bits_get( bits, 2 ); /* fscod2 (2) */ - info->numblkscod = 0x3; - } - else - info->numblkscod = lsmash_bits_get( bits, 2 ); /* numblkscod (2) */ - substream_info->acmod = lsmash_bits_get( bits, 3 ); /* acmod (3) */ - substream_info->lfeon = lsmash_bits_get( bits, 1 ); /* lfeon (1) */ - substream_info->bsid = lsmash_bits_get( bits, 5 ); /* bsid (5) */ - lsmash_bits_get( bits, 5 ); /* dialnorm (5) */ - if( lsmash_bits_get( bits, 1 ) ) /* compre (1) */ - lsmash_bits_get( bits, 8 ); /* compr (8) */ - if( substream_info->acmod == 0x0 ) - { - lsmash_bits_get( bits, 5 ); /* dialnorm2 (5) */ - if( lsmash_bits_get( bits, 1 ) ) /* compre2 (1) */ - lsmash_bits_get( bits, 8 ); /* compr2 (8) */ - } - if( info->strmtyp == 0x1 && lsmash_bits_get( bits, 1 ) ) /* chanmape (1) */ - { - uint16_t chanmap = lsmash_bits_get( bits, 16 ); /* chanmap (16) */ - info->independent_info[ info->current_independent_substream_id ].chan_loc |= lsmash_eac3_get_chan_loc_from_chanmap( chanmap ); - } - if( lsmash_bits_get( bits, 1 ) ) /* mixmdate (1) */ - { - if( substream_info->acmod > 0x2 ) - lsmash_bits_get( bits, 2 ); /* dmixmod (2) */ - if( ((substream_info->acmod & 0x1) && (substream_info->acmod > 0x2)) || (substream_info->acmod & 0x4) ) - lsmash_bits_get( bits, 6 ); /* ltrt[c/sur]mixlev (3) - * loro[c/sur]mixlev (3) */ - if( substream_info->lfeon && lsmash_bits_get( bits, 1 ) ) /* lfemixlevcode (1) */ - lsmash_bits_get( bits, 5 ); /* lfemixlevcod (5) */ - if( info->strmtyp == 0x0 ) - { - if( lsmash_bits_get( bits, 1 ) ) /* pgmscle (1) */ - lsmash_bits_get( bits, 6 ); /* pgmscl (6) */ - if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) ) /* pgmscle2 (1) */ - lsmash_bits_get( bits, 6 ); /* pgmscl2 (6) */ - if( lsmash_bits_get( bits, 1 ) ) /* extpgmscle (1) */ - lsmash_bits_get( bits, 6 ); /* extpgmscl (6) */ - uint8_t mixdef = lsmash_bits_get( bits, 2 ); /* mixdef (2) */ - if( mixdef == 0x1 ) - lsmash_bits_get( bits, 5 ); /* premixcmpsel (1) - * drcsrc (1) - * premixcmpscl (3) */ - else if( mixdef == 0x2 ) - lsmash_bits_get( bits, 12 ); /* mixdata (12) */ - else if( mixdef == 0x3 ) - { - uint8_t mixdeflen = lsmash_bits_get( bits, 5 ); /* mixdeflen (5) */ - lsmash_bits_get( bits, 8 * (mixdeflen + 2) ); /* mixdata (8 * (mixdeflen + 2)) - * mixdatafill (0-7) */ - } - if( substream_info->acmod < 0x2 ) - { - if( lsmash_bits_get( bits, 1 ) ) /* paninfoe (1) */ - lsmash_bits_get( bits, 14 ); /* panmean (8) - * paninfo (6) */ - if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) ) /* paninfo2e (1) */ - lsmash_bits_get( bits, 14 ); /* panmean2 (8) - * paninfo2 (6) */ - } - if( lsmash_bits_get( bits, 1 ) ) /* frmmixcfginfoe (1) */ - { - if( info->numblkscod == 0x0 ) - lsmash_bits_get( bits, 5 ); /* blkmixcfginfo[0] (5) */ - else - { - int number_of_blocks_per_syncframe = ((int []){ 1, 2, 3, 6 })[ info->numblkscod ]; - for( int blk = 0; blk < number_of_blocks_per_syncframe; blk++ ) - if( lsmash_bits_get( bits, 1 ) ) /* blkmixcfginfoe (1)*/ - lsmash_bits_get( bits, 5 ); /* blkmixcfginfo[blk] (5) */ - } - } - } - } - if( lsmash_bits_get( bits, 1 ) ) /* infomdate (1) */ - { - substream_info->bsmod = lsmash_bits_get( bits, 3 ); /* bsmod (3) */ - lsmash_bits_get( bits, 1 ); /* copyrightb (1) */ - lsmash_bits_get( bits, 1 ); /* origbs (1) */ - if( substream_info->acmod == 0x2 ) - lsmash_bits_get( bits, 4 ); /* dsurmod (2) - * dheadphonmod (2) */ - else if( substream_info->acmod >= 0x6 ) - lsmash_bits_get( bits, 2 ); /* dsurexmod (2) */ - if( lsmash_bits_get( bits, 1 ) ) /* audprodie (1) */ - lsmash_bits_get( bits, 8 ); /* mixlevel (5) - * roomtyp (2) - * adconvtyp (1) */ - if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) ) /* audprodie2 (1) */ - lsmash_bits_get( bits, 8 ); /* mixlevel2 (5) - * roomtyp2 (2) - * adconvtyp2 (1) */ - if( substream_info->fscod < 0x3 ) - lsmash_bits_get( bits, 1 ); /* sourcefscod (1) */ - } - else - substream_info->bsmod = 0; - if( info->strmtyp == 0x0 && info->numblkscod != 0x3 ) - lsmash_bits_get( bits, 1 ); /* convsync (1) */ - if( info->strmtyp == 0x2 ) - { - int blkid; - if( info->numblkscod == 0x3 ) - blkid = 1; - else - blkid = lsmash_bits_get( bits, 1 ); /* blkid (1) */ - if( blkid ) - lsmash_bits_get( bits, 6 ); /* frmsizecod (6) */ - } - if( lsmash_bits_get( bits, 1 ) ) /* addbsie (1) */ - { - uint8_t addbsil = lsmash_bits_get( bits, 6 ); /* addbsil (6) */ - lsmash_bits_get( bits, (addbsil + 1) * 8 ); /* addbsi ((addbsil + 1) * 8) */ - } - lsmash_bits_empty( bits ); - return eac3_check_syncframe_header( info ); -} - -void eac3_update_specific_param( eac3_info_t *info ) -{ - lsmash_eac3_specific_parameters_t *param = &info->dec3_param; - param->data_rate = 0; - param->num_ind_sub = info->number_of_independent_substreams - 1; - for( uint8_t i = 0; i <= param->num_ind_sub; i++ ) - param->independent_info[i] = info->independent_info[i]; - info->dec3_param_initialized = 1; -} - -#define EAC3_SPECIFIC_BOX_MIN_LENGTH 13 - -int eac3_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < EAC3_SPECIFIC_BOX_MIN_LENGTH ) - return -1; - lsmash_eac3_specific_parameters_t *param = (lsmash_eac3_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - param->data_rate = (data[0] << 5) | ((data[1] >> 3) & 0x1F); /* XXXX XXXX XXXX Xxxx */ - param->num_ind_sub = data[1] & 0x07; /* xxxx xxxx xxxx xXXX */ - data += 2; - size -= 2; - for( int i = 0; i <= param->num_ind_sub; i++ ) - { - if( size < 3 ) - return -1; - lsmash_eac3_substream_info_t *independent_info = ¶m->independent_info[i]; - independent_info->fscod = (data[0] >> 6) & 0x03; /* XXxx xxxx xxxx xxxx xxxx xxxx */ - independent_info->bsid = (data[0] >> 1) & 0x1F; /* xxXX XXXx xxxx xxxx xxxx xxxx */ - independent_info->bsmod = ((data[0] & 0x01) << 4) | ((data[1] >> 4) & 0x0F); /* xxxx xxxX XXXX xxxx xxxx xxxx */ - independent_info->acmod = (data[1] >> 1) & 0x07; /* xxxx xxxx xxxx XXXx xxxx xxxx */ - independent_info->lfeon = data[1] & 0x01; /* xxxx xxxx xxxx xxxX xxxx xxxx */ - independent_info->num_dep_sub = (data[2] >> 1) & 0x0F; /* xxxx xxxx xxxx xxxx xxxX XXXx */ - data += 3; - size -= 3; - if( independent_info->num_dep_sub > 0 ) - { - if( size < 1 ) - return -1; - independent_info->chan_loc = ((data[-1] & 0x01) << 8) | data[0]; /* xxxx xxxX XXXX XXXX */ - data += 1; - size -= 1; - } - } - return 0; -} - -int eac3_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: EC3 Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - if( box->size < EAC3_SPECIFIC_BOX_MIN_LENGTH ) - return -1; - uint8_t *data = box->binary; - isom_skip_box_common( &data ); - lsmash_ifprintf( fp, indent, "data_rate = %"PRIu16" kbit/s\n", (data[0] << 5) | ((data[1] >> 3) & 0x1F) ); - uint8_t num_ind_sub = data[1] & 0x07; - lsmash_ifprintf( fp, indent, "num_ind_sub = %"PRIu8"\n", num_ind_sub ); - data += 2; - for( int i = 0; i <= num_ind_sub; i++ ) - { - lsmash_ifprintf( fp, indent, "independent_substream[%d]\n", i ); - int sub_indent = indent + 1; - uint8_t fscod = (data[0] >> 6) & 0x03; - uint8_t bsid = (data[0] >> 1) & 0x1F; - uint8_t bsmod = ((data[0] & 0x01) << 4) | ((data[1] >> 4) & 0x0F); - uint8_t acmod = (data[1] >> 1) & 0x07; - uint8_t lfeon = data[1] & 0x01; - uint8_t num_dep_sub = (data[2] >> 1) & 0x0F; - if( fscod != 0x03 ) - lsmash_ifprintf( fp, sub_indent, "fscod = %"PRIu8" (%"PRIu32" Hz)\n", fscod, ac3_sample_rate_table[fscod] ); - else - lsmash_ifprintf( fp, sub_indent, "fscod = 0x03 (reduced sample rate)\n" ); - lsmash_ifprintf( fp, sub_indent, "bsid = %"PRIu8"\n", bsid ); - if( bsmod < 0x08 ) - lsmash_ifprintf( fp, sub_indent, "bsmod = %"PRIu8" (%s)\n", bsmod, bit_stream_mode[bsmod + (acmod == 0x01 ? 1 : acmod > 0x01 ? 2 : 0)] ); - else - lsmash_ifprintf( fp, sub_indent, "bsmod = %"PRIu8" (Undefined service)\n" ); - lsmash_ifprintf( fp, sub_indent, "acmod = %"PRIu8" (%s)\n", acmod, audio_coding_mode[acmod + (bsmod == 0x07 ? 8 : 0)] ); - lsmash_ifprintf( fp, sub_indent, "lfeon = %s\n", lfeon ? "1 (LFE)" : "0" ); - lsmash_ifprintf( fp, sub_indent, "num_dep_sub = %"PRIu8"\n", num_dep_sub ); - data += 3; - if( num_dep_sub > 0 ) - { - static const char *channel_location[] = - { - "LFE2", - "Cvh", - "Lvh/Rvh pair", - "Lw/Rw pair", - "Lsd/Rsd pair", - "Ts", - "Cs", - "Lrs/Rrs pair", - "Lc/Rc pair" - }; - uint16_t chan_loc = ((data[-1] & 0x01) << 8) | data[0]; - lsmash_ifprintf( fp, sub_indent, "chan_loc = 0x%04"PRIu16"\n", chan_loc ); - for( int j = 0; j < 9; j++ ) - if( (chan_loc >> j & 0x01) ) - lsmash_ifprintf( fp, sub_indent + 1, "%s\n", channel_location[j] ); - data += 1; - } - else - lsmash_ifprintf( fp, sub_indent, "reserved = %"PRIu8"\n", data[2] & 0x01 ); - } - return 0; -} - -#undef EAC3_SPECIFIC_BOX_MIN_LENGTH diff -Nru l-smash-1.9.1/a52.h l-smash-2.3.0/a52.h --- l-smash-1.9.1/a52.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/a52.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -/***************************************************************************** - * a52.h: - ***************************************************************************** - * Copyright (C) 2012-2014 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. */ - -#define AC3_MIN_SYNCFRAME_LENGTH 128 -#define AC3_MAX_SYNCFRAME_LENGTH 3840 -#define EAC3_MAX_SYNCFRAME_LENGTH 4096 - -#define IF_A52_SYNCWORD( x ) if( (x)[0] != 0x0b || (x)[1] != 0x77 ) - - -typedef struct -{ - lsmash_ac3_specific_parameters_t dac3_param; - lsmash_bits_t *bits; - uint8_t buffer[AC3_MAX_SYNCFRAME_LENGTH]; - uint8_t *next_dac3; - uint32_t au_number; -} ac3_info_t; - -typedef struct -{ - lsmash_eac3_specific_parameters_t dec3_param; - lsmash_eac3_substream_info_t independent_info[8]; - lsmash_eac3_substream_info_t dependent_info; - uint8_t dec3_param_initialized; - uint8_t strmtyp; - uint8_t substreamid; - uint8_t current_independent_substream_id; - uint8_t numblkscod; - uint8_t number_of_audio_blocks; - uint8_t frmsizecod; - uint8_t number_of_independent_substreams; - uint8_t no_more_read; - uint8_t *next_dec3; - uint32_t next_dec3_length; - uint32_t syncframe_count; - uint32_t syncframe_count_in_au; - uint32_t frame_size; - uint8_t buffer[2 * EAC3_MAX_SYNCFRAME_LENGTH]; - uint8_t *buffer_pos; - uint8_t *buffer_end; - lsmash_bits_t *bits; - lsmash_multiple_buffers_t *au_buffers; - uint8_t *au; - uint32_t au_length; - uint8_t *incomplete_au; - uint32_t incomplete_au_length; - uint32_t au_number; -} eac3_info_t; - -static const uint32_t ac3_sample_rate_table[4] = { 48000, 44100, 32000, 0 }; -static const uint8_t eac3_audio_block_table[4] = { 1, 2, 3, 6 }; - -int ac3_parse_syncframe_header( ac3_info_t *info, uint8_t *data ); -int eac3_parse_syncframe( eac3_info_t *info, uint8_t *data, uint32_t data_length ); -void eac3_update_specific_param( eac3_info_t *info ); diff -Nru l-smash-1.9.1/alac.c l-smash-2.3.0/alac.c --- l-smash-1.9.1/alac.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/alac.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -/***************************************************************************** - * alac.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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 - -#include "box.h" - -#define ALAC_SPECIFIC_BOX_LENGTH 36 - -uint8_t *lsmash_create_alac_specific_info( lsmash_alac_specific_parameters_t *param, uint32_t *data_length ) -{ - uint8_t buffer[ALAC_SPECIFIC_BOX_LENGTH]; - lsmash_bs_t bs = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = ALAC_SPECIFIC_BOX_LENGTH; - lsmash_bs_put_be32( &bs, ALAC_SPECIFIC_BOX_LENGTH ); /* box size */ - lsmash_bs_put_be32( &bs, ISOM_BOX_TYPE_ALAC.fourcc ); /* box type: 'alac' */ - lsmash_bs_put_be32( &bs, 0 ); /* version + flags */ - lsmash_bs_put_be32( &bs, param->frameLength ); - lsmash_bs_put_byte( &bs, 0 ); /* compatibleVersion */ - lsmash_bs_put_byte( &bs, param->bitDepth ); - lsmash_bs_put_byte( &bs, 40 ); /* pb */ - lsmash_bs_put_byte( &bs, 14 ); /* mb */ - lsmash_bs_put_byte( &bs, 10 ); /* kb */ - lsmash_bs_put_byte( &bs, param->numChannels ); - lsmash_bs_put_be16( &bs, 255 ); /* maxRun */ - lsmash_bs_put_be32( &bs, param->maxFrameBytes ); - lsmash_bs_put_be32( &bs, param->avgBitrate ); - lsmash_bs_put_be32( &bs, param->sampleRate ); - return lsmash_bs_export_data( &bs, data_length ); -} - -int alac_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < ALAC_SPECIFIC_BOX_LENGTH ) - return -1; - lsmash_alac_specific_parameters_t *param = (lsmash_alac_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - data += 4; /* Skip version and flags. */ - param->frameLength = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - param->bitDepth = data[5]; - param->numChannels = data[9]; - param->maxFrameBytes = (data[12] << 24) | (data[13] << 16) | (data[14] << 8) | data[15]; - param->avgBitrate = (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19]; - param->sampleRate = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23]; - return 0; -} - -int alac_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: ALAC Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - if( box->size < ALAC_SPECIFIC_BOX_LENGTH ) - return -1; - uint8_t *data = box->binary; - isom_skip_box_common( &data ); - lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", data[0] ); - lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", (data[1] << 16) | (data[2] << 8) | data[3] ); - data += 4; - lsmash_ifprintf( fp, indent, "frameLength = %"PRIu32"\n", (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3] ); - lsmash_ifprintf( fp, indent, "compatibleVersion = %"PRIu8"\n", data[4] ); - lsmash_ifprintf( fp, indent, "bitDepth = %"PRIu8"\n", data[5] ); - lsmash_ifprintf( fp, indent, "pb = %"PRIu8"\n", data[6] ); - lsmash_ifprintf( fp, indent, "mb = %"PRIu8"\n", data[7] ); - lsmash_ifprintf( fp, indent, "kb = %"PRIu8"\n", data[8] ); - lsmash_ifprintf( fp, indent, "numChannels = %"PRIu8"\n", data[9] ); - lsmash_ifprintf( fp, indent, "maxRun = %"PRIu16"\n", (data[10] << 8) | data[11] ); - lsmash_ifprintf( fp, indent, "maxFrameBytes = %"PRIu32"\n", (data[12] << 24) | (data[13] << 16) | (data[14] << 8) | data[15] ); - lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32"\n", (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19] ); - lsmash_ifprintf( fp, indent, "sampleRate = %"PRIu32"\n", (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23] ); - return 0; -} - -#undef ALAC_SPECIFIC_BOX_LENGTH diff -Nru l-smash-1.9.1/box.c l-smash-2.3.0/box.c --- l-smash-1.9.1/box.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/box.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,3771 +0,0 @@ -/***************************************************************************** - * box.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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 "box.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "write.h" -#ifdef LSMASH_DEMUXER_ENABLED -#include "print.h" -#include "timeline.h" -#endif - -void isom_init_box_common -( - void *_box, - void *_parent, - lsmash_box_type_t box_type, - uint64_t precedence, - void *destructor, - void *updater -) -{ - isom_box_t *box = (isom_box_t *)_box; - isom_box_t *parent = (isom_box_t *)_parent; - assert( box && parent && parent->root ); - box->class = &lsmash_box_class; - box->root = parent->root; - box->file = parent->file; - box->parent = parent; - box->precedence = precedence; - box->destruct = destructor ? destructor : lsmash_free; - box->update = updater; - box->size = 0; - box->type = box_type; - if( !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) && isom_is_fullbox( box ) ) - { - box->version = 0; - box->flags = 0; - } - isom_set_box_writer( box ); -} - -int isom_add_box_to_extension_list( void *parent_box, void *child_box ) -{ - assert( parent_box && child_box ); - isom_box_t *parent = (isom_box_t *)parent_box; - isom_box_t *child = (isom_box_t *)child_box; - /* Append at the end of the list. */ - lsmash_entry_list_t *list = &parent->extensions; - if( lsmash_add_entry( list, child ) ) - return -1; - /* Don't reorder the appended box when the file is opened for reading. */ - if( !parent->file || (parent->file->flags & LSMASH_FILE_MODE_READ) ) - return 0; - /* Reorder the appended box by 'precedence'. */ - lsmash_entry_t *x = list->tail; - assert( x ); - for( lsmash_entry_t *y = x->prev; y; y = y->prev ) - { - isom_box_t *box = (isom_box_t *)y->data; - if( !box || child->precedence > box->precedence ) - { - /* Exchange the entity data of adjacent two entries. */ - y->data = x->data; - x->data = box; - x = y; - } - else - break; - } - return 0; -} - -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.fourcc ); - 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.fourcc ); - } - if( box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ) - { - lsmash_bs_put_be32( bs, box->type.user.fourcc ); - lsmash_bs_put_bytes( bs, 12, box->type.user.id ); - } -} - -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 ); -} - -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 && lsmash_check_box_type_identical( 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 ); -} - -/* Return 1 if the box is fullbox, Otherwise return 0. */ -int isom_is_fullbox( void *box ) -{ - isom_box_t *current = (isom_box_t *)box; - lsmash_box_type_t type = current->type; - static lsmash_box_type_t fullbox_type_table[50] = { LSMASH_BOX_TYPE_INITIALIZER }; - if( !lsmash_check_box_type_specified( &fullbox_type_table[0] ) ) - { - /* Initialize the table. */ - int i = 0; - fullbox_type_table[i++] = ISOM_BOX_TYPE_SIDX; - fullbox_type_table[i++] = ISOM_BOX_TYPE_MVHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_TKHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_IODS; - fullbox_type_table[i++] = ISOM_BOX_TYPE_ESDS; - fullbox_type_table[i++] = QT_BOX_TYPE_ESDS; - fullbox_type_table[i++] = QT_BOX_TYPE_CLEF; - fullbox_type_table[i++] = QT_BOX_TYPE_PROF; - fullbox_type_table[i++] = QT_BOX_TYPE_ENOF; - fullbox_type_table[i++] = ISOM_BOX_TYPE_ELST; - fullbox_type_table[i++] = ISOM_BOX_TYPE_MDHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_HDLR; - fullbox_type_table[i++] = ISOM_BOX_TYPE_VMHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_SMHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_HMHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_NMHD; - fullbox_type_table[i++] = QT_BOX_TYPE_GMIN; - fullbox_type_table[i++] = ISOM_BOX_TYPE_DREF; - fullbox_type_table[i++] = ISOM_BOX_TYPE_URL; - fullbox_type_table[i++] = ISOM_BOX_TYPE_STSD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_STSL; - fullbox_type_table[i++] = QT_BOX_TYPE_CHAN; - fullbox_type_table[i++] = ISOM_BOX_TYPE_SRAT; - fullbox_type_table[i++] = ISOM_BOX_TYPE_STTS; - fullbox_type_table[i++] = ISOM_BOX_TYPE_CTTS; - fullbox_type_table[i++] = ISOM_BOX_TYPE_CSLG; - fullbox_type_table[i++] = ISOM_BOX_TYPE_STSS; - fullbox_type_table[i++] = QT_BOX_TYPE_STPS; - 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_STCO; - fullbox_type_table[i++] = ISOM_BOX_TYPE_CO64; - fullbox_type_table[i++] = ISOM_BOX_TYPE_SGPD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_SBGP; - fullbox_type_table[i++] = ISOM_BOX_TYPE_CHPL; - fullbox_type_table[i++] = ISOM_BOX_TYPE_META; - fullbox_type_table[i++] = QT_BOX_TYPE_KEYS; - fullbox_type_table[i++] = ISOM_BOX_TYPE_MEAN; - fullbox_type_table[i++] = ISOM_BOX_TYPE_NAME; - fullbox_type_table[i++] = ISOM_BOX_TYPE_MEHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_TREX; - fullbox_type_table[i++] = ISOM_BOX_TYPE_MFHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_TFHD; - fullbox_type_table[i++] = ISOM_BOX_TYPE_TFDT; - fullbox_type_table[i++] = ISOM_BOX_TYPE_TRUN; - fullbox_type_table[i++] = ISOM_BOX_TYPE_TFRA; - fullbox_type_table[i++] = ISOM_BOX_TYPE_MFRO; - fullbox_type_table[i] = LSMASH_BOX_TYPE_UNSPECIFIED; - } - for( int i = 0; lsmash_check_box_type_specified( &fullbox_type_table[i] ); i++ ) - if( lsmash_check_box_type_identical( type, fullbox_type_table[i] ) ) - return 1; - return lsmash_check_box_type_identical( type, ISOM_BOX_TYPE_CPRT ) - && current->parent && lsmash_check_box_type_identical( current->parent->type, ISOM_BOX_TYPE_UDTA ); -} - -/* Return 1 if the sample type is LPCM audio, Otherwise return 0. */ -int isom_is_lpcm_audio( void *box ) -{ - isom_box_t *current = (isom_box_t *)box; - lsmash_box_type_t type = current->type; - return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_23NI_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NONE_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_LPCM_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SOWT_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TWOS_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL32_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL64_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN24_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN32_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NOT_SPECIFIED ) - || (lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RAW_AUDIO ) && (current->manager & LSMASH_AUDIO_DESCRIPTION)); -} - -int isom_is_qt_audio( lsmash_codec_type_t type ) -{ - return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_23NI_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MAC3_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MAC6_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NONE_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_QDM2_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_QDMC_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_QCLP_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_AC_3_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_AGSM_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ALAC_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ALAW_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CDX2_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CDX4_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVCA_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVI_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL32_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL64_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IMA4_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN24_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN32_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_LPCM_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MP4A_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RAW_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SOWT_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TWOS_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULAW_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_VDVA_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FULLMP3_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MP3_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM2_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM17_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_GSM49_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NOT_SPECIFIED ); -} - -/* Return 1 if the sample type is uncompressed Y'CbCr video, Otherwise return 0. */ -int isom_is_uncompressed_ycbcr( lsmash_codec_type_t type ) -{ - return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_2VUY_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V210_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V216_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V308_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V408_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V410_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_YUV2_VIDEO ); -} - -int isom_is_waveform_audio( lsmash_box_type_t type ) -{ - return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM2_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM17_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_GSM49_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FULLMP3_AUDIO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MP3_AUDIO ); -} - -size_t isom_skip_box_common( uint8_t **p_data ) -{ - uint8_t *orig = *p_data; - uint8_t *data = *p_data; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - *p_data = data; - return data - orig; -} - -static void isom_destruct_extension_binary( void *ext ) -{ - if( !ext ) - return; - isom_box_t *box = (isom_box_t *)ext; - lsmash_free( box->binary ); - lsmash_free( box ); -} - -int isom_add_extension_binary -( - void *parent_box, - lsmash_box_type_t box_type, - uint64_t precedence, - uint8_t *box_data, - uint32_t box_size -) -{ - if( !parent_box || !box_data || box_size < ISOM_BASEBOX_COMMON_SIZE - || !lsmash_check_box_type_specified( &box_type ) ) - return -1; - isom_box_t *ext = lsmash_malloc_zero( sizeof(isom_box_t) ); - if( !ext ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - ext->class = &lsmash_box_class; - ext->root = parent->root; - ext->file = parent->file; - ext->parent = parent; - ext->manager = LSMASH_BINARY_CODED_BOX; - ext->precedence = precedence; - ext->size = box_size; - ext->type = box_type; - ext->binary = box_data; - ext->destruct = isom_destruct_extension_binary; - ext->update = NULL; - if( isom_add_box_to_extension_list( parent, ext ) ) - { - lsmash_free( ext ); - return -1; - } - isom_set_box_writer( ext ); - return 0; -} - -void isom_remove_extension_box( isom_box_t *ext ) -{ - if( !ext ) - return; - if( ext->destruct ) - ext->destruct( ext ); - else - lsmash_free( ext ); -} - -void isom_remove_all_extension_boxes( lsmash_entry_list_t *extensions ) -{ - lsmash_remove_entries( extensions, isom_remove_extension_box ); -} - -isom_box_t *isom_get_extension_box( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ) -{ - for( lsmash_entry_t *entry = extensions->head; entry; entry = entry->next ) - { - isom_box_t *ext = (isom_box_t *)entry->data; - if( !ext ) - continue; - if( lsmash_check_box_type_identical( ext->type, box_type ) ) - return ext; - } - return NULL; -} - -void *isom_get_extension_box_format( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ) -{ - for( lsmash_entry_t *entry = extensions->head; entry; entry = entry->next ) - { - isom_box_t *ext = (isom_box_t *)entry->data; - if( !ext || (ext->manager & LSMASH_BINARY_CODED_BOX) || !lsmash_check_box_type_identical( ext->type, box_type ) ) - continue; - return ext; - } - return NULL; -} - -lsmash_entry_t *isom_get_entry_of_box -( - lsmash_box_t *parent, - const lsmash_box_path_t box_path[] -) -{ - if( !parent ) - return NULL; - lsmash_entry_t *entry = NULL; - const lsmash_box_path_t *path = &box_path[0]; - while( lsmash_check_box_type_specified( &path->type ) ) - { - entry = parent->extensions.head; - if( !entry ) - return NULL; - parent = NULL; - uint32_t i = 1; - uint32_t number = path->number ? path->number : 1; - while( entry ) - { - isom_box_t *box = entry->data; - if( box && lsmash_check_box_type_identical( path->type, box->type ) ) - { - if( i == number ) - { - /* Found a box. Move to a child box. */ - parent = box; - ++path; - break; - } - ++i; - } - entry = entry->next; - } - if( !parent ) - return NULL; - } - return entry; -} - -/* box destructors */ -static void isom_remove_predefined_box( void *opaque_box, size_t offset_of_box ) -{ - assert( opaque_box ); - isom_box_t *box = (isom_box_t *)opaque_box; - if( box->parent ) - { - isom_box_t **p = (isom_box_t **)(((int8_t *)box->parent) + offset_of_box); - if( *p == box ) - *p = NULL; - } - isom_remove_all_extension_boxes( &box->extensions ); - lsmash_free( box ); -} -#define isom_remove_box( box_name, parent_type ) \ - isom_remove_predefined_box( box_name, offsetof( parent_type, box_name ) ) - -/* We always free boxes through the extension list of the parent box. - * Therefore, don't free boxes through any list other than the extension list. */ -static void isom_remove_box_in_predefined_list( void *opaque_box, size_t offset_of_list ) -{ - assert( opaque_box ); - isom_box_t *box = (isom_box_t *)opaque_box; - if( box->parent ) - { - lsmash_entry_list_t *list = (lsmash_entry_list_t *)(((int8_t *)box->parent) + offset_of_list); - if( list ) - for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) - if( box == entry->data ) - { - /* We don't free this box here. - * Because of freeing an entry of the list here, don't pass the list to free this box. - * Or double free. */ - entry->data = NULL; - lsmash_remove_entry_direct( list, entry, NULL ); - break; - } - } - /* Free this box actually here. */ - isom_remove_all_extension_boxes( &box->extensions ); - lsmash_free( box ); -} - -#define isom_remove_box_in_list( box_name, parent_type ) \ - isom_remove_box_in_predefined_list( box_name, offsetof( parent_type, box_name##_list ) ) - -/* Remove a box by the pointer containing its address. - * In addition, remove from the extension list of the parent box if possible. - * Don't call this function within a function freeing one or more entries of any extension list because of double free. - * Basically, don't use this function as a callback function. */ -void isom_remove_box_by_itself( void *opaque_box ) -{ - if( !opaque_box ) - return; - isom_box_t *box = (isom_box_t *)opaque_box; - if( box->parent ) - { - isom_box_t *parent = box->parent; - for( lsmash_entry_t *entry = parent->extensions.head; entry; entry = entry->next ) - if( box == entry->data ) - { - /* Free the corresponding entry here, therefore don't call this function as a callback function - * if a function frees the same entry later and calls this function. */ - lsmash_remove_entry_direct( &parent->extensions, entry, isom_remove_extension_box ); - return; - } - } - isom_remove_extension_box( box ); -} - -void isom_remove_unknown_box( isom_unknown_box_t *unknown_box ) -{ - if( !unknown_box ) - return; - if( unknown_box->unknown_field ) - lsmash_free( unknown_box->unknown_field ); - isom_remove_all_extension_boxes( &unknown_box->extensions ); - lsmash_free( unknown_box ); -} - -static void isom_remove_file( lsmash_file_t *file ) -{ - if( !file ) - return; -#ifdef LSMASH_DEMUXER_ENABLED - isom_remove_print_funcs( file ); - isom_remove_timelines( file ); -#endif - lsmash_free( file->compatible_brands ); - if( file->bs ) - { - if( file->bc_fclose && file->bs->stream ) - fclose( file->bs->stream ); - lsmash_bs_free( file->bs ); - } - if( file->fragment ) - { - lsmash_remove_list( file->fragment->pool, isom_remove_sample_pool ); - lsmash_free( file->fragment ); - } - isom_remove_box_in_list( file, lsmash_root_t ); -} - -static void isom_remove_ftyp( isom_ftyp_t *ftyp ) -{ - if( !ftyp ) - return; - if( ftyp->compatible_brands ) - lsmash_free( ftyp->compatible_brands ); - isom_remove_box( ftyp, lsmash_file_t ); -} - -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_trak( isom_trak_t *trak ) -{ - if( !trak ) - return; - if( trak->cache ) - { - isom_remove_sample_pool( trak->cache->chunk.pool ); - lsmash_remove_list( trak->cache->roll.pool, NULL ); - if( trak->cache->rap ) - lsmash_free( trak->cache->rap ); - lsmash_free( trak->cache ); - } - isom_remove_box_in_list( trak, isom_moov_t ); -} - -static void isom_remove_tkhd( isom_tkhd_t *tkhd ) -{ - if( !tkhd ) - return; - isom_remove_box( tkhd, isom_trak_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 ); -} - -static void isom_remove_tapt( isom_tapt_t *tapt ) -{ - if( !tapt ) - return; - isom_remove_box( tapt, isom_trak_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_box( edts, isom_trak_t ); -} - -static void isom_remove_track_reference_type( isom_tref_type_t *ref ) -{ - if( !ref ) - return; - if( ref->track_ID ) - lsmash_free( ref->track_ID ); - isom_remove_box_in_predefined_list( ref, offsetof( isom_tref_t, ref_list ) ); -} - -static void isom_remove_tref( isom_tref_t *tref ) -{ - if( !tref ) - return; - isom_remove_box( tref, isom_trak_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_box( gmhd, isom_minf_t ); -} - -static void isom_remove_hdlr( isom_hdlr_t *hdlr ) -{ - if( !hdlr ) - return; - if( hdlr->componentName ) - lsmash_free( hdlr->componentName ); - if( hdlr->parent ) - { - if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_MDIA ) ) - isom_remove_box( hdlr, isom_mdia_t ); - else if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_META ) - || lsmash_check_box_type_identical( hdlr->parent->type, QT_BOX_TYPE_META ) ) - isom_remove_box( hdlr, isom_meta_t ); - else if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_MINF ) ) - isom_remove_box( hdlr, isom_minf_t ); - else - assert( 0 ); - return; - } - isom_remove_all_extension_boxes( &hdlr->extensions ); - lsmash_free( hdlr ); -} - -static void isom_remove_clap( isom_clap_t *clap ) -{ - if( !clap ) - return; - isom_remove_all_extension_boxes( &clap->extensions ); - lsmash_free( clap ); -} - -static void isom_remove_pasp( isom_pasp_t *pasp ) -{ - if( !pasp ) - return; - isom_remove_all_extension_boxes( &pasp->extensions ); - lsmash_free( pasp ); -} - -static void isom_remove_glbl( isom_glbl_t *glbl ) -{ - if( !glbl ) - return; - if( glbl->header_data ) - free( glbl->header_data ); - isom_remove_all_extension_boxes( &glbl->extensions ); - lsmash_free( glbl ); -} - -static void isom_remove_colr( isom_colr_t *colr ) -{ - if( !colr ) - return; - isom_remove_all_extension_boxes( &colr->extensions ); - lsmash_free( colr ); -} - -static void isom_remove_gama( isom_gama_t *gama ) -{ - if( !gama ) - return; - isom_remove_all_extension_boxes( &gama->extensions ); - lsmash_free( gama ); -} - -static void isom_remove_fiel( isom_fiel_t *fiel ) -{ - if( !fiel ) - return; - isom_remove_all_extension_boxes( &fiel->extensions ); - lsmash_free( fiel ); -} - -static void isom_remove_cspc( isom_cspc_t *cspc ) -{ - if( !cspc ) - return; - isom_remove_all_extension_boxes( &cspc->extensions ); - lsmash_free( cspc ); -} - -static void isom_remove_sgbt( isom_sgbt_t *sgbt ) -{ - if( !sgbt ) - return; - isom_remove_all_extension_boxes( &sgbt->extensions ); - lsmash_free( sgbt ); -} - -static void isom_remove_stsl( isom_stsl_t *stsl ) -{ - if( !stsl ) - return; - isom_remove_all_extension_boxes( &stsl->extensions ); - lsmash_free( stsl ); -} - -static void isom_remove_esds( isom_esds_t *esds ) -{ - if( !esds ) - return; - mp4sys_remove_ES_Descriptor( esds->ES ); - isom_remove_all_extension_boxes( &esds->extensions ); - lsmash_free( esds ); -} - -static void isom_remove_btrt( isom_btrt_t *btrt ) -{ - if( !btrt ) - return; - isom_remove_all_extension_boxes( &btrt->extensions ); - lsmash_free( btrt ); -} - -static void isom_remove_font_record( isom_font_record_t *font_record ) -{ - if( !font_record ) - return; - if( font_record->font_name ) - lsmash_free( font_record->font_name ); - lsmash_free( font_record ); -} - -static 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 ); -} - -static void isom_remove_frma( isom_frma_t *frma ) -{ - if( !frma ) - return; - isom_remove_box( frma, isom_wave_t ); -} - -static void isom_remove_enda( isom_enda_t *enda ) -{ - if( !enda ) - return; - isom_remove_box( enda, isom_wave_t ); -} - -static void isom_remove_mp4a( isom_mp4a_t *mp4a ) -{ - if( !mp4a ) - return; - isom_remove_box( mp4a, isom_wave_t ); -} - -static void isom_remove_terminator( isom_terminator_t *terminator ) -{ - if( !terminator ) - return; - isom_remove_box( terminator, isom_wave_t ); -} - -static void isom_remove_wave( isom_wave_t *wave ) -{ - if( !wave ) - return; - isom_remove_all_extension_boxes( &wave->extensions ); - lsmash_free( wave ); -} - -static void isom_remove_chan( isom_chan_t *chan ) -{ - if( !chan ) - return; - if( chan->channelDescriptions ) - lsmash_free( chan->channelDescriptions ); - isom_remove_all_extension_boxes( &chan->extensions ); - lsmash_free( chan ); -} - -static void isom_remove_srat( isom_srat_t *srat ) -{ - if( !srat ) - return; - isom_remove_all_extension_boxes( &srat->extensions ); - lsmash_free( srat ); -} - -static void isom_remove_stsd( isom_stsd_t *stsd ) -{ - if( !stsd ) - return; - isom_remove_box( stsd, isom_stbl_t ); -} - -static void isom_remove_visual_description( isom_sample_entry_t *description ) -{ - isom_visual_entry_t *visual = (isom_visual_entry_t *)description; - isom_remove_all_extension_boxes( &visual->extensions ); - if( visual->color_table.array ) - lsmash_free( visual->color_table.array ); - lsmash_free( visual ); -} - -static void isom_remove_audio_description( isom_sample_entry_t *description ) -{ - isom_audio_entry_t *audio = (isom_audio_entry_t *)description; - isom_remove_all_extension_boxes( &audio->extensions ); - lsmash_free( audio ); -} - -static void isom_remove_hint_description( isom_sample_entry_t *description ) -{ - isom_hint_entry_t *hint = (isom_hint_entry_t *)description; - isom_remove_all_extension_boxes( &hint->extensions ); - if( hint->data ) - lsmash_free( hint->data ); - lsmash_free( hint ); -} - -static void isom_remove_metadata_description( isom_sample_entry_t *description ) -{ - isom_metadata_entry_t *metadata = (isom_metadata_entry_t *)description; - isom_remove_all_extension_boxes( &metadata->extensions ); - lsmash_free( metadata ); -} - -static void isom_remove_tx3g_description( isom_sample_entry_t *description ) -{ - isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)description; - isom_remove_all_extension_boxes( &tx3g->extensions ); - lsmash_free( tx3g ); -} - -static void isom_remove_qt_text_description( isom_sample_entry_t *description ) -{ - isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)description; - isom_remove_all_extension_boxes( &text->extensions ); - if( text->font_name ) - free( text->font_name ); - lsmash_free( text ); -} - -static void isom_remove_mp4s_description( isom_sample_entry_t *description ) -{ - isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)description; - isom_remove_all_extension_boxes( &mp4s->extensions ); - lsmash_free( mp4s ); -} - -void isom_remove_sample_description( isom_sample_entry_t *sample ) -{ - if( !sample ) - return; - lsmash_codec_type_t sample_type = sample->type; - if( lsmash_check_box_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) ) - { - if( sample->manager & LSMASH_VIDEO_DESCRIPTION ) - { - isom_remove_visual_description( sample ); - return; - } - else if( sample->manager & LSMASH_AUDIO_DESCRIPTION ) - { - isom_remove_audio_description( sample ); - return; - } - } - static struct description_remover_table_tag - { - lsmash_codec_type_t type; - void (*func)( isom_sample_entry_t * ); - } description_remover_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } }; - if( !description_remover_table[0].func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( type, func ) \ - description_remover_table[i++] = (struct description_remover_table_tag){ type, func } - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_remove_visual_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_remove_audio_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_FDP_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M2TS_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_PM2T_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_PRTP_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RM2T_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RRTP_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RSRP_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RTP_HINT , isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SM2T_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SRTP_HINT, isom_remove_hint_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_IXSE_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_METT_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_METX_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLIX_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_OKSD_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVCM_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TEXT_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_URIM_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_XML_META, isom_remove_metadata_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_remove_tx3g_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_remove_qt_text_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_remove_mp4s_description ); - ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL ); - } - for( int i = 0; description_remover_table[i].func; i++ ) - if( lsmash_check_codec_type_identical( sample_type, description_remover_table[i].type ) ) - { - description_remover_table[i].func( sample ); - return; - } -} - -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 ); - if( sdtp->parent ) - { - if( lsmash_check_box_type_identical( sdtp->parent->type, ISOM_BOX_TYPE_STBL ) ) - isom_remove_box( sdtp, isom_stbl_t ); - else if( lsmash_check_box_type_identical( sdtp->parent->type, ISOM_BOX_TYPE_TRAF ) ) - isom_remove_box( sdtp, isom_traf_t ); - else - assert( 0 ); - return; - } - isom_remove_all_extension_boxes( &sdtp->extensions ); - lsmash_free( sdtp ); -} - -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_t *sgpd ) -{ - if( !sgpd ) - return; - lsmash_remove_list( sgpd->list, NULL ); - isom_remove_box_in_list( sgpd, isom_stbl_t ); -} - -static void isom_remove_sbgp( isom_sbgp_t *sbgp ) -{ - if( !sbgp ) - return; - lsmash_remove_list( sbgp->list, NULL ); - isom_remove_box_in_list( sbgp, isom_stbl_t ); -} - -static void isom_remove_stbl( isom_stbl_t *stbl ) -{ - if( !stbl ) - return; - isom_remove_box( stbl, isom_minf_t ); -} - -static void isom_remove_dref_entry( isom_dref_entry_t *data_entry ) -{ - if( !data_entry ) - return; - lsmash_free( data_entry->name ); - lsmash_free( data_entry->location ); - isom_remove_box_in_predefined_list( data_entry, offsetof( isom_dref_t, list ) ); -} - -static void isom_remove_dref( isom_dref_t *dref ) -{ - if( !dref ) - return; - isom_remove_box( dref, isom_dinf_t ); -} - -static void isom_remove_dinf( isom_dinf_t *dinf ) -{ - if( !dinf ) - return; - isom_remove_box( dinf, isom_minf_t ); -} - -static void isom_remove_minf( isom_minf_t *minf ) -{ - if( !minf ) - return; - isom_remove_box( minf, isom_mdia_t ); -} - -static void isom_remove_mdia( isom_mdia_t *mdia ) -{ - if( !mdia ) - return; - isom_remove_box( mdia, isom_trak_t ); -} - -static void isom_remove_chpl( isom_chpl_t *chpl ) -{ - if( !chpl ) - return; - if( !chpl->list ) - { - lsmash_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 ) - lsmash_free( data->chapter_name ); - lsmash_free( data ); - } - lsmash_entry_t *next = entry->next; - lsmash_free( entry ); - entry = next; - } - lsmash_free( chpl->list ); - isom_remove_box( chpl, isom_udta_t ); -} - -static void isom_remove_keys_entry( isom_keys_entry_t *data ) -{ - if( !data ) - return; - if( data->key_value ) - lsmash_free( data->key_value ); - lsmash_free( data ); -} - -static void isom_remove_keys( isom_keys_t *keys ) -{ - if( !keys ) - return; - lsmash_remove_list( keys->list, isom_remove_keys_entry ); - isom_remove_box( keys, isom_meta_t ); -} - -static void isom_remove_mean( isom_mean_t *mean ) -{ - if( !mean ) - return; - if( mean->meaning_string ) - lsmash_free( mean->meaning_string ); - isom_remove_box( mean, isom_metaitem_t ); -} - -static void isom_remove_name( isom_name_t *name ) -{ - if( !name ) - return; - if( name->name ) - lsmash_free( name->name ); - isom_remove_box( name, isom_metaitem_t ); -} - -static void isom_remove_data( isom_data_t *data ) -{ - if( !data ) - return; - if( data->value ) - lsmash_free( data->value ); - isom_remove_box( data, isom_metaitem_t ); -} - -static void isom_remove_metaitem( isom_metaitem_t *metaitem ) -{ - if( !metaitem ) - return; - isom_remove_box_in_predefined_list( metaitem, offsetof( isom_ilst_t, item_list ) ); -} - -static void isom_remove_ilst( isom_ilst_t *ilst ) -{ - if( !ilst ) - return; - isom_remove_box( ilst, isom_meta_t ); -} - -static void isom_remove_meta( isom_meta_t *meta ) -{ - if( !meta ) - return; - if( meta->parent ) - { - if( lsmash_check_box_type_identical( meta->parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) - isom_remove_box( meta, lsmash_file_t ); - else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_MOOV ) ) - isom_remove_box( meta, isom_moov_t ); - else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_TRAK ) ) - isom_remove_box( meta, isom_trak_t ); - else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_UDTA ) ) - isom_remove_box( meta, isom_udta_t ); - else - assert( 0 ); - return; - } - isom_remove_all_extension_boxes( &meta->extensions ); - lsmash_free( meta ); -} - -static void isom_remove_cprt( isom_cprt_t *cprt ) -{ - if( !cprt ) - return; - if( cprt->notice ) - lsmash_free( cprt->notice ); - isom_remove_box_in_list( cprt, isom_udta_t ); -} - -static void isom_remove_udta( isom_udta_t *udta ) -{ - if( !udta ) - return; - if( udta->parent ) - { - if( lsmash_check_box_type_identical( udta->parent->type, ISOM_BOX_TYPE_MOOV ) ) - isom_remove_box( udta, isom_moov_t ); - else if( lsmash_check_box_type_identical( udta->parent->type, ISOM_BOX_TYPE_TRAK ) ) - isom_remove_box( udta, isom_trak_t ); - else - assert( 0 ); - return; - } - isom_remove_all_extension_boxes( &udta->extensions ); - lsmash_free( udta ); -} - -static void isom_remove_WLOC( isom_WLOC_t *WLOC ) -{ - if( !WLOC ) - return; - isom_remove_box( WLOC, isom_udta_t ); -} - -static void isom_remove_LOOP( isom_LOOP_t *LOOP ) -{ - if( !LOOP ) - return; - isom_remove_box( LOOP, isom_udta_t ); -} - -static void isom_remove_SelO( isom_SelO_t *SelO ) -{ - if( !SelO ) - return; - isom_remove_box( SelO, isom_udta_t ); -} - -static void isom_remove_AllF( isom_AllF_t *AllF ) -{ - if( !AllF ) - return; - isom_remove_box( AllF, isom_udta_t ); -} - -static void isom_remove_ctab( isom_ctab_t *ctab ) -{ - if( !ctab ) - return; - if( ctab->color_table.array ) - lsmash_free( ctab->color_table.array ); - if( ctab->parent && lsmash_check_box_type_identical( ctab->parent->type, ISOM_BOX_TYPE_MOOV ) ) - isom_remove_box( ctab, isom_moov_t ); - else - { - isom_remove_all_extension_boxes( &ctab->extensions ); - lsmash_free( ctab ); - } -} - -static void isom_remove_mehd( isom_mehd_t *mehd ) -{ - if( !mehd ) - return; - isom_remove_box( mehd, isom_mvex_t ); -} - -static void isom_remove_trex( isom_trex_t *trex ) -{ - if( !trex ) - return; - isom_remove_box_in_list( trex, isom_mvex_t ); -} - -static void isom_remove_mvex( isom_mvex_t *mvex ) -{ - if( !mvex ) - return; - isom_remove_box( mvex, isom_moov_t ); -} - -static void isom_remove_mvhd( isom_mvhd_t *mvhd ) -{ - if( !mvhd ) - return; - isom_remove_box( mvhd, isom_moov_t ); -} - -static void isom_remove_moov( isom_moov_t *moov ) -{ - if( !moov ) - return; - isom_remove_box( moov, lsmash_file_t ); -} - -static void isom_remove_mfhd( isom_mfhd_t *mfhd ) -{ - if( !mfhd ) - return; - isom_remove_box( mfhd, isom_moof_t ); -} - -static void isom_remove_tfhd( isom_tfhd_t *tfhd ) -{ - if( !tfhd ) - return; - isom_remove_box( tfhd, isom_traf_t ); -} - -static void isom_remove_tfdt( isom_tfdt_t *tfdt ) -{ - if( !tfdt ) - return; - isom_remove_box( tfdt, isom_traf_t ); -} - -static void isom_remove_trun( isom_trun_t *trun ) -{ - if( !trun ) - return; - lsmash_remove_list( trun->optional, NULL ); - isom_remove_box_in_list( trun, isom_traf_t ); -} - -static void isom_remove_traf( isom_traf_t *traf ) -{ - if( !traf ) - return; - isom_remove_box_in_list( traf, isom_moof_t ); -} - -static void isom_remove_moof( isom_moof_t *moof ) -{ - if( !moof ) - return; - isom_remove_box_in_list( moof, lsmash_file_t ); -} - -static void isom_remove_mdat( isom_mdat_t *mdat ) -{ - if( !mdat ) - return; - isom_remove_box( mdat, lsmash_file_t ); -} - -static void isom_remove_free( isom_free_t *skip ) -{ - if( !skip ) - return; - if( skip->data ) - lsmash_free( skip->data ); - isom_remove_predefined_box( skip, offsetof( lsmash_file_t, free ) ); -} -#define isom_remove_skip isom_remove_free - -static void isom_remove_tfra( isom_tfra_t *tfra ) -{ - if( !tfra ) - return; - lsmash_remove_list( tfra->list, NULL ); - isom_remove_box_in_list( tfra, isom_mfra_t ); -} - -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; - isom_remove_box( mfra, lsmash_file_t ); -} - -static void isom_remove_styp( isom_styp_t *styp ) -{ - if( !styp ) - return; - if( styp->compatible_brands ) - lsmash_free( styp->compatible_brands ); - isom_remove_box_in_list( styp, lsmash_file_t ); -} - -static void isom_remove_sidx( isom_sidx_t *sidx ) -{ - if( !sidx ) - return; - lsmash_remove_list( sidx->list, NULL ); - isom_remove_box_in_list( sidx, lsmash_file_t ); -} - -/* box size updaters */ -#define CHECK_LARGESIZE( x ) \ - (x)->size += isom_update_extension_boxes( x ); \ - if( (x)->size > UINT32_MAX ) \ - (x)->size += 8 - -static uint64_t isom_update_extension_boxes( void *box ); - -uint64_t isom_update_unknown_box_size( isom_unknown_box_t *unknown_box ) -{ - if( !unknown_box ) - return 0; - unknown_box->size = ISOM_BASEBOX_COMMON_SIZE + unknown_box->unknown_size; - CHECK_LARGESIZE( unknown_box ); - return unknown_box->size; -} - -static uint64_t isom_update_ftyp_size( isom_ftyp_t *ftyp ) -{ - if( !ftyp ) - return 0; - ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8 + ftyp->brand_count * 4; - CHECK_LARGESIZE( ftyp ); - return ftyp->size; -} - -static uint64_t isom_update_moov_size( isom_moov_t *moov ) -{ - if( !moov ) - return 0; - moov->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( moov ); - return moov->size; -} - -static uint64_t isom_update_mvhd_size( isom_mvhd_t *mvhd ) -{ - if( !mvhd ) - return 0; - mvhd->version = 0; - if( (mvhd->file && !mvhd->file->undefined_64_ver) - && (mvhd->creation_time > UINT32_MAX - || mvhd->modification_time > UINT32_MAX - || mvhd->duration > UINT32_MAX) ) - mvhd->version = 1; - mvhd->size = ISOM_FULLBOX_COMMON_SIZE + 96 + (uint64_t)mvhd->version * 12; - CHECK_LARGESIZE( mvhd ); - return mvhd->size; -} - -static uint64_t isom_update_iods_size( isom_iods_t *iods ) -{ - if( !iods - || !iods->OD ) - return 0; - iods->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ObjectDescriptor_size( iods->OD ); - CHECK_LARGESIZE( iods ); - return iods->size; -} - -static uint64_t isom_update_ctab_size( isom_ctab_t *ctab ) -{ - if( !ctab ) - return 0; - ctab->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)(1 + ctab->color_table.size + !!ctab->color_table.array) * 8; - CHECK_LARGESIZE( ctab ); - return ctab->size; -} - -static uint64_t isom_update_trak_size( isom_trak_t *trak ) -{ - if( !trak ) - return 0; - trak->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( trak ); - return trak->size; -} - -static uint64_t isom_update_tkhd_size( isom_tkhd_t *tkhd ) -{ - if( !tkhd ) - return 0; - tkhd->version = 0; - if( (tkhd->file && !tkhd->file->undefined_64_ver) - && (tkhd->creation_time > UINT32_MAX - || tkhd->modification_time > UINT32_MAX - || tkhd->duration > UINT32_MAX) ) - tkhd->version = 1; - tkhd->size = ISOM_FULLBOX_COMMON_SIZE + 80 + (uint64_t)tkhd->version * 12; - CHECK_LARGESIZE( tkhd ); - return tkhd->size; -} - -static uint64_t isom_update_tapt_size( isom_tapt_t *tapt ) -{ - if( !tapt ) - return 0; - tapt->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( tapt ); - return tapt->size; -} - -static uint64_t isom_update_clef_size( isom_clef_t *clef ) -{ - if( !clef ) - return 0; - clef->size = ISOM_FULLBOX_COMMON_SIZE + 8; - CHECK_LARGESIZE( clef ); - return clef->size; -} - -static uint64_t isom_update_prof_size( isom_prof_t *prof ) -{ - if( !prof ) - return 0; - prof->size = ISOM_FULLBOX_COMMON_SIZE + 8; - CHECK_LARGESIZE( prof ); - return prof->size; -} - -static uint64_t isom_update_enof_size( isom_enof_t *enof ) -{ - if( !enof ) - return 0; - enof->size = ISOM_FULLBOX_COMMON_SIZE + 8; - CHECK_LARGESIZE( enof ); - return enof->size; -} - -static uint64_t isom_update_edts_size( isom_edts_t *edts ) -{ - if( !edts ) - return 0; - edts->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( edts ); - return edts->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( (elst->file && !elst->file->undefined_64_ver) - && (data->segment_duration > UINT32_MAX - || data->media_time > INT32_MAX - || data->media_time < INT32_MIN) ) - elst->version = 1; - } - elst->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)i * ( elst->version ? 20 : 12 ); - CHECK_LARGESIZE( elst ); - return elst->size; -} - -static uint64_t isom_update_tref_size( isom_tref_t *tref ) -{ - if( !tref ) - return 0; - tref->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( tref ); - return tref->size; -} - -static uint64_t isom_update_track_reference_type_size( isom_tref_type_t *ref ) -{ - ref->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)ref->ref_count * 4; - CHECK_LARGESIZE( ref ); - return ref->size; -} - -static uint64_t isom_update_mdia_size( isom_mdia_t *mdia ) -{ - if( !mdia ) - return 0; - mdia->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( mdia ); - return mdia->size; -} - -static uint64_t isom_update_mdhd_size( isom_mdhd_t *mdhd ) -{ - if( !mdhd ) - return 0; - mdhd->version = 0; - if( (mdhd->file && !mdhd->file->undefined_64_ver) - && (mdhd->creation_time > UINT32_MAX - || mdhd->modification_time > UINT32_MAX - || mdhd->duration > UINT32_MAX) ) - mdhd->version = 1; - mdhd->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)mdhd->version * 12; - CHECK_LARGESIZE( mdhd ); - return mdhd->size; -} - -static uint64_t isom_update_hdlr_size( isom_hdlr_t *hdlr ) -{ - if( !hdlr ) - return 0; - hdlr->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)hdlr->componentName_length; - CHECK_LARGESIZE( hdlr ); - return hdlr->size; -} - -static uint64_t isom_update_minf_size( isom_minf_t *minf ) -{ - if( !minf ) - return 0; - minf->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( minf ); - return minf->size; -} - -static uint64_t isom_update_vmhd_size( isom_vmhd_t *vmhd ) -{ - if( !vmhd ) - return 0; - vmhd->size = ISOM_FULLBOX_COMMON_SIZE + 8; - CHECK_LARGESIZE( vmhd ); - return vmhd->size; -} - -static uint64_t isom_update_smhd_size( isom_smhd_t *smhd ) -{ - if( !smhd ) - return 0; - smhd->size = ISOM_FULLBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( smhd ); - return smhd->size; -} - -static uint64_t isom_update_hmhd_size( isom_hmhd_t *hmhd ) -{ - if( !hmhd ) - return 0; - hmhd->size = ISOM_FULLBOX_COMMON_SIZE + 16; - CHECK_LARGESIZE( hmhd ); - return hmhd->size; -} - -static uint64_t isom_update_nmhd_size( isom_nmhd_t *nmhd ) -{ - if( !nmhd ) - return 0; - nmhd->size = ISOM_FULLBOX_COMMON_SIZE; - CHECK_LARGESIZE( nmhd ); - return nmhd->size; -} - -static uint64_t isom_update_gmhd_size( isom_gmhd_t *gmhd ) -{ - if( !gmhd ) - return 0; - gmhd->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( gmhd ); - return gmhd->size; -} - -static uint64_t isom_update_gmin_size( isom_gmin_t *gmin ) -{ - if( !gmin ) - return 0; - gmin->size = ISOM_FULLBOX_COMMON_SIZE + 12; - CHECK_LARGESIZE( gmin ); - return gmin->size; -} - -static uint64_t isom_update_text_size( isom_text_t *text ) -{ - if( !text ) - return 0; - text->size = ISOM_BASEBOX_COMMON_SIZE + 36; - CHECK_LARGESIZE( text ); - return text->size; -} - -static uint64_t isom_update_dinf_size( isom_dinf_t *dinf ) -{ - if( !dinf ) - return 0; - dinf->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( dinf ); - return dinf->size; -} - -static uint64_t isom_update_dref_size( isom_dref_t *dref ) -{ - if( !dref ) - return 0; - dref->size = ISOM_LIST_FULLBOX_COMMON_SIZE; - CHECK_LARGESIZE( dref ); - return dref->size; -} - -static uint64_t isom_update_dref_entry_size( isom_dref_entry_t *urln ) -{ - if( !urln ) - return 0; - urln->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)urln->name_length + urln->location_length; - CHECK_LARGESIZE( urln ); - return urln->size; -} - -static uint64_t isom_update_stbl_size( isom_stbl_t *stbl ) -{ - if( !stbl ) - return 0; - stbl->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( stbl ); - return stbl->size; -} - -static uint64_t isom_update_pasp_size( isom_pasp_t *pasp ) -{ - if( !pasp ) - return 0; - pasp->size = ISOM_BASEBOX_COMMON_SIZE + 8; - CHECK_LARGESIZE( pasp ); - return pasp->size; -} - -static uint64_t isom_update_clap_size( isom_clap_t *clap ) -{ - if( !clap ) - return 0; - clap->size = ISOM_BASEBOX_COMMON_SIZE + 32; - CHECK_LARGESIZE( clap ); - return clap->size; -} - -static uint64_t isom_update_glbl_size( isom_glbl_t *glbl ) -{ - if( !glbl ) - return 0; - glbl->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)glbl->header_size; - CHECK_LARGESIZE( glbl ); - return glbl->size; -} - -static uint64_t isom_update_colr_size( isom_colr_t *colr ) -{ - if( !colr - || (colr->color_parameter_type != ISOM_COLOR_PARAMETER_TYPE_NCLX - && colr->color_parameter_type != QT_COLOR_PARAMETER_TYPE_NCLC) ) - return 0; - colr->size = ISOM_BASEBOX_COMMON_SIZE + 10 + (colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX); - CHECK_LARGESIZE( colr ); - return colr->size; -} - -static uint64_t isom_update_gama_size( isom_gama_t *gama ) -{ - if( !gama || !gama->parent ) - return 0; - /* Note: 'gama' box is superseded by 'colr' box. - * Therefore, writers of QTFF should never write both 'colr' and 'gama' box into an Image Description. */ - if( isom_get_extension_box_format( &((isom_visual_entry_t *)gama->parent)->extensions, QT_BOX_TYPE_COLR ) ) - return 0; - gama->size = ISOM_BASEBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( gama ); - return gama->size; -} - -static uint64_t isom_update_fiel_size( isom_fiel_t *fiel ) -{ - if( !fiel ) - return 0; - fiel->size = ISOM_BASEBOX_COMMON_SIZE + 2; - CHECK_LARGESIZE( fiel ); - return fiel->size; -} - -static uint64_t isom_update_cspc_size( isom_cspc_t *cspc ) -{ - if( !cspc ) - return 0; - cspc->size = ISOM_BASEBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( cspc ); - return cspc->size; -} - -static uint64_t isom_update_sgbt_size( isom_sgbt_t *sgbt ) -{ - if( !sgbt ) - return 0; - sgbt->size = ISOM_BASEBOX_COMMON_SIZE + 1; - CHECK_LARGESIZE( sgbt ); - return sgbt->size; -} - -static uint64_t isom_update_stsl_size( isom_stsl_t *stsl ) -{ - if( !stsl ) - return 0; - stsl->size = ISOM_FULLBOX_COMMON_SIZE + 6; - CHECK_LARGESIZE( stsl ); - return stsl->size; -} - -static uint64_t isom_update_esds_size( isom_esds_t *esds ) -{ - if( !esds ) - return 0; - esds->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ES_Descriptor_size( esds->ES ); - CHECK_LARGESIZE( esds ); - return esds->size; -} - -static uint64_t isom_update_btrt_size( isom_btrt_t *btrt ) -{ - if( !btrt ) - return 0; - btrt->size = ISOM_BASEBOX_COMMON_SIZE + 12; - CHECK_LARGESIZE( btrt ); - return btrt->size; -} - -static uint64_t isom_update_visual_entry_size( isom_sample_entry_t *description ) -{ - if( !description ) - return 0; - isom_visual_entry_t *visual = (isom_visual_entry_t *)description; - visual->size = ISOM_BASEBOX_COMMON_SIZE + 78; - if( visual->color_table_ID == 0 ) - visual->size += (uint64_t)(1 + visual->color_table.size + !!visual->color_table.array) * 8; - CHECK_LARGESIZE( visual ); - return visual->size; -} - -#if 0 -static uint64_t isom_update_mp4s_entry_size( isom_sample_entry_t *description ) -{ - if( !description || !lsmash_check_box_type_identical( description->type, ISOM_CODEC_TYPE_MP4S_SYSTEM ) ) - return 0; - isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)description; - mp4s->size = ISOM_BASEBOX_COMMON_SIZE + 8; - CHECK_LARGESIZE( mp4s ); - return mp4s->size; -} -#endif - -static uint64_t isom_update_frma_size( isom_frma_t *frma ) -{ - if( !frma ) - return 0; - frma->size = ISOM_BASEBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( frma ); - return frma->size; -} - -static uint64_t isom_update_enda_size( isom_enda_t *enda ) -{ - if( !enda ) - return 0; - enda->size = ISOM_BASEBOX_COMMON_SIZE + 2; - CHECK_LARGESIZE( enda ); - return enda->size; -} - -static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a ) -{ - if( !mp4a ) - return 0; - mp4a->size = ISOM_BASEBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( mp4a ); - return mp4a->size; -} - -static uint64_t isom_update_terminator_size( isom_terminator_t *terminator ) -{ - if( !terminator ) - return 0; - terminator->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( terminator ); - return terminator->size; -} - -static uint64_t isom_update_wave_size( isom_wave_t *wave ) -{ - if( !wave ) - return 0; - wave->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( wave ); - return wave->size; -} - -static uint64_t isom_update_chan_size( isom_chan_t *chan ) -{ - if( !chan ) - return 0; - chan->size = ISOM_FULLBOX_COMMON_SIZE + 12 + 20 * (uint64_t)chan->numberChannelDescriptions; - CHECK_LARGESIZE( chan ); - return chan->size; -} - -static uint64_t isom_update_srat_size( isom_srat_t *srat ) -{ - if( !srat ) - return 0; - srat->size = ISOM_FULLBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( srat ); - return srat->size; -} - -static uint64_t isom_update_audio_entry_size( isom_sample_entry_t *description ) -{ - if( !description ) - return 0; - isom_audio_entry_t *audio = (isom_audio_entry_t *)description; - audio->size = ISOM_BASEBOX_COMMON_SIZE + 28; - if( audio->version == 1 ) - audio->size += 16; - else if( audio->version == 2 ) - audio->size += 36; - CHECK_LARGESIZE( audio ); - return audio->size; -} - -static uint64_t isom_update_qt_text_entry_size( isom_sample_entry_t *description ) -{ - if( !description ) - return 0; - isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)description; - text->size = ISOM_BASEBOX_COMMON_SIZE + 51 + (uint64_t)text->font_name_length; - CHECK_LARGESIZE( text ); - return text->size; -} - -static uint64_t isom_update_ftab_size( isom_ftab_t *ftab ) -{ - if( !ftab || !ftab->list ) - return 0; - ftab->size = ISOM_BASEBOX_COMMON_SIZE + 2; - for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next ) - { - isom_font_record_t *data = (isom_font_record_t *)entry->data; - ftab->size += 3 + data->font_name_length; - } - CHECK_LARGESIZE( ftab ); - return ftab->size; -} - -static uint64_t isom_update_tx3g_entry_size( isom_sample_entry_t *description ) -{ - if( !description ) - return 0; - isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)description; - tx3g->size = ISOM_BASEBOX_COMMON_SIZE + 38; - CHECK_LARGESIZE( tx3g ); - return tx3g->size; -} - -static uint64_t isom_update_stsd_size( isom_stsd_t *stsd ) -{ - if( !stsd ) - return 0; - stsd->size = ISOM_LIST_FULLBOX_COMMON_SIZE; - CHECK_LARGESIZE( stsd ); - return stsd->size; -} - -static uint64_t isom_update_stts_size( isom_stts_t *stts ) -{ - if( !stts - || !stts->list ) - return 0; - stts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stts->list->entry_count * 8; - CHECK_LARGESIZE( stts ); - return stts->size; -} - -static uint64_t isom_update_ctts_size( isom_ctts_t *ctts ) -{ - if( !ctts - || !ctts->list ) - return 0; - ctts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)ctts->list->entry_count * 8; - CHECK_LARGESIZE( ctts ); - return ctts->size; -} - -static uint64_t isom_update_cslg_size( isom_cslg_t *cslg ) -{ - if( !cslg ) - return 0; - cslg->size = ISOM_FULLBOX_COMMON_SIZE + 20; - CHECK_LARGESIZE( cslg ); - return cslg->size; -} - -static uint64_t isom_update_stsz_size( isom_stsz_t *stsz ) -{ - if( !stsz ) - return 0; - stsz->size = ISOM_FULLBOX_COMMON_SIZE + 8 + ( stsz->list ? (uint64_t)stsz->list->entry_count * 4 : 0 ); - CHECK_LARGESIZE( stsz ); - return stsz->size; -} - -static uint64_t isom_update_stss_size( isom_stss_t *stss ) -{ - if( !stss - || !stss->list ) - return 0; - stss->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stss->list->entry_count * 4; - CHECK_LARGESIZE( stss ); - return stss->size; -} - -static uint64_t isom_update_stps_size( isom_stps_t *stps ) -{ - if( !stps - || !stps->list ) - return 0; - stps->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stps->list->entry_count * 4; - CHECK_LARGESIZE( stps ); - return stps->size; -} - -static uint64_t isom_update_sdtp_size( isom_sdtp_t *sdtp ) -{ - if( !sdtp - || !sdtp->list ) - return 0; - sdtp->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)sdtp->list->entry_count; - CHECK_LARGESIZE( sdtp ); - return sdtp->size; -} - -static uint64_t isom_update_stsc_size( isom_stsc_t *stsc ) -{ - if( !stsc - || !stsc->list ) - return 0; - stsc->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stsc->list->entry_count * 12; - CHECK_LARGESIZE( stsc ); - return stsc->size; -} - -static uint64_t isom_update_stco_size( isom_stco_t *stco ) -{ - if( !stco - || !stco->list ) - return 0; - stco->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stco->list->entry_count * (stco->large_presentation ? 8 : 4); - CHECK_LARGESIZE( stco ); - return stco->size; -} - -static uint64_t isom_update_sbgp_size( isom_sbgp_t *sbgp ) -{ - if( !sbgp - || !sbgp->list ) - return 0; - sbgp->size = ISOM_LIST_FULLBOX_COMMON_SIZE + 4 + (uint64_t)sbgp->list->entry_count * 8; - CHECK_LARGESIZE( sbgp ); - return sbgp->size; -} - -static uint64_t isom_update_sgpd_size( isom_sgpd_t *sgpd ) -{ - if( !sgpd - || !sgpd->list ) - return 0; - uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE + (1 + (sgpd->version == 1)) * 4; - size += (uint64_t)sgpd->list->entry_count * ((sgpd->version == 1) && !sgpd->default_length) * 4; - switch( sgpd->grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - size += sgpd->list->entry_count; - break; - case ISOM_GROUP_TYPE_ROLL : - size += (uint64_t)sgpd->list->entry_count * 2; - break; - default : - /* We don't consider other grouping types currently. */ - break; - } - sgpd->size = size; - CHECK_LARGESIZE( sgpd ); - return sgpd->size; -} - -static uint64_t isom_update_chpl_size( isom_chpl_t *chpl ) -{ - if( !chpl ) - return 0; - chpl->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (chpl->version == 1) + 1; - for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next ) - { - isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data; - chpl->size += 9 + data->chapter_name_length; - } - CHECK_LARGESIZE( chpl ); - return chpl->size; -} - -static uint64_t isom_update_mean_size( isom_mean_t *mean ) -{ - if( !mean ) - return 0; - mean->size = ISOM_FULLBOX_COMMON_SIZE + mean->meaning_string_length; - CHECK_LARGESIZE( mean ); - return mean->size; -} - -static uint64_t isom_update_name_size( isom_name_t *name ) -{ - if( !name ) - return 0; - name->size = ISOM_FULLBOX_COMMON_SIZE + name->name_length; - CHECK_LARGESIZE( name ); - return name->size; -} - -static uint64_t isom_update_data_size( isom_data_t *data ) -{ - if( !data ) - return 0; - data->size = ISOM_BASEBOX_COMMON_SIZE + 8 + data->value_length; - CHECK_LARGESIZE( data ); - return data->size; -} - -static uint64_t isom_update_metaitem_size( isom_metaitem_t *metaitem ) -{ - if( !metaitem ) - return 0; - metaitem->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( metaitem ); - return metaitem->size; -} - -static uint64_t isom_update_keys_size( isom_keys_t *keys ) -{ - if( !keys ) - return 0; - keys->size = ISOM_FULLBOX_COMMON_SIZE + 4; - for( lsmash_entry_t *entry = keys->list->head; entry; entry = entry->next ) - { - isom_keys_entry_t *data = (isom_keys_entry_t *)entry->data; - if( !data ) - continue; - keys->size += data->key_size; - } - CHECK_LARGESIZE( keys ); - return keys->size; -} - -static uint64_t isom_update_ilst_size( isom_ilst_t *ilst ) -{ - if( !ilst ) - return 0; - ilst->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( ilst ); - return ilst->size; -} - -static uint64_t isom_update_meta_size( isom_meta_t *meta ) -{ - if( !meta ) - return 0; - meta->size = ISOM_FULLBOX_COMMON_SIZE; - CHECK_LARGESIZE( meta ); - return meta->size; -} - -static uint64_t isom_update_cprt_size( isom_cprt_t *cprt ) -{ - if( !cprt ) - return 0; - cprt->size = ISOM_FULLBOX_COMMON_SIZE + 2 + cprt->notice_length; - CHECK_LARGESIZE( cprt ); - return cprt->size; -} - -static uint64_t isom_update_udta_size( isom_udta_t *udta ) -{ - if( !udta || !udta->parent ) - return 0; - udta->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( udta ); - return udta->size; -} - -static uint64_t isom_update_WLOC_size( isom_WLOC_t *WLOC ) -{ - if( !WLOC ) - return 0; - WLOC->size = ISOM_BASEBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( WLOC ); - return WLOC->size; -} - -static uint64_t isom_update_LOOP_size( isom_LOOP_t *LOOP ) -{ - if( !LOOP ) - return 0; - LOOP->size = ISOM_BASEBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( LOOP ); - return LOOP->size; -} - -static uint64_t isom_update_SelO_size( isom_SelO_t *SelO ) -{ - if( !SelO ) - return 0; - SelO->size = ISOM_BASEBOX_COMMON_SIZE + 1; - CHECK_LARGESIZE( SelO ); - return SelO->size; -} - -static uint64_t isom_update_AllF_size( isom_AllF_t *AllF ) -{ - if( !AllF ) - return 0; - AllF->size = ISOM_BASEBOX_COMMON_SIZE + 1; - CHECK_LARGESIZE( AllF ); - return AllF->size; -} - -static uint64_t isom_update_mvex_size( isom_mvex_t *mvex ) -{ - if( !mvex ) - return 0; - mvex->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( mvex ); - return mvex->size; -} - -static uint64_t isom_update_mehd_size( isom_mehd_t *mehd ) -{ - if( !mehd ) - return 0; - if( mehd->manager & LSMASH_PLACEHOLDER ) - mehd->size = ISOM_BASEBOX_COMMON_SIZE + 12; - else - { - if( mehd->fragment_duration > UINT32_MAX ) - mehd->version = 1; - mehd->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (1 + (mehd->version == 1)); - } - CHECK_LARGESIZE( mehd ); - return mehd->size; -} - -static uint64_t isom_update_trex_size( isom_trex_t *trex ) -{ - if( !trex ) - return 0; - trex->size = ISOM_FULLBOX_COMMON_SIZE + 20; - CHECK_LARGESIZE( trex ); - return trex->size; -} - -static uint64_t isom_update_moof_size( isom_moof_t *moof ) -{ - if( !moof ) - return 0; - moof->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( moof ); - return moof->size; -} - -static uint64_t isom_update_mfhd_size( isom_mfhd_t *mfhd ) -{ - if( !mfhd ) - return 0; - mfhd->size = ISOM_FULLBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( mfhd ); - return mfhd->size; -} - -static uint64_t isom_update_traf_size( isom_traf_t *traf ) -{ - if( !traf ) - return 0; - traf->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( traf ); - return traf->size; -} - -static uint64_t isom_update_tfhd_size( isom_tfhd_t *tfhd ) -{ - if( !tfhd ) - return 0; - tfhd->size = ISOM_FULLBOX_COMMON_SIZE - + 4 - + 8 * !!( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) - + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) - + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) - + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) - + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ); - CHECK_LARGESIZE( tfhd ); - return tfhd->size; -} - -static uint64_t isom_update_tfdt_size( isom_tfdt_t *tfdt ) -{ - if( !tfdt ) - return 0; - tfdt->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (1 + (tfdt->version == 1)); - CHECK_LARGESIZE( tfdt ); - return tfdt->size; -} - -static uint64_t isom_update_trun_size( isom_trun_t *trun ) -{ - if( !trun ) - return 0; - trun->size = ISOM_FULLBOX_COMMON_SIZE - + 4 - + 4 * !!( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) - + 4 * !!( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ); - uint64_t row_size = 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) - + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) - + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) - + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ); - trun->size += row_size * trun->sample_count; - CHECK_LARGESIZE( trun ); - return trun->size; -} - -static uint64_t isom_update_mfra_size( isom_mfra_t *mfra ) -{ - if( !mfra ) - return 0; - mfra->size = ISOM_BASEBOX_COMMON_SIZE; - CHECK_LARGESIZE( mfra ); - if( mfra->mfro ) - mfra->mfro->length = mfra->size; - return mfra->size; -} - -static uint64_t isom_update_tfra_size( isom_tfra_t *tfra ) -{ - if( !tfra ) - return 0; - tfra->size = ISOM_FULLBOX_COMMON_SIZE + 12; - uint32_t entry_size = 8 * (1 + (tfra->version == 1)) - + tfra->length_size_of_traf_num + 1 - + tfra->length_size_of_trun_num + 1 - + tfra->length_size_of_sample_num + 1; - tfra->size += entry_size * tfra->number_of_entry; - CHECK_LARGESIZE( tfra ); - return tfra->size; -} - -static uint64_t isom_update_mfro_size( isom_mfro_t *mfro ) -{ - if( !mfro ) - return 0; - mfro->size = ISOM_FULLBOX_COMMON_SIZE + 4; - CHECK_LARGESIZE( mfro ); - return mfro->size; -} - -static uint64_t isom_update_mdat_size( isom_mdat_t *mdat ) -{ - if( !mdat ) - return 0; - /* Do nothing */ - //mdat->size = ISOM_BASEBOX_COMMON_SIZE; - //CHECK_LARGESIZE( mdat ); - return mdat->size; -} - -static uint64_t isom_update_skip_size( isom_skip_t *skip ) -{ - if( !skip ) - return 0; - skip->size = ISOM_BASEBOX_COMMON_SIZE + (skip->data ? skip->length : 0ULL); - CHECK_LARGESIZE( skip ); - return skip->size; -} - -static uint64_t isom_update_styp_size( isom_styp_t *styp ) -{ - if( !styp ) - return 0; - styp->size = ISOM_BASEBOX_COMMON_SIZE + 8 + styp->brand_count * 4; - CHECK_LARGESIZE( styp ); - return styp->size; -} - -static uint64_t isom_update_sidx_size( isom_sidx_t *sidx ) -{ - if( !sidx ) - return 0; - sidx->size = ISOM_FULLBOX_COMMON_SIZE - + 12 + (sidx->version == 0 ? 8 : 16) - + 12 * sidx->reference_count; - CHECK_LARGESIZE( sidx ); - return sidx->size; -} - -static uint64_t isom_update_extension_boxes( void *box ) -{ - assert( box ); - uint64_t size = 0; - lsmash_entry_list_t *extensions = &((isom_box_t *)box)->extensions; - for( lsmash_entry_t *entry = extensions->head; entry; entry = entry->next ) - { - isom_box_t *ext = (isom_box_t *)entry->data; - if( !ext ) - continue; - if( ext->update ) - size += isom_update_box_size( ext ); - else if( ext->manager & LSMASH_BINARY_CODED_BOX ) - size += ext->size; - else if( ext->manager & LSMASH_UNKNOWN_BOX ) - size += isom_update_unknown_box_size( (isom_unknown_box_t *)ext ); - } - return size; -} - -/* box adding functions */ -#define isom_create_box_base( box_name, parent, box_type, precedence, ret ) \ - assert( parent ); \ - isom_##box_name##_t *box_name = lsmash_malloc_zero( sizeof(isom_##box_name##_t) ); \ - if( !box_name ) \ - return ret; \ - isom_init_box_common( box_name, parent, box_type, precedence, \ - isom_remove_##box_name, isom_update_##box_name##_size ); \ - if( isom_add_box_to_extension_list( parent, box_name ) ) \ - { \ - lsmash_free( box_name ); \ - return ret; \ - } - -#define isom_create_list_box_base( box_name, parent, box_type, precedence, ret ) \ - isom_create_box_base( box_name, parent, box_type, precedence, ret ); \ - box_name->list = lsmash_create_entry_list(); \ - if( !box_name->list ) \ - { \ - lsmash_remove_entry_tail( &(parent)->extensions, isom_remove_##box_name ); \ - return ret; \ - } - -#define isom_create_box( box_name, parent, box_type, precedence ) \ - isom_create_box_base( box_name, parent, box_type, precedence, -1 ); - -#define isom_create_box_pointer( box_name, parent, box_type, precedence ) \ - isom_create_box_base( box_name, parent, box_type, precedence, NULL ); - -#define isom_create_list_box( box_name, parent, box_type, precedence ) \ - isom_create_list_box_base( box_name, parent, box_type, precedence, -1 ); - -#define isom_create_list_box_null( box_name, parent, box_type, precedence ) \ - isom_create_list_box_base( box_name, parent, box_type, precedence, NULL ); - -#define isom_add_box_template( box_name, parent_name, box_type, precedence, create_func ) \ - if( !parent_name ) \ - return -1; \ - create_func( box_name, parent_name, box_type, precedence ); \ - if( !parent_name->box_name ) \ - parent_name->box_name = box_name - -#define isom_add_box( box_name, parent, box_type, precedence ) \ - isom_add_box_template( box_name, parent, box_type, precedence, isom_create_box ) -#define isom_add_list_box( box_name, parent, box_type, precedence ) \ - isom_add_box_template( box_name, parent, box_type, precedence, isom_create_list_box ) - -lsmash_file_t *isom_add_file( lsmash_root_t *root ) -{ - lsmash_file_t *file = lsmash_malloc_zero( sizeof(lsmash_file_t) ); - if( !file ) - return NULL; - file->class = &lsmash_box_class; - file->root = root; - file->file = file; - file->parent = (isom_box_t *)root; - file->destruct = (isom_extension_destructor_t)isom_remove_file; - file->size = 0; - file->type = LSMASH_BOX_TYPE_UNSPECIFIED; - if( isom_add_box_to_extension_list( root, file ) < 0 ) - { - lsmash_free( file ); - return NULL; - } - if( lsmash_add_entry( &root->file_list, file ) < 0 ) - { - lsmash_remove_entry_tail( &root->extensions, isom_remove_file ); - return NULL; - } - return file; -} - -isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type ) -{ - if( !tref ) - return NULL; - isom_tref_type_t *ref = lsmash_malloc_zero( sizeof(isom_tref_type_t) ); - if( !ref ) - return NULL; - /* Initialize common fields. */ - ref->root = tref->root; - ref->file = tref->file; - ref->parent = (isom_box_t *)tref; - ref->size = 0; - ref->type = lsmash_form_iso_box_type( type ); - ref->precedence = LSMASH_BOX_PRECEDENCE_ISOM_TREF_TYPE; - ref->destruct = (isom_extension_destructor_t)isom_remove_track_reference_type; - ref->update = (isom_extension_updater_t)isom_update_track_reference_type_size; - isom_set_box_writer( (isom_box_t *)ref ); - if( isom_add_box_to_extension_list( tref, ref ) ) - { - lsmash_free( ref ); - return NULL; - } - if( lsmash_add_entry( &tref->ref_list, ref ) ) - { - lsmash_remove_entry_tail( &tref->extensions, isom_remove_track_reference_type ); - return NULL; - } - return ref; -} - -int isom_add_frma( isom_wave_t *wave ) -{ - isom_add_box( frma, wave, QT_BOX_TYPE_FRMA, LSMASH_BOX_PRECEDENCE_QTFF_FRMA ); - return 0; -} - -int isom_add_enda( isom_wave_t *wave ) -{ - isom_add_box( enda, wave, QT_BOX_TYPE_ENDA, LSMASH_BOX_PRECEDENCE_QTFF_ENDA ); - return 0; -} - -int isom_add_mp4a( isom_wave_t *wave ) -{ - isom_add_box( mp4a, wave, QT_BOX_TYPE_MP4A, LSMASH_BOX_PRECEDENCE_QTFF_MP4A ); - return 0; -} - -int isom_add_terminator( isom_wave_t *wave ) -{ - isom_add_box( terminator, wave, QT_BOX_TYPE_TERMINATOR, LSMASH_BOX_PRECEDENCE_QTFF_TERMINATOR ); - return 0; -} - -int isom_add_ftab( isom_tx3g_entry_t *tx3g ) -{ - isom_add_list_box( ftab, tx3g, ISOM_BOX_TYPE_FTAB, LSMASH_BOX_PRECEDENCE_ISOM_FTAB ); - return 0; -} - -int isom_add_stco( isom_stbl_t *stbl ) -{ - isom_add_list_box( stco, stbl, ISOM_BOX_TYPE_STCO, LSMASH_BOX_PRECEDENCE_ISOM_STCO ); - stco->large_presentation = 0; - return 0; -} - -int isom_add_co64( isom_stbl_t *stbl ) -{ - isom_add_list_box( stco, stbl, ISOM_BOX_TYPE_CO64, LSMASH_BOX_PRECEDENCE_ISOM_CO64 ); - stco->large_presentation = 1; - return 0; -} - -int isom_add_ftyp( lsmash_file_t *file ) -{ - isom_add_box( ftyp, file, ISOM_BOX_TYPE_FTYP, LSMASH_BOX_PRECEDENCE_ISOM_FTYP ); - return 0; -} - -int isom_add_moov( lsmash_file_t *file ) -{ - isom_add_box( moov, file, ISOM_BOX_TYPE_MOOV, LSMASH_BOX_PRECEDENCE_ISOM_MOOV ); - return 0; -} - -int isom_add_mvhd( isom_moov_t *moov ) -{ - isom_add_box( mvhd, moov, ISOM_BOX_TYPE_MVHD, LSMASH_BOX_PRECEDENCE_ISOM_MVHD ); - return 0; -} - -int isom_add_iods( isom_moov_t *moov ) -{ - isom_add_box( iods, moov, ISOM_BOX_TYPE_IODS, LSMASH_BOX_PRECEDENCE_ISOM_IODS ); - return 0; -} - -int isom_add_ctab( void *parent_box ) -{ - /* According to QuickTime File Format Specification, this box is placed inside Movie Box if present. - * However, sometimes this box occurs inside an image description entry or the end of Sample Description Box. */ - if( !parent_box ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - isom_create_box( ctab, parent, QT_BOX_TYPE_CTAB, LSMASH_BOX_PRECEDENCE_QTFF_CTAB ); - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) ) - { - isom_moov_t *moov = (isom_moov_t *)parent; - if( !moov->ctab ) - moov->ctab = ctab; - } - return 0; -} - -isom_trak_t *isom_add_trak( isom_moov_t *moov ) -{ - if( !moov || !moov->file ) - return NULL; - isom_create_box_pointer( trak, moov, ISOM_BOX_TYPE_TRAK, LSMASH_BOX_PRECEDENCE_ISOM_TRAK ); - isom_cache_t *cache = lsmash_malloc_zero( sizeof(isom_cache_t) ); - if( !cache ) - goto fail; - isom_fragment_t *fragment = NULL; - if( moov->file->fragment ) - { - fragment = lsmash_malloc_zero( sizeof(isom_fragment_t) ); - if( !fragment ) - goto fail; - cache->fragment = fragment; - } - if( lsmash_add_entry( &moov->trak_list, trak ) ) - goto fail; - trak->cache = cache; - return trak; -fail: - lsmash_free( fragment ); - lsmash_free( cache ); - lsmash_remove_entry_tail( &moov->extensions, isom_remove_trak ); - return NULL; -} - -int isom_add_tkhd( isom_trak_t *trak ) -{ - isom_add_box( tkhd, trak, ISOM_BOX_TYPE_TKHD, LSMASH_BOX_PRECEDENCE_ISOM_TKHD ); - return 0; -} - -int isom_add_tapt( isom_trak_t *trak ) -{ - isom_add_box( tapt, trak, QT_BOX_TYPE_TAPT, LSMASH_BOX_PRECEDENCE_QTFF_TAPT ); - return 0; -} - -int isom_add_clef( isom_tapt_t *tapt ) -{ - isom_add_box( clef, tapt, QT_BOX_TYPE_CLEF, LSMASH_BOX_PRECEDENCE_QTFF_CLEF ); - return 0; -} - -int isom_add_prof( isom_tapt_t *tapt ) -{ - isom_add_box( prof, tapt, QT_BOX_TYPE_PROF, LSMASH_BOX_PRECEDENCE_QTFF_PROF ); - return 0; -} - -int isom_add_enof( isom_tapt_t *tapt ) -{ - isom_add_box( enof, tapt, QT_BOX_TYPE_ENOF, LSMASH_BOX_PRECEDENCE_QTFF_ENOF ); - return 0; -} - -int isom_add_elst( isom_edts_t *edts ) -{ - isom_add_list_box( elst, edts, ISOM_BOX_TYPE_ELST, LSMASH_BOX_PRECEDENCE_ISOM_ELST ); - return 0; -} - -int isom_add_edts( isom_trak_t *trak ) -{ - isom_add_box( edts, trak, ISOM_BOX_TYPE_EDTS, LSMASH_BOX_PRECEDENCE_ISOM_EDTS ); - return 0; -} - -int isom_add_tref( isom_trak_t *trak ) -{ - isom_add_box( tref, trak, ISOM_BOX_TYPE_TREF, LSMASH_BOX_PRECEDENCE_ISOM_TREF ); - return 0; -} - -int isom_add_mdia( isom_trak_t *trak ) -{ - isom_add_box( mdia, trak, ISOM_BOX_TYPE_MDIA, LSMASH_BOX_PRECEDENCE_ISOM_MDIA ); - return 0; -} - - -int isom_add_mdhd( isom_mdia_t *mdia ) -{ - isom_add_box( mdhd, mdia, ISOM_BOX_TYPE_MDHD, LSMASH_BOX_PRECEDENCE_ISOM_MDHD ); - return 0; -} - -int isom_add_hdlr( void *parent_box ) -{ - if( !parent_box ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - isom_create_box( hdlr, parent, ISOM_BOX_TYPE_HDLR, LSMASH_BOX_PRECEDENCE_ISOM_HDLR ); - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ) - { - isom_mdia_t *mdia = (isom_mdia_t *)parent; - if( !mdia->hdlr ) - mdia->hdlr = hdlr; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) - || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ) - { - isom_meta_t *meta = (isom_meta_t *)parent; - if( !meta->hdlr ) - meta->hdlr = hdlr; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) ) - { - isom_minf_t *minf = (isom_minf_t *)parent; - if( !minf->hdlr ) - minf->hdlr = hdlr; - } - else - assert( 0 ); - return 0; -} - -int isom_add_minf( isom_mdia_t *mdia ) -{ - isom_add_box( minf, mdia, ISOM_BOX_TYPE_MINF, LSMASH_BOX_PRECEDENCE_ISOM_MINF ); - return 0; -} - -int isom_add_vmhd( isom_minf_t *minf ) -{ - isom_add_box( vmhd, minf, ISOM_BOX_TYPE_VMHD, LSMASH_BOX_PRECEDENCE_ISOM_VMHD ); - return 0; -} - -int isom_add_smhd( isom_minf_t *minf ) -{ - isom_add_box( smhd, minf, ISOM_BOX_TYPE_SMHD, LSMASH_BOX_PRECEDENCE_ISOM_SMHD ); - return 0; -} - -int isom_add_hmhd( isom_minf_t *minf ) -{ - isom_add_box( hmhd, minf, ISOM_BOX_TYPE_HMHD, LSMASH_BOX_PRECEDENCE_ISOM_HMHD ); - return 0; -} - -int isom_add_nmhd( isom_minf_t *minf ) -{ - isom_add_box( nmhd, minf, ISOM_BOX_TYPE_NMHD, LSMASH_BOX_PRECEDENCE_ISOM_NMHD ); - return 0; -} - -int isom_add_gmhd( isom_minf_t *minf ) -{ - isom_add_box( gmhd, minf, QT_BOX_TYPE_GMHD, LSMASH_BOX_PRECEDENCE_QTFF_GMHD ); - return 0; -} - -int isom_add_gmin( isom_gmhd_t *gmhd ) -{ - isom_add_box( gmin, gmhd, QT_BOX_TYPE_GMIN, LSMASH_BOX_PRECEDENCE_QTFF_GMIN ); - return 0; -} - -int isom_add_text( isom_gmhd_t *gmhd ) -{ - isom_add_box( text, gmhd, QT_BOX_TYPE_TEXT, LSMASH_BOX_PRECEDENCE_QTFF_TEXT ); - return 0; -} - -int isom_add_dinf( void *parent_box ) -{ - if( !parent_box ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - isom_create_box( dinf, parent, ISOM_BOX_TYPE_DINF, LSMASH_BOX_PRECEDENCE_ISOM_DINF ); - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) ) - { - isom_minf_t *minf = (isom_minf_t *)parent; - if( !minf->dinf ) - minf->dinf = dinf; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) - || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ) - { - isom_meta_t *meta = (isom_meta_t *)parent; - if( !meta->dinf ) - meta->dinf = dinf; - } - else - assert( 0 ); - return 0; -} - -isom_dref_entry_t *isom_add_dref_entry( isom_dref_t *dref ) -{ - if( !dref ) - return NULL; - isom_dref_entry_t *data = lsmash_malloc_zero( sizeof(isom_dref_entry_t) ); - if( !data ) - return NULL; - isom_init_box_common( data, dref, ISOM_BOX_TYPE_URL, LSMASH_BOX_PRECEDENCE_ISOM_URL, - isom_remove_dref_entry, isom_update_dref_entry_size ); - if( isom_add_box_to_extension_list( dref, data ) ) - { - lsmash_free( data ); - return NULL; - } - if( lsmash_add_entry( &dref->list, data ) ) - { - lsmash_remove_entry_tail( &dref->extensions, isom_remove_dref_entry ); - return NULL; - } - return data; -} - -int isom_add_dref( isom_dinf_t *dinf ) -{ - isom_add_box( dref, dinf, ISOM_BOX_TYPE_DREF, LSMASH_BOX_PRECEDENCE_ISOM_DREF ); - return 0; -} - -int isom_add_stbl( isom_minf_t *minf ) -{ - isom_add_box( stbl, minf, ISOM_BOX_TYPE_STBL, LSMASH_BOX_PRECEDENCE_ISOM_STBL ); - return 0; -} - -int isom_add_stsd( isom_stbl_t *stbl ) -{ - isom_add_box( stsd, stbl, ISOM_BOX_TYPE_STSD, LSMASH_BOX_PRECEDENCE_ISOM_STSD ); - return 0; -} - -static int isom_add_sample_description_entry( isom_stsd_t *stsd, void *description, void *destructor ) -{ - if( isom_add_box_to_extension_list( stsd, description ) ) - { - lsmash_free( description ); - return -1; - } - if( lsmash_add_entry( &stsd->list, description ) ) - { - lsmash_remove_entry_tail( &stsd->extensions, destructor ); - return -1; - } - return 0; -} - -isom_visual_entry_t *isom_add_visual_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ) -{ - assert( stsd ); - isom_visual_entry_t *visual = lsmash_malloc_zero( sizeof(isom_visual_entry_t) ); - if( !visual ) - return NULL; - isom_init_box_common( visual, stsd, sample_type, LSMASH_BOX_PRECEDENCE_HM, - isom_remove_visual_description, isom_update_visual_entry_size ); - visual->manager |= LSMASH_VIDEO_DESCRIPTION; - return isom_add_sample_description_entry( stsd, visual, isom_remove_visual_description ) ? NULL : visual; -} - -isom_audio_entry_t *isom_add_audio_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ) -{ - assert( stsd ); - isom_audio_entry_t *audio = lsmash_malloc_zero( sizeof(isom_audio_entry_t) ); - if( !audio ) - return NULL; - isom_init_box_common( audio, stsd, sample_type, LSMASH_BOX_PRECEDENCE_HM, - isom_remove_audio_description, isom_update_audio_entry_size ); - audio->manager |= LSMASH_AUDIO_DESCRIPTION; - return isom_add_sample_description_entry( stsd, audio, isom_remove_audio_description ) ? NULL : audio; -} - -isom_qt_text_entry_t *isom_add_qt_text_description( isom_stsd_t *stsd ) -{ - assert( stsd ); - isom_qt_text_entry_t *text = lsmash_malloc_zero( sizeof(isom_qt_text_entry_t) ); - if( !text ) - return NULL; - isom_init_box_common( text, stsd, QT_CODEC_TYPE_TEXT_TEXT, LSMASH_BOX_PRECEDENCE_HM, - isom_remove_qt_text_description, isom_update_qt_text_entry_size ); - return isom_add_sample_description_entry( stsd, text, isom_remove_qt_text_description ) ? NULL : text; -} - -isom_tx3g_entry_t *isom_add_tx3g_description( isom_stsd_t *stsd ) -{ - assert( stsd ); - isom_tx3g_entry_t *tx3g = lsmash_malloc_zero( sizeof(isom_tx3g_entry_t) ); - if( !tx3g ) - return NULL; - isom_init_box_common( tx3g, stsd, ISOM_CODEC_TYPE_TX3G_TEXT, LSMASH_BOX_PRECEDENCE_HM, - isom_remove_tx3g_description, isom_update_tx3g_entry_size ); - return isom_add_sample_description_entry( stsd, tx3g, isom_remove_tx3g_description ) ? NULL : tx3g; -} - -isom_esds_t *isom_add_esds( void *parent_box ) -{ - isom_box_t *parent = (isom_box_t *)parent_box; - int is_qt = lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ); - lsmash_box_type_t box_type = is_qt ? QT_BOX_TYPE_ESDS : ISOM_BOX_TYPE_ESDS; - uint64_t precedence = is_qt ? LSMASH_BOX_PRECEDENCE_QTFF_ESDS : LSMASH_BOX_PRECEDENCE_ISOM_ESDS; - isom_create_box_pointer( esds, parent, box_type, precedence ); - return esds; -} - -isom_glbl_t *isom_add_glbl( void *parent_box ) -{ - isom_create_box_pointer( glbl, (isom_box_t *)parent_box, QT_BOX_TYPE_GLBL, LSMASH_BOX_PRECEDENCE_QTFF_GLBL ); - return glbl; -} - -isom_clap_t *isom_add_clap( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( clap, visual, ISOM_BOX_TYPE_CLAP, LSMASH_BOX_PRECEDENCE_ISOM_CLAP ); - return clap; -} - -isom_pasp_t *isom_add_pasp( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( pasp, visual, ISOM_BOX_TYPE_PASP, LSMASH_BOX_PRECEDENCE_ISOM_PASP ); - return pasp; -} - -isom_colr_t *isom_add_colr( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( colr, visual, ISOM_BOX_TYPE_COLR, LSMASH_BOX_PRECEDENCE_ISOM_COLR ); - return colr; -} - -isom_gama_t *isom_add_gama( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( gama, visual, QT_BOX_TYPE_GAMA, LSMASH_BOX_PRECEDENCE_QTFF_GAMA ); - return gama; -} - -isom_fiel_t *isom_add_fiel( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( fiel, visual, QT_BOX_TYPE_FIEL, LSMASH_BOX_PRECEDENCE_QTFF_FIEL ); - return fiel; -} - -isom_cspc_t *isom_add_cspc( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( cspc, visual, QT_BOX_TYPE_CSPC, LSMASH_BOX_PRECEDENCE_QTFF_CSPC ); - return cspc; -} - -isom_sgbt_t *isom_add_sgbt( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( sgbt, visual, QT_BOX_TYPE_SGBT, LSMASH_BOX_PRECEDENCE_QTFF_SGBT ); - return sgbt; -} - -isom_stsl_t *isom_add_stsl( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( stsl, visual, ISOM_BOX_TYPE_STSL, LSMASH_BOX_PRECEDENCE_ISOM_STSL ); - return stsl; -} - -isom_btrt_t *isom_add_btrt( isom_visual_entry_t *visual ) -{ - isom_create_box_pointer( btrt, visual, ISOM_BOX_TYPE_BTRT, LSMASH_BOX_PRECEDENCE_ISOM_BTRT ); - return btrt; -} - -isom_wave_t *isom_add_wave( isom_audio_entry_t *audio ) -{ - isom_create_box_pointer( wave, audio, QT_BOX_TYPE_WAVE, LSMASH_BOX_PRECEDENCE_QTFF_WAVE ); - return wave; -} - -isom_chan_t *isom_add_chan( isom_audio_entry_t *audio ) -{ - isom_create_box_pointer( chan, audio, QT_BOX_TYPE_CHAN, LSMASH_BOX_PRECEDENCE_QTFF_CHAN ); - return chan; -} - -isom_srat_t *isom_add_srat( isom_audio_entry_t *audio ) -{ - isom_create_box_pointer( srat, audio, ISOM_BOX_TYPE_SRAT, LSMASH_BOX_PRECEDENCE_ISOM_SRAT ); - return srat; -} - -int isom_add_stts( isom_stbl_t *stbl ) -{ - isom_add_list_box( stts, stbl, ISOM_BOX_TYPE_STTS, LSMASH_BOX_PRECEDENCE_ISOM_STTS ); - return 0; -} - -int isom_add_ctts( isom_stbl_t *stbl ) -{ - isom_add_list_box( ctts, stbl, ISOM_BOX_TYPE_CTTS, LSMASH_BOX_PRECEDENCE_ISOM_CTTS ); - return 0; -} - -int isom_add_cslg( isom_stbl_t *stbl ) -{ - isom_add_box( cslg, stbl, ISOM_BOX_TYPE_CSLG, LSMASH_BOX_PRECEDENCE_ISOM_CSLG ); - return 0; -} - -int isom_add_stsc( isom_stbl_t *stbl ) -{ - isom_add_list_box( stsc, stbl, ISOM_BOX_TYPE_STSC, LSMASH_BOX_PRECEDENCE_ISOM_STSC ); - return 0; -} - -int isom_add_stsz( isom_stbl_t *stbl ) -{ - /* We don't create a list here. */ - isom_add_box( stsz, stbl, ISOM_BOX_TYPE_STSZ, LSMASH_BOX_PRECEDENCE_ISOM_STSZ ); - return 0; -} - -int isom_add_stss( isom_stbl_t *stbl ) -{ - isom_add_list_box( stss, stbl, ISOM_BOX_TYPE_STSS, LSMASH_BOX_PRECEDENCE_ISOM_STSS ); - return 0; -} - -int isom_add_stps( isom_stbl_t *stbl ) -{ - isom_add_list_box( stps, stbl, QT_BOX_TYPE_STPS, LSMASH_BOX_PRECEDENCE_QTFF_STPS ); - return 0; -} - -int isom_add_sdtp( isom_box_t *parent ) -{ - if( !parent ) - return -1; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) - { - isom_stbl_t *stbl = (isom_stbl_t *)parent; - isom_add_list_box( sdtp, stbl, ISOM_BOX_TYPE_SDTP, LSMASH_BOX_PRECEDENCE_ISOM_SDTP ); - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - { - isom_traf_t *traf = (isom_traf_t *)parent; - isom_add_list_box( sdtp, traf, ISOM_BOX_TYPE_SDTP, LSMASH_BOX_PRECEDENCE_ISOM_SDTP ); - } - else - assert( 0 ); - return 0; -} - -isom_sgpd_t *isom_add_sgpd( void *parent_box ) -{ - if( !parent_box ) - return NULL; - isom_box_t *parent = (isom_box_t *)parent_box; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) - { - isom_stbl_t *stbl = (isom_stbl_t *)parent; - isom_create_list_box_null( sgpd, stbl, ISOM_BOX_TYPE_SGPD, LSMASH_BOX_PRECEDENCE_ISOM_SGPD ); - if( lsmash_add_entry( &stbl->sgpd_list, sgpd ) ) - { - lsmash_remove_entry_tail( &stbl->extensions, isom_remove_sgpd ); - return NULL; - } - return sgpd; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - { - isom_traf_t *traf = (isom_traf_t *)parent; - isom_create_list_box_null( sgpd, traf, ISOM_BOX_TYPE_SGPD, LSMASH_BOX_PRECEDENCE_ISOM_SGPD ); - if( lsmash_add_entry( &traf->sgpd_list, sgpd ) ) - { - lsmash_remove_entry_tail( &traf->extensions, isom_remove_sgpd ); - return NULL; - } - return sgpd; - } - assert( 0 ); - return NULL; -} - -isom_sbgp_t *isom_add_sbgp( void *parent_box ) -{ - if( !parent_box ) - return NULL; - isom_box_t *parent = (isom_box_t *)parent_box; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) - { - isom_stbl_t *stbl = (isom_stbl_t *)parent; - isom_create_list_box_null( sbgp, stbl, ISOM_BOX_TYPE_SBGP, LSMASH_BOX_PRECEDENCE_ISOM_SBGP ); - if( lsmash_add_entry( &stbl->sbgp_list, sbgp ) ) - { - lsmash_remove_entry_tail( &stbl->extensions, isom_remove_sbgp ); - return NULL; - } - return sbgp; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - { - isom_traf_t *traf = (isom_traf_t *)parent; - isom_create_list_box_null( sbgp, traf, ISOM_BOX_TYPE_SBGP, LSMASH_BOX_PRECEDENCE_ISOM_SBGP ); - if( lsmash_add_entry( &traf->sbgp_list, sbgp ) ) - { - lsmash_remove_entry_tail( &traf->extensions, isom_remove_sbgp ); - return NULL; - } - return sbgp; - } - assert( 0 ); - return NULL; -} - -int isom_add_chpl( isom_udta_t *udta ) -{ - isom_add_list_box( chpl, udta, ISOM_BOX_TYPE_CHPL, LSMASH_BOX_PRECEDENCE_ISOM_CHPL ); - return 0; -} - -int isom_add_metaitem( isom_ilst_t *ilst, lsmash_itunes_metadata_item item ) -{ - if( !ilst ) - return -1; - lsmash_box_type_t type = lsmash_form_iso_box_type( item ); - isom_create_box( metaitem, ilst, type, LSMASH_BOX_PRECEDENCE_ISOM_METAITEM ); - if( lsmash_add_entry( &ilst->item_list, metaitem ) ) - { - lsmash_remove_entry_tail( &ilst->extensions, isom_remove_metaitem ); - return -1; - } - return 0; -} - -int isom_add_mean( isom_metaitem_t *metaitem ) -{ - isom_add_box( mean, metaitem, ISOM_BOX_TYPE_MEAN, LSMASH_BOX_PRECEDENCE_ISOM_MEAN ); - return 0; -} - -int isom_add_name( isom_metaitem_t *metaitem ) -{ - isom_add_box( name, metaitem, ISOM_BOX_TYPE_NAME, LSMASH_BOX_PRECEDENCE_ISOM_NAME ); - return 0; -} - -int isom_add_data( isom_metaitem_t *metaitem ) -{ - isom_add_box( data, metaitem, ISOM_BOX_TYPE_DATA, LSMASH_BOX_PRECEDENCE_ISOM_DATA ); - return 0; -} - -int isom_add_ilst( isom_meta_t *meta ) -{ - isom_add_box( ilst, meta, ISOM_BOX_TYPE_ILST, LSMASH_BOX_PRECEDENCE_ISOM_ILST ); - return 0; -} - -int isom_add_keys( isom_meta_t *meta ) -{ - isom_add_list_box( keys, meta, QT_BOX_TYPE_KEYS, LSMASH_BOX_PRECEDENCE_QTFF_KEYS ); - return 0; -} - -int isom_add_meta( void *parent_box ) -{ - if( !parent_box ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - isom_create_box( meta, parent, ISOM_BOX_TYPE_META, LSMASH_BOX_PRECEDENCE_ISOM_META ); - if( parent->file == (lsmash_file_t *)parent ) - { - lsmash_file_t *file = (lsmash_file_t *)parent; - if( !file->meta ) - file->meta = meta; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) ) - { - isom_moov_t *moov = (isom_moov_t *)parent; - if( !moov->meta ) - moov->meta = meta; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) - { - isom_trak_t *trak = (isom_trak_t *)parent; - if( !trak->meta ) - trak->meta = meta; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_UDTA ) ) - { - isom_udta_t *udta = (isom_udta_t *)parent; - if( !udta->meta ) - udta->meta = meta; - } - else - assert( 0 ); - return 0; -} - -int isom_add_cprt( isom_udta_t *udta ) -{ - if( !udta ) - return -1; - isom_create_box( cprt, udta, ISOM_BOX_TYPE_CPRT, LSMASH_BOX_PRECEDENCE_ISOM_CPRT ); - if( lsmash_add_entry( &udta->cprt_list, cprt ) ) - { - lsmash_remove_entry_tail( &udta->extensions, isom_remove_cprt ); - return -1; - } - return 0; -} - -int isom_add_udta( void *parent_box ) -{ - if( !parent_box ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) ) - { - isom_moov_t *moov = (isom_moov_t *)parent; - isom_add_box( udta, moov, ISOM_BOX_TYPE_UDTA, LSMASH_BOX_PRECEDENCE_ISOM_UDTA ); - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) - { - isom_trak_t *trak = (isom_trak_t *)parent; - isom_add_box( udta, trak, ISOM_BOX_TYPE_UDTA, LSMASH_BOX_PRECEDENCE_ISOM_UDTA ); - } - else - assert( 0 ); - return 0; -} - -int isom_add_WLOC( isom_udta_t *udta ) -{ - isom_add_box( WLOC, udta, QT_BOX_TYPE_WLOC, LSMASH_BOX_PRECEDENCE_QTFF_WLOC ); - return 0; -} - -int isom_add_LOOP( isom_udta_t *udta ) -{ - isom_add_box( LOOP, udta, QT_BOX_TYPE_LOOP, LSMASH_BOX_PRECEDENCE_QTFF_LOOP ); - return 0; -} - -int isom_add_SelO( isom_udta_t *udta ) -{ - isom_add_box( SelO, udta, QT_BOX_TYPE_SELO, LSMASH_BOX_PRECEDENCE_QTFF_SELO ); - return 0; -} - -int isom_add_AllF( isom_udta_t *udta ) -{ - isom_add_box( AllF, udta, QT_BOX_TYPE_ALLF, LSMASH_BOX_PRECEDENCE_QTFF_ALLF ); - return 0; -} - -int isom_add_mvex( isom_moov_t *moov ) -{ - isom_add_box( mvex, moov, ISOM_BOX_TYPE_MVEX, LSMASH_BOX_PRECEDENCE_ISOM_MVEX ); - return 0; -} - -int isom_add_mehd( isom_mvex_t *mvex ) -{ - isom_add_box( mehd, mvex, ISOM_BOX_TYPE_MEHD, LSMASH_BOX_PRECEDENCE_ISOM_MEHD ); - return 0; -} - -isom_trex_t *isom_add_trex( isom_mvex_t *mvex ) -{ - if( !mvex ) - return NULL; - isom_create_box_pointer( trex, mvex, ISOM_BOX_TYPE_TREX, LSMASH_BOX_PRECEDENCE_ISOM_TREX ); - if( lsmash_add_entry( &mvex->trex_list, trex ) ) - { - lsmash_remove_entry_tail( &mvex->extensions, isom_remove_trex ); - return NULL; - } - return trex; -} - -isom_moof_t *isom_add_moof( lsmash_file_t *file ) -{ - if( !file ) - return NULL; - isom_create_box_pointer( moof, file, ISOM_BOX_TYPE_MOOF, LSMASH_BOX_PRECEDENCE_ISOM_MOOF ); - if( lsmash_add_entry( &file->moof_list, moof ) ) - { - lsmash_remove_entry_tail( &file->extensions, isom_remove_moof ); - return NULL; - } - return moof; -} - -int isom_add_mfhd( isom_moof_t *moof ) -{ - isom_add_box( mfhd, moof, ISOM_BOX_TYPE_MFHD, LSMASH_BOX_PRECEDENCE_ISOM_MFHD ); - return 0; -} - -isom_traf_t *isom_add_traf( isom_moof_t *moof ) -{ - if( !moof ) - return NULL; - isom_create_box_pointer( traf, moof, ISOM_BOX_TYPE_TRAF, LSMASH_BOX_PRECEDENCE_ISOM_TRAF ); - if( lsmash_add_entry( &moof->traf_list, traf ) ) - { - lsmash_remove_entry_tail( &moof->extensions, isom_remove_traf ); - return NULL; - } - isom_cache_t *cache = lsmash_malloc( sizeof(isom_cache_t) ); - if( !cache ) - { - lsmash_remove_entry_tail( &moof->extensions, isom_remove_traf ); - return NULL; - } - memset( cache, 0, sizeof(isom_cache_t) ); - traf->cache = cache; - return traf; -} - -int isom_add_tfhd( isom_traf_t *traf ) -{ - isom_add_box( tfhd, traf, ISOM_BOX_TYPE_TFHD, LSMASH_BOX_PRECEDENCE_ISOM_TFHD ); - return 0; -} - -int isom_add_tfdt( isom_traf_t *traf ) -{ - isom_add_box( tfdt, traf, ISOM_BOX_TYPE_TFDT, LSMASH_BOX_PRECEDENCE_ISOM_TFDT ); - return 0; -} - -isom_trun_t *isom_add_trun( isom_traf_t *traf ) -{ - if( !traf ) - return NULL; - isom_create_box_pointer( trun, traf, ISOM_BOX_TYPE_TRUN, LSMASH_BOX_PRECEDENCE_ISOM_TRUN ); - if( lsmash_add_entry( &traf->trun_list, trun ) ) - { - lsmash_remove_entry_tail( &traf->extensions, isom_remove_trun ); - return NULL; - } - return trun; -} - -int isom_add_mfra( lsmash_file_t *file ) -{ - isom_add_box( mfra, file, ISOM_BOX_TYPE_MFRA, LSMASH_BOX_PRECEDENCE_ISOM_MFRA ); - return 0; -} - -isom_tfra_t *isom_add_tfra( isom_mfra_t *mfra ) -{ - if( !mfra ) - return NULL; - isom_create_box_pointer( tfra, mfra, ISOM_BOX_TYPE_TFRA, LSMASH_BOX_PRECEDENCE_ISOM_TFRA ); - if( lsmash_add_entry( &mfra->tfra_list, tfra ) ) - { - lsmash_remove_entry_tail( &mfra->extensions, isom_remove_tfra ); - return NULL; - } - return tfra; -} - -int isom_add_mfro( isom_mfra_t *mfra ) -{ - isom_add_box( mfro, mfra, ISOM_BOX_TYPE_MFRO, LSMASH_BOX_PRECEDENCE_ISOM_MFRO ); - return 0; -} - -int isom_add_mdat( lsmash_file_t *file ) -{ - assert( !file->mdat ); - isom_create_box( mdat, file, ISOM_BOX_TYPE_MDAT, LSMASH_BOX_PRECEDENCE_ISOM_MDAT ); - file->mdat = mdat; - return 0; -} - -int isom_add_free( void *parent_box ) -{ - if( !parent_box ) - return -1; - isom_box_t *parent = (isom_box_t *)parent_box; - if( parent->file == (lsmash_file_t *)parent ) - { - lsmash_file_t *file = (lsmash_file_t *)parent; - isom_create_box( skip, file, ISOM_BOX_TYPE_FREE, LSMASH_BOX_PRECEDENCE_ISOM_FREE ); - if( !file->free ) - file->free = skip; - return 0; - } - isom_create_box( skip, parent, ISOM_BOX_TYPE_FREE, LSMASH_BOX_PRECEDENCE_ISOM_FREE ); - return 0; -} - -isom_styp_t *isom_add_styp( lsmash_file_t *file ) -{ - isom_create_box_pointer( styp, file, ISOM_BOX_TYPE_STYP, LSMASH_BOX_PRECEDENCE_ISOM_STYP ); - if( lsmash_add_entry( &file->styp_list, styp ) ) - { - lsmash_remove_entry_tail( &file->extensions, isom_remove_styp ); - return NULL; - } - return styp; -} - -isom_sidx_t *isom_add_sidx( lsmash_file_t *file ) -{ - isom_create_list_box_null( sidx, file, ISOM_BOX_TYPE_SIDX, LSMASH_BOX_PRECEDENCE_ISOM_SIDX ); - if( lsmash_add_entry( &file->sidx_list, sidx ) ) - { - lsmash_remove_entry_tail( &file->extensions, isom_remove_sidx ); - return NULL; - } - return sidx; -} - -/* Public functions */ -lsmash_extended_box_type_t lsmash_form_extended_box_type( uint32_t fourcc, const uint8_t id[12] ) -{ - return (lsmash_extended_box_type_t){ fourcc, { id[0], id[1], id[2], id[3], id[4], id[5], - id[6], id[7], id[8], id[9], id[10], id[11] } }; -} - -lsmash_box_type_t lsmash_form_iso_box_type( uint32_t fourcc ) -{ - return (lsmash_box_type_t){ fourcc, lsmash_form_extended_box_type( fourcc, LSMASH_ISO_12_BYTES ) }; -} - -lsmash_box_type_t lsmash_form_qtff_box_type( uint32_t fourcc ) -{ - return (lsmash_box_type_t){ fourcc, lsmash_form_extended_box_type( fourcc, LSMASH_QTFF_12_BYTES ) }; -} - -#define CHECK_BOX_TYPE_IDENTICAL( a, b ) \ - a.fourcc == b.fourcc \ - && a.user.fourcc == b.user.fourcc \ - && a.user.id[0] == b.user.id[0] \ - && a.user.id[1] == b.user.id[1] \ - && a.user.id[2] == b.user.id[2] \ - && a.user.id[3] == b.user.id[3] \ - && a.user.id[4] == b.user.id[4] \ - && a.user.id[5] == b.user.id[5] \ - && a.user.id[6] == b.user.id[6] \ - && a.user.id[7] == b.user.id[7] \ - && a.user.id[8] == b.user.id[8] \ - && a.user.id[9] == b.user.id[9] \ - && a.user.id[10] == b.user.id[10] \ - && a.user.id[11] == b.user.id[11] - -int lsmash_check_box_type_identical( lsmash_box_type_t a, lsmash_box_type_t b ) -{ - return CHECK_BOX_TYPE_IDENTICAL( a, b ); -} - -int lsmash_check_codec_type_identical( lsmash_codec_type_t a, lsmash_codec_type_t b ) -{ - return CHECK_BOX_TYPE_IDENTICAL( a, b ); -} - -int lsmash_check_box_type_specified( const lsmash_box_type_t *box_type ) -{ - assert( box_type ); - if( !box_type ) - return 0; - return !!(box_type->fourcc - | box_type->user.fourcc - | box_type->user.id[0] | box_type->user.id[1] | box_type->user.id[2] | box_type->user.id[3] - | box_type->user.id[4] | box_type->user.id[5] | box_type->user.id[6] | box_type->user.id[7] - | box_type->user.id[8] | box_type->user.id[9] | box_type->user.id[10] | box_type->user.id[11]); -} - -lsmash_box_t *lsmash_get_box -( - lsmash_box_t *parent, - const lsmash_box_path_t box_path[] -) -{ - lsmash_entry_t *entry = isom_get_entry_of_box( parent, box_path ); - return (lsmash_box_t *)(entry ? entry->data : NULL); -} - -lsmash_box_t *lsmash_create_box -( - lsmash_box_type_t type, - uint8_t *data, - uint32_t size, - uint64_t precedence -) -{ - if( !lsmash_check_box_type_specified( &type ) || !data || size < ISOM_BASEBOX_COMMON_SIZE ) - return NULL; - isom_unknown_box_t *box = lsmash_malloc_zero( sizeof(isom_unknown_box_t) ); - if( !box ) - return NULL; - box->unknown_size = size; - box->unknown_field = lsmash_memdup( data, size ); - if( !box->unknown_field ) - { - lsmash_free( box ); - return NULL; - } - box->class = &lsmash_box_class; - box->root = NULL; - box->file = NULL; - box->parent = NULL; - box->destruct = (isom_extension_destructor_t)isom_remove_unknown_box; - box->update = (isom_extension_updater_t)isom_update_unknown_box_size; - box->manager = LSMASH_UNKNOWN_BOX; - box->precedence = precedence; - box->size = ISOM_BASEBOX_COMMON_SIZE + size + (type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ? 16 : 0); - box->type = type; - isom_set_box_writer( (lsmash_box_t *)box ); - return (lsmash_box_t *)box; -} - -int lsmash_add_box -( - lsmash_box_t *parent, - lsmash_box_t *box -) -{ - if( !parent ) - /* You cannot add any box without a box being its parent. */ - return -1; - if( !box || !(box->manager & LSMASH_UNKNOWN_BOX) || box->size < ISOM_BASEBOX_COMMON_SIZE ) - return -1; - if( parent->root == (lsmash_root_t *)parent ) - { - /* Only files can be added into any ROOT. - * For backward compatibility, use the active file as the parent. */ - if( parent->file ) - parent = (isom_box_t *)parent->file; - else - return -1; - } - /* Add a box as a child box. */ - box->root = parent->root; - box->file = parent->file; - box->parent = parent; - return isom_add_box_to_extension_list( parent, box ); -} - -void lsmash_destroy_box -( - lsmash_box_t *box -) -{ - isom_remove_box_by_itself( box ); -} - -void lsmash_destroy_children -( - lsmash_box_t *box -) -{ - if( box ) - isom_remove_all_extension_boxes( &box->extensions ); -} - -int lsmash_get_box_precedence -( - lsmash_box_t *box, - uint64_t *precedence -) -{ - if( !box || !precedence ) - return -1; - *precedence = box->precedence; - return 0; -} - -lsmash_box_t *lsmash_root_as_box -( - lsmash_root_t *root -) -{ - return (lsmash_box_t *)root; -} - -lsmash_box_t *lsmash_file_as_box -( - lsmash_file_t *file -) -{ - return (lsmash_box_t *)file; -} - -int lsmash_write_top_level_box -( - lsmash_box_t *box -) -{ - if( !box || (isom_box_t *)box->file != box->parent ) - return -1; - if( isom_write_box( box->file->bs, box ) < 0 ) - return -1; - box->file->size += box->size; - return 0; -} - -uint8_t *lsmash_export_box -( - lsmash_box_t *box, - uint32_t *size -) -{ - if( !box || !size ) - return NULL; - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return NULL; - if( isom_write_box( bs, box ) < 0 ) - { - lsmash_bs_cleanup( bs ); - return NULL; - } - *size = bs->buffer.store; - uint8_t *data = bs->buffer.data; - bs->buffer.data = NULL; - lsmash_bs_cleanup( bs ); - return data; -} diff -Nru l-smash-1.9.1/boxdumper.c l-smash-2.3.0/boxdumper.c --- l-smash-1.9.1/boxdumper.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/boxdumper.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -/***************************************************************************** - * boxdumper.c: - ***************************************************************************** - * Copyright (C) 2010-2014 L-SMASH project - * - * Authors: Yusuke Nakamura - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - *****************************************************************************/ - -/* This file is available under an ISC license. */ - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#include -#endif - -#include "lsmash.h" -#include "cli.h" - -#include "config.h" - -#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) - -static void display_version( void ) -{ - eprintf( "\n" - "L-SMASH isom/mov structual analyzer rev%s %s\n" - "Built on %s %s\n" - "Copyright (C) 2010-2014 L-SMASH project\n", - LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); -} - -static void display_help( void ) -{ - display_version(); - eprintf( "\n" - "Usage: boxdumper [option] input\n" - " options:\n" - " --help Display help\n" - " --version Display version information\n" - " --box Dump box structure\n" - " --chapter Extract chapter list\n" - " --timestamp Dump media timestamps\n" ); -} - -static int boxdumper_error -( - lsmash_root_t *root, - lsmash_file_parameters_t *file_param, - const char *message -) -{ - lsmash_close_file( file_param ); - lsmash_destroy_root( root ); - eprintf( "%s", message ); - return -1; -} - -#define BOXDUMPER_ERR( message ) boxdumper_error( root, &file_param, message ) -#define DO_NOTHING - -int main( int argc, char *argv[] ) -{ - if ( argc < 2 ) - { - display_help(); - return -1; - } - else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) - { - display_help(); - return 0; - } - else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) - { - display_version(); - return 0; - } - int dump_box = 1; - int chapter = 0; - char *filename; - lsmash_get_mainargs( &argc, &argv ); - if( argc > 2 ) - { - if( !strcasecmp( argv[1], "--box" ) ) - DO_NOTHING; - else if( !strcasecmp( argv[1], "--chapter" ) ) - chapter = 1; - else if( !strcasecmp( argv[1], "--timestamp" ) ) - dump_box = 0; - else - { - display_help(); - return -1; - } - filename = argv[2]; - } - else - { - filename = argv[1]; - } -#ifdef _WIN32 - _setmode( _fileno(stdin), _O_BINARY ); -#endif - /* Open the input file. */ - lsmash_root_t *root = lsmash_create_root(); - if( !root ) - { - fprintf( stderr, "Failed to create a ROOT.\n" ); - return -1; - } - lsmash_file_parameters_t file_param = { 0 }; - if( lsmash_open_file( filename, 1, &file_param ) < 0 ) - return BOXDUMPER_ERR( "Failed to open an input file.\n" ); - if( dump_box ) - file_param.mode |= LSMASH_FILE_MODE_DUMP; - lsmash_file_t *file = lsmash_set_file( root, &file_param ); - if( !file ) - return BOXDUMPER_ERR( "Failed to add a file into a ROOT.\n" ); - if( lsmash_read_file( file, &file_param ) < 0 ) - return BOXDUMPER_ERR( "Failed to read a file\n" ); - /* Dump the input file. */ - if( chapter ) - { - if( lsmash_print_chapter_list( root ) ) - return BOXDUMPER_ERR( "Failed to extract chapter.\n" ); - } - else if( dump_box ) - { - if( lsmash_print_movie( root, "-" ) ) - return BOXDUMPER_ERR( "Failed to dump box structure.\n" ); - } - else - { - lsmash_movie_parameters_t movie_param; - lsmash_initialize_movie_parameters( &movie_param ); - lsmash_get_movie_parameters( root, &movie_param ); - uint32_t num_tracks = movie_param.number_of_tracks; - for( uint32_t track_number = 1; track_number <= num_tracks; track_number++ ) - { - uint32_t track_ID = lsmash_get_track_ID( root, track_number ); - if( !track_ID ) - return BOXDUMPER_ERR( "Failed to get track_ID.\n" ); - lsmash_media_parameters_t media_param; - lsmash_initialize_media_parameters( &media_param ); - if( lsmash_get_media_parameters( root, track_ID, &media_param ) ) - return BOXDUMPER_ERR( "Failed to get media parameters.\n" ); - if( lsmash_construct_timeline( root, track_ID ) ) - return BOXDUMPER_ERR( "Failed to construct timeline.\n" ); - uint32_t timeline_shift; - if( lsmash_get_composition_to_decode_shift_from_media_timeline( root, track_ID, &timeline_shift ) ) - return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); - lsmash_media_ts_list_t ts_list; - if( lsmash_get_media_timestamps( root, track_ID, &ts_list ) ) - return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); - fprintf( stdout, "track_ID: %"PRIu32"\n", track_ID ); - fprintf( stdout, "Media timescale: %"PRIu32"\n", media_param.timescale ); - lsmash_media_ts_t *ts_array = ts_list.timestamp; - if( !ts_array ) - { - fprintf( stdout, "\n" ); - continue; - } - for( uint32_t i = 0; i < ts_list.sample_count; i++ ) - fprintf( stdout, "DTS = %"PRIu64", CTS = %"PRIu64"\n", ts_array[i].dts, ts_array[i].cts + timeline_shift ); - lsmash_free( ts_array ); - fprintf( stdout, "\n" ); - } - } - lsmash_destroy_root( root ); - return 0; -} diff -Nru l-smash-1.9.1/box.h l-smash-2.3.0/box.h --- l-smash-1.9.1/box.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/box.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,2608 +0,0 @@ -/***************************************************************************** - * box.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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" - -static const lsmash_class_t lsmash_box_class = -{ - "box" -}; - -typedef struct lsmash_box_tag isom_box_t; -typedef void (*isom_extension_destructor_t)( void *extension_data ); -typedef uint64_t (*isom_extension_updater_t)( void *extension_data ); -typedef int (*isom_extension_writer_t)( lsmash_bs_t *bs, isom_box_t *box ); - -/* If size is 1, then largesize is actual size. - * If size is 0, then this box is the last one in the file. */ -#define ISOM_BASEBOX_COMMON \ - const lsmash_class_t *class; \ - lsmash_root_t *root; /* pointer of root */ \ - lsmash_file_t *file; /* pointer of file */ \ - isom_box_t *parent; /* pointer of the parent box of this box */ \ - uint8_t *binary; /* used only when LSMASH_BINARY_CODED_BOX */ \ - isom_extension_destructor_t destruct; /* box specific destructor */ \ - isom_extension_updater_t update; /* box specific size updater */ \ - isom_extension_writer_t write; /* box specific writer */ \ - uint32_t manager; /* flags for L-SMASH */ \ - uint64_t precedence; /* precedence of the box position */ \ - uint64_t pos; /* starting position of this box in the file */ \ - lsmash_entry_list_t extensions; /* extension boxes */ \ - uint64_t size; /* the number of bytes in this box */ \ - lsmash_box_type_t type - -#define ISOM_FULLBOX_COMMON \ - ISOM_BASEBOX_COMMON; \ - uint8_t version; /* Basically, version is either 0 or 1 */ \ - uint32_t flags /* In the actual structure of box, flags is 24 bits. */ - -#define ISOM_BASEBOX_COMMON_SIZE 8 -#define ISOM_FULLBOX_COMMON_SIZE 12 -#define ISOM_LIST_FULLBOX_COMMON_SIZE 16 - -/* flags for L-SMASH */ -#define LSMASH_UNKNOWN_BOX 0x001 -#define LSMASH_ABSENT_IN_ROOT 0x002 -#define LSMASH_QTFF_BASE 0x004 -#define LSMASH_VIDEO_DESCRIPTION 0x008 -#define LSMASH_AUDIO_DESCRIPTION 0x010 -#define LSMASH_FULLBOX 0x020 -#define LSMASH_LAST_BOX 0x040 -#define LSMASH_INCOMPLETE_BOX 0x080 -#define LSMASH_BINARY_CODED_BOX 0x100 -#define LSMASH_PLACEHOLDER 0x200 -#define LSMASH_WRITTEN_BOX 0x400 - -/* 12-byte ISO reserved value: - * 0xXXXXXXXX-0011-0010-8000-00AA00389B71 */ -static const uint8_t static_lsmash_iso_12_bytes[12] - = { 0x00, 0x11, 0x00, 0x10, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }; -#define LSMASH_ISO_12_BYTES static_lsmash_iso_12_bytes - -/* L-SMASH original 12-byte QuickTime file format value for CODEC discrimination mainly: - * 0xXXXXXXXX-0F11-4DA5-BF4E-F2C48C6AA11E */ -static const uint8_t static_lsmash_qtff_12_bytes[12] - = { 0x0F, 0x11, 0x4D, 0xA5, 0xBF, 0x4E, 0xF2, 0xC4, 0x8C, 0x6A, 0xA1, 0x1E }; -#define LSMASH_QTFF_12_BYTES static_lsmash_qtff_12_bytes - -struct lsmash_box_tag -{ - ISOM_FULLBOX_COMMON; -}; - -/* Unknown Box - * This structure is for boxes we don't know or define yet. - * This box must be always appended as an extension box. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t unknown_size; - uint8_t *unknown_field; -} isom_unknown_box_t; - -/* File Type Box - * This box identifies the specifications to which this file complies. - * This box shall occur before any variable-length box. - * In the absence of this box, the file is QuickTime file format or MP4 version 1 file format. - * In MP4 version 1 file format, Object Descriptor Box is mandatory. - * In QuickTime file format, Object Descriptor Box isn't defined. - * Therefore, if this box and an Object Descriptor Box are absent in the file, the file shall be QuickTime file format. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t major_brand; /* brand identifier */ - uint32_t minor_version; /* the minor version of the major brand */ - uint32_t *compatible_brands; /* a list, to the end of the box, of brands */ - - uint32_t brand_count; /* the number of factors in compatible_brands array */ -} isom_ftyp_t; - -/* Color Table Box - * This box defines a list of preferred colors for displaying the movie on devices that support only 256 colors. - * The list may contain up to 256 colors. This box contains a Macintosh color table data structure. - * This box is defined in QuickTime File Format Specification. - * The color table structure is also defined in struct ColorTable defined in Quickdraw.h. */ -typedef struct -{ - /* An array of colors. - * Each color is made of four unsigned 16-bit integers. */ - uint16_t value; /* index or other value - * Must be set to 0. */ - /* true color */ - uint16_t r; /* magnitude of red component */ - uint16_t g; /* magnitude of green component */ - uint16_t b; /* magnitude of blue component */ -} isom_qt_color_array_t; - -typedef struct -{ - uint32_t seed; /* unique identifier for table - * Must be set to 0. */ - uint16_t flags; /* high bit: 0 = PixMap; 1 = device - * Must be set to 0x8000. */ - uint16_t size; /* the number of colors in the following color array - * This is a zero-relative value; - * setting this field to 0 means that there is one color in the array. */ - isom_qt_color_array_t *array; -} isom_qt_color_table_t; - -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_qt_color_table_t color_table; -} isom_ctab_t; - -/* Track Header Box - * This box specifies the characteristics of a single track. */ -typedef struct -{ - /* version is either 0 or 1 - * flags - * 0x000001: Indicates that the track is enabled. - * A disabled track is treated as if it were not present. - * 0x000002: Indicates that the track is used in the presentation. - * 0x000004: Indicates that the track is used when previewing the presentation. - * 0x000008: Indicates that the track is used in the movie's poster. (only defined in QuickTime file format) - * ISOM: If in a presentation all tracks have neither track_in_movie nor track_in_preview set, - * then all tracks shall be treated as if both flags were set on all tracks. */ - ISOM_FULLBOX_COMMON; - /* version == 0: uint64_t -> uint32_t */ - uint64_t creation_time; /* the creation time of this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */ - uint64_t modification_time; /* the most recent time the track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ - uint32_t track_ID; /* an integer that uniquely identifies the track - * Track IDs are never re-used and cannot be zero. */ - uint32_t reserved1; - uint64_t duration; /* the duration of this track expressed in the movie timescale units */ - /* The following fields are treated as - * ISOM: template fields. - * MP41: reserved fields. - * MP42: ignored fileds since compositions are done using BIFS system. - * 3GPP: ignored fields except for alternate_group. - * QTFF: usable fields. */ - uint32_t reserved2[2]; - int16_t layer; /* the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer. */ - int16_t alternate_group; /* an integer that specifies a group or collection of tracks - * If this field is not 0, it should be the same for tracks that contain alternate data for one another - * and different for tracks belonging to different such groups. - * Only one track within an alternate group should be played or streamed at any one time. */ - int16_t volume; /* fixed point 8.8 number. 0x0100 is full volume. */ - uint16_t reserved3; - int32_t matrix[9]; /* transformation matrix for the video */ - /* track's visual presentation size - * All images in the sequence are scaled to this size, before any overall transformation of the track represented by the matrix. - * Note: these fields are treated as reserved in MP4 version 1. */ - uint32_t width; /* fixed point 16.16 number */ - uint32_t height; /* fixed point 16.16 number */ - /* */ -} isom_tkhd_t; - -/* Track Clean Aperture Dimensions Box - * A presentation mode where clap and pasp are reflected. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t width; /* fixed point 16.16 number */ - uint32_t height; /* fixed point 16.16 number */ -} isom_clef_t; - -/* Track Production Aperture Dimensions Box - * A presentation mode where pasp is reflected. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t width; /* fixed point 16.16 number */ - uint32_t height; /* fixed point 16.16 number */ -} isom_prof_t; - -/* Track Encoded Pixels Dimensions Box - * A presentation mode where clap and pasp are not reflected. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t width; /* fixed point 16.16 number */ - uint32_t height; /* fixed point 16.16 number */ -} isom_enof_t; - -/* Track Aperture Mode Dimensions Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_clef_t *clef; /* Track Clean Aperture Dimensions Box */ - isom_prof_t *prof; /* Track Production Aperture Dimensions Box */ - isom_enof_t *enof; /* Track Encoded Pixels Dimensions Box */ -} isom_tapt_t; - -/* Edit List Box - * This box contains an explicit timeline map. - * Each entry defines part of the track timeline: by mapping part of the media timeline, or by indicating 'empty' time, - * or by defining a 'dwell', where a single time-point in the media is held for a period. - * The last edit in a track shall never be an empty edit. - * Any difference between the duration in the Movie Header Box, and the track's duration is expressed as an implicit empty edit at the end. - * It is recommended that any edits, explicit or implied, not select any portion of the composition timeline that doesn't map to a sample. - * Therefore, if the first sample in the track has non-zero CTS, then this track should have at least one edit and the start time in it should - * correspond to the value of the CTS the first sample has or more not to exceed the largest CTS in this track. */ -typedef struct -{ - /* This entry is called Timeline Mapping Edit (TME) entry in UltraViolet Common File Format. - * version == 0: 64bits -> 32bits */ - uint64_t segment_duration; /* the duration of this edit expressed in the movie timescale units */ - int64_t media_time; /* the starting composition time within the media of this edit segment - * If this field is set to -1, it is an empty edit. */ - int32_t media_rate; /* the relative rate at which to play the media corresponding to this edit segment - * If this value is 0, then the edit is specifying a 'dwell': - * the media at media_time is presented for the segment_duration. - * This field is expressed as 16.16 fixed-point number. */ -} isom_elst_entry_t; - -typedef struct -{ - ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ - lsmash_entry_list_t *list; -} isom_elst_t; - -/* Edit Box - * This optional box maps the presentation time-line to the media time-line as it is stored in the file. - * In the absence of this box, there is an implicit one-to-one mapping of these time-lines, - * and the presentation of a track starts at the beginning of the presentation. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_elst_t *elst; /* Edit List Box */ -} isom_edts_t; - -/* Track Reference Box - * The Track Reference Box contains Track Reference Type Boxes. - * Track Reference Type Boxes define relationships between tracks. - * They allow one track to specify how it is related to other tracks. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t *track_ID; /* track_IDs of reference tracks / Zero value must not be used */ - - uint32_t ref_count; /* number of reference tracks */ -} isom_tref_type_t; - -typedef struct -{ - ISOM_BASEBOX_COMMON; - lsmash_entry_list_t ref_list; /* Track Reference Type Boxes */ -} isom_tref_t; - -/* Media Header Box - * This box declares overall information that is media-independent, and relevant to characteristics of the media in a track.*/ -typedef struct -{ - ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ - /* version == 0: uint64_t -> uint32_t */ - uint64_t creation_time; /* the creation time of the media in this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */ - uint64_t modification_time; /* the most recent time the media in this track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ - uint32_t timescale; /* media timescale: timescale for this media */ - uint64_t duration; /* the duration of this media expressed in the timescale indicated in this box */ - /* */ - uint16_t language; /* ISOM: ISO-639-2/T language codes. Most significant 1-bit is 0. - * Each character is packed as the difference between its ASCII value and 0x60. - * QTFF: Macintosh language codes is usually used. - * Mac's value is less than 0x800 while ISO's value is 0x800 or greater. */ - int16_t quality; /* ISOM: pre_defined / QTFF: the media's playback quality */ -} isom_mdhd_t; - -/* Handler Reference Box - * In Media Box, this box is mandatory and (ISOM: should/QTFF: must) come before Media Information Box. - * ISOM: this box might be also in Meta Box. - * QTFF: this box might be also in Media Information Box. If this box is present there, it must come before Data Information Box. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t componentType; /* ISOM: pre_difined = 0 - * QTFF: 'mhlr' for Media Handler Reference Box and 'dhlr' for Data Handler Reference Box */ - uint32_t componentSubtype; /* Both ISOM and QT: when present in Media Handler Reference Box, this field defines the type of media data. - * ISOM: when present in Metadata Handler Reference Box, this field defines the format of the meta box contents. - * QTFF: when present in Data Handler Reference Box, this field defines the data reference type. */ - /* The following fields are defined in QTFF however these fields aren't mentioned in QuickTime SDK and are reserved in the specification. - * In ISOM, these fields are still defined as reserved. */ - uint32_t componentManufacturer; /* vendor indentification / A value of 0 matches any manufacturer. */ - uint32_t componentFlags; /* flags describing required component capabilities - * The high-order 8 bits should be set to 0. - * The low-order 24 bits are specific to each component type. */ - uint32_t componentFlagsMask; /* This field indicates which flags in the componentFlags field are relevant to this operation. */ - /* */ - uint8_t *componentName; /* ISOM: a null-terminated string in UTF-8 characters - * QTFF: Pascal string */ - - uint32_t componentName_length; -} isom_hdlr_t; - - -/** Media Information Header Boxes - ** There is a different media information header for each track type - ** (corresponding to the media handler-type); the matching header shall be present. **/ -/* Video Media Header Box - * This box contains general presentation information, independent of the coding, for video media. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; /* flags is 1 */ - uint16_t graphicsmode; /* template: graphicsmode = 0 */ - uint16_t opcolor[3]; /* template: opcolor = { 0, 0, 0 } */ -} isom_vmhd_t; - -/* Sound Media Header Box - * This box contains general presentation information, independent of the coding, for audio media. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - int16_t balance; /* a fixed-point 8.8 number that places mono audio tracks in a stereo space. template: balance = 0 */ - uint16_t reserved; -} isom_smhd_t; - -/* Hint Media Header Box - * This box contains general information, independent of the protocol, for hint tracks. (A PDU is a Protocol Data Unit.) */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint16_t maxPDUsize; /* the size in bytes of the largest PDU in this (hint) stream */ - uint16_t avgPDUsize; /* the average size of a PDU over the entire presentation */ - uint32_t maxbitrate; /* the maximum rate in bits/second over any window of one second */ - uint32_t avgbitrate; /* the average rate in bits/second over the entire presentation */ - uint32_t reserved; -} isom_hmhd_t; - -/* Null Media Header Box - * This box may be used for streams other than visual and audio (e.g., timed metadata streams). */ -typedef struct -{ - /* Streams other than visual and audio may use a Null Media Header Box */ - ISOM_FULLBOX_COMMON; /* flags is currently all zero */ -} isom_nmhd_t; - -/* Generic Media Information Box */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint16_t graphicsmode; - uint16_t opcolor[3]; - int16_t balance; /* This field is nomally set to 0. */ - uint16_t reserved; /* Reserved for use by Apple. Set this field to 0. */ -} isom_gmin_t; - -/* Text Media Information Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - int32_t matrix[9]; /* Unkown fields. Default values are probably: - * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */ -} isom_text_t; - -/* Generic Media Information Header Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_gmin_t *gmin; /* Generic Media Information Box */ - isom_text_t *text; /* Text Media Information Box */ -} isom_gmhd_t; -/** **/ - -/* Data Reference Box - * name and location fields are expressed in null-terminated string using UTF-8 characters. */ -typedef struct -{ - /* This box is DataEntryUrlBox or DataEntryUrnBox */ - ISOM_FULLBOX_COMMON; /* flags == 0x000001 means that the media data is in the same file - * as the Movie Box containing this data reference. */ - char *name; /* only for DataEntryUrnBox */ - char *location; /* a location to find the resource with the given name */ - - uint32_t name_length; - uint32_t location_length; -} isom_dref_entry_t; - -typedef struct -{ - ISOM_FULLBOX_COMMON; - lsmash_entry_list_t list; -} isom_dref_t; - -/* Data Information Box */ -typedef struct -{ - /* This box is in Media Information Box or Meta Box */ - ISOM_BASEBOX_COMMON; - isom_dref_t *dref; /* Data Reference Box */ -} isom_dinf_t; - -/** Sample Description **/ -/* ES Descriptor Box */ -struct mp4sys_ES_Descriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - struct mp4sys_ES_Descriptor_t *ES; -} isom_esds_t; - -/* Parameter Set Entry within AVC/HEVC Decoder Configuration Record */ -typedef struct -{ - uint16_t nalUnitLength; - uint8_t *nalUnit; - /* */ - int unused; -} isom_dcr_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; - -/* Global Header Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t header_size; - uint8_t *header_data; -} isom_glbl_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; - * QTFF: this box is a mandatory extension for all uncompressed Y'CbCr data formats. */ -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; - -/* ISOM: Colour Information Box / QTFF: 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. - * If colour information is supplied in both this box, and also in the video bitstream, - * this box takes precedence, and over-rides the information in the bitstream. - * For QuickTime file format: - * This box ('colr') supersedes the Gamma Level Box ('gama'). - * Writers of QTFF should never write both into an Image Description, and readers of QTFF should ignore 'gama' if 'colr' is present. - * Note: this box is a mandatory extension for all uncompressed Y'CbCr data formats. - * For ISO Base Media file format: - * Colour information may be supplied in one or more Colour Information Boxes placed in a VisualSampleEntry. - * These should be placed in order in the sample entry starting with the most accurate (and potentially the most difficult to process), in progression to the least. - * These are advisory and concern rendering and colour conversion, and there is no normative behaviour associated with them; a reader may choose to use the most suitable. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t color_parameter_type; /* QTFF: 'nclc' or 'prof' - * ISOM: 'nclx', 'rICC' or 'prof' */ - /* for 'nclc' and 'nclx' */ - 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 */ - /* for 'nclx' */ - unsigned full_range_flag : 1; - unsigned reserved : 7; -} isom_colr_t; - -/* Gamma Level Box - * This box is used to indicate that the decompressor corrects gamma level at display time. - * This box is defined in QuickTime File Format Specification and ImageCompression.h. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t level; /* A fixed-point 16.16 number indicating the gamma level at which the image was captured. - * Zero value indicates platform's standard gamma. */ -} isom_gama_t; - -/* Field/Frame Information Box - * This box is used by applications to modify decompressed image data or by decompressor components to determine field display order. - * This box is defined in QuickTime File Format Specification, dispatch019 and ImageCodec.h. - * Note: this box is a mandatory extension for all uncompressed Y'CbCr data formats. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint8_t fields; /* the number of fields per frame - * 1: progressive scan - * 2: 2:1 interlaced */ - uint8_t detail; /* field ordering */ -} isom_fiel_t; - -/* Colorspace Box - * This box is defined in ImageCompression.h. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t pixel_format; /* the native pixel format of an image */ -} isom_cspc_t; - -/* Significant Bits Box - * This box is defined in Letters from the Ice Floe dispatch019. - * Note: this box is a mandatory extension for 'v216' (Uncompressed Y'CbCr, 10, 12, 14, or 16-bit-per-component 4:2:2). */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint8_t significantBits; /* the number of significant bits per component */ -} isom_sgbt_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. - * This box is defined in ISO Base Media file format. */ -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_mp4s_entry_t; - -/* ISOM: Visual Sample Entry / QTFF: Image Description - * For maximum compatibility, the following extension boxes should follow, not precede, - * any extension boxes defined in or required by derived specifications. - * Clean Aperture Box - * Pixel Aspect Ratio Box */ -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 -1, 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. */ - /* Color table follows color_table_ID only when color_table_ID is set to 0. */ - isom_qt_color_table_t color_table; /* a list of preferred colors for displaying the movie on devices that support only 256 colors */ -} 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 is defined in QuickTime file format. - * 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_terminator_t *terminator; /* Terminator Box */ -} isom_wave_t; - -/* Audio Channel Layout Box - * This box is defined in QuickTime file format or Apple Lossless Audio inside ISO Base Media. */ -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; - -/* Sampling Rate Box - * This box may be present only in an AudioSampleEntryV1, and when present, - * it overrides the samplerate field and documents the actual sampling rate. - * When this box is present, the media timescale should be the same as the - * sampling rate, or an integer division or multiple of it. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t sampling_rate; /* the actual sampling rate of the audio media, expressed as a 32-bit integer - * The value of this field overrides the samplerate field in the AudioSampleEntryV1 - * and documents the actual sampling rate. */ -} isom_srat_t; - -/* ISOM: Audio Sample Entry / QTFF: Sound Description */ -typedef struct -{ - ISOM_SAMPLE_ENTRY; - int16_t version; /* ISOM: version = 0 is used to support non-high samplerate audio format. - * version = 1, called AudioSampleEntryV1, is used to support high samplerate audio format. - * An AudioSampleEntryV1 requires that the enclosing Sample Description Box also takes the version 1. - * For maximum compatibility, an AudioSampleEntryV1 should only be used when needed. - * QTFF: 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 format. */ - 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 - * channelcount is a value greater than zero that indicates the maximum number of channels that the - * audio could deliver. - * A channelcount of 1 indicates mono audio, and 2 indicates stereo (left/right). - * When values greater than 2 are used, the codec configuration should identify the channel assignment. - * 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 - * When it is desired to indicate an audio sampling rate greater than the value that can be represented in - * this field, this field should contain a value left-shifted 16 bits that matches the media timescale, - * or be an integer division or multiple of it. - * 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. */ - /* QTFF-based 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 */ - /* QTFF-based 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 Audio Channel Layout 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 */ - - 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_qt_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; - -/* 3GPP 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; - uint32_t entry_count; /* print only */ - 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. */ -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: if version is set to 1, sample_offset is signed 32-bit integer. - * QTFF: sample_offset is always signed 32-bit integer. */ -} 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_t; - -/* Random Access Entry - * Samples marked by this group must be random access points, and may also be sync points. */ -typedef struct -{ - /* grouping_type is 'rap ' */ - uint32_t description_length; /* This field is available only if version == 1 and default_length == 0. */ - unsigned num_leading_samples_known : 1; /* the value of one indicates that the number of leading samples is known for each sample in this group, - * and the number is specified by num_leading_samples. */ - unsigned num_leading_samples : 7; /* the number of leading samples for each sample in this group - * Note: when num_leading_samples_known is equal to 0, this field should be ignored. */ -} isom_rap_entry_t; - -/* Roll Recovery Entry - * This grouping type is defined as that group of samples having the same roll distance. */ -typedef struct -{ - /* grouping_type is 'roll' */ - uint32_t description_length; /* This field is available only if version == 1 and default_length == 0. */ - int16_t roll_distance; /* the number of samples that must be decoded in order for a sample to be decoded correctly - * A positive value indicates post-roll, and a negative value indicates pre-roll. - * The value zero must not be used. */ -} isom_roll_entry_t; - -/* Sample to Group Box - * This box is used to find the group that a sample belongs to and the associated description of that sample group. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t grouping_type; /* Links it to its sample group description table with the same value for grouping type. */ - uint32_t grouping_type_parameter; /* an indication of the sub-type of the grouping - * This field is available only if version == 1. */ - lsmash_entry_list_t *list; -} isom_sbgp_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. - * Within the Sample to Group Box in movie fragment, the group description indexes for groups defined - * within the same fragment start at 0x10001, i.e. the index value 1, with the value 1 in the top 16 bits. */ -} isom_group_assignment_entry_t; - -/* Sample Table Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_stsd_t *stsd; /* Sample Description Box */ - isom_stts_t *stts; /* Decoding Time to Sample Box */ - isom_ctts_t *ctts; /* Composition Time to Sample Box */ - isom_cslg_t *cslg; /* ISOM: Composition to Decode Box / QTFF: Composition Shift Least Greatest Box */ - isom_stss_t *stss; /* Sync Sample Box */ - isom_stps_t *stps; /* ISOM: null / QTFF: Partial Sync Sample Box */ - isom_sdtp_t *sdtp; /* Independent and Disposable Samples Box */ - isom_stsc_t *stsc; /* Sample To Chunk Box */ - isom_stsz_t *stsz; /* Sample Size Box */ - isom_stco_t *stco; /* Chunk Offset Box */ - lsmash_entry_list_t sgpd_list; /* Sample Group Description Boxes */ - lsmash_entry_list_t sbgp_list; /* Sample To Group Boxes */ -} isom_stbl_t; - -/* Media Information Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - /* Media Information Header Boxes */ - isom_vmhd_t *vmhd; /* Video Media Header Box */ - isom_smhd_t *smhd; /* Sound Media Header Box */ - isom_hmhd_t *hmhd; /* ISOM: Hint Media Header Box / QTFF: null */ - isom_nmhd_t *nmhd; /* ISOM: Null Media Header Box / QTFF: null */ - isom_gmhd_t *gmhd; /* ISOM: null / QTFF: Generic Media Information Header Box */ - /* */ - isom_hdlr_t *hdlr; /* ISOM: null / QTFF: Data Handler Reference Box - * Note: this box must come before Data Information Box. */ - isom_dinf_t *dinf; /* Data Information Box */ - isom_stbl_t *stbl; /* Sample Table Box */ -} isom_minf_t; - -/* Media Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_mdhd_t *mdhd; /* Media Header Box */ - isom_hdlr_t *hdlr; /* ISOM: Handler Reference Box / QTFF: Media Handler Reference Box - * Note: this box must come before Media Information Box. */ - isom_minf_t *minf; /* Media Information Box */ -} isom_mdia_t; - -/* Movie Header Box - * This box defines overall information which is media-independent, and relevant to the entire presentation considered as a whole. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ - /* version == 0: uint64_t -> uint32_t */ - uint64_t creation_time; /* the creation time of the presentation (in seconds since midnight, Jan. 1, 1904, in UTC time) */ - uint64_t modification_time; /* the most recent time the presentation was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ - uint32_t timescale; /* movie timescale: timescale for the entire presentation */ - uint64_t duration; /* the duration, expressed in movie timescale, of the longest track */ - /* The following fields are treated as - * ISOM: template fields. - * MP41: reserved fields. - * MP42: ignored fileds since compositions are done using BIFS system. - * 3GPP: ignored fields. - * QTFF: usable fields. */ - int32_t rate; /* fixed point 16.16 number. 0x00010000 is normal forward playback. */ - int16_t volume; /* fixed point 8.8 number. 0x0100 is full volume. */ - int16_t reserved; - int32_t preferredLong[2]; /* ISOM: reserved / QTFF: unknown */ - int32_t matrix[9]; /* transformation matrix for the video */ - /* The following fields are defined in QuickTime file format. - * In ISO Base Media file format, these fields are treated as pre_defined. */ - int32_t previewTime; /* the time value in the movie at which the preview begins */ - int32_t previewDuration; /* the duration of the movie preview in movie timescale units */ - int32_t posterTime; /* the time value of the time of the movie poster */ - int32_t selectionTime; /* the time value for the start time of the current selection */ - int32_t selectionDuration; /* the duration of the current selection in movie timescale units */ - int32_t currentTime; /* the time value for current time position within the movie */ - /* */ - uint32_t next_track_ID; /* larger than the largest track-ID in use */ -} isom_mvhd_t; - -/* Object Descriptor Box - * Note that this box is mandatory under 14496-1:2001 (mp41) while not mandatory under 14496-14:2003 (mp42). */ -struct mp4sys_ObjectDescriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - struct mp4sys_ObjectDescriptor_t *OD; -} isom_iods_t; - -/* Media Data Box - * This box contains the media data. - * A presentation may contain zero or more Media Data Boxes.*/ -typedef struct -{ - ISOM_BASEBOX_COMMON; /* If size is 0, then this box is the last box. */ - - uint64_t media_size; /* the total media size already written in this box */ -} 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. - * Basically, this box exists in User Data Box inside Movie Box if present. */ -typedef struct -{ - uint64_t start_time; /* version = 0: expressed in movie timescale units - * version = 1: expressed in 100 nanoseconds */ - /* Chapter name is Pascal string */ - uint8_t chapter_name_length; - char *chapter_name; -} isom_chpl_entry_t; - -typedef struct -{ - ISOM_FULLBOX_COMMON; /* version = 0 is defined in F4V file format. */ - uint8_t unknown; /* only available under version = 1 */ - lsmash_entry_list_t *list; /* if version is set to 0, entry_count is uint8_t. */ -} isom_chpl_t; - -typedef struct -{ - char *chapter_name; - uint64_t start_time; -} isom_chapter_entry_t; - -/* Metadata Item Keys Box */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - lsmash_entry_list_t *list; -} isom_keys_t; - -typedef struct -{ - uint32_t key_size; /* the size of the entire structure containing a key definition - * key_size = sizeof(key_size) + sizeof(key_namespace) + sizeof(key_value) */ - uint32_t key_namespace; /* a naming scheme used for metadata keys - * Location metadata keys, for example, use the 'mdta' key namespace. */ - uint8_t *key_value; /* the actual name of the metadata key - * Keys with the 'mdta' namespace use a reverse DNS naming convention. */ -} isom_keys_entry_t; - -/* Meaning Box */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint8_t *meaning_string; /* to fill the box */ - - uint32_t meaning_string_length; -} isom_mean_t; - -/* Name Box */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint8_t *name; /* to fill the box */ - - uint32_t name_length; -} isom_name_t; - -/* Data Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - /* type indicator */ - uint16_t reserved; /* always 0 */ - uint8_t type_set_identifier; /* 0: type set of the common basic data types */ - uint8_t type_code; /* type of data code */ - /* */ - uint32_t the_locale; /* reserved to be 0 */ - uint8_t *value; /* to fill the box */ - - uint32_t value_length; -} isom_data_t; - -/* Metadata Item Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_mean_t *mean; /* Meaning Box */ - isom_name_t *name; /* Name Box */ - isom_data_t *data; /* Data Box */ -} isom_metaitem_t; - -/* Metadata Item List Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - lsmash_entry_list_t item_list; /* Metadata Item Box List - * There is no entry_count field. */ -} isom_ilst_t; - -/* Meta Box */ -typedef struct -{ - ISOM_FULLBOX_COMMON; /* ISOM: FullBox / QTFF: BaseBox */ - isom_hdlr_t *hdlr; /* Metadata Handler Reference Box */ - isom_dinf_t *dinf; /* ISOM: Data Information Box / QTFF: null */ - isom_keys_t *keys; /* ISOM: null / QTFF: Metadata Item Keys Box */ - isom_ilst_t *ilst; /* Metadata Item List Box only defined in Apple MPEG-4 and QTFF */ -} isom_meta_t; - -/* Window Location Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - /* default window location for movie */ - uint16_t x; - uint16_t y; -} isom_WLOC_t; - -/* Looping Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint32_t looping_mode; /* 0 for none, 1 for looping, 2 for palindromic looping */ -} isom_LOOP_t; - -/* Play Selection Only Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint8_t selection_only; /* whether only the selected area of the movie should be played */ -} isom_SelO_t; - -/* Play All Frames Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - uint8_t play_all_frames; /* whether all frames of video should be played, regardless of timing */ -} isom_AllF_t; - -/* Copyright Box - * The Copyright box contains a copyright declaration which applies to the entire presentation, - * when contained within the Movie Box, or, when contained in a track, to that entire track. - * There may be multiple copyright boxes using different language codes. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint16_t language; /* ISO-639-2/T language codes. Most significant 1-bit is 0. - * Each character is packed as the difference between its ASCII value and 0x60. */ - uint8_t *notice; /* a null-terminated string in either UTF-8 or UTF-16 characters, giving a copyright notice. - * If UTF-16 is used, the string shall start with the BYTE ORDER MARK (0xFEFF), to distinguish it from a UTF-8 string. - * This mark does not form part of the final string. */ - uint32_t notice_length; -} isom_cprt_t; - -/* User Data Box - * This box is a container box for informative user-data. - * This user data is formatted as a set of boxes with more specific box types, which declare more precisely their content. - * QTFF: for historical reasons, this box is optionally terminated by a 32-bit integer set to 0. */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_chpl_t *chpl; /* Chapter List Box */ - isom_meta_t *meta; /* Meta Box extended by Apple for iTunes movie */ - /* QuickTime user data */ - isom_WLOC_t *WLOC; /* Window Location Box */ - isom_LOOP_t *LOOP; /* Looping Box */ - isom_SelO_t *SelO; /* Play Selection Only Box */ - isom_AllF_t *AllF; /* Play All Frames Box */ - /* Copyright Box List */ - lsmash_entry_list_t cprt_list; /* Copyright Boxes is defined in ISO Base Media and 3GPP file format */ -} isom_udta_t; - -/** Caches for handling tracks **/ -typedef struct -{ - uint64_t alloc; /* total buffer size for the pool */ - uint64_t size; /* total size of samples in the pool */ - uint32_t sample_count; /* number of samples in the pool */ - uint8_t *data; /* actual data of samples in the pool */ -} isom_sample_pool_t; - -typedef struct -{ - uint32_t chunk_number; /* chunk number */ - uint32_t sample_description_index; /* sample description index */ - uint64_t first_dts; /* the first DTS in chunk */ - isom_sample_pool_t *pool; /* samples pooled to interleave */ -} isom_chunk_t; - -typedef struct -{ - uint64_t dts; - uint64_t cts; - int32_t ctd_shift; -} isom_timestamp_t; - -typedef struct -{ - isom_group_assignment_entry_t *assignment; /* the address corresponding to the entry in Sample to Group Box */ - isom_group_assignment_entry_t *prev_assignment; /* the address of the previous assignment */ - isom_rap_entry_t *random_access; /* the address corresponding to the random access entry in Sample Group Description Box */ - uint8_t is_prev_rap; /* whether the previous sample is a random access point or not */ -} isom_rap_group_t; - -typedef struct -{ - isom_group_assignment_entry_t *assignment; /* the address corresponding to the entry in Sample to Group Box */ - isom_sgpd_t *sgpd; /* the address to the active Sample Group Description 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 */ - uint64_t rp_cts; /* the CTS of the recovery point */ - int16_t roll_distance; /* the current roll_distance - * The value may be updated when 'described' is set to ROLL_DISTANCE_INITIALIZED. */ -#define MAX_ROLL_WAIT_AND_SEE_COUNT 64 - uint8_t wait_and_see_count; /* Wait-and-see after initialization of roll_distance until reaching MAX_ROLL_WAIT_AND_SEE. */ - uint8_t is_fragment; /* the flag if the current group is in fragment */ - uint8_t prev_is_recovery_start; /* whether the previous sample is a starting point of recovery or not */ - uint8_t delimited; /* the flag if the sample_count is determined */ -#define ROLL_DISTANCE_INITIALIZED 1 -#define ROLL_DISTANCE_DETERMINED 2 - uint8_t described; /* the status of the group description */ -} isom_roll_group_t; - -typedef struct -{ - lsmash_entry_list_t *pool; /* grouping pooled to delimit and describe */ -} isom_grouping_t; - -typedef struct -{ - uint64_t largest_cts; /* the largest CTS of a subsegment of the reference stream */ - uint64_t smallest_cts; /* the smallest CTS of a subsegment of the reference stream */ - uint64_t first_cts; /* the CTS of the first sample of a subsegment of the reference stream */ - uint64_t segment_duration; /* the sum of the subsegment_duration of preceeding subsegments */ - /* SAP related info within the active subsegment of the reference stream */ - uint64_t first_ed_cts; /* the earliest CTS of decodable samples after the first recovery point */ - uint64_t first_rp_cts; /* the CTS of the first recovery point */ - uint32_t first_rp_number; /* the number of the first recovery point */ - uint32_t first_ra_number; /* the number of the first random accessible sample */ - lsmash_random_access_flag first_ra_flags; /* the flags of the first random accessible sample */ - int decodable; -} isom_subsegment_t; - -typedef struct -{ - uint8_t has_samples; - uint8_t roll_grouping; - uint8_t rap_grouping; - 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 fragment */ - uint64_t sample_count; /* the number of samples in this track fragment */ - isom_subsegment_t subsegment; -} 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-present: - * This flag indicates the presence of the base_data_offset field. - * The base_data_offset is the base offset to use when calculating data offsets. - * Offsets are file offsets as like as chunk_offset in Chunk Offset Box. - * If this flag is set and default-base-is-moof is not set, 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. */ - ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT = 0x000002, /* sample-description-index-present - * This flag indicates the presence of the sample_description_index field. */ - ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT = 0x000008, /* default-sample-duration-present: - * This flag indicates the presence of the default_sample_duration field. */ - ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT = 0x000010, /* default-sample-size-present: - * This flag indicates the presence of the default_sample_size field. */ - ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT = 0x000020, /* default-sample-flags-present: - * This flag indicates the presence of the default_sample_flags field. */ - ISOM_TF_FLAGS_DURATION_IS_EMPTY = 0x010000, /* duration-is-empty: - * This flag indicates there are no samples for this time interval. */ - ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF = 0x020000, /* default-base-is-moof: - * If base-data-offset-present is not set, this flag indicates the implicit - * base_data_offset is always equal to the position of the first byte of the - * enclosing Movie Fragment BOX. - * This flag is only available under the 'iso5' or later brands and cannot be set - * when earlier brands are included in the File Type box. */ -} isom_tf_flags_code; - -/* Track Run Flags ('tr_flags') */ -typedef enum -{ - ISOM_TR_FLAGS_DATA_OFFSET_PRESENT = 0x000001, /* data-offset-present: - * This flag indicates the presence of the data_offset field. */ - ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT = 0x000004, /* first-sample-flags-present: - * This flag indicates the presence of the first_sample_flags field. */ - ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT = 0x000100, /* sample-duration-present: - * This flag indicates the presence of the sample_duration field. */ - ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT = 0x000200, /* sample-size-present: - * This flag indicates the presence of the sample_size field. */ - ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT = 0x000400, /* sample-flags-present: - * This flag indicates the presence of the sample_flags field. */ - ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT = 0x000800, /* sample-composition-time-offsets-present: - * This flag indicates the presence of the sample_composition_time_offset field. */ -} 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_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 */ -} 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 - * 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 Base Media Decode Time Box - * This box provides the absolute decode time, measured on the media timeline, of the first sample in decode order in the track fragment. - * This can be useful, for example, when performing random access in a file; - * it is not necessary to sum the sample durations of all preceding samples in previous fragments to find this value - * (where the sample durations are the deltas in the Decoding Time to Sample Box and the sample_durations in the preceding track runs). - * This box, if present, shall be positioned after the Track Fragment Header Box and before the first Track Fragment Run box. */ -typedef struct -{ - ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ - /* version == 0: 64bits -> 32bits */ - uint64_t baseMediaDecodeTime; /* an integer equal to the sum of the decode durations of all earlier samples in the media, expressed in the media's timescale - * It does not include the samples added in the enclosing track fragment. - * NOTE: the decode timeline is a media timeline, established before any explicit or implied mapping of media time to presentation time, - * for example by an edit list or similar structure. */ -} isom_tfdt_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_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 - * If version == 0, unsigned 32-bit integer. - * Otherwise, signed 32-bit integer. */ -} isom_trun_optional_row_t; - -/* Track Fragment Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_tfhd_t *tfhd; /* Track Fragment Header Box */ - isom_tfdt_t *tfdt; /* Track Fragment Base Media Decode Time 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_sdtp_t *sdtp; /* Independent and Disposable Samples Box (available under Protected Interoperable File Format) */ - lsmash_entry_list_t sgpd_list; /* Sample Group Description Boxes (available under ISO Base Media version 6 or later) */ - lsmash_entry_list_t sbgp_list; /* Sample To Group Boxes */ - - isom_cache_t *cache; -} isom_traf_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_t; - -/* Track Fragment Random Access Box - * Each entry in this box contains the location and the presentation time of the sync sample. - * Note that not every sync 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 sync sample and no table entry follows. */ - lsmash_entry_list_t *list; /* entry_count corresponds to number_of_entry. */ -} isom_tfra_t; - -typedef struct -{ - /* version == 0: 64bits -> 32bits */ - uint64_t time; /* the presentation time of the sync sample in units defined in the Media Header Box of the associated track - * For segments based on movie sample tables or movie fragments, presentation times are in the movie timeline, - * that is they are composition times after the application of any edit list for the track. - * Note: the definition of segment is portion of an ISO base media file format file, consisting of either - * (a) a movie box, with its associated media data (if any) and other associated boxes - * or - * (b) one or more movie fragment boxes, with their associated media data, and other associated boxes. */ - 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 sync 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 sync sample - * The number ranges from 1 in each Track Fragment Box ('traf'). */ - uint32_t sample_number; /* the sample number that contains the sync 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 sync samples 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_t *movie; /* the address corresponding to the current Movie Fragment Box */ - uint64_t fragment_count; /* the number of movie fragments we created */ - uint64_t first_moof_pos; - uint64_t pool_size; /* the total sample size in the current movie fragment */ - uint64_t sample_count; /* the number of samples within the current movie fragment */ - lsmash_entry_list_t *pool; /* samples pooled to interleave for the current movie fragment */ -} isom_fragment_manager_t; - -/** **/ - -/* Track Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_tkhd_t *tkhd; /* Track Header Box */ - isom_tapt_t *tapt; /* ISOM: null / QTFF: Track Aperture Mode Dimensions Box */ - isom_edts_t *edts; /* Edit Box */ - isom_tref_t *tref; /* Track Reference Box */ - isom_mdia_t *mdia; /* Media Box */ - isom_udta_t *udta; /* User Data Box */ - isom_meta_t *meta; /* Meta Box */ - - isom_cache_t *cache; - uint32_t related_track_ID; - uint8_t is_chapter; -} isom_trak_t; - -/* Movie Box */ -typedef struct -{ - ISOM_BASEBOX_COMMON; - isom_mvhd_t *mvhd; /* Movie Header Box */ - isom_iods_t *iods; /* MP4: Object Descriptor Box */ - lsmash_entry_list_t trak_list; /* Track Box List */ - isom_udta_t *udta; /* User Data Box */ - isom_ctab_t *ctab; /* ISOM: null / QTFF: Color Table Box */ - isom_meta_t *meta; /* Meta Box */ - isom_mvex_t *mvex; /* Movie Extends Box */ -} isom_moov_t; - -/** Segments - * segment - * portion of an ISO base media file format file, consisting of either (a) a movie box, with its associated media data - * (if any) and other associated boxes or (b) one or more movie fragment boxes, with their associated media data, and - * and other associated boxes - * subsegment - * time interval of a segment formed from movie fragment boxes, that is also a valid segment - * A subsegment is defined as a time interval of the containing (sub)segment, and corresponds to a single range of - * bytes of the containing (sub)segment. The durations of all the subsegments sum to the duration of the containing - * (sub)segment. - **/ -/* Segment Type Box - * Media presentations may be divided into segments for delivery, for example, it is possible (e.g. in HTTP streaming) to - * form files that contain a segment ? or concatenated segments ? which would not necessarily form ISO Base Media file - * format compliant files (e.g. they do not contain a Movie Box). - * If segments are stored in separate files (e.g. on a standard HTTP server) it is recommended that these 'segment files' - * contain a Segment Type Box, which must be first if present, to enable identification of those files, and declaration of - * the specifications with which they are compliant. - * Segment Type Boxes that are not first in a file may be ignored. - * Valid Segment Type Boxes shall be the first box in a segment. - * Note: - * The 'valid' here does not always mean that any brand of that segment has compatibility against other brands of it. - * After concatenations of segments, the result file might contain incompatibilities among brands. */ -typedef isom_ftyp_t isom_styp_t; - -/* Segment Index Box - * This box provides a compact index of one media stream within the media segment to which it applies. - * - * Each Segment Index Box documents how a (sub)segment is divided into one or more subsegments (which may themselves be - * further subdivided using Segment Index boxes). - * - * Each entry in the Segment Index Box contains a reference type that indicates whether the reference points directly to - * the media bytes of a referenced leaf subsegment, which is a subsegment that does not contain any indexing information - * that would enable its further division into subsegments, or to a Segment Index box that describes how the referenced - * subsegment is further subdivided; as a result, the segment may be indexed in a 'hierarchical' or 'daisy-chain' or - * other form by documenting time and byte offset information for other Segment Index Boxes applying to portions of the - * same (sub)segment. - * - * For segments based on ISO Base Media file format (i.e. based on movie sample tables or movie fragments): - * ! an access unit is a sample; - * ! a subsegment is a self-contained set of one or more consecutive movie fragments; a self-contained set contains - * one or more Movie Fragment Boxes with the corresponding Media Data Box(es), and a Media Data Box containing data - * referenced by a Movie Fragment Box must follow that Movie Fragment Box and precede the next Movie Fragment box - * containing information about the same track; - * ! Segment Index Boxes shall be placed before subsegment material they document, that is, before any Movie Fragment - * Box of the documented material of the subsegment; - * ! streams are tracks in the file format, and stream IDs are track IDs; - * ! a subsegment contains a stream access point if a track fragment within the subsegment for the track with track_ID - * equal to reference_ID contains a stream access point; - * ! initialisation data for SAPs consists of the Movie Box; - * ! presentation times are in the movie timeline, that is they are composition times after the application of any edit - * list for the track; - * ! the ISAP is a position exactly pointing to the start of a top-level box, such as a Movie Fragment Box; - * ! a SAP of type 1 or type 2 is indicated as a sync sample; - * ! a SAP of type 3 is marked as a member of a sample group of type 'rap '; - * ! a SAP of type 4 is marked as a member of a sample group of type 'roll' where the value of the roll_distance field - * is greater than 0. - * For SAPs of type 5 and 6, no specific signalling in the ISO Base Media file format is supported. */ -typedef struct -{ - unsigned int reference_type : 1; /* 1: the reference is to a Segment Index Box - * 0: the reference is to media content - * For files based on the ISO Base Media file format, the reference is to a - * Movie Fragment Box. - * If a separate index segment is used, then entries with reference type 1 are - * in the index segment, and entries with reference type 0 are in the media file. */ - unsigned int reference_size : 31; /* the distance in bytes from the first byte of the referenced item to the first - * byte of the next referenced item, or in the case of the last entry, the end of - * the referenced material */ - uint32_t subsegment_duration; /* when the reference is to Segment Index Box, i.e. reference_type is equal to 1: - * this field carries the sum of the subsegment_duration fields in that box; - * when the reference is to a subsegment: - * this field carries the difference between the earliest presentation time of - * any access unit of the reference stream in the next subsegment (or the first - * subsegment of the next segment, if this is the last subsegment of the segment, - * or the end presentation time of the reference stream if this is the last - * subsegment of the stream) and the earliest presentation time of any access - * unit of the reference stream in the referenced subsegment; - * The duration is expressed in the timescale of the enclosing Segment Index Box. */ - unsigned int starts_with_SAP : 1; /* whether the referenced subsegments start with a SAP */ - unsigned int SAP_type : 3; /* a SAP type or the value 0 - * When starting with a SAP, the value 0 means a SAP may be of an unknown type. - * Otherwise, the value 0 means no information of SAPs is provided. */ - unsigned int SAP_delta_time : 28; /* TSAP of the first SAP, in decoding order, in the referenced subsegment for - * the reference stream - * If the referenced subsegments do not contain a SAP, SAP_delta_time is - * reserved with the value 0, otherwise SAP_delta_time is the difference between - * the earliest presentation time of the subsegment, and the TSAP. - * Note that this difference may be zero, in the case that the subsegment starts - * with a SAP. */ -} isom_sidx_referenced_item_t; - -typedef struct -{ - ISOM_FULLBOX_COMMON; - uint32_t reference_ID; /* the stream ID for the reference stream - * If this Segment Index box is referenced from a "parent" Segment Index box, the value - * of the value of reference_ID shall be the same as the value of reference_ID of the - * "parent" Segment Index Box. */ - uint32_t timescale; /* the timescale, in ticks per second, for the time and duration fields within this box - * It is recommended that this match the timescale of the reference stream or track. - * For files based on the ISO Base Media file format, that is the timescale field of - * the Media Header Box of the track. */ - /* version == 0: 64bits -> 32bits */ - uint64_t earliest_presentation_time; /* the earliest presentation time of any access unit in the reference stream - * in the first subsegment, in the timescale indicated in the timescale field */ - uint64_t first_offset; /* the distance in bytes, in the file containing media, from the anchor point, - * to the first byte of the indexed material */ - /* */ - uint16_t reserved; /* 0 */ - uint16_t reference_count; /* the number of referenced items */ - lsmash_entry_list_t *list; /* entry_count corresponds to reference_count. */ -} isom_sidx_t; - -/** **/ - -/* File */ -struct lsmash_file_tag -{ - ISOM_FULLBOX_COMMON; /* The 'size' field indicates total file size. - * The 'flags' field indicates file mode. */ - isom_ftyp_t *ftyp; /* File Type Box */ - lsmash_entry_list_t styp_list; /* Segment Type Box List */ - isom_moov_t *moov; /* Movie Box */ - lsmash_entry_list_t sidx_list; /* Segment Index Box List */ - lsmash_entry_list_t moof_list; /* Movie Fragment Box List */ - isom_mdat_t *mdat; /* Media Data Box */ - isom_free_t *free; /* Free Space Box */ - isom_meta_t *meta; /* Meta Box */ - isom_mfra_t *mfra; /* Movie Fragment Random Access Box */ - - lsmash_bs_t *bs; /* bytestream manager */ - isom_fragment_manager_t *fragment; /* movie fragment manager */ - lsmash_entry_list_t *print; - lsmash_entry_list_t *timeline; - 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. */ - uint32_t brand_count; - uint32_t *compatible_brands; /* the backup of the compatible brands in the File Type Box or the valid Segment Type Box */ - uint8_t bc_fclose; /* a flag for backward compatible file closing */ - /* flags for compatibility */ - uint8_t qt_compatible; /* compatibility with QuickTime file format */ - uint8_t isom_compatible; /* compatibility with ISO Base Media file format */ - uint8_t avc_extensions; /* compatibility with AVC extensions */ - uint8_t mp4_version1; /* compatibility with MP4 ver.1 file format */ - uint8_t mp4_version2; /* compatibility with MP4 ver.2 file format */ - uint8_t itunes_movie; /* compatibility with iTunes Movie */ - uint8_t max_3gpp_version; /* maximum 3GPP version */ - uint8_t max_isom_version; /* maximum ISO Base Media file format version */ - uint8_t min_isom_version; /* minimum ISO Base Media file format version */ - uint8_t forbid_tref; /* If set to 1, track reference is forbidden. */ - uint8_t undefined_64_ver; /* If set to 1, 64-bit version fields, e.g. duration, are undefined. */ - uint8_t allow_moof_base; /* If set to 1, default-base-is-moof is available for muxing. */ - uint8_t media_segment; /* If set to 1, this file is a media segment. */ -}; - -/* ROOT */ -struct lsmash_root_tag -{ - ISOM_FULLBOX_COMMON; /* The 'file' field contains the address of the current active file. */ - lsmash_entry_list_t file_list; /* the list of all files the ROOT contains */ -}; - -/** **/ - -/* Box types */ -#define ISOM_BOX_TYPE_ID32 lsmash_form_iso_box_type( LSMASH_4CC( 'I', 'D', '3', '2' ) ) -#define ISOM_BOX_TYPE_ALBM lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'l', 'b', 'm' ) ) -#define ISOM_BOX_TYPE_AUTH lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'u', 't', 'h' ) ) -#define ISOM_BOX_TYPE_BPCC lsmash_form_iso_box_type( LSMASH_4CC( 'b', 'p', 'c', 'c' ) ) -#define ISOM_BOX_TYPE_BUFF lsmash_form_iso_box_type( LSMASH_4CC( 'b', 'u', 'f', 'f' ) ) -#define ISOM_BOX_TYPE_BXML lsmash_form_iso_box_type( LSMASH_4CC( 'b', 'x', 'm', 'l' ) ) -#define ISOM_BOX_TYPE_CCID lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'c', 'i', 'd' ) ) -#define ISOM_BOX_TYPE_CDEF lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'd', 'e', 'f' ) ) -#define ISOM_BOX_TYPE_CLSF lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'l', 's', 'f' ) ) -#define ISOM_BOX_TYPE_CMAP lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'm', 'a', 'p' ) ) -#define ISOM_BOX_TYPE_CO64 lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'o', '6', '4' ) ) -#define ISOM_BOX_TYPE_COLR lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'o', 'l', 'r' ) ) -#define ISOM_BOX_TYPE_CPRT lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'p', 'r', 't' ) ) -#define ISOM_BOX_TYPE_CSLG lsmash_form_iso_box_type( LSMASH_4CC( 'c', 's', 'l', 'g' ) ) -#define ISOM_BOX_TYPE_CTTS lsmash_form_iso_box_type( LSMASH_4CC( 'c', 't', 't', 's' ) ) -#define ISOM_BOX_TYPE_CVRU lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'v', 'r', 'u' ) ) -#define ISOM_BOX_TYPE_DCFD lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'c', 'f', 'D' ) ) -#define ISOM_BOX_TYPE_DINF lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'i', 'n', 'f' ) ) -#define ISOM_BOX_TYPE_DREF lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'r', 'e', 'f' ) ) -#define ISOM_BOX_TYPE_DSCP lsmash_form_iso_box_type( LSMASH_4CC( 'd', 's', 'c', 'p' ) ) -#define ISOM_BOX_TYPE_DSGD lsmash_form_iso_box_type( LSMASH_4CC( 'd', 's', 'g', 'd' ) ) -#define ISOM_BOX_TYPE_DSTG lsmash_form_iso_box_type( LSMASH_4CC( 'd', 's', 't', 'g' ) ) -#define ISOM_BOX_TYPE_EDTS lsmash_form_iso_box_type( LSMASH_4CC( 'e', 'd', 't', 's' ) ) -#define ISOM_BOX_TYPE_ELST lsmash_form_iso_box_type( LSMASH_4CC( 'e', 'l', 's', 't' ) ) -#define ISOM_BOX_TYPE_FECI lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'e', 'c', 'i' ) ) -#define ISOM_BOX_TYPE_FECR lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'e', 'c', 'r' ) ) -#define ISOM_BOX_TYPE_FIIN lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'i', 'i', 'n' ) ) -#define ISOM_BOX_TYPE_FIRE lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'i', 'r', 'e' ) ) -#define ISOM_BOX_TYPE_FPAR lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'p', 'a', 'r' ) ) -#define ISOM_BOX_TYPE_FREE lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'r', 'e', 'e' ) ) -#define ISOM_BOX_TYPE_FRMA lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'r', 'm', 'a' ) ) -#define ISOM_BOX_TYPE_FTYP lsmash_form_iso_box_type( LSMASH_4CC( 'f', 't', 'y', 'p' ) ) -#define ISOM_BOX_TYPE_GITN lsmash_form_iso_box_type( LSMASH_4CC( 'g', 'i', 't', 'n' ) ) -#define ISOM_BOX_TYPE_GNRE lsmash_form_iso_box_type( LSMASH_4CC( 'g', 'n', 'r', 'e' ) ) -#define ISOM_BOX_TYPE_GRPI lsmash_form_iso_box_type( LSMASH_4CC( 'g', 'r', 'p', 'i' ) ) -#define ISOM_BOX_TYPE_HDLR lsmash_form_iso_box_type( LSMASH_4CC( 'h', 'd', 'l', 'r' ) ) -#define ISOM_BOX_TYPE_HMHD lsmash_form_iso_box_type( LSMASH_4CC( 'h', 'm', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_ICNU lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'c', 'n', 'u' ) ) -#define ISOM_BOX_TYPE_IDAT lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'd', 'a', 't' ) ) -#define ISOM_BOX_TYPE_IHDR lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'h', 'd', 'r' ) ) -#define ISOM_BOX_TYPE_IINF lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'i', 'n', 'f' ) ) -#define ISOM_BOX_TYPE_ILOC lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'l', 'o', 'c' ) ) -#define ISOM_BOX_TYPE_IMIF lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'm', 'i', 'f' ) ) -#define ISOM_BOX_TYPE_INFU lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'n', 'f', 'u' ) ) -#define ISOM_BOX_TYPE_IODS lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'o', 'd', 's' ) ) -#define ISOM_BOX_TYPE_IPHD lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'p', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_IPMC lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'p', 'm', 'c' ) ) -#define ISOM_BOX_TYPE_IPRO lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'p', 'r', 'o' ) ) -#define ISOM_BOX_TYPE_IREF lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'r', 'e', 'f' ) ) -#define ISOM_BOX_TYPE_JP lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', ' ', ' ' ) ) -#define ISOM_BOX_TYPE_JP2C lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', '2', 'c' ) ) -#define ISOM_BOX_TYPE_JP2H lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', '2', 'h' ) ) -#define ISOM_BOX_TYPE_JP2I lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', '2', 'i' ) ) -#define ISOM_BOX_TYPE_KYWD lsmash_form_iso_box_type( LSMASH_4CC( 'k', 'y', 'w', 'd' ) ) -#define ISOM_BOX_TYPE_LOCI lsmash_form_iso_box_type( LSMASH_4CC( 'l', 'o', 'c', 'i' ) ) -#define ISOM_BOX_TYPE_LRCU lsmash_form_iso_box_type( LSMASH_4CC( 'l', 'r', 'c', 'u' ) ) -#define ISOM_BOX_TYPE_MDAT lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'a', 't' ) ) -#define ISOM_BOX_TYPE_MDHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_MDIA lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'i', 'a' ) ) -#define ISOM_BOX_TYPE_MDRI lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'r', 'i' ) ) -#define ISOM_BOX_TYPE_MECO lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'c', 'o' ) ) -#define ISOM_BOX_TYPE_MEHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_M7HD lsmash_form_iso_box_type( LSMASH_4CC( 'm', '7', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_MERE lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'r', 'e' ) ) -#define ISOM_BOX_TYPE_META lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 't', 'a' ) ) -#define ISOM_BOX_TYPE_MFHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'f', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_MFRA lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'f', 'r', 'a' ) ) -#define ISOM_BOX_TYPE_MFRO lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'f', 'r', 'o' ) ) -#define ISOM_BOX_TYPE_MINF lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'i', 'n', 'f' ) ) -#define ISOM_BOX_TYPE_MJHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'j', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_MOOF lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'o', 'o', 'f' ) ) -#define ISOM_BOX_TYPE_MOOV lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'o', 'o', 'v' ) ) -#define ISOM_BOX_TYPE_MVCG lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'c', 'g' ) ) -#define ISOM_BOX_TYPE_MVCI lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'c', 'i' ) ) -#define ISOM_BOX_TYPE_MVEX lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'e', 'x' ) ) -#define ISOM_BOX_TYPE_MVHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_MVRA lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'r', 'a' ) ) -#define ISOM_BOX_TYPE_NMHD lsmash_form_iso_box_type( LSMASH_4CC( 'n', 'm', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_OCHD lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'c', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_ODAF lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'a', 'f' ) ) -#define ISOM_BOX_TYPE_ODDA lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'd', 'a' ) ) -#define ISOM_BOX_TYPE_ODHD lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_ODHE lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'h', 'e' ) ) -#define ISOM_BOX_TYPE_ODRB lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'r', 'b' ) ) -#define ISOM_BOX_TYPE_ODRM lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'r', 'm' ) ) -#define ISOM_BOX_TYPE_ODTT lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 't', 't' ) ) -#define ISOM_BOX_TYPE_OHDR lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'h', 'd', 'r' ) ) -#define ISOM_BOX_TYPE_PADB lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'a', 'd', 'b' ) ) -#define ISOM_BOX_TYPE_PAEN lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'a', 'e', 'n' ) ) -#define ISOM_BOX_TYPE_PCLR lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'c', 'l', 'r' ) ) -#define ISOM_BOX_TYPE_PDIN lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'd', 'i', 'n' ) ) -#define ISOM_BOX_TYPE_PERF lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'e', 'r', 'f' ) ) -#define ISOM_BOX_TYPE_PITM lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'i', 't', 'm' ) ) -#define ISOM_BOX_TYPE_RES lsmash_form_iso_box_type( LSMASH_4CC( 'r', 'e', 's', ' ' ) ) -#define ISOM_BOX_TYPE_RESC lsmash_form_iso_box_type( LSMASH_4CC( 'r', 'e', 's', 'c' ) ) -#define ISOM_BOX_TYPE_RESD lsmash_form_iso_box_type( LSMASH_4CC( 'r', 'e', 's', 'd' ) ) -#define ISOM_BOX_TYPE_RTNG lsmash_form_iso_box_type( LSMASH_4CC( 'r', 't', 'n', 'g' ) ) -#define ISOM_BOX_TYPE_SBGP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'b', 'g', 'p' ) ) -#define ISOM_BOX_TYPE_SCHI lsmash_form_iso_box_type( LSMASH_4CC( 's', 'c', 'h', 'i' ) ) -#define ISOM_BOX_TYPE_SCHM lsmash_form_iso_box_type( LSMASH_4CC( 's', 'c', 'h', 'm' ) ) -#define ISOM_BOX_TYPE_SDEP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 'e', 'p' ) ) -#define ISOM_BOX_TYPE_SDHD lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_SDTP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 't', 'p' ) ) -#define ISOM_BOX_TYPE_SDVP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 'v', 'p' ) ) -#define ISOM_BOX_TYPE_SEGR lsmash_form_iso_box_type( LSMASH_4CC( 's', 'e', 'g', 'r' ) ) -#define ISOM_BOX_TYPE_SGPD lsmash_form_iso_box_type( LSMASH_4CC( 's', 'g', 'p', 'd' ) ) -#define ISOM_BOX_TYPE_SIDX lsmash_form_iso_box_type( LSMASH_4CC( 's', 'i', 'd', 'x' ) ) -#define ISOM_BOX_TYPE_SINF lsmash_form_iso_box_type( LSMASH_4CC( 's', 'i', 'n', 'f' ) ) -#define ISOM_BOX_TYPE_SKIP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'k', 'i', 'p' ) ) -#define ISOM_BOX_TYPE_SMHD lsmash_form_iso_box_type( LSMASH_4CC( 's', 'm', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_SRMB lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'm', 'b' ) ) -#define ISOM_BOX_TYPE_SRMC lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'm', 'c' ) ) -#define ISOM_BOX_TYPE_SRPP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'p', 'p' ) ) -#define ISOM_BOX_TYPE_STBL lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'b', 'l' ) ) -#define ISOM_BOX_TYPE_STCO lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'c', 'o' ) ) -#define ISOM_BOX_TYPE_STDP lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'd', 'p' ) ) -#define ISOM_BOX_TYPE_STSC lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'c' ) ) -#define ISOM_BOX_TYPE_STSD lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'd' ) ) -#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_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' ) ) -#define ISOM_BOX_TYPE_SUBS lsmash_form_iso_box_type( LSMASH_4CC( 's', 'u', 'b', 's' ) ) -#define ISOM_BOX_TYPE_SWTC lsmash_form_iso_box_type( LSMASH_4CC( 's', 'w', 't', 'c' ) ) -#define ISOM_BOX_TYPE_TFHD lsmash_form_iso_box_type( LSMASH_4CC( 't', 'f', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_TFDT lsmash_form_iso_box_type( LSMASH_4CC( 't', 'f', 'd', 't' ) ) -#define ISOM_BOX_TYPE_TFRA lsmash_form_iso_box_type( LSMASH_4CC( 't', 'f', 'r', 'a' ) ) -#define ISOM_BOX_TYPE_TIBR lsmash_form_iso_box_type( LSMASH_4CC( 't', 'i', 'b', 'r' ) ) -#define ISOM_BOX_TYPE_TIRI lsmash_form_iso_box_type( LSMASH_4CC( 't', 'i', 'r', 'i' ) ) -#define ISOM_BOX_TYPE_TITL lsmash_form_iso_box_type( LSMASH_4CC( 't', 'i', 't', 'l' ) ) -#define ISOM_BOX_TYPE_TKHD lsmash_form_iso_box_type( LSMASH_4CC( 't', 'k', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_TRAF lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'a', 'f' ) ) -#define ISOM_BOX_TYPE_TRAK lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'a', 'k' ) ) -#define ISOM_BOX_TYPE_TREF lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'e', 'f' ) ) -#define ISOM_BOX_TYPE_TREX lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'e', 'x' ) ) -#define ISOM_BOX_TYPE_TRGR lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'g', 'r' ) ) -#define ISOM_BOX_TYPE_TRUN lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'u', 'n' ) ) -#define ISOM_BOX_TYPE_TSEL lsmash_form_iso_box_type( LSMASH_4CC( 't', 's', 'e', 'l' ) ) -#define ISOM_BOX_TYPE_UDTA lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'd', 't', 'a' ) ) -#define ISOM_BOX_TYPE_UINF lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'i', 'n', 'f' ) ) -#define ISOM_BOX_TYPE_ULST lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'l', 's', 't' ) ) -#define ISOM_BOX_TYPE_URL lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'r', 'l', ' ' ) ) -#define ISOM_BOX_TYPE_URN lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'r', 'n', ' ' ) ) -#define ISOM_BOX_TYPE_UUID lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'u', 'i', 'd' ) ) -#define ISOM_BOX_TYPE_VMHD lsmash_form_iso_box_type( LSMASH_4CC( 'v', 'm', 'h', 'd' ) ) -#define ISOM_BOX_TYPE_VWDI lsmash_form_iso_box_type( LSMASH_4CC( 'v', 'w', 'd', 'i' ) ) -#define ISOM_BOX_TYPE_XML lsmash_form_iso_box_type( LSMASH_4CC( 'x', 'm', 'l', ' ' ) ) -#define ISOM_BOX_TYPE_YRRC lsmash_form_iso_box_type( LSMASH_4CC( 'y', 'r', 'r', 'c' ) ) - -#define ISOM_BOX_TYPE_BTRT lsmash_form_iso_box_type( LSMASH_4CC( 'b', 't', 'r', 't' ) ) -#define ISOM_BOX_TYPE_CLAP lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'l', 'a', 'p' ) ) -#define ISOM_BOX_TYPE_PASP lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'a', 's', 'p' ) ) -#define ISOM_BOX_TYPE_SRAT lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'a', 't' ) ) -#define ISOM_BOX_TYPE_STSL lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'l' ) ) - -#define ISOM_BOX_TYPE_FTAB lsmash_form_iso_box_type( LSMASH_4CC( 'f', 't', 'a', 'b' ) ) - -/* iTunes Metadata */ -#define ISOM_BOX_TYPE_DATA lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'a', 't', 'a' ) ) -#define ISOM_BOX_TYPE_ILST lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'l', 's', 't' ) ) -#define ISOM_BOX_TYPE_MEAN lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'a', 'n' ) ) -#define ISOM_BOX_TYPE_NAME lsmash_form_iso_box_type( LSMASH_4CC( 'n', 'a', 'm', 'e' ) ) - -/* Tyrant extension */ -#define ISOM_BOX_TYPE_CHPL lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'h', 'p', 'l' ) ) - -/* Decoder Specific Info */ -#define ISOM_BOX_TYPE_ALAC lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'l', 'a', 'c' ) ) -#define ISOM_BOX_TYPE_AVCC lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'v', 'c', 'C' ) ) -#define ISOM_BOX_TYPE_DAC3 lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'a', 'c', '3' ) ) -#define ISOM_BOX_TYPE_DAMR lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'a', 'm', 'r' ) ) -#define ISOM_BOX_TYPE_DDTS lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'd', 't', 's' ) ) -#define ISOM_BOX_TYPE_DEC3 lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'e', 'c', '3' ) ) -#define ISOM_BOX_TYPE_DVC1 lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'v', 'c', '1' ) ) -#define ISOM_BOX_TYPE_ESDS lsmash_form_iso_box_type( LSMASH_4CC( 'e', 's', 'd', 's' ) ) -#define ISOM_BOX_TYPE_HVCC lsmash_form_iso_box_type( LSMASH_4CC( 'h', 'v', 'c', 'C' ) ) - -#define QT_BOX_TYPE_ALLF lsmash_form_qtff_box_type( LSMASH_4CC( 'A', 'l', 'l', 'F' ) ) -#define QT_BOX_TYPE_CLEF lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'l', 'e', 'f' ) ) -#define QT_BOX_TYPE_CLIP lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'l', 'i', 'p' ) ) -#define QT_BOX_TYPE_CRGN lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'r', 'g', 'n' ) ) -#define QT_BOX_TYPE_CTAB lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 't', 'a', 'b' ) ) -#define QT_BOX_TYPE_ENOF lsmash_form_qtff_box_type( LSMASH_4CC( 'e', 'n', 'o', 'f' ) ) -#define QT_BOX_TYPE_GMHD lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'm', 'h', 'd' ) ) -#define QT_BOX_TYPE_GMIN lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'm', 'i', 'n' ) ) -#define QT_BOX_TYPE_ILST lsmash_form_qtff_box_type( LSMASH_4CC( 'i', 'l', 's', 't' ) ) -#define QT_BOX_TYPE_IMAP lsmash_form_qtff_box_type( LSMASH_4CC( 'i', 'm', 'a', 'p' ) ) -#define QT_BOX_TYPE_KEYS lsmash_form_qtff_box_type( LSMASH_4CC( 'k', 'e', 'y', 's' ) ) -#define QT_BOX_TYPE_KMAT lsmash_form_qtff_box_type( LSMASH_4CC( 'k', 'm', 'a', 't' ) ) -#define QT_BOX_TYPE_LOAD lsmash_form_qtff_box_type( LSMASH_4CC( 'l', 'o', 'a', 'd' ) ) -#define QT_BOX_TYPE_LOOP lsmash_form_qtff_box_type( LSMASH_4CC( 'L', 'O', 'O', 'P' ) ) -#define QT_BOX_TYPE_MATT lsmash_form_qtff_box_type( LSMASH_4CC( 'm', 'a', 't', 't' ) ) -#define QT_BOX_TYPE_META lsmash_form_qtff_box_type( LSMASH_4CC( 'm', 'e', 't', 'a' ) ) -#define QT_BOX_TYPE_PNOT lsmash_form_qtff_box_type( LSMASH_4CC( 'p', 'n', 'o', 't' ) ) -#define QT_BOX_TYPE_PROF lsmash_form_qtff_box_type( LSMASH_4CC( 'p', 'r', 'o', 'f' ) ) -#define QT_BOX_TYPE_SELO lsmash_form_qtff_box_type( LSMASH_4CC( 'S', 'e', 'l', 'O' ) ) -#define QT_BOX_TYPE_STPS lsmash_form_qtff_box_type( LSMASH_4CC( 's', 't', 'p', 's' ) ) -#define QT_BOX_TYPE_TAPT lsmash_form_qtff_box_type( LSMASH_4CC( 't', 'a', 'p', 't' ) ) -#define QT_BOX_TYPE_TEXT lsmash_form_qtff_box_type( LSMASH_4CC( 't', 'e', 'x', 't' ) ) -#define QT_BOX_TYPE_WLOC lsmash_form_qtff_box_type( LSMASH_4CC( 'W', 'L', 'O', 'C' ) ) - -#define QT_BOX_TYPE_CHAN lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'h', 'a', 'n' ) ) -#define QT_BOX_TYPE_COLR lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'o', 'l', 'r' ) ) -#define QT_BOX_TYPE_CSPC lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 's', 'p', 'c' ) ) -#define QT_BOX_TYPE_ENDA lsmash_form_qtff_box_type( LSMASH_4CC( 'e', 'n', 'd', 'a' ) ) -#define QT_BOX_TYPE_FIEL lsmash_form_qtff_box_type( LSMASH_4CC( 'f', 'i', 'e', 'l' ) ) -#define QT_BOX_TYPE_FRMA lsmash_form_qtff_box_type( LSMASH_4CC( 'f', 'r', 'm', 'a' ) ) -#define QT_BOX_TYPE_GAMA lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'a', 'm', 'a' ) ) -#define QT_BOX_TYPE_SGBT lsmash_form_qtff_box_type( LSMASH_4CC( 's', 'g', 'b', 't' ) ) -#define QT_BOX_TYPE_WAVE lsmash_form_qtff_box_type( LSMASH_4CC( 'w', 'a', 'v', 'e' ) ) -#define QT_BOX_TYPE_TERMINATOR lsmash_form_qtff_box_type( 0x00000000 ) - -/* Decoder Specific Info */ -#define QT_BOX_TYPE_ALAC lsmash_form_qtff_box_type( LSMASH_4CC( 'a', 'l', 'a', 'c' ) ) -#define QT_BOX_TYPE_ESDS lsmash_form_qtff_box_type( LSMASH_4CC( 'e', 's', 'd', 's' ) ) -#define QT_BOX_TYPE_GLBL lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'l', 'b', 'l' ) ) -#define QT_BOX_TYPE_MP4A lsmash_form_qtff_box_type( LSMASH_4CC( 'm', 'p', '4', 'a' ) ) - -/* Pre-defined precedence */ -#define LSMASH_BOX_PRECEDENCE_ISOM_FTYP (LSMASH_BOX_PRECEDENCE_H - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_STYP (LSMASH_BOX_PRECEDENCE_H - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_SIDX (LSMASH_BOX_PRECEDENCE_N + 1 * LSMASH_BOX_PRECEDENCE_S) /* shall be placed before any 'moof' of the documented subsegments */ -#define LSMASH_BOX_PRECEDENCE_ISOM_MOOV (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MVHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_IODS (LSMASH_BOX_PRECEDENCE_HM - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TRAK (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TKHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_TAPT (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_CLEF (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_PROF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_ENOF (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_EDTS (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_ELST (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TREF (LSMASH_BOX_PRECEDENCE_N - 3 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TREF_TYPE (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MDIA (LSMASH_BOX_PRECEDENCE_N - 4 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MDHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_HDLR (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MINF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_VMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_SMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_HMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_NMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_GMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_GMIN (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_TEXT (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_DINF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_DREF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_URL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_STBL (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_STSD (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_GLBL (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_ESDS (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_ESDS (LSMASH_BOX_PRECEDENCE_HM - 1 * LSMASH_BOX_PRECEDENCE_S) /* preceded by 'frma' and 'mp4a' */ -#define LSMASH_BOX_PRECEDENCE_ISOM_BTRT (LSMASH_BOX_PRECEDENCE_HM - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_COLR (LSMASH_BOX_PRECEDENCE_LP + 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_COLR (LSMASH_BOX_PRECEDENCE_LP + 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_GAMA (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_FIEL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_CSPC (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_SGBT (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) /* 'v216' specific */ -#define LSMASH_BOX_PRECEDENCE_ISOM_CLAP (LSMASH_BOX_PRECEDENCE_LP + 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_PASP (LSMASH_BOX_PRECEDENCE_LP - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_STSL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_CHAN (LSMASH_BOX_PRECEDENCE_LP - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_CHAN (LSMASH_BOX_PRECEDENCE_LP - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_WAVE (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_FRMA (LSMASH_BOX_PRECEDENCE_HM + 1 * LSMASH_BOX_PRECEDENCE_S) /* precede any as much as possible */ -#define LSMASH_BOX_PRECEDENCE_QTFF_ENDA (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_MP4A (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_TERMINATOR (LSMASH_BOX_PRECEDENCE_L - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_SRAT (LSMASH_BOX_PRECEDENCE_LP - 1 * LSMASH_BOX_PRECEDENCE_S) /* place at the end for maximum compatibility */ -#define LSMASH_BOX_PRECEDENCE_ISOM_FTAB (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_STTS (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_CTTS (LSMASH_BOX_PRECEDENCE_N - 4 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_CSLG (LSMASH_BOX_PRECEDENCE_N - 6 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_STSS (LSMASH_BOX_PRECEDENCE_N - 8 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_STPS (LSMASH_BOX_PRECEDENCE_N - 10 * LSMASH_BOX_PRECEDENCE_S) -#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_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) -#define LSMASH_BOX_PRECEDENCE_ISOM_SBGP (LSMASH_BOX_PRECEDENCE_N - 22 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_UDTA (LSMASH_BOX_PRECEDENCE_N - 5 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MEAN (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_NAME (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_DATA (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_KEYS (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_ILST (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_METAITEM (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_CHPL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_META (LSMASH_BOX_PRECEDENCE_N - 7 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_WLOC (LSMASH_BOX_PRECEDENCE_N - 8 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_LOOP (LSMASH_BOX_PRECEDENCE_N - 9 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_SELO (LSMASH_BOX_PRECEDENCE_N - 10 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_ALLF (LSMASH_BOX_PRECEDENCE_N - 11 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_CPRT (LSMASH_BOX_PRECEDENCE_N - 12 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_QTFF_CTAB (LSMASH_BOX_PRECEDENCE_N - 6 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MVEX (LSMASH_BOX_PRECEDENCE_N - 8 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MEHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TREX (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MOOF (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MFHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TRAF (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TFHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TFDT (LSMASH_BOX_PRECEDENCE_HM - 1 * LSMASH_BOX_PRECEDENCE_S) /* shall be positioned after 'tfhd' and before 'trun' */ -#define LSMASH_BOX_PRECEDENCE_ISOM_TRUN (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MFRA (LSMASH_BOX_PRECEDENCE_L - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_TFRA (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MFRO (LSMASH_BOX_PRECEDENCE_L - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_MDAT (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_FREE (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) -#define LSMASH_BOX_PRECEDENCE_ISOM_SKIP (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) - -/* Track reference types */ -typedef enum -{ - ISOM_TREF_TYPE_AVCP = LSMASH_4CC( 'a', 'v', 'c', 'p' ), /* AVC parameter set stream link */ - ISOM_TREF_TYPE_CDSC = LSMASH_4CC( 'c', 'd', 's', 'c' ), /* This track describes the referenced track. */ - ISOM_TREF_TYPE_DPND = LSMASH_4CC( 'd', 'p', 'n', 'd' ), /* This track has an MPEG-4 dependency on the referenced track. */ - ISOM_TREF_TYPE_HIND = LSMASH_4CC( 'h', 'i', 'n', 'd' ), /* Hint dependency */ - ISOM_TREF_TYPE_HINT = LSMASH_4CC( 'h', 'i', 'n', 't' ), /* Links hint track to original media track */ - ISOM_TREF_TYPE_IPIR = LSMASH_4CC( 'i', 'p', 'i', 'r' ), /* This track contains IPI declarations for the referenced track. */ - ISOM_TREF_TYPE_MPOD = LSMASH_4CC( 'm', 'p', 'o', 'd' ), /* This track is an OD track which uses the referenced track as an included elementary stream track. */ - ISOM_TREF_TYPE_SBAS = LSMASH_4CC( 's', 'b', 'a', 's' ), /* Scalable base */ - ISOM_TREF_TYPE_SCAL = LSMASH_4CC( 's', 'c', 'a', 'l' ), /* Scalable extraction */ - ISOM_TREF_TYPE_SWFR = LSMASH_4CC( 's', 'w', 'f', 'r' ), /* AVC Switch from */ - ISOM_TREF_TYPE_SWTO = LSMASH_4CC( 's', 'w', 't', 'o' ), /* AVC Switch to */ - ISOM_TREF_TYPE_SYNC = LSMASH_4CC( 's', 'y', 'n', 'c' ), /* This track uses the referenced track as its synchronization source. */ - ISOM_TREF_TYPE_VDEP = LSMASH_4CC( 'v', 'd', 'e', 'p' ), /* Auxiliary video depth */ - ISOM_TREF_TYPE_VPLX = LSMASH_4CC( 'v', 'p', 'l', 'x' ), /* Auxiliary video parallax */ - - QT_TREF_TYPE_CHAP = LSMASH_4CC( 'c', 'h', 'a', 'p' ), /* Chapter or scene list. Usually references a text track. */ - QT_TREF_TYPE_SCPT = LSMASH_4CC( 's', 'c', 'p', 't' ), /* Transcript. Usually references a text track. */ - QT_TREF_TYPE_SSRC = LSMASH_4CC( 's', 's', 'r', 'c' ), /* Nonprimary source. Indicates that the referenced track should send its data to this track, rather than presenting it. */ - QT_TREF_TYPE_TMCD = LSMASH_4CC( 't', 'm', 'c', 'd' ), /* Time code. Usually references a time code track. */ -} isom_track_reference_type; - -/* Handler types */ -enum isom_handler_type -{ - QT_HANDLER_TYPE_DATA = LSMASH_4CC( 'd', 'h', 'l', 'r' ), - QT_HANDLER_TYPE_MEDIA = LSMASH_4CC( 'm', 'h', 'l', 'r' ), -}; - -enum isom_meta_type -{ - ISOM_META_HANDLER_TYPE_ITUNES_METADATA = LSMASH_4CC( 'm', 'd', 'i', 'r' ), -}; - -/* Data reference types */ -enum isom_data_reference_type -{ - ISOM_REFERENCE_HANDLER_TYPE_URL = LSMASH_4CC( 'u', 'r', 'l', ' ' ), - ISOM_REFERENCE_HANDLER_TYPE_URN = LSMASH_4CC( 'u', 'r', 'n', ' ' ), - - QT_REFERENCE_HANDLER_TYPE_ALIAS = LSMASH_4CC( 'a', 'l', 'i', 's' ), - QT_REFERENCE_HANDLER_TYPE_RESOURCE = LSMASH_4CC( 'r', 's', 'r', 'c' ), - QT_REFERENCE_HANDLER_TYPE_URL = LSMASH_4CC( 'u', 'r', 'l', ' ' ), -}; - -/* Lanuage codes */ -typedef struct -{ - uint16_t mac_value; - uint16_t iso_name; -} isom_language_t; - -static const isom_language_t isom_languages[] = -{ - { 0, ISOM_LANGUAGE_CODE_ENGLISH }, - { 1, ISOM_LANGUAGE_CODE_FRENCH }, - { 2, ISOM_LANGUAGE_CODE_GERMAN }, - { 3, ISOM_LANGUAGE_CODE_ITALIAN }, - { 4, ISOM_LANGUAGE_CODE_DUTCH_M }, - { 5, ISOM_LANGUAGE_CODE_SWEDISH }, - { 6, ISOM_LANGUAGE_CODE_SPANISH }, - { 7, ISOM_LANGUAGE_CODE_DANISH }, - { 8, ISOM_LANGUAGE_CODE_PORTUGUESE }, - { 9, ISOM_LANGUAGE_CODE_NORWEGIAN }, - { 10, ISOM_LANGUAGE_CODE_HEBREW }, - { 11, ISOM_LANGUAGE_CODE_JAPANESE }, - { 12, ISOM_LANGUAGE_CODE_ARABIC }, - { 13, ISOM_LANGUAGE_CODE_FINNISH }, - { 14, ISOM_LANGUAGE_CODE_GREEK }, - { 15, ISOM_LANGUAGE_CODE_ICELANDIC }, - { 16, ISOM_LANGUAGE_CODE_MALTESE }, - { 17, ISOM_LANGUAGE_CODE_TURKISH }, - { 18, ISOM_LANGUAGE_CODE_CROATIAN }, - { 19, ISOM_LANGUAGE_CODE_CHINESE }, - { 20, ISOM_LANGUAGE_CODE_URDU }, - { 21, ISOM_LANGUAGE_CODE_HINDI }, - { 22, ISOM_LANGUAGE_CODE_THAI }, - { 23, ISOM_LANGUAGE_CODE_KOREAN }, - { 24, ISOM_LANGUAGE_CODE_LITHUANIAN }, - { 25, ISOM_LANGUAGE_CODE_POLISH }, - { 26, ISOM_LANGUAGE_CODE_HUNGARIAN }, - { 27, ISOM_LANGUAGE_CODE_ESTONIAN }, - { 28, ISOM_LANGUAGE_CODE_LATVIAN }, - { 29, ISOM_LANGUAGE_CODE_SAMI }, - { 30, ISOM_LANGUAGE_CODE_FAROESE }, - { 32, ISOM_LANGUAGE_CODE_RUSSIAN }, - { 33, ISOM_LANGUAGE_CODE_CHINESE }, - { 34, ISOM_LANGUAGE_CODE_DUTCH }, - { 35, ISOM_LANGUAGE_CODE_IRISH }, - { 36, ISOM_LANGUAGE_CODE_ALBANIAN }, - { 37, ISOM_LANGUAGE_CODE_ROMANIAN }, - { 38, ISOM_LANGUAGE_CODE_CZECH }, - { 39, ISOM_LANGUAGE_CODE_SLOVAK }, - { 40, ISOM_LANGUAGE_CODE_SLOVENIA }, - { 41, ISOM_LANGUAGE_CODE_YIDDISH }, - { 42, ISOM_LANGUAGE_CODE_SERBIAN }, - { 43, ISOM_LANGUAGE_CODE_MACEDONIAN }, - { 44, ISOM_LANGUAGE_CODE_BULGARIAN }, - { 45, ISOM_LANGUAGE_CODE_UKRAINIAN }, - { 46, ISOM_LANGUAGE_CODE_BELARUSIAN }, - { 47, ISOM_LANGUAGE_CODE_UZBEK }, - { 48, ISOM_LANGUAGE_CODE_KAZAKH }, - { 49, ISOM_LANGUAGE_CODE_AZERBAIJANI }, - { 51, ISOM_LANGUAGE_CODE_ARMENIAN }, - { 52, ISOM_LANGUAGE_CODE_GEORGIAN }, - { 53, ISOM_LANGUAGE_CODE_MOLDAVIAN }, - { 54, ISOM_LANGUAGE_CODE_KIRGHIZ }, - { 55, ISOM_LANGUAGE_CODE_TAJIK }, - { 56, ISOM_LANGUAGE_CODE_TURKMEN }, - { 57, ISOM_LANGUAGE_CODE_MONGOLIAN }, - { 59, ISOM_LANGUAGE_CODE_PASHTO }, - { 60, ISOM_LANGUAGE_CODE_KURDISH }, - { 61, ISOM_LANGUAGE_CODE_KASHMIRI }, - { 62, ISOM_LANGUAGE_CODE_SINDHI }, - { 63, ISOM_LANGUAGE_CODE_TIBETAN }, - { 64, ISOM_LANGUAGE_CODE_NEPALI }, - { 65, ISOM_LANGUAGE_CODE_SANSKRIT }, - { 66, ISOM_LANGUAGE_CODE_MARATHI }, - { 67, ISOM_LANGUAGE_CODE_BENGALI }, - { 68, ISOM_LANGUAGE_CODE_ASSAMESE }, - { 69, ISOM_LANGUAGE_CODE_GUJARATI }, - { 70, ISOM_LANGUAGE_CODE_PUNJABI }, - { 71, ISOM_LANGUAGE_CODE_ORIYA }, - { 72, ISOM_LANGUAGE_CODE_MALAYALAM }, - { 73, ISOM_LANGUAGE_CODE_KANNADA }, - { 74, ISOM_LANGUAGE_CODE_TAMIL }, - { 75, ISOM_LANGUAGE_CODE_TELUGU }, - { 76, ISOM_LANGUAGE_CODE_SINHALESE }, - { 77, ISOM_LANGUAGE_CODE_BURMESE }, - { 78, ISOM_LANGUAGE_CODE_KHMER }, - { 79, ISOM_LANGUAGE_CODE_LAO }, - { 80, ISOM_LANGUAGE_CODE_VIETNAMESE }, - { 81, ISOM_LANGUAGE_CODE_INDONESIAN }, - { 82, ISOM_LANGUAGE_CODE_TAGALOG }, - { 83, ISOM_LANGUAGE_CODE_MALAY_ROMAN }, - { 84, ISOM_LANGUAGE_CODE_MAYAY_ARABIC }, - { 85, ISOM_LANGUAGE_CODE_AMHARIC }, - { 87, ISOM_LANGUAGE_CODE_OROMO }, - { 88, ISOM_LANGUAGE_CODE_SOMALI }, - { 89, ISOM_LANGUAGE_CODE_SWAHILI }, - { 90, ISOM_LANGUAGE_CODE_KINYARWANDA }, - { 91, ISOM_LANGUAGE_CODE_RUNDI }, - { 92, ISOM_LANGUAGE_CODE_CHEWA }, - { 93, ISOM_LANGUAGE_CODE_MALAGASY }, - { 94, ISOM_LANGUAGE_CODE_ESPERANTO }, - { 128, ISOM_LANGUAGE_CODE_WELSH }, - { 129, ISOM_LANGUAGE_CODE_BASQUE }, - { 130, ISOM_LANGUAGE_CODE_CATALAN }, - { 131, ISOM_LANGUAGE_CODE_LATIN }, - { 132, ISOM_LANGUAGE_CODE_QUECHUA }, - { 133, ISOM_LANGUAGE_CODE_GUARANI }, - { 134, ISOM_LANGUAGE_CODE_AYMARA }, - { 135, ISOM_LANGUAGE_CODE_TATAR }, - { 136, ISOM_LANGUAGE_CODE_UIGHUR }, - { 137, ISOM_LANGUAGE_CODE_DZONGKHA }, - { 138, ISOM_LANGUAGE_CODE_JAVANESE }, - { UINT16_MAX, 0 } -}; - -/* Color parameters */ -enum isom_color_patameter_type -{ - ISOM_COLOR_PARAMETER_TYPE_NCLX = LSMASH_4CC( 'n', 'c', 'l', 'x' ), /* on-screen colours */ - ISOM_COLOR_PARAMETER_TYPE_RICC = LSMASH_4CC( 'r', 'I', 'C', 'C' ), /* restricted ICC profile */ - ISOM_COLOR_PARAMETER_TYPE_PROF = LSMASH_4CC( 'p', 'r', 'o', 'f' ), /* unrestricted ICC profile */ - - 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 */ -}; - -/* Sample grouping types */ -typedef enum -{ - ISOM_GROUP_TYPE_3GAG = LSMASH_4CC( '3', 'g', 'a', 'g' ), /* Text track3GPP PSS Annex G video buffer parameters */ - ISOM_GROUP_TYPE_ALST = LSMASH_4CC( 'a', 'l', 's', 't' ), /* Alternative startup sequence */ - ISOM_GROUP_TYPE_AVCB = LSMASH_4CC( 'a', 'v', 'c', 'b' ), /* AVC HRD parameters */ - ISOM_GROUP_TYPE_AVLL = LSMASH_4CC( 'a', 'v', 'l', 'l' ), /* AVC Layer */ - ISOM_GROUP_TYPE_AVSS = LSMASH_4CC( 'a', 'v', 's', 's' ), /* AVC Sub Sequence */ - ISOM_GROUP_TYPE_DTRT = LSMASH_4CC( 'd', 't', 'r', 't' ), /* Decode re-timing */ - ISOM_GROUP_TYPE_MVIF = LSMASH_4CC( 'm', 'v', 'i', 'f' ), /* MVC Scalability Information */ - ISOM_GROUP_TYPE_RAP = LSMASH_4CC( 'r', 'a', 'p', ' ' ), /* Random Access Point */ - ISOM_GROUP_TYPE_RASH = LSMASH_4CC( 'r', 'a', 's', 'h' ), /* Rate Share */ - ISOM_GROUP_TYPE_ROLL = LSMASH_4CC( 'r', 'o', 'l', 'l' ), /* Random Access Recovery Point */ - ISOM_GROUP_TYPE_SCIF = LSMASH_4CC( 's', 'c', 'i', 'f' ), /* SVC Scalability Information */ - ISOM_GROUP_TYPE_SCNM = LSMASH_4CC( 's', 'c', 'n', 'm' ), /* AVC/SVC/MVC map groups */ - ISOM_GROUP_TYPE_VIPR = LSMASH_4CC( 'v', 'i', 'p', 'r' ), /* View priority */ -} isom_grouping_type; - -int isom_is_fullbox( void *box ); -int isom_is_lpcm_audio( void *box ); -int isom_is_qt_audio( lsmash_codec_type_t type ); -int isom_is_uncompressed_ycbcr( lsmash_codec_type_t type ); -int isom_is_waveform_audio( lsmash_box_type_t type ); - -void isom_init_box_common( void *box, void *parent, lsmash_box_type_t box_type, uint64_t precedence, void *destructor, void *updater ); -size_t isom_skip_box_common( uint8_t **p_data ); - -void isom_bs_put_basebox_common( lsmash_bs_t *bs, isom_box_t *box ); -void isom_bs_put_fullbox_common( lsmash_bs_t *bs, isom_box_t *box ); -void isom_bs_put_box_common( lsmash_bs_t *bs, void *box ); - -int isom_check_compatibility( lsmash_file_t *file ); - -char *isom_4cc2str( uint32_t fourcc ); - -isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID ); -isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID ); -isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID ); -isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID ); -isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ); -isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ); -isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type ); -isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type ); - -isom_dcr_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size ); -void isom_remove_dcr_ps( isom_dcr_ps_entry_t *ps ); - -int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type ); -int isom_setup_iods( isom_moov_t *moov ); - -uint32_t isom_get_sample_count( isom_trak_t *trak ); -isom_sample_pool_t *isom_create_sample_pool( uint64_t size ); -int isom_update_sample_tables( isom_trak_t *trak, lsmash_sample_t *sample, uint32_t *samples_per_packet ); -int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet ); - -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 ); - -int isom_update_tkhd_duration( isom_trak_t *trak ); -int isom_update_bitrate_description( isom_mdia_t *mdia ); -int isom_complement_data_reference( isom_minf_t *minf ); -int isom_establish_movie( lsmash_file_t *file ); -int isom_convert_stco_to_co64( isom_stbl_t *stbl ); -int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment ); -int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ); - -int isom_rearrange_boxes -( - lsmash_file_t *file, - lsmash_adhoc_remux_t *remux, - uint8_t *buf[2], - size_t read_num, - size_t size, - uint64_t read_pos, - uint64_t write_pos, - uint64_t file_size -); - -lsmash_file_t *isom_add_file( lsmash_root_t *root ); -int isom_add_ftyp( lsmash_file_t *file ); -int isom_add_moov( lsmash_file_t *file ); -int isom_add_mvhd( isom_moov_t *moov ); -int isom_add_iods( isom_moov_t *moov ); -int isom_add_ctab( void *parent_box ); -isom_trak_t *isom_add_trak( isom_moov_t *moov ); -int isom_add_tkhd( isom_trak_t *trak ); -int isom_add_tapt( isom_trak_t *trak ); -int isom_add_clef( isom_tapt_t *tapt ); -int isom_add_prof( isom_tapt_t *tapt ); -int isom_add_enof( isom_tapt_t *tapt ); -int isom_add_edts( isom_trak_t *trak ); -int isom_add_elst( isom_edts_t *edts ); -int isom_add_tref( isom_trak_t *trak ); -isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type ); -int isom_add_mdia( isom_trak_t *trak ); -int isom_add_mdhd( isom_mdia_t *mdia ); -int isom_add_hdlr( void *parent_box ); -int isom_add_minf( isom_mdia_t *mdia ); -int isom_add_vmhd( isom_minf_t *minf ); -int isom_add_smhd( isom_minf_t *minf ); -int isom_add_hmhd( isom_minf_t *minf ); -int isom_add_nmhd( isom_minf_t *minf ); -int isom_add_gmhd( isom_minf_t *minf ); -int isom_add_gmin( isom_gmhd_t *gmhd ); -int isom_add_text( isom_gmhd_t *gmhd ); -int isom_add_dinf( void *parent_box ); -int isom_add_dref( isom_dinf_t *dinf ); -isom_dref_entry_t *isom_add_dref_entry( isom_dref_t *dref ); -int isom_add_stbl( isom_minf_t *minf ); -int isom_add_stsd( isom_stbl_t *stbl ); -isom_visual_entry_t *isom_add_visual_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ); -isom_audio_entry_t *isom_add_audio_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ); -isom_qt_text_entry_t *isom_add_qt_text_description( isom_stsd_t *stsd ); -isom_tx3g_entry_t *isom_add_tx3g_description( isom_stsd_t *stsd ); -isom_esds_t *isom_add_esds( void *parent_box ); -isom_glbl_t *isom_add_glbl( void *parent_box ); -isom_clap_t *isom_add_clap( isom_visual_entry_t *visual ); -isom_pasp_t *isom_add_pasp( isom_visual_entry_t *visual ); -isom_colr_t *isom_add_colr( isom_visual_entry_t *visual ); -isom_gama_t *isom_add_gama( isom_visual_entry_t *visual ); -isom_fiel_t *isom_add_fiel( isom_visual_entry_t *visual ); -isom_cspc_t *isom_add_cspc( isom_visual_entry_t *visual ); -isom_sgbt_t *isom_add_sgbt( isom_visual_entry_t *visual ); -isom_stsl_t *isom_add_stsl( isom_visual_entry_t *visual ); -isom_btrt_t *isom_add_btrt( isom_visual_entry_t *visual ); -isom_wave_t *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 ); -isom_chan_t *isom_add_chan( isom_audio_entry_t *audio ); -isom_srat_t *isom_add_srat( isom_audio_entry_t *audio ); -int isom_add_ftab( isom_tx3g_entry_t *tx3g ); -int isom_add_stts( isom_stbl_t *stbl ); -int isom_add_ctts( isom_stbl_t *stbl ); -int isom_add_cslg( isom_stbl_t *stbl ); -int isom_add_stsc( isom_stbl_t *stbl ); -int isom_add_stsz( isom_stbl_t *stbl ); -int isom_add_stss( isom_stbl_t *stbl ); -int isom_add_stps( isom_stbl_t *stbl ); -int isom_add_sdtp( isom_box_t *parent ); -isom_sgpd_t *isom_add_sgpd( void *parent_box ); -isom_sbgp_t *isom_add_sbgp( void *parent_box ); -int isom_add_stco( isom_stbl_t *stbl ); -int isom_add_co64( isom_stbl_t *stbl ); -int isom_add_udta( void *parent_box ); -int isom_add_cprt( isom_udta_t *udta ); -int isom_add_WLOC( isom_udta_t *udta ); -int isom_add_LOOP( isom_udta_t *udta ); -int isom_add_SelO( isom_udta_t *udta ); -int isom_add_AllF( isom_udta_t *udta ); -int isom_add_chpl( isom_udta_t *udta ); -int isom_add_meta( void *parent_box ); -int isom_add_keys( isom_meta_t *meta ); -int isom_add_ilst( isom_meta_t *meta ); -int isom_add_metaitem( isom_ilst_t *ilst, lsmash_itunes_metadata_item item ); -int isom_add_mean( isom_metaitem_t *metaitem ); -int isom_add_name( isom_metaitem_t *metaitem ); -int isom_add_data( isom_metaitem_t *metaitem ); -int isom_add_mvex( isom_moov_t *moov ); -int isom_add_mehd( isom_mvex_t *mvex ); -isom_trex_t *isom_add_trex( isom_mvex_t *mvex ); -isom_moof_t *isom_add_moof( lsmash_file_t *file ); -int isom_add_mfhd( isom_moof_t *moof ); -isom_traf_t *isom_add_traf( isom_moof_t *moof ); -int isom_add_tfhd( isom_traf_t *traf ); -int isom_add_tfdt( isom_traf_t *traf ); -isom_trun_t *isom_add_trun( isom_traf_t *traf ); -int isom_add_mfra( lsmash_file_t *file ); -isom_tfra_t *isom_add_tfra( isom_mfra_t *mfra ); -int isom_add_mfro( isom_mfra_t *mfra ); -int isom_add_mdat( lsmash_file_t *file ); -int isom_add_free( void *parent_box ); -isom_styp_t *isom_add_styp( lsmash_file_t *file ); -isom_sidx_t *isom_add_sidx( lsmash_file_t *file ); - -void isom_remove_sample_description( isom_sample_entry_t *sample ); -void isom_remove_unknown_box( isom_unknown_box_t *unknown_box ); -void isom_remove_sample_pool( isom_sample_pool_t *pool ); - -static inline uint64_t isom_update_box_size( void *box ) -{ - assert( box ); - return ((isom_box_t *)box)->update( box ); -} - -uint64_t isom_update_unknown_box_size( isom_unknown_box_t *unknown_box ); - -int isom_add_extension_binary( void *parent_box, lsmash_box_type_t box_type, uint64_t precedence, uint8_t *box_data, uint32_t box_size ); -void isom_remove_extension_box( isom_box_t *ext ); -void isom_remove_all_extension_boxes( lsmash_entry_list_t *extensions ); -isom_box_t *isom_get_extension_box( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ); -void *isom_get_extension_box_format( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ); -void isom_remove_box_by_itself( void *opaque_box ); - -#endif diff -Nru l-smash-1.9.1/chapter.c l-smash-2.3.0/chapter.c --- l-smash-1.9.1/chapter.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/chapter.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,416 +0,0 @@ -/***************************************************************************** - * chapter.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 - -#include "box.h" - -#define CHAPTER_BUFSIZE 512 -#define UTF8_BOM "\xEF\xBB\xBF" -#define UTF8_BOM_LENGTH 3 - -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]; - /* 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; - int len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */ - data->chapter_name = (char *)lsmash_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]; - if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) ) /* read newline */ - return -1; - char *p_buff = &buff[ !memcmp( buff, UTF8_BOM, UTF8_BOM_LENGTH ) ? UTF8_BOM_LENGTH : 0 ]; /* BOM detection */ - if( isom_get_start_time( p_buff, data ) ) /* get start_time */ - return -1; - /* get chapter_name */ - char *chapter_name = strchr( buff, ' ' ); /* find separator */ - if( !chapter_name++ ) - return -1; - int len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */ - data->chapter_name = (char *)lsmash_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 ) -{ - FILE *fp = lsmash_fopen( file_name, "rb" ); - if( !fp ) - { - lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name ); - return NULL; - } - char buff[CHAPTER_BUFSIZE]; - fn_get_chapter_data fnc = NULL; - if( fgets( buff, CHAPTER_BUFSIZE, fp ) != NULL ) - { - char *p_buff = &buff[ !memcmp( buff, UTF8_BOM, UTF8_BOM_LENGTH ) ? UTF8_BOM_LENGTH : 0 ]; /* BOM detection */ - if( !strncmp( p_buff, "CHAPTER", 7 ) ) - fnc = isom_read_simple_chapter; - else if( isdigit( p_buff[0] ) && isdigit( p_buff[1] ) && p_buff[2] == ':' - && isdigit( p_buff[3] ) && isdigit( p_buff[4] ) && p_buff[5] == ':' ) - fnc = isom_read_minimum_chapter; - else - lsmash_log( NULL, LSMASH_LOG_ERROR, "the chapter file is malformed.\n" ); - } - fclose( fp ); - return fnc; -} - -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 = lsmash_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 *)lsmash_malloc( data->chapter_name_length + 1 ); - if( !data->chapter_name ) - { - lsmash_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 ) ) - { - lsmash_free( data->chapter_name ); - lsmash_free( data ); - return -1; - } - return 0; -} - -int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name, int add_bom ) -{ - if( !root ) - goto error_message; - /* This function should be called after updating of the latest movie duration. */ - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->moov->mvhd - || file->moov->mvhd->timescale == 0 - || file->moov->mvhd->duration == 0 ) - goto error_message; - /* check each line format */ - fn_get_chapter_data fnc = isom_check_chap_line( file_name ); - if( !fnc ) - goto error_message; - FILE *chapter = lsmash_fopen( file_name, "rb" ); - if( !chapter ) - { - lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name ); - goto error_message; - } - if( (!file->moov->udta && isom_add_udta( file->moov )) - || (!file->moov->udta->chpl && isom_add_chpl( file->moov->udta )) ) - goto fail; - file->moov->udta->chpl->version = 1; /* version = 1 is popular. */ - isom_chapter_entry_t data = {0}; - while( !fnc( chapter, &data ) ) - { - if( add_bom ) - { - char *chapter_name_with_bom = (char *)lsmash_malloc( strlen( data.chapter_name ) + 1 + UTF8_BOM_LENGTH ); - if( !chapter_name_with_bom ) - goto fail2; - sprintf( chapter_name_with_bom, "%s%s", UTF8_BOM, data.chapter_name ); - lsmash_free( data.chapter_name ); - data.chapter_name = chapter_name_with_bom; - } - data.start_time = (data.start_time + 50) / 100; /* convert to 100ns unit */ - if( data.start_time / 1e7 > (double)file->moov->mvhd->duration / file->moov->mvhd->timescale ) - { - lsmash_log( NULL, LSMASH_LOG_WARNING, - "a chapter point exceeding the actual duration detected." - "This chapter point and the following ones (if any) will be cut off.\n" ); - lsmash_free( data.chapter_name ); - break; - } - if( isom_add_chpl_entry( file->moov->udta->chpl, &data ) ) - goto fail2; - lsmash_freep( &data.chapter_name ); - } - fclose( chapter ); - return 0; -fail2: - if( data.chapter_name ) - lsmash_free( data.chapter_name ); -fail: - fclose( chapter ); -error_message: - lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to set chapter list.\n" ); - return -1; -} - -int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name ) -{ - if( !root ) - goto error_message; - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->moov->mvhd ) - goto error_message; - if( file->forbid_tref || (!file->qt_compatible && !file->itunes_movie) ) - { - lsmash_log( NULL, LSMASH_LOG_ERROR, "reference chapter is not available for this file.\n" ); - goto error_message; - } - FILE *chapter = NULL; /* shut up 'uninitialized' warning */ - /* Create a Track Reference Box. */ - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak ) - { - lsmash_log( NULL, LSMASH_LOG_ERROR, "the specified track ID to apply the chapter doesn't exist.\n" ); - goto error_message; - } - if( !trak->tref && isom_add_tref( trak ) ) - goto error_message; - /* Create a track_ID for a new chapter track. */ - uint32_t *id = (uint32_t *)lsmash_malloc( sizeof(uint32_t) ); - if( !id ) - goto error_message; - uint32_t chapter_track_ID = *id = file->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 ); - if( !chap ) - goto error_message; /* no need to free id */ - chap->ref_count = 1; - chap->track_ID = id; - /* Create a reference chapter track. */ - if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) ) - goto error_message; - /* 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 = file->max_3gpp_version >= 6 || file->itunes_movie ? ISOM_LANGUAGE_CODE_UNDEFINED : 0; - media_param.MAC_language = 0; - if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) ) - goto fail; - /* Create a sample description. */ - lsmash_codec_type_t sample_type = file->max_3gpp_version >= 6 || file->itunes_movie - ? ISOM_CODEC_TYPE_TX3G_TEXT - : QT_CODEC_TYPE_TEXT_TEXT; - lsmash_summary_t summary = { .sample_type = sample_type }; - uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, &summary ); - if( !sample_entry ) - goto fail; - /* Check each line format. */ - fn_get_chapter_data fnc = isom_check_chap_line( file_name ); - if( !fnc ) - goto fail; - /* Open chapter format file. */ - chapter = lsmash_fopen( file_name, "rb" ); - if( !chapter ) - { - lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name ); - 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 */ - int is_qt_text = lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TEXT_TEXT ); - uint16_t name_length = strlen( data.chapter_name ); - lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * is_qt_text ); - if( !sample ) - { - lsmash_free( data.chapter_name ); - goto fail; - } - sample->data[0] = (name_length >> 8) & 0xff; - sample->data[1] = name_length & 0xff; - memcpy( sample->data + 2, data.chapter_name, name_length ); - if( is_qt_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 = data.start_time; - sample->cts = data.start_time; - sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - sample->index = sample_entry; - if( lsmash_append_sample( root, chapter_track_ID, sample ) ) - { - lsmash_free( data.chapter_name ); - goto fail; - } - lsmash_freep( &data.chapter_name ); - } - if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) ) - goto fail; - isom_trak_t *chapter_trak = isom_get_trak( file, chapter_track_ID ); - if( !chapter_trak ) - goto fail; - fclose( chapter ); - chapter_trak->is_chapter = 1; - chapter_trak->related_track_ID = track_ID; - return 0; -fail: - if( chapter ) - fclose( chapter ); - /* Remove chapter track reference. */ - if( trak->tref->ref_list.tail ) - isom_remove_box_by_itself( trak->tref->ref_list.tail->data ); - if( trak->tref->ref_list.entry_count == 0 ) - isom_remove_box_by_itself( trak->tref ); - /* Remove the reference chapter track attached at tail of the list. */ - if( file->moov->trak_list.tail ) - isom_remove_box_by_itself( file->moov->trak_list.tail->data ); -error_message: - lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to set reference chapter.\n" ); - return -1; -} - -int lsmash_print_chapter_list( lsmash_root_t *root ) -{ - if( !root || !root->file || !(root->file->flags & LSMASH_FILE_MODE_READ) ) - return -1; - lsmash_file_t *file = root->file; - if( file->moov - && file->moov->udta - && file->moov->udta->chpl ) - { - isom_chpl_t *chpl = file->moov->udta->chpl; - uint32_t timescale; - if( !chpl->version ) - { - if( !file->moov - && !file->moov->mvhd ) - return -1; - timescale = file->moov->mvhd->timescale; - } - else - timescale = 10000000; - uint32_t i = 1; - for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next ) - { - isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data; - int64_t start_time = data->start_time / timescale; - int hh = start_time / 3600; - int mm = (start_time / 60) % 60; - int ss = start_time % 60; - int ms = ((data->start_time / (double)timescale) - hh * 3600 - mm * 60 - ss) * 1e3 + 0.5; - if( !memcmp( data->chapter_name, UTF8_BOM, UTF8_BOM_LENGTH ) ) /* detect BOM */ - { - data->chapter_name += UTF8_BOM_LENGTH; -#ifdef _WIN32 - if( i == 1 ) - printf( UTF8_BOM ); /* add BOM on Windows */ -#endif - } - printf( "CHAPTER%02"PRIu32"=%02d:%02d:%02d.%03d\n", i, hh, mm, ss, ms ); - printf( "CHAPTER%02"PRIu32"NAME=%s\n", i++, data->chapter_name ); - } - return 0; - } - else - lsmash_log( NULL, LSMASH_LOG_ERROR, "this file doesn't have a chapter list.\n" ); - return -1; -} diff -Nru l-smash-1.9.1/cli/a52_imp.c l-smash-2.3.0/cli/a52_imp.c --- l-smash-1.9.1/cli/a52_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/a52_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,651 @@ +/***************************************************************************** + * a52_imp.c + ***************************************************************************** + * Copyright (C) 2011-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + AC-3 importer + ETSI TS 102 366 V1.2.1 (2008-08) +***************************************************************************/ +#include "codecs/a52.h" + +#define AC3_SAMPLE_DURATION 1536 /* 256 (samples per audio block) * 6 (audio blocks) */ + +typedef struct +{ + importer_status status; + ac3_info_t info; + uint64_t next_frame_pos; + uint8_t *next_dac3; + uint8_t buffer[AC3_MAX_SYNCFRAME_LENGTH]; + uint32_t au_number; +} ac3_importer_t; + +static void remove_ac3_importer( ac3_importer_t *ac3_imp ) +{ + if( !ac3_imp ) + return; + lsmash_bits_adhoc_cleanup( ac3_imp->info.bits ); + lsmash_free( ac3_imp ); +} + +static ac3_importer_t *create_ac3_importer( void ) +{ + ac3_importer_t *ac3_imp = (ac3_importer_t *)lsmash_malloc_zero( sizeof(ac3_importer_t) ); + if( !ac3_imp ) + return NULL; + ac3_imp->info.bits = lsmash_bits_adhoc_create(); + if( !ac3_imp->info.bits ) + { + lsmash_free( ac3_imp ); + return NULL; + } + return ac3_imp; +} + +static void ac3_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_ac3_importer( importer->info ); +} + +static const uint32_t ac3_frame_size_table[19][3] = +{ + /* 48, 44.1, 32 */ + { 128, 138, 192 }, + { 160, 174, 240 }, + { 192, 208, 288 }, + { 224, 242, 336 }, + { 256, 278, 384 }, + { 320, 348, 480 }, + { 384, 416, 576 }, + { 448, 486, 672 }, + { 512, 556, 768 }, + { 640, 696, 960 }, + { 768, 834, 1152 }, + { 896, 974, 1344 }, + { 1024, 1114, 1536 }, + { 1280, 1392, 1920 }, + { 1536, 1670, 2304 }, + { 1792, 1950, 2688 }, + { 2048, 2228, 3072 }, + { 2304, 2506, 3456 }, + { 2560, 2786, 3840 } +}; + +static lsmash_audio_summary_t *ac3_create_summary( ac3_info_t *info ) +{ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + lsmash_ac3_specific_parameters_t *param = &info->dac3_param; + lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3, + LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + cs->data.unstructured = lsmash_create_ac3_specific_info( &info->dac3_param, &cs->size ); + if( !cs->data.unstructured + || lsmash_add_entry( &summary->opaque->list, cs ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + summary->sample_type = ISOM_CODEC_TYPE_AC_3_AUDIO; + summary->max_au_length = AC3_MAX_SYNCFRAME_LENGTH; + summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ + summary->frequency = ac3_get_sample_rate( param ); + summary->channels = ac3_get_channel_count( param ); + summary->sample_size = 16; /* no effect */ + summary->samples_in_frame = AC3_SAMPLE_DURATION; + summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ + return summary; +} + +static int ac3_compare_specific_param( lsmash_ac3_specific_parameters_t *a, lsmash_ac3_specific_parameters_t *b ) +{ + return (a->fscod != b->fscod) + || (a->bsid != b->bsid) + || (a->bsmod != b->bsmod) + || (a->acmod != b->acmod) + || (a->lfeon != b->lfeon) + || ((a->frmsizecod >> 1) != (b->frmsizecod >> 1)); +} + +static int ac3_buffer_frame( uint8_t *buffer, lsmash_bs_t *bs ) +{ + uint64_t remain_size = lsmash_bs_get_remaining_buffer_size( bs ); + if( remain_size < AC3_MAX_SYNCFRAME_LENGTH ) + { + int err = lsmash_bs_read( bs, bs->buffer.max_size ); + if( err < 0 ) + return err; + remain_size = lsmash_bs_get_remaining_buffer_size( bs ); + } + uint64_t copy_size = LSMASH_MIN( remain_size, AC3_MAX_SYNCFRAME_LENGTH ); + memcpy( buffer, lsmash_bs_get_buffer_data( bs ), copy_size ); + return 0; +} + +static int ac3_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + ac3_importer_t *ac3_imp = (ac3_importer_t *)importer->info; + ac3_info_t *info = &ac3_imp->info; + importer_status current_status = ac3_imp->status; + if( current_status == IMPORTER_ERROR ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + lsmash_ac3_specific_parameters_t *param = &info->dac3_param; + uint32_t frame_size = ac3_frame_size_table[ param->frmsizecod >> 1 ][ param->fscod ]; + if( param->fscod == 0x1 && param->frmsizecod & 0x1 ) + frame_size += 2; + if( buffered_sample->length < frame_size ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_CHANGE ) + { + lsmash_codec_specific_t *cs = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 ); + if( cs ) + { + cs->destruct( cs->data.unstructured ); + cs->data.unstructured = ac3_imp->next_dac3; + } + summary->frequency = ac3_get_sample_rate( param ); + summary->channels = ac3_get_channel_count( param ); + //summary->layout_tag = ac3_channel_layout_table[ param->acmod ][ param->lfeon ]; + } + memcpy( buffered_sample->data, ac3_imp->buffer, frame_size ); + buffered_sample->length = frame_size; + buffered_sample->dts = ac3_imp->au_number++ * summary->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + buffered_sample->prop.pre_roll.distance = 1; /* MDCT */ + lsmash_bs_t *bs = info->bits->bs; + ac3_imp->next_frame_pos += frame_size; + lsmash_bs_read_seek( bs, ac3_imp->next_frame_pos, SEEK_SET ); + uint8_t syncword[2] = + { + lsmash_bs_show_byte( bs, 0 ), + lsmash_bs_show_byte( bs, 1 ) + }; + if( bs->eob || (bs->eof && 0 == lsmash_bs_get_remaining_buffer_size( bs )) ) + ac3_imp->status = IMPORTER_EOF; + else + { + /* Parse the next syncframe header. */ + if( syncword[0] != 0x0b + || syncword[1] != 0x77 + || ac3_buffer_frame( ac3_imp->buffer, bs ) < 0 ) + { + ac3_imp->status = IMPORTER_ERROR; + return current_status; + } + lsmash_ac3_specific_parameters_t current_param = info->dac3_param; + ac3_parse_syncframe_header( info ); + if( ac3_compare_specific_param( ¤t_param, &info->dac3_param ) ) + { + uint32_t dummy; + uint8_t *dac3 = lsmash_create_ac3_specific_info( &info->dac3_param, &dummy ); + if( !dac3 ) + { + ac3_imp->status = IMPORTER_ERROR; + return current_status; + } + ac3_imp->status = IMPORTER_CHANGE; + ac3_imp->next_dac3 = dac3; + } + else + ac3_imp->status = IMPORTER_OK; + } + return current_status; +} + +static int ac3_importer_probe( importer_t *importer ) +{ + ac3_importer_t *ac3_imp = create_ac3_importer(); + if( !ac3_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bits_t *bits = ac3_imp->info.bits; + lsmash_bs_t *bs = bits->bs; + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = AC3_MAX_SYNCFRAME_LENGTH; + /* Check the syncword and parse the syncframe header */ + int err; + if( lsmash_bs_show_byte( bs, 0 ) != 0x0b + || lsmash_bs_show_byte( bs, 1 ) != 0x77 ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + if( (err = ac3_buffer_frame( ac3_imp->buffer, bs )) < 0 + || (err = ac3_parse_syncframe_header( &ac3_imp->info )) < 0 ) + goto fail; + lsmash_audio_summary_t *summary = ac3_create_summary( &ac3_imp->info ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + ac3_imp->status = IMPORTER_OK; + ac3_imp->au_number = 0; + importer->info = ac3_imp; + return 0; +fail: + remove_ac3_importer( ac3_imp ); + return err; +} + +static uint32_t ac3_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + ac3_importer_t *ac3_imp = (ac3_importer_t *)importer->info; + if( !ac3_imp || track_number != 1 || ac3_imp->status != IMPORTER_EOF ) + return 0; + return AC3_SAMPLE_DURATION; +} + +const importer_functions ac3_importer = +{ + { "AC-3" }, + 1, + ac3_importer_probe, + ac3_importer_get_accessunit, + ac3_importer_get_last_delta, + ac3_importer_cleanup +}; + +/*************************************************************************** + Enhanced AC-3 importer + ETSI TS 102 366 V1.2.1 (2008-08) +***************************************************************************/ +#define EAC3_MIN_SAMPLE_DURATION 256 + +typedef struct +{ + importer_status status; + eac3_info_t info; + uint64_t next_frame_pos; + uint32_t next_dec3_length; + uint8_t *next_dec3; + uint8_t current_fscod2; + uint8_t buffer[EAC3_MAX_SYNCFRAME_LENGTH]; + lsmash_multiple_buffers_t *au_buffers; + uint8_t *au; + uint8_t *incomplete_au; + uint32_t au_length; + uint32_t incomplete_au_length; + uint32_t au_number; + uint32_t syncframe_count_in_au; +} eac3_importer_t; + +static void remove_eac3_importer( eac3_importer_t *eac3_imp ) +{ + if( !eac3_imp ) + return; + lsmash_destroy_multiple_buffers( eac3_imp->au_buffers ); + lsmash_bits_adhoc_cleanup( eac3_imp->info.bits ); + lsmash_free( eac3_imp ); +} + +static eac3_importer_t *create_eac3_importer( void ) +{ + eac3_importer_t *eac3_imp = (eac3_importer_t *)lsmash_malloc_zero( sizeof(eac3_importer_t) ); + if( !eac3_imp ) + return NULL; + eac3_info_t *info = &eac3_imp->info; + info->bits = lsmash_bits_adhoc_create(); + if( !info->bits ) + { + lsmash_free( eac3_imp ); + return NULL; + } + eac3_imp->au_buffers = lsmash_create_multiple_buffers( 2, EAC3_MAX_SYNCFRAME_LENGTH ); + if( !eac3_imp->au_buffers ) + { + lsmash_bits_adhoc_cleanup( info->bits ); + lsmash_free( eac3_imp ); + return NULL; + } + eac3_imp->au = lsmash_withdraw_buffer( eac3_imp->au_buffers, 1 ); + eac3_imp->incomplete_au = lsmash_withdraw_buffer( eac3_imp->au_buffers, 2 ); + return eac3_imp; +} + +static void eac3_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_eac3_importer( importer->info ); +} + +static int eac3_importer_get_next_accessunit_internal( importer_t *importer ) +{ + int au_completed = 0; + eac3_importer_t *eac3_imp = (eac3_importer_t *)importer->info; + eac3_info_t *info = &eac3_imp->info; + lsmash_bs_t *bs = info->bits->bs; + while( !au_completed ) + { + /* Read data from the stream if needed. */ + eac3_imp->next_frame_pos += info->frame_size; + lsmash_bs_read_seek( bs, eac3_imp->next_frame_pos, SEEK_SET ); + uint64_t remain_size = lsmash_bs_get_remaining_buffer_size( bs ); + if( remain_size < EAC3_MAX_SYNCFRAME_LENGTH ) + { + int err = lsmash_bs_read( bs, bs->buffer.max_size ); + if( err < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to read data from the stream.\n" ); + return err; + } + remain_size = lsmash_bs_get_remaining_buffer_size( bs ); + } + uint64_t copy_size = LSMASH_MIN( remain_size, EAC3_MAX_SYNCFRAME_LENGTH ); + memcpy( eac3_imp->buffer, lsmash_bs_get_buffer_data( bs ), copy_size ); + /* Check the remainder length of the buffer. + * If there is enough length, then parse the syncframe in it. + * The length 5 is the required byte length to get frame size. */ + if( bs->eob || (bs->eof && remain_size < 5) ) + { + /* Reached the end of stream. + * According to ETSI TS 102 366 V1.2.1 (2008-08), + * one access unit consists of 6 audio blocks and begins with independent substream 0. + * The specification doesn't mention the case where a enhanced AC-3 stream ends at non-mod6 audio blocks. + * At the end of the stream, therefore, we might make an access unit which has less than 6 audio blocks anyway. */ + eac3_imp->status = IMPORTER_EOF; + au_completed = !!eac3_imp->incomplete_au_length; + if( !au_completed ) + { + /* No more access units in the stream. */ + if( lsmash_bs_get_remaining_buffer_size( bs ) ) + { + lsmash_log( importer, LSMASH_LOG_WARNING, "the stream is truncated at the end.\n" ); + return LSMASH_ERR_INVALID_DATA; + } + return 0; + } + if( !info->dec3_param_initialized ) + eac3_update_specific_param( info ); + } + else + { + /* Check the syncword. */ + if( lsmash_bs_show_byte( bs, 0 ) != 0x0b + || lsmash_bs_show_byte( bs, 1 ) != 0x77 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "a syncword is not found.\n" ); + return LSMASH_ERR_INVALID_DATA; + } + /* Parse syncframe. */ + info->frame_size = 0; + int err = eac3_parse_syncframe( info ); + if( err < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to parse syncframe.\n" ); + return err; + } + if( remain_size < info->frame_size ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "a frame is truncated.\n" ); + return LSMASH_ERR_INVALID_DATA; + } + int independent = info->strmtyp != 0x1; + if( independent && info->substreamid == 0x0 ) + { + if( info->number_of_audio_blocks == 6 ) + { + /* Encountered the first syncframe of the next access unit. */ + info->number_of_audio_blocks = 0; + au_completed = 1; + } + else if( info->number_of_audio_blocks > 6 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "greater than 6 consecutive independent substreams.\n" ); + return LSMASH_ERR_INVALID_DATA; + } + info->number_of_audio_blocks += eac3_audio_block_table[ info->numblkscod ]; + info->number_of_independent_substreams = 0; + eac3_imp->current_fscod2 = info->fscod2; + } + else if( info->syncframe_count == 0 ) + { + /* The first syncframe in an AU must be independent and assigned substream ID 0. */ + lsmash_log( importer, LSMASH_LOG_ERROR, "the first syncframe is NOT an independent substream.\n" ); + return LSMASH_ERR_INVALID_DATA; + } + if( independent ) + info->independent_info[info->number_of_independent_substreams ++].num_dep_sub = 0; + else + ++ info->independent_info[info->number_of_independent_substreams - 1].num_dep_sub; + } + if( au_completed ) + { + memcpy( eac3_imp->au, eac3_imp->incomplete_au, eac3_imp->incomplete_au_length ); + eac3_imp->au_length = eac3_imp->incomplete_au_length; + eac3_imp->incomplete_au_length = 0; + eac3_imp->syncframe_count_in_au = info->syncframe_count; + info->syncframe_count = 0; + if( eac3_imp->status == IMPORTER_EOF ) + break; + } + /* Increase buffer size to store AU if short. */ + if( eac3_imp->incomplete_au_length + info->frame_size > eac3_imp->au_buffers->buffer_size ) + { + lsmash_multiple_buffers_t *temp = lsmash_resize_multiple_buffers( eac3_imp->au_buffers, + eac3_imp->au_buffers->buffer_size + EAC3_MAX_SYNCFRAME_LENGTH ); + if( !temp ) + return LSMASH_ERR_MEMORY_ALLOC; + eac3_imp->au_buffers = temp; + eac3_imp->au = lsmash_withdraw_buffer( eac3_imp->au_buffers, 1 ); + eac3_imp->incomplete_au = lsmash_withdraw_buffer( eac3_imp->au_buffers, 2 ); + } + /* Append syncframe data. */ + memcpy( eac3_imp->incomplete_au + eac3_imp->incomplete_au_length, eac3_imp->buffer, info->frame_size ); + eac3_imp->incomplete_au_length += info->frame_size; + ++ info->syncframe_count; + } + return info->bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int eac3_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + eac3_importer_t *eac3_imp = (eac3_importer_t *)importer->info; + eac3_info_t *info = &eac3_imp->info; + importer_status current_status = eac3_imp->status; + if( current_status == IMPORTER_ERROR || buffered_sample->length < eac3_imp->au_length ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF && eac3_imp->au_length == 0 ) + { + buffered_sample->length = 0; + return 0; + } + if( current_status == IMPORTER_CHANGE ) + { + lsmash_codec_specific_t *cs = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ); + if( cs ) + { + cs->destruct( cs->data.unstructured ); + cs->data.unstructured = eac3_imp->next_dec3; + cs->size = eac3_imp->next_dec3_length; + } + summary->max_au_length = eac3_imp->syncframe_count_in_au * EAC3_MAX_SYNCFRAME_LENGTH; + eac3_update_sample_rate( &summary->frequency, &info->dec3_param, &eac3_imp->current_fscod2 ); + eac3_update_channel_count( &summary->channels, &info->dec3_param ); + } + memcpy( buffered_sample->data, eac3_imp->au, eac3_imp->au_length ); + buffered_sample->length = eac3_imp->au_length; + buffered_sample->dts = eac3_imp->au_number++ * summary->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + buffered_sample->prop.pre_roll.distance = 1; /* MDCT */ + if( eac3_imp->status == IMPORTER_EOF ) + { + eac3_imp->au_length = 0; + return 0; + } + uint32_t old_syncframe_count_in_au = eac3_imp->syncframe_count_in_au; + if( eac3_importer_get_next_accessunit_internal( importer ) < 0 ) + { + eac3_imp->status = IMPORTER_ERROR; + return current_status; + } + if( eac3_imp->syncframe_count_in_au ) + { + /* Check sample description change. */ + uint32_t new_length; + uint8_t *dec3 = lsmash_create_eac3_specific_info( &info->dec3_param, &new_length ); + if( !dec3 ) + { + eac3_imp->status = IMPORTER_ERROR; + return current_status; + } + lsmash_codec_specific_t *cs = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ); + if( (eac3_imp->syncframe_count_in_au > old_syncframe_count_in_au) + || (cs && (new_length != cs->size || memcmp( dec3, cs->data.unstructured, cs->size ))) ) + { + eac3_imp->status = IMPORTER_CHANGE; + eac3_imp->next_dec3 = dec3; + eac3_imp->next_dec3_length = new_length; + } + else + { + if( eac3_imp->status != IMPORTER_EOF ) + eac3_imp->status = IMPORTER_OK; + lsmash_free( dec3 ); + } + } + return current_status; +} + +static lsmash_audio_summary_t *eac3_create_summary( eac3_importer_t *eac3_imp ) +{ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + eac3_info_t *info = &eac3_imp->info; + lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3, + LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + cs->data.unstructured = lsmash_create_eac3_specific_info( &info->dec3_param, &cs->size ); + if( !cs->data.unstructured + || lsmash_add_entry( &summary->opaque->list, cs ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + summary->sample_type = ISOM_CODEC_TYPE_EC_3_AUDIO; + summary->max_au_length = eac3_imp->syncframe_count_in_au * EAC3_MAX_SYNCFRAME_LENGTH; + summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ + summary->sample_size = 16; /* no effect */ + summary->samples_in_frame = EAC3_MIN_SAMPLE_DURATION * 6; /* 256 (samples per audio block) * 6 (audio blocks) */ + summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ + eac3_update_sample_rate( &summary->frequency, &info->dec3_param, &eac3_imp->current_fscod2 ); + eac3_update_channel_count( &summary->channels, &info->dec3_param ); + return summary; +} + +static int eac3_importer_probe( importer_t *importer ) +{ + eac3_importer_t *eac3_imp = create_eac3_importer(); + if( !eac3_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bits_t *bits = eac3_imp->info.bits; + lsmash_bs_t *bs = bits->bs; + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = EAC3_MAX_SYNCFRAME_LENGTH; + importer->info = eac3_imp; + int err = eac3_importer_get_next_accessunit_internal( importer ); + if( err < 0 ) + goto fail; + lsmash_audio_summary_t *summary = eac3_create_summary( eac3_imp ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + if( eac3_imp->status != IMPORTER_EOF ) + eac3_imp->status = IMPORTER_OK; + eac3_imp->au_number = 0; + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + return 0; +fail: + remove_eac3_importer( eac3_imp ); + importer->info = NULL; + return err; +} + +static uint32_t eac3_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + eac3_importer_t *eac3_imp = (eac3_importer_t *)importer->info; + if( !eac3_imp || track_number != 1 || eac3_imp->status != IMPORTER_EOF || eac3_imp->au_length ) + return 0; + return EAC3_MIN_SAMPLE_DURATION * eac3_imp->info.number_of_audio_blocks; +} + +const importer_functions eac3_importer = +{ + { "Enhanced AC-3", offsetof( importer_t, log_level ) }, + 1, + eac3_importer_probe, + eac3_importer_get_accessunit, + eac3_importer_get_last_delta, + eac3_importer_cleanup +}; diff -Nru l-smash-1.9.1/cli/adts_imp.c l-smash-2.3.0/cli/adts_imp.c --- l-smash-1.9.1/cli/adts_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/adts_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,464 @@ +/***************************************************************************** + * adts_imp.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + ADTS importer +***************************************************************************/ +#include "codecs/mp4a.h" + +#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 LSMASH_ERR_INVALID_DATA; +// if( header->ID != 0x0 ) return LSMASH_ERR_NAMELESS; /* we don't care. */ + if( header->layer != 0x0 ) return LSMASH_ERR_INVALID_DATA; /* must be 0b00 for any type of AAC */ +// if( header->protection_absent != 0x1 ) return LSMASH_ERR_NAMELESS; /* we don't care. */ + if( header->profile_ObjectType != 0x1 ) return LSMASH_ERR_PATCH_WELCOME; /* FIXME: 0b00=Main, 0b01=LC, 0b10=SSR, 0b11=LTP. */ + if( header->sampling_frequency_index > 0xB ) return LSMASH_ERR_INVALID_DATA; /* must not be > 0xB. */ + if( header->channel_configuration == 0x0 ) return LSMASH_ERR_PATCH_WELCOME; /* FIXME: we do not support 0b000 currently. */ + if( header->profile_ObjectType == 0x3 && header->ID != 0x0 ) return LSMASH_ERR_INVALID_DATA; /* 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 LSMASH_ERR_INVALID_DATA; /* 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 LSMASH_ERR_INVALID_DATA; + } + 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 LSMASH_ERR_INVALID_DATA; + raw_data_block_position[i] = LSMASH_GET_BE16( buf2 ); + } + /* 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 LSMASH_ERR_INVALID_DATA; + 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 LSMASH_ERR_NAMELESS; + } + + /* 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 ); + int err = mp4sys_adts_check_fixed_header( header ); + if( err < 0 ) + return err; + /* get payload length & skip extra(crc) header */ + return mp4sys_adts_parse_variable_header( stream, buf, header->protection_absent, variable_header ); +} + +static lsmash_audio_summary_t *mp4sys_adts_create_summary( mp4sys_adts_fixed_header_t *header ) +{ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; + summary->max_au_length = MP4SYS_ADTS_MAX_FRAME_LENGTH; + summary->frequency = mp4a_sampling_frequency_table[header->sampling_frequency_index][1]; + summary->channels = header->channel_configuration + ( header->channel_configuration == 0x07 ); /* 0x07 means 7.1ch */ + summary->sample_size = 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 + uint32_t data_length; + uint8_t *data = mp4a_export_AudioSpecificConfig( header->profile_ObjectType + MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN, + summary->frequency, summary->channels, summary->sbr_mode, + NULL, 0, &data_length ); + if( !data ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; + } + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_free( data ); + return NULL; + } + lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured; + param->objectTypeIndication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3; + param->streamType = MP4SYS_STREAM_TYPE_AudioStream; + if( lsmash_set_mp4sys_decoder_specific_info( param, data, data_length ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + lsmash_free( data ); + return NULL; + } + lsmash_free( data ); + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + return summary; +} + +typedef struct +{ + importer_status status; + unsigned int raw_data_block_idx; + mp4sys_adts_fixed_header_t header; + mp4sys_adts_variable_header_t variable_header; + uint32_t samples_in_frame; + uint32_t au_number; +} mp4sys_adts_info_t; + +static int mp4sys_adts_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + mp4sys_adts_info_t* info = (mp4sys_adts_info_t*)importer->info; + 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 == IMPORTER_ERROR || buffered_sample->length < raw_data_block_size ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + if( current_status == IMPORTER_CHANGE ) + { + lsmash_audio_summary_t* summary = mp4sys_adts_create_summary( &info->header ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + lsmash_entry_t* entry = lsmash_get_entry( importer->summaries, track_number ); + if( !entry || !entry->data ) + return LSMASH_ERR_NAMELESS; + lsmash_cleanup_summary( entry->data ); + entry->data = summary; + info->samples_in_frame = summary->samples_in_frame; + } + + /* read a raw_data_block(), typically == payload of a ADTS frame */ + if( fread( buffered_sample->data, 1, raw_data_block_size, importer->stream ) != raw_data_block_size ) + { + info->status = IMPORTER_ERROR; + return LSMASH_ERR_INVALID_DATA; + } + buffered_sample->length = raw_data_block_size; + buffered_sample->dts = info->au_number++ * info->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + buffered_sample->prop.pre_roll.distance = 1; /* MDCT */ + + /* now we succeeded to read current frame, so "return" takes 0 always below. */ + + /* skip adts_raw_data_block_error_check() */ + if( info->header.protection_absent == 0 + && info->variable_header.number_of_raw_data_blocks_in_frame != 0 + && fread( buffered_sample->data, 1, 2, importer->stream ) != 2 ) + { + info->status = 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 = 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 = IMPORTER_EOF; + return 0; + } + if( ret != MP4SYS_ADTS_BASIC_HEADER_LENGTH ) + { + info->status = 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 ) < 0 ) + { + info->status = 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 = 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 = IMPORTER_CHANGE; + return 0; + } + /* no change which matters to mp4 muxing was found */ + info->status = IMPORTER_OK; + return 0; +} + +static void mp4sys_adts_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + lsmash_free( importer->info ); +} + +/* returns 0 if it seems adts. */ +static int mp4sys_adts_probe( 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 LSMASH_ERR_INVALID_DATA; + mp4sys_adts_fixed_header_t header = { 0 }; + mp4sys_adts_variable_header_t variable_header = { 0 }; + int err = mp4sys_adts_parse_headers( importer->stream, buf, &header, &variable_header ); + if( err < 0 ) + return err; + /* now the stream seems valid ADTS */ + lsmash_audio_summary_t* summary = mp4sys_adts_create_summary( &header ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + /* importer status */ + mp4sys_adts_info_t* info = lsmash_malloc_zero( sizeof(mp4sys_adts_info_t) ); + if( !info || lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_free( info ); + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return LSMASH_ERR_MEMORY_ALLOC; + } + info->status = IMPORTER_OK; + info->raw_data_block_idx = 0; + info->header = header; + info->variable_header = variable_header; + info->samples_in_frame = summary->samples_in_frame; + importer->info = info; + return 0; +} + +static uint32_t mp4sys_adts_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + mp4sys_adts_info_t *info = (mp4sys_adts_info_t *)importer->info; + if( !info || track_number != 1 || info->status != IMPORTER_EOF ) + return 0; + return info->samples_in_frame; +} + +const importer_functions mp4sys_adts_importer = +{ + { "adts" }, + 1, + mp4sys_adts_probe, + mp4sys_adts_get_accessunit, + mp4sys_adts_get_last_delta, + mp4sys_adts_cleanup +}; diff -Nru l-smash-1.9.1/cli/als_imp.c l-smash-2.3.0/cli/als_imp.c --- l-smash-1.9.1/cli/als_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/als_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,405 @@ +/***************************************************************************** + * als_imp.c + ***************************************************************************** + * Copyright (C) 2011-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + MPEG-4 ALS importer + ISO/IEC 14496-3 2009 Fourth edition +***************************************************************************/ +#include "codecs/mp4a.h" + +#define ALSSC_TWELVE_LENGTH 22 + +typedef struct +{ + uint32_t size; + uint32_t samp_freq; + uint32_t samples; + uint32_t channels; + uint16_t frame_length; + uint8_t resolution; + uint8_t random_access; + uint8_t ra_flag; + uint32_t access_unit_size; + uint32_t number_of_ra_units; + uint32_t *ra_unit_size; + uint8_t *sc_data; + size_t alloc; +} als_specific_config_t; + +typedef struct +{ + importer_status status; + lsmash_bs_t *bs; + als_specific_config_t alssc; + uint32_t samples_in_frame; + uint32_t au_number; +} mp4a_als_importer_t; + +static void remove_mp4a_als_importer( mp4a_als_importer_t *als_imp ) +{ + lsmash_bs_cleanup( als_imp->bs ); + lsmash_free( als_imp->alssc.ra_unit_size ); + lsmash_free( als_imp->alssc.sc_data ); + lsmash_free( als_imp ); +} + +static void mp4a_als_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_mp4a_als_importer( importer->info ); +} + +static mp4a_als_importer_t *create_mp4a_als_importer( importer_t *importer ) +{ + mp4a_als_importer_t *als_imp = lsmash_malloc_zero( sizeof(mp4a_als_importer_t) ); + if( !als_imp ) + return NULL; + als_imp->bs = lsmash_bs_create(); + if( !als_imp->bs ) + { + lsmash_free( als_imp ); + return NULL; + } + return als_imp; +} + +static void als_copy_from_buffer( als_specific_config_t *alssc, lsmash_bs_t *bs, uint64_t size ) +{ + if( alssc->alloc < size ) + { + size_t alloc = alssc->alloc ? (alssc->alloc << 1) : (1 << 10); + uint8_t *temp = lsmash_realloc( alssc->sc_data, alloc ); + if( !temp ) + return; + alssc->sc_data = temp; + alssc->alloc = alloc; + } + memcpy( alssc->sc_data + alssc->size, lsmash_bs_get_buffer_data( bs ), size ); + alssc->size += size; + lsmash_bs_read_seek( bs, size, SEEK_CUR ); +} + +static int als_parse_specific_config( mp4a_als_importer_t *als_imp ) +{ + lsmash_bs_t *bs = als_imp->bs; + /* Check ALS identifier( = 0x414C5300). */ + if( 0x414C5300 != lsmash_bs_show_be32( bs, 0 ) ) + return LSMASH_ERR_INVALID_DATA; + als_specific_config_t *alssc = &als_imp->alssc; + alssc->samp_freq = lsmash_bs_show_be32( bs, 4 ); + alssc->samples = lsmash_bs_show_be32( bs, 8 ); + if( alssc->samples == 0xffffffff ) + return LSMASH_ERR_PATCH_WELCOME; /* We don't support this case. */ + alssc->channels = lsmash_bs_show_be16( bs, 12 ); + alssc->resolution = (lsmash_bs_show_byte( bs, 14 ) & 0x1c) >> 2; + if( alssc->resolution > 3 ) + return LSMASH_ERR_NAMELESS; /* reserved */ + alssc->frame_length = lsmash_bs_show_be16( bs, 15 ); + alssc->random_access = lsmash_bs_show_byte( bs, 17 ); + alssc->ra_flag = (lsmash_bs_show_byte( bs, 18 ) & 0xc0) >> 6; + if( alssc->ra_flag == 0 ) + return LSMASH_ERR_PATCH_WELCOME; /* We don't support this case. */ +#if 0 + if( alssc->samples == 0xffffffff && alssc->ra_flag == 2 ) + return LSMASH_ERR_NAMELESS; +#endif + uint8_t temp8 = lsmash_bs_show_byte( bs, 20 ); + int chan_sort = !!(temp8 & 0x1); + if( alssc->channels == 0 ) + { + if( temp8 & 0x8 ) + return LSMASH_ERR_INVALID_DATA; /* If channels = 0 (mono), joint_stereo = 0. */ + else if( temp8 & 0x4 ) + return LSMASH_ERR_INVALID_DATA; /* If channels = 0 (mono), mc_coding = 0. */ + else if( chan_sort ) + return LSMASH_ERR_INVALID_DATA; /* If channels = 0 (mono), chan_sort = 0. */ + } + int chan_config = !!(temp8 & 0x2); + temp8 = lsmash_bs_show_byte( bs, 21 ); + int crc_enabled = !!(temp8 & 0x80); + int aux_data_enabled = !!(temp8 & 0x1); + als_copy_from_buffer( alssc, bs, ALSSC_TWELVE_LENGTH ); + if( chan_config ) + { + /* chan_config_info */ + lsmash_bs_read( bs, 2 ); + als_copy_from_buffer( alssc, bs, 2 ); + } + if( chan_sort ) + { + uint32_t ChBits = lsmash_ceil_log2( alssc->channels + 1 ); + uint32_t chan_pos_length = (alssc->channels + 1) * ChBits; + chan_pos_length = ((uint64_t)chan_pos_length + 7) / 8; /* byte_align */ + lsmash_bs_read( bs, chan_pos_length ); + als_copy_from_buffer( alssc, bs, chan_pos_length ); + } + /* orig_header, orig_trailer and crc. */ + { + uint32_t header_size = lsmash_bs_show_be32( bs, 0 ); + uint32_t trailer_size = lsmash_bs_show_be32( bs, 4 ); + als_copy_from_buffer( alssc, bs, 8 ); + if( header_size != 0xffffffff ) + { + lsmash_bs_read( bs, header_size ); + als_copy_from_buffer( alssc, bs, header_size ); + } + if( trailer_size != 0xffffffff ) + { + lsmash_bs_read( bs, trailer_size ); + als_copy_from_buffer( alssc, bs, trailer_size ); + } + if( crc_enabled ) + { + lsmash_bs_read( bs, 4 ); + als_copy_from_buffer( alssc, bs, 4 ); + } + } + /* Random access units */ + { + uint32_t number_of_frames = ((alssc->samples + alssc->frame_length) / (alssc->frame_length + 1)); + if( alssc->random_access != 0 ) + alssc->number_of_ra_units = ((uint64_t)number_of_frames + alssc->random_access - 1) / alssc->random_access; + else + alssc->number_of_ra_units = 0; + if( alssc->ra_flag == 2 && alssc->random_access != 0 ) + { + /* We don't copy all ra_unit_size into alssc->sc_data. */ + int64_t read_size = (int64_t)alssc->number_of_ra_units * 4; + int64_t end_offset = lsmash_bs_get_stream_pos( bs ) + read_size; + alssc->ra_unit_size = lsmash_malloc( read_size ); + if( !alssc->ra_unit_size ) + return LSMASH_ERR_MEMORY_ALLOC; + uint32_t max_ra_unit_size = 0; + for( uint32_t i = 0; i < alssc->number_of_ra_units; i++ ) + { + alssc->ra_unit_size[i] = lsmash_bs_get_be32( bs ); + max_ra_unit_size = LSMASH_MAX( max_ra_unit_size, alssc->ra_unit_size[i] ); + } + lsmash_bs_read_seek( bs, end_offset, SEEK_SET ); + } + else + alssc->ra_unit_size = NULL; + } + /* auxiliary data */ + if( aux_data_enabled ) + { + uint32_t aux_size = lsmash_bs_show_be32( bs, 0 ); + als_copy_from_buffer( alssc, bs, 4 ); + if( aux_size && aux_size != 0xffffffff ) + { + lsmash_bs_read( bs, aux_size ); + als_copy_from_buffer( alssc, bs, aux_size ); + } + } + /* Set 0 to ra_flag. We will remove ra_unit_size in each access unit. */ + alssc->sc_data[18] &= 0x3f; + return 0; +} + +static int mp4a_als_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + mp4a_als_importer_t *als_imp = (mp4a_als_importer_t *)importer->info; + importer_status current_status = als_imp->status; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + lsmash_bs_t *bs = als_imp->bs; + als_specific_config_t *alssc = &als_imp->alssc; + if( alssc->number_of_ra_units == 0 ) + { + memcpy( buffered_sample->data, lsmash_bs_get_buffer_data( bs ), alssc->access_unit_size ); + buffered_sample->length = alssc->access_unit_size; + buffered_sample->cts = 0; + buffered_sample->dts = 0; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + als_imp->status = IMPORTER_EOF; + return 0; + } + uint32_t au_length; + if( alssc->ra_flag == 2 ) + au_length = alssc->ra_unit_size[ als_imp->au_number ]; + else /* if( alssc->ra_flag == 1 ) */ + /* We don't export ra_unit_size into a sample. */ + au_length = lsmash_bs_get_be32( bs ); + if( buffered_sample->length < au_length ) + { + lsmash_log( importer, LSMASH_LOG_WARNING, "the buffer has not enough size.\n" ); + return LSMASH_ERR_NAMELESS; + } + if( lsmash_bs_get_bytes_ex( bs, au_length, buffered_sample->data ) != au_length ) + { + lsmash_log( importer, LSMASH_LOG_WARNING, "failed to read an access unit.\n" ); + als_imp->status = IMPORTER_ERROR; + return LSMASH_ERR_INVALID_DATA; + } + buffered_sample->length = au_length; + buffered_sample->dts = als_imp->au_number ++ * als_imp->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + if( als_imp->au_number == alssc->number_of_ra_units ) + als_imp->status = IMPORTER_EOF; + return 0; +} + +#undef CHECK_UPDATE + +static lsmash_audio_summary_t *als_create_summary( lsmash_bs_t *bs, als_specific_config_t *alssc ) +{ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; + summary->aot = MP4A_AUDIO_OBJECT_TYPE_ALS; + summary->frequency = alssc->samp_freq; + summary->channels = alssc->channels + 1; + summary->sample_size = (alssc->resolution + 1) * 8; + summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ + if( alssc->random_access != 0 ) + { + summary->samples_in_frame = (alssc->frame_length + 1) * alssc->random_access; + summary->max_au_length = summary->channels * (summary->sample_size / 8) * summary->samples_in_frame; + } + else + { + /* Read the remainder of overall stream as an access unit. */ + alssc->access_unit_size = lsmash_bs_get_remaining_buffer_size( bs ); + while( !bs->eof ) + { + if( lsmash_bs_read( bs, bs->buffer.max_size ) < 0 ) + return NULL; + alssc->access_unit_size = lsmash_bs_get_remaining_buffer_size( bs ); + } + summary->max_au_length = alssc->access_unit_size; + summary->samples_in_frame = 0; /* hack for mp4sys_als_importer_get_last_delta() */ + } + uint32_t data_length; + uint8_t *data = mp4a_export_AudioSpecificConfig( MP4A_AUDIO_OBJECT_TYPE_ALS, + summary->frequency, summary->channels, summary->sbr_mode, + alssc->sc_data, alssc->size, &data_length ); + if( !data ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; + } + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_free( data ); + return NULL; + } + lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured; + param->objectTypeIndication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3; + param->streamType = MP4SYS_STREAM_TYPE_AudioStream; + if( lsmash_set_mp4sys_decoder_specific_info( param, data, data_length ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + lsmash_free( data ); + return NULL; + } + lsmash_free( data ); + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + return summary; +} + +static int mp4a_als_importer_probe( importer_t *importer ) +{ + mp4a_als_importer_t *als_imp = create_mp4a_als_importer( importer ); + if( !als_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = als_imp->bs; + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = BS_MAX_DEFAULT_READ_SIZE; + /* Parse ALS specific configuration. */ + int err = als_parse_specific_config( als_imp ); + if( err < 0 ) + goto fail; + lsmash_audio_summary_t *summary = als_create_summary( bs, &als_imp->alssc ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + /* importer status */ + als_imp->status = IMPORTER_OK; + als_imp->samples_in_frame = summary->samples_in_frame; + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + importer->info = als_imp; + return 0; +fail: + remove_mp4a_als_importer( als_imp ); + return err; +} + +static uint32_t mp4a_als_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + mp4a_als_importer_t *als_imp = (mp4a_als_importer_t *)importer->info; + if( !als_imp || track_number != 1 || als_imp->status != IMPORTER_EOF ) + return 0; + als_specific_config_t *alssc = &als_imp->alssc; + /* If alssc->number_of_ra_units == 0, then the last sample duration is just alssc->samples + * since als_create_summary sets 0 to summary->samples_in_frame i.e. als_imp->samples_in_frame. */ + return alssc->samples - (alssc->number_of_ra_units - 1) * als_imp->samples_in_frame; +} + +const importer_functions mp4a_als_importer = +{ + { "MPEG-4 ALS", offsetof( importer_t, log_level ) }, + 1, + mp4a_als_importer_probe, + mp4a_als_importer_get_accessunit, + mp4a_als_importer_get_last_delta, + mp4a_als_importer_cleanup +}; diff -Nru l-smash-1.9.1/cli/amr_imp.c l-smash-2.3.0/cli/amr_imp.c --- l-smash-1.9.1/cli/amr_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/amr_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,305 @@ +/***************************************************************************** + * amr_imp.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + AMR-NB/WB storage format importer + 3GPP TS 26.101 V11.0.0 (2012-9) + 3GPP TS 26.201 V11.0.0 (2012-9) + 3GPP TS 26.244 V12.3.0 (2014-03) + http://www.ietf.org/rfc/rfc3267.txt (Obsoleted) + http://www.ietf.org/rfc/rfc4867.txt +***************************************************************************/ +typedef struct +{ + importer_status status; + lsmash_bs_t *bs; + int wb; /* 0: AMR-NB, 1: AMR-WB */ + uint32_t samples_in_frame; + uint32_t au_number; +} amr_importer_t; + +static void remove_amr_importer +( + amr_importer_t *amr_imp +) +{ + lsmash_bs_cleanup( amr_imp->bs ); + lsmash_free( amr_imp ); +} + +static amr_importer_t *create_amr_importer +( + importer_t *importer +) +{ + amr_importer_t *amr_imp = (amr_importer_t *)lsmash_malloc_zero( sizeof(amr_importer_t) ); + if( !amr_imp ) + return NULL; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + { + lsmash_free( amr_imp ); + return NULL; + } + amr_imp->bs = bs; + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = BS_MAX_DEFAULT_READ_SIZE; + return amr_imp; +} + +static void amr_cleanup +( + importer_t *importer +) +{ + debug_if( importer && importer->info ) + remove_amr_importer( importer->info ); +} + +static int amr_get_accessunit +( + importer_t *importer, + uint32_t track_number, + lsmash_sample_t *buffered_sample +) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + amr_importer_t *amr_imp = (amr_importer_t *)importer->info; + lsmash_bs_t *bs = amr_imp->bs; + if( amr_imp->status == IMPORTER_EOF || lsmash_bs_is_end( bs, 0 ) ) + { + /* EOF */ + amr_imp->status = IMPORTER_EOF; + buffered_sample->length = 0; + return 0; + } + /* Each speech frame consists of one speech frame header and one speech data. + * At the end of each speech data, octet alignment if needed. + * Speech frame header + * 0 1 2 3 4 5 6 7 + * +-+-------+-+-+-+ + * |P| FT |Q|P|P| + * +-+-------+-+-+-+ + * FT: Frame type index + * Q : Frame quality indicator + * P : Must be set to 0 + * FT= 9, 10 and 11 for AMR-NB shall not be used in the file format. + * FT=12, 13 and 14 for AMR-NB are not defined yet in the file format. + * FT=10, 11, 12 and 13 for AMR-WB are not defined yet in the file format. + * FT determines the size of the speech frame starting with it. + */ + uint8_t FT = (lsmash_bs_show_byte( bs, 0 ) >> 3) & 0x0F; + const int frame_size[2][16] = + { + { 13, 14, 16, 18, 20, 21, 27, 32, 6, -1, -1, -1, 0, 0, 0, 1 }, + { 18, 24, 33, 37, 41, 47, 51, 59, 61, 6, 0, 0, 0, 0, 1, 1 } + }; + int read_size = frame_size[ amr_imp->wb ][FT]; + if( read_size <= 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "an %s speech frame is detected.\n", read_size < 0 ? "invalid" : "unknown" ); + amr_imp->status = IMPORTER_ERROR; + return read_size < 0 ? LSMASH_ERR_INVALID_DATA : LSMASH_ERR_NAMELESS; + } + if( buffered_sample->length < read_size ) + return LSMASH_ERR_NAMELESS; + if( lsmash_bs_get_bytes_ex( bs, read_size, buffered_sample->data ) != read_size ) + { + lsmash_log( importer, LSMASH_LOG_WARNING, "the stream is truncated at the end.\n" ); + amr_imp->status = IMPORTER_EOF; + return LSMASH_ERR_INVALID_DATA; + } + buffered_sample->length = read_size; + buffered_sample->dts = amr_imp->au_number ++ * amr_imp->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + return 0; +} + +static int amr_check_magic_number +( + lsmash_bs_t *bs +) +{ +#define AMR_STORAGE_MAGIC_LENGTH 6 +#define AMR_AMRWB_EX_MAGIC_LENGTH 3 + /* Check the magic number for single-channel AMR-NB/AMR-WB files. + * For AMR-NB, "#!AMR\n" (or 0x2321414d520a in hexadecimal). + * For AMR-WB, "#!AMR-WB\n" (or 0x2321414d522d57420a in hexadecimal). + * Note that AMR-NB and AMR-WB data is stored in the 3GPP/3GPP2 file format according to + * the AMR-NB and AMR-WB storage format for single channel header without the AMR magic numbers. */ + uint8_t buf[AMR_STORAGE_MAGIC_LENGTH]; + if( lsmash_bs_get_bytes_ex( bs, AMR_STORAGE_MAGIC_LENGTH, buf ) != AMR_STORAGE_MAGIC_LENGTH + || memcmp( buf, "#!AMR", AMR_STORAGE_MAGIC_LENGTH - 1 ) ) + return LSMASH_ERR_INVALID_DATA; + if( buf[AMR_STORAGE_MAGIC_LENGTH - 1] == '\n' ) + /* single-channel AMR-NB file */ + return 0; + if( buf[AMR_STORAGE_MAGIC_LENGTH - 1] != '-' + || lsmash_bs_get_bytes_ex( bs, AMR_AMRWB_EX_MAGIC_LENGTH, buf ) != AMR_AMRWB_EX_MAGIC_LENGTH + || memcmp( buf, "WB\n", AMR_AMRWB_EX_MAGIC_LENGTH ) ) + return LSMASH_ERR_INVALID_DATA; + /* single-channel AMR-WB file */ + return 1; +#undef AMR_STORAGE_MAGIC_LENGTH +#undef AMR_AMRWB_EX_MAGIC_LENGTH +} + +static int amr_create_damr +( + lsmash_audio_summary_t *summary, + int wb +) +{ +#define AMR_DAMR_LENGTH 17 + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_put_be32( bs, AMR_DAMR_LENGTH ); + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_DAMR.fourcc ); + /* 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, wb ? 0xC3FF : 0x81FF ); /* mode_set, represents for all possibly existing and supported frame-types. */ + lsmash_bs_put_byte( bs, 1 ); /* mode_change_period */ + lsmash_bs_put_byte( bs, 1 ); /* frames_per_sample */ + lsmash_codec_specific_t *cs = lsmash_malloc_zero( sizeof(lsmash_codec_specific_t) ); + if( !cs ) + { + lsmash_bs_cleanup( bs ); + return LSMASH_ERR_MEMORY_ALLOC; + } + cs->type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; + cs->format = LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED; + cs->destruct = (lsmash_codec_specific_destructor_t)lsmash_free; + cs->data.unstructured = lsmash_bs_export_data( bs, &cs->size ); + cs->size = AMR_DAMR_LENGTH; + lsmash_bs_cleanup( bs ); + if( !cs->data.unstructured + || lsmash_add_entry( &summary->opaque->list, cs ) < 0 ) + { + lsmash_destroy_codec_specific_data( cs ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +#undef AMR_DAMR_LENGTH +} + +static lsmash_audio_summary_t *amr_create_summary +( + importer_t *importer, + int wb +) +{ + /* Establish an audio summary for AMR-NB or AMR-WB stream. */ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + summary->sample_type = wb ? ISOM_CODEC_TYPE_SAWB_AUDIO : ISOM_CODEC_TYPE_SAMR_AUDIO; + summary->max_au_length = wb ? 61 : 32; + summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ + summary->frequency = (8000 << wb); + summary->channels = 1; /* always single channel */ + summary->sample_size = 16; + summary->samples_in_frame = (160 << wb); + summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ + if( amr_create_damr( summary, wb ) < 0 + || lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; + } + return summary; +} + +static int amr_probe +( + importer_t *importer +) +{ + + amr_importer_t *amr_imp = create_amr_importer( importer ); + if( !amr_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + int err; + int wb = amr_check_magic_number( amr_imp->bs ); + if( wb < 0 ) + { + err = wb; + goto fail; + } + lsmash_audio_summary_t *summary = amr_create_summary( importer, wb ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + amr_imp->status = IMPORTER_OK; + amr_imp->wb = wb; + amr_imp->samples_in_frame = summary->samples_in_frame; + amr_imp->au_number = 0; + importer->info = amr_imp; + return 0; +fail: + remove_amr_importer( amr_imp ); + return err; +} + +static uint32_t amr_get_last_delta +( + importer_t *importer, + uint32_t track_number +) +{ + debug_if( !importer || !importer->info ) + return 0; + amr_importer_t *amr_imp = (amr_importer_t *)importer->info; + if( !amr_imp || track_number != 1 ) + return 0; + return amr_imp->samples_in_frame; +} + +const importer_functions amr_importer = +{ + { "AMR", offsetof( importer_t, log_level ) }, + 1, + amr_probe, + amr_get_accessunit, + amr_get_last_delta, + amr_cleanup +}; diff -Nru l-smash-1.9.1/cli/boxdumper.c l-smash-2.3.0/cli/boxdumper.c --- l-smash-1.9.1/cli/boxdumper.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/boxdumper.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,188 @@ +/***************************************************************************** + * boxdumper.c: + ***************************************************************************** + * Copyright (C) 2010-2014 L-SMASH project + * + * Authors: Yusuke Nakamura + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *****************************************************************************/ + +/* This file is available under an ISC license. */ + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +#include "lsmash.h" +#include "cli.h" + +#include "config.h" + +#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) + +static void display_version( void ) +{ + eprintf( "\n" + "L-SMASH isom/mov structual analyzer rev%s %s\n" + "Built on %s %s\n" + "Copyright (C) 2010-2014 L-SMASH project\n", + LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); +} + +static void display_help( void ) +{ + display_version(); + eprintf( "\n" + "Usage: boxdumper [option] input\n" + " options:\n" + " --help Display help\n" + " --version Display version information\n" + " --box Dump box structure\n" + " --chapter Extract chapter list\n" + " --timestamp Dump media timestamps\n" ); +} + +static int boxdumper_error +( + lsmash_root_t *root, + lsmash_file_parameters_t *file_param, + const char *message +) +{ + lsmash_close_file( file_param ); + lsmash_destroy_root( root ); + eprintf( "%s", message ); + return -1; +} + +#define BOXDUMPER_ERR( message ) boxdumper_error( root, &file_param, message ) +#define DO_NOTHING + +int main( int argc, char *argv[] ) +{ + if ( argc < 2 ) + { + display_help(); + return -1; + } + else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) + { + display_help(); + return 0; + } + else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) + { + display_version(); + return 0; + } + int dump_box = 1; + int chapter = 0; + char *filename; + lsmash_get_mainargs( &argc, &argv ); + if( argc > 2 ) + { + if( !strcasecmp( argv[1], "--box" ) ) + DO_NOTHING; + else if( !strcasecmp( argv[1], "--chapter" ) ) + chapter = 1; + else if( !strcasecmp( argv[1], "--timestamp" ) ) + dump_box = 0; + else + { + display_help(); + return -1; + } + filename = argv[2]; + } + else + { + filename = argv[1]; + } +#ifdef _WIN32 + _setmode( _fileno(stdin), _O_BINARY ); +#endif + /* Open the input file. */ + lsmash_root_t *root = lsmash_create_root(); + if( !root ) + { + fprintf( stderr, "Failed to create a ROOT.\n" ); + return -1; + } + lsmash_file_parameters_t file_param = { 0 }; + if( lsmash_open_file( filename, 1, &file_param ) < 0 ) + return BOXDUMPER_ERR( "Failed to open an input file.\n" ); + if( dump_box ) + file_param.mode |= LSMASH_FILE_MODE_DUMP; + lsmash_file_t *file = lsmash_set_file( root, &file_param ); + if( !file ) + return BOXDUMPER_ERR( "Failed to add a file into a ROOT.\n" ); + if( lsmash_read_file( file, &file_param ) < 0 ) + return BOXDUMPER_ERR( "Failed to read a file\n" ); + /* Dump the input file. */ + if( chapter ) + { + if( lsmash_print_chapter_list( root ) ) + return BOXDUMPER_ERR( "Failed to extract chapter.\n" ); + } + else if( dump_box ) + { + if( lsmash_print_movie( root, "-" ) ) + return BOXDUMPER_ERR( "Failed to dump box structure.\n" ); + } + else + { + lsmash_movie_parameters_t movie_param; + lsmash_initialize_movie_parameters( &movie_param ); + lsmash_get_movie_parameters( root, &movie_param ); + uint32_t num_tracks = movie_param.number_of_tracks; + for( uint32_t track_number = 1; track_number <= num_tracks; track_number++ ) + { + uint32_t track_ID = lsmash_get_track_ID( root, track_number ); + if( !track_ID ) + return BOXDUMPER_ERR( "Failed to get track_ID.\n" ); + lsmash_media_parameters_t media_param; + lsmash_initialize_media_parameters( &media_param ); + if( lsmash_get_media_parameters( root, track_ID, &media_param ) ) + return BOXDUMPER_ERR( "Failed to get media parameters.\n" ); + if( lsmash_construct_timeline( root, track_ID ) ) + return BOXDUMPER_ERR( "Failed to construct timeline.\n" ); + uint32_t timeline_shift; + if( lsmash_get_composition_to_decode_shift_from_media_timeline( root, track_ID, &timeline_shift ) ) + return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); + lsmash_media_ts_list_t ts_list; + if( lsmash_get_media_timestamps( root, track_ID, &ts_list ) ) + return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); + fprintf( stdout, "track_ID: %"PRIu32"\n", track_ID ); + fprintf( stdout, "Media timescale: %"PRIu32"\n", media_param.timescale ); + lsmash_media_ts_t *ts_array = ts_list.timestamp; + if( !ts_array ) + { + fprintf( stdout, "\n" ); + continue; + } + for( uint32_t i = 0; i < ts_list.sample_count; i++ ) + fprintf( stdout, "DTS = %"PRIu64", CTS = %"PRIu64"\n", ts_array[i].dts, ts_array[i].cts + timeline_shift ); + lsmash_free( ts_array ); + fprintf( stdout, "\n" ); + } + } + lsmash_destroy_root( root ); + return 0; +} diff -Nru l-smash-1.9.1/cli/cli.c l-smash-2.3.0/cli/cli.c --- l-smash-1.9.1/cli/cli.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/cli.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,61 @@ +/***************************************************************************** + * cli.c: + ***************************************************************************** + * Copyright (C) 2013-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#ifdef _WIN32 +void lsmash_get_mainargs( int *argc, char ***argv ) +{ + struct SI { int newmode; } si = { 0 }; + int __wgetmainargs( int *, wchar_t ***, wchar_t ***, int, struct SI * ); + wchar_t **wargv, **envp; + __wgetmainargs( argc, &wargv, &envp, 1, &si ); + *argv = lsmash_malloc_zero( (*argc + 1) * sizeof(char *) ); + for( int i = 0; i < *argc; ++i ) + lsmash_string_from_wchar( CP_UTF8, wargv[i], &(*argv)[i] ); +} +#endif + +int lsmash_write_lsmash_indicator( lsmash_root_t *root ) +{ + /* Write a tag in a free space to indicate the output file is written by L-SMASH. */ + char *string = "Multiplexed by L-SMASH"; + int length = strlen( string ); + lsmash_box_type_t type = lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'r', 'e', 'e' ) ); + lsmash_box_t *free_box = lsmash_create_box( type, (uint8_t *)string, length, LSMASH_BOX_PRECEDENCE_N ); + if( !free_box ) + return 0; + if( lsmash_add_box_ex( lsmash_root_as_box( root ), &free_box ) < 0 ) + { + lsmash_destroy_box( free_box ); + return 0; + } + return lsmash_write_top_level_box( free_box ); +} diff -Nru l-smash-1.9.1/cli/cli.h l-smash-2.3.0/cli/cli.h --- l-smash-1.9.1/cli/cli.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/cli.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,34 @@ +/***************************************************************************** + * cli.h: + ***************************************************************************** + * Copyright (C) 2013-2014 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 CLI_H +#define CLI_H + +#ifdef _WIN32 + void lsmash_get_mainargs( int *argc, char ***argv ); +#else +# define lsmash_get_mainargs( argc, argv ) (void)0 +#endif + +int lsmash_write_lsmash_indicator( lsmash_root_t *root ); + +#endif diff -Nru l-smash-1.9.1/cli/dts_imp.c l-smash-2.3.0/cli/dts_imp.c --- l-smash-1.9.1/cli/dts_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/dts_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,363 @@ +/***************************************************************************** + * dts_imp.c + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + DTS importer + ETSI TS 102 114 V1.2.1 (2002-12) + ETSI TS 102 114 V1.3.1 (2011-08) + ETSI TS 102 114 V1.4.1 (2012-09) +***************************************************************************/ +#include "codecs/dts.h" + +typedef struct +{ + importer_status status; + dts_info_t info; + uint64_t next_frame_pos; + uint8_t buffer[DTS_MAX_EXSS_SIZE]; + lsmash_multiple_buffers_t *au_buffers; + uint8_t *au; + uint32_t au_length; + uint8_t *incomplete_au; + uint32_t incomplete_au_length; + uint32_t au_number; +} dts_importer_t; + +static void remove_dts_importer( dts_importer_t *dts_imp ) +{ + if( !dts_imp ) + return; + lsmash_destroy_multiple_buffers( dts_imp->au_buffers ); + lsmash_bits_adhoc_cleanup( dts_imp->info.bits ); + lsmash_free( dts_imp ); +} + +static dts_importer_t *create_dts_importer( void ) +{ + dts_importer_t *dts_imp = (dts_importer_t *)lsmash_malloc_zero( sizeof(dts_importer_t) ); + if( !dts_imp ) + return NULL; + dts_info_t *dts_info = &dts_imp->info; + dts_info->bits = lsmash_bits_adhoc_create(); + if( !dts_info->bits ) + { + lsmash_free( dts_imp ); + return NULL; + } + dts_imp->au_buffers = lsmash_create_multiple_buffers( 2, DTS_MAX_EXSS_SIZE ); + if( !dts_imp->au_buffers ) + { + lsmash_bits_adhoc_cleanup( dts_info->bits ); + lsmash_free( dts_imp ); + return NULL; + } + dts_imp->au = lsmash_withdraw_buffer( dts_imp->au_buffers, 1 ); + dts_imp->incomplete_au = lsmash_withdraw_buffer( dts_imp->au_buffers, 2 ); + dts_setup_parser( dts_info ); + return dts_imp; +} + +static void dts_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_dts_importer( importer->info ); +} + +static int dts_importer_get_next_accessunit_internal( importer_t *importer ) +{ + int au_completed = 0; + dts_importer_t *dts_imp = (dts_importer_t *)importer->info; + dts_info_t *info = &dts_imp->info; + lsmash_bs_t *bs = info->bits->bs; + while( !au_completed ) + { + /* Read data from the stream if needed. */ + dts_imp->next_frame_pos += info->frame_size; + lsmash_bs_read_seek( bs, dts_imp->next_frame_pos, SEEK_SET ); + uint64_t remain_size = lsmash_bs_get_remaining_buffer_size( bs ); + if( remain_size < DTS_MAX_EXSS_SIZE ) + { + int err = lsmash_bs_read( bs, bs->buffer.max_size ); + if( err < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to read data from the stream.\n" ); + return err; + } + remain_size = lsmash_bs_get_remaining_buffer_size( bs ); + } + memcpy( dts_imp->buffer, lsmash_bs_get_buffer_data( bs ), LSMASH_MIN( remain_size, DTS_MAX_EXSS_SIZE ) ); + /* Check the remainder length of the buffer. + * If there is enough length, then parse the frame in it. + * The length 10 is the required byte length to get frame size. */ + if( bs->eob || (bs->eof && remain_size < 10) ) + { + /* Reached the end of stream. */ + dts_imp->status = IMPORTER_EOF; + au_completed = !!dts_imp->incomplete_au_length; + if( !au_completed ) + { + /* No more access units in the stream. */ + if( lsmash_bs_get_remaining_buffer_size( bs ) ) + { + lsmash_log( importer, LSMASH_LOG_WARNING, "the stream is truncated at the end.\n" ); + return LSMASH_ERR_INVALID_DATA; + } + return 0; + } + if( !info->ddts_param_initialized ) + dts_update_specific_param( info ); + } + else + { + /* Parse substream frame. */ + dts_substream_type prev_substream_type = info->substream_type; + info->substream_type = dts_get_substream_type( info ); + int err; + int (*dts_parse_frame)( dts_info_t * ) = NULL; + switch( info->substream_type ) + { + /* Decide substream frame parser and check if this frame and the previous frame belong to the same AU. */ + case DTS_SUBSTREAM_TYPE_CORE : + if( prev_substream_type != DTS_SUBSTREAM_TYPE_NONE ) + au_completed = 1; + dts_parse_frame = dts_parse_core_substream; + break; + case DTS_SUBSTREAM_TYPE_EXTENSION : + { + uint8_t prev_exss_index = info->exss_index; + if( (err = dts_get_exss_index( info, &info->exss_index )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to get the index of an extension substream.\n" ); + return err; + } + if( prev_substream_type == DTS_SUBSTREAM_TYPE_EXTENSION + && info->exss_index <= prev_exss_index ) + au_completed = 1; + dts_parse_frame = dts_parse_extension_substream; + break; + } + default : + lsmash_log( importer, LSMASH_LOG_ERROR, "unknown substream type is detected.\n" ); + return LSMASH_ERR_NAMELESS; + } + if( !info->ddts_param_initialized && au_completed ) + dts_update_specific_param( info ); + info->frame_size = 0; + if( (err = dts_parse_frame( info )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to parse a frame.\n" ); + return err; + } + } + if( au_completed ) + { + memcpy( dts_imp->au, dts_imp->incomplete_au, dts_imp->incomplete_au_length ); + dts_imp->au_length = dts_imp->incomplete_au_length; + dts_imp->incomplete_au_length = 0; + info->exss_count = (info->substream_type == DTS_SUBSTREAM_TYPE_EXTENSION); + if( dts_imp->status == IMPORTER_EOF ) + break; + } + /* Increase buffer size to store AU if short. */ + if( dts_imp->incomplete_au_length + info->frame_size > dts_imp->au_buffers->buffer_size ) + { + lsmash_multiple_buffers_t *temp = lsmash_resize_multiple_buffers( dts_imp->au_buffers, + dts_imp->au_buffers->buffer_size + DTS_MAX_EXSS_SIZE ); + if( !temp ) + return LSMASH_ERR_MEMORY_ALLOC; + dts_imp->au_buffers = temp; + dts_imp->au = lsmash_withdraw_buffer( dts_imp->au_buffers, 1 ); + dts_imp->incomplete_au = lsmash_withdraw_buffer( dts_imp->au_buffers, 2 ); + } + /* Append frame data. */ + memcpy( dts_imp->incomplete_au + dts_imp->incomplete_au_length, dts_imp->buffer, info->frame_size ); + dts_imp->incomplete_au_length += info->frame_size; + } + return info->bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + dts_importer_t *dts_imp = (dts_importer_t *)importer->info; + dts_info_t *info = &dts_imp->info; + importer_status current_status = dts_imp->status; + if( current_status == IMPORTER_ERROR || buffered_sample->length < dts_imp->au_length ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF && dts_imp->au_length == 0 ) + { + buffered_sample->length = 0; + return 0; + } + if( current_status == IMPORTER_CHANGE ) + summary->max_au_length = 0; + memcpy( buffered_sample->data, dts_imp->au, dts_imp->au_length ); + buffered_sample->length = dts_imp->au_length; + buffered_sample->dts = dts_imp->au_number++ * summary->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + buffered_sample->prop.pre_roll.distance = !!(info->flags & DTS_EXT_SUBSTREAM_LBR_FLAG); /* MDCT */ + if( dts_imp->status == IMPORTER_EOF ) + { + dts_imp->au_length = 0; + return 0; + } + if( dts_importer_get_next_accessunit_internal( importer ) < 0 ) + dts_imp->status = IMPORTER_ERROR; + return current_status; +} + +static lsmash_audio_summary_t *dts_create_summary( dts_info_t *info ) +{ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + lsmash_dts_specific_parameters_t *param = &info->ddts_param; + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS, + LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + specific->data.unstructured = lsmash_create_dts_specific_info( param, &specific->size ); + if( !specific->data.unstructured + || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + /* The CODEC identifiers probably should not be the combination of 'mp4a' and + * the objectTypeIndications for DTS audio since there is no public specification + * which defines the encapsulation of the stream as the MPEG-4 Audio context yet. + * In the world, there are muxers which is using such doubtful implementation. + * The objectTypeIndications are registered at MP4RA, but this does not always + * mean we can mux by using those objectTypeIndications. + * If available, there shall be the specification which defines the existence of + * DecoderSpecificInfo and its semantics, and what access unit consists of. */ + summary->sample_type = lsmash_dts_get_codingname( param ); + summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* make no sense */ + summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* make no sense */ + switch( param->DTSSamplingFrequency ) + { + case 12000 : /* Invalid? (No reference in the spec) */ + case 24000 : + case 48000 : + case 96000 : + case 192000 : + case 384000 : /* Invalid? (No reference in the spec) */ + summary->frequency = 48000; + break; + case 22050 : + case 44100 : + case 88200 : + case 176400 : + case 352800 : /* Invalid? (No reference in the spec) */ + summary->frequency = 44100; + break; + case 8000 : /* Invalid? (No reference in the spec) */ + case 16000 : + case 32000 : + case 64000 : + case 128000 : + summary->frequency = 32000; + break; + default : + summary->frequency = 0; + break; + } + summary->samples_in_frame = (summary->frequency * info->frame_duration) / param->DTSSamplingFrequency; + summary->max_au_length = DTS_MAX_CORE_SIZE + DTS_MAX_NUM_EXSS * DTS_MAX_EXSS_SIZE; + summary->sample_size = param->pcmSampleDepth; + summary->channels = dts_get_max_channel_count( info ); + return summary; +} + +static int dts_importer_probe( importer_t *importer ) +{ + dts_importer_t *dts_imp = create_dts_importer(); + if( !dts_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bits_t *bits = dts_imp->info.bits; + lsmash_bs_t *bs = bits->bs; + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = DTS_MAX_EXSS_SIZE; + importer->info = dts_imp; + int err = dts_importer_get_next_accessunit_internal( importer ); + if( err < 0 ) + goto fail; + lsmash_audio_summary_t *summary = dts_create_summary( &dts_imp->info ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + if( dts_imp->status != IMPORTER_EOF ) + dts_imp->status = IMPORTER_OK; + dts_imp->au_number = 0; + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + return 0; +fail: + remove_dts_importer( dts_imp ); + importer->info = NULL; + return err; +} + +static uint32_t dts_importer_get_last_delta( importer_t* importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + dts_importer_t *dts_imp = (dts_importer_t *)importer->info; + if( !dts_imp || track_number != 1 || dts_imp->status != IMPORTER_EOF || dts_imp->au_length ) + return 0; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); + if( !summary ) + return 0; + return (summary->frequency * dts_imp->info.frame_duration) / dts_imp->info.ddts_param.DTSSamplingFrequency; +} + +const importer_functions dts_importer = +{ + { "DTS Coherent Acoustics", offsetof( importer_t, log_level ) }, + 1, + dts_importer_probe, + dts_importer_get_accessunit, + dts_importer_get_last_delta, + dts_importer_cleanup +}; diff -Nru l-smash-1.9.1/cli/importer.c l-smash-2.3.0/cli/importer.c --- l-smash-1.9.1/cli/importer.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/importer.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,221 @@ +/***************************************************************************** + * importer.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + importer classes +***************************************************************************/ +static const lsmash_class_t lsmash_importer_class = +{ + "importer", + offsetof( importer_t, log_level ) +}; + +extern const importer_functions mp4sys_adts_importer; +extern const importer_functions mp4sys_mp3_importer; +extern const importer_functions amr_importer; +extern const importer_functions ac3_importer; +extern const importer_functions eac3_importer; +extern const importer_functions mp4a_als_importer; +extern const importer_functions dts_importer; +extern const importer_functions h264_importer; +extern const importer_functions hevc_importer; +extern const importer_functions vc1_importer; + +/******** importer listing table ********/ +static const importer_functions *importer_func_table[] = +{ + &mp4sys_adts_importer, + &mp4sys_mp3_importer, + &amr_importer, + &ac3_importer, + &eac3_importer, + &mp4a_als_importer, + &dts_importer, + &h264_importer, + &hevc_importer, + &vc1_importer, + NULL, +}; + +/*************************************************************************** + importer public interfaces +***************************************************************************/ + +/******** importer public functions ********/ + +void lsmash_importer_close( importer_t *importer ) +{ + if( !importer ) + return; + if( !importer->is_stdin && importer->stream ) + fclose( importer->stream ); + if( importer->funcs.cleanup ) + importer->funcs.cleanup( importer ); + lsmash_remove_list( importer->summaries, lsmash_cleanup_summary ); + lsmash_free( importer ); +} + +importer_t *lsmash_importer_open( const char *identifier, const char *format ) +{ + if( identifier == NULL ) + return NULL; + int auto_detect = ( format == NULL || !strcmp( format, "auto" ) ); + importer_t *importer = (importer_t *)lsmash_malloc_zero( sizeof(importer_t) ); + if( !importer ) + return NULL; + importer->class = &lsmash_importer_class; + if( !strcmp( identifier, "-" ) ) + { + /* special treatment for stdin */ + if( auto_detect ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "auto importer detection on stdin is not supported.\n" ); + goto fail; + } + importer->stream = stdin; + importer->is_stdin = 1; + } + else if( (importer->stream = lsmash_fopen( identifier, "rb" )) == NULL ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to open %s.\n", identifier ); + goto fail; + } + importer->summaries = lsmash_create_entry_list(); + if( !importer->summaries ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to set up the importer.\n" ); + goto fail; + } + /* find importer */ + importer->log_level = LSMASH_LOG_QUIET; /* Any error log is confusing for the probe step. */ + const importer_functions *funcs; + if( auto_detect ) + { + /* just rely on detector. */ + for( int i = 0; (funcs = importer_func_table[i]) != NULL; i++ ) + { + importer->class = &funcs->class; + if( !funcs->detectable ) + continue; + if( !funcs->probe( importer ) || lsmash_fseek( importer->stream, 0, SEEK_SET ) != 0 ) + break; + } + } + else + { + /* needs name matching. */ + for( int i = 0; (funcs = importer_func_table[i]) != NULL; i++ ) + { + importer->class = &funcs->class; + if( strcmp( importer->class->name, format ) ) + continue; + if( funcs->probe( importer ) < 0 ) + funcs = NULL; + break; + } + } + importer->log_level = LSMASH_LOG_INFO; + if( !funcs ) + { + importer->class = &lsmash_importer_class; + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to find the matched importer.\n" ); + goto fail; + } + importer->funcs = *funcs; + return importer; +fail: + lsmash_importer_close( importer ); + return NULL; +} + +/* 0 if success, positive if changed, negative if failed */ +int lsmash_importer_get_access_unit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer || !buffered_sample->data || buffered_sample->length == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !importer->funcs.get_accessunit ) + return LSMASH_ERR_NAMELESS; + return importer->funcs.get_accessunit( importer, track_number, buffered_sample ); +} + +/* Return 0 if failed, otherwise succeeded. */ +uint32_t lsmash_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + if( !importer || !importer->funcs.get_last_delta ) + return 0; + return importer->funcs.get_last_delta( importer, track_number ); +} + +uint32_t lsmash_importer_get_track_count( importer_t *importer ) +{ + if( !importer || !importer->summaries ) + return 0; + return importer->summaries->entry_count; +} + +lsmash_summary_t *lsmash_duplicate_summary( importer_t *importer, uint32_t track_number ) +{ + if( !importer ) + return NULL; + lsmash_summary_t *src_summary = lsmash_get_entry_data( importer->summaries, track_number ); + if( !src_summary ) + return NULL; + lsmash_summary_t *summary = lsmash_create_summary( src_summary->summary_type ); + if( !summary ) + return NULL; + lsmash_codec_specific_list_t *opaque = summary->opaque; + switch( src_summary->summary_type ) + { + case LSMASH_SUMMARY_TYPE_VIDEO : + *(lsmash_video_summary_t *)summary = *(lsmash_video_summary_t *)src_summary; + break; + case LSMASH_SUMMARY_TYPE_AUDIO : + *(lsmash_audio_summary_t *)summary = *(lsmash_audio_summary_t *)src_summary; + break; + default : + lsmash_cleanup_summary( summary ); + return NULL; + } + summary->opaque = opaque; + for( lsmash_entry_t *entry = src_summary->opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *src_specific = (lsmash_codec_specific_t *)entry->data; + if( !src_specific ) + continue; + lsmash_codec_specific_t *dup = isom_duplicate_codec_specific_data( src_specific ); + if( lsmash_add_entry( &summary->opaque->list, dup ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( dup ); + return NULL; + } + } + return summary; +} diff -Nru l-smash-1.9.1/cli/importer.h l-smash-2.3.0/cli/importer.h --- l-smash-1.9.1/cli/importer.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/importer.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,114 @@ +/***************************************************************************** + * importer.h: + ***************************************************************************** + * Copyright (C) 2010-2014 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 +***************************************************************************/ + +#ifdef LSMASH_IMPORTER_INTERNAL + +#include "core/box.h" +#include "codecs/description.h" + +struct importer_tag; + +typedef void ( *importer_cleanup ) ( struct importer_tag * ); +typedef int ( *importer_get_accessunit ) ( struct importer_tag *, uint32_t, lsmash_sample_t * ); +typedef int ( *importer_probe ) ( struct importer_tag * ); +typedef uint32_t ( *importer_get_last_duration )( struct importer_tag *, uint32_t ); + +typedef struct +{ + lsmash_class_t class; + int detectable; + importer_probe probe; + importer_get_accessunit get_accessunit; + importer_get_last_duration get_last_delta; + importer_cleanup cleanup; +} importer_functions; + +typedef struct importer_tag +{ + const lsmash_class_t *class; + lsmash_log_level log_level; + FILE *stream; /* will be deprecated */ + int is_stdin; + void *info; /* importer internal status information. */ + importer_functions funcs; + lsmash_entry_list_t *summaries; +} importer_t; + +typedef enum +{ + IMPORTER_ERROR = -1, + IMPORTER_OK = 0, + IMPORTER_CHANGE = 1, + IMPORTER_EOF = 2, +} importer_status; + +#else + +typedef void importer_t; + +/* importing functions */ +importer_t *lsmash_importer_open +( + const char *identifier, + const char *format +); + +void lsmash_importer_close +( + importer_t *importer +); + +int lsmash_importer_get_access_unit +( + importer_t *importer, + uint32_t track_number, + lsmash_sample_t *buffered_sample +); + +uint32_t lsmash_importer_get_last_delta +( + importer_t *importer, + uint32_t track_number +); + +uint32_t lsmash_importer_get_track_count +( + importer_t *importer +); + +lsmash_summary_t *lsmash_duplicate_summary +( + importer_t *importer, + uint32_t track_number +); + +#endif /* #ifdef LSMASH_IMPORTER_INTERNAL */ + +#endif /* #ifndef LSMASH_IMPORTER_H */ diff -Nru l-smash-1.9.1/cli/mp3_imp.c l-smash-2.3.0/cli/mp3_imp.c --- l-smash-1.9.1/cli/mp3_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/mp3_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,482 @@ +/***************************************************************************** + * mp3_imp.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + mp3 (Legacy Interface) importer +***************************************************************************/ +#include "codecs/mp4a.h" + +#define USE_MP4SYS_LEGACY_INTERFACE 1 + +#define MP4SYS_MP3_MAX_FRAME_LENGTH (1152*(16/8)*2) +#define MP4SYS_MP3_HEADER_LENGTH 4 +#define MP4SYS_MODE_IS_2CH( mode ) ((mode)!=3) +#define MP4SYS_LAYER_III 0x1 +#define MP4SYS_LAYER_II 0x2 +#define MP4SYS_LAYER_I 0x3 + +static void mp4sys_mp3_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + lsmash_free( importer->info ); +} + +typedef struct +{ + uint16_t syncword; /* <12> */ + uint8_t ID; /* <1> */ + uint8_t layer; /* <2> */ + uint8_t protection_bit; /* <1> */ + 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 = LSMASH_GET_BE32( buf ); + 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; + 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 LSMASH_ERR_INVALID_DATA; + if( header->layer == 0x0 ) return LSMASH_ERR_NAMELESS; /* 0b00: reserved */ + if( header->bitrate_index == 0x0 ) return LSMASH_ERR_PATCH_WELCOME; /* FIXME: "free" bitrate is unsupported currently. */ + if( header->bitrate_index == 0xF ) return LSMASH_ERR_INVALID_DATA; /* Forbidden */ + if( header->sampling_frequency == 0x3 ) return LSMASH_ERR_NAMELESS; /* 0b11: reserved */ + if( header->emphasis == 0x2 ) return LSMASH_ERR_NAMELESS; /* 0b10: reserved */ + return 0; +} + +static const uint32_t mp4sys_mp3_frequency_tbl[2][3] = +{ + { 22050, 24000, 16000 }, /* MPEG-2 BC audio */ + { 44100, 48000, 32000 } /* MPEG-1 audio */ +}; + +static int mp4sys_mp3_samples_in_frame( mp4sys_mp3_header_t *header ) +{ + if( header->layer == MP4SYS_LAYER_I ) + return 384; + else if( header->ID == 1 || header->layer == MP4SYS_LAYER_II ) + return 1152; + else + return 576; +} + +static lsmash_audio_summary_t *mp4sys_mp3_create_summary( mp4sys_mp3_header_t *header, int legacy_mode ) +{ + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; + 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->sample_size = 16; + summary->samples_in_frame = mp4sys_mp3_samples_in_frame( header ); + 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 */ +#if !USE_MP4SYS_LEGACY_INTERFACE /* 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 ) < 0 ) + { + lsmash_cleanup_summary( summary ); + return NULL; + } + } + uint32_t data_length; + uint8_t *data = mp4a_export_AudioSpecificConfig( MP4A_AUDIO_OBJECT_TYPE_Layer_1 + (MP4SYS_LAYER_I - header->layer), + summary->frequency, summary->channels, summary->sbr_mode, + NULL, 0, &data_length ); + if( !data ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; + } +#endif + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); +#if !USE_MP4SYS_LEGACY_INTERFACE + lsmash_free( data ); +#endif + return NULL; + } + lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured; + param->objectTypeIndication = header->ID ? MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3 : MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3; + param->streamType = MP4SYS_STREAM_TYPE_AudioStream; +#if !USE_MP4SYS_LEGACY_INTERFACE + if( lsmash_set_mp4sys_decoder_specific_info( param, data, data_length ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + lsmash_free( data ); + return NULL; + } + lsmash_free( data ); +#endif + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + return summary; +} + +typedef struct +{ + importer_status status; + mp4sys_mp3_header_t header; + uint8_t raw_header[MP4SYS_MP3_HEADER_LENGTH]; + uint32_t samples_in_frame; + uint32_t au_number; + uint16_t main_data_size[32]; /* size of main_data of the last 32 frames, FIFO */ + uint16_t prev_preroll_count; /* number of dependent frames of *previous* frame */ + uint16_t enc_delay; + uint16_t padding; + uint64_t valid_samples; +} mp4sys_mp3_info_t; + +static int parse_xing_info_header( mp4sys_mp3_info_t *info, mp4sys_mp3_header_t *header, uint8_t *frame ) +{ + unsigned int sip = header->protection_bit ? 4 : 6; + unsigned int side_info_size; + if( header->ID == 1 ) + side_info_size = MP4SYS_MODE_IS_2CH( header->mode ) ? 32 : 17; + else + side_info_size = MP4SYS_MODE_IS_2CH( header->mode ) ? 17 : 9; + + uint8_t *mdp = frame + sip + side_info_size; + if( memcmp( mdp, "Info", 4 ) + && memcmp( mdp, "Xing", 4 ) ) + return 0; + uint32_t flags = LSMASH_GET_BE32( &mdp[4] ); + uint32_t off = 8; + uint32_t frame_count = 0; + if( flags & 1 ) + { + frame_count = LSMASH_GET_BE32( &mdp[8] ); + info->valid_samples = (uint64_t)frame_count * mp4sys_mp3_samples_in_frame( header ); + off += 4; + } + if( flags & 2 ) off += 4; /* file size */ + if( flags & 4 ) off += 100; /* TOC */ + if( flags & 8 ) off += 4; /* VBR quality */ + + if( mdp[off] == 'L' ) + { /* LAME header present */ + unsigned int v = LSMASH_GET_BE24( &mdp[off + 21] ); + info->enc_delay = v >> 12; + info->padding = v & 0xfff; + if( frame_count ) + info->valid_samples -= info->enc_delay + info->padding; + } + return 1; +} + +static int parse_vbri_header( mp4sys_mp3_info_t *info, mp4sys_mp3_header_t *header, uint8_t *frame ) +{ + return memcmp( frame + 36, "VBRI", 4 ) == 0; +} + +static int mp4sys_mp3_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + mp4sys_mp3_info_t *info = (mp4sys_mp3_info_t *)importer->info; + mp4sys_mp3_header_t *header = (mp4sys_mp3_header_t *)&info->header; + importer_status current_status = info->status; + /* bitrate */ + const uint32_t bitrate_tbl[2][3][16] = + { + { /* MPEG-2 BC audio */ + { 1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, /* Layer III */ + { 1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, /* Layer II */ + { 1, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } /* Layer I */ + }, + { /* MPEG-1 audio */ + { 1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, /* Layer III */ + { 1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, /* Layer II */ + { 1, 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 ]; + if( bitrate == 0 ) + return LSMASH_ERR_INVALID_DATA; + else if( bitrate == 1 ) + return LSMASH_ERR_PATCH_WELCOME; /* free format */ + /* sampling frequency */ + uint32_t frequency = mp4sys_mp3_frequency_tbl[header->ID][header->sampling_frequency]; + if( frequency == 0 ) + return LSMASH_ERR_NAMELESS; /* reserved */ + /* frame size */ + 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. */ + uint32_t div = frequency; + if( header->layer == MP4SYS_LAYER_III && header->ID == 0 ) + div <<= 1; + frame_size = 144 * 1000 * bitrate / div + header->padding_bit; + } + if( frame_size <= 4 ) + return LSMASH_ERR_INVALID_DATA; + if( current_status == IMPORTER_ERROR || buffered_sample->length < frame_size ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + if( current_status == IMPORTER_CHANGE ) + { + lsmash_audio_summary_t *summary = mp4sys_mp3_create_summary( header, 1 ); /* FIXME: use legacy mode. */ + if( !summary ) + return LSMASH_ERR_NAMELESS; + lsmash_entry_t *entry = lsmash_get_entry( importer->summaries, track_number ); + if( !entry || !entry->data ) + return LSMASH_ERR_NAMELESS; + lsmash_cleanup_summary( entry->data ); + entry->data = summary; + info->samples_in_frame = summary->samples_in_frame; + } + /* read a frame's data. */ + memcpy( buffered_sample->data, info->raw_header, MP4SYS_MP3_HEADER_LENGTH ); + frame_size -= MP4SYS_MP3_HEADER_LENGTH; + if( fread( ((uint8_t *)buffered_sample->data) + MP4SYS_MP3_HEADER_LENGTH, 1, frame_size, importer->stream ) != frame_size ) + { + info->status = IMPORTER_ERROR; + return LSMASH_ERR_INVALID_DATA; + } + buffered_sample->length = MP4SYS_MP3_HEADER_LENGTH + frame_size; + buffered_sample->dts = info->au_number++ * info->samples_in_frame; + buffered_sample->cts = buffered_sample->dts; + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + buffered_sample->prop.pre_roll.distance = header->layer == MP4SYS_LAYER_III ? 1 : 0; /* Layer III uses MDCT */ + + int vbr_header_present = 0; + if( info->au_number == 1 + && (parse_xing_info_header( info, header, buffered_sample->data ) + || parse_vbri_header( info, header, buffered_sample->data )) ) + { + vbr_header_present = 1; + info->au_number--; + } + + /* handle additional inter-frame dependency due to bit reservoir */ + if( !vbr_header_present && header->layer == MP4SYS_LAYER_III ) + { + /* position of side_info */ + unsigned int sip = header->protection_bit ? 4 : 6; + unsigned int main_data_begin = buffered_sample->data[sip]; + if( header->ID == 1 ) + { + main_data_begin <<= 1; + main_data_begin |= (buffered_sample->data[sip + 1] >> 7); + } + if( main_data_begin > 0 ) + { + /* main_data_begin is a backpointer to the start of + * bit reservoir data for this frame. + * it contains total amount of bytes required from + * preceding frames. + * we just add up main_data size from history until it reaches + * the required amount. + */ + unsigned int reservoir_data = 0; + unsigned int i; + for( i = 0; i < 32 && reservoir_data < main_data_begin; ++i ) + { + reservoir_data += info->main_data_size[i]; + if( info->main_data_size[i] == 0 ) + break; + } + buffered_sample->prop.pre_roll.distance += info->prev_preroll_count; + info->prev_preroll_count = i; + } + uint16_t side_info_size; + if( header->ID == 1 ) + side_info_size = MP4SYS_MODE_IS_2CH( header->mode ) ? 32 : 17; + else + side_info_size = MP4SYS_MODE_IS_2CH( header->mode ) ? 17 : 9; + + /* pop back main_data_size[] and push main_data size of this frame + * to the front */ + memmove( info->main_data_size + 1, info->main_data_size, sizeof(info->main_data_size) - sizeof( info->main_data_size[0] ) ); + info->main_data_size[0] = frame_size - sip - side_info_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 = IMPORTER_EOF; + return 0; + } + if( ret >= 2 && (!memcmp( buf, "TA", 2 ) || !memcmp( buf, "AP", 2 )) ) + { + /* ID3v1 or APE tag */ + info->status = IMPORTER_EOF; + return 0; + } + if( ret == 1 && *buf == 0x00 ) + { + /* NOTE: ugly hack for mp1 stream created with SCMPX. */ + info->status = IMPORTER_EOF; + return 0; + } + if( ret != MP4SYS_MP3_HEADER_LENGTH ) + { + info->status = IMPORTER_ERROR; + return 0; + } + + mp4sys_mp3_header_t new_header = {0}; + if( mp4sys_mp3_parse_header( buf, &new_header ) < 0 ) + { + info->status = 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 = IMPORTER_ERROR; + return 0; + } + + /* currently supported "change(s)". */ + if( MP4SYS_MODE_IS_2CH( header->mode ) != MP4SYS_MODE_IS_2CH( new_header.mode ) ) + info->status = IMPORTER_CHANGE; + else + info->status = IMPORTER_OK; /* no change which matters to mp4 muxing was found */ + info->header = new_header; + + if( vbr_header_present ) + return mp4sys_mp3_get_accessunit( importer, track_number, buffered_sample ); + return 0; +} + +static int mp4sys_mp3_probe( importer_t *importer ) +{ + int c; + if( (c = getc( importer->stream )) == 'I' + && (c = getc( importer->stream )) == 'D' + && (c = getc( importer->stream )) == '3' ) + { + lsmash_fseek( importer->stream, 3, SEEK_CUR ); + uint32_t size = 0; + for( int i = 0 ; i < 4; i++ ) + { + size <<= 7; + size |= getc( importer->stream ); + } + lsmash_fseek( importer->stream, size, SEEK_CUR ); + } + else + ungetc( c, importer->stream ); + /* Parse the header. */ + uint8_t buf[MP4SYS_MP3_HEADER_LENGTH]; + if( fread( buf, 1, MP4SYS_MP3_HEADER_LENGTH, importer->stream ) != MP4SYS_MP3_HEADER_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + mp4sys_mp3_header_t header = { 0 }; + int err = mp4sys_mp3_parse_header( buf, &header ); + if( err < 0 ) + return err; + /* Now, the stream seems valid mp3. */ + lsmash_audio_summary_t *summary = mp4sys_mp3_create_summary( &header, 1 ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + /* importer status */ + mp4sys_mp3_info_t *info = lsmash_malloc_zero( sizeof(mp4sys_mp3_info_t) ); + if( !info || lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_free( info ); + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return LSMASH_ERR_MEMORY_ALLOC; + } + info->status = IMPORTER_OK; + info->header = header; + info->samples_in_frame = summary->samples_in_frame; + memcpy( info->raw_header, buf, MP4SYS_MP3_HEADER_LENGTH ); + importer->info = info; + return 0; +} + +static uint32_t mp4sys_mp3_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + mp4sys_mp3_info_t *info = (mp4sys_mp3_info_t *)importer->info; + if( !info || track_number != 1 || info->status != IMPORTER_EOF ) + return 0; + return info->samples_in_frame; +} + +const importer_functions mp4sys_mp3_importer = +{ + { "MPEG-1/2BC Audio Legacy" }, + 1, + mp4sys_mp3_probe, + mp4sys_mp3_get_accessunit, + mp4sys_mp3_get_last_delta, + mp4sys_mp3_cleanup +}; diff -Nru l-smash-1.9.1/cli/muxer.c l-smash-2.3.0/cli/muxer.c --- l-smash-1.9.1/cli/muxer.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/muxer.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1223 @@ +/***************************************************************************** + * muxer.c: + ***************************************************************************** + * Copyright (C) 2010-2014 L-SMASH project + * + * Authors: Yusuke Nakamura + * 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 +#include +#include +#include +#include +#include + +#include "lsmash.h" +#include "importer.h" +#include "cli.h" + +#include "config.h" + +#define MAX_NUM_OF_BRANDS 50 +#define MAX_NUM_OF_INPUTS 10 +#define MAX_NUM_OF_TRACKS 1 + +typedef struct +{ + char *album_name; + char *artist; + char *comment; + char *release_date; + char *encoder; + char *genre; + char *lyrics; + char *title; + char *composer; + char *album_artist; + char *copyright; + char *description; + char *grouping; + uint32_t beats_per_minute; +} itunes_metadata_t; + +typedef struct +{ + int help; + int version; + int isom; + int isom_version; + int itunes_movie; + int qtff; + int brand_3gx; + int optimize_pd; + int timeline_shift; + uint32_t interleave; + uint32_t num_of_brands; + uint32_t brands[MAX_NUM_OF_BRANDS]; + uint32_t major_brand; + uint32_t minor_version; + uint32_t num_of_inputs; + uint32_t chap_track; + char *chap_file; + int add_bom_to_chpl; + char *copyright_notice; + uint16_t copyright_language; + itunes_metadata_t itunes_metadata; + uint16_t default_language; +} option_t; + +typedef struct +{ + char *raws; + int disable; + int sbr; + int user_fps; + uint32_t fps_num; + uint32_t fps_den; + uint32_t encoder_delay; + int16_t alternate_group; + uint16_t ISO_language; + uint16_t copyright_language; + char *copyright_notice; + char *handler_name; +} input_track_option_t; + +typedef struct +{ + lsmash_summary_t *summary; + input_track_option_t opt; + int active; +} input_track_t; + +typedef struct +{ + char *whole_track_option; + int num_of_track_delimiters; +} input_option_t; + +typedef struct +{ + input_option_t opt; + char *file_name; + importer_t *importer; + input_track_t track[MAX_NUM_OF_TRACKS]; + uint32_t num_of_tracks; + uint32_t num_of_active_tracks; + uint32_t current_track_number; +} input_t; + +typedef struct +{ + lsmash_summary_t *summary; + lsmash_sample_t *sample; + int active; + uint32_t track_ID; + uint32_t timescale; + uint32_t timebase; + uint32_t sample_entry; + uint32_t current_sample_number; + uint32_t ctd_shift; + uint32_t priming_samples; + uint32_t last_delta; + uint64_t prev_dts; + int64_t start_offset; + double dts; +} output_track_t; + +typedef struct +{ + output_track_t *track; + uint32_t num_of_tracks; + uint32_t current_track_number; +} output_movie_t; + +typedef struct +{ + char *name; + lsmash_file_t *fh; + lsmash_file_parameters_t param; + output_movie_t movie; +} output_file_t; + +typedef struct +{ + lsmash_root_t *root; + output_file_t file; +} output_t; + +typedef struct +{ + option_t opt; + output_t output; + input_t input[MAX_NUM_OF_INPUTS]; + uint32_t num_of_inputs; +} muxer_t; + +static void cleanup_muxer( muxer_t *muxer ) +{ + if( !muxer ) + return; + output_t *output = &muxer->output; + lsmash_close_file( &output->file.param ); + lsmash_destroy_root( output->root ); + if( output->file.movie.track ) + { + for( uint32_t i = 0; i < output->file.movie.num_of_tracks; i++ ) + { + output_track_t *out_track = &output->file.movie.track[i]; + lsmash_delete_sample( out_track->sample ); + } + lsmash_free( output->file.movie.track ); + } + for( uint32_t i = 0; i < muxer->num_of_inputs; i++ ) + { + input_t *input = &muxer->input[i]; + lsmash_importer_close( input->importer ); + for( uint32_t j = 0; j < input->num_of_tracks; j++ ) + lsmash_cleanup_summary( input->track[j].summary ); + } +} + +#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) +#define REFRESH_CONSOLE eprintf( " \r" ) + +static int muxer_error( muxer_t *muxer, const char *message, ... ) +{ + cleanup_muxer( muxer ); + REFRESH_CONSOLE; + eprintf( "Error: " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +static int error_message( const char *message, ... ) +{ + REFRESH_CONSOLE; + eprintf( "Error: " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +static void display_version( void ) +{ + eprintf( "\n" + "L-SMASH isom/mov multiplexer rev%s %s\n" + "Built on %s %s\n" + "Copyright (C) 2010-2014 L-SMASH project\n", + LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); +} + +static void display_help( void ) +{ + display_version(); + eprintf( "\n" + "Usage: muxer [global_options] -i input1 [-i input2 -i input3 ...] -o output\n" + "Global options:\n" + " --help Display help\n" + " --version Display version information\n" + " --optimize-pd Optimize for progressive download\n" + " --interleave Specify time interval for media interleaving in milliseconds\n" + " --file-format Specify output file format\n" + " Multiple file format can be specified by comma separators\n" + " The first is applied as the best used one\n" + " --isom-version Specify maximum compatible ISO Base Media version\n" + " --shift-timeline Enable composition to decode timeline shift\n" + " --chapter Set chapters from the file.\n" + " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n" + " in the chapter list. (experimental)\n" + " --chapter-track Set which track the chapter applies to.\n" + " This option takes effect only when reference\n" + " chapter is available.\n" + " If this option is not used, it defaults to 1.\n" + " --copyright-notice Specify copyright notice with or without language (latter string)\n" + " is or /\n" + " --language Specify the default language for all the output tracks.\n" + " This option is overridden by the track options.\n" + "Output file formats:\n" + " mp4, mov, 3gp, 3g2, m4a, m4v\n" + "\n" + "Track options:\n" + " disable Disable this track\n" + " fps= Specify video framerate\n" + " is or /\n" + " language= Specify media language\n" + " alternate-group= Specify alternate group\n" + " encoder-delay= Represent audio encoder delay (priming samples) explicitly\n" + " copyright= Specify copyright notice with or without language (latter string)\n" + " is or /\n" + " handler= Set media handler name\n" + " sbr Enable backward-compatible SBR explicit signaling mode\n" + "How to use track options:\n" + " -i input?[track_option1],[track_option2]...\n" + "\n" + "iTunes Metadata:\n" + " --album-name Album name\n" + " --artist Artist\n" + " --comment User comment\n" + " --release-date Release date (YYYY-MM-DD)\n" + " --encoder Person or company that encoded the recording\n" + " --genre Genre\n" + " --lyrics Lyrics\n" + " --title Title or song name\n" + " --composer Composer\n" + " --album-artist Artist for the whole album (if different than the individual tracks)\n" + " --copyright Copyright\n" + " --description Description\n" + " --grouping Grouping\n" + " --tempo Beats per minute\n" ); +} + +static int muxer_usage_error( void ) +{ + display_help(); + return -1; +} + +#define MUXER_ERR( ... ) muxer_error( &muxer, __VA_ARGS__ ) +#define ERROR_MSG( ... ) error_message( __VA_ARGS__ ) +#define MUXER_USAGE_ERR() muxer_usage_error(); + +static int add_brand( option_t *opt, uint32_t brand ) +{ + if( opt->num_of_brands > MAX_NUM_OF_BRANDS ) + return -1; + /* Avoid duplication. */ + for( uint32_t i = 0; i < opt->num_of_brands; i++ ) + if( opt->brands[i] == brand ) + return -2; + opt->brands[opt->num_of_brands ++] = brand; + return 0; +} + +static int setup_isom_version( option_t *opt ) +{ + add_brand( opt, ISOM_BRAND_TYPE_ISOM ); + if( opt->isom_version > 6 ) + return ERROR_MSG( "unknown ISO Base Media version.\n" ); +#define SET_ISOM_VERSION( version ) \ + if( opt->isom_version >= version ) \ + add_brand( opt, ISOM_BRAND_TYPE_ISO##version ) + SET_ISOM_VERSION( 2 ); + SET_ISOM_VERSION( 3 ); + SET_ISOM_VERSION( 4 ); + SET_ISOM_VERSION( 5 ); + SET_ISOM_VERSION( 6 ); +#undef SET_ISOM_VERSION + return 0; +} + +static int decide_brands( option_t *opt ) +{ + if( opt->num_of_brands == 0 ) + { + /* default file format */ + opt->major_brand = ISOM_BRAND_TYPE_MP42; + opt->minor_version = 0x00000000; + add_brand( opt, ISOM_BRAND_TYPE_MP42 ); + add_brand( opt, ISOM_BRAND_TYPE_MP41 ); + add_brand( opt, ISOM_BRAND_TYPE_ISOM ); + opt->isom = 1; + eprintf( "MP4 muxing mode\n" ); + return setup_isom_version( opt ); + } + opt->major_brand = opt->brands[0]; /* Pick the first brand as major brand. */ + for( uint32_t i = 0; i < opt->num_of_brands; i++ ) + { + switch( opt->brands[i] ) + { + case ISOM_BRAND_TYPE_3GP6 : + /* When being compatible with 3gp6, also compatible with 3g2a. */ + add_brand( opt, ISOM_BRAND_TYPE_3G2A ); + opt->brand_3gx = 1; + break; + case ISOM_BRAND_TYPE_3G2A : + opt->brand_3gx = 2; + break; + case ISOM_BRAND_TYPE_QT : + opt->qtff = 1; + break; + case ISOM_BRAND_TYPE_M4A : + case ISOM_BRAND_TYPE_M4V : + opt->itunes_movie = 1; + case ISOM_BRAND_TYPE_MP42 : + add_brand( opt, ISOM_BRAND_TYPE_MP42 ); + add_brand( opt, ISOM_BRAND_TYPE_MP41 ); + break; + default : + break; + } + if( opt->brands[i] != ISOM_BRAND_TYPE_QT ) + opt->isom = 1; + } + switch( opt->major_brand ) + { + case ISOM_BRAND_TYPE_MP42 : + opt->minor_version = 0x00000000; + eprintf( "MP4 muxing mode\n" ); + break; + case ISOM_BRAND_TYPE_M4A : + case ISOM_BRAND_TYPE_M4V : + opt->minor_version = 0x00000000; + eprintf( "iTunes MP4 muxing mode\n" ); + break; + case ISOM_BRAND_TYPE_3GP6 : + opt->minor_version = 0x00000000; /* means, 3gp(3gp6) 6.0.0 : "6" is not included in minor_version. */ + eprintf( "3GPP muxing mode\n" ); + break; + case ISOM_BRAND_TYPE_3G2A : + opt->minor_version = 0x00010000; /* means, 3g2(3g2a) 1.0.0 : a == 1 */ + eprintf( "3GPP2 muxing mode\n" ); + break; + case ISOM_BRAND_TYPE_QT : + opt->minor_version = 0x00000000; /* We don't know exact version of the spec to use QTFF features. */ + eprintf( "QuickTime file format muxing mode\n" ); + break; + default : + break; + } + /* Set up ISO Base Media version. */ + if( opt->isom ) + setup_isom_version( opt ); + if( opt->num_of_brands > MAX_NUM_OF_BRANDS ) + return ERROR_MSG( "exceed the maximum number of brands we can deal with.\n" ); + return 0; +} + +static int parse_global_options( int argc, char **argv, muxer_t *muxer ) +{ + if ( argc < 2 ) + return -1; + else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) + { + muxer->opt.help = 1; + return 0; + } + else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) + { + muxer->opt.version = 1; + return 0; + } + else if( argc < 5 ) + return -1; + uint32_t i = 1; + option_t *opt = &muxer->opt; + opt->chap_track = 1; + opt->add_bom_to_chpl = 0; + while( argc > i && *argv[i] == '-' ) + { +#define CHECK_NEXT_ARG if( argc == ++i ) return -1 + if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) + { + CHECK_NEXT_ARG; + if( opt->num_of_inputs + 1 > MAX_NUM_OF_INPUTS ) + return ERROR_MSG( "exceed the maximum number of input files.\n" ); + input_t *input = &muxer->input[opt->num_of_inputs]; + input_option_t *input_movie_opt = &input->opt; + char *p = argv[i]; + while( *p ) + input_movie_opt->num_of_track_delimiters += (*p++ == '?'); + if( input_movie_opt->num_of_track_delimiters > MAX_NUM_OF_TRACKS ) + return ERROR_MSG( "you specified options to exceed the maximum number of tracks per input files.\n" ); + input->file_name = strtok( argv[i], "?" ); + input_movie_opt->whole_track_option = strtok( NULL, "" ); + if( input_movie_opt->num_of_track_delimiters ) + { + input_track_option_t *track_opt = &input->track[0].opt; + track_opt->raws = strtok( input_movie_opt->whole_track_option, "?" ); +#if (MAX_NUM_OF_TRACKS - 1) + for( uint32_t j = 1; j < input_movie_opt->num_of_track_delimiters; j++ ) + { + track_opt = &input->track[j].opt; + track_opt->raws = strtok( NULL, "?" ); + } +#endif + } + ++ opt->num_of_inputs; + } + else if( !strcasecmp( argv[i], "-o" ) || !strcasecmp( argv[i], "--output" ) ) + { + CHECK_NEXT_ARG; + muxer->output.file.name = argv[i]; + } + else if( !strcasecmp( argv[i], "--optimize-pd" ) ) + opt->optimize_pd = 1; + else if( !strcasecmp( argv[i], "--interleave" ) ) + { + CHECK_NEXT_ARG; + if( opt->interleave ) + return ERROR_MSG( "you specified --interleave twice.\n" ); + opt->interleave = atoi( argv[i] ); + } + else if( !strcasecmp( argv[i], "--file-format" ) ) + { + CHECK_NEXT_ARG; + static const struct + { + uint32_t brand_4cc; + char *file_format; + } file_format_list[] + = { + { ISOM_BRAND_TYPE_MP42, "mp4" }, + { ISOM_BRAND_TYPE_QT, "mov" }, + { ISOM_BRAND_TYPE_3GP6, "3gp" }, + { ISOM_BRAND_TYPE_3G2A, "3g2" }, + { ISOM_BRAND_TYPE_M4A, "m4a" }, + { ISOM_BRAND_TYPE_M4V, "m4v" }, + { 0, NULL } + }; + char *file_format = NULL; + while( (file_format = strtok( file_format ? NULL : argv[i], "," )) != NULL ) + { + int j; + for( j = 0; file_format_list[j].file_format; j++ ) + if( !strcmp( file_format, file_format_list[j].file_format ) ) + { + int ret = add_brand( opt, file_format_list[j].brand_4cc ); + if( ret == -2 ) + return ERROR_MSG( "you specified same output file format twice.\n" ); + else if( ret == -1 ) + return ERROR_MSG( "exceed the maximum number of brands we can deal with.\n" ); + break; + } + if( !file_format_list[j].file_format ) + return MUXER_USAGE_ERR(); + } + } + else if( !strcasecmp( argv[i], "--isom-version" ) ) + { + CHECK_NEXT_ARG; + if( opt->isom_version ) + return ERROR_MSG( "you specified --isom-version twice.\n" ); + opt->isom_version = atoi( argv[i] ); + } + else if( !strcasecmp( argv[i], "--shift-timeline" ) ) + opt->timeline_shift = 1; + else if( !strcasecmp( argv[i], "--chapter" ) ) + { + CHECK_NEXT_ARG; + opt->chap_file = argv[i]; + } + else if( !strcasecmp( argv[i], "--chapter-track" ) ) + { + CHECK_NEXT_ARG; + opt->chap_track = atoi( argv[i] ); + if( !opt->chap_track ) + return ERROR_MSG( "%s is an invalid track number.\n", argv[i] ); + } + else if( !strcasecmp( argv[i], "--chpl-with-bom" ) ) + opt->add_bom_to_chpl = 1; + else if( !strcasecmp( argv[i], "--copyright-notice" ) ) + { + CHECK_NEXT_ARG; + if( opt->copyright_notice ) + return ERROR_MSG( "you specified --copyright-notice twice.\n" ); + opt->copyright_notice = argv[i]; + char *language = opt->copyright_notice; + while( *language ) + { + if( *language == '/' ) + { + *language++ = '\0'; + break; + } + ++language; + } + opt->copyright_language = language ? lsmash_pack_iso_language( language ) : ISOM_LANGUAGE_CODE_UNDEFINED; + } + /* iTunes metadata */ +#define CHECK_ITUNES_METADATA_ARG_STRING( argument, value ) \ + else if( !strcasecmp( argv[i], "--"#argument ) ) \ + { \ + CHECK_NEXT_ARG; \ + if( opt->itunes_metadata.value ) \ + return ERROR_MSG( "you specified --"#argument" twice.\n" ); \ + opt->itunes_metadata.value = argv[i]; \ + } + CHECK_ITUNES_METADATA_ARG_STRING( album-name, album_name ) + CHECK_ITUNES_METADATA_ARG_STRING( artist, artist ) + CHECK_ITUNES_METADATA_ARG_STRING( comment, comment ) + CHECK_ITUNES_METADATA_ARG_STRING( release-date, release_date ) + CHECK_ITUNES_METADATA_ARG_STRING( encoder, encoder ) + CHECK_ITUNES_METADATA_ARG_STRING( genre, genre ) + CHECK_ITUNES_METADATA_ARG_STRING( lyrics, lyrics ) + CHECK_ITUNES_METADATA_ARG_STRING( title, title ) + CHECK_ITUNES_METADATA_ARG_STRING( composer, composer ) + CHECK_ITUNES_METADATA_ARG_STRING( album-artist, album_artist ) + CHECK_ITUNES_METADATA_ARG_STRING( copyright, copyright ) + CHECK_ITUNES_METADATA_ARG_STRING( description, description ) + CHECK_ITUNES_METADATA_ARG_STRING( grouping, grouping ) +#undef CHECK_ITUNES_METADATA_ARG_STRING + else if( !strcasecmp( argv[i], "--tempo" ) ) + { + CHECK_NEXT_ARG; + if( opt->itunes_metadata.beats_per_minute ) + return ERROR_MSG( "you specified --tempo twice.\n" ); + opt->itunes_metadata.beats_per_minute = atoi( argv[i] ); + } + else if( !strcasecmp( argv[i], "--language" ) ) + { + CHECK_NEXT_ARG; + opt->default_language = lsmash_pack_iso_language( argv[i] ); + } +#undef CHECK_NEXT_ARG + else + return ERROR_MSG( "you specified invalid option: %s.\n", argv[i] ); + ++i; + } + if( !muxer->output.file.name ) + return ERROR_MSG( "output file name is not specified.\n" ); + if( decide_brands( opt ) ) + return ERROR_MSG( "failed to set up output file format.\n" ); + if( opt->timeline_shift && !opt->qtff && opt->isom_version < 4 ) + return ERROR_MSG( "timeline shift requires --file-format mov, or --isom-version 4 or later.\n" ); + muxer->num_of_inputs = opt->num_of_inputs; + return 0; +} + +static int parse_track_options( input_t *input ) +{ + for( input->current_track_number = 1; + input->current_track_number <= input->num_of_tracks; + input->current_track_number ++ ) + { + input_track_t *in_track = &input->track[input->current_track_number - 1]; + input_track_option_t *track_opt = &in_track->opt; + if( track_opt->raws == NULL ) + break; +#if 0 + if( !strchr( track_opt->raws, ':' ) + || strchr( track_opt->raws, ':' ) == track_opt->raws ) + return ERROR_MSG( "track number is not specified in %s\n", track_opt->raws ); + if( strchr( track_opt->raws, ':' ) != strrchr( track_opt->raws, ':' ) ) + return ERROR_MSG( "multiple colons inside one track option in %s.\n", track_opt->raws ); + uint32_t track_number = atoi( strtok( track_opt->raws, ":" ) ); + if( track_number == 0 || track_number > MAX_NUM_OF_TRACKS ) + return ERROR_MSG( "%s is an invalid track number %"PRIu32".\n", strtok( track_opt->raws, ":" ), track_number ); + in_track = &input->track[track_number - 1]; + track_opt = &in_track->opt; + char *track_option; + while( (track_option = strtok( NULL, "," )) != NULL ) +#else + char *track_option = NULL; + while( (track_option = strtok( track_option ? NULL : track_opt->raws, "," )) != NULL ) +#endif + { + if( strchr( track_option, '=' ) != strrchr( track_option, '=' ) ) + return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option ); + if( strstr( track_option, "disable" ) ) + track_opt->disable = 1; + else if( strstr( track_option, "alternate-group=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + track_opt->alternate_group = atoi( track_parameter ); + } + else if( strstr( track_option, "encoder-delay=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + track_opt->encoder_delay = atoi( track_parameter ); + } + else if( strstr( track_option, "language=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + track_opt->ISO_language = lsmash_pack_iso_language( track_parameter ); + } + else if( strstr( track_option, "fps=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + if( sscanf( track_parameter, "%"SCNu32"/%"SCNu32, &track_opt->fps_num, &track_opt->fps_den ) == 1 ) + { + track_opt->fps_num = atoi( track_parameter ); + track_opt->fps_den = 1; + } + track_opt->user_fps = 1; + } + else if( strstr( track_option, "copyright=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + track_opt->copyright_notice = track_parameter; + while( *track_parameter ) + { + if( *track_parameter == '/' ) + { + *track_parameter++ = '\0'; + break; + } + ++track_parameter; + } + track_opt->copyright_language = track_parameter ? lsmash_pack_iso_language( track_parameter ) : ISOM_LANGUAGE_CODE_UNDEFINED; + } + else if( strstr( track_option, "handler=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + track_opt->handler_name = track_parameter; + } + else if( strstr( track_option, "sbr" ) ) + track_opt->sbr = 1; + else + return ERROR_MSG( "unknown track option %s\n", track_option ); + } + } + return 0; +} + +static void display_codec_name( lsmash_codec_type_t codec_type, uint32_t track_number ) +{ +#define DISPLAY_CODEC_NAME( codec, codec_name ) \ + else if( lsmash_check_codec_type_identical( codec_type, codec ) ) \ + eprintf( "Track %"PRIu32": "#codec_name"\n", track_number ) + if( 0 ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_AVC1_VIDEO, H.264 Advanced Video Coding ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_HVC1_VIDEO, H.265 High Efficiency Video Coding ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_VC_1_VIDEO, SMPTE VC-1 Advanced Profile ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_MP4A_AUDIO, MPEG-4 Audio ); + DISPLAY_CODEC_NAME( QT_CODEC_TYPE_MP4A_AUDIO, MPEG-4 Audio ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_AC_3_AUDIO, AC-3 ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_EC_3_AUDIO, Enhanced AC-3 ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSC_AUDIO, DTS ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSE_AUDIO, DTS LBR ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSH_AUDIO, DTS-HD ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSL_AUDIO, DTS-HD Lossless ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_SAWB_AUDIO, Wideband AMR voice ); + DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_SAMR_AUDIO, Narrowband AMR voice ); +#undef DISPLAY_CODEC_NAME +} + +static int open_input_files( muxer_t *muxer ) +{ + output_movie_t *out_movie = &muxer->output.file.movie; + option_t *opt = &muxer->opt; + for( uint32_t current_input_number = 1; current_input_number <= muxer->num_of_inputs; current_input_number++ ) + { + input_t *input = &muxer->input[current_input_number - 1]; + /* Initialize importer framework. */ + input->importer = lsmash_importer_open( input->file_name, "auto" ); + if( !input->importer ) + return ERROR_MSG( "failed to open input file.\n" ); + input->num_of_tracks = lsmash_importer_get_track_count( input->importer ); + if( input->num_of_tracks == 0 ) + return ERROR_MSG( "there is no valid track in input file.\n" ); + if( opt->default_language ) + for( int i = 0; i < input->num_of_tracks; i ++ ) + input->track[i].opt.ISO_language = opt->default_language; + /* Parse track options */ + if( parse_track_options( input ) ) + return ERROR_MSG( "failed to parse track options.\n" ); + /* Activate tracks by CODEC type. */ + for( input->current_track_number = 1; + input->current_track_number <= input->num_of_tracks; + input->current_track_number ++ ) + { + input_track_t *in_track = &input->track[input->current_track_number - 1]; + in_track->summary = lsmash_duplicate_summary( input->importer, input->current_track_number ); + if( !in_track->summary ) + return ERROR_MSG( "failed to get input summary.\n" ); + /* Check codec type. */ + lsmash_codec_type_t codec_type = in_track->summary->sample_type; + in_track->active = 1; + if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) ) + { + if( opt->isom ) + add_brand( opt, ISOM_BRAND_TYPE_AVC1 ); + } + else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) ) + { + if( !opt->isom && opt->qtff ) + return ERROR_MSG( "the input seems HEVC, at present available only for ISO Base Media file format.\n" ); + } + else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_VC_1_VIDEO ) ) + { + if( !opt->isom && opt->qtff ) + return ERROR_MSG( "the input seems VC-1, at present available only for ISO Base Media file format.\n" ); + } + else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) + || lsmash_check_codec_type_identical( codec_type, QT_CODEC_TYPE_MP4A_AUDIO ) ) + /* Do nothing. */; + else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_AC_3_AUDIO ) + || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) ) + { + if( !opt->isom && opt->qtff ) + return ERROR_MSG( "the input seems (Enhanced) AC-3, at present available only for ISO Base Media file format.\n" ); + add_brand( opt, ISOM_BRAND_TYPE_DBY1 ); + } + else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) + || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) + || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) + || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) + { + if( !opt->isom && opt->qtff ) + return ERROR_MSG( "the input seems DTS(-HD) Audio, at present available only for ISO Base Media file format.\n" ); + } + else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_SAWB_AUDIO ) + || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_SAMR_AUDIO ) ) + { + if( !opt->brand_3gx ) + return ERROR_MSG( "the input seems AMR-NB/WB, available for 3GPP(2) file format.\n" ); + } + else + { + lsmash_cleanup_summary( in_track->summary ); + in_track->summary = NULL; + in_track->active = 0; + } + if( in_track->active ) + { + ++ input->num_of_active_tracks; + display_codec_name( codec_type, out_movie->num_of_tracks + input->num_of_active_tracks ); + } + } + out_movie->num_of_tracks += input->num_of_active_tracks; + } + if( out_movie->num_of_tracks == 0 ) + return ERROR_MSG( "there is no media that can be stored in output movie.\n" ); + return 0; +} + +static int set_itunes_metadata( output_t *output, option_t *opt ) +{ + if( !opt->itunes_movie ) + return 0; + itunes_metadata_t *metadata = &opt->itunes_metadata; +#define SET_ITUNES_METADATA( item, type, value ) \ + if( value \ + && lsmash_set_itunes_metadata( output->root, (lsmash_itunes_metadata_t){ item, ITUNES_METADATA_TYPE_NONE, { .type = value }, NULL, NULL } ) ) \ + return -1 + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ENCODING_TOOL, string, "L-SMASH" ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ALBUM_NAME, string, metadata->album_name ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ARTIST, string, metadata->artist ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_USER_COMMENT, string, metadata->comment ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_RELEASE_DATE, string, metadata->release_date ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ENCODED_BY, string, metadata->encoder ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_USER_GENRE, string, metadata->genre ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_LYRICS, string, metadata->lyrics ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_TITLE, string, metadata->title ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_COMPOSER, string, metadata->composer ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ALBUM_ARTIST, string, metadata->album_artist ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_COPYRIGHT, string, metadata->copyright ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_DESCRIPTION, string, metadata->description ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_GROUPING, string, metadata->grouping ); + SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, integer, metadata->beats_per_minute ); +#undef SET_ITUNES_METADATA + return 0; +} + +static int prepare_output( muxer_t *muxer ) +{ + option_t *opt = &muxer->opt; + output_t *output = &muxer->output; + output_file_t *out_file = &output->file; + output_movie_t *out_movie = &out_file->movie; + /* Allocate output tracks. */ + out_movie->track = lsmash_malloc( out_movie->num_of_tracks * sizeof(output_track_t) ); + if( !out_movie->track ) + return ERROR_MSG( "failed to allocate output tracks.\n" ); + memset( out_movie->track, 0, out_movie->num_of_tracks * sizeof(output_track_t) ); + /* Initialize L-SMASH muxer */ + output->root = lsmash_create_root(); + if( !output->root ) + return ERROR_MSG( "failed to create a ROOT.\n" ); + lsmash_file_parameters_t *file_param = &out_file->param; + if( lsmash_open_file( out_file->name, 0, file_param ) < 0 ) + return ERROR_MSG( "failed to open an output file.\n" ); + file_param->major_brand = opt->major_brand; + file_param->brands = opt->brands; + file_param->brand_count = opt->num_of_brands; + file_param->minor_version = opt->minor_version; + if( opt->interleave ) + file_param->max_chunk_duration = opt->interleave * 1e-3; + out_file->fh = lsmash_set_file( output->root, file_param ); + if( !out_file->fh ) + return ERROR_MSG( "failed to add an output file into a ROOT.\n" ); + /* Initialize movie */ + lsmash_movie_parameters_t movie_param; + lsmash_initialize_movie_parameters( &movie_param ); + if( lsmash_set_movie_parameters( output->root, &movie_param ) ) + return ERROR_MSG( "failed to set movie parameters.\n" ); + if( opt->copyright_notice + && lsmash_set_copyright( output->root, 0, opt->copyright_language, opt->copyright_notice ) ) + return ERROR_MSG( "failed to set a copyright notice for the entire movie.\n" ); + if( set_itunes_metadata( output, opt ) ) + return ERROR_MSG( "failed to set iTunes metadata.\n" ); + out_movie->current_track_number = 1; + for( uint32_t current_input_number = 1; current_input_number <= muxer->num_of_inputs; current_input_number++ ) + { + input_t *input = &muxer->input[current_input_number - 1]; + for( input->current_track_number = 1; + input->current_track_number <= input->num_of_tracks; + input->current_track_number ++ ) + { + input_track_t *in_track = &input->track[ input->current_track_number - 1 ]; + if( !in_track->active ) + continue; + input_track_option_t *track_opt = &in_track->opt; + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + /* Set up 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( !track_opt->disable ) + track_param.mode |= ISOM_TRACK_ENABLED; + if( opt->qtff ) + track_param.mode |= QT_TRACK_IN_POSTER; + 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; + switch( in_track->summary->summary_type ) + { + case LSMASH_SUMMARY_TYPE_VIDEO : + { + out_track->track_ID = lsmash_create_track( output->root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ); + if( !out_track->track_ID ) + return ERROR_MSG( "failed to create a track.\n" ); + lsmash_video_summary_t *summary = (lsmash_video_summary_t *)in_track->summary; + uint64_t display_width = summary->width << 16; + uint64_t display_height = summary->height << 16; + if( summary->par_h && summary->par_v ) + { + double sar = (double)summary->par_h / summary->par_v; + if( sar > 1.0 ) + display_width *= sar; + else + display_height /= sar; + } + track_param.display_width = display_width; + track_param.display_height = display_height; + /* Initialize media */ + uint32_t timescale = 25; /* default value */ + uint32_t timebase = 1; /* default value */ + if( track_opt->user_fps ) + { + timescale = track_opt->fps_num << (!!summary->sample_per_field); + timebase = track_opt->fps_den; + } + else if( !summary->vfr ) + { + if( lsmash_check_codec_type_identical( summary->sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) + || lsmash_check_codec_type_identical( summary->sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) ) + { + uint32_t compare_timebase = summary->timebase; + uint32_t compare_timescale = summary->timescale; + static const struct + { + uint32_t timescale; + uint32_t timebase; + } well_known_fps[] + = { + { 24000, 1001 }, { 30000, 1001 }, { 60000, 1001 }, { 120000, 1001 }, { 72000, 1001 }, + { 25, 1 }, { 50, 1 }, { 24, 1 }, { 30, 1 }, { 60, 1 }, { 120, 1 }, { 72, 1 }, { 0, 0 } + }; + for( int i = 0; well_known_fps[i].timescale; i++ ) + if( well_known_fps[i].timescale == compare_timescale + && well_known_fps[i].timebase == compare_timebase ) + { + timescale = well_known_fps[i].timescale; + timebase = well_known_fps[i].timebase; + break; + } + lsmash_codec_specific_t *bitrate = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( bitrate ) + lsmash_add_codec_specific_data( in_track->summary, bitrate ); + lsmash_destroy_codec_specific_data( bitrate ); + } + else + { + timescale = summary->timescale; + timebase = summary->timebase; + } + } + media_param.timescale = timescale; + media_param.media_handler_name = track_opt->handler_name ? track_opt->handler_name : "L-SMASH Video Handler"; + media_param.roll_grouping = 1; + media_param.rap_grouping = opt->isom_version >= 6; + out_track->timescale = timescale; + out_track->timebase = timebase; + break; + } + case LSMASH_SUMMARY_TYPE_AUDIO : + { + out_track->track_ID = lsmash_create_track( output->root, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ); + if( !out_track->track_ID ) + return ERROR_MSG( "failed to create a track.\n" ); + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)in_track->summary; + if( track_opt->sbr ) + { + /* Check if explicit SBR is valid or not. */ + if( lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ) != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 ) + return ERROR_MSG( "--sbr is only valid with MPEG-4 Audio.\n" ); + summary->sbr_mode = MP4A_AAC_SBR_BACKWARD_COMPATIBLE; + if( lsmash_setup_AudioSpecificConfig( summary ) ) + return ERROR_MSG( "failed to set SBR mode.\n" ); + } + media_param.timescale = summary->frequency; + media_param.media_handler_name = track_opt->handler_name ? track_opt->handler_name : "L-SMASH Audio Handler"; + media_param.roll_grouping = (opt->isom_version >= 2 || opt->qtff); + out_track->priming_samples = track_opt->encoder_delay; + out_track->timescale = summary->frequency; + out_track->timebase = 1; + break; + } + default : + return ERROR_MSG( "not supported stream type.\n" ); + } + /* Reset the movie timescale in order to match the media timescale if only one track is there. */ + if( muxer->num_of_inputs == 1 + && current_input_number == 1 + && input->current_track_number == 1 ) + { + movie_param.timescale = media_param.timescale; + if( lsmash_set_movie_parameters( output->root, &movie_param ) ) + return ERROR_MSG( "failed to set movie parameters.\n" ); + } + /* Set copyright information. */ + if( track_opt->copyright_notice + && lsmash_set_copyright( output->root, out_track->track_ID, track_opt->copyright_language, track_opt->copyright_notice ) ) + return ERROR_MSG( "failed to set a copyright notice.\n" ); + /* Set track parameters. */ + if( lsmash_set_track_parameters( output->root, out_track->track_ID, &track_param ) ) + return ERROR_MSG( "failed to set track parameters.\n" ); + /* Set media parameters. */ + if( lsmash_set_media_parameters( output->root, out_track->track_ID, &media_param ) ) + return ERROR_MSG( "failed to set media parameters.\n" ); + out_track->summary = in_track->summary; + out_track->sample_entry = lsmash_add_sample_entry( output->root, out_track->track_ID, out_track->summary ); + if( !out_track->sample_entry ) + return ERROR_MSG( "failed to add sample description entry.\n" ); + out_track->active = 1; + ++ out_movie->current_track_number; + } + input->current_track_number = 1; + } + out_movie->current_track_number = 1; + return 0; +} + +static void set_reference_chapter_track( output_t *output, option_t *opt ) +{ + if( !opt->chap_file || (!opt->qtff && !opt->itunes_movie) || (opt->brand_3gx == 1) ) + return; + lsmash_create_reference_chapter_track( output->root, opt->chap_track, opt->chap_file ); +} + +static int do_mux( muxer_t *muxer ) +{ +#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) + option_t *opt = &muxer->opt; + output_t *output = &muxer->output; + output_movie_t *out_movie = &output->file.movie; + set_reference_chapter_track( output, opt ); + double largest_dts = 0; + uint32_t current_input_number = 1; + uint32_t num_consecutive_sample_skip = 0; + uint32_t num_active_input_tracks = out_movie->num_of_tracks; + uint64_t total_media_size = 0; + uint8_t sample_count = 0; + while( 1 ) + { + input_t *input = &muxer->input[current_input_number - 1]; + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + if( out_track->active ) + { + lsmash_sample_t *sample = out_track->sample; + /* Get a new sample data if the track doesn't hold any one. */ + if( !sample ) + { + /* Allocate sample buffer. */ + sample = lsmash_create_sample( out_track->summary->max_au_length ); + if( !sample ) + return ERROR_MSG( "failed to alloc memory for buffer.\n" ); + /* lsmash_importer_get_access_unit() returns 1 if there're any changes in stream's properties. */ + int ret = lsmash_importer_get_access_unit( input->importer, input->current_track_number, sample ); + if( ret == -1 ) + { + lsmash_delete_sample( sample ); + ERROR_MSG( "failed to get a frame from input file. Maybe corrupted.\n" + "Aborting muxing operation and trying to let output be valid file.\n" ); + break; + } + else if( ret == 1 ) + { + input_track_t *in_track = &input->track[input->current_track_number - 1]; + lsmash_cleanup_summary( in_track->summary ); + in_track->summary = lsmash_duplicate_summary( input->importer, input->current_track_number ); + out_track->summary = in_track->summary; + out_track->sample_entry = lsmash_add_sample_entry( output->root, out_track->track_ID, out_track->summary ); + if( !out_track->sample_entry ) + { + ERROR_MSG( "failed to add sample description entry.\n" ); + break; + } + } + if( sample->length == 0 ) + { + /* No more appendable samples in this track. */ + lsmash_delete_sample( sample ); + sample = NULL; + out_track->active = 0; + out_track->last_delta = lsmash_importer_get_last_delta( input->importer, input->current_track_number ); + if( out_track->last_delta == 0 ) + ERROR_MSG( "failed to get the last sample delta.\n" ); + out_track->last_delta *= out_track->timebase; + if( --num_active_input_tracks == 0 ) + break; /* Reached the end of whole tracks. */ + } + else + { + sample->index = out_track->sample_entry; + sample->dts *= out_track->timebase; + sample->cts *= out_track->timebase; + if( opt->timeline_shift ) + { + if( out_track->current_sample_number == 0 ) + out_track->ctd_shift = sample->cts; + sample->cts -= out_track->ctd_shift; + } + out_track->dts = (double)sample->dts / out_track->timescale; + out_track->sample = sample; + } + } + if( sample ) + { + /* Append a sample if meeting a condition. */ + if( out_track->dts <= largest_dts || num_consecutive_sample_skip == num_active_input_tracks ) + { + uint64_t sample_size = sample->length; /* sample might be deleted internally after appending. */ + uint64_t sample_dts = sample->dts; /* same as above */ + uint64_t sample_cts = sample->cts; /* same as above */ + if( lsmash_append_sample( output->root, out_track->track_ID, sample ) ) + return ERROR_MSG( "failed to append a sample.\n" ); + if( out_track->current_sample_number == 0 ) + out_track->start_offset = sample_cts; + else + out_track->last_delta = sample_dts - out_track->prev_dts; /* for any changes in stream's properties */ + out_track->prev_dts = sample_dts; + out_track->sample = NULL; + largest_dts = LSMASH_MAX( largest_dts, out_track->dts ); + total_media_size += sample_size; + ++ out_track->current_sample_number; + num_consecutive_sample_skip = 0; + /* Print, per 256 samples, total size of imported media. */ + if( ++sample_count == 0 ) + { + REFRESH_CONSOLE; + eprintf( "Importing: %"PRIu64" bytes\r", total_media_size ); + } + } + else + ++num_consecutive_sample_skip; /* Skip appendig sample. */ + } + } + if( ++ out_movie->current_track_number > out_movie->num_of_tracks ) + out_movie->current_track_number = 1; /* Back the first output track. */ + /* Move the next track. */ + if( ++ input->current_track_number > input->num_of_tracks ) + { + /* Move the next input movie. */ + input->current_track_number = 1; + if( ++ current_input_number > muxer->num_of_inputs ) + current_input_number = 1; /* Back the first input movie. */ + } + } + for( out_movie->current_track_number = 1; + out_movie->current_track_number <= out_movie->num_of_tracks; + out_movie->current_track_number ++ ) + { + /* Close track. */ + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, out_track->last_delta ) ) + ERROR_MSG( "failed to flush the rest of samples.\n" ); + /* Create edit list. + * Don't trust media duration. It's just duration of media, not duration of track presentation. + * Calculation of presentation duration by DTS is reliable since this muxer handles CFR only. */ + uint64_t actual_duration = out_track->prev_dts + out_track->last_delta - out_track->priming_samples; + lsmash_edit_t edit; + edit.duration = actual_duration * ((double)lsmash_get_movie_timescale( output->root ) / out_track->timescale); + edit.start_time = out_track->priming_samples + out_track->start_offset; + edit.rate = ISOM_EDIT_MODE_NORMAL; + if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, edit ) ) + ERROR_MSG( "failed to set timeline map.\n" ); + } + return 0; +#undef LSMASH_MAX +} + +static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size ) +{ + REFRESH_CONSOLE; + eprintf( "Finalizing: [%5.2lf%%]\r", total_movie_size ? ((double)written_movie_size / total_movie_size) * 100.0 : 0 ); + return 0; +} + +static int finish_movie( output_t *output, option_t *opt ) +{ + /* Set chapter list. */ + if( opt->chap_file ) + lsmash_set_tyrant_chapter( output->root, opt->chap_file, opt->add_bom_to_chpl ); + /* Close movie. */ + REFRESH_CONSOLE; + if( opt->optimize_pd ) + { + lsmash_adhoc_remux_t moov_to_front; + moov_to_front.func = moov_to_front_callback; + moov_to_front.buffer_size = 4*1024*1024; /* 4MiB */ + moov_to_front.param = NULL; + return lsmash_finish_movie( output->root, &moov_to_front ); + } + if( lsmash_finish_movie( output->root, NULL ) ) + return -1; + return lsmash_write_lsmash_indicator( output->root ); +} + +int main( int argc, char *argv[] ) +{ + muxer_t muxer = { { 0 } }; + lsmash_get_mainargs( &argc, &argv ); + if( parse_global_options( argc, argv, &muxer ) ) + return MUXER_USAGE_ERR(); + if( muxer.opt.help ) + { + display_help(); + cleanup_muxer( &muxer ); + return 0; + } + else if( muxer.opt.version ) + { + display_version(); + cleanup_muxer( &muxer ); + return 0; + } + if( open_input_files( &muxer ) ) + return MUXER_ERR( "failed to open input files.\n" ); + if( prepare_output( &muxer ) ) + return MUXER_ERR( "failed to set up preparation for output.\n" ); + if( do_mux( &muxer ) ) + return MUXER_ERR( "failed to do muxing.\n" ); + if( finish_movie( &muxer.output, &muxer.opt ) ) + return MUXER_ERR( "failed to finish movie.\n" ); + REFRESH_CONSOLE; + eprintf( "Muxing completed!\n" ); + cleanup_muxer( &muxer ); /* including lsmash_destroy_root() */ + return 0; +} diff -Nru l-smash-1.9.1/cli/nalu_imp.c l-smash-2.3.0/cli/nalu_imp.c --- l-smash-1.9.1/cli/nalu_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/nalu_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1562 @@ +/***************************************************************************** + * nalu_imp.c + ***************************************************************************** + * Copyright (C) 2011-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + H.264 importer + ITU-T Recommendation H.264 (04/13) + ISO/IEC 14496-15:2010 +***************************************************************************/ +#include "codecs/h264.h" +#include "codecs/nalu.h" + +typedef struct +{ + importer_status status; + h264_info_t info; + lsmash_entry_list_t avcC_list[1]; /* stored as lsmash_codec_specific_t */ + lsmash_media_ts_list_t ts_list; + lsmash_bs_t *bs; + uint32_t max_au_length; + uint32_t num_undecodable; + uint32_t avcC_number; + uint32_t last_delta; + uint64_t last_intra_cts; + uint64_t sc_head_pos; + uint8_t composition_reordering_present; + uint8_t field_pic_present; +} h264_importer_t; + +typedef struct +{ + int64_t poc; + uint32_t delta; + uint16_t poc_delta; + uint16_t reset; +} nal_pic_timing_t; + +static void remove_h264_importer( h264_importer_t *h264_imp ) +{ + if( !h264_imp ) + return; + lsmash_remove_entries( h264_imp->avcC_list, lsmash_destroy_codec_specific_data ); + h264_cleanup_parser( &h264_imp->info ); + lsmash_bs_cleanup( h264_imp->bs ); + lsmash_free( h264_imp->ts_list.timestamp ); + lsmash_free( h264_imp ); +} + +static void h264_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_h264_importer( importer->info ); +} + +static h264_importer_t *create_h264_importer( importer_t *importer ) +{ + h264_importer_t *h264_imp = lsmash_malloc_zero( sizeof(h264_importer_t) ); + if( !h264_imp ) + return NULL; + if( h264_setup_parser( &h264_imp->info, 0 ) < 0 ) + { + remove_h264_importer( h264_imp ); + return NULL; + } + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + { + remove_h264_importer( h264_imp ); + return NULL; + } + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = BS_MAX_DEFAULT_READ_SIZE; + lsmash_init_entry_list( h264_imp->avcC_list ); + h264_imp->bs = bs; + return h264_imp; +} + +static inline int h264_complete_au( h264_access_unit_t *au, int probe ) +{ + if( !au->picture.has_primary || au->incomplete_length == 0 ) + return 0; + if( !probe ) + memcpy( au->data, au->incomplete_data, au->incomplete_length ); + au->length = au->incomplete_length; + au->incomplete_length = 0; + au->picture.has_primary = 0; + return 1; +} + +static void h264_append_nalu_to_au( h264_access_unit_t *au, uint8_t *src_nalu, uint32_t nalu_length, int probe ) +{ + if( !probe ) + { + uint8_t *dst_nalu = au->incomplete_data + au->incomplete_length + NALU_DEFAULT_NALU_LENGTH_SIZE; + for( int i = NALU_DEFAULT_NALU_LENGTH_SIZE; i; i-- ) + *(dst_nalu - i) = (nalu_length >> ((i - 1) * 8)) & 0xff; + memcpy( dst_nalu, src_nalu, nalu_length ); + } + /* Note: au->incomplete_length shall be 0 immediately after AU has completed. + * Therefore, possible_au_length in h264_get_access_unit_internal() can't be used here + * to avoid increasing AU length monotonously through the entire stream. */ + au->incomplete_length += NALU_DEFAULT_NALU_LENGTH_SIZE + nalu_length; +} + +static inline void h264_get_au_internal_end( h264_importer_t *h264_imp, h264_access_unit_t *au ) +{ + if( lsmash_bs_is_end( h264_imp->bs, 0 ) && (au->incomplete_length == 0) ) + h264_imp->status = IMPORTER_EOF; + else if( h264_imp->status != IMPORTER_CHANGE ) + h264_imp->status = IMPORTER_OK; +} + +static int h264_get_au_internal_succeeded( h264_importer_t *h264_imp, h264_access_unit_t *au ) +{ + h264_get_au_internal_end( h264_imp, au ); + au->number += 1; + return 0; +} + +static int h264_get_au_internal_failed( h264_importer_t *h264_imp, h264_access_unit_t *au, int complete_au, int ret ) +{ + h264_get_au_internal_end( h264_imp, au ); + if( complete_au ) + au->number += 1; + return ret; +} + +static lsmash_video_summary_t *h264_create_summary +( + lsmash_h264_specific_parameters_t *param, + h264_sps_t *sps, + uint32_t max_au_length +) +{ + lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); + if( !summary ) + return NULL; + /* Update summary here. + * max_au_length is set at the last of mp4sys_h264_probe function. */ + lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264, + LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + cs->data.unstructured = lsmash_create_h264_specific_info( param, &cs->size ); + if( !cs->data.unstructured + || lsmash_add_entry( &summary->opaque->list, cs ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + summary->sample_type = ISOM_CODEC_TYPE_AVC1_VIDEO; + summary->max_au_length = max_au_length; + summary->timescale = sps->vui.time_scale; + summary->timebase = sps->vui.num_units_in_tick; + summary->vfr = !sps->vui.fixed_frame_rate_flag; + summary->sample_per_field = 0; + summary->width = sps->cropped_width; + summary->height = sps->cropped_height; + summary->par_h = sps->vui.sar_width; + summary->par_v = sps->vui.sar_height; + summary->color.primaries_index = sps->vui.colour_primaries; + summary->color.transfer_index = sps->vui.transfer_characteristics; + summary->color.matrix_index = sps->vui.matrix_coefficients; + summary->color.full_range = sps->vui.video_full_range_flag; + return summary; +} + +static int h264_store_codec_specific +( + h264_importer_t *h264_imp, + lsmash_h264_specific_parameters_t *avcC_param +) +{ + lsmash_codec_specific_t *src_cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !src_cs ) + return LSMASH_ERR_NAMELESS; + lsmash_h264_specific_parameters_t *src_param = (lsmash_h264_specific_parameters_t *)src_cs->data.structured; + *src_param = *avcC_param; + lsmash_codec_specific_t *dst_cs = lsmash_convert_codec_specific_format( src_cs, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + src_param->parameter_sets = NULL; /* Avoid freeing parameter sets within avcC_param. */ + lsmash_destroy_codec_specific_data( src_cs ); + if( !dst_cs ) + { + lsmash_destroy_codec_specific_data( dst_cs ); + return LSMASH_ERR_NAMELESS; + } + if( lsmash_add_entry( h264_imp->avcC_list, dst_cs ) < 0 ) + { + lsmash_destroy_codec_specific_data( dst_cs ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static inline void h264_new_access_unit +( + h264_access_unit_t *au +) +{ + au->length = 0; + au->picture.type = H264_PICTURE_TYPE_NONE; + au->picture.random_accessible = 0; + au->picture.recovery_frame_cnt = 0; + au->picture.has_mmco5 = 0; + au->picture.has_redundancy = 0; + au->picture.broken_link_flag = 0; +} + +/* If probe equals 0, don't get the actual data (EBPS) of an access unit and only parse NALU. + * Currently, you can get AU of AVC video elemental stream only, not AVC parameter set elemental stream defined in 14496-15. */ +static int h264_get_access_unit_internal +( + importer_t *importer, + int probe +) +{ + h264_importer_t *h264_imp = (h264_importer_t *)importer->info; + h264_info_t *info = &h264_imp->info; + h264_slice_info_t *slice = &info->slice; + h264_access_unit_t *au = &info->au; + h264_picture_info_t *picture = &au->picture; + h264_stream_buffer_t *sb = &info->buffer; + lsmash_bs_t *bs = h264_imp->bs; + int complete_au = 0; + h264_new_access_unit( au ); + while( 1 ) + { + h264_nalu_header_t nuh; + uint64_t start_code_length; + uint64_t trailing_zero_bytes; + uint64_t nalu_length = h264_find_next_start_code( bs, &nuh, &start_code_length, &trailing_zero_bytes ); + if( start_code_length <= NALU_SHORT_START_CODE_LENGTH && lsmash_bs_is_end( bs, nalu_length ) ) + { + /* For the last NALU. + * This NALU already has been appended into the latest access unit and parsed. */ + h264_update_picture_info( info, picture, slice, &info->sei ); + complete_au = h264_complete_au( au, probe ); + if( complete_au ) + return h264_get_au_internal_succeeded( h264_imp, au ); + else + return h264_get_au_internal_failed( h264_imp, au, complete_au, LSMASH_ERR_INVALID_DATA ); + } + uint8_t nalu_type = nuh.nal_unit_type; + uint64_t next_sc_head_pos = h264_imp->sc_head_pos + + start_code_length + + nalu_length + + trailing_zero_bytes; +#if 0 + if( probe ) + { + fprintf( stderr, "NALU type: %"PRIu8" \n", nalu_type ); + fprintf( stderr, " NALU header position: %"PRIx64" \n", h264_imp->sc_head_pos + start_code_length ); + fprintf( stderr, " EBSP position: %"PRIx64" \n", h264_imp->sc_head_pos + start_code_length + nuh.length ); + fprintf( stderr, " EBSP length: %"PRIx64" (%"PRIu64") \n", nalu_length - nuh.length, nalu_length - nuh.length ); + fprintf( stderr, " trailing_zero_bytes: %"PRIx64" \n", trailing_zero_bytes ); + fprintf( stderr, " Next start code position: %"PRIx64"\n", next_sc_head_pos ); + } +#endif + if( nalu_type == H264_NALU_TYPE_FD ) + { + /* We don't support streams with both filler and HRD yet. + * Otherwise, just skip filler because 'avc1' and 'avc2' samples are forbidden to use filler. */ + if( info->sps.vui.hrd.present ) + return h264_get_au_internal_failed( h264_imp, au, complete_au, LSMASH_ERR_PATCH_WELCOME ); + } + else if( (nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SPS_EXT) + || nalu_type == H264_NALU_TYPE_SLICE_AUX ) + { + int err; + /* Increase the buffer if needed. */ + uint64_t possible_au_length = au->incomplete_length + NALU_DEFAULT_NALU_LENGTH_SIZE + nalu_length; + if( sb->bank->buffer_size < possible_au_length + && (err = h264_supplement_buffer( sb, au, 2 * possible_au_length )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to increase the buffer size.\n" ); + return h264_get_au_internal_failed( h264_imp, au, complete_au, err ); + } + /* Get the EBSP of the current NALU here. + * AVC elemental stream defined in 14496-15 can recognizes from 0 to 13, and 19 of nal_unit_type. + * We don't support SVC and MVC elemental stream defined in 14496-15 yet. */ + uint8_t *nalu = lsmash_bs_get_buffer_data( bs ) + start_code_length; + if( nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SLICE_IDR ) + { + /* VCL NALU (slice) */ + h264_slice_info_t prev_slice = *slice; + if( (err = h264_parse_slice( info, &nuh, sb->rbsp, nalu + nuh.length, nalu_length - nuh.length )) < 0 ) + return h264_get_au_internal_failed( h264_imp, au, complete_au, err ); + if( probe && info->avcC_pending ) + { + /* Copy and append a Codec Specific info. */ + if( (err = h264_store_codec_specific( h264_imp, &info->avcC_param )) < 0 ) + return err; + } + if( (err = h264_move_pending_avcC_param( info )) < 0 ) + return err; + if( prev_slice.present ) + { + /* Check whether the AU that contains the previous VCL NALU completed or not. */ + if( h264_find_au_delimit_by_slice_info( slice, &prev_slice ) ) + { + /* The current NALU is the first VCL NALU of the primary coded picture of an new AU. + * Therefore, the previous slice belongs to the AU you want at this time. */ + h264_update_picture_info( info, picture, &prev_slice, &info->sei ); + complete_au = h264_complete_au( au, probe ); + } + else + h264_update_picture_info_for_slice( info, picture, &prev_slice ); + } + h264_append_nalu_to_au( au, nalu, nalu_length, probe ); + slice->present = 1; + } + else + { + if( h264_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) + { + /* The last slice belongs to the AU you want at this time. */ + h264_update_picture_info( info, picture, slice, &info->sei ); + complete_au = h264_complete_au( au, probe ); + } + switch( nalu_type ) + { + case H264_NALU_TYPE_SEI : + { + if( (err = h264_parse_sei( info->bits, &info->sps, &info->sei, sb->rbsp, + nalu + nuh.length, + nalu_length - nuh.length )) < 0 ) + return h264_get_au_internal_failed( h264_imp, au, complete_au, err ); + h264_append_nalu_to_au( au, nalu, nalu_length, probe ); + break; + } + case H264_NALU_TYPE_SPS : + if( (err = h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPS, nalu, nalu_length )) < 0 ) + return h264_get_au_internal_failed( h264_imp, au, complete_au, err ); + break; + case H264_NALU_TYPE_PPS : + if( (err = h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_PPS, nalu, nalu_length )) < 0 ) + return h264_get_au_internal_failed( h264_imp, au, complete_au, err ); + break; + case H264_NALU_TYPE_AUD : /* We drop access unit delimiters. */ + break; + case H264_NALU_TYPE_SPS_EXT : + if( (err = h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPSEXT, nalu, nalu_length )) < 0 ) + return h264_get_au_internal_failed( h264_imp, au, complete_au, err ); + break; + default : + h264_append_nalu_to_au( au, nalu, nalu_length, probe ); + break; + } + if( info->avcC_pending ) + h264_imp->status = IMPORTER_CHANGE; + } + } + /* Move to the first byte of the next start code. */ + info->prev_nalu_type = nalu_type; + if( lsmash_bs_read_seek( bs, next_sc_head_pos, SEEK_SET ) != next_sc_head_pos ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to seek the next start code.\n" ); + return h264_get_au_internal_failed( h264_imp, au, complete_au, LSMASH_ERR_NAMELESS ); + } + /* Check if no more data to read from the stream. */ + if( !lsmash_bs_is_end( bs, NALU_SHORT_START_CODE_LENGTH ) ) + h264_imp->sc_head_pos = next_sc_head_pos; + /* If there is no more data in the stream, and flushed chunk of NALUs, flush it as complete AU here. */ + else if( au->incomplete_length && au->length == 0 ) + { + h264_update_picture_info( info, picture, slice, &info->sei ); + h264_complete_au( au, probe ); + return h264_get_au_internal_succeeded( h264_imp, au ); + } + if( complete_au ) + return h264_get_au_internal_succeeded( h264_imp, au ); + } +} + +static int h264_importer_get_accessunit +( + importer_t *importer, + uint32_t track_number, + lsmash_sample_t *buffered_sample +) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + h264_importer_t *h264_imp = (h264_importer_t *)importer->info; + h264_info_t *info = &h264_imp->info; + importer_status current_status = h264_imp->status; + if( current_status == IMPORTER_ERROR || buffered_sample->length < h264_imp->max_au_length ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + int err = h264_get_access_unit_internal( importer, 0 ); + if( err < 0 ) + { + h264_imp->status = IMPORTER_ERROR; + return err; + } + if( h264_imp->status == IMPORTER_CHANGE && !info->avcC_pending ) + current_status = IMPORTER_CHANGE; + if( current_status == IMPORTER_CHANGE ) + { + /* Update the active summary. */ + lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( h264_imp->avcC_list, ++ h264_imp->avcC_number ); + if( !cs ) + return LSMASH_ERR_NAMELESS; + lsmash_h264_specific_parameters_t *avcC_param = (lsmash_h264_specific_parameters_t *)cs->data.structured; + lsmash_video_summary_t *summary = h264_create_summary( avcC_param, &info->sps, h264_imp->max_au_length ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + lsmash_remove_entry( importer->summaries, track_number, lsmash_cleanup_summary ); + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return LSMASH_ERR_MEMORY_ALLOC; + } + h264_imp->status = IMPORTER_OK; + } + h264_access_unit_t *au = &info->au; + h264_picture_info_t *picture = &au->picture; + buffered_sample->dts = h264_imp->ts_list.timestamp[ au->number - 1 ].dts; + buffered_sample->cts = h264_imp->ts_list.timestamp[ au->number - 1 ].cts; + if( au->number < h264_imp->num_undecodable ) + buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING; + else + buffered_sample->prop.leading = picture->independent || buffered_sample->cts >= h264_imp->last_intra_cts + ? ISOM_SAMPLE_IS_NOT_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING; + if( picture->independent ) + h264_imp->last_intra_cts = buffered_sample->cts; + if( h264_imp->composition_reordering_present && !picture->disposable && !picture->idr ) + buffered_sample->prop.allow_earlier = QT_SAMPLE_EARLIER_PTS_ALLOWED; + buffered_sample->prop.independent = picture->independent ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT; + buffered_sample->prop.disposable = picture->disposable ? ISOM_SAMPLE_IS_DISPOSABLE : ISOM_SAMPLE_IS_NOT_DISPOSABLE; + buffered_sample->prop.redundant = picture->has_redundancy ? ISOM_SAMPLE_HAS_REDUNDANCY : ISOM_SAMPLE_HAS_NO_REDUNDANCY; + buffered_sample->prop.post_roll.identifier = picture->frame_num; + if( picture->random_accessible ) + { + if( picture->idr ) + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + else if( picture->recovery_frame_cnt ) + { + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START; + buffered_sample->prop.post_roll.complete = (picture->frame_num + picture->recovery_frame_cnt) % info->sps.MaxFrameNum; + } + else + { + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; + if( !picture->broken_link_flag ) + buffered_sample->prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC; + } + } + buffered_sample->length = au->length; + memcpy( buffered_sample->data, au->data, au->length ); + return current_status; +} + +static void nalu_deduplicate_poc +( + nal_pic_timing_t *npt, + uint32_t *max_composition_delay, + uint32_t num_access_units, + uint32_t max_num_reorder_pics +) +{ + /* Deduplicate POCs. */ + int64_t poc_offset = 0; + int64_t poc_min = 0; + int64_t invalid_poc_min = 0; + uint32_t last_poc_reset = UINT32_MAX; + uint32_t invalid_poc_start = 0; + int invalid_poc_present = 0; + for( uint32_t i = 0; ; i++ ) + { + if( i < num_access_units && npt[i].poc != 0 && !npt[i].reset ) + { + /* poc_offset is not added to each POC here. + * It is done when we encounter the next coded video sequence. */ + if( npt[i].poc < 0 ) + { + /* Pictures with negative POC shall precede IDR-picture in composition order. + * The minimum POC is added to poc_offset when we encounter the next coded video sequence. */ + if( last_poc_reset == UINT32_MAX || i > last_poc_reset + max_num_reorder_pics ) + { + if( !invalid_poc_present ) + { + invalid_poc_present = 1; + invalid_poc_start = i; + } + if( invalid_poc_min > npt[i].poc ) + invalid_poc_min = npt[i].poc; + } + else if( poc_min > npt[i].poc ) + { + poc_min = npt[i].poc; + *max_composition_delay = LSMASH_MAX( *max_composition_delay, i - last_poc_reset ); + } + } + continue; + } + /* Encountered a new coded video sequence or no more POCs. + * Add poc_offset to each POC of the previous coded video sequence. */ + poc_offset -= poc_min; + int64_t poc_max = 0; + for( uint32_t j = last_poc_reset; j < i + !!npt[i].reset; j++ ) + if( npt[j].poc >= 0 || (j <= last_poc_reset + max_num_reorder_pics) ) + { + npt[j].poc += poc_offset; + if( poc_max < npt[j].poc ) + poc_max = npt[j].poc; + } + poc_offset = poc_max + 1; + if( invalid_poc_present ) + { + /* Pictures with invalid negative POC is probably supposed to be composited + * both before the next coded video sequence and after the current one. */ + poc_offset -= invalid_poc_min; + for( uint32_t j = invalid_poc_start; j < i + !!npt[i].reset; j++ ) + if( npt[j].poc < 0 ) + { + npt[j].poc += poc_offset; + if( poc_max < npt[j].poc ) + poc_max = npt[j].poc; + } + invalid_poc_present = 0; + invalid_poc_start = 0; + invalid_poc_min = 0; + poc_offset = poc_max + 1; + } + if( i < num_access_units ) + { + if( npt[i].reset ) + npt[i].poc = 0; + poc_min = 0; + last_poc_reset = i; + } + else + break; /* no more POCs */ + } +} + +static void nalu_generate_timestamps_from_poc +( + importer_t *importer, + lsmash_media_ts_t *timestamp, + nal_pic_timing_t *npt, + uint8_t *composition_reordering_present, + uint32_t *last_delta, + uint32_t max_composition_delay, + uint32_t num_access_units +) +{ + /* Check if composition delay derived from reordering is present. */ + if( max_composition_delay == 0 ) + { + for( uint32_t i = 1; i < num_access_units; i++ ) + if( npt[i].poc < npt[i - 1].poc ) + { + *composition_reordering_present = 1; + break; + } + } + else + *composition_reordering_present = 1; + /* Generate timestamps. */ + if( *composition_reordering_present ) + { + /* Generate timestamps. + * Here, DTSs and CTSs are temporary values for sort. */ + for( uint32_t i = 0; i < num_access_units; i++ ) + { + timestamp[i].cts = (uint64_t)npt[i].poc; + timestamp[i].dts = (uint64_t)i; + } + qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_cts ); + /* Check POC gap in output order. */ + lsmash_class_t *logger = &(lsmash_class_t){ .name = importer->class->name }; + for( uint32_t i = 1; i < num_access_units; i++ ) + if( timestamp[i].cts > timestamp[i - 1].cts + npt[i - 1].poc_delta ) + lsmash_log( &logger, LSMASH_LOG_WARNING, + "POC gap is detected at picture %"PRIu64". Maybe some pictures are lost.\n", timestamp[i].dts ); + /* Get the maximum composition delay derived from reordering. */ + for( uint32_t i = 0; i < num_access_units; i++ ) + if( i < timestamp[i].dts ) + { + uint32_t composition_delay = timestamp[i].dts - i; + max_composition_delay = LSMASH_MAX( max_composition_delay, composition_delay ); + } + uint64_t *ts_buffer = (uint64_t *)lsmash_malloc( (num_access_units + max_composition_delay) * sizeof(uint64_t) ); + if( !ts_buffer ) + { + /* It seems that there is no enough memory to generate more appropriate timestamps. + * Anyway, generate CTSs and DTSs. */ + for( uint32_t i = 0; i < num_access_units; i++ ) + timestamp[i].cts = i + max_composition_delay; + qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_dts ); + *last_delta = 1; + return; + } + uint64_t *reorder_cts = ts_buffer; + uint64_t *prev_reorder_cts = ts_buffer + num_access_units; + *last_delta = npt[num_access_units - 1].delta; + /* Generate CTSs. */ + timestamp[0].cts = 0; + for( uint32_t i = 1; i < num_access_units; i++ ) + timestamp[i].cts = timestamp[i - 1].cts + npt[i - 1].delta; + int64_t composition_delay_time = timestamp[max_composition_delay].cts; + for( uint32_t i = 0; i < num_access_units; i++ ) + { + timestamp[i].cts += composition_delay_time; + reorder_cts[i] = timestamp[i].cts; + } + /* Generate DTSs. */ + qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_dts ); + for( uint32_t i = 0; i < num_access_units; i++ ) + { + timestamp[i].dts = i <= max_composition_delay + ? reorder_cts[i] - composition_delay_time + : prev_reorder_cts[(i - max_composition_delay) % max_composition_delay]; + prev_reorder_cts[i % max_composition_delay] = reorder_cts[i]; + } + lsmash_free( ts_buffer ); +#if 0 + fprintf( stderr, "max_composition_delay=%"PRIu32", composition_delay_time=%"PRIu64"\n", + max_composition_delay, composition_delay_time ); +#endif + } + else + { + timestamp[0].dts = 0; + timestamp[0].cts = 0; + for( uint32_t i = 1; i < num_access_units; i++ ) + { + timestamp[i].dts = timestamp[i - 1].dts + npt[i - 1].delta; + timestamp[i].cts = timestamp[i - 1].cts + npt[i - 1].delta; + } + *last_delta = npt[num_access_units - 1].delta; + } +} + +static void nalu_reduce_timescale +( + lsmash_media_ts_t *timestamp, + nal_pic_timing_t *npt, + uint32_t *last_delta, + uint32_t *timescale, + uint32_t num_access_units +) +{ + uint64_t gcd_delta = *timescale; + for( uint32_t i = 0; i < num_access_units && gcd_delta > 1; i++ ) + gcd_delta = lsmash_get_gcd( gcd_delta, npt[i].delta ); + if( gcd_delta > 1 ) + { + for( uint32_t i = 0; i < num_access_units; i++ ) + { + timestamp[i].dts /= gcd_delta; + timestamp[i].cts /= gcd_delta; + } + *last_delta /= gcd_delta; + *timescale /= gcd_delta; + } +#if 0 + for( uint32_t i = 0; i < num_access_units; i++ ) + fprintf( stderr, "Timestamp[%"PRIu32"]: POC=%"PRId64", DTS=%"PRIu64", CTS=%"PRIu64"\n", + i, npt[i].poc, timestamp[i].dts, timestamp[i].cts ); +#endif +} + +static lsmash_video_summary_t *h264_setup_first_summary +( + importer_t *importer +) +{ + h264_importer_t *h264_imp = (h264_importer_t *)importer->info; + lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( h264_imp->avcC_list, ++ h264_imp->avcC_number ); + if( !cs || !cs->data.structured ) + { + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + lsmash_video_summary_t *summary = h264_create_summary( (lsmash_h264_specific_parameters_t *)cs->data.structured, + &h264_imp->info.sps, h264_imp->max_au_length ); + if( !summary ) + { + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; + } + summary->sample_per_field = h264_imp->field_pic_present; + return summary; +} + +static int h264_analyze_whole_stream +( + importer_t *importer +) +{ + /* Parse all NALU in the stream for preparation of calculating timestamps. */ + uint32_t npt_alloc = (1 << 12) * sizeof(nal_pic_timing_t); + nal_pic_timing_t *npt = lsmash_malloc( npt_alloc ); + if( !npt ) + return LSMASH_ERR_MEMORY_ALLOC; + uint32_t picture_stats[H264_PICTURE_TYPE_NONE + 1] = { 0 }; + uint32_t num_access_units = 0; + lsmash_class_t *logger = &(lsmash_class_t){ "H.264" }; + lsmash_log( &logger, LSMASH_LOG_INFO, "Analyzing stream as H.264\r" ); + h264_importer_t *h264_imp = (h264_importer_t *)importer->info; + h264_info_t *info = &h264_imp->info; + h264_imp->status = IMPORTER_OK; + int err = LSMASH_ERR_MEMORY_ALLOC; + while( h264_imp->status != IMPORTER_EOF ) + { +#if 0 + lsmash_log( &logger, LSMASH_LOG_INFO, "Analyzing stream as H.264: %"PRIu32"\n", num_access_units + 1 ); +#endif + h264_picture_info_t *picture = &info->au.picture; + h264_picture_info_t prev_picture = *picture; + if( (err = h264_get_access_unit_internal( importer, 1 )) < 0 + || (err = h264_calculate_poc( info, picture, &prev_picture )) < 0 ) + goto fail; + if( npt_alloc <= num_access_units * sizeof(nal_pic_timing_t) ) + { + uint32_t alloc = 2 * num_access_units * sizeof(nal_pic_timing_t); + nal_pic_timing_t *temp = (nal_pic_timing_t *)lsmash_realloc( npt, alloc ); + if( !temp ) + goto fail; + npt = temp; + npt_alloc = alloc; + } + h264_imp->field_pic_present |= picture->field_pic_flag; + npt[num_access_units].poc = picture->PicOrderCnt; + npt[num_access_units].delta = picture->delta; + npt[num_access_units].poc_delta = picture->field_pic_flag ? 1 : 2; + npt[num_access_units].reset = picture->has_mmco5; + ++num_access_units; + h264_imp->max_au_length = LSMASH_MAX( info->au.length, h264_imp->max_au_length ); + if( picture->idr ) + ++picture_stats[H264_PICTURE_TYPE_IDR]; + else if( picture->type >= H264_PICTURE_TYPE_NONE ) + ++picture_stats[H264_PICTURE_TYPE_NONE]; + else + ++picture_stats[ picture->type ]; + } + lsmash_log_refresh_line( &logger ); + lsmash_log( &logger, LSMASH_LOG_INFO, + "IDR: %"PRIu32", I: %"PRIu32", P: %"PRIu32", B: %"PRIu32", " + "SI: %"PRIu32", SP: %"PRIu32", Unknown: %"PRIu32"\n", + picture_stats[H264_PICTURE_TYPE_IDR ], + picture_stats[H264_PICTURE_TYPE_I ], + picture_stats[H264_PICTURE_TYPE_I_P ], + picture_stats[H264_PICTURE_TYPE_I_P_B ], + picture_stats[H264_PICTURE_TYPE_SI ] + + picture_stats[H264_PICTURE_TYPE_I_SI ], + picture_stats[H264_PICTURE_TYPE_SI_SP ] + + picture_stats[H264_PICTURE_TYPE_I_SI_P_SP ] + + picture_stats[H264_PICTURE_TYPE_I_SI_P_SP_B], + picture_stats[H264_PICTURE_TYPE_NONE ] ); + /* Copy and append the last Codec Specific info. */ + if( (err = h264_store_codec_specific( h264_imp, &info->avcC_param )) < 0 ) + goto fail; + /* Set up the first summary. */ + lsmash_video_summary_t *summary = h264_setup_first_summary( importer ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + /* Allocate timestamps. */ + lsmash_media_ts_t *timestamp = lsmash_malloc( num_access_units * sizeof(lsmash_media_ts_t) ); + if( !timestamp ) + goto fail; + /* Count leading samples that are undecodable. */ + for( uint32_t i = 0; i < num_access_units; i++ ) + { + if( npt[i].poc == 0 ) + break; + ++ h264_imp->num_undecodable; + } + /* Deduplicate POCs. */ + uint32_t max_composition_delay = 0; + nalu_deduplicate_poc( npt, &max_composition_delay, num_access_units, 32 ); + /* Generate timestamps. */ + nalu_generate_timestamps_from_poc( importer, timestamp, npt, + &h264_imp->composition_reordering_present, + &h264_imp->last_delta, + max_composition_delay, num_access_units ); + nalu_reduce_timescale( timestamp, npt, &h264_imp->last_delta, &summary->timescale, num_access_units ); + lsmash_free( npt ); + h264_imp->ts_list.sample_count = num_access_units; + h264_imp->ts_list.timestamp = timestamp; + return 0; +fail: + lsmash_log_refresh_line( &logger ); + lsmash_free( npt ); + return err; +} + +static int h264_importer_probe( importer_t *importer ) +{ + /* Find the first start code. */ + h264_importer_t *h264_imp = create_h264_importer( importer ); + if( !h264_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = h264_imp->bs; + uint64_t first_sc_head_pos = nalu_find_first_start_code( bs ); + int err; + if( first_sc_head_pos == NALU_NO_START_CODE_FOUND ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + /* OK. It seems the stream has a long start code of H.264. */ + importer->info = h264_imp; + h264_info_t *info = &h264_imp->info; + lsmash_bs_read_seek( bs, first_sc_head_pos, SEEK_SET ); + h264_imp->sc_head_pos = first_sc_head_pos; + if( (err = h264_analyze_whole_stream( importer )) < 0 ) + goto fail; + /* Go back to the start code of the first NALU. */ + h264_imp->status = IMPORTER_OK; + lsmash_bs_read_seek( bs, first_sc_head_pos, SEEK_SET ); + h264_imp->sc_head_pos = first_sc_head_pos; + info->prev_nalu_type = H264_NALU_TYPE_UNSPECIFIED0; + uint8_t *temp_au = info->au.data; + uint8_t *temp_incomplete_au = info->au.incomplete_data; + memset( &info->au, 0, sizeof(h264_access_unit_t) ); + info->au.data = temp_au; + info->au.incomplete_data = temp_incomplete_au; + memset( &info->slice, 0, sizeof(h264_slice_info_t) ); + memset( &info->sps, 0, sizeof(h264_sps_t) ); + memset( &info->pps, 0, sizeof(h264_pps_t) ); + lsmash_remove_entries( info->avcC_param.parameter_sets->sps_list, isom_remove_dcr_ps ); + lsmash_remove_entries( info->avcC_param.parameter_sets->pps_list, isom_remove_dcr_ps ); + lsmash_remove_entries( info->avcC_param.parameter_sets->spsext_list, isom_remove_dcr_ps ); + lsmash_destroy_h264_parameter_sets( &info->avcC_param_next ); + return 0; +fail: + remove_h264_importer( h264_imp ); + importer->info = NULL; + lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); + return err; +} + +static uint32_t h264_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + h264_importer_t *h264_imp = (h264_importer_t *)importer->info; + if( !h264_imp || track_number != 1 || h264_imp->status != IMPORTER_EOF ) + return 0; + return h264_imp->ts_list.sample_count + ? h264_imp->last_delta + : UINT32_MAX; /* arbitrary */ +} + +const importer_functions h264_importer = +{ + { "H.264", offsetof( importer_t, log_level ) }, + 1, + h264_importer_probe, + h264_importer_get_accessunit, + h264_importer_get_last_delta, + h264_importer_cleanup +}; + +/*************************************************************************** + HEVC importer + ITU-T Recommendation H.265 (04/13) + ISO/IEC 14496-15:2014 +***************************************************************************/ +#include "codecs/hevc.h" + +typedef struct +{ + importer_status status; + hevc_info_t info; + lsmash_entry_list_t hvcC_list[1]; /* stored as lsmash_codec_specific_t */ + lsmash_media_ts_list_t ts_list; + lsmash_bs_t *bs; + uint32_t max_au_length; + uint32_t num_undecodable; + uint32_t hvcC_number; + uint32_t last_delta; + uint64_t last_intra_cts; + uint64_t sc_head_pos; + uint8_t composition_reordering_present; + uint8_t field_pic_present; + uint8_t max_TemporalId; +} hevc_importer_t; + +static void remove_hevc_importer( hevc_importer_t *hevc_imp ) +{ + if( !hevc_imp ) + return; + lsmash_remove_entries( hevc_imp->hvcC_list, lsmash_destroy_codec_specific_data ); + hevc_cleanup_parser( &hevc_imp->info ); + lsmash_bs_cleanup( hevc_imp->bs ); + lsmash_free( hevc_imp->ts_list.timestamp ); + lsmash_free( hevc_imp ); +} + +static void hevc_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_hevc_importer( importer->info ); +} + +static hevc_importer_t *create_hevc_importer( importer_t *importer ) +{ + hevc_importer_t *hevc_imp = lsmash_malloc_zero( sizeof(hevc_importer_t) ); + if( !hevc_imp ) + return NULL; + if( hevc_setup_parser( &hevc_imp->info, 0 ) < 0 ) + { + remove_hevc_importer( hevc_imp ); + return NULL; + } + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + { + remove_hevc_importer( hevc_imp ); + return NULL; + } + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = BS_MAX_DEFAULT_READ_SIZE; + lsmash_init_entry_list( hevc_imp->hvcC_list ); + hevc_imp->bs = bs; + hevc_imp->info.eos = 1; + return hevc_imp; +} + +static inline int hevc_complete_au( hevc_access_unit_t *au, int probe ) +{ + if( !au->picture.has_primary || au->incomplete_length == 0 ) + return 0; + if( !probe ) + memcpy( au->data, au->incomplete_data, au->incomplete_length ); + au->TemporalId = au->picture.TemporalId; + au->length = au->incomplete_length; + au->incomplete_length = 0; + au->picture.has_primary = 0; + return 1; +} + +static void hevc_append_nalu_to_au( hevc_access_unit_t *au, uint8_t *src_nalu, uint32_t nalu_length, int probe ) +{ + if( !probe ) + { + uint8_t *dst_nalu = au->incomplete_data + au->incomplete_length + NALU_DEFAULT_NALU_LENGTH_SIZE; + for( int i = NALU_DEFAULT_NALU_LENGTH_SIZE; i; i-- ) + *(dst_nalu - i) = (nalu_length >> ((i - 1) * 8)) & 0xff; + memcpy( dst_nalu, src_nalu, nalu_length ); + } + /* Note: picture->incomplete_au_length shall be 0 immediately after AU has completed. + * Therefore, possible_au_length in hevc_get_access_unit_internal() can't be used here + * to avoid increasing AU length monotonously through the entire stream. */ + au->incomplete_length += NALU_DEFAULT_NALU_LENGTH_SIZE + nalu_length; +} + +static inline void hevc_get_au_internal_end( hevc_importer_t *hevc_imp, hevc_access_unit_t *au ) +{ + if( lsmash_bs_is_end( hevc_imp->bs, 0 ) && (au->incomplete_length == 0) ) + hevc_imp->status = IMPORTER_EOF; + else if( hevc_imp->status != IMPORTER_CHANGE ) + hevc_imp->status = IMPORTER_OK; +} + +static int hevc_get_au_internal_succeeded( hevc_importer_t *hevc_imp, hevc_access_unit_t *au ) +{ + hevc_get_au_internal_end( hevc_imp, au ); + au->number += 1; + return 0; +} + +static int hevc_get_au_internal_failed( hevc_importer_t *hevc_imp, hevc_access_unit_t *au, int complete_au, int ret ) +{ + hevc_get_au_internal_end( hevc_imp, au ); + if( complete_au ) + au->number += 1; + return ret; +} + +static lsmash_video_summary_t *hevc_create_summary +( + lsmash_hevc_specific_parameters_t *param, + hevc_sps_t *sps, + uint32_t max_au_length +) +{ + lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); + if( !summary ) + return NULL; + /* Update summary here. + * max_au_length is set at the last of hevc_importer_probe function. */ + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC, + LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + specific->data.unstructured = lsmash_create_hevc_specific_info( param, &specific->size ); + if( !specific->data.unstructured + || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + summary->sample_type = ISOM_CODEC_TYPE_HVC1_VIDEO; + summary->max_au_length = max_au_length; + summary->timescale = sps->vui.time_scale; + summary->timebase = sps->vui.num_units_in_tick; + summary->vfr = (param->constantFrameRate == 0); + summary->sample_per_field = 0; + summary->width = sps->cropped_width; + summary->height = sps->cropped_height; + summary->par_h = sps->vui.sar_width; + summary->par_v = sps->vui.sar_height; + summary->color.primaries_index = sps->vui.colour_primaries != 2 ? sps->vui.colour_primaries : 0; + summary->color.transfer_index = sps->vui.transfer_characteristics != 2 ? sps->vui.transfer_characteristics : 0; + summary->color.matrix_index = sps->vui.matrix_coeffs != 2 ? sps->vui.matrix_coeffs : 0; + summary->color.full_range = sps->vui.video_full_range_flag; + lsmash_convert_crop_into_clap( sps->vui.def_disp_win_offset, summary->width, summary->height, &summary->clap ); + return summary; +} + +static int hevc_store_codec_specific +( + hevc_importer_t *hevc_imp, + lsmash_hevc_specific_parameters_t *hvcC_param +) +{ + lsmash_codec_specific_t *src_cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !src_cs ) + return LSMASH_ERR_NAMELESS; + lsmash_hevc_specific_parameters_t *src_param = (lsmash_hevc_specific_parameters_t *)src_cs->data.structured; + *src_param = *hvcC_param; + lsmash_codec_specific_t *dst_cs = lsmash_convert_codec_specific_format( src_cs, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + src_param->parameter_arrays = NULL; /* Avoid freeing parameter arrays within hvcC_param. */ + lsmash_destroy_codec_specific_data( src_cs ); + if( !dst_cs ) + { + lsmash_destroy_codec_specific_data( dst_cs ); + return LSMASH_ERR_NAMELESS; + } + if( lsmash_add_entry( hevc_imp->hvcC_list, dst_cs ) < 0 ) + { + lsmash_destroy_codec_specific_data( dst_cs ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static inline void hevc_new_access_unit( hevc_access_unit_t *au ) +{ + au->length = 0; + au->picture.type = HEVC_PICTURE_TYPE_NONE; + au->picture.random_accessible = 0; + au->picture.recovery_poc_cnt = 0; +} + +/* If probe equals 0, don't get the actual data (EBPS) of an access unit and only parse NALU. */ +static int hevc_get_access_unit_internal +( + importer_t *importer, + int probe +) +{ + hevc_importer_t *hevc_imp = (hevc_importer_t *)importer->info; + hevc_info_t *info = &hevc_imp->info; + hevc_slice_info_t *slice = &info->slice; + hevc_access_unit_t *au = &info->au; + hevc_picture_info_t *picture = &au->picture; + hevc_stream_buffer_t *sb = &info->buffer; + lsmash_bs_t *bs = hevc_imp->bs; + int complete_au = 0; + hevc_new_access_unit( au ); + while( 1 ) + { + hevc_nalu_header_t nuh; + uint64_t start_code_length; + uint64_t trailing_zero_bytes; + uint64_t nalu_length = hevc_find_next_start_code( bs, &nuh, &start_code_length, &trailing_zero_bytes ); + if( start_code_length <= NALU_SHORT_START_CODE_LENGTH && lsmash_bs_is_end( bs, nalu_length ) ) + { + /* For the last NALU. + * This NALU already has been appended into the latest access unit and parsed. */ + hevc_update_picture_info( info, picture, slice, &info->sps, &info->sei ); + complete_au = hevc_complete_au( au, probe ); + if( complete_au ) + return hevc_get_au_internal_succeeded( hevc_imp, au ); + else + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, LSMASH_ERR_INVALID_DATA ); + } + uint8_t nalu_type = nuh.nal_unit_type; + uint64_t next_sc_head_pos = hevc_imp->sc_head_pos + + start_code_length + + nalu_length + + trailing_zero_bytes; +#if 0 + if( probe ) + { + fprintf( stderr, "NALU type: %"PRIu8" \n", nalu_type ); + fprintf( stderr, " NALU header position: %"PRIx64" \n", hevc_imp->sc_head_pos + start_code_length ); + fprintf( stderr, " EBSP position: %"PRIx64" \n", hevc_imp->sc_head_pos + start_code_length + nuh.length ); + fprintf( stderr, " EBSP length: %"PRIx64" (%"PRIu64") \n", nalu_length - nuh.length, nalu_length - nuh.length ); + fprintf( stderr, " trailing_zero_bytes: %"PRIx64" \n", trailing_zero_bytes ); + fprintf( stderr, " Next start code position: %"PRIx64"\n", next_sc_head_pos ); + } +#endif + /* Check if the end of sequence. Used for POC calculation. */ + info->eos |= info->prev_nalu_type == HEVC_NALU_TYPE_EOS + || info->prev_nalu_type == HEVC_NALU_TYPE_EOB; + /* Process the current NALU by its type. */ + if( nalu_type == HEVC_NALU_TYPE_FD ) + { + /* We don't support streams with both filler and HRD yet. Otherwise, just skip filler. */ + if( info->sps.vui.hrd.present ) + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, LSMASH_ERR_PATCH_WELCOME ); + } + else if( nalu_type <= HEVC_NALU_TYPE_RASL_R + || (nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && nalu_type <= HEVC_NALU_TYPE_CRA) + || (nalu_type >= HEVC_NALU_TYPE_VPS && nalu_type <= HEVC_NALU_TYPE_SUFFIX_SEI) ) + { + int err; + /* Increase the buffer if needed. */ + uint64_t possible_au_length = au->incomplete_length + NALU_DEFAULT_NALU_LENGTH_SIZE + nalu_length; + if( sb->bank->buffer_size < possible_au_length + && (err = hevc_supplement_buffer( sb, au, 2 * possible_au_length )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to increase the buffer size.\n" ); + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, err ); + } + /* Get the EBSP of the current NALU here. */ + uint8_t *nalu = lsmash_bs_get_buffer_data( bs ) + start_code_length; + if( nalu_type <= HEVC_NALU_TYPE_RSV_VCL31 ) + { + /* VCL NALU (slice) */ + hevc_slice_info_t prev_slice = *slice; + if( (err = hevc_parse_slice_segment_header( info, &nuh, sb->rbsp, + nalu + nuh.length, + nalu_length - nuh.length )) < 0 ) + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, err ); + if( probe && info->hvcC_pending ) + { + /* Copy and append a Codec Specific info. */ + if( (err = hevc_store_codec_specific( hevc_imp, &info->hvcC_param )) < 0 ) + return err; + } + if( (err = hevc_move_pending_hvcC_param( info )) < 0 ) + return err; + if( prev_slice.present ) + { + /* Check whether the AU that contains the previous VCL NALU completed or not. */ + if( hevc_find_au_delimit_by_slice_info( info, slice, &prev_slice ) ) + { + /* The current NALU is the first VCL NALU of the primary coded picture of a new AU. + * Therefore, the previous slice belongs to the AU you want at this time. */ + hevc_update_picture_info( info, picture, &prev_slice, &info->sps, &info->sei ); + complete_au = hevc_complete_au( au, probe ); + } + else + hevc_update_picture_info_for_slice( info, picture, &prev_slice ); + } + hevc_append_nalu_to_au( au, nalu, nalu_length, probe ); + slice->present = 1; + } + else + { + if( hevc_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) + { + /* The last slice belongs to the AU you want at this time. */ + hevc_update_picture_info( info, picture, slice, &info->sps, &info->sei ); + complete_au = hevc_complete_au( au, probe ); + } + switch( nalu_type ) + { + case HEVC_NALU_TYPE_PREFIX_SEI : + case HEVC_NALU_TYPE_SUFFIX_SEI : + { + if( (err = hevc_parse_sei( info->bits, &info->vps, &info->sps, &info->sei, &nuh, + sb->rbsp, nalu + nuh.length, nalu_length - nuh.length )) < 0 ) + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, err ); + hevc_append_nalu_to_au( au, nalu, nalu_length, probe ); + break; + } + case HEVC_NALU_TYPE_VPS : + if( (err = hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_VPS, nalu, nalu_length )) < 0 ) + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, err ); + break; + case HEVC_NALU_TYPE_SPS : + if( (err = hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_SPS, nalu, nalu_length )) < 0 ) + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, err ); + break; + case HEVC_NALU_TYPE_PPS : + if( (err = hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_PPS, nalu, nalu_length )) < 0 ) + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, err ); + break; + case HEVC_NALU_TYPE_AUD : /* We drop access unit delimiters. */ + break; + default : + hevc_append_nalu_to_au( au, nalu, nalu_length, probe ); + break; + } + if( info->hvcC_pending ) + hevc_imp->status = IMPORTER_CHANGE; + } + } + /* Move to the first byte of the next start code. */ + info->prev_nalu_type = nalu_type; + if( lsmash_bs_read_seek( bs, next_sc_head_pos, SEEK_SET ) != next_sc_head_pos ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to seek the next start code.\n" ); + return hevc_get_au_internal_failed( hevc_imp, au, complete_au, LSMASH_ERR_NAMELESS ); + } + if( !lsmash_bs_is_end( bs, NALU_SHORT_START_CODE_LENGTH ) ) + hevc_imp->sc_head_pos = next_sc_head_pos; + /* If there is no more data in the stream, and flushed chunk of NALUs, flush it as complete AU here. */ + else if( au->incomplete_length && au->length == 0 ) + { + hevc_update_picture_info( info, picture, slice, &info->sps, &info->sei ); + hevc_complete_au( au, probe ); + return hevc_get_au_internal_succeeded( hevc_imp, au ); + } + if( complete_au ) + return hevc_get_au_internal_succeeded( hevc_imp, au ); + } +} + +static int hevc_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + hevc_importer_t *hevc_imp = (hevc_importer_t *)importer->info; + hevc_info_t *info = &hevc_imp->info; + importer_status current_status = hevc_imp->status; + if( current_status == IMPORTER_ERROR || buffered_sample->length < hevc_imp->max_au_length ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + int err = hevc_get_access_unit_internal( importer, 0 ); + if( err < 0 ) + { + hevc_imp->status = IMPORTER_ERROR; + return err; + } + if( hevc_imp->status == IMPORTER_CHANGE && !info->hvcC_pending ) + current_status = IMPORTER_CHANGE; + if( current_status == IMPORTER_CHANGE ) + { + /* Update the active summary. */ + lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( hevc_imp->hvcC_list, ++ hevc_imp->hvcC_number ); + if( !cs ) + return LSMASH_ERR_NAMELESS; + lsmash_hevc_specific_parameters_t *hvcC_param = (lsmash_hevc_specific_parameters_t *)cs->data.structured; + lsmash_video_summary_t *summary = hevc_create_summary( hvcC_param, &info->sps, hevc_imp->max_au_length ); + if( !summary ) + return LSMASH_ERR_NAMELESS; + lsmash_remove_entry( importer->summaries, track_number, lsmash_cleanup_summary ); + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return LSMASH_ERR_MEMORY_ALLOC; + } + hevc_imp->status = IMPORTER_OK; + } + hevc_access_unit_t *au = &info->au; + hevc_picture_info_t *picture = &au->picture; + buffered_sample->dts = hevc_imp->ts_list.timestamp[ au->number - 1 ].dts; + buffered_sample->cts = hevc_imp->ts_list.timestamp[ au->number - 1 ].cts; + /* Set property of disposability. */ + if( picture->sublayer_nonref && au->TemporalId == hevc_imp->max_TemporalId ) + /* Sub-layer non-reference pictures are not referenced by subsequent pictures of + * the same sub-layer in decoding order. */ + buffered_sample->prop.disposable = ISOM_SAMPLE_IS_DISPOSABLE; + else + buffered_sample->prop.disposable = ISOM_SAMPLE_IS_NOT_DISPOSABLE; + /* Set property of leading. */ + if( picture->radl || picture->rasl ) + buffered_sample->prop.leading = picture->radl ? ISOM_SAMPLE_IS_DECODABLE_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING; + else + { + if( au->number < hevc_imp->num_undecodable ) + buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING; + else + { + if( picture->independent || buffered_sample->cts >= hevc_imp->last_intra_cts ) + buffered_sample->prop.leading = ISOM_SAMPLE_IS_NOT_LEADING; + else + buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING; + } + } + if( picture->independent ) + hevc_imp->last_intra_cts = buffered_sample->cts; + /* Set property of independence. */ + buffered_sample->prop.independent = picture->independent ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT; + buffered_sample->prop.redundant = ISOM_SAMPLE_HAS_NO_REDUNDANCY; + buffered_sample->prop.post_roll.identifier = picture->poc; + if( picture->random_accessible ) + { + if( picture->irap ) + { + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + if( picture->closed_rap ) + buffered_sample->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_CLOSED_RAP; + else + buffered_sample->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; + } + else if( picture->recovery_poc_cnt ) + { + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START; + buffered_sample->prop.post_roll.complete = picture->poc + picture->recovery_poc_cnt; + } + else + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; + } + buffered_sample->length = au->length; + memcpy( buffered_sample->data, au->data, au->length ); + return current_status; +} + +static lsmash_video_summary_t *hevc_setup_first_summary +( + importer_t *importer +) +{ + hevc_importer_t *hevc_imp = (hevc_importer_t *)importer->info; + lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( hevc_imp->hvcC_list, ++ hevc_imp->hvcC_number ); + if( !cs || !cs->data.structured ) + { + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + lsmash_video_summary_t *summary = hevc_create_summary( (lsmash_hevc_specific_parameters_t *)cs->data.structured, + &hevc_imp->info.sps, hevc_imp->max_au_length ); + if( !summary ) + { + lsmash_destroy_codec_specific_data( cs ); + return NULL; + } + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; + } + summary->sample_per_field = hevc_imp->field_pic_present; + return summary; +} + +static int hevc_analyze_whole_stream +( + importer_t *importer +) +{ + /* Parse all NALU in the stream for preparation of calculating timestamps. */ + uint32_t npt_alloc = (1 << 12) * sizeof(nal_pic_timing_t); + nal_pic_timing_t *npt = (nal_pic_timing_t *)lsmash_malloc( npt_alloc ); + if( !npt ) + return LSMASH_ERR_MEMORY_ALLOC; + uint32_t picture_stats[HEVC_PICTURE_TYPE_NONE + 1] = { 0 }; + uint32_t num_access_units = 0; + lsmash_class_t *logger = &(lsmash_class_t){ "HEVC" }; + lsmash_log( &logger, LSMASH_LOG_INFO, "Analyzing stream as HEVC\r" ); + hevc_importer_t *hevc_imp = (hevc_importer_t *)importer->info; + hevc_info_t *info = &hevc_imp->info; + hevc_imp->status = IMPORTER_OK; + int err = LSMASH_ERR_MEMORY_ALLOC; + while( hevc_imp->status != IMPORTER_EOF ) + { +#if 0 + lsmash_log( &logger, LSMASH_LOG_INFO, "Analyzing stream as HEVC: %"PRIu32"\n", num_access_units + 1 ); +#endif + hevc_picture_info_t *picture = &info->au.picture; + hevc_picture_info_t prev_picture = *picture; + if( (err = hevc_get_access_unit_internal( importer, 1 )) < 0 + || (err = hevc_calculate_poc( info, &info->au.picture, &prev_picture )) < 0 ) + goto fail; + if( npt_alloc <= num_access_units * sizeof(nal_pic_timing_t) ) + { + uint32_t alloc = 2 * num_access_units * sizeof(nal_pic_timing_t); + nal_pic_timing_t *temp = (nal_pic_timing_t *)lsmash_realloc( npt, alloc ); + if( !temp ) + goto fail; + npt = temp; + npt_alloc = alloc; + } + hevc_imp->field_pic_present |= picture->field_coded; + npt[num_access_units].poc = picture->poc; + npt[num_access_units].delta = picture->delta; + npt[num_access_units].poc_delta = 1; + npt[num_access_units].reset = 0; + ++num_access_units; + hevc_imp->max_au_length = LSMASH_MAX( hevc_imp->max_au_length, info->au.length ); + hevc_imp->max_TemporalId = LSMASH_MAX( hevc_imp->max_TemporalId, info->au.TemporalId ); + if( picture->idr ) + ++picture_stats[HEVC_PICTURE_TYPE_IDR]; + else if( picture->irap ) + ++picture_stats[ picture->broken_link ? HEVC_PICTURE_TYPE_BLA : HEVC_PICTURE_TYPE_CRA ]; + else if( picture->type >= HEVC_PICTURE_TYPE_NONE ) + ++picture_stats[HEVC_PICTURE_TYPE_NONE]; + else + ++picture_stats[ picture->type ]; + } + lsmash_log_refresh_line( &logger ); + lsmash_log( &logger, LSMASH_LOG_INFO, + "IDR: %"PRIu32", CRA: %"PRIu32", BLA: %"PRIu32", I: %"PRIu32", P: %"PRIu32", B: %"PRIu32", Unknown: %"PRIu32"\n", + picture_stats[HEVC_PICTURE_TYPE_IDR], picture_stats[HEVC_PICTURE_TYPE_CRA], + picture_stats[HEVC_PICTURE_TYPE_BLA], picture_stats[HEVC_PICTURE_TYPE_I], + picture_stats[HEVC_PICTURE_TYPE_I_P], picture_stats[HEVC_PICTURE_TYPE_I_P_B], + picture_stats[HEVC_PICTURE_TYPE_NONE]); + /* Copy and append the last Codec Specific info. */ + if( (err = hevc_store_codec_specific( hevc_imp, &info->hvcC_param )) < 0 ) + goto fail; + /* Set up the first summary. */ + lsmash_video_summary_t *summary = hevc_setup_first_summary( importer ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + /* */ + lsmash_media_ts_t *timestamp = lsmash_malloc( num_access_units * sizeof(lsmash_media_ts_t) ); + if( !timestamp ) + goto fail; + /* Count leading samples that are undecodable. */ + for( uint32_t i = 0; i < num_access_units; i++ ) + { + if( npt[i].poc == 0 ) + break; + ++ hevc_imp->num_undecodable; + } + /* Deduplicate POCs. */ + uint32_t max_composition_delay = 0; + nalu_deduplicate_poc( npt, &max_composition_delay, num_access_units, 15 ); + /* Generate timestamps. */ + nalu_generate_timestamps_from_poc( importer, timestamp, npt, + &hevc_imp->composition_reordering_present, + &hevc_imp->last_delta, + max_composition_delay, num_access_units ); + summary->timescale *= 2; /* We assume that picture timing is in field level. + * For HEVC, it seems time_scale is set in frame level basically. + * So multiply by 2 for reducing timebase and timescale. */ + nalu_reduce_timescale( timestamp, npt, &hevc_imp->last_delta, &summary->timescale, num_access_units ); + lsmash_free( npt ); + hevc_imp->ts_list.sample_count = num_access_units; + hevc_imp->ts_list.timestamp = timestamp; + return 0; +fail: + lsmash_log_refresh_line( &logger ); + lsmash_free( npt ); + return err; +} + +static int hevc_importer_probe( importer_t *importer ) +{ + /* Find the first start code. */ + hevc_importer_t *hevc_imp = create_hevc_importer( importer ); + if( !hevc_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = hevc_imp->bs; + uint64_t first_sc_head_pos = nalu_find_first_start_code( bs ); + int err; + if( first_sc_head_pos == NALU_NO_START_CODE_FOUND ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + /* OK. It seems the stream has a long start code of HEVC. */ + importer->info = hevc_imp; + hevc_info_t *info = &hevc_imp->info; + lsmash_bs_read_seek( bs, first_sc_head_pos, SEEK_SET ); + hevc_imp->sc_head_pos = first_sc_head_pos; + if( (err = hevc_analyze_whole_stream( importer )) < 0 ) + goto fail; + /* Go back to the start code of the first NALU. */ + hevc_imp->status = IMPORTER_OK; + lsmash_bs_read_seek( bs, first_sc_head_pos, SEEK_SET ); + hevc_imp->sc_head_pos = first_sc_head_pos; + info->prev_nalu_type = HEVC_NALU_TYPE_UNKNOWN; + uint8_t *temp_au = info->au.data; + uint8_t *temp_incomplete_au = info->au.incomplete_data; + memset( &info->au, 0, sizeof(hevc_access_unit_t) ); + info->au.data = temp_au; + info->au.incomplete_data = temp_incomplete_au; + memset( &info->slice, 0, sizeof(hevc_slice_info_t) ); + memset( &info->vps, 0, sizeof(hevc_vps_t) ); + memset( &info->sps, 0, sizeof(hevc_sps_t) ); + memset( &info->pps, 0, SIZEOF_PPS_EXCLUDING_HEAP ); + for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) + lsmash_remove_entries( info->hvcC_param.parameter_arrays->ps_array[i].list, isom_remove_dcr_ps ); + lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param_next ); + return 0; +fail: + remove_hevc_importer( hevc_imp ); + importer->info = NULL; + lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); + return err; +} + +static uint32_t hevc_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + hevc_importer_t *hevc_imp = (hevc_importer_t *)importer->info; + if( !hevc_imp || track_number != 1 || hevc_imp->status != IMPORTER_EOF ) + return 0; + return hevc_imp->ts_list.sample_count + ? hevc_imp->last_delta + : UINT32_MAX; /* arbitrary */ +} + +const importer_functions hevc_importer = +{ + { "HEVC", offsetof( importer_t, log_level ) }, + 1, + hevc_importer_probe, + hevc_importer_get_accessunit, + hevc_importer_get_last_delta, + hevc_importer_cleanup +}; diff -Nru l-smash-1.9.1/cli/remuxer.c l-smash-2.3.0/cli/remuxer.c --- l-smash-1.9.1/cli/remuxer.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/remuxer.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1698 @@ +/***************************************************************************** + * remuxer.c: + ***************************************************************************** + * Copyright (C) 2011-2014 L-SMASH project + * + * Authors: Yusuke Nakamura + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *****************************************************************************/ + +/* This file is available under an ISC license. */ + +#include +#include +#include +#include +#include +#include + +#include "lsmash.h" +#include "cli.h" + +#include "config.h" + +typedef struct +{ + uint32_t track_ID; + uint32_t last_sample_delta; + uint32_t current_sample_number; + uint32_t *summary_remap; + uint64_t skip_dt_interval; + uint64_t last_sample_dts; + lsmash_track_parameters_t track_param; + lsmash_media_parameters_t media_param; +} output_track_t; + +typedef struct +{ + output_track_t *track; + lsmash_movie_parameters_t param; + uint32_t num_tracks; + uint32_t current_track_number; +} output_movie_t; + +typedef struct +{ + const char *name; + lsmash_file_t *fh; + lsmash_file_parameters_t param; + lsmash_file_parameters_t seg_param; + output_movie_t movie; + uint32_t current_subseg_number; +} output_file_t; + +typedef struct +{ + lsmash_root_t *root; + output_file_t file; + uint32_t current_seg_number; +} output_t; + +typedef struct +{ + int active; + lsmash_summary_t *summary; +} input_summary_t; + +typedef struct +{ + lsmash_file_t *fh; + lsmash_file_parameters_t param; +} input_data_ref_t; + +typedef struct +{ + lsmash_media_parameters_t param; + uint32_t num_data_refs; + input_data_ref_t *data_refs; +} input_media_t; + +typedef struct +{ + int active; + lsmash_sample_t *sample; + double dts; + uint64_t composition_delay; + uint64_t skip_duration; + int reach_end_of_media_timeline; + uint32_t track_ID; + uint32_t last_sample_delta; + uint32_t current_sample_number; + uint32_t current_sample_index; + uint32_t num_summaries; + input_summary_t *summaries; + lsmash_track_parameters_t track_param; + input_media_t media; +} input_track_t; + +typedef struct +{ + input_track_t *track; + lsmash_itunes_metadata_t *itunes_metadata; + lsmash_movie_parameters_t param; + uint32_t movie_ID; + uint32_t num_tracks; + uint32_t num_itunes_metadata; + uint32_t current_track_number; +} input_movie_t; + +typedef struct +{ + lsmash_file_t *fh; + lsmash_file_parameters_t param; + input_movie_t movie; +} input_file_t; + +typedef struct +{ + lsmash_root_t *root; + input_file_t file; +} input_t; + +typedef struct +{ + char *raw_track_option; + int remove; + int disable; + int16_t alternate_group; + uint16_t ISO_language; + uint32_t seek; + int consider_rap; + char *handler_name; +} track_media_option; + +typedef struct +{ + output_t *output; + input_t *input; + track_media_option **track_option; + int num_input; + int add_bom_to_chpl; + int ref_chap_available; + uint32_t chap_track; + char *chap_file; + uint16_t default_language; + uint32_t frag_base_track; + uint32_t subseg_per_seg; + int dash; +} remuxer_t; + +typedef struct +{ + char *whole_track_option; + int num_track_delimiter; +} file_option; + +static void cleanup_input_movie( input_t *input ) +{ + if( !input ) + return; + input_movie_t *in_movie = &input->file.movie; + if( in_movie->itunes_metadata ) + { + for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ ) + lsmash_cleanup_itunes_metadata( &in_movie->itunes_metadata[i] ); + lsmash_freep( &in_movie->itunes_metadata ); + } + if( in_movie->track ) + { + for( uint32_t i = 0; i < in_movie->num_tracks; i++ ) + { + input_track_t *in_track = &in_movie->track[i]; + if( in_track->summaries ) + { + for( uint32_t j = 0; j < in_track->num_summaries; j++ ) + lsmash_cleanup_summary( in_track->summaries[j].summary ); + lsmash_free( in_track->summaries ); + } + input_media_t *in_media = &in_track->media; + for( uint32_t j = 0; j < in_media->num_data_refs; j++ ) + if( input->file.fh != in_media->data_refs[j].fh ) + lsmash_close_file( &in_media->data_refs[j].param ); + lsmash_free( in_media->data_refs ); + } + lsmash_freep( &in_movie->track ); + } + lsmash_close_file( &input->file.param ); + lsmash_destroy_root( input->root ); + input->root = NULL; +} + +static void cleanup_output_movie( output_t *output ) +{ + if( !output ) + return; + output_movie_t *out_movie = &output->file.movie; + if( out_movie->track ) + { + for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) + lsmash_free( out_movie->track[i].summary_remap ); + lsmash_freep( &out_movie->track ); + } + if( !(output->file.seg_param.mode & LSMASH_FILE_MODE_INITIALIZATION) ) + { + lsmash_freep( &output->file.seg_param.brands ); + lsmash_close_file( &output->file.seg_param ); + } + lsmash_freep( &output->file.param.brands ); + lsmash_close_file( &output->file.param ); + lsmash_destroy_root( output->root ); + output->root = NULL; +} + +static void cleanup_remuxer( remuxer_t *remuxer ) +{ + for( int i = 0; i < remuxer->num_input; i++ ) + { + cleanup_input_movie( &remuxer->input[i] ); + if( remuxer->track_option[i] ) + lsmash_free( remuxer->track_option[i] ); + } + cleanup_output_movie( remuxer->output ); +} + +#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) +#define REFRESH_CONSOLE eprintf( " \r" ) + +static int remuxer_error( remuxer_t *remuxer, const char *message, ... ) +{ + cleanup_remuxer( remuxer ); + REFRESH_CONSOLE; + eprintf( "[Error] " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +static int error_message( const char *message, ... ) +{ + REFRESH_CONSOLE; + eprintf( "[Error] " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +static int warning_message( const char *message, ... ) +{ + REFRESH_CONSOLE; + eprintf( "[Warning] " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +#define REMUXER_ERR( ... ) remuxer_error( &remuxer, __VA_ARGS__ ) +#define ERROR_MSG( ... ) error_message( __VA_ARGS__ ) +#define WARNING_MSG( ... ) warning_message( __VA_ARGS__ ) + +static void display_version( void ) +{ + eprintf( "\n" + "L-SMASH isom/mov re-muliplexer rev%s %s\n" + "Built on %s %s\n" + "Copyright (C) 2011-2014 L-SMASH project\n", + LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); +} + +static void display_help( void ) +{ + display_version(); + eprintf( "\n" + "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\n" + "Global options:\n" + " --help Display help.\n" + " --version Display version information.\n" + " --chapter Set chapters from the file.\n" + " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n" + " in the chapter list. (experimental)\n" + " --chapter-track Set which track the chapter applies to.\n" + " This option takes effect only when reference\n" + " chapter is available.\n" + " If this option is not used, it defaults to 1.\n" + " --language Specify the default language for all the output tracks.\n" + " This option is overridden by the track options.\n" + " --fragment Enable fragmentation per random accessible point.\n" + " Set which track the fragmentation is based on.\n" + " --dash Enable DASH ISOBMFF-based Media segmentation.\n" + " The value is the number of subsegments per segment.\n" + " If zero, Indexed self-initializing Media Segment.\n" + " This option requires --fragment.\n" + "Track options:\n" + " remove Remove this track\n" + " disable Disable this track\n" + " language= Specify media language\n" + " alternate-group= Specify alternate group\n" + " handler= Set media handler name\n" + " seek= Specify starting point in media\n" + " safe-seek= Same as seek except for considering random accessible point\n" + " Media starts from the closest random accessible point\n" + "How to use track options:\n" + " -i input?[track_number1]:[track_option1],[track_option2]?[track_number2]:...\n" + "For example:\n" + " remuxer -i input1 -i input2?2:alternate-group=1?3:language=jpn,alternate-group=1 -o output\n" ); +} + +static char *duplicate_string( char *src ) +{ + if( !src ) + return NULL; + int dst_size = strlen( src ) + 1; + char *dst = lsmash_malloc( dst_size ); + if( !dst ) + return NULL; + memcpy( dst, src, dst_size ); + return dst; +} + +static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata ) +{ + memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) ); + if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) ) + return -1; + lsmash_itunes_metadata_t shadow = *metadata; + metadata->meaning = NULL; + metadata->name = NULL; + memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) ); + if( shadow.meaning ) + { + metadata->meaning = duplicate_string( shadow.meaning ); + if( !metadata->meaning ) + return -1; + } + if( shadow.name ) + { + metadata->name = duplicate_string( shadow.name ); + if( !metadata->name ) + goto fail; + } + if( shadow.type == ITUNES_METADATA_TYPE_STRING ) + { + metadata->value.string = duplicate_string( shadow.value.string ); + if( !metadata->value.string ) + goto fail; + } + else if( shadow.type == ITUNES_METADATA_TYPE_BINARY ) + { + metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size ); + if( !metadata->value.binary.data ) + goto fail; + memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size ); + metadata->value.binary.size = shadow.value.binary.size; + metadata->value.binary.subtype = shadow.value.binary.subtype; + } + else + metadata->value = shadow.value; + return 0; +fail: + lsmash_freep( &metadata->meaning ); + lsmash_freep( &metadata->name ); + return -1; +} + +static inline int is_relative_path( const char *path ) +{ + return path[0] == '/' || path[0] == '\\' || (path[0] != '\0' && path[1] == ':') ? 0 : 1; +} + +static int input_data_reference +( + input_t *input, + uint32_t track_ID, + input_data_ref_t *in_data_ref, + lsmash_data_reference_t *data_ref +) +{ + if( lsmash_open_file( data_ref->location, 1, &in_data_ref->param ) < 0 ) + { + WARNING_MSG( "failed to open an external media file.\n" ); + return -1; + } + in_data_ref->param.mode |= LSMASH_FILE_MODE_MEDIA; + in_data_ref->fh = lsmash_set_file( input->root, &in_data_ref->param ); + if( !in_data_ref->fh ) + { + WARNING_MSG( "failed to set an external media file as a data reference.\n" ); + return -1; + } + if( lsmash_assign_data_reference( input->root, track_ID, data_ref->index, in_data_ref->fh ) < 0 ) + { + WARNING_MSG( "failed to assign an external media a data reference.\n" ); + return -1; + } + return 0; +} + +static int get_movie( input_t *input, char *input_name ) +{ + if( !strcmp( input_name, "-" ) ) + return ERROR_MSG( "standard input not supported.\n" ); + /* Read an input file. */ + input->root = lsmash_create_root(); + if( !input->root ) + return ERROR_MSG( "failed to create a ROOT for an input file.\n" ); + input_file_t *in_file = &input->file; + if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 ) + return ERROR_MSG( "failed to open an input file.\n" ); + in_file->fh = lsmash_set_file( input->root, &in_file->param ); + if( !in_file->fh ) + return ERROR_MSG( "failed to add an input file into a ROOT.\n" ); + if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 ) + return ERROR_MSG( "failed to read an input file\n" ); + /* Get iTunes metadata. */ + input_movie_t *in_movie = &in_file->movie; + in_movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root ); + if( in_movie->num_itunes_metadata ) + { + in_movie->itunes_metadata = lsmash_malloc( in_movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) ); + if( !in_movie->itunes_metadata ) + return ERROR_MSG( "failed to alloc iTunes metadata.\n" ); + uint32_t itunes_metadata_count = 0; + for( uint32_t i = 1; i <= in_movie->num_itunes_metadata; i++ ) + { + if( get_itunes_metadata( input->root, i, &in_movie->itunes_metadata[itunes_metadata_count] ) ) + { + WARNING_MSG( "failed to get an iTunes metadata.\n" ); + continue; + } + ++itunes_metadata_count; + } + in_movie->num_itunes_metadata = itunes_metadata_count; + } + in_movie->current_track_number = 1; + lsmash_initialize_movie_parameters( &in_movie->param ); + if( lsmash_get_movie_parameters( input->root, &in_movie->param ) ) + return ERROR_MSG( "failed to get movie parameters.\n" ); + uint32_t num_tracks = in_movie->num_tracks = in_movie->param.number_of_tracks; + /* Create tracks. */ + input_track_t *in_track = in_movie->track = lsmash_malloc_zero( num_tracks * sizeof(input_track_t) ); + if( !in_track ) + return ERROR_MSG( "failed to alloc input tracks.\n" ); + for( uint32_t i = 0; i < num_tracks; i++ ) + { + in_track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 ); + if( !in_track[i].track_ID ) + return ERROR_MSG( "failed to get track_ID.\n" ); + } + for( uint32_t i = 0; i < num_tracks; i++ ) + { + lsmash_initialize_track_parameters( &in_track[i].track_param ); + if( lsmash_get_track_parameters( input->root, in_track[i].track_ID, &in_track[i].track_param ) ) + { + WARNING_MSG( "failed to get track parameters.\n" ); + continue; + } + lsmash_initialize_media_parameters( &in_track[i].media.param ); + if( lsmash_get_media_parameters( input->root, in_track[i].track_ID, &in_track[i].media.param ) ) + { + WARNING_MSG( "failed to get media parameters.\n" ); + continue; + } + uint32_t data_ref_count = lsmash_count_data_reference( input->root, in_track[i].track_ID ); + if( data_ref_count == 0 ) + { + WARNING_MSG( "failed to get the number of data references.\n" ); + continue; + } + in_track[i].media.data_refs = lsmash_malloc_zero( data_ref_count * sizeof(input_data_ref_t) ); + if( !in_track[i].media.data_refs ) + { + WARNING_MSG( "failed to allocate handles of data reference.\n" ); + continue; + } + for( uint32_t j = 0; j < data_ref_count; j++ ) + { + input_data_ref_t *in_data_ref = &in_track[i].media.data_refs[j]; + lsmash_data_reference_t data_ref = { .index = j + 1 }; + if( lsmash_get_data_reference( input->root, in_track[i].track_ID, &data_ref ) < 0 ) + { + WARNING_MSG( "failed to get a data references.\n" ); + continue; + } + if( data_ref.location ) + { + if( is_relative_path( data_ref.location ) && !is_relative_path( input_name ) ) + { + /* Append the directory path from the referencing file. */ + int location_length = strlen( data_ref.location ); + int input_name_length = strlen( input_name ); + char *p = input_name + input_name_length; + while( p != input_name && *p != '/' && *p != '\\' ) + --p; + int relative_path_length = p == input_name ? 2 : p - input_name; + char *location = lsmash_malloc( relative_path_length + location_length + 2 ); + if( location ) + { + memcpy( location, input_name, relative_path_length ); + memcpy( location + relative_path_length + 1, data_ref.location, location_length ); + location[relative_path_length] = '/'; + location[relative_path_length + location_length + 1] = '\0'; + lsmash_cleanup_data_reference( &data_ref ); + data_ref.location = location; + } + } + int ret = input_data_reference( input, in_track[i].track_ID, in_data_ref, &data_ref ); + lsmash_cleanup_data_reference( &data_ref ); + if( ret < 0 ) + continue; + } + else + { + in_data_ref->fh = in_file->fh; + in_data_ref->param = in_file->param; + } + } + if( lsmash_construct_timeline( input->root, in_track[i].track_ID ) ) + { + WARNING_MSG( "failed to construct timeline.\n" ); + continue; + } + if( lsmash_get_last_sample_delta_from_media_timeline( input->root, in_track[i].track_ID, &in_track[i].last_sample_delta ) ) + { + WARNING_MSG( "failed to get the last sample delta.\n" ); + continue; + } + in_track[i].num_summaries = lsmash_count_summary( input->root, in_track[i].track_ID ); + if( in_track[i].num_summaries == 0 ) + { + WARNING_MSG( "failed to find valid summaries.\n" ); + continue; + } + in_track[i].summaries = lsmash_malloc_zero( in_track[i].num_summaries * sizeof(input_summary_t) ); + if( !in_track[i].summaries ) + return ERROR_MSG( "failed to alloc input summaries.\n" ); + for( uint32_t j = 0; j < in_track[i].num_summaries; j++ ) + { + lsmash_summary_t *summary = lsmash_get_summary( input->root, in_track[i].track_ID, j + 1 ); + if( !summary ) + { + WARNING_MSG( "failed to get a summary.\n" ); + continue; + } + if( !LSMASH_FLAGS_SATISFIED( lsmash_check_codec_support( summary->sample_type ), LSMASH_CODEC_SUPPORT_FLAG_REMUX ) ) + { + lsmash_cleanup_summary( summary ); + WARNING_MSG( "no support to remux this stream.\n" ); + continue; + } + in_track[i].summaries[j].summary = summary; + in_track[i].summaries[j].active = 1; + } + in_track[i].active = 1; + in_track[i].current_sample_number = 1; + in_track[i].sample = NULL; + in_track[i].dts = 0; + in_track[i].composition_delay = 0; + in_track[i].skip_duration = 0; + } + lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) ); + return 0; +} + +static int parse_track_option( remuxer_t *remuxer ) +{ + track_media_option **track = remuxer->track_option; + for( int i = 0; i < remuxer->num_input; i++ ) + for( uint32_t j = 0; j < remuxer->input[i].file.movie.num_tracks; j++ ) + { + track_media_option *current_track_opt = &track[i][j]; + if( current_track_opt->raw_track_option == NULL ) + break; + if( !strchr( current_track_opt->raw_track_option, ':' ) + || strchr( current_track_opt->raw_track_option, ':' ) == current_track_opt->raw_track_option ) + return ERROR_MSG( "track number is not specified in %s\n", current_track_opt->raw_track_option ); + if( strchr( current_track_opt->raw_track_option, ':' ) != strrchr( current_track_opt->raw_track_option, ':' ) ) + return ERROR_MSG( "multiple colons inside one track option in %s.\n", current_track_opt->raw_track_option ); + uint32_t track_number = atoi( strtok( current_track_opt->raw_track_option, ":" ) ); + if( track_number == 0 ) + return ERROR_MSG( "%s is an invalid track number.\n", strtok( current_track_opt->raw_track_option, ":" ) ); + if( track_number > remuxer->input[i].file.movie.num_tracks ) + return ERROR_MSG( "%d is an invalid track number.\n", track_number ); + char *track_option; + while( (track_option = strtok( NULL, "," )) != NULL ) + { + if( strchr( track_option, '=' ) != strrchr( track_option, '=' ) ) + return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option ); + current_track_opt = &track[i][track_number - 1]; + if( strstr( track_option, "remove" ) ) + { + current_track_opt->remove = 1; + /* No need to parse track options for this track anymore. */ + break; + } + else if( strstr( track_option, "disable" ) ) + current_track_opt->disable = 1; + else if( strstr( track_option, "alternate-group=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + current_track_opt->alternate_group = atoi( track_parameter ); + } + else if( strstr( track_option, "language=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + current_track_opt->ISO_language = lsmash_pack_iso_language( track_parameter ); + } + else if( strstr( track_option, "handler=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + current_track_opt->handler_name = track_parameter; + } + else if( strstr( track_option, "safe-seek=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + current_track_opt->seek = atoi( track_parameter ); + current_track_opt->consider_rap = 1; + } + else if( strstr( track_option, "seek=" ) ) + { + char *track_parameter = strchr( track_option, '=' ) + 1; + current_track_opt->seek = atoi( track_parameter ); + } + else + return ERROR_MSG( "unknown track option %s\n", track_option ); + } + } + return 0; +} + +static int parse_cli_option( int argc, char **argv, remuxer_t *remuxer ) +{ + input_t *input = remuxer->input; + track_media_option **track_option = remuxer->track_option; + file_option input_file_option[ remuxer->num_input ]; + int input_movie_number = 0; + for( int i = 1; i < argc ; i++ ) + { + /* Get input movies. */ + if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) /* input file */ + { + if( ++i == argc ) + return ERROR_MSG( "-i requires an argument.\n" ); + input_file_option[input_movie_number].num_track_delimiter = 0; + char *p = argv[i]; + while( *p ) + input_file_option[input_movie_number].num_track_delimiter += (*p++ == '?'); + if( get_movie( &input[input_movie_number], strtok( argv[i], "?" ) ) ) + return ERROR_MSG( "failed to get input movie.\n" ); + uint32_t num_tracks = input[input_movie_number].file.movie.num_tracks; + track_option[input_movie_number] = lsmash_malloc_zero( num_tracks * sizeof(track_media_option) ); + if( !track_option[input_movie_number] ) + return ERROR_MSG( "couldn't allocate memory.\n" ); + input_file_option[input_movie_number].whole_track_option = strtok( NULL, "" ); + input[input_movie_number].file.movie.movie_ID = input_movie_number + 1; + ++input_movie_number; + } + /* Create output movie. */ + else if( !strcasecmp( argv[i], "-o" ) || !strcasecmp( argv[i], "--output" ) ) /* output file */ + { + if( ++i == argc ) + return ERROR_MSG( "-o requires an argument.\n" ); + output_t *output = remuxer->output; + output->root = lsmash_create_root(); + if( !output->root ) + return ERROR_MSG( "failed to create a ROOT.\n" ); + if( lsmash_open_file( argv[i], 0, &output->file.param ) < 0 ) + return ERROR_MSG( "failed to open an output file.\n" ); + output->file.name = argv[i]; + } + else if( !strcasecmp( argv[i], "--chapter" ) ) /* chapter file */ + { + if( ++i == argc ) + return ERROR_MSG( "--chapter requires an argument.\n" ); + remuxer->chap_file = argv[i]; + } + else if( !strcasecmp( argv[i], "--chpl-with-bom" ) ) + remuxer->add_bom_to_chpl = 1; + else if( !strcasecmp( argv[i], "--chapter-track" ) ) /* track to apply reference chapter to */ + { + if( ++i == argc ) + return ERROR_MSG( "--chapter-track requires an argument.\n" ); + remuxer->chap_track = atoi( argv[i] ); + if( !remuxer->chap_track ) + return ERROR_MSG( "%s is an invalid track number.\n", argv[i] ); + } + else if( !strcasecmp( argv[i], "--language" ) ) + { + if( ++i == argc ) + return ERROR_MSG( "--chapter requires an argument.\n" ); + remuxer->default_language = lsmash_pack_iso_language( argv[i] ); + } + else if( !strcasecmp( argv[i], "--fragment" ) ) + { + if( ++i == argc ) + return ERROR_MSG( "--fragment requires an argument.\n" ); + remuxer->frag_base_track = atoi( argv[i] ); + if( remuxer->frag_base_track == 0 ) + return ERROR_MSG( "%s is an invalid track number.\n", argv[i] ); + } + else if( !strcasecmp( argv[i], "--dash" ) ) + { + if( ++i == argc ) + return ERROR_MSG( "--dash requires an argument.\n" ); + remuxer->subseg_per_seg = atoi( argv[i] ); + remuxer->dash = 1; + } + else + return ERROR_MSG( "unkown option found: %s\n", argv[i] ); + } + if( !remuxer->output->root ) + return ERROR_MSG( "output file name is not specified.\n" ); + /* Parse track options */ + /* Get the current track and media parameters */ + for( int i = 0; i < remuxer->num_input; i++ ) + for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ ) + { + input_track_t *in_track = &input[i].file.movie.track[j]; + if( !in_track->active ) + continue; + track_option[i][j].alternate_group = in_track->track_param.alternate_group; + track_option[i][j].ISO_language = in_track->media.param.ISO_language; + track_option[i][j].handler_name = in_track->media.param.media_handler_name; + } + /* Set the default language */ + if( remuxer->default_language ) + for( int i = 0; i < remuxer->num_input; i++ ) + for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ ) + track_option[i][j].ISO_language = remuxer->default_language; + /* Get the track and media parameters specified by users */ + for( int i = 0; i < remuxer->num_input; i++ ) + { + if( input_file_option[i].num_track_delimiter > input[i].file.movie.num_tracks ) + return ERROR_MSG( "more track options specified than the actual number of the tracks (%"PRIu32").\n", + input[i].file.movie.num_tracks ); + if( input_file_option[i].num_track_delimiter ) + { + track_option[i][0].raw_track_option = strtok( input_file_option[i].whole_track_option, "?" ); + for( int j = 1; j < input_file_option[i].num_track_delimiter ; j++ ) + track_option[i][j].raw_track_option = strtok( NULL, "?" ); + } + } + if( parse_track_option( remuxer ) ) + return ERROR_MSG( "failed to parse track options.\n" ); + return 0; +} + +static void replace_with_valid_brand( remuxer_t *remuxer ) +{ + static const lsmash_brand_type brand_filter_list[] = + { + ISOM_BRAND_TYPE_3G2A, + ISOM_BRAND_TYPE_3GG6, + ISOM_BRAND_TYPE_3GG9, + ISOM_BRAND_TYPE_3GP4, + ISOM_BRAND_TYPE_3GP5, + ISOM_BRAND_TYPE_3GP6, + ISOM_BRAND_TYPE_3GP7, + ISOM_BRAND_TYPE_3GP8, + ISOM_BRAND_TYPE_3GP9, + ISOM_BRAND_TYPE_3GR6, + ISOM_BRAND_TYPE_3GR9, + ISOM_BRAND_TYPE_M4A , + ISOM_BRAND_TYPE_M4B , + ISOM_BRAND_TYPE_M4V , + ISOM_BRAND_TYPE_AVC1, + ISOM_BRAND_TYPE_DBY1, + ISOM_BRAND_TYPE_ISO2, + ISOM_BRAND_TYPE_ISO3, + ISOM_BRAND_TYPE_ISO4, + ISOM_BRAND_TYPE_ISO5, + ISOM_BRAND_TYPE_ISO6, + ISOM_BRAND_TYPE_ISO7, + ISOM_BRAND_TYPE_ISOM, + ISOM_BRAND_TYPE_MP41, + ISOM_BRAND_TYPE_MP42, + ISOM_BRAND_TYPE_QT , + 0 + }; + input_t *input = remuxer->input; + /* Check the number of video and audio tracks, and the number of video + * and audio sample descriptions for the restrictions of 3GPP Basic Profile. + * - the maximum number of tracks shall be one for video (or alternatively + * one for scene description), one for audio and one for text + * - the maximum number of sample entries shall be one per track for video + * and audio (but unrestricted for text and scene description) */ + uint32_t video_track_count = 0; + uint32_t audio_track_count = 0; + uint32_t video_num_summaries = 0; + uint32_t audio_num_summaries = 0; + for( int i = 0; i < remuxer->num_input; i++ ) + { + input_movie_t *movie = &input[i].file.movie; + for( int j = 0; j < movie->num_tracks; j++ ) + { + if( movie->track[j].media.param.handler_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ) + { + if( ++video_track_count == 1 ) + video_num_summaries = movie->track[j].num_summaries; + } + else if( movie->track[j].media.param.handler_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ) + { + if( ++audio_track_count == 1 ) + audio_num_summaries = movie->track[j].num_summaries; + } + } + } + for( int i = 0; i < remuxer->num_input; i++ ) + for( uint32_t j = 0; j <= input[i].file.param.brand_count; j++ ) + { + int invalid = 1; + uint32_t *brand = j ? &input[i].file.param.brands[j - 1] : &input[i].file.param.major_brand; + uint32_t *version = j ? NULL : &input[i].file.param.minor_version; + for( int k = 0; brand_filter_list[k]; k++ ) + { + if( *brand == brand_filter_list[k] ) + { + if( ((*brand >> 24) & 0xFF) == '3' + && ((*brand >> 16) & 0xFF) == 'g' + && (((*brand >> 8) & 0xFF) == 'p' || ((*brand >> 8) & 0xFF) == 'r') ) + { + if( remuxer->frag_base_track == 0 /* Movie fragments are not allowed in '3gp4' and '3gp5'. */ + && video_track_count <= 1 && audio_track_count <= 1 + && video_num_summaries <= 1 && audio_num_summaries <= 1 ) + continue; + /* Replace with the General Profile for maximum compatibility. */ + if( (*brand & 0xFF) < '6' ) + { + /* 3GPP version 6.7.0 General Profile */ + *brand = ISOM_BRAND_TYPE_3GG6; + if( version ) + *version = 0x00000700; + } + else + *brand = LSMASH_4CC( '3', 'g', 'g', *brand & 0xFF ); + } + if( remuxer->dash + && (*brand == ISOM_BRAND_TYPE_AVC1 + || (((*brand >> 24) & 0xFF) == 'i' + && ((*brand >> 16) & 0xFF) == 's' + && ((*brand >> 8) & 0xFF) == 'o' + && ((*brand & 0xFF) == 'm' || (*brand & 0xFF) < '6'))) ) + *brand = ISOM_BRAND_TYPE_ISO6; + invalid = 0; + break; + } + } + if( invalid ) + { + /* Replace with the 'mp42' brand. */ + *brand = ISOM_BRAND_TYPE_MP42; + if( version ) + *version = 0; + } + } +} + +static int set_movie_parameters( remuxer_t *remuxer ) +{ + int num_input = remuxer->num_input; + input_t *input = remuxer->input; + output_t *output = remuxer->output; + output_file_t *out_file = &output->file; + if( remuxer->frag_base_track ) + out_file->param.mode |= LSMASH_FILE_MODE_FRAGMENTED; + int self_containd_segment = (remuxer->dash && remuxer->subseg_per_seg == 0); + if( remuxer->dash ) + { + if( remuxer->frag_base_track ) + { + if( self_containd_segment ) + out_file->param.mode |= LSMASH_FILE_MODE_INDEX; + else + { + out_file->param.mode &= ~LSMASH_FILE_MODE_MEDIA; + out_file->param.mode |= LSMASH_FILE_MODE_SEGMENT; + } + } + else + WARNING_MSG( "--dash requires --fragment.\n" ); + } + replace_with_valid_brand( remuxer ); + if( self_containd_segment ) + { + out_file->param.major_brand = ISOM_BRAND_TYPE_DASH; + out_file->param.minor_version = 0; + } + else + { + /* Pick the most used major_brands. */ + lsmash_brand_type major_brand [num_input]; + uint32_t minor_version [num_input]; + uint32_t major_brand_count[num_input]; + uint32_t num_major_brand = 0; + for( int i = 0; i < num_input; i++ ) + { + major_brand [num_major_brand] = input[i].file.param.major_brand; + minor_version [num_major_brand] = input[i].file.param.minor_version; + major_brand_count[num_major_brand] = 0; + for( int j = 0; j < num_input; j++ ) + if( (major_brand [num_major_brand] == input[j].file.param.major_brand) + && (minor_version[num_major_brand] == input[j].file.param.minor_version) ) + { + if( i <= j ) + ++major_brand_count[num_major_brand]; + else + { + /* This major_brand already exists. Skip this. */ + major_brand_count[num_major_brand] = 0; + --num_major_brand; + break; + } + } + ++num_major_brand; + } + uint32_t most_used_count = 0; + for( uint32_t i = 0; i < num_major_brand; i++ ) + if( major_brand_count[i] > most_used_count ) + { + most_used_count = major_brand_count[i]; + out_file->param.major_brand = major_brand [i]; + out_file->param.minor_version = minor_version[i]; + } + } + /* Deduplicate compatible brands. */ + uint32_t num_input_brands = num_input + (self_containd_segment ? 1 : 0); + for( int i = 0; i < num_input; i++ ) + num_input_brands += input[i].file.param.brand_count; + lsmash_brand_type input_brands[num_input_brands]; + num_input_brands = 0; + if( self_containd_segment ) + input_brands[num_input_brands++] = ISOM_BRAND_TYPE_DASH; + for( int i = 0; i < num_input; i++ ) + { + input_brands[num_input_brands++] = input[i].file.param.major_brand; + for( uint32_t j = 0; j < input[i].file.param.brand_count; j++ ) + if( input[i].file.param.brands[j] ) + input_brands[num_input_brands++] = input[i].file.param.brands[j]; + } + lsmash_brand_type *output_brands = lsmash_malloc_zero( num_input_brands * sizeof(lsmash_brand_type) ); + if( !output_brands ) + return ERROR_MSG( "failed to allocate brands for an output file.\n" ); + uint32_t num_output_brands = 0; + for( uint32_t i = 0; i < num_input_brands; i++ ) + { + output_brands[num_output_brands] = input_brands[i]; + for( uint32_t j = 0; j < num_output_brands; j++ ) + if( output_brands[num_output_brands] == output_brands[j] ) + { + /* This brand already exists. Skip this. */ + --num_output_brands; + break; + } + ++num_output_brands; + } + out_file->param.brand_count = num_output_brands; + out_file->param.brands = output_brands; + /* Set up a file. */ + out_file->fh = lsmash_set_file( output->root, &out_file->param ); + if( !out_file->fh ) + return ERROR_MSG( "failed to add an output file into a ROOT.\n" ); + out_file->seg_param = out_file->param; + /* Check whether a reference chapter track is allowed or not. */ + if( remuxer->chap_file ) + for( uint32_t i = 0; i < out_file->param.brand_count; i++ ) + { + uint32_t brand = out_file->param.brands[i]; + /* According to the restrictions of 3GPP Basic Profile, + * - there shall be no references between tracks, e.g., a scene description track + * shall not refer to a media track since all tracks are on equal footing and + * played in parallel by a conforming player. + * Therefore, the referenced chapter track is forbidden to use for 3GPP Basic Profile. */ + if( ((brand >> 24) & 0xFF) == '3' + && ((brand >> 16) & 0xFF) == 'g' + && ((brand >> 8) & 0xFF) == 'p' ) + break; + /* QuickTime file and iTunes MP4 file can contain the referenced chapter track. */ + if( brand == ISOM_BRAND_TYPE_QT || brand == ISOM_BRAND_TYPE_M4A + || brand == ISOM_BRAND_TYPE_M4B || brand == ISOM_BRAND_TYPE_M4P + || brand == ISOM_BRAND_TYPE_M4V ) + { + remuxer->ref_chap_available = 1; + break; + } + } + /* Set the movie timescale in order to match the media timescale if only one track is there. */ + lsmash_initialize_movie_parameters( &out_file->movie.param ); + if( out_file->movie.num_tracks == 1 ) + for( int i = 0; i < remuxer->num_input; i++ ) + { + input_movie_t *in_movie = &input[i].file.movie; + for( uint32_t j = 0; j < in_movie->num_tracks; j++ ) + if( in_movie->track[j].active ) + { + out_file->movie.param.timescale = in_movie->track[j].media.param.timescale; + break; + } + } + return lsmash_set_movie_parameters( output->root, &out_file->movie.param ); +} + +static void set_itunes_metadata( output_t *output, input_t *input, int num_input ) +{ + for( int i = 0; i < num_input; i++ ) + for( uint32_t j = 0; j < input[i].file.movie.num_itunes_metadata; j++ ) + if( lsmash_set_itunes_metadata( output->root, input[i].file.movie.itunes_metadata[j] ) ) + { + WARNING_MSG( "failed to set an iTunes metadata.\n" ); + continue; + } +} + +static int set_starting_point( input_t *input, input_track_t *in_track, uint32_t seek_point, int consider_rap ) +{ + if( seek_point == 0 ) + return 0; + uint32_t rap_number; + if( lsmash_get_closest_random_accessible_point_from_media_timeline( input->root, in_track->track_ID, 1, &rap_number ) ) + { + if( consider_rap ) + return ERROR_MSG( "failed to get the first random accessible point.\n" ); + else + { + WARNING_MSG( "no random access point!\n" ); + /* Set number of the first sample to be muxed. */ + in_track->current_sample_number = seek_point; + return 0; + } + } + /* Get composition delay. */ + uint64_t rap_dts; + uint64_t rap_cts; + uint32_t ctd_shift; + if( lsmash_get_dts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_dts ) ) + return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" ); + if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_cts ) ) + return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" ); + if( lsmash_get_composition_to_decode_shift_from_media_timeline( input->root, in_track->track_ID, &ctd_shift ) ) + return ERROR_MSG( "failed to get composition to decode timeline shfit.\n" ); + in_track->composition_delay = rap_cts - rap_dts + ctd_shift; + /* Check if starting point is random accessible. */ + if( lsmash_get_closest_random_accessible_point_from_media_timeline( input->root, in_track->track_ID, seek_point, &rap_number ) ) + return ERROR_MSG( "failed to get a random accessible point.\n" ); + if( rap_number != seek_point ) + { + WARNING_MSG( "starting point you specified is not a random accessible point.\n" ); + if( consider_rap ) + { + /* Get duration that should be skipped. */ + if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_cts ) ) + return ERROR_MSG( "failed to get CTS of the closest and past random accessible sample of starting point.\n" ); + uint64_t seek_cts; + if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, seek_point, &seek_cts ) ) + return ERROR_MSG( "failed to get CTS of starting point.\n" ); + if( rap_cts < seek_cts ) + in_track->skip_duration = seek_cts - rap_cts; + } + } + /* Set number of the first sample to be muxed. */ + in_track->current_sample_number = consider_rap ? rap_number : seek_point; + return 0; +} + +static void exclude_invalid_output_track( output_t *output, output_track_t *out_track, + input_movie_t *in_movie, input_track_t *in_track, + const char *message, ... ) +{ + REFRESH_CONSOLE; + eprintf( "[Warning] in %"PRIu32"/%"PRIu32" -> out %"PRIu32": ", in_movie->movie_ID, in_track->track_ID, out_track->track_ID ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + lsmash_delete_track( output->root, out_track->track_ID ); + -- output->file.movie.num_tracks; + in_track->active = 0; +} + +static int prepare_output( remuxer_t *remuxer ) +{ + input_t *input = remuxer->input; + output_t *output = remuxer->output; + output_movie_t *out_movie = &output->file.movie; + /* Count the number of output tracks. */ + for( int i = 0; i < remuxer->num_input; i++ ) + out_movie->num_tracks += input[i].file.movie.num_tracks; + for( int i = 0; i < remuxer->num_input; i++ ) + { + input_movie_t *in_movie = &input[i].file.movie; + for( uint32_t j = 0; j < in_movie->num_tracks; j++ ) + { + /* Don't remux tracks specified as 'remove' by a user. */ + if( remuxer->track_option[i][j].remove ) + in_movie->track[j].active = 0; + if( !in_movie->track[j].active ) + -- out_movie->num_tracks; + } + } + if( set_movie_parameters( remuxer ) < 0 ) + return ERROR_MSG( "failed to set output movie parameters.\n" ); + set_itunes_metadata( output, input, remuxer->num_input ); + /* Allocate output tracks. */ + out_movie->track = lsmash_malloc( out_movie->num_tracks * sizeof(output_track_t) ); + if( !out_movie->track ) + return ERROR_MSG( "failed to alloc output tracks.\n" ); + out_movie->current_track_number = 1; + for( int i = 0; i < remuxer->num_input; i++ ) + { + input_movie_t *in_movie = &input[i].file.movie; + for( uint32_t j = 0; j < in_movie->num_tracks; j++ ) + { + track_media_option *current_track_opt = &remuxer->track_option[i][j]; + input_track_t *in_track = &in_movie->track[j]; + if( !in_track->active ) + continue; + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + out_track->summary_remap = lsmash_malloc_zero( in_track->num_summaries * sizeof(uint32_t) ); + if( !out_track->summary_remap ) + return ERROR_MSG( "failed to create summary mapping for a track.\n" ); + out_track->track_ID = lsmash_create_track( output->root, in_track->media.param.handler_type ); + if( !out_track->track_ID ) + return ERROR_MSG( "failed to create a track.\n" ); + /* Copy track and media parameters except for track_ID. */ + 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; + 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 ) + { + exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set track parameters.\n" ); + continue; + } + if( lsmash_set_media_parameters( output->root, out_track->track_ID, &out_track->media_param ) < 0 ) + { + exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set media parameters.\n" ); + continue; + } + lsmash_data_reference_t data_ref = { .index = 1, .location = NULL }; + if( lsmash_create_data_reference( output->root, out_track->track_ID, &data_ref, output->file.fh ) < 0 ) + return ERROR_MSG( "failed to create a data reference for output movie.\n" ); + uint32_t valid_summary_count = 0; + for( uint32_t k = 0; k < in_track->num_summaries; k++ ) + { + if( !in_track->summaries[k].active ) + { + out_track->summary_remap[k] = 0; + continue; + } + lsmash_summary_t *summary = in_track->summaries[k].summary; + summary->data_ref_index = 1; + if( lsmash_add_sample_entry( output->root, out_track->track_ID, summary ) == 0 ) + { + WARNING_MSG( "failed to append a summary.\n" ); + lsmash_cleanup_summary( summary ); + in_track->summaries[k].summary = NULL; + in_track->summaries[k].active = 0; + out_track->summary_remap[k] = 0; + continue; + } + out_track->summary_remap[k] = ++valid_summary_count; + } + if( valid_summary_count == 0 ) + { + exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to append all summaries.\n" ); + continue; + } + out_track->last_sample_delta = in_track->last_sample_delta; + if( set_starting_point( input, in_track, current_track_opt->seek, current_track_opt->consider_rap ) < 0 ) + { + exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set starting point.\n" ); + continue; + } + out_track->current_sample_number = 1; + out_track->skip_dt_interval = 0; + out_track->last_sample_dts = 0; + ++ out_movie->current_track_number; + } + } + if( out_movie->num_tracks == 0 ) + return ERROR_MSG( "failed to create the output movie.\n" ); + out_movie->current_track_number = 1; + output->current_seg_number = 1; + return 0; +} + +static void set_reference_chapter_track( remuxer_t *remuxer ) +{ + if( remuxer->ref_chap_available ) + lsmash_create_reference_chapter_track( remuxer->output->root, remuxer->chap_track, remuxer->chap_file ); +} + +static int flush_movie_fragment( remuxer_t *remuxer ) +{ + input_t *inputs = remuxer->input; + output_t *output = remuxer->output; + output_movie_t *out_movie = &output->file.movie; + uint32_t out_current_track_number = 1; + for( uint32_t i = 1; i <= remuxer->num_input; i++ ) + { + input_t *in = &inputs[i - 1]; + input_movie_t *in_movie = &in->file.movie; + for( uint32_t j = 1; j <= in_movie->num_tracks; j++ ) + { + input_track_t *in_track = &in_movie->track[j - 1]; + if( !in_track->active ) + continue; + output_track_t *out_track = &out_movie->track[out_current_track_number - 1]; + if( !in_track->reach_end_of_media_timeline ) + { + lsmash_sample_t sample; + if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number, &sample ) < 0 ) + return ERROR_MSG( "failed to get the information of the next sample.\n" ); + uint64_t sample_dts = sample.dts - out_track->skip_dt_interval; + if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, sample_dts - out_track->last_sample_dts ) < 0 ) + return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" ); + } + else + if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, out_track->last_sample_delta ) < 0 ) + return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" ); + if( ++out_current_track_number > out_movie->num_tracks ) + break; + } + } + return 0; +} + +static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size ) +{ + REFRESH_CONSOLE; + eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 ); + return 0; +} + +static lsmash_adhoc_remux_t moov_to_front = +{ + .func = moov_to_front_callback, + .buffer_size = 4 * 1024 * 1024, /* 4MiB */ + .param = NULL +}; + +static int open_media_segment( output_t *output, lsmash_file_parameters_t *seg_param ) +{ + /* Open a media segment file. + * Each file is named as follows. + * a.mp4 + * a_1.mp4 + * a_2.mp4 + * ... + * a_N.mp4 + * N is the number of segment files excluding the initialization segment file. + */ + output_file_t *out_file = &output->file; + int out_file_name_length = strlen( out_file->name ); + const char *end = &out_file->name[ out_file_name_length ]; + const char *p = end; + while( p >= out_file->name && *p != '.' && *p != '/' && *p != '\\' ) + --p; + if( p < out_file->name ) + ++p; + if( *p != '.' ) + p = end; + int suffix_length = 1; + for( uint32_t i = output->current_seg_number; i; i /= 10 ) + ++suffix_length; + int seg_name_length = out_file_name_length + suffix_length; + int suffixless_length = p - out_file->name; + char seg_name[ seg_name_length + 1 ]; + seg_name[ seg_name_length ] = '\0'; + memcpy( seg_name, out_file->name, suffixless_length ); + sprintf( seg_name + suffixless_length, "_%"PRIu32, output->current_seg_number ); + if( *p == '.' ) + memcpy( seg_name + suffixless_length + suffix_length, p, end - p ); + int ret = lsmash_open_file( seg_name, 0, seg_param ); + if( ret == 0 ) + eprintf( "[Segment] out: %s\n", seg_name ); + return ret; +} + +static int switch_segment( remuxer_t *remuxer ) +{ + output_t *output = remuxer->output; + output_file_t *out_file = &output->file; + lsmash_file_parameters_t seg_param = { 0 }; + if( open_media_segment( output, &seg_param ) < 0 ) + return ERROR_MSG( "failed to open an output file for segmentation.\n" ); + /* Set up the media segment file. + * Copy the parameters of the previous segment if the previous is not the initialization segment. */ + if( out_file->seg_param.mode & LSMASH_FILE_MODE_INITIALIZATION ) + { + uint32_t brand_count = out_file->param.brand_count + 2; + lsmash_brand_type *brands = lsmash_malloc_zero( brand_count * sizeof(lsmash_brand_type) ); + if( !brands ) + return ERROR_MSG( "failed to allocate brands for an output segment file.\n" ); + brands[0] = ISOM_BRAND_TYPE_MSDH; + brands[1] = ISOM_BRAND_TYPE_MSIX; + for( uint32_t i = 0; i < out_file->param.brand_count; i++ ) + brands[i + 2] = out_file->param.brands[i]; + seg_param.major_brand = ISOM_BRAND_TYPE_MSDH; + seg_param.brand_count = brand_count; + seg_param.brands = brands; + seg_param.mode = LSMASH_FILE_MODE_WRITE | LSMASH_FILE_MODE_FRAGMENTED + | LSMASH_FILE_MODE_BOX | LSMASH_FILE_MODE_MEDIA + | LSMASH_FILE_MODE_INDEX | LSMASH_FILE_MODE_SEGMENT; + } + else + { + void *opaque = seg_param.opaque; + seg_param = out_file->seg_param; + seg_param.opaque = opaque; + } + lsmash_file_t *segment = lsmash_set_file( output->root, &seg_param ); + if( !segment ) + return ERROR_MSG( "failed to add an output segment file into a ROOT.\n" ); + /* Switch to the next segment. + * After switching, close the previous segment if the previous is not the initialization segment. */ + if( lsmash_switch_media_segment( output->root, segment, &moov_to_front ) < 0 ) + return ERROR_MSG( "failed to switch to the next segment.\n" ); + if( !(out_file->seg_param.mode & LSMASH_FILE_MODE_INITIALIZATION) ) + return lsmash_close_file( &out_file->seg_param ); + out_file->seg_param = seg_param; + return 0; +} + +static int handle_segmentation( remuxer_t *remuxer ) +{ + if( remuxer->subseg_per_seg == 0 ) + return 0; + output_t *output = remuxer->output; + if( remuxer->subseg_per_seg == output->file.current_subseg_number + || output->current_seg_number == 1 ) + { + if( switch_segment( remuxer ) < 0 ) + { + ERROR_MSG( "failed to switch to a segment.\n" ); + return -1; + } + output->file.current_subseg_number = 1; + ++ output->current_seg_number; + } + else + ++ output->file.current_subseg_number; + return 0; +} + +static void adapt_description_index( output_track_t *out_track, input_track_t *in_track, lsmash_sample_t *sample ) +{ + sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries + : sample->index == 0 ? 1 + : sample->index; + sample->index = out_track->summary_remap[ sample->index - 1 ]; + if( in_track->current_sample_index == 0 ) + in_track->current_sample_index = sample->index; +} + +static void adjust_timestamp( output_track_t *out_track, lsmash_sample_t *sample ) +{ + /* The first DTS must be 0. */ + if( out_track->current_sample_number == 1 ) + out_track->skip_dt_interval = sample->dts; + if( out_track->skip_dt_interval ) + { + sample->dts -= out_track->skip_dt_interval; + sample->cts -= out_track->skip_dt_interval; + } +} + +static int do_remux( remuxer_t *remuxer ) +{ +#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) + input_t *inputs = remuxer->input; + output_t *output = remuxer->output; + output_movie_t *out_movie = &output->file.movie; + set_reference_chapter_track( remuxer ); + double largest_dts = 0; /* in seconds */ + double frag_base_dts = 0; /* in seconds */ + uint32_t input_movie_number = 1; + uint32_t num_consecutive_sample_skip = 0; + uint32_t num_active_input_tracks = out_movie->num_tracks; + uint64_t total_media_size = 0; + uint8_t sample_count = 0; + uint8_t pending_flush_fragments = (remuxer->frag_base_track != 0); /* For non-fragmented movie, always set to 0. */ + while( 1 ) + { + input_t *in = &inputs[input_movie_number - 1]; + input_movie_t *in_movie = &in->file.movie; + input_track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ]; + if( !in_track->active ) + { + /* Move the next track. */ + if( ++ in_movie->current_track_number > in_movie->num_tracks ) + { + /* Move the next input movie. */ + in_movie->current_track_number = 1; + ++input_movie_number; + } + if( input_movie_number > remuxer->num_input ) + input_movie_number = 1; /* Back the first input movie. */ + continue; + } + /* Try append a sample in an input track where we didn't reach the end of media timeline. */ + if( !in_track->reach_end_of_media_timeline ) + { + lsmash_sample_t *sample = in_track->sample; + /* Get a new sample data if the track doesn't hold any one. */ + if( !sample ) + { + sample = lsmash_get_sample_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number ); + if( sample ) + { + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + adapt_description_index( out_track, in_track, sample ); + adjust_timestamp( out_track, sample ); + in_track->sample = sample; + in_track->dts = (double)sample->dts / in_track->media.param.timescale; + } + else + { + if( lsmash_check_sample_existence_in_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number ) ) + { + ERROR_MSG( "failed to get a sample.\n" ); + break; + } + lsmash_sample_t sample_info = { 0 }; + if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number, &sample_info ) < 0 ) + { + /* No more appendable samples in this track. */ + in_track->sample = NULL; + in_track->reach_end_of_media_timeline = 1; + if( --num_active_input_tracks == 0 ) + break; /* end of muxing */ + } + else + { + ERROR_MSG( "failed to get a sample.\n" ); + break; + } + } + } + if( sample ) + { + /* Flushing the active movie fragment is pending until random accessible point sample within all active tracks are ready. */ + if( remuxer->frag_base_track ) + { + if( pending_flush_fragments == 0 ) + { + if( remuxer->frag_base_track == out_movie->current_track_number + && sample->prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + { + pending_flush_fragments = 1; + frag_base_dts = in_track->dts; + } + } + else if( num_consecutive_sample_skip == num_active_input_tracks || total_media_size == 0 ) + { + if( flush_movie_fragment( remuxer ) < 0 ) + { + ERROR_MSG( "failed to flush a movie fragment.\n" ); + break; + } + if( handle_segmentation( remuxer ) < 0 ) + break; + if( lsmash_create_fragment_movie( output->root ) < 0 ) + { + ERROR_MSG( "failed to create a movie fragment.\n" ); + break; + } + pending_flush_fragments = 0; + } + } + /* Append a sample if meeting a condition. */ + int append = 0; + int need_new_fragment = (remuxer->frag_base_track && sample->index != in_track->current_sample_index); + if( pending_flush_fragments == 0 ) + append = (in_track->dts <= largest_dts || num_consecutive_sample_skip == num_active_input_tracks) && !need_new_fragment; + else if( remuxer->frag_base_track != out_movie->current_track_number && !need_new_fragment ) + { + /* Wait as much as possible both to make the last sample within each track fragment close to the DTS of + * the first sample within the track fragment corresponding to the base track within the next movie + * fragment and to make all the track fragments within the next movie fragment start with RAP. */ + if( sample->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + append = 1; + else + { + /* Check the DTS and random accessibilities of the next sample. */ + lsmash_sample_t info; + if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number + 1, &info ) < 0 ) + append = 0; + else + append = (info.prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) + && ((double)info.dts / in_track->media.param.timescale <= frag_base_dts); + } + } + if( append ) + { + if( sample->index ) + { + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + uint64_t sample_size = sample->length; /* sample might be deleted internally after appending. */ + uint64_t last_sample_dts = sample->dts; /* same as above */ + uint32_t sample_index = sample->index; /* same as above */ + /* Append a sample into output movie. */ + if( lsmash_append_sample( output->root, out_track->track_ID, sample ) < 0 ) + { + lsmash_delete_sample( sample ); + return ERROR_MSG( "failed to append a sample.\n" ); + } + largest_dts = LSMASH_MAX( largest_dts, in_track->dts ); + in_track->sample = NULL; + in_track->current_sample_number += 1; + in_track->current_sample_index = sample_index; + out_track->current_sample_number += 1; + out_track->last_sample_dts = last_sample_dts; + num_consecutive_sample_skip = 0; + total_media_size += sample_size; + /* Print, per 256 samples, total size of imported media. */ + if( ++sample_count == 0 ) + eprintf( "Importing: %"PRIu64" bytes\r", total_media_size ); + } + else + { + lsmash_delete_sample( sample ); + in_track->sample = NULL; + in_track->current_sample_number += 1; + } + } + else + ++num_consecutive_sample_skip; /* Skip appendig sample. */ + } + } + /* Move the next track. */ + if( ++ in_movie->current_track_number > in_movie->num_tracks ) + { + /* Move the next input movie. */ + in_movie->current_track_number = 1; + ++input_movie_number; + } + if( input_movie_number > remuxer->num_input ) + input_movie_number = 1; /* Back the first input movie. */ + if( ++ out_movie->current_track_number > out_movie->num_tracks ) + out_movie->current_track_number = 1; /* Back the first track in the output movie. */ + } + for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) + if( lsmash_flush_pooled_samples( output->root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) ) + return ERROR_MSG( "failed to flush samples.\n" ); + return 0; +#undef LSMASH_MAX +} + +static int construct_timeline_maps( remuxer_t *remuxer ) +{ + input_t *input = remuxer->input; + output_t *output = remuxer->output; + output_movie_t *out_movie = &output->file.movie; + track_media_option **track_option = remuxer->track_option; + out_movie->current_track_number = 1; + for( int i = 0; i < remuxer->num_input; i++ ) + for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ ) + { + input_track_t *in_track = &input[i].file.movie.track[j]; + if( !in_track->active ) + continue; + output_track_t *out_track = &out_movie->track[ out_movie->current_track_number ++ - 1 ]; + if( track_option[i][j].seek ) + { + /* Reconstruct timeline maps. */ + if( lsmash_delete_explicit_timeline_map( output->root, out_track->track_ID ) ) + return ERROR_MSG( "failed to delete explicit timeline maps.\n" ); + uint32_t movie_timescale = lsmash_get_movie_timescale( output->root ); + uint32_t media_timescale = lsmash_get_media_timescale( output->root, out_track->track_ID ); + if( !media_timescale ) + return ERROR_MSG( "media timescale is broken.\n" ); + double timescale_convert_multiplier = (double)movie_timescale / media_timescale; + lsmash_edit_t edit; + edit.start_time = in_track->composition_delay + in_track->skip_duration; + if( edit.start_time ) + { + uint64_t empty_duration = edit.start_time + lsmash_get_composition_to_decode_shift( output->root, out_track->track_ID ); + lsmash_edit_t empty_edit; + empty_edit.duration = empty_duration * timescale_convert_multiplier + 0.5; + empty_edit.start_time = ISOM_EDIT_MODE_EMPTY; + empty_edit.rate = ISOM_EDIT_MODE_NORMAL; + if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, empty_edit ) ) + return ERROR_MSG( "failed to create a empty duration.\n" ); + } + if( remuxer->frag_base_track == 0 ) + edit.duration = (out_track->last_sample_dts + out_track->last_sample_delta - in_track->skip_duration) * timescale_convert_multiplier; + else + edit.duration = ISOM_EDIT_DURATION_IMPLICIT; + edit.rate = ISOM_EDIT_MODE_NORMAL; + if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, edit ) ) + return ERROR_MSG( "failed to create a explicit timeline map.\n" ); + } + else if( lsmash_copy_timeline_map( output->root, out_track->track_ID, input[i].root, in_track->track_ID ) ) + return ERROR_MSG( "failed to copy timeline maps.\n" ); + } + out_movie->current_track_number = 1; + return 0; +} + +static int finish_movie( remuxer_t *remuxer ) +{ + output_t *output = remuxer->output; + /* Set chapter list */ + if( remuxer->chap_file ) + lsmash_set_tyrant_chapter( output->root, remuxer->chap_file, remuxer->add_bom_to_chpl ); + /* Finish muxing. */ + REFRESH_CONSOLE; + if( lsmash_finish_movie( output->root, &moov_to_front ) ) + return -1; + return remuxer->frag_base_track ? 0 : lsmash_write_lsmash_indicator( output->root ); +} + +int main( int argc, char *argv[] ) +{ + if ( argc < 2 ) + { + display_help(); + return -1; + } + else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) + { + display_help(); + return 0; + } + else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) + { + display_version(); + return 0; + } + else if( argc < 5 ) + { + display_help(); + return -1; + } + + lsmash_get_mainargs( &argc, &argv ); + int num_input = 0; + for( int i = 1 ; i < argc ; i++ ) + if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) + num_input++; + if( !num_input ) + return ERROR_MSG( "no input file specified.\n" ); + output_t output = { 0 }; + input_t input[ num_input ]; + track_media_option *track_option[ num_input ]; + remuxer_t remuxer = + { + .output = &output, + .input = input, + .track_option = track_option, + .num_input = num_input, + .add_bom_to_chpl = 0, + .ref_chap_available = 0, + .chap_track = 1, + .chap_file = NULL, + .default_language = 0, + .frag_base_track = 0, + .subseg_per_seg = 0, + .dash = 0 + }; + memset( input, 0, num_input * sizeof(input_t) ); + memset( track_option, 0, num_input * sizeof(track_media_option *) ); + if( parse_cli_option( argc, argv, &remuxer ) ) + return REMUXER_ERR( "failed to parse command line options.\n" ); + if( prepare_output( &remuxer ) ) + return REMUXER_ERR( "failed to set up preparation for output.\n" ); + if( remuxer.frag_base_track && construct_timeline_maps( &remuxer ) ) + return REMUXER_ERR( "failed to construct timeline maps.\n" ); + if( do_remux( &remuxer ) ) + return REMUXER_ERR( "failed to remux movies.\n" ); + if( remuxer.frag_base_track == 0 && construct_timeline_maps( &remuxer ) ) + return REMUXER_ERR( "failed to construct timeline maps.\n" ); + if( finish_movie( &remuxer ) ) + return REMUXER_ERR( "failed to finish output movie.\n" ); + REFRESH_CONSOLE; + eprintf( "%s completed!\n", !remuxer.dash || remuxer.subseg_per_seg == 0 ? "Remuxing" : "Segmentation" ); + cleanup_remuxer( &remuxer ); + return 0; +} diff -Nru l-smash-1.9.1/cli/timelineeditor.c l-smash-2.3.0/cli/timelineeditor.c --- l-smash-1.9.1/cli/timelineeditor.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/timelineeditor.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1220 @@ +/***************************************************************************** + * timelineeditor.c: + ***************************************************************************** + * Copyright (C) 2011-2014 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 "common/internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "lsmash.h" +#include "cli.h" + +#include "config.h" + +#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) + +#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) +#define REFRESH_CONSOLE eprintf( " \r" ) + +typedef struct +{ + int active; + lsmash_summary_t *summary; +} summary_t; + +typedef struct +{ + int active; + uint32_t track_ID; + uint32_t last_sample_delta; + uint32_t current_sample_number; + int reach_end_of_media_timeline; + uint32_t *summary_remap; + uint32_t num_summaries; + summary_t *summaries; + lsmash_track_parameters_t track_param; + lsmash_media_parameters_t media_param; +} track_t; + +typedef struct +{ + lsmash_itunes_metadata_t *itunes_metadata; + track_t *track; + lsmash_movie_parameters_t param; + uint32_t num_tracks; + uint32_t num_itunes_metadata; + uint32_t current_track_number; +} movie_t; + +typedef struct +{ + lsmash_file_t *fh; + lsmash_file_parameters_t param; + movie_t movie; +} file_t; + +typedef struct +{ + lsmash_root_t *root; + file_t file; +} root_t; + +typedef struct +{ + FILE *file; + uint64_t *ts; + uint32_t sample_count; + int auto_media_timescale; + int auto_media_timebase; + uint64_t media_timescale; + uint64_t media_timebase; + uint64_t duration; + uint64_t composition_delay; + uint64_t empty_delay; +} timecode_t; + +typedef struct +{ + root_t *output; + root_t *input; + timecode_t *timecode; +} movie_io_t; + +typedef struct +{ + uint32_t track_number; + uint32_t media_timescale; + uint32_t media_timebase; + uint32_t skip_duration; + uint32_t empty_delay; + int dts_compression; +} opt_t; + +static void cleanup_root( root_t *h ) +{ + if( !h ) + return; + movie_t *movie = &h->file.movie; + if( movie->itunes_metadata ) + { + for( uint32_t i = 0; i < movie->num_itunes_metadata; i++ ) + { + lsmash_itunes_metadata_t *metadata = &movie->itunes_metadata[i]; + if( metadata->type == ITUNES_METADATA_TYPE_STRING ) + { + if( metadata->value.string ) + lsmash_free( metadata->value.string ); + } + else if( metadata->type == ITUNES_METADATA_TYPE_BINARY ) + if( metadata->value.binary.data ) + lsmash_free( metadata->value.binary.data ); + if( metadata->meaning ) + lsmash_free( metadata->meaning ); + if( metadata->name ) + lsmash_free( metadata->name ); + } + lsmash_freep( &movie->itunes_metadata ); + } + if( movie->track ) + lsmash_freep( &movie->track ); + lsmash_close_file( &h->file.param ); + lsmash_destroy_root( h->root ); + h->root = NULL; +} + +static void cleanup_timecode( timecode_t *timecode ) +{ + if( !timecode ) + return; + if( timecode->file ) + { + fclose( timecode->file ); + timecode->file = NULL; + } + if( timecode->ts ) + lsmash_freep( &timecode->ts ); +} + +static int error_message( const char* message, ... ) +{ + REFRESH_CONSOLE; + eprintf( "Error: " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +static int warning_message( const char* message, ... ) +{ + REFRESH_CONSOLE; + eprintf( "Warning: " ); + va_list args; + va_start( args, message ); + vfprintf( stderr, message, args ); + va_end( args ); + return -1; +} + +static int timelineeditor_error( movie_io_t *io, const char *message, ... ) +{ + cleanup_root( io->input ); + cleanup_root( io->output ); + cleanup_timecode( io->timecode ); + va_list args; + va_start( args, message ); + error_message( message, args ); + va_end( args ); + return -1; +} + +#define TIMELINEEDITOR_ERR( ... ) timelineeditor_error( &io, __VA_ARGS__ ) +#define ERROR_MSG( ... ) error_message( __VA_ARGS__ ) +#define WARNING_MSG( ... ) warning_message( __VA_ARGS__ ) + +static char *duplicate_string( char *src ) +{ + if( !src ) + return NULL; + int dst_size = strlen( src ) + 1; + char *dst = lsmash_malloc( dst_size ); + if( !dst ) + return NULL; + memcpy( dst, src, dst_size ); + return dst; +} + +static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata ) +{ + memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) ); + if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) ) + return -1; + lsmash_itunes_metadata_t shadow = *metadata; + metadata->meaning = NULL; + metadata->name = NULL; + memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) ); + if( shadow.meaning ) + { + metadata->meaning = duplicate_string( shadow.meaning ); + if( !metadata->meaning ) + return -1; + } + if( shadow.name ) + { + metadata->name = duplicate_string( shadow.name ); + if( !metadata->name ) + goto fail; + } + if( shadow.type == ITUNES_METADATA_TYPE_STRING ) + { + metadata->value.string = duplicate_string( shadow.value.string ); + if( !metadata->value.string ) + goto fail; + } + else if( shadow.type == ITUNES_METADATA_TYPE_BINARY ) + { + metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size ); + if( !metadata->value.binary.data ) + goto fail; + memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size ); + } + return 0; +fail: + if( metadata->meaning ) + lsmash_free( metadata->meaning ); + if( metadata->name ) + lsmash_free( metadata->name ); + return -1; +} + +static int get_summaries( root_t *input, track_t *track ) +{ + track->num_summaries = lsmash_count_summary( input->root, track->track_ID ); + if( track->num_summaries == 0 ) + return ERROR_MSG( "Failed to get find valid summaries.\n" ); + track->summaries = lsmash_malloc( track->num_summaries * sizeof(summary_t) ); + if( !track->summaries ) + return ERROR_MSG( "failed to alloc input summaries.\n" ); + memset( track->summaries, 0, track->num_summaries * sizeof(summary_t) ); + for( uint32_t j = 0; j < track->num_summaries; j++ ) + { + lsmash_summary_t *summary = lsmash_get_summary( input->root, track->track_ID, j + 1 ); + if( !summary ) + { + WARNING_MSG( "failed to get a summary.\n" ); + continue; + } + track->summaries[j].summary = summary; + track->summaries[j].active = 1; + } + return 0; +} + +static int get_movie( root_t *input, char *input_name ) +{ + if( !strcmp( input_name, "-" ) ) + return ERROR_MSG( "Standard input not supported.\n" ); + input->root = lsmash_create_root(); + if( !input->root ) + return ERROR_MSG( "failed to create a ROOT for an input file.\n" ); + file_t *in_file = &input->file; + if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 ) + return ERROR_MSG( "failed to open an input file.\n" ); + in_file->fh = lsmash_set_file( input->root, &in_file->param ); + if( !in_file->fh ) + return ERROR_MSG( "failed to add an input file into a ROOT.\n" ); + if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 ) + return ERROR_MSG( "failed to read an input file\n" ); + movie_t *movie = &in_file->movie; + movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root ); + if( movie->num_itunes_metadata ) + { + movie->itunes_metadata = lsmash_malloc( movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) ); + if( !movie->itunes_metadata ) + return ERROR_MSG( "failed to alloc iTunes metadata.\n" ); + uint32_t itunes_metadata_count = 0; + for( uint32_t i = 1; i <= movie->num_itunes_metadata; i++ ) + { + if( get_itunes_metadata( input->root, i, &movie->itunes_metadata[itunes_metadata_count] ) ) + { + WARNING_MSG( "failed to get an iTunes metadata.\n" ); + continue; + } + ++itunes_metadata_count; + } + movie->num_itunes_metadata = itunes_metadata_count; + } + lsmash_initialize_movie_parameters( &movie->param ); + lsmash_get_movie_parameters( input->root, &movie->param ); + movie->num_tracks = movie->param.number_of_tracks; + movie->current_track_number = 1; + /* Create tracks. */ + track_t *track = movie->track = lsmash_malloc( movie->num_tracks * sizeof(track_t) ); + if( !track ) + return ERROR_MSG( "Failed to alloc input tracks.\n" ); + memset( track, 0, movie->num_tracks * sizeof(track_t) ); + for( uint32_t i = 0; i < movie->num_tracks; i++ ) + { + track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 ); + if( !track[i].track_ID ) + return ERROR_MSG( "Failed to get track_ID.\n" ); + } + for( uint32_t i = 0; i < movie->num_tracks; i++ ) + { + lsmash_initialize_track_parameters( &track[i].track_param ); + if( lsmash_get_track_parameters( input->root, track[i].track_ID, &track[i].track_param ) ) + { + WARNING_MSG( "failed to get track parameters.\n" ); + continue; + } + lsmash_initialize_media_parameters( &track[i].media_param ); + if( lsmash_get_media_parameters( input->root, track[i].track_ID, &track[i].media_param ) ) + { + WARNING_MSG( "failed to get media parameters.\n" ); + continue; + } + if( lsmash_construct_timeline( input->root, track[i].track_ID ) ) + { + WARNING_MSG( "failed to construct timeline.\n" ); + continue; + } + if( lsmash_get_last_sample_delta_from_media_timeline( input->root, track[i].track_ID, &track[i].last_sample_delta ) ) + { + WARNING_MSG( "failed to get the last sample delta.\n" ); + continue; + } + if( get_summaries( input, &track[i] ) ) + { + WARNING_MSG( "failed to get valid summaries.\n" ); + continue; + } + track[i].active = 1; + track[i].current_sample_number = 1; + } + lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) ); + return 0; +} + +static inline uint64_t get_gcd( uint64_t a, uint64_t b ) +{ + if( !b ) + return a; + while( 1 ) + { + uint64_t c = a % b; + if( !c ) + return b; + a = b; + b = c; + } +} + +static inline uint64_t get_lcm( uint64_t a, uint64_t b ) +{ + if( !a ) + return 0; + return (a / get_gcd( a, b )) * b; +} + +static uint64_t get_media_timebase( lsmash_media_ts_list_t *ts_list ) +{ + uint64_t timebase = ts_list->timestamp[0].cts; + for( uint32_t i = 1; i < ts_list->sample_count; i++ ) + timebase = get_gcd( timebase, ts_list->timestamp[i].cts ); + for( uint32_t i = 0; i < ts_list->sample_count; i++ ) + timebase = get_gcd( timebase, ts_list->timestamp[i].dts ); + return timebase; +} + +static inline double sigexp10( double value, double *exponent ) +{ + /* This function separates significand and exp10 from double floating point. */ + *exponent = 1; + while( value < 1 ) + { + value *= 10; + *exponent /= 10; + } + while( value >= 10 ) + { + value /= 10; + *exponent *= 10; + } + return value; +} + +#define DOUBLE_EPSILON 5e-6 +#define MATROSKA_TIMESCALE 1000000000 +#define SKIP_LINE_CHARACTER( x ) ((x) == '#' || (x) == '\n' || (x) == '\r') + +static double correct_fps( double fps, timecode_t *timecode ) +{ + int i = 1; + uint64_t fps_num, fps_den; + double exponent; + double fps_sig = sigexp10( fps, &exponent ); + while( 1 ) + { + fps_den = i * timecode->media_timebase; + fps_num = round( fps_den * fps_sig ) * exponent; + if( fps_num > UINT32_MAX ) + return ERROR_MSG( "framerate correction failed.\n" + "Specify an appropriate timebase manually or remake timecode file.\n" ); + if( fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON ) + break; + ++i; + } + if( timecode->auto_media_timescale ) + { + timecode->media_timescale = timecode->media_timescale + ? get_lcm( timecode->media_timescale, fps_num ) + : fps_num; + if( timecode->media_timescale > UINT32_MAX ) + timecode->auto_media_timescale = 0; + } + return (double)fps_num / fps_den; +} + +static int try_matroska_timescale( double *fps_array, timecode_t *timecode, uint32_t num_loops ) +{ + timecode->media_timebase = 0; + timecode->media_timescale = MATROSKA_TIMESCALE; + for( uint32_t i = 0; i < num_loops; i++ ) + { + uint64_t fps_den; + double exponent; + double fps_sig = sigexp10( fps_array[i], &exponent ); + fps_den = round( MATROSKA_TIMESCALE / fps_sig ) / exponent; + timecode->media_timebase = fps_den && timecode->media_timebase + ? get_gcd( timecode->media_timebase, fps_den ) + : fps_den; + if( timecode->media_timebase > UINT32_MAX || !timecode->media_timebase ) + return ERROR_MSG( "Automatic media timescale generation failed.\n" + "Specify media timescale manually.\n" ); + } + return 0; +} + +static int parse_timecode( timecode_t *timecode, uint32_t sample_count ) +{ + int tcfv; + int ret = fscanf( timecode->file, "# timecode format v%d", &tcfv ); + if( ret != 1 || (tcfv != 1 && tcfv != 2) ) + return ERROR_MSG( "Unsupported timecode format\n" ); + char buff[256]; + double *timecode_array = NULL; + if( tcfv == 1 ) + { + double assume_fps = 0; + /* Get assumed framerate. */ + while( fgets( buff, sizeof(buff), timecode->file ) ) + { + if( SKIP_LINE_CHARACTER( buff[0] ) ) + continue; + if( sscanf( buff, "assume %lf", &assume_fps ) != 1 + && sscanf( buff, "Assume %lf", &assume_fps ) != 1 ) + return ERROR_MSG( "Assumed fps not found\n" ); + break; + } + if( assume_fps <= 0 ) + return ERROR_MSG( "Invalid assumed fps\n" ); + uint64_t file_pos = ftell( timecode->file ); + /* Check whether valid or not and count number of sequences. */ + uint32_t num_sequences = 0; + int64_t start, end; + int64_t prev_start = -1, prev_end = -1; + double sequence_fps; + while( fgets( buff, sizeof(buff), timecode->file ) ) + { + if( SKIP_LINE_CHARACTER( buff[0] ) ) + continue; + ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps ); + if( ret != 3 && ret != EOF ) + return ERROR_MSG( "Invalid input timecode file\n" ); + if( start > end || start <= prev_start || end <= prev_end || sequence_fps <= 0 ) + return ERROR_MSG( "Invalid input timecode file\n" ); + prev_start = start; + prev_end = end; + if( timecode->auto_media_timescale || timecode->auto_media_timebase ) + ++num_sequences; + } + fseek( timecode->file, file_pos, SEEK_SET ); + /* Preparation storing timecodes. */ + double fps_array[ (timecode->auto_media_timescale || timecode->auto_media_timebase) * num_sequences + 1 ]; + double corrected_assume_fps = correct_fps( assume_fps, timecode ); + if( corrected_assume_fps < 0 ) + return ERROR_MSG( "Failed to correct the assumed framerate\n" ); + timecode_array = lsmash_malloc( sample_count * sizeof(double) ); + if( !timecode_array ) + return ERROR_MSG( "Failed to alloc timecodes\n" ); + timecode_array[0] = 0; + num_sequences = 0; + uint32_t i = 0; + while( i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ) ) + { + if( SKIP_LINE_CHARACTER( buff[0] ) ) + continue; + ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps ); + if( ret != 3 ) + start = end = sample_count - 1; + for( ; i < start && i < sample_count - 1; i++ ) + timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; + if( i < sample_count - 1 ) + { + if( timecode->auto_media_timescale || timecode->auto_media_timebase ) + fps_array[num_sequences++] = sequence_fps; + sequence_fps = correct_fps( sequence_fps, timecode ); + if( sequence_fps < 0 ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Failed to correct the framerate of a sequence.\n" ); + } + for( i = start; i <= end && i < sample_count - 1; i++ ) + timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps; + } + } + for( ; i < sample_count - 1; i++ ) + timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; + if( timecode->auto_media_timescale || timecode->auto_media_timebase ) + fps_array[num_sequences] = assume_fps; + /* Assume matroska timebase if automatic timescale generation isn't done yet. */ + if( timecode->auto_media_timebase && !timecode->auto_media_timescale ) + { + double exponent; + double assume_fps_sig, sequence_fps_sig; + if( try_matroska_timescale( fps_array, timecode, num_sequences + 1 ) < 0 ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Failed to try matroska timescale.\n" ); + } + fseek( timecode->file, file_pos, SEEK_SET ); + assume_fps_sig = sigexp10( assume_fps, &exponent ); + corrected_assume_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / assume_fps_sig ) / exponent ); + for( i = 0; i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ); ) + { + if( SKIP_LINE_CHARACTER( buff[0] ) ) + continue; + ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps ); + if( ret != 3 ) + start = end = sample_count - 1; + sequence_fps_sig = sigexp10( sequence_fps, &exponent ); + sequence_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / sequence_fps_sig ) / exponent ); + for( ; i < start && i < sample_count - 1; i++ ) + timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; + for( i = start; i <= end && i < sample_count - 1; i++ ) + timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps; + } + for( ; i < sample_count - 1; i++ ) + timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; + } + } + else /* tcfv == 2 */ + { + uint32_t num_timecodes = 0; + uint64_t file_pos = ftell( timecode->file ); + while( fgets( buff, sizeof(buff), timecode->file ) ) + { + if( SKIP_LINE_CHARACTER( buff[0] ) ) + { + if( !num_timecodes ) + file_pos = ftell( timecode->file ); + continue; + } + ++num_timecodes; + } + if( !num_timecodes ) + return ERROR_MSG( "No timecodes!\n" ); + if( sample_count > num_timecodes ) + return ERROR_MSG( "Lack number of timecodes.\n" ); + fseek( timecode->file, file_pos, SEEK_SET ); + timecode_array = lsmash_malloc( sample_count * sizeof(uint64_t) ); + if( !timecode_array ) + return ERROR_MSG( "Failed to alloc timecodes.\n" ); + uint32_t i = 0; + if( fgets( buff, sizeof(buff), timecode->file ) ) + { + ret = sscanf( buff, "%lf", &timecode_array[0] ); + if( ret != 1 ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Invalid timecode number: 0\n" ); + } + timecode_array[i++] *= 1e-3; /* Timescale of timecode format v2 is 1000. */ + while( i < sample_count && fgets( buff, sizeof(buff), timecode->file ) ) + { + if( SKIP_LINE_CHARACTER( buff[0] ) ) + continue; + ret = sscanf( buff, "%lf", &timecode_array[i] ); + timecode_array[i] *= 1e-3; /* Timescale of timecode format v2 is 1000. */ + if( ret != 1 || timecode_array[i] <= timecode_array[i - 1] ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Invalid input timecode.\n" ); + } + ++i; + } + } + if( i < sample_count ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Failed to get timecodes.\n" ); + } + /* Generate media timescale automatically if needed. */ + if( sample_count != 1 && timecode->auto_media_timescale ) + { + double fps_array[sample_count - 1]; + for( i = 0; i < sample_count - 1; i++ ) + { + fps_array[i] = 1 / (timecode_array[i + 1] - timecode_array[i]); + if( timecode->auto_media_timescale ) + { + int j = 1; + uint64_t fps_num, fps_den; + double exponent; + double fps_sig = sigexp10( fps_array[i], &exponent ); + while( 1 ) + { + fps_den = j * timecode->media_timebase; + fps_num = round( fps_den * fps_sig ) * exponent; + if( fps_num > UINT32_MAX + || fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON ) + break; + ++j; + } + timecode->media_timescale = fps_num && timecode->media_timescale + ? get_lcm( timecode->media_timescale, fps_num ) + : fps_num; + if( timecode->media_timescale > UINT32_MAX ) + { + timecode->auto_media_timescale = 0; + continue; /* Don't break because all framerate is needed for try_matroska_timescale. */ + } + } + } + if( timecode->auto_media_timebase && !timecode->auto_media_timescale + && try_matroska_timescale( fps_array, timecode, sample_count - 1 ) < 0 ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Failed to try matroska timescale.\n" ); + } + } + } + if( timecode->auto_media_timescale || timecode->auto_media_timebase ) + { + uint64_t reduce = get_gcd( timecode->media_timebase, timecode->media_timescale ); + timecode->media_timebase /= reduce; + timecode->media_timescale /= reduce; + } + else if( timecode->media_timescale > UINT32_MAX || !timecode->media_timescale ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Failed to generate media timescale automatically.\n" + "Specify an appropriate media timescale manually.\n" ); + } + uint32_t timescale = timecode->media_timescale; + uint32_t timebase = timecode->media_timebase; + double delay_tc = timecode_array[0]; + timecode->empty_delay = ((uint64_t)(delay_tc * ((double)timescale / timebase) + 0.5)) * timebase; + timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) ); + if( !timecode->ts ) + { + lsmash_free( timecode_array ); + return ERROR_MSG( "Failed to allocate timestamps.\n" ); + } + timecode->ts[0] = 0; + for( uint32_t i = 1; i < sample_count; i++ ) + { + timecode->ts[i] = ((uint64_t)((timecode_array[i] - delay_tc) * ((double)timescale / timebase) + 0.5)) * timebase; + if( timecode->ts[i] <= timecode->ts[i - 1] ) + { + lsmash_free( timecode_array ); + lsmash_free( timecode->ts ); + timecode->ts = NULL; + return ERROR_MSG( "Invalid timecode.\n" ); + } + } + lsmash_free( timecode_array ); + return 0; +} + +#undef DOUBLE_EPSILON +#undef MATROSKA_TIMESCALE +#undef SKIP_LINE_CHARACTER + +static int edit_media_timeline( root_t *input, timecode_t *timecode, opt_t *opt ) +{ + if( !timecode->file && !opt->media_timescale && !opt->media_timebase && !opt->dts_compression ) + return 0; + track_t *in_track = &input->file.movie.track[opt->track_number - 1]; + uint32_t track_ID = in_track->track_ID; + lsmash_media_ts_list_t ts_list; + if( lsmash_get_media_timestamps( input->root, track_ID, &ts_list ) ) + return ERROR_MSG( "Failed to get media timestamps.\n" ); + uint64_t timebase = get_media_timebase( &ts_list ); + if( !timebase ) + return ERROR_MSG( "Failed to get media timebase.\n" ); + lsmash_media_ts_t *timestamp = ts_list.timestamp; + uint32_t sample_count = ts_list.sample_count; + uint32_t orig_timebase = timebase; + uint32_t timescale; + double timebase_convert_multiplier; + if( opt->media_timescale || opt->media_timebase ) + { + uint32_t orig_timescale = in_track->media_param.timescale; + timescale = opt->media_timescale ? opt->media_timescale : orig_timescale; + timebase = opt->media_timebase ? opt->media_timebase : orig_timebase; + if( !opt->media_timescale && opt->media_timebase && (timebase > orig_timebase) ) + timescale = timescale * ((double)timebase / orig_timebase) + 0.5; + timebase_convert_multiplier = ((double)timescale / orig_timescale) * ((double)orig_timebase / timebase); + } + else + { + /* Reduce timescale and timebase. */ + timescale = in_track->media_param.timescale; + uint64_t reduce = get_gcd( timescale, timebase ); + timescale /= reduce; + timebase /= reduce; + timebase_convert_multiplier = 1; + } + /* Parse timecode file. */ + if( timecode->file ) + { + timecode->auto_media_timescale = !opt->media_timescale; + timecode->auto_media_timebase = !opt->media_timebase; + timecode->media_timescale = timecode->auto_media_timescale ? 0 : timescale; + timecode->media_timebase = timebase; + if( parse_timecode( timecode, sample_count ) ) + return ERROR_MSG( "Failed to parse timecode file.\n" ); + timescale = timecode->media_timescale; + timebase = timecode->media_timebase; + } + /* Get maximum composition sample delay for DTS generation. */ + uint32_t sample_delay; + if( lsmash_get_max_sample_delay( &ts_list, &sample_delay ) ) + return ERROR_MSG( "Failed to get maximum composition sample delay.\n" ); + if( sample_delay ) /* Reorder composition order. */ + lsmash_sort_timestamps_composition_order( &ts_list ); + if( !timecode->file ) + { + /* Genarate timestamps timescale converted. */ + timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) ); + if( !timecode->ts ) + return ERROR_MSG( "Failed to alloc timestamps\n" ); + for( uint32_t i = 0; i < sample_count; i++ ) + { + timecode->ts[i] = (timestamp[i].cts - timestamp[0].cts) / orig_timebase; + timecode->ts[i] = ((uint64_t)(timecode->ts[i] * timebase_convert_multiplier + 0.5)) * timebase; + if( i && (timecode->ts[i] <= timecode->ts[i - 1]) ) + return ERROR_MSG( "Invalid timescale conversion.\n" ); + } + } + if( sample_delay ) + { + /* If media timescale is specified, disable DTS compression multiplier. */ + uint32_t dts_compression_multiplier = opt->dts_compression * !opt->media_timescale * sample_delay + 1; + uint64_t initial_delta = timecode->ts[1]; + timescale *= dts_compression_multiplier; + if( dts_compression_multiplier > 1 ) + for( uint32_t i = 0; i < sample_count; i++ ) + timecode->ts[i] *= dts_compression_multiplier; + /* Generate CTS. */ + uint64_t sample_delay_time = timecode->composition_delay = opt->dts_compression ? 0 : timecode->ts[sample_delay]; + for( uint32_t i = 0; i < sample_count; i++ ) + timestamp[i].cts = timecode->ts[i] + sample_delay_time; + /* Reorder decode order and generate new DTS from CTS. */ + lsmash_sort_timestamps_decoding_order( &ts_list ); + uint64_t prev_reordered_cts[sample_delay]; + for( uint32_t i = 0; i <= sample_delay; i++ ) + { + if( !opt->dts_compression ) + timestamp[i].dts = timecode->ts[i]; + else + { + timestamp[i].dts = (i * initial_delta) / (!!opt->media_timescale * sample_delay + 1); + if( i && (timestamp[i].dts <= timestamp[i - 1].dts) ) + return ERROR_MSG( "Failed to do DTS compression.\n" ); + } + prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time; + } + for( uint32_t i = sample_delay + 1; i < sample_count; i++ ) + { + timestamp[i].dts = prev_reordered_cts[ (i - sample_delay) % sample_delay ]; + prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time; + } + } + else + for( uint32_t i = 0; i < sample_count; i++ ) + timestamp[i].cts = timestamp[i].dts = timecode->ts[i]; + if( sample_count > 1 ) + { + in_track->last_sample_delta = timecode->ts[sample_count - 1] - timecode->ts[sample_count - 2]; + timecode->duration = timecode->ts[sample_count - 1] + in_track->last_sample_delta; + } + else /* still image */ + timecode->duration = in_track->last_sample_delta = UINT32_MAX; + in_track->media_param.timescale = timescale; + if( lsmash_set_media_timestamps( input->root, track_ID, &ts_list ) ) + return ERROR_MSG( "Failed to set media timestamps.\n" ); + lsmash_delete_media_timestamps( &ts_list ); + return 0; +} + +static int check_white_brand( lsmash_brand_type brand ) +{ + static const lsmash_brand_type brand_white_list[] = + { + ISOM_BRAND_TYPE_3G2A, + ISOM_BRAND_TYPE_3GG6, + ISOM_BRAND_TYPE_3GG9, + ISOM_BRAND_TYPE_3GP4, + ISOM_BRAND_TYPE_3GP5, + ISOM_BRAND_TYPE_3GP6, + ISOM_BRAND_TYPE_3GP7, + ISOM_BRAND_TYPE_3GP8, + ISOM_BRAND_TYPE_3GP9, + ISOM_BRAND_TYPE_3GR6, + ISOM_BRAND_TYPE_3GR9, + ISOM_BRAND_TYPE_M4A , + ISOM_BRAND_TYPE_M4B , + ISOM_BRAND_TYPE_M4V , + ISOM_BRAND_TYPE_AVC1, + ISOM_BRAND_TYPE_DBY1, + ISOM_BRAND_TYPE_ISO2, + ISOM_BRAND_TYPE_ISO3, + ISOM_BRAND_TYPE_ISO4, + ISOM_BRAND_TYPE_ISO5, + ISOM_BRAND_TYPE_ISO6, + ISOM_BRAND_TYPE_ISOM, + ISOM_BRAND_TYPE_MP41, + ISOM_BRAND_TYPE_MP42, + ISOM_BRAND_TYPE_QT , + 0 + }; + for( int i = 0; brand_white_list[i]; i++ ) + if( brand == brand_white_list[i] ) + return 1; + return 0; +} + +static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size ) +{ + eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 ); + return 0; +} + +static void display_version( void ) +{ + eprintf( "\n" + "L-SMASH isom/mov timeline editor rev%s %s\n" + "Built on %s %s\n" + "Copyright (C) 2011-2014 L-SMASH project\n", + LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); +} + +static void display_help( void ) +{ + display_version(); + eprintf( "\n" + "Usage: timelineeditor [options] input output\n" + " options:\n" + " --help Display help\n" + " --version Display version information\n" + " --track Specify track number to edit [1]\n" + " --timecode Specify timecode file to edit timeline\n" + " --media-timescale Specify media timescale to convert\n" + " --media-timebase Specify media timebase to convert\n" + " --skip Skip start of media presentation in milliseconds\n" + " --delay Insert blank clip before actual media presentation in milliseconds\n" + " --dts-compression Eliminate composition delay with DTS hack\n" + " Multiply media timescale and timebase automatically\n" ); +} + +int main( int argc, char *argv[] ) +{ + if ( argc < 2 ) + { + display_help(); + return -1; + } + else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) + { + display_help(); + return 0; + } + else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) + { + display_version(); + return 0; + } + else if( argc < 3 ) + { + display_help(); + return -1; + } + root_t output = { 0 }; + root_t input = { 0 }; + timecode_t timecode = { 0 }; + movie_io_t io = { &output, &input, &timecode }; + opt_t opt = { 1, 0, 0, 0, 0, 0 }; + /* Parse options. */ + lsmash_get_mainargs( &argc, &argv ); + int argn = 1; + while( argn < argc - 2 ) + { + if( !strcasecmp( argv[argn], "--track" ) ) + { + opt.track_number = atoi( argv[++argn] ); + if( !opt.track_number ) + return TIMELINEEDITOR_ERR( "Invalid track number.\n" ); + ++argn; + } + else if( !strcasecmp( argv[argn], "--timecode" ) ) + { + timecode.file = lsmash_fopen( argv[++argn], "rb" ); + if( !timecode.file ) + return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" ); + ++argn; + } + else if( !strcasecmp( argv[argn], "--media-timescale" ) ) + { + opt.media_timescale = atoi( argv[++argn] ); + if( !opt.media_timescale ) + return TIMELINEEDITOR_ERR( "Invalid media timescale.\n" ); + ++argn; + } + else if( !strcasecmp( argv[argn], "--media-timebase" ) ) + { + opt.media_timebase = atoi( argv[++argn] ); + if( !opt.media_timebase ) + return TIMELINEEDITOR_ERR( "Invalid media timebase.\n" ); + ++argn; + } + else if( !strcasecmp( argv[argn], "--skip" ) ) + { + opt.skip_duration = atoi( argv[++argn] ); + if( !opt.skip_duration ) + return TIMELINEEDITOR_ERR( "Invalid skip duration.\n" ); + ++argn; + } + else if( !strcasecmp( argv[argn], "--delay" ) ) + { + opt.empty_delay = atoi( argv[++argn] ); + if( !opt.empty_delay ) + return TIMELINEEDITOR_ERR( "Invalid delay time.\n" ); + ++argn; + } + else if( !strcasecmp( argv[argn], "--dts-compression" ) ) + { + opt.dts_compression = 1; + ++argn; + } + else + return TIMELINEEDITOR_ERR( "Invalid option.\n" ); + } + if( argn > argc - 2 ) + return TIMELINEEDITOR_ERR( "Invalid arguments.\n" ); + /* Get input movies. */ + if( get_movie( &input, argv[argn++] ) ) + return TIMELINEEDITOR_ERR( "Failed to get input movie.\n" ); + movie_t *in_movie = &input.file.movie; + if( opt.track_number && (opt.track_number > in_movie->num_tracks) ) + return TIMELINEEDITOR_ERR( "Invalid track number.\n" ); + /* Create output movie. */ + file_t *out_file = &output.file; + output.root = lsmash_create_root(); + if( !output.root ) + return TIMELINEEDITOR_ERR( "failed to create a ROOT for an output file.\n" ); + if( lsmash_open_file( argv[argn], 0, &out_file->param ) < 0 ) + return TIMELINEEDITOR_ERR( "failed to open an output file.\n" ); + file_t *in_file = &input.file; + out_file->param.major_brand = in_file->param.major_brand; + out_file->param.minor_version = in_file->param.minor_version; + out_file->param.brands = in_file->param.brands; + out_file->param.brand_count = in_file->param.brand_count; + out_file->param.max_chunk_duration = 0.5; + out_file->param.max_async_tolerance = 2.0; + out_file->param.max_chunk_size = 4*1024*1024; + if( !check_white_brand( out_file->param.major_brand ) ) + { + /* Replace with whitelisted brand 'mp42'. */ + out_file->param.major_brand = ISOM_BRAND_TYPE_MP42; + out_file->param.minor_version = 0; + uint32_t i; + for( i = 0; i < out_file->param.brand_count; i++ ) + if( out_file->param.brands[i] == ISOM_BRAND_TYPE_MP42 ) + break; + if( i == out_file->param.brand_count ) + { + /* Add 'mp42' into the list of compatible brands. */ + out_file->param.brands = lsmash_malloc( (i + 1) * sizeof(lsmash_brand_type) ); + if( out_file->param.brands ) + { + memcpy( out_file->param.brands, in_file->param.brands, i * sizeof(lsmash_brand_type) ); + out_file->param.brands[i] = ISOM_BRAND_TYPE_MP42; + } + } + } + out_file->fh = lsmash_set_file( output.root, &out_file->param ); + if( !out_file->fh ) + return TIMELINEEDITOR_ERR( "failed to add an output file into a ROOT.\n" ); + if( out_file->param.brands != in_file->param.brands ) + lsmash_freep( &out_file->param.brands ); + /* Set movie parameters. */ + movie_t *out_movie = &out_file->movie; + out_movie->param = in_movie->param; /* Copy movie parameters. */ + if( in_movie->num_tracks == 1 ) + out_movie->param.timescale = in_movie->track[0].media_param.timescale; + if( lsmash_set_movie_parameters( output.root, &out_movie->param ) ) + return TIMELINEEDITOR_ERR( "Failed to set output movie parameters.\n" ); + /* Set iTunes metadata. */ + for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ ) + if( lsmash_set_itunes_metadata( output.root, in_movie->itunes_metadata[i] ) ) + { + WARNING_MSG( "failed to set an iTunes metadata.\n" ); + continue; + } + /* Create tracks of the output movie. */ + out_movie->track = lsmash_malloc( in_movie->num_tracks * sizeof(track_t) ); + if( !out_movie->track ) + return TIMELINEEDITOR_ERR( "Failed to alloc output tracks.\n" ); + /* Edit timeline. */ + if( edit_media_timeline( &input, &timecode, &opt ) ) + return TIMELINEEDITOR_ERR( "Failed to edit timeline.\n" ); + out_movie->num_tracks = in_movie->num_tracks; + out_movie->current_track_number = 1; + for( uint32_t i = 0; i < in_movie->num_tracks; i++ ) + { + track_t *in_track = &in_movie->track[i]; + if( !in_track->active ) + { + -- out_movie->num_tracks; + continue; + } + track_t *out_track = &out_movie->track[i]; + out_track->summary_remap = lsmash_malloc( in_track->num_summaries * sizeof(uint32_t) ); + if( !out_track->summary_remap ) + return TIMELINEEDITOR_ERR( "failed to create summary mapping for a track.\n" ); + memset( out_track->summary_remap, 0, in_track->num_summaries * sizeof(uint32_t) ); + out_track->track_ID = lsmash_create_track( output.root, in_track->media_param.handler_type ); + if( !out_track->track_ID ) + return TIMELINEEDITOR_ERR( "Failed to create a track.\n" ); + /* Copy track and media parameters except for track_ID. */ + out_track->track_param = in_track->track_param; + out_track->media_param = in_track->media_param; + out_track->track_param.track_ID = out_track->track_ID; + if( lsmash_set_track_parameters( output.root, out_track->track_ID, &out_track->track_param ) ) + return TIMELINEEDITOR_ERR( "Failed to set track parameters.\n" ); + if( lsmash_set_media_parameters( output.root, out_track->track_ID, &out_track->media_param ) ) + return TIMELINEEDITOR_ERR( "Failed to set media parameters.\n" ); + uint32_t valid_summary_count = 0; + for( uint32_t k = 0; k < in_track->num_summaries; k++ ) + { + if( !in_track->summaries[k].active ) + { + out_track->summary_remap[k] = 0; + continue; + } + lsmash_summary_t *summary = in_track->summaries[k].summary; + if( lsmash_add_sample_entry( output.root, out_track->track_ID, summary ) == 0 ) + { + WARNING_MSG( "failed to append a summary.\n" ); + lsmash_cleanup_summary( summary ); + in_track->summaries[k].summary = NULL; + in_track->summaries[k].active = 0; + out_track->summary_remap[k] = 0; + continue; + } + out_track->summary_remap[k] = ++valid_summary_count; + } + if( valid_summary_count == 0 ) + return TIMELINEEDITOR_ERR( "failed to append all summaries.\n" ); + out_track->last_sample_delta = in_track->last_sample_delta; + out_track->current_sample_number = 1; + out_track->reach_end_of_media_timeline = 0; + } + /* Start muxing. */ + double largest_dts = 0; + uint32_t num_consecutive_sample_skip = 0; + uint32_t num_active_input_tracks = out_movie->num_tracks; + uint64_t total_media_size = 0; + uint8_t sample_count = 0; + while( 1 ) + { + track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ]; + /* Try append a sample in an input track where we didn't reach the end of media timeline. */ + if( !in_track->reach_end_of_media_timeline ) + { + track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; + uint32_t in_track_ID = in_track->track_ID; + uint32_t out_track_ID = out_track->track_ID; + uint32_t input_media_timescale = in_track->media_param.timescale; + /* Get a DTS from a track in an input movie. */ + uint64_t dts; + if( lsmash_get_dts_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number, &dts ) ) + { + if( lsmash_check_sample_existence_in_media_timeline( input.root, in_track_ID, in_track->current_sample_number ) ) + return TIMELINEEDITOR_ERR( "Failed to get the DTS.\n" ); + else + { + in_track->reach_end_of_media_timeline = 1; + if( --num_active_input_tracks == 0 ) + break; /* end of muxing */ + } + } + /* Get and append a sample if it's good time. */ + else if( ((double)dts / input_media_timescale) <= largest_dts + || num_consecutive_sample_skip == num_active_input_tracks ) + { + /* Get an actual sample data from a track in an input movie. */ + lsmash_sample_t *sample = lsmash_get_sample_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number ); + if( !sample ) + return TIMELINEEDITOR_ERR( "Failed to get sample.\n" ); + sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries + : sample->index == 0 ? 1 + : sample->index; + sample->index = out_track->summary_remap[ sample->index - 1 ]; + if( sample->index ) + { + /* Append sample into output movie. */ + uint64_t sample_size = sample->length; /* sample will be deleted internally after appending. */ + if( lsmash_append_sample( output.root, out_track_ID, sample ) ) + { + lsmash_delete_sample( sample ); + return TIMELINEEDITOR_ERR( "Failed to append a sample.\n" ); + } + largest_dts = LSMASH_MAX( largest_dts, (double)dts / input_media_timescale ); + total_media_size += sample_size; + ++ in_track->current_sample_number; + num_consecutive_sample_skip = 0; + /* Print, per 256 samples, total size of imported media. */ + if( ++sample_count == 0 ) + eprintf( "Importing: %"PRIu64" bytes\r", total_media_size ); + } + } + else + ++num_consecutive_sample_skip; /* Skip appendig sample. */ + } + /* Move the next track. */ + if( ++ in_movie->current_track_number > in_movie->num_tracks ) + in_movie->current_track_number = 1; /* Back the first track. */ + if( ++ out_movie->current_track_number > out_movie->num_tracks ) + out_movie->current_track_number = 1; /* Back the first track in the output movie. */ + } + for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) + if( lsmash_flush_pooled_samples( output.root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) ) + return TIMELINEEDITOR_ERR( "Failed to flush samples.\n" ); + /* Copy timeline maps. */ + for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) + if( lsmash_copy_timeline_map( output.root, out_movie->track[i].track_ID, input.root, in_movie->track[i].track_ID ) ) + return TIMELINEEDITOR_ERR( "Failed to copy a timeline map.\n" ); + /* Edit timeline map. */ + if( argc > 3 ) + { + track_t *out_track = &out_movie->track[ opt.track_number - 1 ]; + uint32_t track_ID = out_track->track_ID; + uint32_t movie_timescale = lsmash_get_movie_timescale( output.root ); + uint32_t media_timescale = lsmash_get_media_timescale( output.root, track_ID ); + uint64_t empty_delay = timecode.empty_delay + (uint64_t)(opt.empty_delay * (1e-3 * media_timescale) + 0.5); + uint64_t duration = timecode.duration + empty_delay; + if( lsmash_delete_explicit_timeline_map( output.root, track_ID ) ) + return TIMELINEEDITOR_ERR( "Failed to delete explicit timeline maps.\n" ); + if( timecode.empty_delay ) + { + lsmash_edit_t empty_edit; + empty_edit.duration = ((double)timecode.empty_delay / media_timescale) * movie_timescale; + empty_edit.start_time = ISOM_EDIT_MODE_EMPTY; + empty_edit.rate = ISOM_EDIT_MODE_NORMAL; + if( lsmash_create_explicit_timeline_map( output.root, track_ID, empty_edit ) ) + return TIMELINEEDITOR_ERR( "Failed to create a empty duration.\n" ); + duration = ((double)duration / media_timescale) * movie_timescale; + duration -= empty_edit.duration; + } + else + duration = ((double)duration / media_timescale) * movie_timescale; + lsmash_edit_t edit; + edit.duration = duration; + edit.start_time = timecode.composition_delay + (uint64_t)(opt.skip_duration * (1e-3 * media_timescale) + 0.5); + edit.rate = ISOM_EDIT_MODE_NORMAL; + if( lsmash_create_explicit_timeline_map( output.root, track_ID, edit ) ) + return TIMELINEEDITOR_ERR( "Failed to create a explicit timeline map.\n" ); + } + /* Finish muxing. */ + lsmash_adhoc_remux_t moov_to_front; + moov_to_front.func = moov_to_front_callback; + moov_to_front.buffer_size = 4*1024*1024; + moov_to_front.param = NULL; + eprintf( " \r" ); + if( lsmash_finish_movie( output.root, &moov_to_front ) + || lsmash_write_lsmash_indicator( output.root ) ) + return TIMELINEEDITOR_ERR( "Failed to finish output movie.\n" ); + cleanup_root( io.input ); + cleanup_root( io.output ); + cleanup_timecode( io.timecode ); + eprintf( "Timeline editing completed! \n" ); + return 0; +} diff -Nru l-smash-1.9.1/cli/vc1_imp.c l-smash-2.3.0/cli/vc1_imp.c --- l-smash-1.9.1/cli/vc1_imp.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/cli/vc1_imp.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,550 @@ +/***************************************************************************** + * vc1_imp.c + ***************************************************************************** + * Copyright (C) 2011-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +/*************************************************************************** + SMPTE VC-1 importer (only for Advanced Profile) + SMPTE 421M-2006 + SMPTE RP 2025-2007 +***************************************************************************/ +#include "codecs/vc1.h" + +typedef struct +{ + importer_status status; + vc1_info_t info; + vc1_sequence_header_t first_sequence; + lsmash_media_ts_list_t ts_list; + lsmash_bs_t *bs; + uint8_t composition_reordering_present; + uint32_t max_au_length; + uint32_t num_undecodable; + uint64_t last_ref_intra_cts; +} vc1_importer_t; + +static void remove_vc1_importer( vc1_importer_t *vc1_imp ) +{ + if( !vc1_imp ) + return; + vc1_cleanup_parser( &vc1_imp->info ); + lsmash_bs_cleanup( vc1_imp->bs ); + lsmash_free( vc1_imp->ts_list.timestamp ); + lsmash_free( vc1_imp ); +} + +static void vc1_importer_cleanup( importer_t *importer ) +{ + debug_if( importer && importer->info ) + remove_vc1_importer( importer->info ); +} + +static vc1_importer_t *create_vc1_importer( importer_t *importer ) +{ + vc1_importer_t *vc1_imp = lsmash_malloc_zero( sizeof(vc1_importer_t) ); + if( !vc1_imp ) + return NULL; + if( vc1_setup_parser( &vc1_imp->info, 0 ) < 0 ) + { + remove_vc1_importer( vc1_imp ); + return NULL; + } + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + { + remove_vc1_importer( vc1_imp ); + return NULL; + } + bs->stream = importer->stream; + bs->read = lsmash_fread_wrapper; + bs->seek = lsmash_fseek_wrapper; + bs->unseekable = importer->is_stdin; + bs->buffer.max_size = BS_MAX_DEFAULT_READ_SIZE; + vc1_imp->bs = bs; + return vc1_imp; +} + +static inline int vc1_complete_au( vc1_access_unit_t *access_unit, vc1_picture_info_t *picture, int probe ) +{ + if( !picture->present ) + return 0; + if( !probe ) + memcpy( access_unit->data, access_unit->incomplete_data, access_unit->incomplete_data_length ); + access_unit->data_length = access_unit->incomplete_data_length; + access_unit->incomplete_data_length = 0; + vc1_update_au_property( access_unit, picture ); + return 1; +} + +static inline void vc1_append_ebdu_to_au( vc1_access_unit_t *access_unit, uint8_t *ebdu, uint32_t ebdu_length, int probe ) +{ + if( !probe ) + memcpy( access_unit->incomplete_data + access_unit->incomplete_data_length, ebdu, ebdu_length ); + /* Note: access_unit->incomplete_data_length shall be 0 immediately after AU has completed. + * Therefore, possible_au_length in vc1_get_access_unit_internal() can't be used here + * to avoid increasing AU length monotonously through the entire stream. */ + access_unit->incomplete_data_length += ebdu_length; +} + +static inline void vc1_get_au_internal_end( vc1_importer_t *vc1_imp, vc1_access_unit_t *access_unit ) +{ + vc1_imp->status = lsmash_bs_is_end( vc1_imp->bs, 0 ) && (access_unit->incomplete_data_length == 0) + ? IMPORTER_EOF + : IMPORTER_OK; +} + +static int vc1_get_au_internal_succeeded( vc1_importer_t *vc1_imp ) +{ + vc1_access_unit_t *access_unit = &vc1_imp->info.access_unit; + vc1_get_au_internal_end( vc1_imp, access_unit ); + access_unit->number += 1; + return 0; +} + +static int vc1_get_au_internal_failed( vc1_importer_t *vc1_imp, int complete_au, int ret ) +{ + vc1_access_unit_t *access_unit = &vc1_imp->info.access_unit; + vc1_get_au_internal_end( vc1_imp, access_unit ); + if( complete_au ) + access_unit->number += 1; + return ret; +} + +static int vc1_importer_get_access_unit_internal( importer_t *importer, int probe ) +{ + vc1_importer_t *vc1_imp = (vc1_importer_t *)importer->info; + vc1_info_t *info = &vc1_imp->info; + vc1_stream_buffer_t *sb = &info->buffer; + vc1_access_unit_t *access_unit = &info->access_unit; + lsmash_bs_t *bs = vc1_imp->bs; + int complete_au = 0; + access_unit->data_length = 0; + while( 1 ) + { + int err; + uint8_t bdu_type; + uint64_t trailing_zero_bytes; + uint64_t ebdu_length = vc1_find_next_start_code_prefix( bs, &bdu_type, &trailing_zero_bytes ); + if( ebdu_length <= VC1_START_CODE_LENGTH && lsmash_bs_is_end( bs, ebdu_length ) ) + { + /* For the last EBDU. + * This EBDU already has been appended into the latest access unit and parsed. */ + vc1_complete_au( access_unit, &info->picture, probe ); + return vc1_get_au_internal_succeeded( vc1_imp ); + } + else if( bdu_type == 0xFF ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "a forbidden BDU type is detected.\n" ); + return vc1_get_au_internal_failed( vc1_imp, complete_au, LSMASH_ERR_INVALID_DATA ); + } + uint64_t next_ebdu_head_pos = info->ebdu_head_pos + + ebdu_length + + trailing_zero_bytes; +#if 0 + if( probe ) + { + fprintf( stderr, "BDU type: %"PRIu8" \n", bdu_type ); + fprintf( stderr, " EBDU position: %"PRIx64" \n", info->ebdu_head_pos ); + fprintf( stderr, " EBDU length: %"PRIx64" (%"PRIu64")\n", ebdu_length, ebdu_length ); + fprintf( stderr, " trailing_zero_bytes: %"PRIx64" \n", trailing_zero_bytes ); + fprintf( stderr, " Next EBDU position: %"PRIx64" \n", next_ebdu_head_pos ); + } +#endif + if( bdu_type >= 0x0A && bdu_type <= 0x0F ) + { + /* Complete the current access unit if encountered delimiter of current access unit. */ + if( vc1_find_au_delimit_by_bdu_type( bdu_type, info->prev_bdu_type ) ) + /* The last video coded EBDU belongs to the access unit you want at this time. */ + complete_au = vc1_complete_au( access_unit, &info->picture, probe ); + /* Increase the buffer if needed. */ + uint64_t possible_au_length = access_unit->incomplete_data_length + ebdu_length; + if( sb->bank->buffer_size < possible_au_length + && (err = vc1_supplement_buffer( sb, access_unit, 2 * possible_au_length )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to increase the buffer size.\n" ); + return vc1_get_au_internal_failed( vc1_imp, complete_au, err ); + } + /* Process EBDU by its BDU type and append it to access unit. */ + uint8_t *ebdu = lsmash_bs_get_buffer_data( bs ); + switch( bdu_type ) + { + /* FRM_SC: Frame start code + * FLD_SC: Field start code + * SLC_SC: Slice start code + * SEQ_SC: Sequence header start code + * EP_SC: Entry-point start code + * PIC_L: Picture layer + * SLC_L: Slice layer + * SEQ_L: Sequence layer + * EP_L: Entry-point layer */ + case 0x0D : /* Frame + * For the Progressive or Frame Interlace mode, shall signal the beginning of a new video frame. + * For the Field Interlace mode, shall signal the beginning of a sequence of two independently coded video fields. + * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][[SLC_SC][SLC_L] (optional)] ... */ + if( (err = vc1_parse_advanced_picture( info->bits, &info->sequence, &info->picture, sb->rbdu, ebdu, ebdu_length )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to parse a frame.\n" ); + return vc1_get_au_internal_failed( vc1_imp, complete_au, err ); + } + case 0x0C : /* Field + * Shall only be used for Field Interlaced frames + * and shall only be used to signal the beginning of the second field of the frame. + * [FRM_SC][PIC_L][FLD_SC][PIC_L][[SLC_SC][SLC_L] (optional)] ... + * Field start code is followed by INTERLACE_FIELD_PICTURE_FIELD2() which doesn't have info of its field picture type.*/ + break; + case 0x0B : /* Slice + * Shall not be used for start code of the first slice of a frame. + * Shall not be used for start code of the first slice of an interlace field coded picture. + * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][SLC_SC][SLC_L][[SLC_SC][SLC_L] (optional)] ... + * Slice layer may repeat frame header. We just ignore it. */ + info->dvc1_param.slice_present = 1; + break; + case 0x0E : /* Entry-point header + * Entry-point indicates the direct followed frame is a start of group of frames. + * Entry-point doesn't indicates the frame is a random access point when multiple sequence headers are present, + * since it is necessary to decode sequence header which subsequent frames belong to for decoding them. + * Entry point shall be followed by + * 1. I-picture - progressive or frame interlace + * 2. I/I-picture, I/P-picture, or P/I-picture - field interlace + * [[SEQ_SC][SEQ_L] (optional)][EP_SC][EP_L][FRM_SC][PIC_L] ... */ + if( (err = vc1_parse_entry_point_header( info, ebdu, ebdu_length, probe )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to parse an entry point.\n" ); + return vc1_get_au_internal_failed( vc1_imp, complete_au, err ); + } + /* Signal random access type of the frame that follows this entry-point header. */ + info->picture.closed_gop = info->entry_point.closed_entry_point; + info->picture.random_accessible = info->dvc1_param.multiple_sequence ? info->picture.start_of_sequence : 1; + break; + case 0x0F : /* Sequence header + * [SEQ_SC][SEQ_L][EP_SC][EP_L][FRM_SC][PIC_L] ... */ + if( (err = vc1_parse_sequence_header( info, ebdu, ebdu_length, probe )) < 0 ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to parse a sequence header.\n" ); + return vc1_get_au_internal_failed( vc1_imp, complete_au, err ); + } + /* The frame that is the first frame after this sequence header shall be a random accessible point. */ + info->picture.start_of_sequence = 1; + if( probe && !vc1_imp->first_sequence.present ) + vc1_imp->first_sequence = info->sequence; + break; + default : /* End-of-sequence (0x0A) */ + break; + } + /* Append the current EBDU into the end of an incomplete access unit. */ + vc1_append_ebdu_to_au( access_unit, ebdu, ebdu_length, probe ); + } + else /* We don't support other BDU types such as user data yet. */ + return vc1_get_au_internal_failed( vc1_imp, complete_au, LSMASH_ERR_PATCH_WELCOME ); + /* Move to the first byte of the next EBDU. */ + info->prev_bdu_type = bdu_type; + if( lsmash_bs_read_seek( bs, next_ebdu_head_pos, SEEK_SET ) != next_ebdu_head_pos ) + { + lsmash_log( importer, LSMASH_LOG_ERROR, "failed to seek the next start code suffix.\n" ); + return vc1_get_au_internal_failed( vc1_imp, complete_au, LSMASH_ERR_NAMELESS ); + } + /* Check if no more data to read from the stream. */ + if( !lsmash_bs_is_end( bs, VC1_START_CODE_PREFIX_LENGTH ) ) + info->ebdu_head_pos = next_ebdu_head_pos; + /* If there is no more data in the stream, and flushed chunk of EBDUs, flush it as complete AU here. */ + else if( access_unit->incomplete_data_length && access_unit->data_length == 0 ) + { + vc1_complete_au( access_unit, &info->picture, probe ); + return vc1_get_au_internal_succeeded( vc1_imp ); + } + if( complete_au ) + return vc1_get_au_internal_succeeded( vc1_imp ); + } +} + +static int vc1_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) +{ + if( !importer->info ) + return LSMASH_ERR_NAMELESS; + if( track_number != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + vc1_importer_t *vc1_imp = (vc1_importer_t *)importer->info; + vc1_info_t *info = &vc1_imp->info; + importer_status current_status = vc1_imp->status; + if( current_status == IMPORTER_ERROR || buffered_sample->length < vc1_imp->max_au_length ) + return LSMASH_ERR_NAMELESS; + if( current_status == IMPORTER_EOF ) + { + buffered_sample->length = 0; + return 0; + } + int err = vc1_importer_get_access_unit_internal( importer, 0 ); + if( err < 0 ) + { + vc1_imp->status = IMPORTER_ERROR; + return err; + } + vc1_access_unit_t *access_unit = &info->access_unit; + buffered_sample->dts = vc1_imp->ts_list.timestamp[ access_unit->number - 1 ].dts; + buffered_sample->cts = vc1_imp->ts_list.timestamp[ access_unit->number - 1 ].cts; + buffered_sample->prop.leading = access_unit->independent + || access_unit->non_bipredictive + || buffered_sample->cts >= vc1_imp->last_ref_intra_cts + ? ISOM_SAMPLE_IS_NOT_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING; + if( access_unit->independent && !access_unit->disposable ) + vc1_imp->last_ref_intra_cts = buffered_sample->cts; + if( vc1_imp->composition_reordering_present && !access_unit->disposable && !access_unit->closed_gop ) + buffered_sample->prop.allow_earlier = QT_SAMPLE_EARLIER_PTS_ALLOWED; + buffered_sample->prop.independent = access_unit->independent ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT; + buffered_sample->prop.disposable = access_unit->disposable ? ISOM_SAMPLE_IS_DISPOSABLE : ISOM_SAMPLE_IS_NOT_DISPOSABLE; + buffered_sample->prop.redundant = ISOM_SAMPLE_HAS_NO_REDUNDANCY; + if( access_unit->random_accessible ) + /* All random access point is a sync sample even if it's an open RAP. */ + buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + buffered_sample->length = access_unit->data_length; + memcpy( buffered_sample->data, access_unit->data, access_unit->data_length ); + return current_status; +} + +static lsmash_video_summary_t *vc1_create_summary( vc1_info_t *info, vc1_sequence_header_t *sequence, uint32_t max_au_length ) +{ + if( !info->sequence.present || !info->entry_point.present ) + return NULL; + lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); + if( !summary ) + return NULL; + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1, + LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + specific->data.unstructured = lsmash_create_vc1_specific_info( &info->dvc1_param, &specific->size ); + if( !specific->data.unstructured + || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + summary->sample_type = ISOM_CODEC_TYPE_VC_1_VIDEO; + summary->max_au_length = max_au_length; + summary->timescale = sequence->framerate_numerator; + summary->timebase = sequence->framerate_denominator; + summary->vfr = !sequence->framerate_flag; + summary->sample_per_field = 0; + summary->width = sequence->disp_horiz_size; + summary->height = sequence->disp_vert_size; + summary->par_h = sequence->aspect_width; + summary->par_v = sequence->aspect_height; + summary->color.primaries_index = sequence->color_prim; + summary->color.transfer_index = sequence->transfer_char; + summary->color.matrix_index = sequence->matrix_coef; + return summary; +} + +static int vc1_analyze_whole_stream +( + importer_t *importer +) +{ + /* Parse all EBDU in the stream for preparation of calculating timestamps. */ + uint32_t cts_alloc = (1 << 12) * sizeof(uint64_t); + uint64_t *cts = lsmash_malloc( cts_alloc ); + if( !cts ) + return LSMASH_ERR_MEMORY_ALLOC; /* Failed to allocate CTS list */ + uint32_t num_access_units = 0; + uint32_t num_consecutive_b = 0; + lsmash_class_t *logger = &(lsmash_class_t){ "VC-1" }; + lsmash_log( &logger, LSMASH_LOG_INFO, "Analyzing stream as VC-1\r" ); + vc1_importer_t *vc1_imp = (vc1_importer_t *)importer->info; + vc1_info_t *info = &vc1_imp->info; + vc1_imp->status = IMPORTER_OK; + int err; + while( vc1_imp->status != IMPORTER_EOF ) + { +#if 0 + lsmash_log( &logger, LSMASH_LOG_INFO, "Analyzing stream as VC-1: %"PRIu32"\n", num_access_units + 1 ); +#endif + if( (err = vc1_importer_get_access_unit_internal( importer, 1 )) < 0 ) + goto fail; + /* In the case where B-pictures exist + * Decode order + * I[0]P[1]P[2]B[3]B[4]P[5]... + * DTS + * 0 1 2 3 4 5 ... + * Composition order + * I[0]P[1]B[3]B[4]P[2]P[5]... + * CTS + * 1 2 3 4 5 6 ... + * We assumes B or BI-pictures always be present in the stream here. */ + if( !info->access_unit.disposable ) + { + /* Apply CTS of the last B-picture plus 1 to the last non-B-picture. */ + if( num_access_units > num_consecutive_b ) + cts[ num_access_units - num_consecutive_b - 1 ] = num_access_units; + num_consecutive_b = 0; + } + else /* B or BI-picture */ + { + /* B and BI-pictures shall be output or displayed in the same order as they are encoded. */ + cts[ num_access_units ] = num_access_units; + ++num_consecutive_b; + info->dvc1_param.bframe_present = 1; + } + if( cts_alloc <= num_access_units * sizeof(uint64_t) ) + { + uint32_t alloc = 2 * num_access_units * sizeof(uint64_t); + uint64_t *temp = lsmash_realloc( cts, alloc ); + if( !temp ) + { + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; /* Failed to re-allocate CTS list */ + } + cts = temp; + cts_alloc = alloc; + } + vc1_imp->max_au_length = LSMASH_MAX( info->access_unit.data_length, vc1_imp->max_au_length ); + ++num_access_units; + } + if( num_access_units > num_consecutive_b ) + cts[ num_access_units - num_consecutive_b - 1 ] = num_access_units; + else + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + /* Construct timestamps. */ + lsmash_media_ts_t *timestamp = lsmash_malloc( num_access_units * sizeof(lsmash_media_ts_t) ); + if( !timestamp ) + { + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; /* Failed to allocate timestamp list */ + } + for( uint32_t i = 1; i < num_access_units; i++ ) + if( cts[i] < cts[i - 1] ) + { + vc1_imp->composition_reordering_present = 1; + break; + } + if( vc1_imp->composition_reordering_present ) + for( uint32_t i = 0; i < num_access_units; i++ ) + { + timestamp[i].cts = cts[i]; + timestamp[i].dts = i; + } + else + for( uint32_t i = 0; i < num_access_units; i++ ) + timestamp[i].cts = timestamp[i].dts = i; + lsmash_free( cts ); + lsmash_log_refresh_line( &logger ); +#if 0 + for( uint32_t i = 0; i < num_access_units; i++ ) + fprintf( stderr, "Timestamp[%"PRIu32"]: DTS=%"PRIu64", CTS=%"PRIu64"\n", i, timestamp[i].dts, timestamp[i].cts ); +#endif + vc1_imp->ts_list.sample_count = num_access_units; + vc1_imp->ts_list.timestamp = timestamp; + return 0; +fail: + lsmash_log_refresh_line( &logger ); + lsmash_free( cts ); + return err; +} + +static int vc1_importer_probe( importer_t *importer ) +{ + /* Find the first start code. */ + vc1_importer_t *vc1_imp = create_vc1_importer( importer ); + if( !vc1_imp ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = vc1_imp->bs; + uint64_t first_ebdu_head_pos = 0; + int err; + while( 1 ) + { + /* The first EBDU in decoding order of the stream shall have start code (0x000001). */ + if( 0x000001 == lsmash_bs_show_be24( bs, first_ebdu_head_pos ) ) + break; + /* Invalid if encountered any value of non-zero before the first start code. */ + if( lsmash_bs_show_byte( bs, first_ebdu_head_pos ) ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + ++first_ebdu_head_pos; + } + /* OK. It seems the stream has a sequence header of VC-1. */ + importer->info = vc1_imp; + vc1_info_t *info = &vc1_imp->info; + lsmash_bs_read_seek( bs, first_ebdu_head_pos, SEEK_SET ); + info->ebdu_head_pos = first_ebdu_head_pos; + if( (err = vc1_analyze_whole_stream( importer )) < 0 ) + goto fail; + lsmash_video_summary_t *summary = vc1_create_summary( info, &vc1_imp->first_sequence, vc1_imp->max_au_length ); + if( !summary ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + if( lsmash_add_entry( importer->summaries, summary ) < 0 ) + { + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + /* Go back to layer of the first EBDU. */ + vc1_imp->status = IMPORTER_OK; + lsmash_bs_read_seek( bs, first_ebdu_head_pos, SEEK_SET ); + info->prev_bdu_type = 0xFF; /* 0xFF is a forbidden value. */ + info->ebdu_head_pos = first_ebdu_head_pos; + uint8_t *temp_access_unit = info->access_unit.data; + uint8_t *temp_incomplete_access_unit = info->access_unit.incomplete_data; + memset( &info->access_unit, 0, sizeof(vc1_access_unit_t) ); + info->access_unit.data = temp_access_unit; + info->access_unit.incomplete_data = temp_incomplete_access_unit; + memset( &info->picture, 0, sizeof(vc1_picture_info_t) ); + return 0; +fail: + remove_vc1_importer( vc1_imp ); + importer->info = NULL; + lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); + return err; +} + +static uint32_t vc1_importer_get_last_delta( importer_t *importer, uint32_t track_number ) +{ + debug_if( !importer || !importer->info ) + return 0; + vc1_importer_t *vc1_imp = (vc1_importer_t *)importer->info; + if( !vc1_imp || track_number != 1 || vc1_imp->status != IMPORTER_EOF ) + return 0; + return vc1_imp->ts_list.sample_count + ? 1 + : UINT32_MAX; /* arbitrary */ +} + +const importer_functions vc1_importer = +{ + { "VC-1", offsetof( importer_t, log_level ) }, + 1, + vc1_importer_probe, + vc1_importer_get_accessunit, + vc1_importer_get_last_delta, + vc1_importer_cleanup +}; diff -Nru l-smash-1.9.1/cli.c l-smash-2.3.0/cli.c --- l-smash-1.9.1/cli.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/cli.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -/***************************************************************************** - * cli.c: - ***************************************************************************** - * Copyright (C) 2013-2014 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 -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#ifdef _WIN32 -void lsmash_get_mainargs( int *argc, char ***argv ) -{ - struct SI { int newmode; } si = { 0 }; - int __wgetmainargs( int *, wchar_t ***, wchar_t ***, int, struct SI * ); - wchar_t **wargv, **envp; - __wgetmainargs( argc, &wargv, &envp, 1, &si ); - *argv = calloc( *argc + 1, sizeof(char*) ); - for( int i = 0; i < *argc; ++i ) - lsmash_string_from_wchar( CP_UTF8, wargv[i], &(*argv)[i] ); -} -#endif - -int lsmash_write_lsmash_indicator( lsmash_root_t *root ) -{ - /* Write a tag in a free space to indicate the output file is written by L-SMASH. */ - char *string = "Multiplexed by L-SMASH"; - int length = strlen( string ); - lsmash_box_type_t type = lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'r', 'e', 'e' ) ); - lsmash_box_t *free_box = lsmash_create_box( type, (uint8_t *)string, length, LSMASH_BOX_PRECEDENCE_N ); - if( !free_box ) - return 0; - if( lsmash_add_box( lsmash_root_as_box( root ), free_box ) ) - { - lsmash_destroy_box( free_box ); - return 0; - } - return lsmash_write_top_level_box( free_box ); -} diff -Nru l-smash-1.9.1/cli.h l-smash-2.3.0/cli.h --- l-smash-1.9.1/cli.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/cli.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/***************************************************************************** - * cli.h: - ***************************************************************************** - * Copyright (C) 2013-2014 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 CLI_H -#define CLI_H - -#ifdef _WIN32 - void lsmash_get_mainargs( int *argc, char ***argv ); -#else -# define lsmash_get_mainargs( argc, argv ) (void)0 -#endif - -int lsmash_write_lsmash_indicator( lsmash_root_t *root ); - -#endif diff -Nru l-smash-1.9.1/codecs/a52.c l-smash-2.3.0/codecs/a52.c --- l-smash-1.9.1/codecs/a52.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/a52.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,736 @@ +/***************************************************************************** + * a52.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +static const char *bit_stream_mode[] = + { + "Main audio service: complete main (CM)", + "Main audio service: music and effects (ME)", + "Associated service: visually impaired (VI)", + "Associated service: hearing impaired (HI)", + "Associated service: dialogue (D)", + "Associated service: commentary (C)", + "Associated service: emergency (E)", + "Undefined service", + "Associated service: voice over (VO)", /* only if acmod == 0b001 */ + "Main audio service: karaoke" + }; + +/* For karaoke mode, C->M, S->V1, SL->V1 and SR->V2. */ +static const char *audio_coding_mode[] = + { + "1 + 1: Dual mono", + "1/0: C", + "2/0: L, R", + "3/0: L, C, R", + "2/1: L, R, S", + "3/1: L, C, R, S", + "2/2: L, R, SL, SR", + "3/2: L, C, R, SL, SR", + "Undefined audio coding mode", + "Undefined audio coding mode", + "2/0: L, R", + "3/0: L, M, R", + "2/1: L, R, V1", + "3/1: L, M, R, V1", + "2/2: L, R, V1, V2", + "3/2: L, M, R, V1, V2" + }; + +/*************************************************************************** + AC-3 tools + ETSI TS 102 366 V1.2.1 (2008-08) +***************************************************************************/ +#include "a52.h" + +#define AC3_SPECIFIC_BOX_LENGTH 11 + +uint32_t ac3_get_sample_rate( lsmash_ac3_specific_parameters_t *dac3_param ) +{ + /* The value 3 (or 0b11) of fscod is reserved. */ + uint32_t samplerate = ac3_sample_rate_table[ dac3_param->fscod ]; + if( samplerate == 0 ) + lsmash_log( NULL, LSMASH_LOG_WARNING, "Unknown sampling rate is detected.\n" ); + return samplerate; +} + +#if 0 +/* FIXME: though this table is for AC-3 in QTFF, we don't support it yet since the structure of CODEC specific info is unknown. + * ChannelLayout is given by ac3_channel_layout_table[ acmod ][ lfeon ]. */ +static const lsmash_channel_layout_tag ac3_channel_layout_table[8][2] = +{ + /* LFE: off LFE: on */ + { QT_CHANNEL_LAYOUT_UNKNOWN, QT_CHANNEL_LAYOUT_UNKNOWN }, /* FIXME: dual mono */ + { QT_CHANNEL_LAYOUT_MONO, QT_CHANNEL_LAYOUT_AC3_1_0_1 }, + { QT_CHANNEL_LAYOUT_STEREO, QT_CHANNEL_LAYOUT_DVD_4 }, + { QT_CHANNEL_LAYOUT_AC3_3_0, QT_CHANNEL_LAYOUT_AC3_3_0_1 }, + { QT_CHANNEL_LAYOUT_DVD_2, QT_CHANNEL_LAYOUT_AC3_2_1_1 }, + { QT_CHANNEL_LAYOUT_AC3_3_1, QT_CHANNEL_LAYOUT_AC3_3_1_1 }, + { QT_CHANNEL_LAYOUT_DVD_3, QT_CHANNEL_LAYOUT_DVD_18 }, + { QT_CHANNEL_LAYOUT_MPEG_5_0_C, QT_CHANNEL_LAYOUT_MPEG_5_1_C } +}; +#endif + +uint8_t *lsmash_create_ac3_specific_info( lsmash_ac3_specific_parameters_t *param, uint32_t *data_length ) +{ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + lsmash_bits_init( &bits, &bs ); + uint8_t buffer[AC3_SPECIFIC_BOX_LENGTH] = { 0 }; + bs.buffer.data = buffer; + bs.buffer.alloc = AC3_SPECIFIC_BOX_LENGTH; + lsmash_bits_put( &bits, 32, AC3_SPECIFIC_BOX_LENGTH ); /* box size */ + lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DAC3.fourcc ); /* box type: 'dac3' */ + lsmash_bits_put( &bits, 2, param->fscod ); + lsmash_bits_put( &bits, 5, param->bsid ); + lsmash_bits_put( &bits, 3, param->bsmod ); + lsmash_bits_put( &bits, 3, param->acmod ); + lsmash_bits_put( &bits, 1, param->lfeon ); + lsmash_bits_put( &bits, 5, param->frmsizecod >> 1 ); + lsmash_bits_put( &bits, 5, 0 ); + uint8_t *data = lsmash_bits_export_data( &bits, data_length ); + lsmash_bits_empty( &bits ); + return data; +} + +int lsmash_setup_ac3_specific_parameters_from_syncframe( lsmash_ac3_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) +{ + if( !data || data_length < AC3_MIN_SYNCFRAME_LENGTH ) + return LSMASH_ERR_FUNCTION_PARAM; + /* Check a syncword. */ + if( data[0] != 0x0b + || data[1] != 0x77 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t buffer[AC3_MAX_SYNCFRAME_LENGTH] = { 0 }; + bs.buffer.data = buffer; + bs.buffer.store = data_length; + bs.buffer.alloc = AC3_MAX_SYNCFRAME_LENGTH; + ac3_info_t info = { .bits = &bits }; + lsmash_bits_init( &bits, &bs ); + memcpy( buffer, data, LSMASH_MIN( data_length, AC3_MAX_SYNCFRAME_LENGTH ) ); + int err = ac3_parse_syncframe_header( &info ); + if( err < 0 ) + return err; + *param = info.dac3_param; + return 0; +} + +static int ac3_check_syncframe_header( lsmash_ac3_specific_parameters_t *param ) +{ + if( param->fscod == 0x3 ) + return LSMASH_ERR_INVALID_DATA; /* unknown Sample Rate Code */ + if( param->frmsizecod > 0x25 ) + return LSMASH_ERR_INVALID_DATA; /* unknown Frame Size Code */ + if( param->bsid >= 10 ) + return LSMASH_ERR_INVALID_DATA; /* might be EAC-3 */ + return 0; +} + +int ac3_parse_syncframe_header( ac3_info_t *info ) +{ + lsmash_bits_t *bits = info->bits; + lsmash_ac3_specific_parameters_t *param = &info->dac3_param; + lsmash_bits_get( bits, 32 ); /* syncword + crc1 */ + param->fscod = lsmash_bits_get( bits, 2 ); + param->frmsizecod = lsmash_bits_get( bits, 6 ); + param->bsid = lsmash_bits_get( bits, 5 ); + param->bsmod = lsmash_bits_get( bits, 3 ); + param->acmod = lsmash_bits_get( bits, 3 ); + if( (param->acmod & 0x01) && (param->acmod != 0x01) ) + lsmash_bits_get( bits, 2 ); /* cmixlev */ + if( param->acmod & 0x04 ) + lsmash_bits_get( bits, 2 ); /* surmixlev */ + if( param->acmod == 0x02 ) + lsmash_bits_get( bits, 2 ); /* dsurmod */ + param->lfeon = lsmash_bits_get( bits, 1 ); + lsmash_bits_get_align( bits ); + return ac3_check_syncframe_header( param ); +} + +int ac3_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < AC3_SPECIFIC_BOX_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + lsmash_ac3_specific_parameters_t *param = (lsmash_ac3_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + param->fscod = (data[0] >> 6) & 0x03; /* XXxx xxxx xxxx xxxx xxxx xxxx */ + param->bsid = (data[0] >> 1) & 0x1F; /* xxXX XXXx xxxx xxxx xxxx xxxx */ + param->bsmod = ((data[0] & 0x01) << 2) | ((data[2] >> 6) & 0x03); /* xxxx xxxX XXxx xxxx xxxx xxxx */ + param->acmod = (data[1] >> 3) & 0x07; /* xxxx xxxx xxXX Xxxx xxxx xxxx */ + param->lfeon = (data[1] >> 2) & 0x01; /* xxxx xxxx xxxx xXxx xxxx xxxx */ + param->frmsizecod = ((data[1] & 0x03) << 3) | ((data[3] >> 5) & 0x07); /* xxxx xxxx xxxx xxXX XXXx xxxx */ + param->frmsizecod <<= 1; + return 0; +} + +int ac3_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: AC3 Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + if( box->size < AC3_SPECIFIC_BOX_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *data = box->binary; + isom_skip_box_common( &data ); + uint8_t fscod = (data[0] >> 6) & 0x03; + uint8_t bsid = (data[0] >> 1) & 0x1F; + uint8_t bsmod = ((data[0] & 0x01) << 2) | ((data[1] >> 6) & 0x03); + uint8_t acmod = (data[1] >> 3) & 0x07; + uint8_t lfeon = (data[1] >> 2) & 0x01; + uint8_t bit_rate_code = ((data[1] & 0x03) << 3) | ((data[2] >> 5) & 0x07); + if( fscod != 0x03 ) + lsmash_ifprintf( fp, indent, "fscod = %"PRIu8" (%"PRIu32" Hz)\n", fscod, ac3_sample_rate_table[fscod] ); + else + lsmash_ifprintf( fp, indent, "fscod = 0x03 (reserved)\n" ); + lsmash_ifprintf( fp, indent, "bsid = %"PRIu8"\n", bsid ); + lsmash_ifprintf( fp, indent, "bsmod = %"PRIu8" (%s)\n", bsmod, bit_stream_mode[bsmod + (acmod == 0x01 ? 1 : acmod > 0x01 ? 2 : 0)] ); + lsmash_ifprintf( fp, indent, "acmod = %"PRIu8" (%s)\n", acmod, audio_coding_mode[acmod + (bsmod == 0x07 ? 8 : 0)] ); + lsmash_ifprintf( fp, indent, "lfeon = %s\n", lfeon ? "1 (LFE)" : "0" ); + static const uint32_t bit_rate[] = + { + 32, 40, 48, 56, 64, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384, 448, 512, 576, 640, + 0 /* undefined */ + }; + lsmash_ifprintf( fp, indent, "bit_rate_code = 0x%02"PRIx8" (%"PRIu32" kbit/s)\n", bit_rate_code, bit_rate[bit_rate_code] ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", data[2] & 0x1F ); + return 0; +} + +#undef AC3_SPECIFIC_BOX_LENGTH + +/*************************************************************************** + Enhanced AC-3 tools + ETSI TS 102 366 V1.2.1 (2008-08) +***************************************************************************/ + +uint8_t *lsmash_create_eac3_specific_info( lsmash_eac3_specific_parameters_t *param, uint32_t *data_length ) +{ +#define EAC3_SPECIFIC_BOX_MAX_LENGTH 42 + if( param->num_ind_sub > 7 ) + return NULL; + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + lsmash_bits_init( &bits, &bs ); + uint8_t buffer[EAC3_SPECIFIC_BOX_MAX_LENGTH] = { 0 }; + bs.buffer.data = buffer; + bs.buffer.alloc = EAC3_SPECIFIC_BOX_MAX_LENGTH; + lsmash_bits_put( &bits, 32, 0 ); /* box size */ + lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DEC3.fourcc ); /* box type: 'dec3' */ + lsmash_bits_put( &bits, 13, param->data_rate ); /* data_rate; setup by isom_update_bitrate_description */ + lsmash_bits_put( &bits, 3, param->num_ind_sub ); + /* Apparently, the condition of this loop defined in ETSI TS 102 366 V1.2.1 (2008-08) is wrong. */ + for( int i = 0; i <= param->num_ind_sub; i++ ) + { + lsmash_eac3_substream_info_t *independent_info = ¶m->independent_info[i]; + lsmash_bits_put( &bits, 2, independent_info->fscod ); + lsmash_bits_put( &bits, 5, independent_info->bsid ); + lsmash_bits_put( &bits, 5, independent_info->bsmod ); + lsmash_bits_put( &bits, 3, independent_info->acmod ); + lsmash_bits_put( &bits, 1, independent_info->lfeon ); + lsmash_bits_put( &bits, 3, 0 ); /* reserved */ + lsmash_bits_put( &bits, 4, independent_info->num_dep_sub ); + if( independent_info->num_dep_sub > 0 ) + lsmash_bits_put( &bits, 9, independent_info->chan_loc ); + else + lsmash_bits_put( &bits, 1, 0 ); /* reserved */ + } + uint8_t *data = lsmash_bits_export_data( &bits, data_length ); + lsmash_bits_empty( &bits ); + /* Update box size. */ + LSMASH_SET_BE32( data, *data_length ); + return data; +#undef EAC3_SPECIFIC_BOX_MAX_LENGTH +} + +/* Return -1 if incomplete Enhanced AC-3 sample is given. */ +int lsmash_setup_eac3_specific_parameters_from_frame( lsmash_eac3_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) +{ + if( !data || data_length < 5 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t buffer[EAC3_MAX_SYNCFRAME_LENGTH] = { 0 }; + bs.buffer.data = buffer; + bs.buffer.store = data_length; + bs.buffer.alloc = EAC3_MAX_SYNCFRAME_LENGTH; + eac3_info_t *info = &(eac3_info_t){ .bits = &bits }; + lsmash_bits_init( &bits, &bs ); + memcpy( buffer, data, LSMASH_MIN( data_length, EAC3_MAX_SYNCFRAME_LENGTH ) ); + uint64_t next_frame_pos = 0; + while( 1 ) + { + /* Seek to the head of the next syncframe. */ + bs.buffer.pos = LSMASH_MIN( data_length, next_frame_pos ); + /* Check the remainder length of the input data. + * If there is enough length, then parse the syncframe in it. + * The length 5 is the required byte length to get frame size. */ + uint64_t remain_size = lsmash_bs_get_remaining_buffer_size( &bs ); + if( bs.eob || (bs.eof && remain_size < 5) ) + goto setup_param; /* No more valid data. */ + /* Check the syncword. */ + if( lsmash_bs_show_byte( &bs, 0 ) != 0x0b + || lsmash_bs_show_byte( &bs, 1 ) != 0x77 ) + goto setup_param; + /* Parse syncframe. */ + info->frame_size = 0; + if( eac3_parse_syncframe( info ) < 0 ) + goto setup_param; + if( remain_size < info->frame_size ) + goto setup_param; + int independent = info->strmtyp != 0x1; + if( independent && info->substreamid == 0x0 ) + { + if( info->number_of_audio_blocks == 6 ) + { + /* Encountered the first syncframe of the next access unit. */ + info->number_of_audio_blocks = 0; + goto setup_param; + } + else if( info->number_of_audio_blocks > 6 ) + goto setup_param; + info->number_of_audio_blocks += eac3_audio_block_table[ info->numblkscod ]; + info->number_of_independent_substreams = 0; + } + else if( info->syncframe_count == 0 ) + /* The first syncframe in an AU must be independent and assigned substream ID 0. */ + return LSMASH_ERR_INVALID_DATA; + if( independent ) + info->independent_info[info->number_of_independent_substreams ++].num_dep_sub = 0; + else + ++ info->independent_info[info->number_of_independent_substreams - 1].num_dep_sub; + next_frame_pos += info->frame_size; + ++ info->syncframe_count; + } +setup_param: + if( info->number_of_independent_substreams == 0 || info->number_of_independent_substreams > 8 ) + return LSMASH_ERR_INVALID_DATA; + if( !info->dec3_param_initialized ) + eac3_update_specific_param( info ); + *param = info->dec3_param; + return info->number_of_audio_blocks == 6 ? 0 : LSMASH_ERR_INVALID_DATA; +} + +uint16_t lsmash_eac3_get_chan_loc_from_chanmap( uint16_t chanmap ) +{ + return ((chanmap & 0x7f8) >> 2) | ((chanmap & 0x2) >> 1); +} + +static int eac3_check_syncframe_header( eac3_info_t *info ) +{ + if( info->strmtyp == 0x3 ) + return LSMASH_ERR_INVALID_DATA; /* unknown Stream type */ + lsmash_eac3_substream_info_t *substream_info; + if( info->strmtyp != 0x1 ) + substream_info = &info->independent_info[ info->current_independent_substream_id ]; + else + substream_info = &info->dependent_info; + if( substream_info->fscod == 0x3 && info->fscod2 == 0x3 ) + return LSMASH_ERR_INVALID_DATA; /* unknown Sample Rate Code */ + if( substream_info->bsid < 10 || substream_info->bsid > 16 ) + return LSMASH_ERR_INVALID_DATA; /* not EAC-3 */ + return 0; +} + +int eac3_parse_syncframe( eac3_info_t *info ) +{ + lsmash_bits_t *bits = info->bits; + lsmash_bits_get( bits, 16 ); /* syncword (16) */ + info->strmtyp = lsmash_bits_get( bits, 2 ); /* strmtyp (2) */ + info->substreamid = lsmash_bits_get( bits, 3 ); /* substreamid (3) */ + lsmash_eac3_substream_info_t *substream_info; + if( info->strmtyp != 0x1 ) + { + if( info->substreamid == 0x0 && info->number_of_independent_substreams ) + eac3_update_specific_param( info ); + info->current_independent_substream_id = info->substreamid; + substream_info = &info->independent_info[ info->current_independent_substream_id ]; + substream_info->chan_loc = 0; + } + else + substream_info = &info->dependent_info; + info->frame_size = 2 * (lsmash_bits_get( bits, 11 ) + 1); /* frmsiz (11) */ + substream_info->fscod = lsmash_bits_get( bits, 2 ); /* fscod (2) */ + if( substream_info->fscod == 0x3 ) + { + info->fscod2 = lsmash_bits_get( bits, 2 ); /* fscod2 (2) */ + info->numblkscod = 0x3; + } + else + info->numblkscod = lsmash_bits_get( bits, 2 ); /* numblkscod (2) */ + substream_info->acmod = lsmash_bits_get( bits, 3 ); /* acmod (3) */ + substream_info->lfeon = lsmash_bits_get( bits, 1 ); /* lfeon (1) */ + substream_info->bsid = lsmash_bits_get( bits, 5 ); /* bsid (5) */ + lsmash_bits_get( bits, 5 ); /* dialnorm (5) */ + if( lsmash_bits_get( bits, 1 ) ) /* compre (1) */ + lsmash_bits_get( bits, 8 ); /* compr (8) */ + if( substream_info->acmod == 0x0 ) + { + lsmash_bits_get( bits, 5 ); /* dialnorm2 (5) */ + if( lsmash_bits_get( bits, 1 ) ) /* compre2 (1) */ + lsmash_bits_get( bits, 8 ); /* compr2 (8) */ + } + if( info->strmtyp == 0x1 && lsmash_bits_get( bits, 1 ) ) /* chanmape (1) */ + { + uint16_t chanmap = lsmash_bits_get( bits, 16 ); /* chanmap (16) */ + info->independent_info[ info->current_independent_substream_id ].chan_loc |= lsmash_eac3_get_chan_loc_from_chanmap( chanmap ); + } + if( lsmash_bits_get( bits, 1 ) ) /* mixmdate (1) */ + { + if( substream_info->acmod > 0x2 ) + lsmash_bits_get( bits, 2 ); /* dmixmod (2) */ + if( ((substream_info->acmod & 0x1) && (substream_info->acmod > 0x2)) || (substream_info->acmod & 0x4) ) + lsmash_bits_get( bits, 6 ); /* ltrt[c/sur]mixlev (3) + * loro[c/sur]mixlev (3) */ + if( substream_info->lfeon && lsmash_bits_get( bits, 1 ) ) /* lfemixlevcode (1) */ + lsmash_bits_get( bits, 5 ); /* lfemixlevcod (5) */ + if( info->strmtyp == 0x0 ) + { + if( lsmash_bits_get( bits, 1 ) ) /* pgmscle (1) */ + lsmash_bits_get( bits, 6 ); /* pgmscl (6) */ + if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) ) /* pgmscle2 (1) */ + lsmash_bits_get( bits, 6 ); /* pgmscl2 (6) */ + if( lsmash_bits_get( bits, 1 ) ) /* extpgmscle (1) */ + lsmash_bits_get( bits, 6 ); /* extpgmscl (6) */ + uint8_t mixdef = lsmash_bits_get( bits, 2 ); /* mixdef (2) */ + if( mixdef == 0x1 ) + lsmash_bits_get( bits, 5 ); /* premixcmpsel (1) + * drcsrc (1) + * premixcmpscl (3) */ + else if( mixdef == 0x2 ) + lsmash_bits_get( bits, 12 ); /* mixdata (12) */ + else if( mixdef == 0x3 ) + { + uint8_t mixdeflen = lsmash_bits_get( bits, 5 ); /* mixdeflen (5) */ + lsmash_bits_get( bits, 8 * (mixdeflen + 2) ); /* mixdata (8 * (mixdeflen + 2)) + * mixdatafill (0-7) */ + } + if( substream_info->acmod < 0x2 ) + { + if( lsmash_bits_get( bits, 1 ) ) /* paninfoe (1) */ + lsmash_bits_get( bits, 14 ); /* panmean (8) + * paninfo (6) */ + if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) ) /* paninfo2e (1) */ + lsmash_bits_get( bits, 14 ); /* panmean2 (8) + * paninfo2 (6) */ + } + if( lsmash_bits_get( bits, 1 ) ) /* frmmixcfginfoe (1) */ + { + if( info->numblkscod == 0x0 ) + lsmash_bits_get( bits, 5 ); /* blkmixcfginfo[0] (5) */ + else + { + int number_of_blocks_per_syncframe = ((int []){ 1, 2, 3, 6 })[ info->numblkscod ]; + for( int blk = 0; blk < number_of_blocks_per_syncframe; blk++ ) + if( lsmash_bits_get( bits, 1 ) ) /* blkmixcfginfoe (1)*/ + lsmash_bits_get( bits, 5 ); /* blkmixcfginfo[blk] (5) */ + } + } + } + } + if( lsmash_bits_get( bits, 1 ) ) /* infomdate (1) */ + { + substream_info->bsmod = lsmash_bits_get( bits, 3 ); /* bsmod (3) */ + lsmash_bits_get( bits, 1 ); /* copyrightb (1) */ + lsmash_bits_get( bits, 1 ); /* origbs (1) */ + if( substream_info->acmod == 0x2 ) + lsmash_bits_get( bits, 4 ); /* dsurmod (2) + * dheadphonmod (2) */ + else if( substream_info->acmod >= 0x6 ) + lsmash_bits_get( bits, 2 ); /* dsurexmod (2) */ + if( lsmash_bits_get( bits, 1 ) ) /* audprodie (1) */ + lsmash_bits_get( bits, 8 ); /* mixlevel (5) + * roomtyp (2) + * adconvtyp (1) */ + if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) ) /* audprodie2 (1) */ + lsmash_bits_get( bits, 8 ); /* mixlevel2 (5) + * roomtyp2 (2) + * adconvtyp2 (1) */ + if( substream_info->fscod < 0x3 ) + lsmash_bits_get( bits, 1 ); /* sourcefscod (1) */ + } + else + substream_info->bsmod = 0; + if( info->strmtyp == 0x0 && info->numblkscod != 0x3 ) + lsmash_bits_get( bits, 1 ); /* convsync (1) */ + if( info->strmtyp == 0x2 ) + { + int blkid; + if( info->numblkscod == 0x3 ) + blkid = 1; + else + blkid = lsmash_bits_get( bits, 1 ); /* blkid (1) */ + if( blkid ) + lsmash_bits_get( bits, 6 ); /* frmsizecod (6) */ + } + if( lsmash_bits_get( bits, 1 ) ) /* addbsie (1) */ + { + uint8_t addbsil = lsmash_bits_get( bits, 6 ); /* addbsil (6) */ + lsmash_bits_get( bits, (addbsil + 1) * 8 ); /* addbsi ((addbsil + 1) * 8) */ + } + lsmash_bits_get_align( bits ); + return eac3_check_syncframe_header( info ); +} + +void eac3_update_specific_param( eac3_info_t *info ) +{ + lsmash_eac3_specific_parameters_t *param = &info->dec3_param; + param->data_rate = 0; + param->num_ind_sub = info->number_of_independent_substreams - 1; + for( uint8_t i = 0; i <= param->num_ind_sub; i++ ) + param->independent_info[i] = info->independent_info[i]; + info->dec3_param_initialized = 1; +} + +#define EAC3_SPECIFIC_BOX_MIN_LENGTH 13 + +int eac3_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < EAC3_SPECIFIC_BOX_MIN_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + lsmash_eac3_specific_parameters_t *param = (lsmash_eac3_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + param->data_rate = (data[0] << 5) | ((data[1] >> 3) & 0x1F); /* XXXX XXXX XXXX Xxxx */ + param->num_ind_sub = data[1] & 0x07; /* xxxx xxxx xxxx xXXX */ + data += 2; + size -= 2; + for( int i = 0; i <= param->num_ind_sub; i++ ) + { + if( size < 3 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_eac3_substream_info_t *independent_info = ¶m->independent_info[i]; + independent_info->fscod = (data[0] >> 6) & 0x03; /* XXxx xxxx xxxx xxxx xxxx xxxx */ + independent_info->bsid = (data[0] >> 1) & 0x1F; /* xxXX XXXx xxxx xxxx xxxx xxxx */ + independent_info->bsmod = ((data[0] & 0x01) << 4) | ((data[1] >> 4) & 0x0F); /* xxxx xxxX XXXX xxxx xxxx xxxx */ + independent_info->acmod = (data[1] >> 1) & 0x07; /* xxxx xxxx xxxx XXXx xxxx xxxx */ + independent_info->lfeon = data[1] & 0x01; /* xxxx xxxx xxxx xxxX xxxx xxxx */ + independent_info->num_dep_sub = (data[2] >> 1) & 0x0F; /* xxxx xxxx xxxx xxxx xxxX XXXx */ + data += 3; + size -= 3; + if( independent_info->num_dep_sub > 0 ) + { + if( size < 1 ) + return LSMASH_ERR_INVALID_DATA; + independent_info->chan_loc = ((data[-1] & 0x01) << 8) | data[0]; /* xxxx xxxX XXXX XXXX */ + data += 1; + size -= 1; + } + } + return 0; +} + +void eac3_update_sample_rate +( + uint32_t *frequency, + lsmash_eac3_specific_parameters_t *dec3_param, + uint8_t *fscod2 +) +{ + /* Additional independent substreams 1 to 7 must be encoded at the same sample rate as independent substream 0. */ + uint32_t samplerate = ac3_sample_rate_table[ dec3_param->independent_info[0].fscod ]; + if( samplerate == 0 && fscod2 ) + /* The value 3 (or 0b11) of fscod2 is reserved. */ + samplerate = ac3_sample_rate_table[ *fscod2 ] / 2; + if( samplerate != 0 ) + *frequency = samplerate; + else + lsmash_log( NULL, LSMASH_LOG_WARNING, "Unknown sampling rate is detected.\n" ); +} + +#if 0 +/* FIXME: though this table is for EAC-3 in QTFF, we don't support it yet since the structure of CODEC specific info is unknown. */ +static void eac3_update_channel_layout +( + lsmash_channel_layout_tag *layout_tag, + lsmash_eac3_substream_info_t *independent_info +) +{ + if( independent_info->chan_loc == 0 ) + { + *layout_tag = ac3_channel_layout_table[ independent_info->acmod ][ independent_info->lfeon ]; + return; + } + else if( independent_info->acmod != 0x7 ) + { + *layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN; + return; + } + /* OK. All L, C, R, Ls and Rs exsist. */ + if( !independent_info->lfeon ) + { + if( independent_info->chan_loc == 0x80 ) + *layout_tag = QT_CHANNEL_LAYOUT_EAC_7_0_A; + else if( independent_info->chan_loc == 0x40 ) + *layout_tag = QT_CHANNEL_LAYOUT_EAC_6_0_A; + else + *layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN; + return; + } + /* Also LFE exsists. */ + static const struct + { + uint16_t chan_loc; + lsmash_channel_layout_tag tag; + } eac3_channel_layout_table[] + = { + { 0x100, QT_CHANNEL_LAYOUT_EAC3_7_1_B }, + { 0x80, QT_CHANNEL_LAYOUT_EAC3_7_1_A }, + { 0x40, QT_CHANNEL_LAYOUT_EAC3_6_1_A }, + { 0x20, QT_CHANNEL_LAYOUT_EAC3_6_1_B }, + { 0x10, QT_CHANNEL_LAYOUT_EAC3_7_1_C }, + { 0x10, QT_CHANNEL_LAYOUT_EAC3_7_1_D }, + { 0x4, QT_CHANNEL_LAYOUT_EAC3_7_1_E }, + { 0x2, QT_CHANNEL_LAYOUT_EAC3_6_1_C }, + { 0x60, QT_CHANNEL_LAYOUT_EAC3_7_1_F }, + { 0x42, QT_CHANNEL_LAYOUT_EAC3_7_1_G }, + { 0x22, QT_CHANNEL_LAYOUT_EAC3_7_1_H }, + { 0 } + }; + for( int i = 0; eac3_channel_layout_table[i].chan_loc; i++ ) + if( independent_info->chan_loc == eac3_channel_layout_table[i].chan_loc ) + { + *layout_tag = eac3_channel_layout_table[i].tag; + return; + } + *layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN; +} +#endif + +void eac3_update_channel_count +( + uint32_t *channels, + lsmash_eac3_specific_parameters_t *dec3_param +) +{ + /* The default programme selection should always be Programme 1. + * Thus, pick the number of channels of Programme 1. */ + lsmash_eac3_substream_info_t *independent_info = &dec3_param->independent_info[0]; + *channels = ac3_channel_count_table[ independent_info->acmod ] /* L/C/R/Ls/Rs combination */ + + 2 * !!(independent_info->chan_loc & 0x100) /* Lc/Rc pair */ + + 2 * !!(independent_info->chan_loc & 0x80) /* Lrs/Rrs pair */ + + !!(independent_info->chan_loc & 0x40) /* Cs */ + + !!(independent_info->chan_loc & 0x20) /* Ts */ + + 2 * !!(independent_info->chan_loc & 0x10) /* Lsd/Rsd pair */ + + 2 * !!(independent_info->chan_loc & 0x8) /* Lw/Rw pair */ + + 2 * !!(independent_info->chan_loc & 0x4) /* Lvh/Rvh pair */ + + !!(independent_info->chan_loc & 0x2) /* Cvh */ + + !!(independent_info->chan_loc & 0x1) /* LFE2 */ + + independent_info->lfeon; /* LFE */ +} + +int eac3_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: EC3 Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + if( box->size < EAC3_SPECIFIC_BOX_MIN_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *data = box->binary; + isom_skip_box_common( &data ); + lsmash_ifprintf( fp, indent, "data_rate = %"PRIu16" kbit/s\n", (data[0] << 5) | ((data[1] >> 3) & 0x1F) ); + uint8_t num_ind_sub = data[1] & 0x07; + lsmash_ifprintf( fp, indent, "num_ind_sub = %"PRIu8"\n", num_ind_sub ); + data += 2; + for( int i = 0; i <= num_ind_sub; i++ ) + { + lsmash_ifprintf( fp, indent, "independent_substream[%d]\n", i ); + int sub_indent = indent + 1; + uint8_t fscod = (data[0] >> 6) & 0x03; + uint8_t bsid = (data[0] >> 1) & 0x1F; + uint8_t bsmod = ((data[0] & 0x01) << 4) | ((data[1] >> 4) & 0x0F); + uint8_t acmod = (data[1] >> 1) & 0x07; + uint8_t lfeon = data[1] & 0x01; + uint8_t num_dep_sub = (data[2] >> 1) & 0x0F; + if( fscod != 0x03 ) + lsmash_ifprintf( fp, sub_indent, "fscod = %"PRIu8" (%"PRIu32" Hz)\n", fscod, ac3_sample_rate_table[fscod] ); + else + lsmash_ifprintf( fp, sub_indent, "fscod = 0x03 (reduced sample rate)\n" ); + lsmash_ifprintf( fp, sub_indent, "bsid = %"PRIu8"\n", bsid ); + if( bsmod < 0x08 ) + lsmash_ifprintf( fp, sub_indent, "bsmod = %"PRIu8" (%s)\n", bsmod, bit_stream_mode[bsmod + (acmod == 0x01 ? 1 : acmod > 0x01 ? 2 : 0)] ); + else + lsmash_ifprintf( fp, sub_indent, "bsmod = %"PRIu8" (Undefined service)\n" ); + lsmash_ifprintf( fp, sub_indent, "acmod = %"PRIu8" (%s)\n", acmod, audio_coding_mode[acmod + (bsmod == 0x07 ? 8 : 0)] ); + lsmash_ifprintf( fp, sub_indent, "lfeon = %s\n", lfeon ? "1 (LFE)" : "0" ); + lsmash_ifprintf( fp, sub_indent, "num_dep_sub = %"PRIu8"\n", num_dep_sub ); + data += 3; + if( num_dep_sub > 0 ) + { + static const char *channel_location[] = + { + "LFE2", + "Cvh", + "Lvh/Rvh pair", + "Lw/Rw pair", + "Lsd/Rsd pair", + "Ts", + "Cs", + "Lrs/Rrs pair", + "Lc/Rc pair" + }; + uint16_t chan_loc = ((data[-1] & 0x01) << 8) | data[0]; + lsmash_ifprintf( fp, sub_indent, "chan_loc = 0x%04"PRIx16"\n", chan_loc ); + for( int j = 0; j < 9; j++ ) + if( (chan_loc >> j & 0x01) ) + lsmash_ifprintf( fp, sub_indent + 1, "%s\n", channel_location[j] ); + data += 1; + } + else + lsmash_ifprintf( fp, sub_indent, "reserved = %"PRIu8"\n", data[2] & 0x01 ); + } + return 0; +} + +#undef EAC3_SPECIFIC_BOX_MIN_LENGTH diff -Nru l-smash-1.9.1/codecs/a52.h l-smash-2.3.0/codecs/a52.h --- l-smash-1.9.1/codecs/a52.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/a52.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,94 @@ +/***************************************************************************** + * a52.h: + ***************************************************************************** + * Copyright (C) 2012-2014 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. */ + +#define AC3_MIN_SYNCFRAME_LENGTH 128 +#define AC3_MAX_SYNCFRAME_LENGTH 3840 +#define EAC3_MAX_SYNCFRAME_LENGTH 4096 + +typedef struct +{ + lsmash_ac3_specific_parameters_t dac3_param; + lsmash_bits_t *bits; +} ac3_info_t; + +typedef struct +{ + lsmash_eac3_specific_parameters_t dec3_param; + lsmash_eac3_substream_info_t independent_info[8]; + lsmash_eac3_substream_info_t dependent_info; + uint8_t dec3_param_initialized; + uint8_t strmtyp; + uint8_t substreamid; + uint8_t current_independent_substream_id; + uint8_t fscod2; + uint8_t numblkscod; + uint8_t number_of_audio_blocks; + uint8_t number_of_independent_substreams; + uint32_t syncframe_count; + uint32_t frame_size; + lsmash_bits_t *bits; +} eac3_info_t; + +static const uint32_t ac3_sample_rate_table [4] = { 48000, 44100, 32000, 0 }; +static const uint32_t ac3_channel_count_table[8] = { 2, 1, 2, 3, 3, 4, 4, 5 }; +static const uint8_t eac3_audio_block_table [4] = { 1, 2, 3, 6 }; + +static inline uint32_t ac3_get_channel_count +( + lsmash_ac3_specific_parameters_t *dac3_param +) +{ + return ac3_channel_count_table[ dac3_param->acmod ] + dac3_param->lfeon; +} + +uint32_t ac3_get_sample_rate +( + lsmash_ac3_specific_parameters_t *dac3_param +); + +int ac3_parse_syncframe_header +( + ac3_info_t *info +); + +int eac3_parse_syncframe +( + eac3_info_t *info +); + +void eac3_update_specific_param +( + eac3_info_t *info +); + +void eac3_update_sample_rate +( + uint32_t *frequency, + lsmash_eac3_specific_parameters_t *dec3_param, + uint8_t *fscod2 +); + +void eac3_update_channel_count +( + uint32_t *channels, + lsmash_eac3_specific_parameters_t *dec3_param +); diff -Nru l-smash-1.9.1/codecs/alac.c l-smash-2.3.0/codecs/alac.c --- l-smash-1.9.1/codecs/alac.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/alac.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,110 @@ +/***************************************************************************** + * alac.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +#define ALAC_SPECIFIC_BOX_LENGTH 36 + +uint8_t *lsmash_create_alac_specific_info( lsmash_alac_specific_parameters_t *param, uint32_t *data_length ) +{ + uint8_t buffer[ALAC_SPECIFIC_BOX_LENGTH]; + lsmash_bs_t bs = { 0 }; + bs.buffer.data = buffer; + bs.buffer.alloc = ALAC_SPECIFIC_BOX_LENGTH; + lsmash_bs_put_be32( &bs, ALAC_SPECIFIC_BOX_LENGTH ); /* box size */ + lsmash_bs_put_be32( &bs, ISOM_BOX_TYPE_ALAC.fourcc ); /* box type: 'alac' */ + lsmash_bs_put_be32( &bs, 0 ); /* version + flags */ + lsmash_bs_put_be32( &bs, param->frameLength ); + lsmash_bs_put_byte( &bs, 0 ); /* compatibleVersion */ + lsmash_bs_put_byte( &bs, param->bitDepth ); + lsmash_bs_put_byte( &bs, 40 ); /* pb */ + lsmash_bs_put_byte( &bs, 14 ); /* mb */ + lsmash_bs_put_byte( &bs, 10 ); /* kb */ + lsmash_bs_put_byte( &bs, param->numChannels ); + lsmash_bs_put_be16( &bs, 255 ); /* maxRun */ + lsmash_bs_put_be32( &bs, param->maxFrameBytes ); + lsmash_bs_put_be32( &bs, param->avgBitrate ); + lsmash_bs_put_be32( &bs, param->sampleRate ); + return lsmash_bs_export_data( &bs, data_length ); +} + +int alac_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < ALAC_SPECIFIC_BOX_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + lsmash_alac_specific_parameters_t *param = (lsmash_alac_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + data += 4; /* Skip version and flags. */ + param->frameLength = LSMASH_GET_BE32( &data[0] ); + param->bitDepth = LSMASH_GET_BYTE( &data[5] ); + param->numChannels = LSMASH_GET_BYTE( &data[9] ); + param->maxFrameBytes = LSMASH_GET_BE32( &data[12] ); + param->avgBitrate = LSMASH_GET_BE32( &data[16] ); + param->sampleRate = LSMASH_GET_BE32( &data[20] ); + return 0; +} + +int alac_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: ALAC Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + if( box->size < ALAC_SPECIFIC_BOX_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *data = box->binary; + isom_skip_box_common( &data ); + lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", LSMASH_GET_BYTE( &data[0] ) ); + lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", LSMASH_GET_BE24( &data[1] ) ); + data += 4; + lsmash_ifprintf( fp, indent, "frameLength = %"PRIu32"\n", LSMASH_GET_BE32( &data[0] ) ); + lsmash_ifprintf( fp, indent, "compatibleVersion = %"PRIu8"\n", LSMASH_GET_BYTE( &data[4] ) ); + lsmash_ifprintf( fp, indent, "bitDepth = %"PRIu8"\n", LSMASH_GET_BYTE( &data[5] ) ); + lsmash_ifprintf( fp, indent, "pb = %"PRIu8"\n", LSMASH_GET_BYTE( &data[6] ) ); + lsmash_ifprintf( fp, indent, "mb = %"PRIu8"\n", LSMASH_GET_BYTE( &data[7] ) ); + lsmash_ifprintf( fp, indent, "kb = %"PRIu8"\n", LSMASH_GET_BYTE( &data[8] ) ); + lsmash_ifprintf( fp, indent, "numChannels = %"PRIu8"\n", LSMASH_GET_BYTE( &data[9] ) ); + lsmash_ifprintf( fp, indent, "maxRun = %"PRIu16"\n", LSMASH_GET_BE16( &data[10] ) ); + lsmash_ifprintf( fp, indent, "maxFrameBytes = %"PRIu32"\n", LSMASH_GET_BE32( &data[12] ) ); + lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32"\n", LSMASH_GET_BE32( &data[16] ) ); + lsmash_ifprintf( fp, indent, "sampleRate = %"PRIu32"\n", LSMASH_GET_BE32( &data[20] ) ); + return 0; +} + +#undef ALAC_SPECIFIC_BOX_LENGTH diff -Nru l-smash-1.9.1/codecs/description.c l-smash-2.3.0/codecs/description.c --- l-smash-1.9.1/codecs/description.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/description.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,3006 @@ +/***************************************************************************** + * description.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include + +#include "core/box.h" + +#include "a52.h" +#include "mp4a.h" +#include "mp4sys.h" +#include "description.h" + +typedef isom_wave_t lsmash_qt_decoder_parameters_t; + +static void global_destruct_specific_data( void *data ) +{ + if( !data ) + return; + lsmash_codec_global_header_t *global = (lsmash_codec_global_header_t *)data; + lsmash_free( global->header_data ); + lsmash_free( global ); +} + +static int isom_is_qt_video( lsmash_codec_type_t type ) +{ + return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_2VUY_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCH_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCN_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCS_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCO_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_AP4H_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CFHD_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CIVD_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVC_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVCP_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVPP_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DV5N_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DV5P_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH2_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH3_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH5_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH6_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVHP_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVHQ_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DV10_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVOO_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVOR_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVTV_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVVT_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FLIC_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_GIF_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_H261_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_H263_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_HD10_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_JPEG_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_M105_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MJPA_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MJPB_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_PNG_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_PNTG_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RAW_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RLE_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RPZA_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR0_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR1_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR2_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR3_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR4_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SVQ1_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SVQ3_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TGA_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TIFF_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULRA_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULRG_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULY2_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULY0_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULH2_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULH0_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V210_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V216_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V308_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V408_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V410_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_YUV2_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_WRLE_VIDEO ); +} + +static int isom_is_nalff( lsmash_codec_type_t type ) +{ + return lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC1_VIDEO ) + || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC2_VIDEO ) + || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC3_VIDEO ) + || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC4_VIDEO ) + || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVCP_VIDEO ) + || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_HVC1_VIDEO ) + || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_HEV1_VIDEO ); +} + +int lsmash_convert_crop_into_clap( lsmash_crop_t crop, uint32_t width, uint32_t height, lsmash_clap_t *clap ) +{ + if( !clap || crop.top.d == 0 || crop.bottom.d == 0 || crop.left.d == 0 || crop.right.d == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + uint64_t vertical_crop_lcm = lsmash_get_lcm( crop.top.d, crop.bottom.d ); + uint64_t horizontal_crop_lcm = lsmash_get_lcm( crop.left.d, crop.right.d ); + lsmash_rational_u64_t clap_height; + lsmash_rational_u64_t clap_width; + lsmash_rational_s64_t clap_horizontal_offset; + lsmash_rational_s64_t clap_vertical_offset; + clap_height.d = vertical_crop_lcm; + clap_width.d = horizontal_crop_lcm; + clap_horizontal_offset.d = 2 * vertical_crop_lcm; + clap_vertical_offset.d = 2 * horizontal_crop_lcm; + clap_height.n = height * vertical_crop_lcm + - (crop.top.n * (vertical_crop_lcm / crop.top.d) + crop.bottom.n * (vertical_crop_lcm / crop.bottom.d)); + clap_width.n = width * horizontal_crop_lcm + - (crop.left.n * (horizontal_crop_lcm / crop.left.d) + crop.right.n * (horizontal_crop_lcm / crop.right.d)); + clap_horizontal_offset.n = (int64_t)(crop.left.n * (horizontal_crop_lcm / crop.left.d)) + - crop.right.n * (horizontal_crop_lcm / crop.right.d); + clap_vertical_offset.n = (int64_t)(crop.top.n * (vertical_crop_lcm / crop.top.d)) + - crop.bottom.n * (vertical_crop_lcm / crop.bottom.d); + lsmash_reduce_fraction( &clap_height.n, &clap_height.d ); + lsmash_reduce_fraction( &clap_width.n, &clap_width.d ); + lsmash_reduce_fraction_su( &clap_vertical_offset.n, &clap_vertical_offset.d ); + lsmash_reduce_fraction_su( &clap_horizontal_offset.n, &clap_horizontal_offset.d ); + clap->height = (lsmash_rational_u32_t){ clap_height.n, clap_height.d }; + clap->width = (lsmash_rational_u32_t){ clap_width.n, clap_width.d }; + clap->vertical_offset = (lsmash_rational_s32_t){ clap_vertical_offset.n, clap_vertical_offset.d }; + clap->horizontal_offset = (lsmash_rational_s32_t){ clap_horizontal_offset.n, clap_horizontal_offset.d }; + return 0; +} + +int lsmash_convert_clap_into_crop( lsmash_clap_t clap, uint32_t width, uint32_t height, lsmash_crop_t *crop ) +{ + if( !crop || clap.height.d == 0 || clap.vertical_offset.d == 0 || clap.width.d == 0 || clap.horizontal_offset.d == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + uint64_t clap_vertical_lcm = lsmash_get_lcm( clap.height.d, clap.vertical_offset.d ); + uint64_t clap_horizontal_lcm = lsmash_get_lcm( clap.width.d, clap.horizontal_offset.d ); + lsmash_rational_u64_t crop_top; + lsmash_rational_u64_t crop_bottom; + lsmash_rational_u64_t crop_left; + lsmash_rational_u64_t crop_right; + crop_top.d = 2 * clap_vertical_lcm; + crop_bottom.d = 2 * clap_vertical_lcm; + crop_left.d = 2 * clap_horizontal_lcm; + crop_right.d = 2 * clap_horizontal_lcm; + crop_top.n = (height * crop_top.d - clap.height.n * (crop_top.d / clap.height.d)) / 2 + + clap.vertical_offset.n * (crop_top.d / clap.vertical_offset.d); + crop_bottom.n = (height * crop_bottom.d - clap.height.n * (crop_bottom.d / clap.height.d)) / 2 + - clap.vertical_offset.n * (crop_bottom.d / clap.vertical_offset.d); + crop_left.n = (width * crop_left.d - clap.width.n * (crop_left.d / clap.width.d)) / 2 + + clap.horizontal_offset.n * (crop_left.d / clap.horizontal_offset.d); + crop_right.n = (width * crop_right.d - clap.width.n * (crop_right.d / clap.width.d)) / 2 + - clap.horizontal_offset.n * (crop_right.d / clap.horizontal_offset.d); + lsmash_reduce_fraction( &crop_top.n, &crop_top.d ); + lsmash_reduce_fraction( &crop_bottom.n, &crop_bottom.d ); + lsmash_reduce_fraction( &crop_left.n, &crop_left.d ); + lsmash_reduce_fraction( &crop_right.n, &crop_right.d ); + crop->top = (lsmash_rational_u32_t){ crop_top.n, crop_top.d }; + crop->bottom = (lsmash_rational_u32_t){ crop_bottom.n, crop_bottom.d }; + crop->left = (lsmash_rational_u32_t){ crop_left.n, crop_left.d }; + crop->right = (lsmash_rational_u32_t){ crop_right.n, crop_right.d }; + return 0; +} + +static void isom_destruct_nothing( void *data ) +{ + /* Do nothing. */; +} + +static int isom_initialize_structured_codec_specific_data( lsmash_codec_specific_t *specific ) +{ + extern void mp4sys_destruct_decoder_config( void * ); + extern void h264_destruct_specific_data( void * ); + extern void hevc_destruct_specific_data( void * ); + extern void vc1_destruct_specific_data( void * ); + extern void dts_destruct_specific_data( void * ); + switch( specific->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : + specific->size = sizeof(lsmash_mp4sys_decoder_parameters_t); + specific->destruct = mp4sys_destruct_decoder_config; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 : + specific->size = sizeof(lsmash_h264_specific_parameters_t); + specific->destruct = h264_destruct_specific_data; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC : + specific->size = sizeof(lsmash_hevc_specific_parameters_t); + specific->destruct = hevc_destruct_specific_data; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 : + specific->size = sizeof(lsmash_vc1_specific_parameters_t); + specific->destruct = vc1_destruct_specific_data; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : + specific->size = sizeof(lsmash_ac3_specific_parameters_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : + specific->size = sizeof(lsmash_eac3_specific_parameters_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : + specific->size = sizeof(lsmash_dts_specific_parameters_t); + specific->destruct = dts_destruct_specific_data; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : + specific->size = sizeof(lsmash_alac_specific_parameters_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE : + specific->size = sizeof(lsmash_isom_sample_scale_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE : + specific->size = sizeof(lsmash_h264_bitrate_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON : + specific->size = sizeof(lsmash_qt_video_common_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : + specific->size = sizeof(lsmash_qt_audio_common_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : + specific->size = sizeof(lsmash_qt_audio_format_specific_flags_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : + specific->size = sizeof(lsmash_codec_global_header_t); + specific->destruct = global_destruct_specific_data; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO : + specific->size = sizeof(lsmash_qt_field_info_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT : + specific->size = sizeof(lsmash_qt_pixel_format_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS : + specific->size = sizeof(lsmash_qt_significant_bits_t); + specific->destruct = lsmash_free; + break; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : + specific->size = sizeof(lsmash_qt_audio_channel_layout_t); + specific->destruct = lsmash_free; + break; + default : + specific->size = 0; + specific->destruct = isom_destruct_nothing; + return 0; + } + specific->data.structured = lsmash_malloc_zero( specific->size ); + if( !specific->data.structured ) + { + specific->size = 0; + specific->destruct = NULL; + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static inline int isom_initialize_codec_specific_data( lsmash_codec_specific_t *specific, + lsmash_codec_specific_data_type type, + lsmash_codec_specific_format format ) +{ + specific->type = type; + specific->format = format; + if( format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + int err = isom_initialize_structured_codec_specific_data( specific ); + if( err < 0 ) + return err; + } + else + { + specific->data.unstructured = NULL; + specific->size = 0; + specific->destruct = (lsmash_codec_specific_destructor_t)lsmash_free; + } + return 0; +} + +void lsmash_destroy_codec_specific_data( lsmash_codec_specific_t *specific ) +{ + if( !specific ) + return; + if( specific->destruct ) + { + if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + if( specific->data.structured ) + specific->destruct( specific->data.structured ); + } + else + { + if( specific->data.unstructured ) + specific->destruct( specific->data.unstructured ); + } + } + lsmash_free( specific ); +} + +lsmash_codec_specific_t *lsmash_create_codec_specific_data( lsmash_codec_specific_data_type type, lsmash_codec_specific_format format ) +{ + lsmash_codec_specific_t *specific = lsmash_malloc( sizeof(lsmash_codec_specific_t) ); + if( !specific ) + return NULL; + if( isom_initialize_codec_specific_data( specific, type, format ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + return NULL; + } + return specific; +} + +static int isom_duplicate_structured_specific_data( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + extern int mp4sys_copy_decoder_config( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int h264_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int hevc_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int vc1_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int dts_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + void *src_data = src->data.structured; + void *dst_data = dst->data.structured; + switch( src->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : + return mp4sys_copy_decoder_config( dst, src ); + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 : + return h264_copy_codec_specific( dst, src ); + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC : + return hevc_copy_codec_specific( dst, src ); + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 : + return vc1_copy_codec_specific( dst, src ); + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : + *(lsmash_ac3_specific_parameters_t *)dst_data = *(lsmash_ac3_specific_parameters_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : + *(lsmash_eac3_specific_parameters_t *)dst_data = *(lsmash_eac3_specific_parameters_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : + return dts_copy_codec_specific( dst, src ); + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : + *(lsmash_alac_specific_parameters_t *)dst_data = *(lsmash_alac_specific_parameters_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE : + *(lsmash_isom_sample_scale_t *)dst_data = *(lsmash_isom_sample_scale_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE : + *(lsmash_h264_bitrate_t *)dst_data = *(lsmash_h264_bitrate_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON : + *(lsmash_qt_video_common_t *)dst_data = *(lsmash_qt_video_common_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : + *(lsmash_qt_audio_common_t *)dst_data = *(lsmash_qt_audio_common_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : + *(lsmash_qt_audio_format_specific_flags_t *)dst_data = *(lsmash_qt_audio_format_specific_flags_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : + { + lsmash_codec_global_header_t *src_global = (lsmash_codec_global_header_t *)src_data; + if( src_global->header_data && src_global->header_size ) + { + lsmash_codec_global_header_t *dst_global = (lsmash_codec_global_header_t *)dst_data; + dst_global->header_data = lsmash_memdup( src_global->header_data, src_global->header_size ); + if( !dst_global->header_data ) + return LSMASH_ERR_MEMORY_ALLOC; + dst_global->header_size = src_global->header_size; + } + return 0; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO : + *(lsmash_qt_field_info_t *)dst_data = *(lsmash_qt_field_info_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT : + *(lsmash_qt_pixel_format_t *)dst_data = *(lsmash_qt_pixel_format_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS : + *(lsmash_qt_significant_bits_t *)dst_data = *(lsmash_qt_significant_bits_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_GAMMA_LEVEL : + *(lsmash_qt_gamma_t *)dst_data = *(lsmash_qt_gamma_t *)src_data; + return 0; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : + *(lsmash_qt_audio_channel_layout_t *)dst_data = *(lsmash_qt_audio_channel_layout_t *)src_data; + return 0; + default : + return LSMASH_ERR_NAMELESS; + } +} + +lsmash_codec_specific_t *isom_duplicate_codec_specific_data( lsmash_codec_specific_t *specific ) +{ + if( !specific ) + return NULL; + lsmash_codec_specific_t *dup = lsmash_create_codec_specific_data( specific->type, specific->format ); + if( !dup ) + return NULL; + if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + if( isom_duplicate_structured_specific_data( dup, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( dup ); + return NULL; + } + } + else + { + dup->data.unstructured = lsmash_memdup( specific->data.unstructured, specific->size ); + if( !dup->data.unstructured ) + { + lsmash_destroy_codec_specific_data( dup ); + return NULL; + } + } + dup->size = specific->size; + return dup; +} + +static size_t isom_description_read_box_common( uint8_t **p_data, uint64_t *size, lsmash_box_type_t *type ) +{ + uint8_t *orig = *p_data; + uint8_t *data = *p_data; + *size = LSMASH_GET_BE32( &data[0] ); + type->fourcc = LSMASH_GET_BE32( &data[4] ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( *size == 1 ) + { + *size = LSMASH_GET_BE64( data ); + data += 8; + } + *p_data = data; + if( type->fourcc == ISOM_BOX_TYPE_UUID.fourcc ) + { + type->user.fourcc = LSMASH_GET_BE32( &data[0] ); + memcpy( type->user.id, &data[4], 12 ); + } + return data - orig; +} + +uint8_t *isom_get_child_box_position( uint8_t *parent_data, uint32_t parent_size, lsmash_box_type_t child_type, uint32_t *child_size ) +{ + if( !parent_data || !child_size || parent_size < ISOM_BASEBOX_COMMON_SIZE ) + return NULL; + uint8_t *data = parent_data; + uint64_t size; + lsmash_box_type_t type; + uint32_t offset = isom_description_read_box_common( &data, &size, &type ); + if( size != parent_size ) + return NULL; + uint8_t *end = parent_data + parent_size; + for( uint8_t *pos = data; pos + ISOM_BASEBOX_COMMON_SIZE <= end; ) + { + offset = isom_description_read_box_common( &pos, &size, &type ); + if( lsmash_check_box_type_identical( type, child_type ) ) + { + *child_size = size; + return pos - offset; + } + pos += size - offset; /* Move to the next box. */ + } + return NULL; +} + +static int isom_construct_global_specific_header( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + if( src->size < ISOM_BASEBOX_COMMON_SIZE ) + return LSMASH_ERR_INVALID_DATA; + lsmash_codec_global_header_t *global = (lsmash_codec_global_header_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + global->header_size = size - ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + global->header_size -= 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + if( global->header_size ) + { + global->header_data = lsmash_memdup( data, global->header_size ); + if( !global->header_data ) + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_construct_audio_channel_layout( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + if( src->size < ISOM_FULLBOX_COMMON_SIZE + 12 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_qt_audio_channel_layout_t *layout = (lsmash_qt_audio_channel_layout_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_FULLBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + layout->channelLayoutTag = LSMASH_GET_BE32( &data[0] ); + layout->channelBitmap = LSMASH_GET_BE32( &data[4] ); + return 0; +} + +#if 0 +static int codec_construct_qt_audio_decompression_info( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + if( src->size < ISOM_BASEBOX_COMMON_SIZE ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *data = src->data.unstructured; + uint64_t size; + uint32_t type; + uint32_t offset = isom_description_read_box_common( &data, &size, &type ); + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *end = src->data.unstructured + src->size; + isom_wave_t *wave = lsmash_malloc_zero( sizeof(isom_wave_t) ); + if( !wave ) + return LSMASH_ERR_MEMORY_ALLOC; + wave->type = QT_BOX_TYPE_WAVE; + for( uint8_t *pos = data; pos + ISOM_BASEBOX_COMMON_SIZE <= end; ) + { + offset = isom_description_read_box_common( &pos, &size, &type ); + switch( type ) + { + case QT_BOX_TYPE_FRMA : + { + if( pos + 4 > end ) + return LSMASH_ERR_INVALID_DATA; + isom_frma_t *frma = isom_add_frma( wave ); + if( !frma ) + return LSMASH_ERR_NAMELESS; + frma->data_format = LSMASH_GET_BE32( pos ); + pos += 4; + break; + } + case QT_BOX_TYPE_ENDA : + { + if( pos + 2 > end ) + return LSMASH_ERR_INVALID_DATA; + isom_enda_t *enda = isom_add_enda( wave ); + if( !enda ) + return LSMASH_ERR_NAMELESS; + enda->littleEndian = LSMASH_GET_BE16( pos ); + break; + } + case QT_BOX_TYPE_MP4A : + { + if( pos + 4 > end ) + return LSMASH_ERR_INVALID_DATA; + isom_mp4a_t *mp4a = isom_add_mp4a( wave ); + if( !mp4a ) + return LSMASH_ERR_NAMELESS; + mp4a->unknown = LSMASH_GET_BE32( pos ); + pos += 4; + break; + } + case QT_BOX_TYPE_TERMINATOR : + { + if( !isom_add_terminator( wave ) ) + return LSMASH_ERR_NAMELESS; + break; + } + default : + { + isom_unknown_box_t *box = lsmash_malloc_zero( sizeof(isom_unknown_box_t) ); + if( !box ) + return LSMASH_ERR_MEMORY_ALLOC; + isom_init_box_common( box, wave, type, isom_remove_unknown_box ); + box->unknown_size = size - offset; + box->unknown_field = lsmash_memdup( pos, box->unknown_size ); + if( !box->unknown_field ) + { + lsmash_free( box ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( lsmash_add_entry( &wave->extensions, box ) < 0 ) + { + isom_remove_unknown_box( box ); + return LSMASH_ERR_MEMORY_ALLOC; + } + pos += box->unknown_size; + break; + } + } + } + return 0; +} +#endif + +/* structured <-> unstructured conversion might be irreversible by CODEC + * since structured formats we defined don't always have all contents included in unstructured data. */ +lsmash_codec_specific_t *lsmash_convert_codec_specific_format( lsmash_codec_specific_t *specific, lsmash_codec_specific_format format ) +{ + if( !specific || format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSPECIFIED ) + return NULL; + if( format == specific->format ) + return isom_duplicate_codec_specific_data( specific ); + lsmash_codec_specific_t *dst = lsmash_create_codec_specific_data( specific->type, format ); + if( format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) + /* structured -> unstructured */ + switch( specific->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : + dst->data.unstructured = lsmash_create_mp4sys_decoder_config( (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 : + dst->data.unstructured = lsmash_create_h264_specific_info( (lsmash_h264_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC : + dst->data.unstructured = lsmash_create_hevc_specific_info( (lsmash_hevc_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 : + dst->data.unstructured = lsmash_create_vc1_specific_info( (lsmash_vc1_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : + dst->data.unstructured = lsmash_create_ac3_specific_info( (lsmash_ac3_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : + dst->data.unstructured = lsmash_create_eac3_specific_info( (lsmash_eac3_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : + dst->data.unstructured = lsmash_create_dts_specific_info( (lsmash_dts_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : + dst->data.unstructured = lsmash_create_alac_specific_info( (lsmash_alac_specific_parameters_t *)specific->data.structured, &dst->size ); + if( !dst->data.unstructured ) + goto fail; + return dst; + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : + { + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + goto fail; + lsmash_codec_global_header_t *global = specific->data.structured; + lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE + global->header_size ); + lsmash_bs_put_be32( bs, QT_BOX_TYPE_GLBL.fourcc ); + lsmash_bs_put_bytes( bs, global->header_size, global->header_data ); + dst->data.unstructured = lsmash_bs_export_data( bs, &dst->size ); + lsmash_bs_cleanup( bs ); + if( !dst->data.unstructured || dst->size != (ISOM_BASEBOX_COMMON_SIZE + global->header_size) ) + goto fail; + return dst; + } + default : + break; + } + else if( format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + /* unstructured -> structured */ + extern int mp4sys_construct_decoder_config( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int h264_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int hevc_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int vc1_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int ac3_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int eac3_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int dts_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + extern int alac_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + static const struct + { + lsmash_codec_specific_data_type data_type; + int (*constructor)( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); + } codec_specific_format_constructor_table[] = + { + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, mp4sys_construct_decoder_config }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264, h264_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC, hevc_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1, vc1_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3, ac3_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3, eac3_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS, dts_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC, alac_construct_specific_parameters }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER, isom_construct_global_specific_header }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT, isom_construct_audio_channel_layout }, + { LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN, NULL } + }; + int (*constructor)( lsmash_codec_specific_t *, lsmash_codec_specific_t * ) = NULL; + for( int i = 0; codec_specific_format_constructor_table[i].constructor; i++ ) + if( specific->type == codec_specific_format_constructor_table[i].data_type ) + { + constructor = codec_specific_format_constructor_table[i].constructor; + break; + } + if( constructor && !constructor( dst, specific ) ) + return dst; + } +fail: + lsmash_destroy_codec_specific_data( dst ); + return NULL; +} + +static inline void isom_set_default_compressorname( char *compressorname, lsmash_codec_type_t sample_type ) +{ + static struct compressorname_table_tag + { + lsmash_codec_type_t type; + char name[33]; + } compressorname_table[32] = { { LSMASH_CODEC_TYPE_INITIALIZER, { '\0' } } }; + if( compressorname_table[0].name[0] == '\0' ) + { + int i = 0; +#define ADD_COMPRESSORNAME_TABLE( type, name ) compressorname_table[i++] = (struct compressorname_table_tag){ type, name } + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC1_VIDEO, "\012AVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC2_VIDEO, "\012AVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC3_VIDEO, "\012AVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC4_VIDEO, "\012AVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVCP_VIDEO, "\016AVC Parameters" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_HVC1_VIDEO, "\013HEVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_HEV1_VIDEO, "\013HEVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_SVC1_VIDEO, "\012SVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_MVC1_VIDEO, "\012MVC Coding" ); + ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_MVC2_VIDEO, "\012MVC Coding" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCH_VIDEO, "\023Apple ProRes 422 (HQ)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCN_VIDEO, "\023Apple ProRes 422 (SD)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCS_VIDEO, "\023Apple ProRes 422 (LT)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCO_VIDEO, "\026Apple ProRes 422 (Proxy)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_AP4H_VIDEO, "\019Apple ProRes 4444" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVPP_VIDEO, "\014DVCPRO - PAL" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DV5N_VIDEO, "\017DVCPRO50 - NTSC" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DV5P_VIDEO, "\016DVCPRO50 - PAL" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH2_VIDEO, "\019DVCPRO HD 1080p25" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH3_VIDEO, "\019DVCPRO HD 1080p30" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH5_VIDEO, "\019DVCPRO HD 1080i50" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH6_VIDEO, "\019DVCPRO HD 1080i60" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVHP_VIDEO, "\018DVCPRO HD 720p60" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVHQ_VIDEO, "\018DVCPRO HD 720p50" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULRA_VIDEO, "\017Ut Video (ULRA)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULRG_VIDEO, "\017Ut Video (ULRG)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULY0_VIDEO, "\017Ut Video (ULY0)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULY2_VIDEO, "\017Ut Video (ULY2)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULH0_VIDEO, "\017Ut Video (ULH0)" ); + ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULH2_VIDEO, "\017Ut Video (ULH2)" ); + ADD_COMPRESSORNAME_TABLE( LSMASH_CODEC_TYPE_UNSPECIFIED, { '\0' } ); +#undef ADD_COMPRESSORNAME_TABLE + } + for( int i = 0; compressorname_table[i].name[0] != '\0'; i++ ) + if( lsmash_check_codec_type_identical( sample_type, compressorname_table[i].type ) ) + { + strcpy( compressorname, compressorname_table[i].name ); + return; + } +} + +lsmash_codec_specific_t *isom_get_codec_specific( lsmash_codec_specific_list_t *opaque, lsmash_codec_specific_data_type type ) +{ + for( lsmash_entry_t *entry = opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; + if( !specific || specific->type != type ) + continue; + return specific; + } + return NULL; +} + +static int isom_check_valid_summary( lsmash_summary_t *summary ) +{ + if( !summary ) + return LSMASH_ERR_NAMELESS; + isom_box_t temp_box; + temp_box.type = summary->sample_type; + temp_box.manager = summary->summary_type == LSMASH_SUMMARY_TYPE_AUDIO ? LSMASH_AUDIO_DESCRIPTION: 0; + if( isom_is_lpcm_audio( &temp_box ) ) + { + if( isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS ) ) + return 0; + return LSMASH_ERR_INVALID_DATA; + } + if( isom_is_uncompressed_ycbcr( summary->sample_type ) ) + { + if( isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO ) ) + { + if( !lsmash_check_codec_type_identical( summary->sample_type, QT_CODEC_TYPE_V216_VIDEO ) ) + return 0; + } + else + return LSMASH_ERR_INVALID_DATA; + } + lsmash_codec_type_t sample_type = summary->sample_type; + lsmash_codec_specific_data_type required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNSPECIFIED; + if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HEV1_VIDEO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_VC_1_VIDEO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 ; + else if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULRA_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULRG_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULY0_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULY2_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULH0_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULH2_VIDEO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER; + else if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_V216_VIDEO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_MP4A_AUDIO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AC_3_AUDIO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS; + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) ) + required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC; + if( required_data_type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNSPECIFIED ) + return 0; + return isom_get_codec_specific( summary->opaque, required_data_type ) ? 0 : LSMASH_ERR_INVALID_DATA; +} + +static lsmash_box_type_t isom_guess_video_codec_specific_box_type( lsmash_codec_type_t active_codec_type, lsmash_compact_box_type_t fourcc ) +{ + lsmash_box_type_t box_type = LSMASH_BOX_TYPE_INITIALIZER; + box_type.fourcc = fourcc; +#define GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( codec_type, predefined_box_type ) \ + else if( (codec_type.user.fourcc == 0 \ + || lsmash_check_codec_type_identical( active_codec_type, codec_type )) \ + && box_type.fourcc == predefined_box_type.fourcc ) \ + box_type = predefined_box_type + if( 0 ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC1_VIDEO, ISOM_BOX_TYPE_AVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC2_VIDEO, ISOM_BOX_TYPE_AVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC3_VIDEO, ISOM_BOX_TYPE_AVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC4_VIDEO, ISOM_BOX_TYPE_AVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVCP_VIDEO, ISOM_BOX_TYPE_AVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_HVC1_VIDEO, ISOM_BOX_TYPE_HVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_HEV1_VIDEO, ISOM_BOX_TYPE_HVCC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_VC_1_VIDEO, ISOM_BOX_TYPE_DVC1 ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_MP4V_VIDEO, ISOM_BOX_TYPE_ESDS ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, ISOM_BOX_TYPE_BTRT ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_FIEL ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_CSPC ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_SGBT ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_GAMA ); + GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_GLBL ); +#undef GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE + return box_type; +} + +int isom_setup_visual_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type, lsmash_video_summary_t *summary ) +{ + if( !summary + || !stsd + || !stsd->parent + || !stsd->parent->parent + || !stsd->parent->parent->parent + || !stsd->parent->parent->parent->parent ) + return LSMASH_ERR_NAMELESS; + int err = isom_check_valid_summary( (lsmash_summary_t *)summary ); + if( err < 0 ) + return err; + isom_visual_entry_t *visual = isom_add_visual_description( stsd, sample_type ); + if( !visual ) + return LSMASH_ERR_NAMELESS; + visual->data_reference_index = summary->data_ref_index; + visual->version = 0; + visual->revision_level = 0; + visual->vendor = 0; + visual->temporalQuality = 0; + visual->spatialQuality = 0; + visual->width = (uint16_t)summary->width; + visual->height = (uint16_t)summary->height; + visual->horizresolution = 0x00480000; + visual->vertresolution = 0x00480000; + visual->dataSize = 0; + visual->frame_count = 1; + visual->depth = isom_is_qt_video( summary->sample_type ) || isom_is_nalff( summary->sample_type ) + ? summary->depth : 0x0018; + visual->color_table_ID = -1; + if( summary->compressorname[0] == '\0' ) + isom_set_default_compressorname( visual->compressorname, sample_type ); + else + { + memcpy( visual->compressorname, summary->compressorname, 32 ); + visual->compressorname[32] = '\0'; + } + err = LSMASH_ERR_NAMELESS; + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; + if( !specific ) + goto fail; + if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + continue; /* LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED is not supported. */ + switch( specific->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON : + { + if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) + continue; + lsmash_qt_video_common_t *data = (lsmash_qt_video_common_t *)specific->data.structured; + visual->revision_level = data->revision_level; + visual->vendor = data->vendor; + visual->temporalQuality = data->temporalQuality; + visual->spatialQuality = data->spatialQuality; + visual->horizresolution = data->horizontal_resolution; + visual->vertresolution = data->vertical_resolution; + visual->dataSize = data->dataSize; + visual->frame_count = data->frame_count; + visual->color_table_ID = data->color_table_ID; + if( data->color_table_ID == 0 ) + { + lsmash_qt_color_table_t *src_ct = &data->color_table; + uint16_t element_count = LSMASH_MIN( src_ct->size + 1, 256 ); + isom_qt_color_array_t *dst_array = lsmash_malloc_zero( element_count * sizeof(isom_qt_color_array_t) ); + if( !dst_array ) + { + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + isom_qt_color_table_t *dst_ct = &visual->color_table; + dst_ct->array = dst_array; + dst_ct->seed = src_ct->seed; + dst_ct->flags = src_ct->flags; + dst_ct->size = src_ct->size; + for( uint16_t i = 0; i < element_count; i++ ) + { + dst_array[i].value = src_ct->array[i].unused; + dst_array[i].r = src_ct->array[i].r; + dst_array[i].g = src_ct->array[i].g; + dst_array[i].b = src_ct->array[i].b; + } + } + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_isom_sample_scale_t *data = (lsmash_isom_sample_scale_t *)cs->data.structured; + isom_stsl_t *stsl = isom_add_stsl( visual ); + if( !stsl ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + stsl->constraint_flag = data->constraint_flag; + stsl->scale_method = data->scale_method; + stsl->display_center_x = data->display_center_x; + stsl->display_center_y = data->display_center_y; + lsmash_destroy_codec_specific_data( cs ); + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_h264_bitrate_t *data = (lsmash_h264_bitrate_t *)cs->data.structured; + isom_btrt_t *btrt = isom_add_btrt( visual ); + if( !btrt ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + btrt->bufferSizeDB = data->bufferSizeDB; + btrt->maxBitrate = data->maxBitrate; + btrt->avgBitrate = data->avgBitrate; + lsmash_destroy_codec_specific_data( cs ); + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_qt_field_info_t *data = (lsmash_qt_field_info_t *)cs->data.structured; + isom_fiel_t *fiel = isom_add_fiel( visual ); + if( !fiel ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + fiel->fields = data->fields; + fiel->detail = data->detail; + lsmash_destroy_codec_specific_data( cs ); + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_qt_pixel_format_t *data = (lsmash_qt_pixel_format_t *)cs->data.structured; + isom_cspc_t *cspc = isom_add_cspc( visual ); + if( !cspc ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + cspc->pixel_format = data->pixel_format; + lsmash_destroy_codec_specific_data( cs ); + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_qt_significant_bits_t *data = (lsmash_qt_significant_bits_t *)cs->data.structured; + isom_sgbt_t *sgbt = isom_add_sgbt( visual ); + if( !sgbt ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + sgbt->significantBits = data->significantBits; + lsmash_destroy_codec_specific_data( cs ); + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_GAMMA_LEVEL : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_qt_gamma_t *data = (lsmash_qt_gamma_t *)cs->data.structured; + isom_gama_t *gama = isom_add_gama( visual ); + if( !gama ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + gama->level = data->level; + lsmash_destroy_codec_specific_data( cs ); + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_codec_global_header_t *data = (lsmash_codec_global_header_t *)cs->data.structured; + isom_glbl_t *glbl = isom_add_glbl( visual ); + if( !glbl ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + glbl->header_size = data->header_size; + glbl->header_data = lsmash_memdup( data->header_data, data->header_size ); + lsmash_destroy_codec_specific_data( cs ); + if( !glbl->header_data ) + { + isom_remove_box_by_itself( glbl ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + break; + } + default : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !cs ) + goto fail; + if( cs->size < ISOM_BASEBOX_COMMON_SIZE ) + { + lsmash_destroy_codec_specific_data( cs ); + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + uint8_t *data = cs->data.unstructured; + lsmash_compact_box_type_t fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); + lsmash_box_type_t box_type = isom_guess_video_codec_specific_box_type( visual->type, fourcc ); + /* Append the extension. */ + err = isom_add_extension_binary( visual, box_type, LSMASH_BOX_PRECEDENCE_HM, cs->data.unstructured, cs->size ); + cs->data.unstructured = NULL; /* Avoid freeing the binary data of the extension. */ + lsmash_destroy_codec_specific_data( cs ); + if( err < 0 ) + goto fail; + break; + } + } + } + isom_trak_t *trak = (isom_trak_t *)visual->parent->parent->parent->parent->parent; + int qt_compatible = trak->file->qt_compatible; + isom_tapt_t *tapt = trak->tapt; + isom_stsl_t *stsl = (isom_stsl_t *)isom_get_extension_box_format( &visual->extensions, ISOM_BOX_TYPE_STSL ); + int set_aperture_modes = qt_compatible /* Track Aperture Modes is only available under QuickTime file format. */ + && (!stsl || stsl->scale_method == 0) /* 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 == 1; /* Multiple sample description might conflict with this, so in that case, disable this feature. */ + if( !set_aperture_modes ) + isom_remove_box_by_itself( trak->tapt ); + int uncompressed_ycbcr = qt_compatible && isom_is_uncompressed_ycbcr( visual->type ); + /* Set up Clean Aperture. */ + if( set_aperture_modes || uncompressed_ycbcr + || (summary->clap.width.d && summary->clap.height.d && summary->clap.horizontal_offset.d && summary->clap.vertical_offset.d) ) + { + isom_clap_t *clap = isom_add_clap( visual ); + if( !clap ) + goto fail; + if( summary->clap.width.d && summary->clap.height.d && summary->clap.horizontal_offset.d && summary->clap.vertical_offset.d ) + { + clap->cleanApertureWidthN = summary->clap.width.n; + clap->cleanApertureWidthD = summary->clap.width.d; + clap->cleanApertureHeightN = summary->clap.height.n; + clap->cleanApertureHeightD = summary->clap.height.d; + clap->horizOffN = summary->clap.horizontal_offset.n; + clap->horizOffD = summary->clap.horizontal_offset.d; + clap->vertOffN = summary->clap.vertical_offset.n; + clap->vertOffD = summary->clap.vertical_offset.d; + } + else + { + clap->cleanApertureWidthN = summary->width; + clap->cleanApertureWidthD = 1; + clap->cleanApertureHeightN = summary->height; + clap->cleanApertureHeightD = 1; + clap->horizOffN = 0; + clap->horizOffD = 1; + clap->vertOffN = 0; + clap->vertOffD = 1; + } + } + /* Set up Pixel Aspect Ratio. */ + if( set_aperture_modes || (summary->par_h && summary->par_v) ) + { + isom_pasp_t *pasp = isom_add_pasp( visual ); + if( !pasp ) + goto fail; + pasp->hSpacing = LSMASH_MAX( summary->par_h, 1 ); + pasp->vSpacing = LSMASH_MAX( summary->par_v, 1 ); + } + /* Set up Color Parameter. */ + if( uncompressed_ycbcr + || summary->color.primaries_index + || summary->color.transfer_index + || summary->color.matrix_index + || (trak->file->isom_compatible && summary->color.full_range) ) + { + isom_colr_t *colr = isom_add_colr( visual ); + if( !colr ) + goto fail; + /* Set 'nclc' to parameter type, we don't support 'prof'. */ + uint16_t primaries = summary->color.primaries_index; + uint16_t transfer = summary->color.transfer_index; + uint16_t matrix = summary->color.matrix_index; + if( qt_compatible && !trak->file->isom_compatible ) + { + colr->manager |= LSMASH_QTFF_BASE; + colr->type = QT_BOX_TYPE_COLR; + colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC; + colr->primaries_index = (primaries == 1 || primaries == 5 || primaries == 6) + ? primaries : QT_PRIMARIES_INDEX_UNSPECIFIED; + colr->transfer_function_index = (transfer == 1 || transfer == 7) + ? transfer : QT_TRANSFER_INDEX_UNSPECIFIED; + colr->matrix_index = (matrix == 1 || matrix == 6 || matrix == 7) + ? matrix : QT_MATRIX_INDEX_UNSPECIFIED; + } + else + { + colr->type = ISOM_BOX_TYPE_COLR; + colr->color_parameter_type = ISOM_COLOR_PARAMETER_TYPE_NCLX; + colr->primaries_index = (primaries == 1 || (primaries >= 4 && primaries <= 7)) + ? primaries : ISOM_PRIMARIES_INDEX_UNSPECIFIED; + colr->transfer_function_index = (transfer == 1 || (transfer >= 4 && transfer <= 8) || (transfer >= 11 && transfer <= 13)) + ? transfer : ISOM_TRANSFER_INDEX_UNSPECIFIED; + colr->matrix_index = (matrix == 1 || (matrix >= 4 && matrix <= 8)) + ? matrix : ISOM_MATRIX_INDEX_UNSPECIFIED; + colr->full_range_flag = summary->color.full_range; + } + } + /* Set up Track Apeture Modes. */ + if( set_aperture_modes ) + { + uint32_t width = visual->width << 16; + uint32_t height = visual->height << 16; + isom_clap_t *clap = (isom_clap_t *)isom_get_extension_box_format( &visual->extensions, ISOM_BOX_TYPE_CLAP ); + isom_pasp_t *pasp = (isom_pasp_t *)isom_get_extension_box_format( &visual->extensions, ISOM_BOX_TYPE_PASP ); + double clap_width = ((double)clap->cleanApertureWidthN / clap->cleanApertureWidthD) * (1<<16); + double clap_height = ((double)clap->cleanApertureHeightN / clap->cleanApertureHeightD) * (1<<16); + double par = (double)pasp->hSpacing / 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; +fail: + isom_remove_box_by_itself( visual ); + return err; +} + +static int isom_append_audio_es_descriptor_extension( isom_box_t *box, lsmash_audio_summary_t *summary ) +{ + uint32_t esds_size = 0; + uint8_t *esds_data = NULL; + lsmash_codec_specific_t *specific = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); + if( !specific ) + return LSMASH_ERR_NAMELESS; + if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) + { + esds_size = specific->size; + esds_data = lsmash_memdup( specific->data.unstructured, specific->size ); + if( !esds_data ) + return LSMASH_ERR_MEMORY_ALLOC; + } + else + { + esds_data = lsmash_create_mp4sys_decoder_config( (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured, &esds_size ); + if( !esds_data ) + return LSMASH_ERR_NAMELESS; + } + isom_esds_t *esds = isom_add_esds( box ); + if( !esds ) + { + lsmash_free( esds_data ); + return LSMASH_ERR_NAMELESS; + } + lsmash_bs_t bs = { 0 }; + bs.buffer.data = esds_data + ISOM_FULLBOX_COMMON_SIZE; + bs.buffer.alloc = esds_size - ISOM_FULLBOX_COMMON_SIZE; + bs.buffer.store = bs.buffer.alloc; + esds->ES = mp4sys_get_descriptor( &bs, NULL ); + lsmash_free( esds_data ); + if( !esds->ES ) + { + isom_remove_box_by_itself( esds ); + return LSMASH_ERR_NAMELESS; + } + return 0; +} + +static int isom_append_channel_layout_extension( lsmash_codec_specific_t *specific, void *parent, uint32_t channels ) +{ + assert( parent ); + if( isom_get_extension_box( &((isom_box_t *)parent)->extensions, QT_BOX_TYPE_CHAN ) ) + return 0; /* Audio Channel Layout Box is already present. */ + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + return LSMASH_ERR_NAMELESS; + lsmash_qt_audio_channel_layout_t *data = (lsmash_qt_audio_channel_layout_t *)cs->data.structured; + lsmash_channel_layout_tag channelLayoutTag = data->channelLayoutTag; + lsmash_channel_bitmap channelBitmap = data->channelBitmap; + if( channelLayoutTag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS /* We don't support the feature of Channel Descriptions. */ + || (channelLayoutTag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!channelBitmap || channelBitmap > QT_CHANNEL_BIT_FULL)) ) + { + channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN | channels; + channelBitmap = 0; + } + lsmash_destroy_codec_specific_data( cs ); + /* Don't create Audio Channel Layout Box if the channel layout is unknown. */ + if( (channelLayoutTag ^ QT_CHANNEL_LAYOUT_UNKNOWN) >> 16 ) + { + isom_chan_t *chan = isom_add_chan( parent ); + if( !chan ) + return LSMASH_ERR_NAMELESS; + chan->channelLayoutTag = channelLayoutTag; + chan->channelBitmap = channelBitmap; + chan->numberChannelDescriptions = 0; + chan->channelDescriptions = NULL; + } + return 0; +} + +static int isom_set_qtff_mp4a_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) +{ + isom_wave_t *wave = isom_add_wave( audio ); + isom_frma_t *frma; + if( !(frma = isom_add_frma( wave )) + || !isom_add_mp4a( wave ) + || !isom_add_terminator( wave ) ) + { + lsmash_remove_entry_tail( &audio->extensions, wave->destruct ); + return LSMASH_ERR_NAMELESS; + } + frma->data_format = audio->type.fourcc; + /* Add ES Descriptor Box. */ + int err = isom_append_audio_es_descriptor_extension( (isom_box_t *)wave, summary ); + if( err < 0 ) + return err; + /* */ + audio->type = QT_CODEC_TYPE_MP4A_AUDIO; + 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_AUDIO_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 = 2; + } + 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; + } + return 0; +} + +static int isom_set_isom_mp4a_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) +{ + if( summary->summary_type != LSMASH_SUMMARY_TYPE_AUDIO ) + return LSMASH_ERR_NAMELESS; + /* Check objectTypeIndication. */ + lsmash_mp4sys_object_type_indication objectTypeIndication = lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ); + switch( objectTypeIndication ) + { + 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 LSMASH_ERR_NAMELESS; + } + /* Add ES Descriptor Box. */ + int err = isom_append_audio_es_descriptor_extension( (isom_box_t *)audio, summary ); + if( err < 0 ) + return err; + /* 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->type = ISOM_CODEC_TYPE_MP4A_AUDIO; + audio->version = 0; + audio->revision_level = 0; + audio->vendor = 0; + audio->channelcount = 2; + audio->samplesize = 16; + audio->compression_ID = 0; + audio->packet_size = 0; + /* 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; + return 0; +} + +static int isom_set_qtff_lpcm_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) +{ + lsmash_qt_audio_format_specific_flags_t *lpcm = NULL; + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; + if( !specific ) + continue; + if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS + && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + lpcm = (lsmash_qt_audio_format_specific_flags_t *)specific->data.structured; + break; + } + } + if( !lpcm ) + return LSMASH_ERR_NAMELESS; + audio->manager |= LSMASH_QTFF_BASE; + lsmash_codec_type_t sample_type = audio->type; + /* Convert the sample type into 'lpcm' if the description doesn't match the format or version = 2 fields are needed. */ + if( (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_RAW_AUDIO ) + && (summary->sample_size != 8 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL32_AUDIO ) + && (summary->sample_size != 32 || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL64_AUDIO ) + && (summary->sample_size != 64 || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN24_AUDIO ) + && (summary->sample_size != 24 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN32_AUDIO ) + && (summary->sample_size != 32 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_23NI_AUDIO ) + && (summary->sample_size != 32 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_SOWT_AUDIO ) + && (summary->sample_size != 16 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) + && ((summary->sample_size != 16 && summary->sample_size != 8) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NONE_AUDIO ) + && ((summary->sample_size != 16 && summary->sample_size != 8) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) + || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NOT_SPECIFIED ) + && ((summary->sample_size != 16 && summary->sample_size != 8) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) + || (summary->channels > 2 || summary->frequency > UINT16_MAX || summary->sample_size % 8) ) + { + audio->type = QT_CODEC_TYPE_LPCM_AUDIO; + audio->version = 2; + } + else if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_LPCM_AUDIO ) ) + audio->version = 2; + else if( summary->sample_size > 16 + || (!lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_RAW_AUDIO ) + && !lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) + && !lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NONE_AUDIO ) + && !lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NOT_SPECIFIED )) ) + audio->version = 1; + /* Set up constBytesPerAudioPacket field. + * We use constBytesPerAudioPacket as the actual size of LPCM audio frame even when version is not 2. */ + audio->constBytesPerAudioPacket = (summary->sample_size * 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->sample_size; + audio->constLPCMFramesPerAudioPacket = 1; + audio->formatSpecificFlags = lpcm->format_flags; + if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) && summary->sample_size != 8 ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; + if( lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT ) + audio->formatSpecificFlags &= ~QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER; + if( lpcm->format_flags & QT_LPCM_FORMAT_FLAG_PACKED ) + audio->formatSpecificFlags &= ~QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH; + } + 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( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_RAW_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) ) + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED; + else + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_FIXED_COMPRESSION; + audio->samplerate = summary->frequency << 16; + audio->samplesPerPacket = 1; + audio->bytesPerPacket = summary->sample_size / 8; + audio->bytesPerFrame = audio->bytesPerPacket * summary->channels; /* sample_size field in stsz box is NOT used. */ + audio->bytesPerSample = 1 + (summary->sample_size != 8); + if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL32_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL64_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN24_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN32_AUDIO ) ) + { + isom_wave_t *wave = isom_add_wave( audio ); + isom_frma_t *frma; + isom_enda_t *enda; + if( !(frma = isom_add_frma( wave )) + || !(enda = isom_add_enda( wave )) + || !isom_add_terminator( wave ) ) + { + lsmash_remove_entry_tail( &audio->extensions, wave->destruct ); + return LSMASH_ERR_NAMELESS; + } + frma->data_format = sample_type.fourcc; + enda->littleEndian = !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN); + } + } + else /* audio->version == 0 */ + { + audio->channelcount = summary->channels; + audio->samplesize = summary->sample_size; + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED; + audio->samplerate = summary->frequency << 16; + } + return 0; +} + +static int isom_set_isom_dts_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) +{ + audio->version = 0; + audio->revision_level = 0; + audio->vendor = 0; + audio->channelcount = summary->channels; + audio->samplesize = 16; + audio->compression_ID = 0; + audio->packet_size = 0; + switch( summary->frequency ) + { + case 12000 : /* Invalid? (No reference in the spec) */ + case 24000 : + case 48000 : + case 96000 : + case 192000 : + case 384000 : /* Invalid? (No reference in the spec) */ + audio->samplerate = 48000 << 16; + break; + case 22050 : + case 44100 : + case 88200 : + case 176400 : + case 352800 : /* Invalid? (No reference in the spec) */ + audio->samplerate = 44100 << 16; + break; + case 8000 : /* Invalid? (No reference in the spec) */ + case 16000 : + case 32000 : + case 64000 : + case 128000 : + audio->samplerate = 32000 << 16; + break; + default : + audio->samplerate = 0; + break; + } + return 0; +} + +static lsmash_box_type_t isom_guess_audio_codec_specific_box_type( lsmash_codec_type_t active_codec_type, lsmash_compact_box_type_t fourcc ) +{ + lsmash_box_type_t box_type = LSMASH_BOX_TYPE_INITIALIZER; + box_type.fourcc = fourcc; +#define GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( codec_type, predefined_box_type ) \ + else if( (codec_type.user.fourcc == 0 \ + || lsmash_check_codec_type_identical( active_codec_type, codec_type )) \ + && box_type.fourcc == predefined_box_type.fourcc ) \ + box_type = predefined_box_type + if( 0 ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AC_3_AUDIO, ISOM_BOX_TYPE_DAC3 ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_EC_3_AUDIO, ISOM_BOX_TYPE_DEC3 ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSC_AUDIO, ISOM_BOX_TYPE_DDTS ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSE_AUDIO, ISOM_BOX_TYPE_DDTS ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSH_AUDIO, ISOM_BOX_TYPE_DDTS ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSL_AUDIO, ISOM_BOX_TYPE_DDTS ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_ALAC_AUDIO, ISOM_BOX_TYPE_ALAC ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_MP4A_AUDIO, ISOM_BOX_TYPE_ESDS ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_ALAC_AUDIO, QT_BOX_TYPE_ALAC ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_MP4A_AUDIO, QT_BOX_TYPE_ESDS ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_FULLMP3_AUDIO, QT_CODEC_TYPE_MP3_AUDIO ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_ADPCM2_AUDIO, QT_CODEC_TYPE_ADPCM2_AUDIO ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_ADPCM17_AUDIO, QT_CODEC_TYPE_ADPCM17_AUDIO ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_GSM49_AUDIO, QT_CODEC_TYPE_GSM49_AUDIO ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_CHAN ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_GLBL ); + GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_WAVE ); +#undef GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE + return box_type; +} + +typedef struct +{ + uint16_t wFormatTag; + uint16_t nChannels; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign; + uint16_t wBitsPerSample; + uint16_t cbSize; +} wave_format_ex_t; + +static lsmash_bs_t *isom_create_waveform_audio_info +( + wave_format_ex_t *wfx, + lsmash_box_type_t type +) +{ + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return NULL; + lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE + 18 + wfx->cbSize ); + lsmash_bs_put_be32( bs, type.fourcc ); + lsmash_bs_put_le16( bs, wfx->wFormatTag ); + lsmash_bs_put_le16( bs, wfx->nChannels ); + lsmash_bs_put_le32( bs, wfx->nSamplesPerSec ); + lsmash_bs_put_le32( bs, wfx->nAvgBytesPerSec ); + lsmash_bs_put_le16( bs, wfx->nBlockAlign ); + lsmash_bs_put_le16( bs, wfx->wBitsPerSample ); + lsmash_bs_put_le16( bs, wfx->cbSize ); + return bs; +} + +static int isom_setup_waveform_audio_info +( + isom_wave_t *wave, + isom_audio_entry_t *audio, + lsmash_audio_summary_t *summary, + uint32_t samples_per_packet, + uint32_t bytes_per_frame, + uint32_t sample_size +) +{ + wave_format_ex_t wfx; + wfx.wFormatTag = 0x0000; /* WAVE_FORMAT_UNKNOWN */ + wfx.nChannels = summary->channels; + wfx.nSamplesPerSec = summary->frequency; + wfx.nAvgBytesPerSec = 0; + wfx.nBlockAlign = bytes_per_frame; + wfx.wBitsPerSample = sample_size; + wfx.cbSize = 0; + lsmash_bs_t *bs = NULL; + if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ADPCM2_AUDIO ) ) + { + /* ADPCMWAVEFORMAT */ + wfx.wFormatTag = 0x0002; /* WAVE_FORMAT_ADPCM */ + wfx.cbSize = 32; + bs = isom_create_waveform_audio_info( &wfx, audio->type ); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + uint16_t wSamplesPerBlock = samples_per_packet; /* nBlockAlign * 2 / nChannels - 12 */ + uint16_t wNumCoef = 7; /* Microsoft ADPCM uses just 7 coefficients. */ + static const struct + { + int16_t iCoef1; + int16_t iCoef2; + } aCoef[7] = { { 256, 0 }, { 512, -256 }, { 0,0 }, { 192,64 }, { 240,0 }, { 460, -208 }, { 392,-232 } }; + lsmash_bs_put_le16( bs, wSamplesPerBlock ); + lsmash_bs_put_le16( bs, wNumCoef ); + for( int i = 0; i < 7; i++ ) + { + lsmash_bs_put_le16( bs, aCoef[i].iCoef1 ); + lsmash_bs_put_le16( bs, aCoef[i].iCoef2 ); + } + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ADPCM17_AUDIO ) ) + { + /* IMAADPCMWAVEFORMAT */ + wfx.wFormatTag = 0x0011; /* WAVE_FORMAT_DVI_ADPCM / WAVE_FORMAT_IMA_ADPCM */ + wfx.cbSize = 2; + bs = isom_create_waveform_audio_info( &wfx, audio->type ); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + uint16_t wSamplesPerBlock = samples_per_packet; + lsmash_bs_put_le16( bs, wSamplesPerBlock ); + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_GSM49_AUDIO ) ) + { + /* GSM610WAVEFORMAT */ + wfx.wFormatTag = 0x0031; /* WAVE_FORMAT_GSM610 */ + wfx.cbSize = 2; + bs = isom_create_waveform_audio_info( &wfx, audio->type ); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + uint16_t wSamplesPerBlock = samples_per_packet; + lsmash_bs_put_le16( bs, wSamplesPerBlock ); + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_FULLMP3_AUDIO ) + || lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MP3_AUDIO ) ) + { + /* MPEGLAYER3WAVEFORMAT */ + wfx.wFormatTag = 0x0055; /* WAVE_FORMAT_MPEGLAYER3 */ + wfx.nBlockAlign = 1; /* ? */ + wfx.wBitsPerSample = 0; /* undefined */ + wfx.cbSize = 12; + bs = isom_create_waveform_audio_info( &wfx, audio->type ); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + uint16_t wID = 1; /* MPEGLAYER3_ID_MPEG */ + uint32_t fdwFlags = 0; /* We don't know whether the stream is padded or not here. */ + uint16_t nBlockSize = 0; /* (144 * (bitrate / nSamplesPerSec) + padding) * nFramesPerBlock */ + uint16_t nFramesPerBlock = 1; /* the number of audio frames per block */ + uint16_t nCodecDelay = 0; /* Encoder delay in samples is unknown. */ + lsmash_bs_put_le16( bs, wID ); + lsmash_bs_put_le32( bs, fdwFlags ); + lsmash_bs_put_le16( bs, nBlockSize ); + lsmash_bs_put_le16( bs, nFramesPerBlock ); + lsmash_bs_put_le16( bs, nCodecDelay ); + } + if( !bs ) + { + assert( 0 ); + return LSMASH_ERR_NAMELESS; + } + uint32_t wfx_size; + uint8_t *wfx_data = lsmash_bs_export_data( bs, &wfx_size ); + lsmash_bs_cleanup( bs ); + if( !wfx_data ) + return LSMASH_ERR_NAMELESS; + if( wfx_size != ISOM_BASEBOX_COMMON_SIZE + 18 + wfx.cbSize ) + { + lsmash_free( wfx_data ); + return LSMASH_ERR_NAMELESS; + } + int err = isom_add_extension_binary( wave, audio->type, LSMASH_BOX_PRECEDENCE_HM, wfx_data, wfx_size ); + if( err < 0 ) + { + lsmash_free( wfx_data ); + return err; + } + return 0; +} + +static int isom_set_qtff_sound_decompression_parameters +( + isom_audio_entry_t *audio, + lsmash_audio_summary_t *summary, + lsmash_qt_audio_format_specific_flag *format_flags, + uint32_t samples_per_packet, + uint32_t bytes_per_frame, + uint32_t sample_size +) +{ + /* A 'wave' extension itself shall be absent in the opaque CODEC specific info list. + * So, create a 'wave' extension here and append it as an extension to the audio sample description. */ + isom_wave_t *wave = isom_add_wave( audio ); + if( !isom_add_frma ( wave ) + || !isom_add_terminator( wave ) ) + { + lsmash_remove_entry_tail( &audio->extensions, wave->destruct ); + return LSMASH_ERR_NAMELESS; + } + wave->frma->data_format = audio->type.fourcc; + /* Append extensions from the opaque CODEC specific info list to 'wave' extension. */ + int err; + int waveform_audio_info_present = 0; + int requires_waveform_audio_info = isom_is_waveform_audio( audio->type ); + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; + if( !specific ) + return LSMASH_ERR_NAMELESS; + if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + continue; /* LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED is not supported. */ + switch( specific->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : + continue; /* These cannot be an extension for 'wave' extension. */ + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : + /* (Legacy?) ALAC might have an Audio Channel Layout Box inside 'wave' extension. */ +#if 1 + continue; +#else + if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAC_AUDIO ) ) + continue; + if( (err = isom_append_channel_layout_extension( specific, wave, summary->channels )) < 0 ) + return err; + break; +#endif + default : + { + assert( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED + || specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS ); + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !cs ) + return LSMASH_ERR_NAMELESS; + if( cs->size < ISOM_BASEBOX_COMMON_SIZE ) + { + lsmash_destroy_codec_specific_data( cs ); + continue; + } + uint8_t *box_data = cs->data.unstructured; + uint64_t box_size = cs->size; + lsmash_compact_box_type_t fourcc = LSMASH_4CC( box_data[4], box_data[5], box_data[6], box_data[7] ); + if( audio->version == 2 && fourcc == QT_BOX_TYPE_ENDA.fourcc ) + { + /* Don't append a 'enda' extension if version == 2. + * Endianness is indicated in QuickTime audio format specific flags. */ + if( box_size >= ISOM_BASEBOX_COMMON_SIZE + 2 ) + { + /* Override endianness indicated in format specific flags. */ + if( box_data[9] == 1 ) + *format_flags &= ~QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN; + else + *format_flags |= QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN; + } + lsmash_destroy_codec_specific_data( cs ); + continue; + } + lsmash_box_type_t box_type = isom_guess_audio_codec_specific_box_type( audio->type, fourcc ); + if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_WAVE ) ) + { + /* It is insane to appened a 'wave' extension to a 'wave' extension. */ + lsmash_destroy_codec_specific_data( cs ); + continue; + } + box_type = lsmash_form_qtff_box_type( box_type.fourcc ); + /* Determine 'precedence'. */ + uint64_t precedence; + if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_FRMA ) ) + precedence = LSMASH_BOX_PRECEDENCE_QTFF_FRMA; + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ESDS ) ) + precedence = LSMASH_BOX_PRECEDENCE_QTFF_ESDS; + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ENDA ) ) + precedence = LSMASH_BOX_PRECEDENCE_QTFF_ENDA; + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_MP4A ) ) + precedence = LSMASH_BOX_PRECEDENCE_QTFF_MP4A; + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_TERMINATOR ) ) + precedence = LSMASH_BOX_PRECEDENCE_QTFF_TERMINATOR; + else + precedence = LSMASH_BOX_PRECEDENCE_HM; + /* Append the extension. */ + err = isom_add_extension_binary( wave, box_type, precedence, cs->data.unstructured, cs->size ); + cs->data.unstructured = NULL; /* Avoid freeing the binary data of the extension. */ + lsmash_destroy_codec_specific_data( cs ); + if( err < 0 ) + return err; + if( isom_is_waveform_audio( box_type ) ) + waveform_audio_info_present = 1; + break; + } + } + } + if( requires_waveform_audio_info && !waveform_audio_info_present + && (err = isom_setup_waveform_audio_info( wave, audio, summary, samples_per_packet, bytes_per_frame, sample_size )) < 0 ) + return err; + return 0; +} + +static int isom_set_qtff_template_audio_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) +{ + audio->manager |= LSMASH_QTFF_BASE; + audio->type = lsmash_form_qtff_box_type( audio->type.fourcc ); + audio->version = (summary->channels > 2 || summary->frequency > UINT16_MAX) ? 2 : 1; + /* Try to get QuickTime audio format specific flags. */ + lsmash_qt_audio_format_specific_flag format_flags = QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN; + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; + if( !specific + || !specific->data.structured ) + continue; + if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS + && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + /* A format specific flags is found. + * Force audio sample description version == 2. */ + format_flags = ((lsmash_qt_audio_format_specific_flags_t *)specific->data.structured)->format_flags; + audio->version = 2; + break; + } + } + uint32_t samples_per_packet; + uint32_t bytes_per_frame; + uint32_t sample_size; + if( !((summary->samples_in_frame == 0 || summary->bytes_per_frame == 0 || summary->sample_size == 0) + && isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, &samples_per_packet, &bytes_per_frame, &sample_size )) ) + { + samples_per_packet = summary->samples_in_frame; + bytes_per_frame = summary->bytes_per_frame; + sample_size = summary->sample_size; + } + if( !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC3_AUDIO ) + && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC6_AUDIO ) + && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_AGSM_AUDIO ) + && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAW_AUDIO ) + && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ULAW_AUDIO ) ) + { + int err = isom_set_qtff_sound_decompression_parameters( audio, summary, &format_flags, + samples_per_packet, bytes_per_frame, sample_size ); + if( err < 0 ) + return err; + } + /* Set up common audio description fields. */ + audio->samplesize = 16; + audio->packet_size = 0; + if( audio->version == 2 ) + { + audio->channelcount = 3; + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION; + 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; + audio->constBytesPerAudioPacket = bytes_per_frame; + audio->constLPCMFramesPerAudioPacket = samples_per_packet; + if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAC_AUDIO ) ) + { + switch( sample_size ) + { + case 16 : + audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_16BIT_SOURCE_DATA; + break; + case 20 : + audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_20BIT_SOURCE_DATA; + break; + case 24 : + audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_24BIT_SOURCE_DATA; + break; + case 32 : + audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_32BIT_SOURCE_DATA; + break; + default : + break; + } + } + else + { + if( format_flags & QT_AUDIO_FORMAT_FLAG_FLOAT ) + format_flags &= ~QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER; + if( format_flags & QT_AUDIO_FORMAT_FLAG_PACKED ) + format_flags &= ~QT_AUDIO_FORMAT_FLAG_ALIGNED_HIGH; + audio->formatSpecificFlags = format_flags; + } + } + else /* if( audio->version == 1 ) */ + { + audio->channelcount = LSMASH_MIN( summary->channels, 2 ); + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_FIXED_COMPRESSION; + audio->samplerate = summary->frequency << 16; + audio->samplesPerPacket = samples_per_packet; + audio->bytesPerPacket = bytes_per_frame / summary->channels; + audio->bytesPerFrame = bytes_per_frame; /* sample_size field in stsz box is NOT used. */ + audio->bytesPerSample = 1 + (sample_size != 8); + } + return 0; +} + +static void isom_set_samplerate_division_of_media_timescale( isom_audio_entry_t *audio, int strict ) +{ + if( audio->parent /* stsd */ + && audio->parent->parent /* stbl */ + && audio->parent->parent->parent /* minf */ + && audio->parent->parent->parent->parent /* mdia */ + && lsmash_check_box_type_identical( audio->parent->parent->parent->parent->type, ISOM_BOX_TYPE_MDIA ) + && ((isom_mdia_t *)audio->parent->parent->parent->parent)->mdhd ) + { + /* Make an effort to match the timescale with samplerate, or be an integer multiple of it. */ + uint32_t orig_timescale = ((isom_mdia_t *)audio->parent->parent->parent->parent)->mdhd->timescale; + uint32_t timescale = orig_timescale; + uint32_t i = 2; + while( timescale > UINT16_MAX && timescale > 1 && i <= UINT32_MAX ) + { + if( timescale % i == 0 ) + timescale /= i; + else + i += i > 2 ? 2 : 1; + } + if( timescale != orig_timescale && strict ) + lsmash_log( NULL, LSMASH_LOG_WARNING, "samplerate does not match the media timescale.\n" ); + if( timescale <= UINT16_MAX && timescale > 1 ) + { + audio->samplerate = timescale << 16; + return; + } + } + audio->samplerate = 0; +} + +static int isom_set_isom_template_audio_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) +{ + audio->version = 0; /* reserved */ + audio->revision_level = 0; /* reserved */ + audio->vendor = 0; /* reserved */ + audio->channelcount = 2; /* template */ + audio->samplesize = 16; /* template */ + audio->compression_ID = 0; /* pre_defined */ + audio->packet_size = 0; /* reserved */ + /* template : default output audio sampling rate at playback */ + if( summary->frequency <= UINT16_MAX ) + audio->samplerate = summary->frequency << 16; + else + isom_set_samplerate_division_of_media_timescale( audio, 0 ); + return 0; +} + +static int isom_set_isom_amr_audio_description( isom_audio_entry_t *audio, int wb ) +{ + /* For AMR-NB and AMR-WB stream, these fields are not meaningful. */ + audio->version = 0; /* always 0 */ + audio->revision_level = 0; /* always 0 */ + audio->vendor = 0; /* always 0 */ + audio->channelcount = 2; /* always 2 although the actual number of channels is always 1 */ + audio->samplesize = 16; /* always 16 */ + audio->compression_ID = 0; /* always 0 */ + audio->packet_size = 0; /* always 0 */ + /* Set samplerate by trying to copy from Media Header Box of this media though the + * actual samplerate is 8000 Hz for AMR-NB and 16000 Hz for AMR-WB. + * 3GPP and 3GPP2 has no restriction for media timescale. Therefore, users should + * set suitable media timescale by themselves within the bounds of common sense. */ + isom_set_samplerate_division_of_media_timescale( audio, 1 ); + if( audio->samplerate == 0 ) + /* Set hard-coded but correct samplerate in the CODEC level. */ + audio->samplerate = wb ? 8000 : 16000; + return 0; +} + +int isom_setup_audio_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type, lsmash_audio_summary_t *summary ) +{ + if( !stsd || !stsd->file || !summary ) + return LSMASH_ERR_NAMELESS; + int err = isom_check_valid_summary( (lsmash_summary_t *)summary ); + if( err < 0 ) + return err; + isom_audio_entry_t *audio = isom_add_audio_description( stsd, sample_type ); + if( !audio ) + return LSMASH_ERR_NAMELESS; + audio->data_reference_index = summary->data_ref_index; + lsmash_file_t *file = stsd->file; + lsmash_codec_type_t audio_type = audio->type; + if( lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_MP4A_AUDIO ) ) + { + if( (file->ftyp && file->ftyp->major_brand == ISOM_BRAND_TYPE_QT) + || (!file->ftyp && (file->qt_compatible || (file->moov && !file->moov->iods))) ) + err = isom_set_qtff_mp4a_description( audio, summary ); + else + err = isom_set_isom_mp4a_description( audio, summary ); + } + else if( isom_is_lpcm_audio( audio ) ) + err = isom_set_qtff_lpcm_description( audio, summary ); + else if( lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) + err = isom_set_isom_dts_description( audio, summary ); + else if( file->qt_compatible ) + err = isom_set_qtff_template_audio_description( audio, summary ); + else if( lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_SAMR_AUDIO ) ) + err = isom_set_isom_amr_audio_description( audio, 0 ); + else if( lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_SAWB_AUDIO ) ) + err = isom_set_isom_amr_audio_description( audio, 1 ); + else + err = isom_set_isom_template_audio_description( audio, summary ); + if( err < 0 ) + goto fail; + err = LSMASH_ERR_NAMELESS; + /* Don't use audio_type since audio->type might have changed. */ + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) + { + lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; + if( !specific ) + goto fail; + if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + continue; /* LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED is not supported. */ + switch( specific->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : + { + if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) + continue; /* Ignore since not fatal. */ + lsmash_qt_audio_common_t *data = (lsmash_qt_audio_common_t *)specific->data.structured; + audio->revision_level = data->revision_level; + audio->vendor = data->vendor; + if( audio->version == 1 + && !isom_is_lpcm_audio( audio ) + && data->compression_ID != QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED ) + { + /* Compressed audio must not be set to QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED. */ + audio->compression_ID = data->compression_ID; + if( audio->compression_ID == QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION ) + { + /* For variable compression, bytesPerPacket and bytesPerFrame are reserved and should be set to 0. */ + audio->bytesPerPacket = 0; + audio->bytesPerFrame = 0; + } + } + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : + { + if( !file->qt_compatible + && !lsmash_check_codec_type_identical( audio->type, ISOM_CODEC_TYPE_ALAC_AUDIO ) + && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAC_AUDIO ) ) + continue; + if( (err = isom_append_channel_layout_extension( specific, audio, summary->channels )) < 0 ) + goto fail; + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + goto fail; + lsmash_codec_global_header_t *data = (lsmash_codec_global_header_t *)cs->data.structured; + isom_glbl_t *glbl = isom_add_glbl( audio ); + if( !glbl ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + glbl->header_size = data->header_size; + glbl->header_data = lsmash_memdup( data->header_data, data->header_size ); + lsmash_destroy_codec_specific_data( cs ); + if( !glbl->header_data ) + { + isom_remove_box_by_itself( glbl ); + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS : + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : + break; /* shall be set up already */ + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : + if( file->qt_compatible ) + continue; /* shall be set up already */ + default : + { + lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !cs ) + goto fail; + if( cs->size < ISOM_BASEBOX_COMMON_SIZE ) + { + lsmash_destroy_codec_specific_data( cs ); + continue; + } + uint8_t *box_data = cs->data.unstructured; + lsmash_compact_box_type_t fourcc = LSMASH_4CC( box_data[4], box_data[5], box_data[6], box_data[7] ); + lsmash_box_type_t box_type = isom_guess_audio_codec_specific_box_type( audio->type, fourcc ); + if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_WAVE ) ) + { + /* CODEC specific info shall be already inside 'wave' extension. */ + lsmash_destroy_codec_specific_data( cs ); + continue; + } + /* Append the extension. */ + err = isom_add_extension_binary( audio, box_type, LSMASH_BOX_PRECEDENCE_HM, cs->data.unstructured, cs->size ); + cs->data.unstructured = NULL; /* Avoid freeing the binary data of the extension. */ + lsmash_destroy_codec_specific_data( cs ); + if( err < 0 ) + goto fail; + break; + } + } + } + if( audio->version == 0 ) + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED; + else if( audio->version == 2 ) + audio->compression_ID = QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION; + return 0; +fail: + isom_remove_box_by_itself( audio ); + return err; +} + +int isom_setup_tx3g_description( isom_stsd_t *stsd, lsmash_summary_t *summary ) +{ + isom_tx3g_entry_t *tx3g = isom_add_tx3g_description( stsd ); + if( !tx3g ) + return LSMASH_ERR_NAMELESS; + /* We create a dummy font record to make valid font_ID in the sample description. + * The specification (3GPP TS 26.245) does not forbid the value 0 for the identifier, + * but we set 1 to it as track_ID begins from 1. */ + tx3g->data_reference_index = summary->data_ref_index; + tx3g->font_ID = 1; /* ID of the default font record */ + int err = LSMASH_ERR_MEMORY_ALLOC; + isom_ftab_t *ftab = isom_add_ftab( tx3g ); + if( !ftab ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + isom_font_record_t *font = lsmash_malloc( sizeof(isom_font_record_t) ); + if( !font ) + goto fail; + if( lsmash_add_entry( ftab->list, font ) < 0 ) + { + lsmash_free( font ); + goto fail; + } + const char font_names[] = "Serif,Sans-serif,Monospace"; + font->font_ID = 1; + font->font_name_length = sizeof(font_names); + font->font_name = lsmash_memdup( font_names, sizeof(font_names) ); + if( !font->font_name ) + goto fail; + return 0; +fail: + isom_remove_box_by_itself( tx3g ); + return err; +} + +static lsmash_codec_specific_data_type isom_get_codec_specific_data_type( lsmash_compact_box_type_t extension_fourcc ) +{ + static struct codec_specific_data_type_table_tag + { + lsmash_compact_box_type_t extension_fourcc; + lsmash_codec_specific_data_type data_type; + } codec_specific_data_type_table[32] = { { 0, LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN } }; + if( codec_specific_data_type_table[0].data_type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN ) + { + int i = 0; +#define ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( extension_type, data_type ) \ + codec_specific_data_type_table[i++] = (struct codec_specific_data_type_table_tag){ extension_type.fourcc, data_type } + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_AVCC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_HVCC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DVC1, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DAC3, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DEC3, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DDTS, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_ALAC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE ); + //ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_ALAC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC ); + //ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_ESDS, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_GAMMA_LEVEL ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER ); + ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN ); +#undef ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT + } + lsmash_codec_specific_data_type data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; + for( int i = 0; codec_specific_data_type_table[i].data_type != LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; i++ ) + if( extension_fourcc == codec_specific_data_type_table[i].extension_fourcc ) + { + data_type = codec_specific_data_type_table[i].data_type; + break; + } + return data_type; +} + +lsmash_summary_t *isom_create_video_summary_from_description( isom_sample_entry_t *sample_entry ) +{ + if( !sample_entry ) + return NULL; + isom_visual_entry_t *visual = (isom_visual_entry_t *)sample_entry; + lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); + if( !summary ) + return NULL; + summary->sample_type = visual->type; + summary->data_ref_index = visual->data_reference_index; + summary->width = visual->width; + summary->height = visual->height; + summary->depth = visual->depth; + memcpy( summary->compressorname, visual->compressorname, 32 ); + summary->compressorname[32] = '\0'; + if( isom_is_qt_video( summary->sample_type ) ) + { + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + lsmash_qt_video_common_t *data = (lsmash_qt_video_common_t *)specific->data.structured; + data->revision_level = visual->revision_level; + data->vendor = visual->vendor; + data->temporalQuality = visual->temporalQuality; + data->spatialQuality = visual->spatialQuality; + data->horizontal_resolution = visual->horizresolution; + data->vertical_resolution = visual->vertresolution; + data->dataSize = visual->dataSize; + data->frame_count = visual->frame_count; + data->color_table_ID = visual->color_table_ID; + if( visual->color_table_ID == 0 ) + { + isom_qt_color_table_t *src_ct = &visual->color_table; + if( !src_ct->array ) + goto fail; + uint16_t element_count = LSMASH_MIN( src_ct->size + 1, 256 ); + lsmash_qt_color_table_t *dst_ct = &data->color_table; + dst_ct->seed = src_ct->seed; + dst_ct->flags = src_ct->flags; + dst_ct->size = src_ct->size; + for( uint16_t i = 0; i < element_count; i++ ) + { + dst_ct->array[i].unused = src_ct->array[i].value; + dst_ct->array[i].r = src_ct->array[i].r; + dst_ct->array[i].g = src_ct->array[i].g; + dst_ct->array[i].b = src_ct->array[i].b; + } + } + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + } + for( lsmash_entry_t *entry = visual->extensions.head; entry; entry = entry->next ) + { + isom_box_t *box = (isom_box_t *)entry->data; + if( !box ) + continue; + if( !(box->manager & LSMASH_BINARY_CODED_BOX) ) + { + lsmash_codec_specific_t *specific = NULL; + if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_CLAP ) ) + { + isom_clap_t *clap = (isom_clap_t *)box; + summary->clap.width.n = clap->cleanApertureWidthN; + summary->clap.width.d = clap->cleanApertureWidthD; + summary->clap.height.n = clap->cleanApertureHeightN; + summary->clap.height.d = clap->cleanApertureHeightD; + summary->clap.horizontal_offset.n = clap->horizOffN; + summary->clap.horizontal_offset.d = clap->horizOffD; + summary->clap.vertical_offset.n = clap->vertOffN; + summary->clap.vertical_offset.d = clap->vertOffD; + continue; + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_PASP ) ) + { + isom_pasp_t *pasp = (isom_pasp_t *)box; + summary->par_h = pasp->hSpacing; + summary->par_v = pasp->vSpacing; + continue; + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_COLR ) + || lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_COLR ) ) + { + isom_colr_t *colr = (isom_colr_t *)box; + summary->color.primaries_index = colr->primaries_index; + summary->color.transfer_index = colr->transfer_function_index; + summary->color.matrix_index = colr->matrix_index; + summary->color.full_range = colr->full_range_flag; + continue; + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_STSL ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_stsl_t *stsl = (isom_stsl_t *)box; + lsmash_isom_sample_scale_t *data = (lsmash_isom_sample_scale_t *)specific->data.structured; + data->constraint_flag = stsl->constraint_flag; + data->scale_method = stsl->scale_method; + data->display_center_x = stsl->display_center_x; + data->display_center_y = stsl->display_center_y; + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_BTRT ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_btrt_t *btrt = (isom_btrt_t *)box; + lsmash_h264_bitrate_t *data = (lsmash_h264_bitrate_t *)specific->data.structured; + data->bufferSizeDB = btrt->bufferSizeDB; + data->maxBitrate = btrt->maxBitrate; + data->avgBitrate = btrt->avgBitrate; + } + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_FIEL ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_fiel_t *fiel = (isom_fiel_t *)box; + lsmash_qt_field_info_t *data = (lsmash_qt_field_info_t *)specific->data.structured; + data->fields = fiel->fields; + data->detail = fiel->detail; + } + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_CSPC ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_cspc_t *cspc = (isom_cspc_t *)box; + lsmash_qt_pixel_format_t *data = (lsmash_qt_pixel_format_t *)specific->data.structured; + data->pixel_format = cspc->pixel_format; + } + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_SGBT ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_sgbt_t *sgbt = (isom_sgbt_t *)box; + lsmash_qt_significant_bits_t *data = (lsmash_qt_significant_bits_t *)specific->data.structured; + data->significantBits = sgbt->significantBits; + } + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_GLBL ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_glbl_t *glbl = (isom_glbl_t *)box; + lsmash_codec_global_header_t *data = (lsmash_codec_global_header_t *)specific->data.structured; + data->header_size = glbl->header_size; + data->header_data = lsmash_memdup( glbl->header_data, glbl->header_size ); + if( !data->header_data ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + } + else + continue; + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + } + else + { + if( box->size < ISOM_BASEBOX_COMMON_SIZE ) + continue; + uint8_t *data = box->binary; + lsmash_compact_box_type_t fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); + lsmash_codec_specific_data_type type = isom_get_codec_specific_data_type( fourcc ); + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !specific ) + goto fail; + specific->size = box->size; + specific->data.unstructured = lsmash_memdup( box->binary, box->size ); + if( !specific->data.unstructured + || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + } + } + return (lsmash_summary_t *)summary; +fail: + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; +} + +static int isom_append_structured_mp4sys_decoder_config( lsmash_codec_specific_list_t *opaque, isom_esds_t *esds ) +{ + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + /* Put box size, type, version and flags fields. */ + lsmash_bs_put_be32( bs, 0 ); + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_ESDS.fourcc ); + lsmash_bs_put_be32( bs, 0 ); + /* Put ES Descriptor. */ + mp4sys_update_descriptor_size( esds->ES ); + mp4sys_write_descriptor( bs, esds->ES ); + /* Export ES Descriptor Box as binary string. */ + uint32_t esds_size; + uint8_t *esds_data = lsmash_bs_export_data( bs, &esds_size ); + lsmash_bs_cleanup( bs ); + if( !esds_data ) + return LSMASH_ERR_NAMELESS; + /* Update box size. */ + LSMASH_SET_BE32( esds_data, esds_size ); + lsmash_codec_specific_data_type type = isom_get_codec_specific_data_type( ISOM_BOX_TYPE_ESDS.fourcc ); + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !specific ) + { + lsmash_free( esds_data ); + return LSMASH_ERR_NAMELESS; + } + specific->data.unstructured = esds_data; + specific->size = esds_size; + /* Convert unstructured CODEC specific data format into structured, and append it to the opaque list. */ + lsmash_codec_specific_t *conv = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + lsmash_destroy_codec_specific_data( specific ); + if( !conv ) + return LSMASH_ERR_NAMELESS; + if( lsmash_add_entry( &opaque->list, conv ) < 0 ) + { + lsmash_destroy_codec_specific_data( conv ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +lsmash_summary_t *isom_create_audio_summary_from_description( isom_sample_entry_t *sample_entry ) +{ + if( !sample_entry || !sample_entry->file || !sample_entry->parent ) + return NULL; + isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + return NULL; + summary->sample_type = audio->type; + summary->data_ref_index = audio->data_reference_index; + summary->sample_size = audio->samplesize; + summary->channels = audio->channelcount; + summary->frequency = audio->samplerate >> 16; + if( ((isom_stsd_t *)audio->parent)->version == 0 + && audio->file->qt_compatible + && isom_is_qt_audio( audio->type ) ) + { + if( audio->version == 0 ) + isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, &summary->samples_in_frame, &summary->bytes_per_frame, &summary->sample_size ); + else if( audio->version == 1 ) + { + summary->channels = audio->bytesPerPacket ? audio->bytesPerFrame / audio->bytesPerPacket : audio->channelcount; + summary->sample_size = audio->bytesPerPacket * 8; + summary->samples_in_frame = audio->samplesPerPacket; + summary->bytes_per_frame = audio->bytesPerFrame; + } + else if( audio->version == 2 ) + { + summary->frequency = (union {uint64_t i; double d;}){audio->audioSampleRate}.d; + summary->channels = audio->numAudioChannels; + summary->sample_size = audio->constBitsPerChannel; + summary->samples_in_frame = audio->constLPCMFramesPerAudioPacket; + summary->bytes_per_frame = audio->constBytesPerAudioPacket; + } + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + lsmash_qt_audio_common_t *common = (lsmash_qt_audio_common_t *)specific->data.structured; + common->revision_level = audio->revision_level; + common->vendor = audio->vendor; + common->compression_ID = audio->compression_ID; + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + if( isom_is_lpcm_audio( audio ) ) + { + specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + lsmash_qt_audio_format_specific_flags_t *data = (lsmash_qt_audio_format_specific_flags_t *)specific->data.structured; + if( audio->version == 2 ) + data->format_flags = audio->formatSpecificFlags; + else + { + data->format_flags = 0; + /* Here, don't override samplesize. + * We should trust samplesize field in the description for misused CODEC indentifier. */ + lsmash_codec_type_t audio_type = audio->type; + if( lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_FL32_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_FL64_AUDIO ) ) + data->format_flags = QT_LPCM_FORMAT_FLAG_FLOAT; + else if( lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_TWOS_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_NONE_AUDIO ) + || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_NOT_SPECIFIED ) ) + { + if( lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_TWOS_AUDIO ) ) + data->format_flags = QT_LPCM_FORMAT_FLAG_BIG_ENDIAN | QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER; + if( summary->sample_size > 8 ) + data->format_flags = QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; + } + } + isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box_format( &audio->extensions, QT_BOX_TYPE_WAVE ); + if( wave && wave->enda && !wave->enda->littleEndian ) + data->format_flags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + } + else if( audio->version == 2 + && (lsmash_check_codec_type_identical( audio->type, ISOM_CODEC_TYPE_ALAC_AUDIO ) + || lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAC_AUDIO )) ) + switch( audio->formatSpecificFlags ) + { + case QT_ALAC_FORMAT_FLAG_16BIT_SOURCE_DATA : + summary->sample_size = 16; + break; + case QT_ALAC_FORMAT_FLAG_20BIT_SOURCE_DATA : + summary->sample_size = 20; + break; + case QT_ALAC_FORMAT_FLAG_24BIT_SOURCE_DATA : + summary->sample_size = 24; + break; + case QT_ALAC_FORMAT_FLAG_32BIT_SOURCE_DATA : + summary->sample_size = 32; + break; + default : + break; + } + } + else if( lsmash_check_codec_type_identical( audio->type, ISOM_CODEC_TYPE_SAMR_AUDIO ) ) + { + summary->channels = 1; + summary->frequency = 8000; + } + else if( lsmash_check_codec_type_identical( audio->type, ISOM_CODEC_TYPE_SAWB_AUDIO ) ) + { + summary->channels = 1; + summary->frequency = 16000; + } + uint32_t actual_sampling_rate = 0; + for( lsmash_entry_t *entry = audio->extensions.head; entry; entry = entry->next ) + { + isom_box_t *box = (isom_box_t *)entry->data; + if( !box ) + continue; + if( !(box->manager & LSMASH_BINARY_CODED_BOX) ) + { + if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_CHAN ) ) + { + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + isom_chan_t *chan = (isom_chan_t *)box; + lsmash_qt_audio_channel_layout_t *data = (lsmash_qt_audio_channel_layout_t *)specific->data.structured; + data->channelLayoutTag = chan->channelLayoutTag; + data->channelBitmap = chan->channelBitmap; + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_ESDS ) + || lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ESDS ) ) + { + isom_esds_t *esds = (isom_esds_t *)box; + if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 + || isom_append_structured_mp4sys_decoder_config( summary->opaque, esds ) < 0 ) + goto fail; + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_SRAT ) ) + { + isom_srat_t *srat = (isom_srat_t *)box; + actual_sampling_rate = srat->sampling_rate; + } + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_WAVE ) ) + { + /* Don't append 'wave' extension itself to the opaque CODEC specific info list. */ + isom_wave_t *wave = (isom_wave_t *)box; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + goto fail; + for( lsmash_entry_t *wave_entry = wave->extensions.head; wave_entry; wave_entry = wave_entry->next ) + { + isom_box_t *wave_ext = (isom_box_t *)wave_entry->data; + if( !wave_ext ) + continue; + lsmash_box_type_t box_type = LSMASH_BOX_TYPE_INITIALIZER; + if( !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) ) + { + box_type = wave_ext->type; + if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ENDA ) ) + { + isom_enda_t *enda = (isom_enda_t *)wave_ext; + isom_bs_put_box_common( bs, enda ); + lsmash_bs_put_be16( bs, enda->littleEndian ); + } + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_MP4A ) ) + { + isom_mp4a_t *mp4a = (isom_mp4a_t *)wave_ext; + isom_bs_put_box_common( bs, mp4a ); + lsmash_bs_put_be32( bs, mp4a->unknown ); + } + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_CHAN ) ) + { + isom_chan_t *chan = (isom_chan_t *)wave_ext; + 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 ) + { + lsmash_bs_cleanup( bs ); + goto fail; + } + 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] ); + } + } + else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ESDS ) ) + { + isom_esds_t *esds = (isom_esds_t *)wave_ext; + if( !esds + || mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 + || isom_append_structured_mp4sys_decoder_config( summary->opaque, esds ) < 0 ) + { + lsmash_bs_cleanup( bs ); + goto fail; + } + continue; + } + else + /* Skip Format Box and Terminator Box since they are mandatory and fixed structure. */ + continue; + } + else + { + if( wave_ext->size < ISOM_BASEBOX_COMMON_SIZE ) + continue; + uint8_t *data = wave_ext->binary; + box_type.fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); + lsmash_bs_put_bytes( bs, wave_ext->size, wave_ext->binary ); + } + /* Export as binary string. */ + uint32_t box_size; + uint8_t *box_data = lsmash_bs_export_data( bs, &box_size ); + lsmash_bs_empty( bs ); + if( !box_data ) + { + lsmash_bs_cleanup( bs ); + goto fail; + } + /* Append as an unstructured CODEC specific info. */ + lsmash_codec_specific_data_type type; + if( box_type.fourcc == QT_BOX_TYPE_CHAN.fourcc ) + /* Complete audio channel layout is stored as binary string. + * We distinguish it from one of the outside of 'wave' extension here. */ + type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS; + else + { + type = isom_get_codec_specific_data_type( box_type.fourcc ); + if( type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN ) + type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS; + } + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !specific ) + { + lsmash_bs_cleanup( bs ); + goto fail; + } + specific->data.unstructured = box_data; + specific->size = box_size; + if( lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + lsmash_bs_cleanup( bs ); + goto fail; + } + } + lsmash_bs_cleanup( bs ); + } + } + else + { + if( box->size < ISOM_BASEBOX_COMMON_SIZE ) + continue; + uint8_t *data = box->binary; + lsmash_compact_box_type_t fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); + lsmash_codec_specific_data_type type = isom_get_codec_specific_data_type( fourcc ); + lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !specific ) + goto fail; + specific->size = box->size; + specific->data.unstructured = lsmash_memdup( box->binary, box->size ); + if( !specific->data.unstructured + || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_destroy_codec_specific_data( specific ); + goto fail; + } + if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS + || specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 + || specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ) + { + specific = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !specific ) + goto fail; + switch( specific->type ) + { + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : + { + lsmash_dts_specific_parameters_t *param = (lsmash_dts_specific_parameters_t *)specific->data.structured; + summary->sample_size = param->pcmSampleDepth; + summary->samples_in_frame = (summary->frequency * (512 << param->FrameDuration)) / param->DTSSamplingFrequency; + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : + { + lsmash_ac3_specific_parameters_t *param = (lsmash_ac3_specific_parameters_t *)specific->data.structured; + summary->frequency = ac3_get_sample_rate( param ); + summary->channels = ac3_get_channel_count( param ); + summary->samples_in_frame = 1536; + break; + } + case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : + { + lsmash_eac3_specific_parameters_t *param = (lsmash_eac3_specific_parameters_t *)specific->data.structured; + eac3_update_sample_rate( &summary->frequency, param, NULL ); + eac3_update_channel_count( &summary->channels, param ); + summary->samples_in_frame = 1536; + break; + } + default : + break; + } + lsmash_destroy_codec_specific_data( specific ); + } + } + } + /* Set the actual sampling rate. */ + if( actual_sampling_rate ) + summary->frequency = actual_sampling_rate; + return (lsmash_summary_t *)summary; +fail: + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + return NULL; +} + +lsmash_codec_specific_t *lsmash_get_codec_specific_data( lsmash_summary_t *summary, uint32_t extension_number ) +{ + if( !summary || !summary->opaque ) + return NULL; + uint32_t i = 0; + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) + if( ++i == extension_number ) + return (lsmash_codec_specific_t *)entry->data; + return NULL; +} + +uint32_t lsmash_count_codec_specific_data( lsmash_summary_t *summary ) +{ + if( !summary || !summary->opaque ) + return 0; + return summary->opaque->list.entry_count; +} + +int isom_compare_opaque_extensions( lsmash_summary_t *a, lsmash_summary_t *b ) +{ + assert( a && b ); + uint32_t in_number_of_extensions = lsmash_count_codec_specific_data( a ); + uint32_t out_number_of_extensions = lsmash_count_codec_specific_data( b ); + if( out_number_of_extensions != in_number_of_extensions ) + return 1; + uint32_t active_number_of_extensions = in_number_of_extensions; + uint32_t identical_count = 0; + for( uint32_t j = 1; j <= in_number_of_extensions; j++ ) + { + lsmash_codec_specific_t *in_cs_orig = lsmash_get_codec_specific_data( a, j ); + lsmash_codec_specific_t *in_cs; + lsmash_codec_specific_format compare_format = LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED; + if( in_cs_orig->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + if( in_cs_orig->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON + || in_cs_orig->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON + || in_cs_orig->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS ) + { + compare_format = LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED; + in_cs = in_cs_orig; + } + else + { + in_cs = lsmash_convert_codec_specific_format( in_cs_orig, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !in_cs ) + { + /* We don't support the format converter of this data type. */ + --active_number_of_extensions; + continue; + } + } + } + else + in_cs = in_cs_orig; + for( uint32_t k = 1; k <= out_number_of_extensions; k++ ) + { + lsmash_codec_specific_t *out_cs_orig = lsmash_get_codec_specific_data( b, k ); + if( out_cs_orig->type != in_cs_orig->type ) + continue; + lsmash_codec_specific_t *out_cs; + if( out_cs_orig->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + { + if( compare_format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + out_cs = out_cs_orig; + else + { + out_cs = lsmash_convert_codec_specific_format( out_cs_orig, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); + if( !out_cs ) + continue; + } + } + else + out_cs = out_cs_orig; + int identical; + if( compare_format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) + identical = out_cs->size == in_cs->size && !memcmp( out_cs->data.unstructured, in_cs->data.unstructured, in_cs->size ); + else + { + if( in_cs->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON ) + { + lsmash_qt_video_common_t *in_data = (lsmash_qt_video_common_t *)in_cs->data.structured; + lsmash_qt_video_common_t *out_data = (lsmash_qt_video_common_t *)out_cs->data.structured; + identical = in_data->revision_level == out_data->revision_level + && in_data->vendor == out_data->vendor + && in_data->temporalQuality == out_data->temporalQuality + && in_data->spatialQuality == out_data->spatialQuality + && in_data->horizontal_resolution == out_data->horizontal_resolution + && in_data->vertical_resolution == out_data->vertical_resolution + && in_data->dataSize == out_data->dataSize + && in_data->frame_count == out_data->frame_count + && in_data->color_table_ID == out_data->color_table_ID; + } + else if( in_cs->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON ) + { + lsmash_qt_audio_common_t *in_data = (lsmash_qt_audio_common_t *)in_cs->data.structured; + lsmash_qt_audio_common_t *out_data = (lsmash_qt_audio_common_t *)out_cs->data.structured; + identical = in_data->revision_level == out_data->revision_level + && in_data->vendor == out_data->vendor + && in_data->compression_ID == out_data->compression_ID; + } + else + { + lsmash_qt_audio_format_specific_flags_t *in_data = (lsmash_qt_audio_format_specific_flags_t *)in_cs->data.structured; + lsmash_qt_audio_format_specific_flags_t *out_data = (lsmash_qt_audio_format_specific_flags_t *)out_cs->data.structured; + identical = (in_data->format_flags == out_data->format_flags); + } + } + if( out_cs != out_cs_orig ) + lsmash_destroy_codec_specific_data( out_cs ); + if( identical ) + { + ++identical_count; + break; + } + } + if( in_cs != in_cs_orig ) + lsmash_destroy_codec_specific_data( in_cs ); + } + return (identical_count != active_number_of_extensions); +} + +int isom_get_implicit_qt_fixed_comp_audio_sample_quants +( + isom_audio_entry_t *audio, + uint32_t *samples_per_packet, + uint32_t *constant_bytes_per_frame, + uint32_t *sample_size +) +{ + if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC3_AUDIO ) ) + { + *samples_per_packet = 6; + *constant_bytes_per_frame = 2 * audio->channelcount; + *sample_size = 8; + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC6_AUDIO ) ) + { + *samples_per_packet = 6; + *constant_bytes_per_frame = audio->channelcount; + *sample_size = 8; + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ADPCM17_AUDIO ) ) + { + *samples_per_packet = 64; + *constant_bytes_per_frame = 34 * audio->channelcount; + *sample_size = 16; + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_AGSM_AUDIO ) ) + { + *samples_per_packet = 160; + *constant_bytes_per_frame = 33; + *sample_size = 16; + } + else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAW_AUDIO ) + || lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ULAW_AUDIO ) ) + { + *samples_per_packet = 1; + *constant_bytes_per_frame = audio->channelcount; + *sample_size = 16; + } + else + return 0; + return 1; +} diff -Nru l-smash-1.9.1/codecs/description.h l-smash-2.3.0/codecs/description.h --- l-smash-1.9.1/codecs/description.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/description.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,89 @@ +/***************************************************************************** + * description.h: + ***************************************************************************** + * Copyright (C) 2012-2014 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. */ + +struct lsmash_codec_specific_list_tag +{ + lsmash_entry_list_t list; +}; + +lsmash_codec_specific_t *isom_duplicate_codec_specific_data +( + lsmash_codec_specific_t *specific +); + +lsmash_codec_specific_t *isom_get_codec_specific +( + lsmash_codec_specific_list_t *opaque, + lsmash_codec_specific_data_type type +); + +uint8_t *isom_get_child_box_position +( + uint8_t *parent_data, + uint32_t parent_size, + lsmash_box_type_t child_type, + uint32_t *child_size +); + +int isom_setup_visual_description +( + isom_stsd_t *stsd, + lsmash_codec_type_t sample_type, + lsmash_video_summary_t *summary +); + +int isom_setup_audio_description +( + isom_stsd_t *stsd, + lsmash_codec_type_t sample_type, + lsmash_audio_summary_t *summary +); + +int isom_setup_tx3g_description +( + isom_stsd_t *stsd, + lsmash_summary_t *summary +); + +lsmash_summary_t *isom_create_video_summary_from_description +( + isom_sample_entry_t *sample_entry +); + +lsmash_summary_t *isom_create_audio_summary_from_description +( + isom_sample_entry_t *sample_entry +); + +int isom_compare_opaque_extensions +( + lsmash_summary_t *a, + lsmash_summary_t *b +); + +int isom_get_implicit_qt_fixed_comp_audio_sample_quants +( + isom_audio_entry_t *audio, + uint32_t *samples_per_packet, + uint32_t *constant_bytes_per_frame, + uint32_t *sample_size +); diff -Nru l-smash-1.9.1/codecs/dts.c l-smash-2.3.0/codecs/dts.c --- l-smash-1.9.1/codecs/dts.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/dts.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1710 @@ +/***************************************************************************** + * dts.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +/*************************************************************************** + ETSI TS 102 114 V1.2.1 (2002-12) + ETSI TS 102 114 V1.3.1 (2011-08) + ETSI TS 102 114 V1.4.1 (2012-09) + + IMPLEMENTATION OF DTS AUDIO IN MEDIA FILES BASED ON ISO/IEC 14496 + Document No.: 9302J81100 + Revision: F + Version: 1.3 +***************************************************************************/ +#include "dts.h" + +#define DTS_MIN_CORE_SIZE 96 +#define DTS_MAX_STREAM_CONSTRUCTION 21 +#define DTS_SPECIFIC_BOX_MIN_LENGTH 28 + +typedef enum +{ + DTS_SYNCWORD_CORE = 0x7FFE8001, + DTS_SYNCWORD_XCH = 0x5A5A5A5A, + DTS_SYNCWORD_XXCH = 0x47004A03, + DTS_SYNCWORD_X96K = 0x1D95F262, + DTS_SYNCWORD_XBR = 0x655E315E, + DTS_SYNCWORD_LBR = 0x0A801921, + DTS_SYNCWORD_XLL = 0x41A29547, + DTS_SYNCWORD_SUBSTREAM = 0x64582025, + DTS_SYNCWORD_SUBSTREAM_CORE = 0x02b09261, +} dts_syncword; + +/* Loudspeaker Masks (up to 32-bit) for + * - nuCoreSpkrActivityMask + * - nuXXChSpkrLayoutMask + * - DownMixChMapMask + * - nChMask + * - nSpkrMask */ +typedef enum +{ + DTS_LOUDSPEAKER_MASK32_C = 0x00000001, /* Centre in front of listener */ + DTS_LOUDSPEAKER_MASK32_L = 0x00000002, /* Left in front */ + DTS_LOUDSPEAKER_MASK32_R = 0x00000004, /* Right in front */ + DTS_LOUDSPEAKER_MASK32_LS = 0x00000008, /* Left surround on side in rear */ + DTS_LOUDSPEAKER_MASK32_RS = 0x00000010, /* Right surround on side in rear */ + DTS_LOUDSPEAKER_MASK32_LFE1 = 0x00000020, /* Low frequency effects subwoofer */ + DTS_LOUDSPEAKER_MASK32_CS = 0x00000040, /* Centre surround in rear */ + DTS_LOUDSPEAKER_MASK32_LSR = 0x00000080, /* Left surround in rear */ + DTS_LOUDSPEAKER_MASK32_RSR = 0x00000100, /* Right surround in rear */ + DTS_LOUDSPEAKER_MASK32_LSS = 0x00000200, /* Left surround on side */ + DTS_LOUDSPEAKER_MASK32_RSS = 0x00000400, /* Right surround on side */ + DTS_LOUDSPEAKER_MASK32_LC = 0x00000800, /* Between left and centre in front */ + DTS_LOUDSPEAKER_MASK32_RC = 0x00001000, /* Between right and centre in front */ + DTS_LOUDSPEAKER_MASK32_LH = 0x00002000, /* Left height in front */ + DTS_LOUDSPEAKER_MASK32_CH = 0x00004000, /* Centre Height in front */ + DTS_LOUDSPEAKER_MASK32_RH = 0x00008000, /* Right Height in front */ + DTS_LOUDSPEAKER_MASK32_LFE2 = 0x00010000, /* Second low frequency effects subwoofer */ + DTS_LOUDSPEAKER_MASK32_LW = 0x00020000, /* Left on side in front */ + DTS_LOUDSPEAKER_MASK32_RW = 0x00040000, /* Right on side in front */ + DTS_LOUDSPEAKER_MASK32_OH = 0x00080000, /* Over the listener's head */ + DTS_LOUDSPEAKER_MASK32_LHS = 0x00100000, /* Left height on side */ + DTS_LOUDSPEAKER_MASK32_RHS = 0x00200000, /* Right height on side */ + DTS_LOUDSPEAKER_MASK32_CHR = 0x00400000, /* Centre height in rear */ + DTS_LOUDSPEAKER_MASK32_LHR = 0x00800000, /* Left height in rear */ + DTS_LOUDSPEAKER_MASK32_RHR = 0x01000000, /* Right height in rear */ + DTS_LOUDSPEAKER_MASK32_CL = 0x02000000, /* Centre in the plane lower than listener's ears */ + DTS_LOUDSPEAKER_MASK32_LL = 0x04000000, /* Left in the plane lower than listener's ears */ + DTS_LOUDSPEAKER_MASK32_RL = 0x08000000, /* Right in the plane lower than listener's ears */ +} dts_loudspeaker_mask; + +/* Loudspeaker Masks (up to 16-bit) for + * - nuSpkrActivityMask + * - nuStndrSpkrLayoutMask + * - nuMixOutChMask + * - ChannelLayout of DTSSpecificBox */ +typedef enum +{ + DTS_CHANNEL_LAYOUT_C = 0x0001, /* Centre in front of listener */ + DTS_CHANNEL_LAYOUT_L_R = 0x0002, /* Left/Right in front */ + DTS_CHANNEL_LAYOUT_LS_RS = 0x0004, /* Left/Right surround on side in rear */ + DTS_CHANNEL_LAYOUT_LFE1 = 0x0008, /* Low frequency effects subwoofer */ + DTS_CHANNEL_LAYOUT_CS = 0x0010, /* Centre surround in rear */ + DTS_CHANNEL_LAYOUT_LH_RH = 0x0020, /* Left/Right height in front */ + DTS_CHANNEL_LAYOUT_LSR_RSR = 0x0040, /* Left/Right surround in rear */ + DTS_CHANNEL_LAYOUT_CH = 0x0080, /* Centre height in front */ + DTS_CHANNEL_LAYOUT_OH = 0x0100, /* Over the listener's head */ + DTS_CHANNEL_LAYOUT_LC_RC = 0x0200, /* Between left/right and centre in front */ + DTS_CHANNEL_LAYOUT_LW_RW = 0x0400, /* Left/Right on side in front */ + DTS_CHANNEL_LAYOUT_LSS_RSS = 0x0800, /* Left/Right surround on side */ + DTS_CHANNEL_LAYOUT_LFE2 = 0x1000, /* Second low frequency effects subwoofer */ + DTS_CHANNEL_LAYOUT_LHS_RHS = 0x2000, /* Left/Right height on side */ + DTS_CHANNEL_LAYOUT_CHR = 0x4000, /* Centre height in rear */ + DTS_CHANNEL_LAYOUT_LHR_RHR = 0x8000, /* Left/Right height in rear */ +} dts_channel_layout; + +static const lsmash_dts_construction_flag construction_info[DTS_MAX_STREAM_CONSTRUCTION + 1] = + { + 0, + DTS_CORE_SUBSTREAM_CORE_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_X96_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG, + DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_X96_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG, + DTS_EXT_SUBSTREAM_XLL_FLAG, + DTS_EXT_SUBSTREAM_LBR_FLAG, + DTS_EXT_SUBSTREAM_CORE_FLAG, + DTS_EXT_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG, + DTS_EXT_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG , + }; + +void dts_setup_parser( dts_info_t *info ) +{ + dts_extension_info_t *exss = &info->exss[0]; + /* By default the core substream data, if present, has the nuBcCoreExtSSIndex = 0 and the nuBcCoreAssetIndex = 0. + * Therefore, we can treat as if one extension substream is there even if no extension substreams. */ + exss->nuNumAudioPresnt = 1; + exss->nuNumAssets = 1; + exss->bBcCorePresent [0] = 0; + exss->nuBcCoreExtSSIndex[0] = 0; + exss->nuBcCoreAssetIndex[0] = 0; +} + +struct lsmash_dts_reserved_box_tag +{ + uint32_t size; + uint8_t *data; +}; + +int lsmash_append_dts_reserved_box( lsmash_dts_specific_parameters_t *param, uint8_t *box_data, uint32_t box_size ) +{ + if( !param || !box_data || box_size == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + param->box = lsmash_malloc( sizeof(lsmash_dts_reserved_box_t) ); + if( !param->box ) + return LSMASH_ERR_MEMORY_ALLOC; + param->box->data = lsmash_memdup( box_data, box_size ); + if( !param->box->data ) + { + lsmash_freep( ¶m->box ); + return LSMASH_ERR_MEMORY_ALLOC; + } + param->box->size = box_size; + return 0; +} + +void lsmash_remove_dts_reserved_box( lsmash_dts_specific_parameters_t *param ) +{ + if( !param->box ) + return; + lsmash_free( param->box->data ); + lsmash_freep( ¶m->box ); +} + +void dts_destruct_specific_data( void *data ) +{ + if( !data ) + return; + lsmash_remove_dts_reserved_box( data ); + lsmash_free( data ); +} + +uint8_t lsmash_dts_get_stream_construction( lsmash_dts_construction_flag flags ) +{ + uint8_t StreamConstruction; + for( StreamConstruction = 1; StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION; StreamConstruction++ ) + if( flags == construction_info[StreamConstruction] ) + break; + /* For any stream type not listed in the above table, + * StreamConstruction shall be set to 0 and the codingname shall default to 'dtsh'. */ + return StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION ? StreamConstruction : 0; +} + +lsmash_dts_construction_flag lsmash_dts_get_construction_flags( uint8_t stream_construction ) +{ + if( stream_construction <= DTS_MAX_STREAM_CONSTRUCTION ) + return construction_info[stream_construction]; + return 0; +} + +lsmash_codec_type_t lsmash_dts_get_codingname( lsmash_dts_specific_parameters_t *param ) +{ + assert( param->StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION ); + if( param->MultiAssetFlag ) + return ISOM_CODEC_TYPE_DTSH_AUDIO; /* Multiple asset streams shall use the 'dtsh' coding_name. */ + static lsmash_codec_type_t codingname_table[DTS_MAX_STREAM_CONSTRUCTION + 1] = { LSMASH_CODEC_TYPE_INITIALIZER }; + if( lsmash_check_codec_type_identical( codingname_table[0], LSMASH_CODEC_TYPE_UNSPECIFIED ) ) + { + int i = 0; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; /* Undefined stream types shall be set to 0 and the codingname shall default to 'dtsh'. */ + codingname_table[i++] = ISOM_CODEC_TYPE_DTSC_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSC_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSC_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSE_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; + codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; + } + return codingname_table[ param->StreamConstruction ]; +} + +uint8_t *lsmash_create_dts_specific_info( lsmash_dts_specific_parameters_t *param, uint32_t *data_length ) +{ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + lsmash_bits_init( &bits, &bs ); + int reserved_box_present = (param->box && param->box->data && param->box->size); + uint32_t buffer_length = DTS_SPECIFIC_BOX_MIN_LENGTH + (reserved_box_present ? param->box->size : 0); + uint8_t buffer[buffer_length]; + memset( buffer, 0, buffer_length ); + bs.buffer.data = buffer; + bs.buffer.alloc = buffer_length; + /* Create a DTSSpecificBox. */ + lsmash_bits_put( &bits, 32, 0 ); /* box size */ + lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DDTS.fourcc ); /* box type: 'ddts' */ + lsmash_bits_put( &bits, 32, param->DTSSamplingFrequency ); + lsmash_bits_put( &bits, 32, param->maxBitrate ); /* maxBitrate; setup by isom_update_bitrate_description */ + lsmash_bits_put( &bits, 32, param->avgBitrate ); /* avgBitrate; setup by isom_update_bitrate_description */ + lsmash_bits_put( &bits, 8, param->pcmSampleDepth ); + lsmash_bits_put( &bits, 2, param->FrameDuration ); + lsmash_bits_put( &bits, 5, param->StreamConstruction ); + lsmash_bits_put( &bits, 1, param->CoreLFEPresent ); + lsmash_bits_put( &bits, 6, param->CoreLayout ); + lsmash_bits_put( &bits, 14, param->CoreSize ); + lsmash_bits_put( &bits, 1, param->StereoDownmix ); + lsmash_bits_put( &bits, 3, param->RepresentationType ); + lsmash_bits_put( &bits, 16, param->ChannelLayout ); + lsmash_bits_put( &bits, 1, param->MultiAssetFlag ); + lsmash_bits_put( &bits, 1, param->LBRDurationMod ); + lsmash_bits_put( &bits, 1, reserved_box_present ); + lsmash_bits_put( &bits, 5, 0 ); /* Reserved */ + /* ReservedBox */ + if( reserved_box_present ) + for( uint32_t i = 0; i < param->box->size; i++ ) + lsmash_bits_put( &bits, 8, param->box->data[i] ); + /* */ + uint8_t *data = lsmash_bits_export_data( &bits, data_length ); + /* Update box size. */ + LSMASH_SET_BE32( data, *data_length ); + return data; +} + +int lsmash_setup_dts_specific_parameters_from_frame( lsmash_dts_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) +{ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t buffer[DTS_MAX_EXSS_SIZE] = { 0 }; + bs.buffer.data = buffer; + bs.buffer.store = data_length; + bs.buffer.alloc = DTS_MAX_EXSS_SIZE; + dts_info_t *info = &(dts_info_t){ .bits = &bits }; + info->bits = &bits; + lsmash_bits_init( &bits, &bs ); + memcpy( buffer, data, LSMASH_MIN( data_length, DTS_MAX_EXSS_SIZE ) ); + dts_setup_parser( info ); + uint64_t next_frame_pos = 0; + while( 1 ) + { + int err; + /* Seek to the head of the next syncframe. */ + bs.buffer.pos = LSMASH_MIN( data_length, next_frame_pos ); + /* Check the remainder length of the buffer. + * If there is enough length, then continue to parse the frame in it. + * The length 10 is the required byte length to get frame size. */ + uint64_t remain_size = lsmash_bs_get_remaining_buffer_size( &bs ); + if( bs.eob || (bs.eof && remain_size < 10) ) + goto setup_param; /* No more valid data. */ + /* Parse substream frame. */ + dts_substream_type prev_substream_type = info->substream_type; + info->substream_type = dts_get_substream_type( info ); + int (*dts_parse_frame)( dts_info_t * ) = NULL; + switch( info->substream_type ) + { + /* Decide substream frame parser and check if this frame and the previous frame belong to the same AU. */ + case DTS_SUBSTREAM_TYPE_CORE : + if( prev_substream_type != DTS_SUBSTREAM_TYPE_NONE ) + goto setup_param; + dts_parse_frame = dts_parse_core_substream; + break; + case DTS_SUBSTREAM_TYPE_EXTENSION : + { + uint8_t prev_exss_index = info->exss_index; + if( (err = dts_get_exss_index( info, &info->exss_index )) < 0 ) + return err; + if( prev_substream_type == DTS_SUBSTREAM_TYPE_EXTENSION && info->exss_index <= prev_exss_index ) + goto setup_param; + dts_parse_frame = dts_parse_extension_substream; + break; + } + default : + /* An unknown stream type is detected. */ + return LSMASH_ERR_NAMELESS; + } + info->frame_size = 0; + if( (err = dts_parse_frame( info )) < 0 ) + return err; /* Failed to parse. */ + next_frame_pos += info->frame_size; + } +setup_param: + dts_update_specific_param( info ); + *param = info->ddts_param; + return 0; +} + +static uint64_t dts_bits_get( lsmash_bits_t *bits, uint32_t width, uint64_t *bits_pos ) +{ + *bits_pos += width; + return lsmash_bits_get( bits, width ); +} + +static int dts_get_channel_count_from_channel_layout( uint16_t channel_layout ) +{ +#define DTS_CHANNEL_PAIR_MASK \ + (DTS_CHANNEL_LAYOUT_L_R \ + | DTS_CHANNEL_LAYOUT_LS_RS \ + | DTS_CHANNEL_LAYOUT_LH_RH \ + | DTS_CHANNEL_LAYOUT_LSR_RSR \ + | DTS_CHANNEL_LAYOUT_LC_RC \ + | DTS_CHANNEL_LAYOUT_LW_RW \ + | DTS_CHANNEL_LAYOUT_LSS_RSS \ + | DTS_CHANNEL_LAYOUT_LHS_RHS \ + | DTS_CHANNEL_LAYOUT_LHR_RHR) + return lsmash_count_bits( channel_layout ) + + lsmash_count_bits( channel_layout & DTS_CHANNEL_PAIR_MASK ); +#undef DTS_CHANNEL_PAIR_MASK +} + +static uint32_t dts_get_channel_layout_from_ls_mask32( uint32_t mask ) +{ + uint32_t layout = 0; + if( mask & DTS_LOUDSPEAKER_MASK32_C ) + layout |= DTS_CHANNEL_LAYOUT_C; + if( mask & (DTS_LOUDSPEAKER_MASK32_L | DTS_LOUDSPEAKER_MASK32_R) ) + layout |= DTS_CHANNEL_LAYOUT_L_R; + if( mask & (DTS_LOUDSPEAKER_MASK32_LS | DTS_LOUDSPEAKER_MASK32_RS) ) + layout |= DTS_CHANNEL_LAYOUT_LS_RS; + if( mask & DTS_LOUDSPEAKER_MASK32_LFE1 ) + layout |= DTS_CHANNEL_LAYOUT_LFE1; + if( mask & DTS_LOUDSPEAKER_MASK32_CS ) + layout |= DTS_CHANNEL_LAYOUT_CS; + if( mask & (DTS_LOUDSPEAKER_MASK32_LH | DTS_LOUDSPEAKER_MASK32_RH) ) + layout |= DTS_CHANNEL_LAYOUT_LH_RH; + if( mask & (DTS_LOUDSPEAKER_MASK32_LSR | DTS_LOUDSPEAKER_MASK32_RSR) ) + layout |= DTS_CHANNEL_LAYOUT_LSR_RSR; + if( mask & DTS_LOUDSPEAKER_MASK32_CH ) + layout |= DTS_CHANNEL_LAYOUT_CH; + if( mask & DTS_LOUDSPEAKER_MASK32_OH ) + layout |= DTS_CHANNEL_LAYOUT_OH; + if( mask & (DTS_LOUDSPEAKER_MASK32_LC | DTS_LOUDSPEAKER_MASK32_RC) ) + layout |= DTS_CHANNEL_LAYOUT_LC_RC; + if( mask & (DTS_LOUDSPEAKER_MASK32_LW | DTS_LOUDSPEAKER_MASK32_RW) ) + layout |= DTS_CHANNEL_LAYOUT_LW_RW; + if( mask & (DTS_LOUDSPEAKER_MASK32_LSS | DTS_LOUDSPEAKER_MASK32_RSS) ) + layout |= DTS_CHANNEL_LAYOUT_LSS_RSS; + if( mask & DTS_LOUDSPEAKER_MASK32_LFE2 ) + layout |= DTS_CHANNEL_LAYOUT_LFE2; + if( mask & (DTS_LOUDSPEAKER_MASK32_LHS | DTS_LOUDSPEAKER_MASK32_RHS) ) + layout |= DTS_CHANNEL_LAYOUT_LHS_RHS; + if( mask & DTS_LOUDSPEAKER_MASK32_CHR ) + layout |= DTS_CHANNEL_LAYOUT_CHR; + if( mask & (DTS_LOUDSPEAKER_MASK32_LHR | DTS_LOUDSPEAKER_MASK32_RHR) ) + layout |= DTS_CHANNEL_LAYOUT_LHR_RHR; + return layout; +} + +/* for channels which cannot be expressed by ChannelLayout; CL, LL and RL */ +static inline uint8_t dts_get_lower_channels_from_ls_mask32( uint32_t mask ) +{ + return (mask >> 25) & 0x7; +} + +static void dts_parse_xll_navigation( lsmash_bits_t *bits, dts_xll_info_t *xll, int nuBits4ExSSFsize, uint64_t *bits_pos ) +{ + xll->size = dts_bits_get( bits, nuBits4ExSSFsize, bits_pos ) + 1; /* nuExSSXLLFsize (nuBits4ExSSFsize) */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bExSSXLLSyncPresent (1) */ + { + dts_bits_get( bits, 4, bits_pos ); /* nuPeakBRCntrlBuffSzkB (4) */ + int nuBitsInitDecDly = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuBitsInitDecDly (5) */ + dts_bits_get( bits, nuBitsInitDecDly, bits_pos ); /* nuInitLLDecDlyFrames (nuBitsInitDecDly) */ + dts_bits_get( bits, nuBits4ExSSFsize, bits_pos ); /* nuExSSXLLSyncOffset (nuBits4ExSSFsize) */ + } +} + +static void dts_parse_lbr_navigation( lsmash_bits_t *bits, dts_lbr_info_t *lbr, uint64_t *bits_pos ) +{ + lbr->size = dts_bits_get( bits, 14, bits_pos ); /* nuExSSLBRFsize (14) */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bExSSLBRSyncPresent (1) */ + dts_bits_get( bits, 2, bits_pos ); /* nuExSSLBRSyncDistInFrames (2) */ +} + +static int dts_parse_asset_descriptor( dts_info_t *info, uint64_t *bits_pos ) +{ + lsmash_bits_t *bits = info->bits; + dts_extension_info_t *exss = &info->exss[ info->exss_index ]; + /* Audio asset descriptor */ + uint64_t asset_descriptor_pos = *bits_pos; + int nuAssetDescriptFsize = dts_bits_get( bits, 9, bits_pos ) + 1; /* nuAssetDescriptFsize (9) */ + dts_audio_asset_t *asset = &exss->asset[ dts_bits_get( bits, 3, bits_pos ) ]; /* nuAssetIndex (3) */ + /* Static metadata */ + int bEmbeddedStereoFlag = 0; + int bEmbeddedSixChFlag = 0; + int nuTotalNumChs = 0; + if( exss->bStaticFieldsPresent ) + { + if( dts_bits_get( bits, 1, bits_pos ) ) /* bAssetTypeDescrPresent (1)*/ + dts_bits_get( bits, 4, bits_pos ); /* nuAssetTypeDescriptor (4) */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bLanguageDescrPresent (1) */ + dts_bits_get( bits, 24, bits_pos ); /* LanguageDescriptor (24) */ + if( dts_bits_get( bits, 1, bits_pos ) ) + { + int nuInfoTextByteSize = dts_bits_get( bits, 10, bits_pos ) + 1; /* nuInfoTextByteSize (10) */ + dts_bits_get( bits, nuInfoTextByteSize * 8, bits_pos ); /* InfoTextString (nuInfoTextByteSize) */ + } + int nuBitResolution = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuBitResolution (5) */ + exss->bit_resolution = LSMASH_MAX( exss->bit_resolution, nuBitResolution ); + int nuMaxSampleRate = dts_bits_get( bits, 4, bits_pos ); /* nuMaxSampleRate (4) */ + static const uint32_t source_sample_rate_table[16] = + { + 8000, 16000, 32000, 64000, 128000, + 22050, 44100, 88200, 176400, 352800, + 12000, 24000, 48000, 96000, 192000, 384000 + }; + exss->sampling_frequency = LSMASH_MAX( exss->sampling_frequency, source_sample_rate_table[nuMaxSampleRate] ); + nuTotalNumChs = dts_bits_get( bits, 8, bits_pos ) + 1; /* nuTotalNumChs (8) */ + asset->bOne2OneMapChannels2Speakers = dts_bits_get( bits, 1, bits_pos ); /* bOne2OneMapChannels2Speakers (1) */ + if( asset->bOne2OneMapChannels2Speakers ) + { + if( nuTotalNumChs > 2 ) + { + bEmbeddedStereoFlag = dts_bits_get( bits, 1, bits_pos ); /* bEmbeddedStereoFlag (1) */ + exss->stereo_downmix |= bEmbeddedStereoFlag; + } + if( nuTotalNumChs > 6 ) + bEmbeddedSixChFlag = dts_bits_get( bits, 1, bits_pos ); /* bEmbeddedSixChFlag (1) */ + int nuNumBits4SAMask; + if( dts_bits_get( bits, 1, bits_pos ) ) /* bSpkrMaskEnabled (1) */ + { + nuNumBits4SAMask = (dts_bits_get( bits, 2, bits_pos ) + 1) << 2; /* nuNumBits4SAMask (2) */ + asset->channel_layout |= dts_bits_get( bits, nuNumBits4SAMask, bits_pos ); /* nuSpkrActivityMask (nuNumBits4SAMask) */ + } + else + /* The specification doesn't mention the value of nuNumBits4SAMask if bSpkrMaskEnabled is set to 0. */ + nuNumBits4SAMask = 16; + int nuNumSpkrRemapSets = dts_bits_get( bits, 3, bits_pos ); + int nuStndrSpkrLayoutMask[8] = { 0 }; + for( int ns = 0; ns < nuNumSpkrRemapSets; ns++ ) + nuStndrSpkrLayoutMask[ns] = dts_bits_get( bits, nuNumBits4SAMask, bits_pos ); + for( int ns = 0; ns < nuNumSpkrRemapSets; ns++ ) + { + int nuNumSpeakers = dts_get_channel_count_from_channel_layout( nuStndrSpkrLayoutMask[ns] ); + int nuNumDecCh4Remap = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuNumDecCh4Remap[ns] (5) */ + for( int nCh = 0; nCh < nuNumSpeakers; nCh++ ) + { + uint32_t nuRemapDecChMask = dts_bits_get( bits, nuNumDecCh4Remap, bits_pos ); + int nCoef = lsmash_count_bits( nuRemapDecChMask ); + for( int nc = 0; nc < nCoef; nc++ ) + dts_bits_get( bits, 5, bits_pos ); /* nuSpkrRemapCodes[ns][nCh][nc] (5) */ + } + } + } + else + { + asset->nuRepresentationType = dts_bits_get( bits, 3, bits_pos ); /* nuRepresentationType (3) */ + if( asset->nuRepresentationType == 2 + || asset->nuRepresentationType == 3 ) + nuTotalNumChs = 2; + } + } + /* Dynamic metadata */ + int bDRCCoefPresent = dts_bits_get( bits, 1, bits_pos ); /* bDRCCoefPresent (1) */ + if( bDRCCoefPresent ) + dts_bits_get( bits, 8, bits_pos ); /* nuDRCCode (8) */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bDialNormPresent (1) */ + dts_bits_get( bits, 5, bits_pos ); /* nuDialNormCode (5) */ + if( bDRCCoefPresent && bEmbeddedStereoFlag ) + dts_bits_get( bits, 8, bits_pos ); /* nuDRC2ChDmixCode (8) */ + int bMixMetadataPresent; + if( exss->bMixMetadataEnbl ) + bMixMetadataPresent = dts_bits_get( bits, 1, bits_pos ); /* bMixMetadataPresent (1) */ + else + bMixMetadataPresent = 0; + if( bMixMetadataPresent ) + { + dts_bits_get( bits, 7, bits_pos ); /* bExternalMixFlag (1) + * nuPostMixGainAdjCode (7) */ + if( dts_bits_get( bits, 2, bits_pos ) < 3 ) /* nuControlMixerDRC (2) */ + dts_bits_get( bits, 3, bits_pos ); /* nuLimit4EmbeddedDRC (3) */ + else + dts_bits_get( bits, 8, bits_pos ); /* nuCustomDRCCode (8) */ + int bEnblPerChMainAudioScale = dts_bits_get( bits, 1, bits_pos ); /* bEnblPerChMainAudioScale (1) */ + for( uint8_t ns = 0; ns < exss->nuNumMixOutConfigs; ns++ ) + if( bEnblPerChMainAudioScale ) + for( uint8_t nCh = 0; nCh < exss->nNumMixOutCh[ns]; nCh++ ) + dts_bits_get( bits, 6, bits_pos ); /* nuMainAudioScaleCode[ns][nCh] (6) */ + else + dts_bits_get( bits, 6, bits_pos ); /* nuMainAudioScaleCode[ns][0] (6) */ + int nEmDM = 1; + int nDecCh[3] = { nuTotalNumChs, 0, 0 }; + if( bEmbeddedSixChFlag ) + { + nDecCh[nEmDM] = 6; + ++nEmDM; + } + if( bEmbeddedStereoFlag ) + { + nDecCh[nEmDM] = 2; + ++nEmDM; + } + for( uint8_t ns = 0; ns < exss->nuNumMixOutConfigs; ns++ ) + for( int nE = 0; nE < nEmDM; nE++ ) + for( int nCh = 0; nCh < nDecCh[nE]; nCh++ ) + { + int nuMixMapMask = dts_bits_get( bits, exss->nNumMixOutCh[ns], bits_pos ); /* nuMixMapMask (nNumMixOutCh[ns]) */ + int nuNumMixCoefs = lsmash_count_bits( nuMixMapMask ); + for( int nC = 0; nC < nuNumMixCoefs; nC++ ) + dts_bits_get( bits, 6, bits_pos ); /* nuMixCoeffs[ns][nE][nCh][nC] (6) */ + } + } + /* Decoder navigation data */ + asset->nuCodingMode = dts_bits_get( bits, 2, bits_pos ); /* nuCodingMode (2) */ + switch( asset->nuCodingMode ) + { + case 0 : /* DTS-HD Coding Mode that may contain multiple coding components */ + { + int nuCoreExtensionMask = dts_bits_get( bits, 12, bits_pos ); /* nuCoreExtensionMask (12) */ + asset->nuCoreExtensionMask = nuCoreExtensionMask; + if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_CORE_FLAG ) + { + asset->core.frame_size = dts_bits_get( bits, 14, bits_pos ) + 1; /* nuExSSCoreFsize (14) */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bExSSCoreSyncPresent (1) */ + dts_bits_get( bits, 2, bits_pos ); /* nuExSSCoreSyncDistInFrames (2) */ + } + if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XBR_FLAG ) + asset->xbr_size = dts_bits_get( bits, 14, bits_pos ) + 1; + if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XXCH_FLAG ) + asset->core.xxch.size = dts_bits_get( bits, 14, bits_pos ) + 1; + if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_X96_FLAG ) + asset->x96_size = dts_bits_get( bits, 12, bits_pos ) + 1; + if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_LBR_FLAG ) + dts_parse_lbr_navigation( bits, &asset->lbr, bits_pos ); + if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XLL_FLAG ) + dts_parse_xll_navigation( bits, &asset->xll, exss->nuBits4ExSSFsize, bits_pos ); + break; + } + case 1 : /* DTS-HD Loss-less coding mode without CBR component */ + dts_parse_xll_navigation( bits, &asset->xll, exss->nuBits4ExSSFsize, bits_pos ); + break; + case 2 : /* DTS-HD Low bit-rate mode */ + dts_parse_lbr_navigation( bits, &asset->lbr, bits_pos ); + break; + case 3 : /* Auxiliary coding mode */ + asset->aux_size = dts_bits_get( bits, 14, bits_pos ) + 1; /* nuExSSAuxFsize (14) */ + break; + default : + assert( 0 ); + break; + } + dts_bits_get( bits, nuAssetDescriptFsize * 8 - (*bits_pos - asset_descriptor_pos), bits_pos ); /* Skip remaining part of Audio asset descriptor. */ + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_xxch( dts_info_t *info, uint64_t *bits_pos, dts_xxch_info_t *xxch ) +{ + lsmash_bits_t *bits = info->bits; + /* XXCH Frame Header */ + uint64_t xxch_pos = *bits_pos - 32; /* SYNCXXCh (32) */ + uint64_t nuHeaderSizeXXCh = dts_bits_get( bits, 6, bits_pos ) + 1; /* nuHeaderSizeXXCh (6) */ + dts_bits_get( bits, 1, bits_pos ); /* bCRCPresent4ChSetHeaderXXCh (1) */ + int nuBits4SpkrMaskXXCh = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuBits4SpkrMaskXXCh (5) */ + int nuNumChSetsInXXCh = dts_bits_get( bits, 2, bits_pos ) + 1; /* nuNumChSetsInXXCh (2) */ + for( int nChSet = 0; nChSet < nuNumChSetsInXXCh; nChSet++ ) + dts_bits_get( bits, 14, bits_pos ); /* pnuChSetFsizeXXCh[nChSet] - 1 (14) */ + /* A 5.1 decoder uses this AMODE to configure its decoded outputs to C, L, R, Ls and Rs layout. + * On the other hand a 7.1 decoder ignores the AMODE information from the core stream and uses + * instead the nuCoreSpkrActivityMask (C, L, R, LFE1, Lss and Rss) and the nuXXChSpkrLayoutMask + * (Lsr and Rsr) from the XXCh stream to get the original 7.1 speaker layout (C, L, R, LFE1, Lss, + * Rsr, Lsr and Rsr) and configures its outputs accordingly. */ + uint32_t xxch_mask = dts_bits_get( bits, nuBits4SpkrMaskXXCh, bits_pos ); /* nuCoreSpkrActivityMask (nuBits4SpkrMaskXXCh) */ + xxch->channel_layout |= dts_get_channel_layout_from_ls_mask32( xxch_mask ); + xxch->lower_planes = dts_get_lower_channels_from_ls_mask32( xxch_mask ); + dts_bits_get( bits, nuHeaderSizeXXCh * 8 - (*bits_pos - xxch_pos), bits_pos ); /* Skip remaining part of XXCH Frame Header. */ + for( int nChSet = 0; nChSet < nuNumChSetsInXXCh; nChSet++ ) + { + /* XXCH Channel Set Header */ + xxch_pos = *bits_pos; + uint64_t nuXXChChSetHeaderSize = dts_bits_get( bits, 7, bits_pos ) + 1; /* nuXXChChSetHeaderSize (7)*/ + dts_bits_get( bits, 3, bits_pos ); /* nuChInChSetXXCh (3) */ + if( nuBits4SpkrMaskXXCh > 6 ) + { + xxch_mask = dts_bits_get( bits, nuBits4SpkrMaskXXCh - 6, bits_pos ) << 6; /* nuXXChSpkrLayoutMask (nuBits4SpkrMaskXXCh - 6) */ + xxch->channel_layout |= dts_get_channel_layout_from_ls_mask32( xxch_mask ); + xxch->lower_planes |= dts_get_lower_channels_from_ls_mask32( xxch_mask ); + } +#if 0 /* FIXME: Can we detect stereo downmixing from only XXCH data within the core substream? */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bDownMixCoeffCodeEmbedded (1) */ + { + int bDownMixEmbedded = dts_bits_get( bits, 1, bits_pos ); /* bDownMixEmbedded (1) */ + dts_bits_get( bits, 6, bits_pos ); /* nDmixScaleFactor (6) */ + uint32_t DownMixChMapMask[8]; + for( int nCh = 0; nCh < nuChInChSetXXCh; nCh++ ) + DownMixChMapMask[nCh] = dts_bits_get( bits, nuBits4SpkrMaskXXCh, bits_pos ); + } +#endif + dts_bits_get( bits, nuXXChChSetHeaderSize * 8 - (*bits_pos - xxch_pos), bits_pos ); /* Skip remaining part of XXCH Channel Set Header. */ + } + return 0; +} + +static int dts_parse_core_xxch( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) +{ + if( core->extension_audio_descriptor == 0 + || core->extension_audio_descriptor == 3 ) + return LSMASH_ERR_INVALID_DATA; + int err = dts_parse_xxch( info, bits_pos, &core->xxch ); + if( err < 0 ) + return err; + info->flags |= DTS_CORE_SUBSTREAM_XXCH_FLAG; + return info->bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_exss_xxch( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) +{ + lsmash_bits_t *bits = info->bits; + if( DTS_SYNCWORD_XXCH != dts_bits_get( bits, 32, bits_pos ) ) + return LSMASH_ERR_INVALID_DATA; + int err = dts_parse_xxch( info, bits_pos, &core->xxch ); + if( err < 0 ) + return err; + info->flags |= DTS_EXT_SUBSTREAM_XXCH_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_core_x96( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) +{ + if( core->extension_audio_descriptor != 2 + && core->extension_audio_descriptor != 3 ) + return 0; /* Probably this is not an X96 extension. We skip this anyway. */ + lsmash_bits_t *bits = info->bits; + /* DTS_BCCORE_X96 Frame Header */ + /* SYNCX96 (32) */ + /* To reduce the probability of false synchronization caused by the presence of pseudo sync words, it is + * imperative to check the distance between the detected sync word and the end of current frame. This + * distance in bytes shall match the value of FSIZE96. */ + uint64_t FSIZE96 = ((lsmash_bs_show_byte( bits->bs, 0 ) << 4) + | ((lsmash_bs_show_byte( bits->bs, 1 ) >> 4) & 0x0F)) + 1; + if( core->frame_size * 8 != (*bits_pos - 32 + FSIZE96 * 8) ) + return 0; /* Encountered four emulation bytes (pseudo sync word). */ + dts_bits_get( bits, 16, bits_pos ); /* FSIZE96 (12) + * REVNO (4) */ + core->sampling_frequency *= 2; + core->frame_duration *= 2; + info->flags |= DTS_CORE_SUBSTREAM_X96_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_core_xch( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) +{ + if( core->extension_audio_descriptor != 0 + && core->extension_audio_descriptor != 3 ) + return 0; /* Probably this is not an XCh extension. We skip this anyway. */ + lsmash_bits_t *bits = info->bits; + /* XCH Frame Header */ + /* XChSYNC (32) */ + /* For compatibility reasons with legacy bitstreams the estimated distance in bytes is checked against + * the XChFSIZE+1 as well as the XChFSIZE. The XCh synchronization is pronounced if the distance matches + * either of these two values. */ + uint64_t XChFSIZE = (lsmash_bs_show_byte( bits->bs, 0 ) << 2) + | ((lsmash_bs_show_byte( bits->bs, 1 ) >> 6) & 0x03); + if( core->frame_size * 8 != (*bits_pos - 32 + (XChFSIZE + 1) * 8) + && core->frame_size * 8 != (*bits_pos - 32 + XChFSIZE * 8) ) + return 0; /* Encountered four emulation bytes (pseudo sync word). */ + if( ((lsmash_bs_show_byte( bits->bs, 1 ) >> 2) & 0xF) != 1 ) + return 0; /* A known value of AMODE is only 1. Otherwise just skip. */ + dts_bits_get( bits, 16, bits_pos ); /* XChFSIZE (10) + * AMODE (4) + * byte align (2) */ + core->channel_layout |= DTS_CHANNEL_LAYOUT_CS; + info->flags |= DTS_CORE_SUBSTREAM_XCH_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_exss_xbr( dts_info_t *info, uint64_t *bits_pos ) +{ + lsmash_bits_t *bits = info->bits; + /* XBR Frame Header */ + uint64_t xbr_pos = *bits_pos; + if( DTS_SYNCWORD_XBR != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCXBR (32) */ + return LSMASH_ERR_INVALID_DATA; + uint64_t nHeaderSizeXBR = dts_bits_get( bits, 6, bits_pos ) + 1; /* nHeaderSizeXBR (6) */ + dts_bits_get( bits, nHeaderSizeXBR * 8 - (*bits_pos - xbr_pos), bits_pos ); /* Skip the remaining bits in XBR Frame Header. */ + info->flags |= DTS_EXT_SUBSTREAM_XBR_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_exss_x96( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) +{ + lsmash_bits_t *bits = info->bits; + /* DTS_EXSUB_STREAM_X96 Frame Header */ + uint64_t x96_pos = *bits_pos; + if( DTS_SYNCWORD_X96K != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCX96 (32) */ + return LSMASH_ERR_INVALID_DATA; + uint64_t nHeaderSizeX96 = dts_bits_get( bits, 6, bits_pos ) + 1; /* nHeaderSizeXBR (6) */ + dts_bits_get( bits, nHeaderSizeX96 * 8 - (*bits_pos - x96_pos), bits_pos ); /* Skip the remaining bits in DTS_EXSUB_STREAM_X96 Frame Header. */ + /* What the fuck! The specification drops 'if' sentence. + * We assume the same behaviour for core substream. */ + core->sampling_frequency *= 2; + core->frame_duration *= 2; + info->flags |= DTS_EXT_SUBSTREAM_X96_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_exss_lbr( dts_info_t *info, uint64_t *bits_pos, dts_audio_asset_t *asset ) +{ + lsmash_bits_t *bits = info->bits; + dts_lbr_info_t *lbr = &asset->lbr; + if( DTS_SYNCWORD_LBR != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCEXTLBR (32) */ + return LSMASH_ERR_INVALID_DATA; + int ucFmtInfoCode = dts_bits_get( bits, 8, bits_pos ); + if( ucFmtInfoCode == 2 ) + { + /* LBR decoder initialization data */ + int nLBRSampleRateCode = dts_bits_get( bits, 8, bits_pos ); /* nLBRSampleRateCode (8) */ + int usLBRSpkrMask = dts_bits_get( bits, 16, bits_pos ); /* usLBRSpkrMask (16) */ + dts_bits_get( bits, 16, bits_pos ); /* nLBRversion (16) */ + int nLBRCompressedFlags = dts_bits_get( bits, 8, bits_pos ); /* nLBRCompressedFlags (8) */ + dts_bits_get( bits, 40, bits_pos ); /* nLBRBitRateMSnybbles (8) + * nLBROriginalBitRate_LSW (16) + * nLBRScaledBitRate_LSW (16) */ + static const uint32_t source_sample_rate_table[16] = + { + 8000, 16000, 32000, 0, 0, + 11025, 22050, 44100, 0, 0, + 12000, 24000, 48000, 0, 0, 0 + }; + enum LBRFlags + { + LBR_FLAG_24_BIT_SAMPLES = 0x01, /* 0b00000001 */ + LBR_FLAG_USE_LFE = 0x02, /* 0b00000010 */ + LBR_FLAG_BANDLMT_MASK = 0x1C, /* 0b00011100 */ + LBR_FLAG_STEREO_DOWNMIX = 0x20, /* 0b00100000 */ + LBR_FLAG_MULTICHANNEL_DOWNMIX = 0x40, /* 0b01000000 */ + }; + lbr->sampling_frequency = source_sample_rate_table[nLBRSampleRateCode]; + lbr->frame_duration = lbr->sampling_frequency < 16000 ? 1024 + : lbr->sampling_frequency < 32000 ? 2048 + : 4096; + lbr->channel_layout = ((usLBRSpkrMask >> 8) & 0xff) | ((usLBRSpkrMask << 8) & 0xff00); /* usLBRSpkrMask is little-endian. */ + lbr->stereo_downmix |= !!(nLBRCompressedFlags & LBR_FLAG_STEREO_DOWNMIX); + lbr->lfe_present |= !!(nLBRCompressedFlags & LBR_FLAG_USE_LFE); + lbr->duration_modifier |= ((nLBRCompressedFlags & LBR_FLAG_BANDLMT_MASK) == 0x04) + || ((nLBRCompressedFlags & LBR_FLAG_BANDLMT_MASK) == 0x0C); + lbr->sample_size = (nLBRCompressedFlags & LBR_FLAG_24_BIT_SAMPLES) ? 24 : 16; + } + else if( ucFmtInfoCode != 1 ) + return LSMASH_ERR_NAMELESS; /* unknown */ + info->flags |= DTS_EXT_SUBSTREAM_LBR_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_exss_xll( dts_info_t *info, uint64_t *bits_pos, dts_audio_asset_t *asset ) +{ + lsmash_bits_t *bits = info->bits; + dts_xll_info_t *xll = &asset->xll; + /* Common Header */ + uint64_t xll_pos = *bits_pos; + if( DTS_SYNCWORD_XLL != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCXLL (32) */ + return LSMASH_ERR_INVALID_DATA; + dts_bits_get( bits, 4, bits_pos ); /* nVersion (4) */ + uint64_t nHeaderSize = dts_bits_get( bits, 8, bits_pos ) + 1; /* nHeaderSize (8) */ + int nBits4FrameFsize = dts_bits_get( bits, 5, bits_pos ) + 1; /* nBits4FrameFsize (5) */ + dts_bits_get( bits, nBits4FrameFsize, bits_pos ); /* nLLFrameSize (nBits4FrameFsize) */ + int nNumChSetsInFrame = dts_bits_get( bits, 4, bits_pos ) + 1; /* nNumChSetsInFrame (4) */ + uint16_t nSegmentsInFrame = 1 << dts_bits_get( bits, 4, bits_pos ); /* nSegmentsInFrame (4) */ + uint16_t nSmplInSeg = 1 << dts_bits_get( bits, 4, bits_pos ); /* nSmplInSeg (4) */ + dts_bits_get( bits, 5, bits_pos ); /* nBits4SSize (5) */ + dts_bits_get( bits, 3, bits_pos ); /* nBandDataCRCEn (2) + * bScalableLSBs (1) */ + int nBits4ChMask = dts_bits_get( bits, 5, bits_pos ) + 1; /* nBits4ChMask (5) */ + dts_bits_get( bits, nHeaderSize * 8 - (*bits_pos - xll_pos), bits_pos ); /* Skip the remaining bits in Common Header. */ + int sum_nChSetLLChannel = 0; + uint32_t nFs1 = 0; + int nNumFreqBands1 = 0; + xll->channel_layout = 0; + for( int nChSet = 0; nChSet < nNumChSetsInFrame; nChSet++ ) + { + /* Channel Set Sub-Header */ + xll_pos = *bits_pos; + uint64_t nChSetHeaderSize = dts_bits_get( bits, 10, bits_pos ) + 1; /* nChSetHeaderSize (10) */ + int nChSetLLChannel = dts_bits_get( bits, 4, bits_pos ) + 1; /* nChSetLLChannel (4) */ + dts_bits_get( bits, nChSetLLChannel, bits_pos ); /* nResidualChEncode (nChSetLLChannel) */ + uint8_t nBitResolution = dts_bits_get( bits, 5, bits_pos ) + 1; /* nBitResolution (5) */ + dts_bits_get( bits, 5, bits_pos ); /* nBitWidth (5) */ + xll->pcm_resolution = LSMASH_MAX( xll->pcm_resolution, nBitResolution ); + static const uint32_t source_sample_rate_table[16] = + { + 8000, 16000, 32000, 64000, 128000, + 22050, 44100, 88200, 176400, 352800, + 12000, 24000, 48000, 96000, 192000, 384000 + }; + int sFreqIndex = dts_bits_get( bits, 4, bits_pos ); /* sFreqIndex (4) */ + uint32_t nFs = source_sample_rate_table[sFreqIndex]; + dts_bits_get( bits, 2, bits_pos ); /* nFsInterpolate (2) */ + int nReplacementSet = dts_bits_get( bits, 2, bits_pos ); /* nReplacementSet (2) */ + if( nReplacementSet > 0 ) + dts_bits_get( bits, 1, bits_pos ); /* bActiveReplaceSet (1) */ + if( asset->bOne2OneMapChannels2Speakers ) + { + /* Downmix is allowed only when the encoded channel represents a signal feed to a corresponding loudspeaker. */ + int bPrimaryChSet = dts_bits_get( bits, 1, bits_pos ); /* bPrimaryChSet (1) */ + int bDownmixCoeffCodeEmbedded = dts_bits_get( bits, 1, bits_pos ); /* bDownmixCoeffCodeEmbedded (1) */ + int nLLDownmixType = 0x7; /* 0b111: Unused */ + if( bDownmixCoeffCodeEmbedded ) + { + dts_bits_get( bits, 1, bits_pos ); /* bDownmixEmbedded (1) */ + if( bPrimaryChSet ) + nLLDownmixType = dts_bits_get( bits, 3, bits_pos ); /* nLLDownmixType (3) */ + } + int bHierChSet = dts_bits_get( bits, 1, bits_pos ); /* bHierChSet (1) */ + if( bDownmixCoeffCodeEmbedded ) + { + /* N: the number of channels in the current channel set + * for non-primary channel set, adding +1 for the down scaling coefficients that prevent overflow + * M: the number of channels that the current channel set is mixed into + * Downmix coefficients are transmitted using 9-bit codes. */ + static const int downmix_channel_count_table[8] = { 1, 2, 2, 3, 3, 4, 4, 0 }; + int N = nChSetLLChannel + (bPrimaryChSet ? 0 : 1); + int M = bPrimaryChSet ? downmix_channel_count_table[nLLDownmixType] : sum_nChSetLLChannel; + int nDownmixCoeffs = N * M; + dts_bits_get( bits, nDownmixCoeffs * 9, bits_pos ); /* DownmixCoeffs (nDownmixCoeffs * 9) */ + if( bPrimaryChSet && downmix_channel_count_table[nLLDownmixType] == 2 ) + xll->stereo_downmix |= 1; + } + if( bHierChSet ) + sum_nChSetLLChannel += nChSetLLChannel; + if( dts_bits_get( bits, 1, bits_pos ) ) /* bChMaskEnabled (1) */ + { + uint32_t nChMask = dts_bits_get( bits, nBits4ChMask, bits_pos ); /* nChMask (nBits4ChMask) */ + xll->channel_layout |= dts_get_channel_layout_from_ls_mask32( nChMask ); + xll->lower_planes |= dts_get_lower_channels_from_ls_mask32( nChMask ); + } + else + dts_bits_get( bits, 25 * nChSetLLChannel, bits_pos ); /* RadiusDelta[ch] (9) + * Theta[ch] (9) + * Phi[ch] (7) + * per channel */ + } + else + { + /* No downmixing is allowed and each channel set is the primary channel set. */ + if( dts_bits_get( bits, 1, bits_pos ) ) /* bMappingCoeffsPresent (1) */ + { + int nBitsCh2SpkrCoef = 6 + 2 * dts_bits_get( bits, 3, bits_pos ); /* nBitsCh2SpkrCoef (3) */ + int nNumSpeakerConfigs = dts_bits_get( bits, 2, bits_pos ) + 1; /* nNumSpeakerConfigs (2) */ + for( int nSpkrConf = 0; nSpkrConf < nNumSpeakerConfigs; nSpkrConf++ ) + { + int pnActiveChannelMask = dts_bits_get( bits, nChSetLLChannel, bits_pos ); /* pnActiveChannelMask[nSpkrConf] (nChSetLLChannel) */ + int pnNumSpeakers = dts_bits_get( bits, 6, bits_pos ) + 1; /* pnNumSpeakers[nSpkrConf] (6) */ + int bSpkrMaskEnabled = dts_bits_get( bits, 1, bits_pos ); /* bSpkrMaskEnabled (1) */ + if( bSpkrMaskEnabled ) + { + uint32_t nSpkrMask = dts_bits_get( bits, nBits4ChMask, bits_pos ); /* nSpkrMask[nSpkrConf] (nBits4ChMask) */ + xll->channel_layout |= dts_get_channel_layout_from_ls_mask32( nSpkrMask ); + xll->lower_planes |= dts_get_lower_channels_from_ls_mask32( nSpkrMask ); + } + for( int nSpkr = 0; nSpkr < pnNumSpeakers; nSpkr++ ) + { + if( !bSpkrMaskEnabled ) + dts_bits_get( bits, 25, bits_pos ); /* ChSetSpeakerConfiguration (25) */ + for( int nCh = 0; nCh < nChSetLLChannel; nCh++ ) + if( pnActiveChannelMask & (1 << nCh) ) + dts_bits_get( bits, nBitsCh2SpkrCoef, bits_pos ); /* pnCh2SpkrMapCoeff (nBitsCh2SpkrCoef) */ + } + } + } + } + int nNumFreqBands; + if( nFs > 96000 ) + { + if( dts_bits_get( bits, 1, bits_pos ) ) /* bXtraFreqBands (1) */ + nNumFreqBands = nFs > 192000 ? 4 : 2; + else + nNumFreqBands = nFs > 192000 ? 2 : 1; + } + else + nNumFreqBands = 1; + uint32_t nSmplInSeg_nChSet; + if( nChSet == 0 ) + { + nFs1 = nFs; + nNumFreqBands1 = nNumFreqBands; + nSmplInSeg_nChSet = nSmplInSeg; + } + else + nSmplInSeg_nChSet = (nSmplInSeg * (nFs * nNumFreqBands1)) / (nFs1 * nNumFreqBands); + if( xll->sampling_frequency < nFs ) + { + xll->sampling_frequency = nFs; + uint32_t samples_per_band_in_frame = nSegmentsInFrame * nSmplInSeg_nChSet; + xll->frame_duration = samples_per_band_in_frame * nNumFreqBands; + } + dts_bits_get( bits, nChSetHeaderSize * 8 - (*bits_pos - xll_pos), bits_pos ); /* Skip the remaining bits in Channel Set Sub-Header. */ + } + info->flags |= DTS_EXT_SUBSTREAM_XLL_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static uint16_t dts_generate_channel_layout_from_core( int channel_arrangement ) +{ + static const uint16_t channel_layout_map_table[] = + { + DTS_CHANNEL_LAYOUT_C, + DTS_CHANNEL_LAYOUT_L_R, /* dual mono */ + DTS_CHANNEL_LAYOUT_L_R, /* stereo */ + DTS_CHANNEL_LAYOUT_L_R, /* sum-difference */ + DTS_CHANNEL_LAYOUT_L_R, /* Lt/Rt */ + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R, + DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_CS, + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_CS, + DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LS_RS, + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LS_RS, + DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LS_RS, + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LSR_RSR | DTS_CHANNEL_LAYOUT_OH, + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_CS | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LSR_RSR, + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_LS_RS, + DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_LS_RS | DTS_CHANNEL_LAYOUT_LSR_RSR, + DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_CS | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_LS_RS + }; + return channel_arrangement < 16 ? channel_layout_map_table[channel_arrangement] : 0; +} + +static int dts_parse_core( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) +{ + lsmash_bits_t *bits = info->bits; + memset( core, 0, sizeof(dts_core_info_t) ); + /* SYNC (32) */ + int frame_type = dts_bits_get( bits, 1, bits_pos ); /* FTYPE (1) */ + int deficit_sample_count = dts_bits_get( bits, 5, bits_pos ); /* SHORT (5) */ + if( frame_type == 1 && deficit_sample_count != 31 ) + return LSMASH_ERR_INVALID_DATA; /* Any normal frame (FTYPE == 1) must have SHORT == 31. */ + int crc_present_flag = dts_bits_get( bits, 1, bits_pos ); /* CPF (1) */ + int num_of_pcm_sample_blocks = dts_bits_get( bits, 7, bits_pos ) + 1; /* NBLKS (7) */ + if( num_of_pcm_sample_blocks <= 5 ) + return LSMASH_ERR_INVALID_DATA; + core->frame_duration = 32 * num_of_pcm_sample_blocks; + if( frame_type == 1 + && core->frame_duration != 256 + && core->frame_duration != 512 && core->frame_duration != 1024 + && core->frame_duration != 2048 && core->frame_duration != 4096 ) + return LSMASH_ERR_INVALID_DATA; /* For any normal frame, the actual number of PCM core samples per channel must be + * either 4096, 2048, 1024, 512, or 256 samples per channel. */ + core->frame_size = dts_bits_get( bits, 14, bits_pos ) + 1; /* FSIZE (14) */ + if( core->frame_size < DTS_MIN_CORE_SIZE ) + return LSMASH_ERR_INVALID_DATA; + core->channel_arrangement = dts_bits_get( bits, 6, bits_pos ); /* AMODE (6) */ + core->channel_layout = dts_generate_channel_layout_from_core( core->channel_arrangement ); + int core_audio_sampling_frequency = dts_bits_get( bits, 4, bits_pos ); /* SFREQ (4) */ + static const uint32_t sampling_frequency_table[16] = + { + 0, + 8000, 16000, 32000, 0, 0, + 11025, 22050, 44100, 0, 0, + 12000, 24000, 48000, 0, 0 + }; + core->sampling_frequency = sampling_frequency_table[core_audio_sampling_frequency]; + if( core->sampling_frequency == 0 ) + return LSMASH_ERR_INVALID_DATA; /* invalid */ + dts_bits_get( bits, 10, bits_pos ); /* Skip remainder 10 bits. + * RATE (5) + * MIX (1) + * DYNF (1) + * TIMEF (1) + * AUXF (1) + * HDCD (1) */ + core->extension_audio_descriptor = dts_bits_get( bits, 3, bits_pos ); /* EXT_AUDIO_ID (3) + * Note: EXT_AUDIO_ID == 3 is defined in V1.2.1. + * However, its definition disappears and is reserved in V1.3.1. */ + int extended_coding_flag = dts_bits_get( bits, 1, bits_pos ); /* EXT_AUDIO (1) */ + dts_bits_get( bits, 1, bits_pos ); /* ASPF (1) */ + int low_frequency_effects_flag = dts_bits_get( bits, 2, bits_pos ); /* LFF (2) */ + if( low_frequency_effects_flag == 0x3 ) + return LSMASH_ERR_INVALID_DATA; /* invalid */ + if( low_frequency_effects_flag ) + core->channel_layout |= DTS_CHANNEL_LAYOUT_LFE1; + dts_bits_get( bits, 8 + crc_present_flag * 16, bits_pos ); /* HFLAG (1) + * HCRC (16) + * FILTS (1) + * VERNUM (4) + * CHIST (2) */ + int PCMR = dts_bits_get( bits, 3, bits_pos ); /* PCMR (3) */ + static const uint8_t source_resolution_table[8] = { 16, 16, 20, 20, 0, 24, 24, 0 }; + core->pcm_resolution = source_resolution_table[PCMR]; + if( core->pcm_resolution == 0 ) + return LSMASH_ERR_INVALID_DATA; /* invalid */ + dts_bits_get( bits, 6, bits_pos ); /* SUMF (1) + * SUMS (1) + * DIALNORM/UNSPEC (4) */ + if( extended_coding_flag ) + { + uint32_t syncword = dts_bits_get( bits, 24, bits_pos ); + uint64_t frame_size_bits = core->frame_size * 8; + while( (*bits_pos + 24) < frame_size_bits ) + { + int err; + syncword = ((syncword << 8) & 0xffffff00) | dts_bits_get( bits, 8, bits_pos ); + switch( syncword ) + { + case DTS_SYNCWORD_XXCH : + if( (err = dts_parse_core_xxch( info, bits_pos, core )) < 0 ) + return err; + syncword = dts_bits_get( bits, 24, bits_pos ); + break; + case DTS_SYNCWORD_X96K : + if( (err = dts_parse_core_x96( info, bits_pos, core )) < 0 ) + return err; + syncword = dts_bits_get( bits, 24, bits_pos ); + break; + case DTS_SYNCWORD_XCH : + if( (err = dts_parse_core_xch( info, bits_pos, core )) < 0 ) + return err; + break; + default : + continue; + } + } + } + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int dts_parse_exss_core( dts_info_t *info, uint64_t *bits_pos, dts_audio_asset_t *asset ) +{ + lsmash_bits_t *bits = info->bits; + if( DTS_SYNCWORD_SUBSTREAM_CORE != dts_bits_get( bits, 32, bits_pos ) ) + return LSMASH_ERR_INVALID_DATA; + int err = dts_parse_core( info, bits_pos, &asset->core ); + if( err < 0 ) + return err; + info->flags |= DTS_EXT_SUBSTREAM_CORE_FLAG; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int dts_parse_core_substream( dts_info_t *info ) +{ + lsmash_bits_t *bits = info->bits; + uint64_t bits_pos = 0; + int err; + if( DTS_SYNCWORD_CORE != dts_bits_get( bits, 32, &bits_pos ) ) + { + err = LSMASH_ERR_INVALID_DATA; + goto parse_fail; + } + /* By default the core substream data, if present, has the nuBcCoreExtSSIndex = 0 and the nuBcCoreAssetIndex = 0. */ + dts_extension_info_t *exss = &info->exss[0]; + if( (err = dts_parse_core( info, &bits_pos, &exss->asset[0].core )) < 0 ) + goto parse_fail; + exss->bBcCorePresent [0] = 1; + exss->nuBcCoreExtSSIndex[0] = 0; + exss->nuBcCoreAssetIndex[0] = 0; + info->flags |= DTS_CORE_SUBSTREAM_CORE_FLAG; + info->exss_count = 0; + info->core = exss->asset[0].core; + info->frame_size = exss->asset[0].core.frame_size; + lsmash_bits_get_align( bits ); + return 0; +parse_fail: + lsmash_bits_get_align( bits ); + return err; +} + +int dts_parse_extension_substream( dts_info_t *info ) +{ + lsmash_bits_t *bits = info->bits; + uint64_t bits_pos = 0; + dts_bits_get( bits, 40, &bits_pos ); /* SYNCEXTSSH (32) + * UserDefinedBits (8) */ + int nExtSSIndex = dts_bits_get( bits, 2, &bits_pos ); /* nExtSSIndex (2) */ + info->exss_index = nExtSSIndex; + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + memset( exss, 0, sizeof(dts_extension_info_t) ); + int bHeaderSizeType = dts_bits_get( bits, 1, &bits_pos ); /* bHeaderSizeType (1) */ + int nuBits4Header = 8 + bHeaderSizeType * 4; + int nuBits4ExSSFsize = 16 + bHeaderSizeType * 4; + exss->nuBits4ExSSFsize = nuBits4ExSSFsize; + uint32_t nuExtSSHeaderSize = dts_bits_get( bits, nuBits4Header, &bits_pos ) + 1; /* nuExtSSHeaderSize (8 or 12) */ + info->frame_size = dts_bits_get( bits, nuBits4ExSSFsize, &bits_pos ) + 1; /* nuExtSSFsize (16 or 20) */ + if( info->frame_size < 10 ) + return LSMASH_ERR_INVALID_DATA; + exss->bStaticFieldsPresent = dts_bits_get( bits, 1, &bits_pos ); /* bStaticFieldsPresent (1) */ + if( exss->bStaticFieldsPresent ) + { + dts_bits_get( bits, 2, &bits_pos ); /* nuRefClockCode (2) */ + exss->frame_duration = 512 * (dts_bits_get( bits, 3, &bits_pos ) + 1); /* nuExSSFrameDurationCode (3) */ + if( dts_bits_get( bits, 1, &bits_pos ) ) /* bTimeStampFlag (1) */ + dts_bits_get( bits, 36, &bits_pos ); /* nuTimeStamp (32) + * nLSB (4) */ + exss->nuNumAudioPresnt = dts_bits_get( bits, 3, &bits_pos ) + 1; /* nuNumAudioPresnt (3) */ + exss->nuNumAssets = dts_bits_get( bits, 3, &bits_pos ) + 1; /* nuNumAssets (3) */ + /* The extension substreams with indexes lower than or equal to the index of the current extension substream can + * be activated in the audio presentations indicated within the current extension substream. */ + for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) + exss->nuActiveExSSMask[nAuPr] + = dts_bits_get( bits, nExtSSIndex + 1, &bits_pos ); /* nuActiveExSSMask[nAuPr] (nExtSSIndex + 1) */ + for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) + for( uint8_t nSS = 0; nSS <= nExtSSIndex; nSS++ ) + exss->nuActiveAssetMask[nAuPr][nSS] + = ((exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1) + ? dts_bits_get( bits, 8, &bits_pos ) /* nuActiveAssetMask[nAuPr][nSS] (8) */ + : 0; + exss->bMixMetadataEnbl = dts_bits_get( bits, 1, &bits_pos ); /* bMixMetadataEnbl (1) */ + if( exss->bMixMetadataEnbl ) + { + dts_bits_get( bits, 2, &bits_pos ); /* nuMixMetadataAdjLevel (2) */ + int nuBits4MixOutMask = (dts_bits_get( bits, 2, &bits_pos ) + 1) << 2; /* nuBits4MixOutMask (2) */ + exss->nuNumMixOutConfigs = dts_bits_get( bits, 2, &bits_pos ) + 1; /* nuNumMixOutConfigs (2) */ + for( int ns = 0; ns < exss->nuNumMixOutConfigs; ns++ ) + { + int nuMixOutChMask = dts_bits_get( bits, nuBits4MixOutMask, &bits_pos ); /* nuMixOutChMask[ns] (nuBits4MixOutMask) */ + exss->nNumMixOutCh[ns] = dts_get_channel_count_from_channel_layout( nuMixOutChMask ); + } + } + } + else + { + exss->nuNumAudioPresnt = 1; + exss->nuNumAssets = 1; + exss->bMixMetadataEnbl = 0; + exss->nuNumMixOutConfigs = 0; + } + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + exss->asset[nAst].size = dts_bits_get( bits, nuBits4ExSSFsize, &bits_pos ) + 1; /* nuAssetFsize[nAst] - 1 (nuBits4ExSSFsize) */ + int err; + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + if( (err = dts_parse_asset_descriptor( info, &bits_pos )) < 0 ) + goto parse_fail; + for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) + exss->bBcCorePresent[nAuPr] = dts_bits_get( bits, 1, &bits_pos ); + for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) + if( exss->bBcCorePresent[nAuPr] ) + { + exss->nuBcCoreExtSSIndex[nAuPr] = dts_bits_get( bits, 2, &bits_pos ); + exss->nuBcCoreAssetIndex[nAuPr] = dts_bits_get( bits, 3, &bits_pos ); + } + dts_bits_get( bits, nuExtSSHeaderSize * 8 - bits_pos, &bits_pos ); + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + { + /* Asset Data */ + dts_audio_asset_t *asset = &exss->asset[nAst]; + uint32_t asset_pos = bits_pos; + switch( asset->nuCodingMode ) + { + case 0 : /* DTS-HD Coding Mode that may contain multiple coding components */ + { + if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_CORE_FLAG ) + { + /* Core component */ + uint64_t core_pos = bits_pos; + if( (err = dts_parse_exss_core( info, &bits_pos, asset )) < 0 ) + goto parse_fail; + dts_bits_get( bits, asset->core.frame_size * 8 - (bits_pos - core_pos), &bits_pos ); + } + if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XBR_FLAG ) + { + /* XBR extension */ + uint64_t xbr_pos = bits_pos; + if( (err = dts_parse_exss_xbr( info, &bits_pos )) < 0 ) + goto parse_fail; + dts_bits_get( bits, asset->xbr_size * 8 - (bits_pos - xbr_pos), &bits_pos ); + } + if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XXCH_FLAG ) + { + /* XXCH extension */ + uint64_t xxch_pos = bits_pos; + if( (err = dts_parse_exss_xxch( info, &bits_pos, &asset->core )) < 0 ) + goto parse_fail; + dts_bits_get( bits, asset->core.xxch.size * 8 - (bits_pos - xxch_pos), &bits_pos ); + } + if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_X96_FLAG ) + { + /* X96 extension */ + uint64_t x96_pos = bits_pos; + if( (err = dts_parse_exss_x96( info, &bits_pos, &asset->core )) < 0 ) + goto parse_fail; + dts_bits_get( bits, asset->x96_size * 8 - (bits_pos - x96_pos), &bits_pos ); + } + if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_LBR_FLAG ) + { + /* LBR component */ + uint64_t lbr_pos = bits_pos; + if( (err = dts_parse_exss_lbr( info, &bits_pos, asset )) < 0 ) + goto parse_fail; + dts_bits_get( bits, asset->lbr.size * 8 - (bits_pos - lbr_pos), &bits_pos ); + } + if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XLL_FLAG ) + { + /* Lossless extension */ + uint64_t xll_pos = bits_pos; + if( (err = dts_parse_exss_xll( info, &bits_pos, asset )) < 0 ) + goto parse_fail; + dts_bits_get( bits, asset->xll.size * 8 - (bits_pos - xll_pos), &bits_pos ); + } + break; + } + case 1 : /* DTS-HD Loss-less coding mode without CBR component */ + if( (err = dts_parse_exss_xll( info, &bits_pos, asset )) < 0 ) + goto parse_fail; + break; + case 2 : /* DTS-HD Low bit-rate mode */ + if( (err = dts_parse_exss_lbr( info, &bits_pos, asset )) < 0 ) + goto parse_fail; + break; + case 3 : /* Auxiliary coding mode */ + dts_bits_get( bits, asset->aux_size * 8, &bits_pos ); + break; + } + dts_bits_get( bits, asset->size * 8 - (bits_pos - asset_pos), &bits_pos ); + } + dts_bits_get( bits, info->frame_size * 8 - bits_pos, &bits_pos ); + lsmash_bits_get_align( bits ); + if( info->exss_count < DTS_MAX_NUM_EXSS ) + info->exss_count += 1; + return 0; +parse_fail: + lsmash_bits_get_align( bits ); + return err; +} + +dts_substream_type dts_get_substream_type( dts_info_t *info ) +{ + if( lsmash_bs_get_remaining_buffer_size( info->bits->bs ) < 4 ) + return DTS_SUBSTREAM_TYPE_NONE; + uint8_t *buffer = lsmash_bs_get_buffer_data( info->bits->bs ); + uint32_t syncword = LSMASH_4CC( buffer[0], buffer[1], buffer[2], buffer[3] ); + switch( syncword ) + { + case DTS_SYNCWORD_CORE : + return DTS_SUBSTREAM_TYPE_CORE; + case DTS_SYNCWORD_SUBSTREAM : + return DTS_SUBSTREAM_TYPE_EXTENSION; + default : + return DTS_SUBSTREAM_TYPE_NONE; + } +} + +int dts_get_exss_index( dts_info_t *info, uint8_t *exss_index ) +{ + if( lsmash_bs_get_remaining_buffer_size( info->bits->bs ) < 6 ) + return LSMASH_ERR_INVALID_DATA; + *exss_index = lsmash_bs_show_byte( info->bits->bs, 5 ) >> 6; + return 0; +} + +int dts_get_max_channel_count( dts_info_t *info ) +{ + int max_channel_count = 0; + for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) + { + /* Get the channel layout of an audio presentation from a core component. */ + uint16_t channel_layout = 0; + int channel_count = 0; + if( exss->bBcCorePresent [nAuPr] + && exss->nuBcCoreAssetIndex[nAuPr] < exss->nuNumAssets ) + { + dts_core_info_t *core = &info->exss[ exss->nuBcCoreExtSSIndex[nAuPr] ].asset[ exss->nuBcCoreAssetIndex[nAuPr] ].core; + if( core->xxch.channel_layout | core->xxch.lower_planes ) + { + channel_layout = core->xxch.channel_layout; + channel_count = lsmash_count_bits( core->xxch.lower_planes ); /* FIXME: Should we count these channels? */ + } + else + channel_layout = core->channel_layout; + } + channel_count += dts_get_channel_count_from_channel_layout( channel_layout ); + max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); + /* Get the channel layouts of an audio presentation from extension substreams. */ + uint16_t ext_channel_layout = 0; + uint16_t lbr_channel_layout = 0; + uint16_t xll_channel_layout = 0; + uint8_t xll_lower_channels = 0; + for( int nSS = 0; nSS <= nExtSSIndex; nSS++ ) + if( (exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1 ) + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + if( (exss->nuActiveAssetMask[nAuPr][nSS] >> nAst) & 0x1 ) + { + dts_audio_asset_t *asset = &exss->asset[nAst]; + ext_channel_layout |= asset->channel_layout; + lbr_channel_layout |= asset->lbr.channel_layout; + xll_channel_layout |= asset->xll.channel_layout; + xll_lower_channels |= asset->xll.lower_planes; + } + /* Audio asset descriptors */ + channel_count = dts_get_channel_count_from_channel_layout( ext_channel_layout ); + max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); + /* LBR components */ + channel_count = dts_get_channel_count_from_channel_layout( lbr_channel_layout ); + max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); + /* Lossless extensions */ + channel_count = dts_get_channel_count_from_channel_layout( xll_channel_layout ) + + lsmash_count_bits( xll_lower_channels ); + max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); + } + } + return max_channel_count; +} + +void dts_update_specific_param( dts_info_t *info ) +{ + lsmash_dts_specific_parameters_t *param = &info->ddts_param; + int exss_index_start = 0; + for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + if( exss->nuNumAudioPresnt && exss->nuNumAssets ) + { + exss_index_start = nExtSSIndex; + break; + } + } + /* DTSSamplingFrequency and FrameDuration */ + for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) + continue; + if( param->DTSSamplingFrequency <= exss->sampling_frequency ) + { + param->DTSSamplingFrequency = exss->sampling_frequency; + info->frame_duration = exss->frame_duration; + } + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + { + dts_audio_asset_t *asset = &exss->asset[nAst]; + if( param->DTSSamplingFrequency <= asset->core.sampling_frequency ) + { + param->DTSSamplingFrequency = asset->core.sampling_frequency; + info->frame_duration = asset->core.frame_duration; + } + if( param->DTSSamplingFrequency <= asset->lbr.sampling_frequency ) + { + param->DTSSamplingFrequency = asset->lbr.sampling_frequency; + info->frame_duration = asset->lbr.frame_duration; + } + if( param->DTSSamplingFrequency <= asset->xll.sampling_frequency ) + { + param->DTSSamplingFrequency = asset->xll.sampling_frequency; + info->frame_duration = asset->xll.frame_duration; + } + } + } + param->FrameDuration = 0; + for( uint32_t frame_duration = info->frame_duration >> 10; frame_duration; frame_duration >>= 1 ) + ++ param->FrameDuration; + /* pcmSampleDepth */ + param->pcmSampleDepth = 0; + for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) + continue; + param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, exss->bit_resolution ); + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + { + dts_audio_asset_t *asset = &exss->asset[nAst]; + param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, asset->core.pcm_resolution ); + param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, asset->lbr.sample_size ); + param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, asset->xll.pcm_resolution ); + } + } + param->pcmSampleDepth = param->pcmSampleDepth > 16 ? 24 : 16; + /* StreamConstruction */ + param->StreamConstruction = lsmash_dts_get_stream_construction( info->flags ); + /* CoreLFEPresent */ + param->CoreLFEPresent = !!(info->core.channel_layout & DTS_CHANNEL_LAYOUT_LFE1); + /* CoreLayout */ + if( param->StreamConstruction == 0 /* Unknown */ + || param->StreamConstruction >= 17 /* No core substream */ ) + /* Use ChannelLayout. */ + param->CoreLayout = 31; + else + { + if( info->core.channel_arrangement != 1 + && info->core.channel_arrangement != 3 + && info->core.channel_arrangement <= 9 ) + param->CoreLayout = info->core.channel_arrangement; + else + /* Use ChannelLayout. */ + param->CoreLayout = 31; + } + /* CoreSize + * The specification says this field is the size of a core substream AU in bytes. + * If we don't assume CoreSize is the copy of FSIZE, when FSIZE equals 0x3FFF, this field overflows and becomes 0. */ + param->CoreSize = info->core.frame_size ? LSMASH_MIN( info->core.frame_size - 1, 0x3FFF ) : 0; + /* StereoDownmix */ + param->StereoDownmix = 0; + for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + param->StereoDownmix |= exss->stereo_downmix; + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + { + param->StereoDownmix |= exss->asset[nAst].lbr.stereo_downmix; + param->StereoDownmix |= exss->asset[nAst].xll.stereo_downmix; + } + } + /* RepresentationType + * Available only when core substream is absent and ChannelLayout is set to 0. */ + for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) + continue; + for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) + { + int asset_count = 0; + for( int nSS = 0; nSS <= nExtSSIndex; nSS++ ) + if( (exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1 ) + asset_count += lsmash_count_bits( exss->nuActiveAssetMask[nAuPr][nSS] ); + if( asset_count > 1 ) + { + /* An audio presentation has mulple audio assets. + * Audio asset designated for mixing with another audio asset. */ + param->RepresentationType = 0; + nExtSSIndex = DTS_MAX_NUM_EXSS; + break; + } + for( int nSS = 0; nSS <= nExtSSIndex; nSS++ ) + if( (exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1 ) + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + if( (exss->nuActiveAssetMask[nAuPr][nSS] >> nAst) & 0x1 ) + { + dts_audio_asset_t *asset = &exss->asset[nAst]; + if( asset->nuRepresentationType == info->exss[exss_index_start].asset[0].nuRepresentationType ) + param->RepresentationType = asset->nuRepresentationType; + else + { + /* Detected different representation types. Use ChannelLayout. */ + param->RepresentationType = 0; + nAuPr = exss->nuNumAudioPresnt; + nExtSSIndex = DTS_MAX_NUM_EXSS; + break; + } + } + } + } + /* ChannelLayout + * complete information on channels coded in the audio stream including core and extensions */ + param->ChannelLayout = 0; + if( param->RepresentationType == 0 ) + for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) + { + dts_extension_info_t *exss = &info->exss[nExtSSIndex]; + if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) + continue; + for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) + { + dts_audio_asset_t *asset = &exss->asset[nAst]; + param->ChannelLayout |= asset->channel_layout; + param->ChannelLayout |= asset->core.channel_layout; + param->ChannelLayout |= asset->core.xxch.channel_layout; + param->ChannelLayout |= asset->lbr.channel_layout; + param->ChannelLayout |= asset->xll.channel_layout; + } + } + /* MultiAssetFlag + * When multiple assets exist, the remaining parameters in the DTSSpecificBox only reflect the coding parameters of the first asset. */ + param->MultiAssetFlag = ((info->exss[0].nuNumAssets + + info->exss[1].nuNumAssets + + info->exss[2].nuNumAssets + + info->exss[3].nuNumAssets) > 1); + /* LBRDurationMod */ + param->LBRDurationMod = info->exss[exss_index_start].asset[0].lbr.duration_modifier; + info->ddts_param_initialized = 1; +} + +int dts_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < DTS_SPECIFIC_BOX_MIN_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + lsmash_dts_specific_parameters_t *param = (lsmash_dts_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + int dts_specific_box_min_length = DTS_SPECIFIC_BOX_MIN_LENGTH; + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + dts_specific_box_min_length += 8; + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + param->DTSSamplingFrequency = LSMASH_GET_BE32( &data[0] ); + param->maxBitrate = LSMASH_GET_BE32( &data[4] ); + param->avgBitrate = LSMASH_GET_BE32( &data[8] ); + param->pcmSampleDepth = LSMASH_GET_BYTE( &data[12] ); + param->FrameDuration = (data[13] >> 6) & 0x03; + param->StreamConstruction = (data[13] >> 1) & 0x1F; + param->CoreLFEPresent = data[13] & 0x01; + param->CoreLayout = (data[14] >> 2) & 0x3F; + param->CoreSize = ((data[14] & 0x03) << 12) | (data[15] << 4) | ((data[16] >> 4) & 0x0F); + param->StereoDownmix = (data[16] >> 3) & 0x01; + param->RepresentationType = data[16] & 0x07; + param->ChannelLayout = (data[17] << 8) | data[18]; + param->MultiAssetFlag = (data[19] >> 7) & 0x01; + param->LBRDurationMod = (data[19] >> 6) & 0x01; + int reserved_box_present = ((data[19] >> 5) & 0x01) && (size > DTS_SPECIFIC_BOX_MIN_LENGTH); + if( reserved_box_present ) + lsmash_append_dts_reserved_box( param, data + 20, size - DTS_SPECIFIC_BOX_MIN_LENGTH ); + return 0; +} + +int dts_copy_codec_specific( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); + assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); + lsmash_dts_specific_parameters_t *src_data = (lsmash_dts_specific_parameters_t *)src->data.structured; + lsmash_dts_specific_parameters_t *dst_data = (lsmash_dts_specific_parameters_t *)dst->data.structured; + lsmash_remove_dts_reserved_box( dst_data ); + *dst_data = *src_data; + if( !src_data->box && src_data->box->data && src_data->box->size ) + return 0; + return lsmash_append_dts_reserved_box( dst_data, src_data->box->data, src_data->box->size ); +} + +int dts_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: DTS Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + if( box->size < DTS_SPECIFIC_BOX_MIN_LENGTH ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *data = box->binary; + isom_skip_box_common( &data ); + uint32_t DTSSamplingFrequency = LSMASH_GET_BE32( &data[0] ); + uint32_t maxBitrate = LSMASH_GET_BE32( &data[4] ); + uint32_t avgBitrate = LSMASH_GET_BE32( &data[8] ); + uint8_t pcmSampleDepth = LSMASH_GET_BYTE( &data[12] ); + uint8_t FrameDuration = (data[13] >> 6) & 0x03; + uint8_t StreamConstruction = (data[13] >> 1) & 0x1F; + uint8_t CoreLFEPresent = data[13] & 0x01; + uint8_t CoreLayout = (data[14] >> 2) & 0x3F; + uint16_t CoreSize = ((data[14] & 0x03) << 12) | (data[15] << 4) | ((data[16] >> 4) & 0x0F); + uint8_t StereoDownmix = (data[16] >> 3) & 0x01; + uint8_t RepresentationType = data[16] & 0x07; + uint16_t ChannelLayout = (data[17] << 8) | data[18]; + uint8_t MultiAssetFlag = (data[19] >> 7) & 0x01; + uint8_t LBRDurationMod = (data[19] >> 6) & 0x01; + uint8_t ReservedBoxPresent = (data[19] >> 5) & 0x01; + uint8_t Reserved = data[19] & 0x1F; + uint32_t frame_duration = 512 << FrameDuration; + int construction_flags = StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION ? construction_info[StreamConstruction] : 0; + static const char *core_layout_description[64] = + { + "Mono (1/0)", + "Undefined", + "Stereo (2/0)", + "Undefined", + "LT,RT (2/0)", + "L, C, R (3/0)", + "L, R, S (2/1)", + "L, C, R, S (3/1)", + "L, R, LS, RS (2/2)", + "L, C, R, LS, RS (3/2)", + [31] = "use ChannelLayout" + }; + static const char *representation_type_description[8] = + { + "Audio asset designated for mixing with another audio asset", + "Reserved", + "Lt/Rt Encoded for matrix surround decoding", + "Audio processed for headphone playback", + "Reserved", + "Reserved", + "Reserved", + "Reserved" + }; + static const char *channel_layout_description[16] = + { + "Center in front of listener", + "Left/Right in front", + "Left/Right surround on side in rear", + "Low frequency effects subwoofer", + "Center surround in rear", + "Left/Right height in front", + "Left/Right surround in rear", + "Center Height in front", + "Over the listener's head", + "Between left/right and center in front", + "Left/Right on side in front", + "Left/Right surround on side", + "Second low frequency effects subwoofer", + "Left/Right height on side", + "Center height in rear", + "Left/Right height in rear" + }; + lsmash_ifprintf( fp, indent, "DTSSamplingFrequency = %"PRIu32" Hz\n", DTSSamplingFrequency ); + lsmash_ifprintf( fp, indent, "maxBitrate = %"PRIu32" bit/s\n", maxBitrate ); + lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32" bit/s\n", avgBitrate ); + lsmash_ifprintf( fp, indent, "pcmSampleDepth = %"PRIu8" bits\n", pcmSampleDepth ); + lsmash_ifprintf( fp, indent, "FrameDuration = %"PRIu8" (%"PRIu32" samples)\n", FrameDuration, frame_duration ); + lsmash_ifprintf( fp, indent, "StreamConstruction = 0x%02"PRIx8"\n", StreamConstruction ); + if( construction_flags & (DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_CORE_SUBSTREAM_X96_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG) ) + { + lsmash_ifprintf( fp, indent + 1, "Core substream\n" ); + if( construction_flags & DTS_CORE_SUBSTREAM_CORE_FLAG ) + lsmash_ifprintf( fp, indent + 2, "Core\n" ); + if( construction_flags & DTS_CORE_SUBSTREAM_XCH_FLAG ) + lsmash_ifprintf( fp, indent + 2, "XCH\n" ); + if( construction_flags & DTS_CORE_SUBSTREAM_X96_FLAG ) + lsmash_ifprintf( fp, indent + 2, "X96\n" ); + if( construction_flags & DTS_CORE_SUBSTREAM_XXCH_FLAG ) + lsmash_ifprintf( fp, indent + 2, "XXCH\n" ); + } + if( construction_flags & (DTS_EXT_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG + | DTS_EXT_SUBSTREAM_XBR_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG | DTS_EXT_SUBSTREAM_LBR_FLAG) ) + { + lsmash_ifprintf( fp, indent + 1, "Extension substream\n" ); + if( construction_flags & DTS_EXT_SUBSTREAM_CORE_FLAG ) + lsmash_ifprintf( fp, indent + 2, "Core\n" ); + if( construction_flags & DTS_EXT_SUBSTREAM_XXCH_FLAG ) + lsmash_ifprintf( fp, indent + 2, "XXCH\n" ); + if( construction_flags & DTS_EXT_SUBSTREAM_X96_FLAG ) + lsmash_ifprintf( fp, indent + 2, "X96\n" ); + if( construction_flags & DTS_EXT_SUBSTREAM_XBR_FLAG ) + lsmash_ifprintf( fp, indent + 2, "XBR\n" ); + if( construction_flags & DTS_EXT_SUBSTREAM_XLL_FLAG ) + lsmash_ifprintf( fp, indent + 2, "XLL\n" ); + if( construction_flags & DTS_EXT_SUBSTREAM_LBR_FLAG ) + lsmash_ifprintf( fp, indent + 2, "LBR\n" ); + } + lsmash_ifprintf( fp, indent, "CoreLFEPresent = %s\n", CoreLFEPresent ? "1 (LFE exists)" : "0 (no LFE)" ); + if( core_layout_description[CoreLayout] ) + lsmash_ifprintf( fp, indent, "CoreLayout = %"PRIu8" (%s)\n", CoreLayout, core_layout_description[CoreLayout] ); + else + lsmash_ifprintf( fp, indent, "CoreLayout = %"PRIu8" (Undefined)\n", CoreLayout ); + if( CoreSize ) + lsmash_ifprintf( fp, indent, "CoreSize = %"PRIu16"\n", CoreSize ); + else + lsmash_ifprintf( fp, indent, "CoreSize = 0 (no core substream exists)\n" ); + lsmash_ifprintf( fp, indent, "StereoDownmix = %s\n", StereoDownmix ? "1 (embedded downmix present)" : "0 (no embedded downmix)" ); + lsmash_ifprintf( fp, indent, "RepresentationType = %"PRIu8" (%s)\n", RepresentationType, representation_type_description[RepresentationType] ); + lsmash_ifprintf( fp, indent, "ChannelLayout = 0x%04"PRIx16"\n", ChannelLayout ); + if( ChannelLayout ) + for( int i = 0; i < 16; i++ ) + if( (ChannelLayout >> i) & 0x01 ) + lsmash_ifprintf( fp, indent + 1, "%s\n", channel_layout_description[i] ); + lsmash_ifprintf( fp, indent, "MultiAssetFlag = %s\n", MultiAssetFlag ? "1 (multiple asset)" : "0 (single asset)" ); + if( LBRDurationMod ) + lsmash_ifprintf( fp, indent, "LBRDurationMod = 1 (%"PRIu32" -> %"PRIu32" samples)\n", frame_duration, (frame_duration * 3) / 2 ); + else + lsmash_ifprintf( fp, indent, "LBRDurationMod = 0 (no LBR duration modifier)\n" ); + lsmash_ifprintf( fp, indent, "ReservedBoxPresent = %s\n", ReservedBoxPresent ? "1 (ReservedBox present)" : "0 (no ReservedBox)" ); + lsmash_ifprintf( fp, indent, "Reserved = 0x%02"PRIx8"\n", Reserved ); + return 0; +} diff -Nru l-smash-1.9.1/codecs/dts.h l-smash-2.3.0/codecs/dts.h --- l-smash-1.9.1/codecs/dts.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/dts.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,134 @@ +/***************************************************************************** + * dts.h: + ***************************************************************************** + * Copyright (C) 2012-2014 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. */ + +#define DTS_MAX_CORE_SIZE 16384 +#define DTS_MAX_EXSS_SIZE 32768 +#define DTS_MAX_NUM_EXSS 4 /* the maximum number of extension substreams */ + +typedef enum +{ + DTS_SUBSTREAM_TYPE_NONE = 0, + DTS_SUBSTREAM_TYPE_CORE = 1, + DTS_SUBSTREAM_TYPE_EXTENSION = 2, +} dts_substream_type; + +typedef struct +{ + uint16_t size; + uint16_t channel_layout; + uint8_t lower_planes; /* CL, LL and RL */ +} dts_xxch_info_t; + +typedef struct +{ + uint32_t sampling_frequency; + uint32_t frame_duration; + uint16_t frame_size; + uint16_t channel_layout; + uint8_t channel_arrangement; + uint8_t extension_audio_descriptor; + uint8_t pcm_resolution; + dts_xxch_info_t xxch; +} dts_core_info_t; + +typedef struct +{ + uint16_t size; + uint16_t channel_layout; + uint32_t sampling_frequency; + uint32_t frame_duration; + uint8_t pcm_resolution; + uint8_t stereo_downmix; + uint8_t lower_planes; /* CL, LL and RL */ +} dts_xll_info_t; + +typedef struct +{ + uint16_t size; + uint16_t channel_layout; + uint32_t sampling_frequency; + uint32_t frame_duration; + uint8_t stereo_downmix; + uint8_t lfe_present; + uint8_t duration_modifier; + uint8_t sample_size; +} dts_lbr_info_t; + +typedef struct +{ + uint32_t size; + uint16_t channel_layout; + uint8_t bOne2OneMapChannels2Speakers; + uint8_t nuRepresentationType; + uint8_t nuCodingMode; + lsmash_dts_construction_flag nuCoreExtensionMask; + dts_core_info_t core; + dts_xll_info_t xll; + dts_lbr_info_t lbr; + uint16_t xbr_size; + uint16_t x96_size; + uint16_t aux_size; +} dts_audio_asset_t; + +typedef struct +{ + uint32_t sampling_frequency; + uint32_t frame_duration; + uint8_t nuBits4ExSSFsize; + uint8_t bStaticFieldsPresent; + uint8_t bMixMetadataEnbl; + uint8_t nuNumMixOutConfigs; + uint8_t nNumMixOutCh[4]; + uint8_t nuNumAudioPresnt; + uint8_t nuNumAssets; + uint8_t nuActiveExSSMask[8]; + uint8_t nuActiveAssetMask[8][4]; + uint8_t bBcCorePresent[8]; + uint8_t nuBcCoreExtSSIndex[8]; + uint8_t nuBcCoreAssetIndex[8]; + uint8_t stereo_downmix; + uint8_t bit_resolution; + dts_audio_asset_t asset[8]; +} dts_extension_info_t; + +typedef struct +{ + dts_substream_type substream_type; + lsmash_dts_construction_flag flags; + lsmash_dts_specific_parameters_t ddts_param; + dts_core_info_t core; /* core component and its extensions in core substream */ + dts_extension_info_t exss[4]; /* extension substreams */ + uint8_t ddts_param_initialized; + uint8_t exss_index; + uint8_t exss_count; + uint32_t frame_duration; + uint32_t frame_size; /* size of substream */ + lsmash_bits_t *bits; +} dts_info_t; + +void dts_setup_parser( dts_info_t *info ); +int dts_parse_core_substream( dts_info_t *info ); +int dts_parse_extension_substream( dts_info_t *info ); +int dts_get_max_channel_count( dts_info_t *info ); +dts_substream_type dts_get_substream_type( dts_info_t *info ); +int dts_get_exss_index( dts_info_t *info, uint8_t *exss_index ); +void dts_update_specific_param( dts_info_t *info ); diff -Nru l-smash-1.9.1/codecs/h264.c l-smash-2.3.0/codecs/h264.c --- l-smash-1.9.1/codecs/h264.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/h264.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,2515 @@ +/***************************************************************************** + * h264.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +/*************************************************************************** + ITU-T Recommendation H.264 (04/13) + ISO/IEC 14496-15:2010 +***************************************************************************/ +#include "h264.h" +#include "nalu.h" + +#define IF_EXCEED_INT32( x ) if( (x) < INT32_MIN || (x) > INT32_MAX ) +#define H264_REQUIRES_AVCC_EXTENSION( x ) ((x) == 100 || (x) == 110 || (x) == 122 || (x) == 144) +#define H264_POC_DEBUG_PRINT 0 + +typedef enum +{ + H264_SLICE_TYPE_P = 0, + H264_SLICE_TYPE_B = 1, + H264_SLICE_TYPE_I = 2, + H264_SLICE_TYPE_SP = 3, + H264_SLICE_TYPE_SI = 4 +} h264_slice_type; + +void lsmash_destroy_h264_parameter_sets +( + lsmash_h264_specific_parameters_t *param +) +{ + if( !param || !param->parameter_sets ) + return; + lsmash_remove_entries( param->parameter_sets->sps_list, isom_remove_dcr_ps ); + lsmash_remove_entries( param->parameter_sets->pps_list, isom_remove_dcr_ps ); + lsmash_remove_entries( param->parameter_sets->spsext_list, isom_remove_dcr_ps ); + lsmash_free( param->parameter_sets ); + param->parameter_sets = NULL; +} + +void h264_destruct_specific_data +( + void *data +) +{ + if( !data ) + return; + lsmash_destroy_h264_parameter_sets( data ); + lsmash_free( data ); +} + +void h264_cleanup_parser +( + h264_info_t *info +) +{ + if( !info ) + return; + lsmash_remove_entries( info->sps_list, NULL ); + lsmash_remove_entries( info->pps_list, NULL ); + lsmash_remove_entries( info->slice_list, NULL ); + lsmash_destroy_h264_parameter_sets( &info->avcC_param ); + lsmash_destroy_h264_parameter_sets( &info->avcC_param_next ); + lsmash_destroy_multiple_buffers( info->buffer.bank ); + lsmash_bits_adhoc_cleanup( info->bits ); + info->bits = NULL; +} + +int h264_setup_parser +( + h264_info_t *info, + int parse_only +) +{ + assert( info ); + memset( info, 0, sizeof(h264_info_t) ); + info->avcC_param .lengthSizeMinusOne = NALU_DEFAULT_NALU_LENGTH_SIZE - 1; + info->avcC_param_next.lengthSizeMinusOne = NALU_DEFAULT_NALU_LENGTH_SIZE - 1; + h264_stream_buffer_t *sb = &info->buffer; + sb->bank = lsmash_create_multiple_buffers( parse_only ? 1 : 3, NALU_DEFAULT_BUFFER_SIZE ); + if( !sb->bank ) + return LSMASH_ERR_MEMORY_ALLOC; + sb->rbsp = lsmash_withdraw_buffer( sb->bank, 1 ); + if( !parse_only ) + { + info->au.data = lsmash_withdraw_buffer( sb->bank, 2 ); + info->au.incomplete_data = lsmash_withdraw_buffer( sb->bank, 3 ); + } + info->bits = lsmash_bits_adhoc_create(); + if( !info->bits ) + { + lsmash_destroy_multiple_buffers( sb->bank ); + return LSMASH_ERR_MEMORY_ALLOC; + } + lsmash_init_entry_list( info->sps_list ); + lsmash_init_entry_list( info->pps_list ); + lsmash_init_entry_list( info->slice_list ); + return 0; +} + +static int h264_check_nalu_header +( + lsmash_bs_t *bs, + h264_nalu_header_t *nuh, + int use_long_start_code +) +{ + uint8_t temp8 = lsmash_bs_show_byte( bs, use_long_start_code ? NALU_LONG_START_CODE_LENGTH : NALU_SHORT_START_CODE_LENGTH ); + nuh->forbidden_zero_bit = (temp8 >> 7) & 0x01; + nuh->nal_ref_idc = (temp8 >> 5) & 0x03; + nuh->nal_unit_type = temp8 & 0x1f; + nuh->length = 1; + if( nuh->nal_unit_type == H264_NALU_TYPE_PREFIX + || nuh->nal_unit_type == H264_NALU_TYPE_SLICE_EXT + || nuh->nal_unit_type == H264_NALU_TYPE_SLICE_EXT_DVC ) + { + /* We don't support these types of NALU. */ + //nuh->length += 3; + return LSMASH_ERR_PATCH_WELCOME; + } + if( nuh->forbidden_zero_bit ) + return LSMASH_ERR_INVALID_DATA; + /* SPS and PPS require long start code (0x00000001). + * Also AU delimiter requires it too because this type of NALU shall be the first NALU of any AU if present. */ + if( !use_long_start_code + && (nuh->nal_unit_type == H264_NALU_TYPE_SPS + || nuh->nal_unit_type == H264_NALU_TYPE_PPS + || nuh->nal_unit_type == H264_NALU_TYPE_AUD) ) + return LSMASH_ERR_INVALID_DATA; + if( nuh->nal_ref_idc ) + { + /* nal_ref_idc shall be equal to 0 for all NALUs having nal_unit_type equal to 6, 9, 10, 11, or 12. */ + if( nuh->nal_unit_type == H264_NALU_TYPE_SEI + || nuh->nal_unit_type == H264_NALU_TYPE_AUD + || nuh->nal_unit_type == H264_NALU_TYPE_EOS + || nuh->nal_unit_type == H264_NALU_TYPE_EOB + || nuh->nal_unit_type == H264_NALU_TYPE_FD ) + return LSMASH_ERR_INVALID_DATA; + } + else + /* nal_ref_idc shall not be equal to 0 for NALUs with nal_unit_type equal to 5. */ + if( nuh->nal_unit_type == H264_NALU_TYPE_SLICE_IDR ) + return LSMASH_ERR_INVALID_DATA; + return 0; +} + +uint64_t h264_find_next_start_code +( + lsmash_bs_t *bs, + h264_nalu_header_t *nuh, + uint64_t *start_code_length, + uint64_t *trailing_zero_bytes +) +{ + uint64_t length = 0; /* the length of the latest NALU */ + uint64_t count = 0; /* the number of the trailing zero bytes after the latest NALU */ + /* Check the type of the current start code. */ + int long_start_code + = (!lsmash_bs_is_end( bs, NALU_LONG_START_CODE_LENGTH ) && 0x00000001 == lsmash_bs_show_be32( bs, 0 )) ? 1 + : (!lsmash_bs_is_end( bs, NALU_SHORT_START_CODE_LENGTH ) && 0x000001 == lsmash_bs_show_be24( bs, 0 )) ? 0 + : -1; + if( long_start_code >= 0 && h264_check_nalu_header( bs, nuh, long_start_code ) == 0 ) + { + *start_code_length = long_start_code ? NALU_LONG_START_CODE_LENGTH : NALU_SHORT_START_CODE_LENGTH; + uint64_t distance = *start_code_length + nuh->length; + /* Find the start code of the next NALU and get the distance from the start code of the latest NALU. */ + if( !lsmash_bs_is_end( bs, distance + NALU_SHORT_START_CODE_LENGTH ) ) + { + uint32_t sync_bytes = lsmash_bs_show_be24( bs, distance ); + while( 0x000001 != sync_bytes ) + { + if( lsmash_bs_is_end( bs, ++distance + NALU_SHORT_START_CODE_LENGTH ) ) + { + distance = lsmash_bs_get_remaining_buffer_size( bs ); + break; + } + sync_bytes <<= 8; + sync_bytes |= lsmash_bs_show_byte( bs, distance + NALU_SHORT_START_CODE_LENGTH - 1 ); + sync_bytes &= 0xFFFFFF; + } + } + else + distance = lsmash_bs_get_remaining_buffer_size( bs ); + /* Any NALU has no consecutive zero bytes at the end. */ + while( 0x00 == lsmash_bs_show_byte( bs, distance - 1 ) ) + { + --distance; + ++count; + } + /* Remove the length of the start code. */ + length = distance - *start_code_length; + /* If there are one or more trailing zero bytes, we treat the last one byte as a part of the next start code. + * This makes the next start code a long start code. */ + if( count ) + --count; + } + else + { + /* No start code. */ + nuh->forbidden_zero_bit = 1; /* shall be 0, so invalid */ + nuh->nal_ref_idc = 0; /* arbitrary */ + nuh->nal_unit_type = H264_NALU_TYPE_UNSPECIFIED0; + nuh->length = 0; + *start_code_length = 0; + } + *trailing_zero_bytes = count; + return length; +} + +static h264_sps_t *h264_get_sps +( + lsmash_entry_list_t *sps_list, + uint8_t sps_id +) +{ + if( !sps_list || sps_id > 31 ) + return NULL; + for( lsmash_entry_t *entry = sps_list->head; entry; entry = entry->next ) + { + h264_sps_t *sps = (h264_sps_t *)entry->data; + if( !sps ) + return NULL; + if( sps->seq_parameter_set_id == sps_id ) + return sps; + } + h264_sps_t *sps = lsmash_malloc_zero( sizeof(h264_sps_t) ); + if( !sps ) + return NULL; + sps->seq_parameter_set_id = sps_id; + if( lsmash_add_entry( sps_list, sps ) < 0 ) + { + lsmash_free( sps ); + return NULL; + } + return sps; +} + +static h264_pps_t *h264_get_pps +( + lsmash_entry_list_t *pps_list, + uint8_t pps_id +) +{ + if( !pps_list ) + return NULL; + for( lsmash_entry_t *entry = pps_list->head; entry; entry = entry->next ) + { + h264_pps_t *pps = (h264_pps_t *)entry->data; + if( !pps ) + return NULL; + if( pps->pic_parameter_set_id == pps_id ) + return pps; + } + h264_pps_t *pps = lsmash_malloc_zero( sizeof(h264_pps_t) ); + if( !pps ) + return NULL; + pps->pic_parameter_set_id = pps_id; + if( lsmash_add_entry( pps_list, pps ) < 0 ) + { + lsmash_free( pps ); + return NULL; + } + return pps; +} + +static h264_slice_info_t *h264_get_slice_info +( + lsmash_entry_list_t *slice_list, + uint8_t slice_id +) +{ + if( !slice_list ) + return NULL; + for( lsmash_entry_t *entry = slice_list->head; entry; entry = entry->next ) + { + h264_slice_info_t *slice = (h264_slice_info_t *)entry->data; + if( !slice ) + return NULL; + if( slice->slice_id == slice_id ) + return slice; + } + h264_slice_info_t *slice = lsmash_malloc_zero( sizeof(h264_slice_info_t) ); + if( !slice ) + return NULL; + slice->slice_id = slice_id; + if( lsmash_add_entry( slice_list, slice ) < 0 ) + { + lsmash_free( slice ); + return NULL; + } + return slice; +} + +int h264_calculate_poc +( + h264_info_t *info, + h264_picture_info_t *picture, + h264_picture_info_t *prev_picture +) +{ +#if H264_POC_DEBUG_PRINT + fprintf( stderr, "PictureOrderCount\n" ); +#endif + h264_pps_t *pps = h264_get_pps( info->pps_list, picture->pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + h264_sps_t *sps = h264_get_sps( info->sps_list, pps->seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + int64_t TopFieldOrderCnt = 0; + int64_t BottomFieldOrderCnt = 0; + if( sps->pic_order_cnt_type == 0 ) + { + int32_t prevPicOrderCntMsb; + int32_t prevPicOrderCntLsb; + if( picture->idr ) + { + prevPicOrderCntMsb = 0; + prevPicOrderCntLsb = 0; + } + else if( prev_picture->ref_pic_has_mmco5 ) + { + prevPicOrderCntMsb = 0; + prevPicOrderCntLsb = prev_picture->ref_pic_bottom_field_flag ? 0 : prev_picture->ref_pic_TopFieldOrderCnt; + } + else + { + prevPicOrderCntMsb = prev_picture->ref_pic_PicOrderCntMsb; + prevPicOrderCntLsb = prev_picture->ref_pic_PicOrderCntLsb; + } + int64_t PicOrderCntMsb; + int32_t pic_order_cnt_lsb = picture->pic_order_cnt_lsb; + uint64_t MaxPicOrderCntLsb = sps->MaxPicOrderCntLsb; + if( (pic_order_cnt_lsb < prevPicOrderCntLsb) + && ((prevPicOrderCntLsb - pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2)) ) + PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; + else if( (pic_order_cnt_lsb > prevPicOrderCntLsb) + && ((pic_order_cnt_lsb - prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2)) ) + PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; + else + PicOrderCntMsb = prevPicOrderCntMsb; + IF_EXCEED_INT32( PicOrderCntMsb ) + return LSMASH_ERR_INVALID_DATA; + BottomFieldOrderCnt = TopFieldOrderCnt = PicOrderCntMsb + pic_order_cnt_lsb; + if( !picture->field_pic_flag ) + BottomFieldOrderCnt += picture->delta_pic_order_cnt_bottom; + IF_EXCEED_INT32( TopFieldOrderCnt ) + return LSMASH_ERR_INVALID_DATA; + IF_EXCEED_INT32( BottomFieldOrderCnt ) + return LSMASH_ERR_INVALID_DATA; + if( !picture->disposable ) + { + picture->ref_pic_has_mmco5 = picture->has_mmco5; + picture->ref_pic_bottom_field_flag = picture->bottom_field_flag; + picture->ref_pic_TopFieldOrderCnt = TopFieldOrderCnt; + picture->ref_pic_PicOrderCntMsb = PicOrderCntMsb; + picture->ref_pic_PicOrderCntLsb = pic_order_cnt_lsb; + } +#if H264_POC_DEBUG_PRINT + fprintf( stderr, " prevPicOrderCntMsb: %"PRId32"\n", prevPicOrderCntMsb ); + fprintf( stderr, " prevPicOrderCntLsb: %"PRId32"\n", prevPicOrderCntLsb ); + fprintf( stderr, " PicOrderCntMsb: %"PRId64"\n", PicOrderCntMsb ); + fprintf( stderr, " pic_order_cnt_lsb: %"PRId32"\n", pic_order_cnt_lsb ); + fprintf( stderr, " MaxPicOrderCntLsb: %"PRIu64"\n", MaxPicOrderCntLsb ); +#endif + } + else if( sps->pic_order_cnt_type == 1 ) + { + uint32_t frame_num = picture->frame_num; + uint32_t prevFrameNum = prev_picture->has_mmco5 ? 0 : prev_picture->frame_num; + uint32_t prevFrameNumOffset = prev_picture->has_mmco5 ? 0 : prev_picture->FrameNumOffset; + uint64_t FrameNumOffset = picture->idr ? 0 : prevFrameNumOffset + (prevFrameNum > frame_num ? sps->MaxFrameNum : 0); + if( FrameNumOffset > INT32_MAX ) + return LSMASH_ERR_INVALID_DATA; + int64_t expectedPicOrderCnt; + if( sps->num_ref_frames_in_pic_order_cnt_cycle ) + { + uint64_t absFrameNum = FrameNumOffset + frame_num; + absFrameNum -= picture->disposable && absFrameNum > 0; + if( absFrameNum ) + { + uint64_t picOrderCntCycleCnt = (absFrameNum - 1) / sps->num_ref_frames_in_pic_order_cnt_cycle; + uint8_t frameNumInPicOrderCntCycle = (absFrameNum - 1) % sps->num_ref_frames_in_pic_order_cnt_cycle; + expectedPicOrderCnt = picOrderCntCycleCnt * sps->ExpectedDeltaPerPicOrderCntCycle; + for( uint8_t i = 0; i <= frameNumInPicOrderCntCycle; i++ ) + expectedPicOrderCnt += sps->offset_for_ref_frame[i]; + } + else + expectedPicOrderCnt = 0; + } + else + expectedPicOrderCnt = 0; + if( picture->disposable ) + expectedPicOrderCnt += sps->offset_for_non_ref_pic; + TopFieldOrderCnt = expectedPicOrderCnt + picture->delta_pic_order_cnt[0]; + BottomFieldOrderCnt = TopFieldOrderCnt + sps->offset_for_top_to_bottom_field; + if( !picture->field_pic_flag ) + BottomFieldOrderCnt += picture->delta_pic_order_cnt[1]; + IF_EXCEED_INT32( TopFieldOrderCnt ) + return LSMASH_ERR_INVALID_DATA; + IF_EXCEED_INT32( BottomFieldOrderCnt ) + return LSMASH_ERR_INVALID_DATA; + picture->FrameNumOffset = FrameNumOffset; + } + else if( sps->pic_order_cnt_type == 2 ) + { + uint32_t frame_num = picture->frame_num; + uint32_t prevFrameNum = prev_picture->has_mmco5 ? 0 : prev_picture->frame_num; + int32_t prevFrameNumOffset = prev_picture->has_mmco5 ? 0 : prev_picture->FrameNumOffset; + int64_t FrameNumOffset; + int64_t tempPicOrderCnt; + if( picture->idr ) + { + FrameNumOffset = 0; + tempPicOrderCnt = 0; + } + else + { + FrameNumOffset = prevFrameNumOffset + (prevFrameNum > frame_num ? sps->MaxFrameNum : 0); + tempPicOrderCnt = 2 * (FrameNumOffset + frame_num) - picture->disposable; + IF_EXCEED_INT32( FrameNumOffset ) + return LSMASH_ERR_INVALID_DATA; + IF_EXCEED_INT32( tempPicOrderCnt ) + return LSMASH_ERR_INVALID_DATA; + } + TopFieldOrderCnt = tempPicOrderCnt; + BottomFieldOrderCnt = tempPicOrderCnt; + picture->FrameNumOffset = FrameNumOffset; + } + if( !picture->field_pic_flag ) + picture->PicOrderCnt = LSMASH_MIN( TopFieldOrderCnt, BottomFieldOrderCnt ); + else + picture->PicOrderCnt = picture->bottom_field_flag ? BottomFieldOrderCnt : TopFieldOrderCnt; +#if H264_POC_DEBUG_PRINT + if( picture->field_pic_flag ) + { + if( !picture->bottom_field_flag ) + fprintf( stderr, " TopFieldOrderCnt: %"PRId64"\n", TopFieldOrderCnt ); + else + fprintf( stderr, " BottomFieldOrderCnt: %"PRId64"\n", BottomFieldOrderCnt ); + } + fprintf( stderr, " POC: %"PRId32"\n", picture->PicOrderCnt ); +#endif + return 0; +} + +static int h264_parse_scaling_list +( + lsmash_bits_t *bits, + int sizeOfScalingList +) +{ + /* scaling_list( scalingList, sizeOfScalingList, useDefaultScalingMatrixFlag ) */ + int nextScale = 8; + for( int i = 0; i < sizeOfScalingList; i++ ) + { + int64_t delta_scale = nalu_get_exp_golomb_se( bits ); + if( delta_scale < -128 || delta_scale > 127 ) + return LSMASH_ERR_INVALID_DATA; + nextScale = (nextScale + delta_scale + 256) % 256; + if( nextScale == 0 ) + break; + } + return 0; +} + +static int h264_parse_hrd_parameters +( + lsmash_bits_t *bits, + h264_hrd_t *hrd +) +{ + /* hrd_parameters() */ + uint64_t cpb_cnt_minus1 = nalu_get_exp_golomb_ue( bits ); + if( cpb_cnt_minus1 > 31 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_bits_get( bits, 4 ); /* bit_rate_scale */ + lsmash_bits_get( bits, 4 ); /* cpb_size_scale */ + for( uint64_t SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++ ) + { + nalu_get_exp_golomb_ue( bits ); /* bit_rate_value_minus1[ SchedSelIdx ] */ + nalu_get_exp_golomb_ue( bits ); /* cpb_size_value_minus1[ SchedSelIdx ] */ + lsmash_bits_get( bits, 1 ); /* cbr_flag [ SchedSelIdx ] */ + } + lsmash_bits_get( bits, 5 ); /* initial_cpb_removal_delay_length_minus1 */ + hrd->cpb_removal_delay_length = lsmash_bits_get( bits, 5 ) + 1; + hrd->dpb_output_delay_length = lsmash_bits_get( bits, 5 ) + 1; + lsmash_bits_get( bits, 5 ); /* time_offset_length */ + return 0; +} + +static int h264_parse_sps_minimally +( + lsmash_bits_t *bits, + h264_sps_t *sps, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + memset( sps, 0, sizeof(h264_sps_t) ); + sps->profile_idc = lsmash_bits_get( bits, 8 ); + sps->constraint_set_flags = lsmash_bits_get( bits, 8 ); + sps->level_idc = lsmash_bits_get( bits, 8 ); + uint64_t seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + if( seq_parameter_set_id > 31 ) + return LSMASH_ERR_INVALID_DATA; + sps->seq_parameter_set_id = seq_parameter_set_id; + if( sps->profile_idc == 100 || sps->profile_idc == 110 || sps->profile_idc == 122 + || sps->profile_idc == 244 || sps->profile_idc == 44 || sps->profile_idc == 83 + || sps->profile_idc == 86 || sps->profile_idc == 118 || sps->profile_idc == 128 + || sps->profile_idc == 138 ) + { + sps->chroma_format_idc = nalu_get_exp_golomb_ue( bits ); + if( sps->chroma_format_idc == 3 ) + sps->separate_colour_plane_flag = lsmash_bits_get( bits, 1 ); + uint64_t bit_depth_luma_minus8 = nalu_get_exp_golomb_ue( bits ); + if( bit_depth_luma_minus8 > 6 ) + return LSMASH_ERR_INVALID_DATA; + uint64_t bit_depth_chroma_minus8 = nalu_get_exp_golomb_ue( bits ); + if( bit_depth_chroma_minus8 > 6 ) + return LSMASH_ERR_INVALID_DATA; + sps->bit_depth_luma_minus8 = bit_depth_luma_minus8; + sps->bit_depth_chroma_minus8 = bit_depth_chroma_minus8; + lsmash_bits_get( bits, 1 ); /* qpprime_y_zero_transform_bypass_flag */ + if( lsmash_bits_get( bits, 1 ) ) /* seq_scaling_matrix_present_flag */ + { + int num_loops = sps->chroma_format_idc != 3 ? 8 : 12; + for( int i = 0; i < num_loops; i++ ) + if( lsmash_bits_get( bits, 1 ) /* seq_scaling_list_present_flag[i] */ + && (err = h264_parse_scaling_list( bits, i < 6 ? 16 : 64 )) < 0 ) + return err; + } + } + else + { + sps->chroma_format_idc = 1; + sps->separate_colour_plane_flag = 0; + sps->bit_depth_luma_minus8 = 0; + sps->bit_depth_chroma_minus8 = 0; + } + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int h264_parse_sps +( + h264_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + lsmash_bits_t *bits = info->bits; + /* seq_parameter_set_data() */ + h264_sps_t temp_sps; + int err = h264_parse_sps_minimally( bits, &temp_sps, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + h264_sps_t *sps = h264_get_sps( info->sps_list, temp_sps.seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + memset( sps, 0, sizeof(h264_sps_t) ); + sps->profile_idc = temp_sps.profile_idc; + sps->constraint_set_flags = temp_sps.constraint_set_flags; + sps->level_idc = temp_sps.level_idc; + sps->seq_parameter_set_id = temp_sps.seq_parameter_set_id; + sps->chroma_format_idc = temp_sps.chroma_format_idc; + sps->separate_colour_plane_flag = temp_sps.separate_colour_plane_flag; + sps->bit_depth_luma_minus8 = temp_sps.bit_depth_luma_minus8; + sps->bit_depth_chroma_minus8 = temp_sps.bit_depth_chroma_minus8; + sps->ChromaArrayType = sps->separate_colour_plane_flag ? 0 : sps->chroma_format_idc; + uint64_t log2_max_frame_num_minus4 = nalu_get_exp_golomb_ue( bits ); + if( log2_max_frame_num_minus4 > 12 ) + return LSMASH_ERR_INVALID_DATA; + sps->log2_max_frame_num = log2_max_frame_num_minus4 + 4; + sps->MaxFrameNum = 1 << sps->log2_max_frame_num; + uint64_t pic_order_cnt_type = nalu_get_exp_golomb_ue( bits ); + if( pic_order_cnt_type > 2 ) + return LSMASH_ERR_INVALID_DATA; + sps->pic_order_cnt_type = pic_order_cnt_type; + if( sps->pic_order_cnt_type == 0 ) + { + uint64_t log2_max_pic_order_cnt_lsb_minus4 = nalu_get_exp_golomb_ue( bits ); + if( log2_max_pic_order_cnt_lsb_minus4 > 12 ) + return LSMASH_ERR_INVALID_DATA; + sps->log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4; + sps->MaxPicOrderCntLsb = 1 << sps->log2_max_pic_order_cnt_lsb; + } + else if( sps->pic_order_cnt_type == 1 ) + { + sps->delta_pic_order_always_zero_flag = lsmash_bits_get( bits, 1 ); + static const int64_t max_value = ((uint64_t)1 << 31) - 1; + static const int64_t min_value = -((uint64_t)1 << 31) + 1; + int64_t offset_for_non_ref_pic = nalu_get_exp_golomb_se( bits ); + if( offset_for_non_ref_pic < min_value || offset_for_non_ref_pic > max_value ) + return LSMASH_ERR_INVALID_DATA; + sps->offset_for_non_ref_pic = offset_for_non_ref_pic; + int64_t offset_for_top_to_bottom_field = nalu_get_exp_golomb_se( bits ); + if( offset_for_top_to_bottom_field < min_value || offset_for_top_to_bottom_field > max_value ) + return LSMASH_ERR_INVALID_DATA; + sps->offset_for_top_to_bottom_field = offset_for_top_to_bottom_field; + uint64_t num_ref_frames_in_pic_order_cnt_cycle = nalu_get_exp_golomb_ue( bits ); + if( num_ref_frames_in_pic_order_cnt_cycle > 255 ) + return LSMASH_ERR_INVALID_DATA; + sps->num_ref_frames_in_pic_order_cnt_cycle = num_ref_frames_in_pic_order_cnt_cycle; + sps->ExpectedDeltaPerPicOrderCntCycle = 0; + for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) + { + int64_t offset_for_ref_frame = nalu_get_exp_golomb_se( bits ); + if( offset_for_ref_frame < min_value || offset_for_ref_frame > max_value ) + return LSMASH_ERR_INVALID_DATA; + sps->offset_for_ref_frame[i] = offset_for_ref_frame; + sps->ExpectedDeltaPerPicOrderCntCycle += offset_for_ref_frame; + } + } + sps->max_num_ref_frames = nalu_get_exp_golomb_ue( bits ); + lsmash_bits_get( bits, 1 ); /* gaps_in_frame_num_value_allowed_flag */ + uint64_t pic_width_in_mbs_minus1 = nalu_get_exp_golomb_ue( bits ); + uint64_t pic_height_in_map_units_minus1 = nalu_get_exp_golomb_ue( bits ); + sps->frame_mbs_only_flag = lsmash_bits_get( bits, 1 ); + if( !sps->frame_mbs_only_flag ) + lsmash_bits_get( bits, 1 ); /* mb_adaptive_frame_field_flag */ + lsmash_bits_get( bits, 1 ); /* direct_8x8_inference_flag */ + uint64_t PicWidthInMbs = pic_width_in_mbs_minus1 + 1; + uint64_t PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1; + sps->PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits; + sps->cropped_width = PicWidthInMbs * 16; + sps->cropped_height = (2 - sps->frame_mbs_only_flag) * PicHeightInMapUnits * 16; + if( lsmash_bits_get( bits, 1 ) ) /* frame_cropping_flag */ + { + uint8_t CropUnitX; + uint8_t CropUnitY; + if( sps->ChromaArrayType == 0 ) + { + CropUnitX = 1; + CropUnitY = 2 - sps->frame_mbs_only_flag; + } + else + { + static const int SubWidthC [] = { 0, 2, 2, 1 }; + static const int SubHeightC[] = { 0, 2, 1, 1 }; + CropUnitX = SubWidthC [ sps->chroma_format_idc ]; + CropUnitY = SubHeightC[ sps->chroma_format_idc ] * (2 - sps->frame_mbs_only_flag); + } + uint64_t frame_crop_left_offset = nalu_get_exp_golomb_ue( bits ); + uint64_t frame_crop_right_offset = nalu_get_exp_golomb_ue( bits ); + uint64_t frame_crop_top_offset = nalu_get_exp_golomb_ue( bits ); + uint64_t frame_crop_bottom_offset = nalu_get_exp_golomb_ue( bits ); + sps->cropped_width -= (frame_crop_left_offset + frame_crop_right_offset) * CropUnitX; + sps->cropped_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * CropUnitY; + } + if( lsmash_bits_get( bits, 1 ) ) /* vui_parameters_present_flag */ + { + /* vui_parameters() */ + if( lsmash_bits_get( bits, 1 ) ) /* aspect_ratio_info_present_flag */ + { + uint8_t aspect_ratio_idc = lsmash_bits_get( bits, 8 ); + if( aspect_ratio_idc == 255 ) + { + /* Extended_SAR */ + sps->vui.sar_width = lsmash_bits_get( bits, 16 ); + sps->vui.sar_height = lsmash_bits_get( bits, 16 ); + } + else + { + static const struct + { + uint16_t sar_width; + uint16_t sar_height; + } pre_defined_sar[] + = { + { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, + { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, { 80, 33 }, + { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, { 4, 3 }, + { 3, 2 }, { 2, 1 } + }; + if( aspect_ratio_idc < (sizeof(pre_defined_sar) / sizeof(pre_defined_sar[0])) ) + { + sps->vui.sar_width = pre_defined_sar[ aspect_ratio_idc ].sar_width; + sps->vui.sar_height = pre_defined_sar[ aspect_ratio_idc ].sar_height; + } + else + { + /* Behavior when unknown aspect_ratio_idc is detected is not specified in the specification. */ + sps->vui.sar_width = 0; + sps->vui.sar_height = 0; + } + } + } + if( lsmash_bits_get( bits, 1 ) ) /* overscan_info_present_flag */ + lsmash_bits_get( bits, 1 ); /* overscan_appropriate_flag */ + if( lsmash_bits_get( bits, 1 ) ) /* video_signal_type_present_flag */ + { + lsmash_bits_get( bits, 3 ); /* video_format */ + sps->vui.video_full_range_flag = lsmash_bits_get( bits, 1 ); + if( lsmash_bits_get( bits, 1 ) ) /* colour_description_present_flag */ + { + sps->vui.colour_primaries = lsmash_bits_get( bits, 8 ); + sps->vui.transfer_characteristics = lsmash_bits_get( bits, 8 ); + sps->vui.matrix_coefficients = lsmash_bits_get( bits, 8 ); + } + } + if( lsmash_bits_get( bits, 1 ) ) /* chroma_loc_info_present_flag */ + { + nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_top_field */ + nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_bottom_field */ + } + if( lsmash_bits_get( bits, 1 ) ) /* timing_info_present_flag */ + { + sps->vui.num_units_in_tick = lsmash_bits_get( bits, 32 ); + sps->vui.time_scale = lsmash_bits_get( bits, 32 ); + sps->vui.fixed_frame_rate_flag = lsmash_bits_get( bits, 1 ); + } + else + { + sps->vui.num_units_in_tick = 1; /* arbitrary */ + sps->vui.time_scale = 50; /* arbitrary */ + sps->vui.fixed_frame_rate_flag = 0; + } + int nal_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); + if( nal_hrd_parameters_present_flag + && (err = h264_parse_hrd_parameters( bits, &sps->vui.hrd )) < 0 ) + return err; + int vcl_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); + if( vcl_hrd_parameters_present_flag + && (err = h264_parse_hrd_parameters( bits, &sps->vui.hrd )) < 0 ) + return err; + if( nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag ) + { + sps->vui.hrd.present = 1; + sps->vui.hrd.CpbDpbDelaysPresentFlag = 1; + lsmash_bits_get( bits, 1 ); /* low_delay_hrd_flag */ + } + sps->vui.pic_struct_present_flag = lsmash_bits_get( bits, 1 ); + if( lsmash_bits_get( bits, 1 ) ) /* bitstream_restriction_flag */ + { + lsmash_bits_get( bits, 1 ); /* motion_vectors_over_pic_boundaries_flag */ + nalu_get_exp_golomb_ue( bits ); /* max_bytes_per_pic_denom */ + nalu_get_exp_golomb_ue( bits ); /* max_bits_per_mb_denom */ + nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_horizontal */ + nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_vertical */ + nalu_get_exp_golomb_ue( bits ); /* max_num_reorder_frames */ + nalu_get_exp_golomb_ue( bits ); /* max_dec_frame_buffering */ + } + } + else + { + sps->vui.video_full_range_flag = 0; + sps->vui.num_units_in_tick = 1; /* arbitrary */ + sps->vui.time_scale = 50; /* arbitrary */ + sps->vui.fixed_frame_rate_flag = 0; + } + /* rbsp_trailing_bits() */ + if( !lsmash_bits_get( bits, 1 ) ) /* rbsp_stop_one_bit */ + return LSMASH_ERR_INVALID_DATA; + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + sps->present = 1; + info->sps = *sps; + return 0; +} + +static int h264_parse_pps_minimally +( + lsmash_bits_t *bits, + h264_pps_t *pps, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + memset( pps, 0, sizeof(h264_pps_t) ); + uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + if( pic_parameter_set_id > 255 ) + return LSMASH_ERR_INVALID_DATA; + pps->pic_parameter_set_id = pic_parameter_set_id; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int h264_parse_pps +( + h264_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + lsmash_bits_t *bits = info->bits; + /* pic_parameter_set_rbsp */ + h264_pps_t temp_pps; + int err = h264_parse_pps_minimally( bits, &temp_pps, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + h264_pps_t *pps = h264_get_pps( info->pps_list, temp_pps.pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + memset( pps, 0, sizeof(h264_pps_t) ); + pps->pic_parameter_set_id = temp_pps.pic_parameter_set_id; + uint64_t seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + if( seq_parameter_set_id > 31 ) + return LSMASH_ERR_INVALID_DATA; + h264_sps_t *sps = h264_get_sps( info->sps_list, seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + pps->seq_parameter_set_id = seq_parameter_set_id; + pps->entropy_coding_mode_flag = lsmash_bits_get( bits, 1 ); + pps->bottom_field_pic_order_in_frame_present_flag = lsmash_bits_get( bits, 1 ); + uint64_t num_slice_groups_minus1 = nalu_get_exp_golomb_ue( bits ); + if( num_slice_groups_minus1 > 7 ) + return LSMASH_ERR_INVALID_DATA; + pps->num_slice_groups_minus1 = num_slice_groups_minus1; + if( num_slice_groups_minus1 ) /* num_slice_groups_minus1 */ + { + uint64_t slice_group_map_type = nalu_get_exp_golomb_ue( bits ); + if( slice_group_map_type > 6 ) + return LSMASH_ERR_INVALID_DATA; + pps->slice_group_map_type = slice_group_map_type; + if( slice_group_map_type == 0 ) + for( uint64_t iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ ) + nalu_get_exp_golomb_ue( bits ); /* run_length_minus1[ iGroup ] */ + else if( slice_group_map_type == 2 ) + for( uint64_t iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) + { + nalu_get_exp_golomb_ue( bits ); /* top_left [ iGroup ] */ + nalu_get_exp_golomb_ue( bits ); /* bottom_right[ iGroup ] */ + } + else if( slice_group_map_type == 3 + || slice_group_map_type == 4 + || slice_group_map_type == 5 ) + { + lsmash_bits_get( bits, 1 ); /* slice_group_change_direction_flag */ + uint64_t slice_group_change_rate_minus1 = nalu_get_exp_golomb_ue( bits ); + if( slice_group_change_rate_minus1 > (sps->PicSizeInMapUnits - 1) ) + return LSMASH_ERR_INVALID_DATA; + pps->SliceGroupChangeRate = slice_group_change_rate_minus1 + 1; + } + else if( slice_group_map_type == 6 ) + { + uint64_t pic_size_in_map_units_minus1 = nalu_get_exp_golomb_ue( bits ); + int length = lsmash_ceil_log2( num_slice_groups_minus1 + 1 ); + for( uint64_t i = 0; i <= pic_size_in_map_units_minus1; i++ ) + /* slice_group_id */ + if( lsmash_bits_get( bits, length ) > num_slice_groups_minus1 ) + return LSMASH_ERR_INVALID_DATA; + } + } + pps->num_ref_idx_l0_default_active_minus1 = nalu_get_exp_golomb_ue( bits ); + pps->num_ref_idx_l1_default_active_minus1 = nalu_get_exp_golomb_ue( bits ); + pps->weighted_pred_flag = lsmash_bits_get( bits, 1 ); + pps->weighted_bipred_idc = lsmash_bits_get( bits, 2 ); + nalu_get_exp_golomb_se( bits ); /* pic_init_qp_minus26 */ + nalu_get_exp_golomb_se( bits ); /* pic_init_qs_minus26 */ + nalu_get_exp_golomb_se( bits ); /* chroma_qp_index_offset */ + pps->deblocking_filter_control_present_flag = lsmash_bits_get( bits, 1 ); + lsmash_bits_get( bits, 1 ); /* constrained_intra_pred_flag */ + pps->redundant_pic_cnt_present_flag = lsmash_bits_get( bits, 1 ); + if( nalu_check_more_rbsp_data( bits ) ) + { + int transform_8x8_mode_flag = lsmash_bits_get( bits, 1 ); + if( lsmash_bits_get( bits, 1 ) ) /* pic_scaling_matrix_present_flag */ + { + int num_loops = 6 + (sps->chroma_format_idc != 3 ? 2 : 6) * transform_8x8_mode_flag; + for( int i = 0; i < num_loops; i++ ) + if( lsmash_bits_get( bits, 1 ) /* pic_scaling_list_present_flag[i] */ + && (err = h264_parse_scaling_list( bits, i < 6 ? 16 : 64 )) < 0 ) + return err; + } + nalu_get_exp_golomb_se( bits ); /* second_chroma_qp_index_offset */ + } + /* rbsp_trailing_bits() */ + if( !lsmash_bits_get( bits, 1 ) ) /* rbsp_stop_one_bit */ + return LSMASH_ERR_INVALID_DATA; + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + pps->present = 1; + info->sps = *sps; + info->pps = *pps; + return 0; +} + +int h264_parse_sei +( + lsmash_bits_t *bits, + h264_sps_t *sps, + h264_sei_t *sei, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + uint8_t *rbsp_start = rbsp_buffer; + uint64_t rbsp_pos = 0; + do + { + /* sei_message() */ + uint32_t payloadType = 0; + for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) + { + /* 0xff : ff_byte + * otherwise: last_payload_type_byte */ + payloadType += temp; + ++rbsp_pos; + if( temp != 0xff ) + break; + } + uint32_t payloadSize = 0; + for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) + { + /* 0xff : ff_byte + * otherwise: last_payload_size_byte */ + payloadSize += temp; + ++rbsp_pos; + if( temp != 0xff ) + break; + } + if( payloadType == 1 ) + { + /* pic_timing */ + h264_hrd_t *hrd = sps ? &sps->vui.hrd : NULL; + if( !hrd ) + goto skip_sei_message; /* Any active SPS is not found. */ + sei->pic_timing.present = 1; + if( hrd->CpbDpbDelaysPresentFlag ) + { + lsmash_bits_get( bits, hrd->cpb_removal_delay_length ); /* cpb_removal_delay */ + lsmash_bits_get( bits, hrd->dpb_output_delay_length ); /* dpb_output_delay */ + } + if( sps->vui.pic_struct_present_flag ) + { + sei->pic_timing.pic_struct = lsmash_bits_get( bits, 4 ); + /* Skip the remaining bits. */ + uint32_t remaining_bits = payloadSize * 8 - 4; + if( hrd->CpbDpbDelaysPresentFlag ) + remaining_bits -= hrd->cpb_removal_delay_length + + hrd->dpb_output_delay_length; + lsmash_bits_get( bits, remaining_bits ); + } + } + else if( payloadType == 3 ) + { + /* filler_payload + * 'avc1' and 'avc2' samples are forbidden to contain this. */ + return LSMASH_ERR_PATCH_WELCOME; + } + else if( payloadType == 6 ) + { + /* recovery_point */ + sei->recovery_point.present = 1; + sei->recovery_point.random_accessible = 1; + sei->recovery_point.recovery_frame_cnt = nalu_get_exp_golomb_ue( bits ); + lsmash_bits_get( bits, 1 ); /* exact_match_flag */ + sei->recovery_point.broken_link_flag = lsmash_bits_get( bits, 1 ); + lsmash_bits_get( bits, 2 ); /* changing_slice_group_idc */ + } + else + { +skip_sei_message: + lsmash_bits_get( bits, payloadSize * 8 ); + } + lsmash_bits_get_align( bits ); + rbsp_pos += payloadSize; + } while( *(rbsp_start + rbsp_pos) != 0x80 ); /* All SEI messages are byte aligned at their end. + * Therefore, 0x80 shall be rbsp_trailing_bits(). */ + lsmash_bits_empty( bits ); + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +static int h264_parse_slice_header +( + h264_info_t *info, + h264_nalu_header_t *nuh +) +{ + h264_slice_info_t *slice = &info->slice; + memset( slice, 0, sizeof(h264_slice_info_t) ); + /* slice_header() */ + lsmash_bits_t *bits = info->bits; + nalu_get_exp_golomb_ue( bits ); /* first_mb_in_slice */ + uint8_t slice_type = slice->type = nalu_get_exp_golomb_ue( bits ); + if( (uint64_t)slice->type > 9 ) + return LSMASH_ERR_INVALID_DATA; + if( slice_type > 4 ) + slice_type = slice->type -= 5; + uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + if( pic_parameter_set_id > 255 ) + return LSMASH_ERR_INVALID_DATA; + slice->pic_parameter_set_id = pic_parameter_set_id; + h264_pps_t *pps = h264_get_pps( info->pps_list, pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + h264_sps_t *sps = h264_get_sps( info->sps_list, pps->seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + slice->seq_parameter_set_id = pps->seq_parameter_set_id; + slice->nal_ref_idc = nuh->nal_ref_idc; + slice->IdrPicFlag = (nuh->nal_unit_type == H264_NALU_TYPE_SLICE_IDR); + slice->pic_order_cnt_type = sps->pic_order_cnt_type; + if( (slice->IdrPicFlag || sps->max_num_ref_frames == 0) && slice_type != 2 && slice_type != 4 ) + return LSMASH_ERR_INVALID_DATA; + if( sps->separate_colour_plane_flag ) + lsmash_bits_get( bits, 2 ); /* colour_plane_id */ + uint64_t frame_num = lsmash_bits_get( bits, sps->log2_max_frame_num ); + if( frame_num >= (1 << sps->log2_max_frame_num) || (slice->IdrPicFlag && frame_num) ) + return LSMASH_ERR_INVALID_DATA; + slice->frame_num = frame_num; + if( !sps->frame_mbs_only_flag ) + { + slice->field_pic_flag = lsmash_bits_get( bits, 1 ); + if( slice->field_pic_flag ) + slice->bottom_field_flag = lsmash_bits_get( bits, 1 ); + } + if( slice->IdrPicFlag ) + { + uint64_t idr_pic_id = nalu_get_exp_golomb_ue( bits ); + if( idr_pic_id > 65535 ) + return LSMASH_ERR_INVALID_DATA; + slice->idr_pic_id = idr_pic_id; + } + if( sps->pic_order_cnt_type == 0 ) + { + uint64_t pic_order_cnt_lsb = lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); + if( pic_order_cnt_lsb >= sps->MaxPicOrderCntLsb ) + return LSMASH_ERR_INVALID_DATA; + slice->pic_order_cnt_lsb = pic_order_cnt_lsb; + if( pps->bottom_field_pic_order_in_frame_present_flag && !slice->field_pic_flag ) + slice->delta_pic_order_cnt_bottom = nalu_get_exp_golomb_se( bits ); + } + else if( sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag ) + { + slice->delta_pic_order_cnt[0] = nalu_get_exp_golomb_se( bits ); + if( pps->bottom_field_pic_order_in_frame_present_flag && !slice->field_pic_flag ) + slice->delta_pic_order_cnt[1] = nalu_get_exp_golomb_se( bits ); + } + if( pps->redundant_pic_cnt_present_flag ) + { + uint64_t redundant_pic_cnt = nalu_get_exp_golomb_ue( bits ); + if( redundant_pic_cnt > 127 ) + return LSMASH_ERR_INVALID_DATA; + slice->has_redundancy = !!redundant_pic_cnt; + } + if( slice_type == H264_SLICE_TYPE_B ) + lsmash_bits_get( bits, 1 ); + uint64_t num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_default_active_minus1; + uint64_t num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_default_active_minus1; + if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_B ) + { + if( lsmash_bits_get( bits, 1 ) ) /* num_ref_idx_active_override_flag */ + { + num_ref_idx_l0_active_minus1 = nalu_get_exp_golomb_ue( bits ); + if( num_ref_idx_l0_active_minus1 > 31 ) + return LSMASH_ERR_INVALID_DATA; + if( slice_type == H264_SLICE_TYPE_B ) + { + num_ref_idx_l1_active_minus1 = nalu_get_exp_golomb_ue( bits ); + if( num_ref_idx_l1_active_minus1 > 31 ) + return LSMASH_ERR_INVALID_DATA; + } + } + } + if( nuh->nal_unit_type == H264_NALU_TYPE_SLICE_EXT + || nuh->nal_unit_type == H264_NALU_TYPE_SLICE_EXT_DVC ) + { + return LSMASH_ERR_PATCH_WELCOME; /* No support of MVC yet */ +#if 0 + /* ref_pic_list_mvc_modification() */ + if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_B || slice_type == H264_SLICE_TYPE_SP ) + { + for( int i = 0; i < 1 + (slice_type == H264_SLICE_TYPE_B); i++ ) + { + if( lsmash_bits_get( bits, 1 ) ) /* (S)P and B: ref_pic_list_modification_flag_l0 + * B: ref_pic_list_modification_flag_l1 */ + { + uint64_t modification_of_pic_nums_idc; + do + { + modification_of_pic_nums_idc = nalu_get_exp_golomb_ue( bits ); +#if 0 + if( modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1 ) + nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1 */ + else if( modification_of_pic_nums_idc == 2 ) + nalu_get_exp_golomb_ue( bits ); /* long_term_pic_num */ + else if( modification_of_pic_nums_idc == 4 || modification_of_pic_nums_idc == 5 ) + nalu_get_exp_golomb_ue( bits ); /* abs_diff_view_idx_minus1 */ +#else + if( modification_of_pic_nums_idc != 3 ) + nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1, long_term_pic_num or abs_diff_view_idx_minus1 */ +#endif + } while( modification_of_pic_nums_idc != 3 ); + } + } + } +#endif + } + else + { + /* ref_pic_list_modification() */ + if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_B || slice_type == H264_SLICE_TYPE_SP ) + { + for( int i = 0; i < 1 + (slice_type == H264_SLICE_TYPE_B); i++ ) + { + if( lsmash_bits_get( bits, 1 ) ) /* (S)P and B: ref_pic_list_modification_flag_l0 + * B: ref_pic_list_modification_flag_l1 */ + { + uint64_t modification_of_pic_nums_idc; + do + { + modification_of_pic_nums_idc = nalu_get_exp_golomb_ue( bits ); +#if 0 + if( modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1 ) + nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1 */ + else if( modification_of_pic_nums_idc == 2 ) + nalu_get_exp_golomb_ue( bits ); /* long_term_pic_num */ +#else + if( modification_of_pic_nums_idc != 3 ) + nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1 or long_term_pic_num */ +#endif + } while( modification_of_pic_nums_idc != 3 ); + } + } + } + } + if( (pps->weighted_pred_flag && (slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_SP)) + || (pps->weighted_bipred_idc == 1 && slice_type == H264_SLICE_TYPE_B) ) + { + /* pred_weight_table() */ + nalu_get_exp_golomb_ue( bits ); /* luma_log2_weight_denom */ + if( sps->ChromaArrayType ) + nalu_get_exp_golomb_ue( bits ); /* chroma_log2_weight_denom */ + for( uint8_t i = 0; i <= num_ref_idx_l0_active_minus1; i++ ) + { + if( lsmash_bits_get( bits, 1 ) ) /* luma_weight_l0_flag */ + { + nalu_get_exp_golomb_se( bits ); /* luma_weight_l0[i] */ + nalu_get_exp_golomb_se( bits ); /* luma_offset_l0[i] */ + } + if( sps->ChromaArrayType + && lsmash_bits_get( bits, 1 ) /* chroma_weight_l0_flag */ ) + for( int j = 0; j < 2; j++ ) + { + nalu_get_exp_golomb_se( bits ); /* chroma_weight_l0[i][j]*/ + nalu_get_exp_golomb_se( bits ); /* chroma_offset_l0[i][j] */ + } + } + if( slice_type == H264_SLICE_TYPE_B ) + for( uint8_t i = 0; i <= num_ref_idx_l1_active_minus1; i++ ) + { + if( lsmash_bits_get( bits, 1 ) ) /* luma_weight_l1_flag */ + { + nalu_get_exp_golomb_se( bits ); /* luma_weight_l1[i] */ + nalu_get_exp_golomb_se( bits ); /* luma_offset_l1[i] */ + } + if( sps->ChromaArrayType + && lsmash_bits_get( bits, 1 ) /* chroma_weight_l1_flag */ ) + for( int j = 0; j < 2; j++ ) + { + nalu_get_exp_golomb_se( bits ); /* chroma_weight_l1[i][j]*/ + nalu_get_exp_golomb_se( bits ); /* chroma_offset_l1[i][j] */ + } + } + } + if( nuh->nal_ref_idc ) + { + /* dec_ref_pic_marking() */ + if( slice->IdrPicFlag ) + { + lsmash_bits_get( bits, 1 ); /* no_output_of_prior_pics_flag */ + lsmash_bits_get( bits, 1 ); /* long_term_reference_flag */ + } + else if( lsmash_bits_get( bits, 1 ) ) /* adaptive_ref_pic_marking_mode_flag */ + { + uint64_t memory_management_control_operation; + do + { + memory_management_control_operation = nalu_get_exp_golomb_ue( bits ); + if( memory_management_control_operation ) + { + if( memory_management_control_operation == 5 ) + slice->has_mmco5 = 1; + else + { + nalu_get_exp_golomb_ue( bits ); + if( memory_management_control_operation == 3 ) + nalu_get_exp_golomb_ue( bits ); + } + } + } while( memory_management_control_operation ); + } + } + /* We needn't read more if not slice data partition A. + * Skip slice_data() and rbsp_slice_trailing_bits(). */ + if( nuh->nal_unit_type == H264_NALU_TYPE_SLICE_DP_A ) + { + if( pps->entropy_coding_mode_flag && slice_type != H264_SLICE_TYPE_I && slice_type != H264_SLICE_TYPE_SI ) + nalu_get_exp_golomb_ue( bits ); /* cabac_init_idc */ + nalu_get_exp_golomb_se( bits ); /* slice_qp_delta */ + if( slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_SI ) + { + if( slice_type == H264_SLICE_TYPE_SP ) + lsmash_bits_get( bits, 1 ); /* sp_for_switch_flag */ + nalu_get_exp_golomb_se( bits ); /* slice_qs_delta */ + } + if( pps->deblocking_filter_control_present_flag + && nalu_get_exp_golomb_ue( bits ) != 1 /* disable_deblocking_filter_idc */ ) + { + int64_t slice_alpha_c0_offset_div2 = nalu_get_exp_golomb_se( bits ); + if( slice_alpha_c0_offset_div2 < -6 || slice_alpha_c0_offset_div2 > 6 ) + return LSMASH_ERR_INVALID_DATA; + int64_t slice_beta_offset_div2 = nalu_get_exp_golomb_se( bits ); + if( slice_beta_offset_div2 < -6 || slice_beta_offset_div2 > 6 ) + return LSMASH_ERR_INVALID_DATA; + } + if( pps->num_slice_groups_minus1 + && (pps->slice_group_map_type == 3 || pps->slice_group_map_type == 4 || pps->slice_group_map_type == 5) ) + { + uint64_t temp = ((uint64_t)sps->PicSizeInMapUnits - 1) / pps->SliceGroupChangeRate + 1; + uint64_t slice_group_change_cycle = lsmash_bits_get( bits, lsmash_ceil_log2( temp + 1 ) ); + if( slice_group_change_cycle > temp ) + return LSMASH_ERR_INVALID_DATA; + } + /* end of slice_header() */ + slice->slice_id = nalu_get_exp_golomb_ue( bits ); + h264_slice_info_t *slice_part = h264_get_slice_info( info->slice_list, slice->slice_id ); + if( !slice_part ) + return LSMASH_ERR_NAMELESS; + *slice_part = *slice; + } + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + info->sps = *sps; + info->pps = *pps; + return 0; +} + +int h264_parse_slice +( + h264_info_t *info, + h264_nalu_header_t *nuh, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + lsmash_bits_t *bits = info->bits; + uint64_t size = nuh->nal_unit_type == H264_NALU_TYPE_SLICE_IDR || nuh->nal_ref_idc == 0 + ? LSMASH_MIN( ebsp_size, 100 ) + : LSMASH_MIN( ebsp_size, 1000 ); + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, size ); + if( err < 0 ) + return err; + if( nuh->nal_unit_type != H264_NALU_TYPE_SLICE_DP_B + && nuh->nal_unit_type != H264_NALU_TYPE_SLICE_DP_C ) + return h264_parse_slice_header( info, nuh ); + /* slice_data_partition_b_layer_rbsp() or slice_data_partition_c_layer_rbsp() */ + uint64_t slice_id = nalu_get_exp_golomb_ue( bits ); + h264_slice_info_t *slice = h264_get_slice_info( info->slice_list, slice_id ); + if( !slice ) + return LSMASH_ERR_NAMELESS; + h264_pps_t *pps = h264_get_pps( info->pps_list, slice->pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + h264_sps_t *sps = h264_get_sps( info->sps_list, pps->seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + slice->seq_parameter_set_id = pps->seq_parameter_set_id; + if( sps->separate_colour_plane_flag ) + lsmash_bits_get( bits, 2 ); /* colour_plane_id */ + if( pps->redundant_pic_cnt_present_flag ) + { + uint64_t redundant_pic_cnt = nalu_get_exp_golomb_ue( bits ); + if( redundant_pic_cnt > 127 ) + return LSMASH_ERR_INVALID_DATA; + slice->has_redundancy = !!redundant_pic_cnt; + } + /* Skip slice_data() and rbsp_slice_trailing_bits(). */ + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + info->sps = *sps; + info->pps = *pps; + return 0; +} + +static int h264_get_sps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id +) +{ + /* max number of bits of sps_id = 11: 0b000001XXXXX + * (24 + 11 - 1) / 8 + 1 = 5 bytes + * Why +1? Because there might be an emulation_prevention_three_byte. */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[6]; + uint8_t buffer [6]; + bs.buffer.data = buffer; + bs.buffer.alloc = 6; + lsmash_bits_init( &bits, &bs ); + int err = nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 6 ) ); + if( err < 0 ) + return err; + lsmash_bits_get( &bits, 24 ); /* profile_idc, constraint_set_flags and level_idc */ + uint64_t sec_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); + if( sec_parameter_set_id > 31 ) + return LSMASH_ERR_INVALID_DATA; + *ps_id = sec_parameter_set_id; + return bs.error ? LSMASH_ERR_NAMELESS : 0; +} + +static int h264_get_pps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id +) +{ + /* max number of bits of pps_id = 17: 0b000000001XXXXXXXX + * (17 - 1) / 8 + 1 = 3 bytes + * Why +1? Because there might be an emulation_prevention_three_byte. */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[4]; + uint8_t buffer [4]; + bs.buffer.data = buffer; + bs.buffer.alloc = 4; + lsmash_bits_init( &bits, &bs ); + int err = nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 4 ) ); + if( err < 0 ) + return err; + uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); + if( pic_parameter_set_id > 255 ) + return LSMASH_ERR_INVALID_DATA; + *ps_id = pic_parameter_set_id; + return bs.error ? LSMASH_ERR_NAMELESS : 0; +} + +static inline int h264_get_ps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id, + lsmash_h264_parameter_set_type ps_type +) +{ + int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) + = ps_type == H264_PARAMETER_SET_TYPE_SPS ? h264_get_sps_id + : ps_type == H264_PARAMETER_SET_TYPE_PPS ? h264_get_pps_id + : NULL; + return get_ps_id ? get_ps_id( ps_ebsp, ps_ebsp_length, ps_id ) : LSMASH_ERR_INVALID_DATA; +} + +static inline lsmash_entry_list_t *h264_get_parameter_set_list +( + lsmash_h264_specific_parameters_t *param, + lsmash_h264_parameter_set_type ps_type +) +{ + if( !param->parameter_sets ) + return NULL; + return ps_type == H264_PARAMETER_SET_TYPE_SPS ? param->parameter_sets->sps_list + : ps_type == H264_PARAMETER_SET_TYPE_PPS ? param->parameter_sets->pps_list + : ps_type == H264_PARAMETER_SET_TYPE_SPSEXT ? param->parameter_sets->spsext_list + : NULL; +} + +static lsmash_entry_t *h264_get_ps_entry_from_param +( + lsmash_h264_specific_parameters_t *param, + lsmash_h264_parameter_set_type ps_type, + uint8_t ps_id +) +{ + int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) + = ps_type == H264_PARAMETER_SET_TYPE_SPS ? h264_get_sps_id + : ps_type == H264_PARAMETER_SET_TYPE_PPS ? h264_get_pps_id + : NULL; + if( !get_ps_id ) + return NULL; + lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( param, ps_type ); + if( !ps_list ) + return NULL; + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return NULL; + uint8_t param_ps_id; + if( get_ps_id( ps->nalUnit + 1, ps->nalUnitLength - 1, ¶m_ps_id ) < 0 ) + return NULL; + if( ps_id == param_ps_id ) + return entry; + } + return NULL; +} + +static inline void h264_update_picture_type +( + h264_picture_info_t *picture, + h264_slice_info_t *slice +) +{ + if( picture->type == H264_PICTURE_TYPE_I_P ) + { + if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_P_B; + else if( slice->type == H264_SLICE_TYPE_SI || slice->type == H264_SLICE_TYPE_SP ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP; + } + else if( picture->type == H264_PICTURE_TYPE_I_P_B ) + { + if( slice->type != H264_SLICE_TYPE_P && slice->type != H264_SLICE_TYPE_B && slice->type != H264_SLICE_TYPE_I ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; + } + else if( picture->type == H264_PICTURE_TYPE_I ) + { + if( slice->type == H264_SLICE_TYPE_P ) + picture->type = H264_PICTURE_TYPE_I_P; + else if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_P_B; + else if( slice->type == H264_SLICE_TYPE_SI ) + picture->type = H264_PICTURE_TYPE_I_SI; + else if( slice->type == H264_SLICE_TYPE_SP ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP; + } + else if( picture->type == H264_PICTURE_TYPE_SI_SP ) + { + if( slice->type == H264_SLICE_TYPE_P || slice->type == H264_SLICE_TYPE_I ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP; + else if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; + } + else if( picture->type == H264_PICTURE_TYPE_SI ) + { + if( slice->type == H264_SLICE_TYPE_P ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP; + else if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; + else if( slice->type != H264_SLICE_TYPE_I ) + picture->type = H264_PICTURE_TYPE_I_SI; + else if( slice->type == H264_SLICE_TYPE_SP ) + picture->type = H264_PICTURE_TYPE_SI_SP; + } + else if( picture->type == H264_PICTURE_TYPE_I_SI ) + { + if( slice->type == H264_SLICE_TYPE_P || slice->type == H264_SLICE_TYPE_SP ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP; + else if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; + } + else if( picture->type == H264_PICTURE_TYPE_I_SI_P_SP ) + { + if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; + } + else if( picture->type == H264_PICTURE_TYPE_NONE ) + { + if( slice->type == H264_SLICE_TYPE_P ) + picture->type = H264_PICTURE_TYPE_I_P; + else if( slice->type == H264_SLICE_TYPE_B ) + picture->type = H264_PICTURE_TYPE_I_P_B; + else if( slice->type == H264_SLICE_TYPE_I ) + picture->type = H264_PICTURE_TYPE_I; + else if( slice->type == H264_SLICE_TYPE_SI ) + picture->type = H264_PICTURE_TYPE_SI; + else if( slice->type == H264_SLICE_TYPE_SP ) + picture->type = H264_PICTURE_TYPE_SI_SP; + } +#if 0 + fprintf( stderr, "Picture type = %s\n", picture->type == H264_PICTURE_TYPE_I_P ? "P" + : picture->type == H264_PICTURE_TYPE_I_P_B ? "B" + : picture->type == H264_PICTURE_TYPE_I ? "I" + : picture->type == H264_PICTURE_TYPE_SI ? "SI" + : picture->type == H264_PICTURE_TYPE_I_SI ? "SI" + : "SP" ); +#endif +} + +/* Shall be called at least once per picture. */ +void h264_update_picture_info_for_slice +( + h264_info_t *info, + h264_picture_info_t *picture, + h264_slice_info_t *slice +) +{ + assert( info ); + picture->has_mmco5 |= slice->has_mmco5; + picture->has_redundancy |= slice->has_redundancy; + picture->has_primary |= !slice->has_redundancy; + h264_update_picture_type( picture, slice ); + /* Mark 'used' on active parameter sets. */ + uint8_t ps_id[2] = { slice->seq_parameter_set_id, slice->pic_parameter_set_id }; + for( int i = 0; i < 2; i++ ) + { + lsmash_h264_parameter_set_type ps_type = (lsmash_h264_parameter_set_type)i; + lsmash_entry_t *entry = h264_get_ps_entry_from_param( &info->avcC_param, ps_type, ps_id[i] ); + if( entry && entry->data ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( ps->unused ) + lsmash_append_h264_parameter_set( &info->avcC_param, ps_type, ps->nalUnit, ps->nalUnitLength ); + } + } + /* Discard this slice info. */ + slice->present = 0; +} + +/* Shall be called exactly once per picture. */ +void h264_update_picture_info +( + h264_info_t *info, + h264_picture_info_t *picture, + h264_slice_info_t *slice, + h264_sei_t *sei +) +{ + picture->frame_num = slice->frame_num; + picture->pic_order_cnt_lsb = slice->pic_order_cnt_lsb; + picture->delta_pic_order_cnt_bottom = slice->delta_pic_order_cnt_bottom; + picture->delta_pic_order_cnt[0] = slice->delta_pic_order_cnt[0]; + picture->delta_pic_order_cnt[1] = slice->delta_pic_order_cnt[1]; + picture->field_pic_flag = slice->field_pic_flag; + picture->bottom_field_flag = slice->bottom_field_flag; + picture->idr = slice->IdrPicFlag; + picture->pic_parameter_set_id = slice->pic_parameter_set_id; + picture->disposable = (slice->nal_ref_idc == 0); + picture->random_accessible = slice->IdrPicFlag; + h264_update_picture_info_for_slice( info, picture, slice ); + picture->independent = picture->type == H264_PICTURE_TYPE_I || picture->type == H264_PICTURE_TYPE_I_SI; + if( sei->pic_timing.present ) + { + if( sei->pic_timing.pic_struct < 9 ) + { + static const uint8_t DeltaTfiDivisor[9] = { 2, 1, 1, 2, 2, 3, 3, 4, 6 }; + picture->delta = DeltaTfiDivisor[ sei->pic_timing.pic_struct ]; + } + else + /* Reserved values in the spec we refer to. */ + picture->delta = picture->field_pic_flag ? 1 : 2; + sei->pic_timing.present = 0; + } + else + picture->delta = picture->field_pic_flag ? 1 : 2; + if( sei->recovery_point.present ) + { + picture->random_accessible |= sei->recovery_point.random_accessible; + picture->broken_link_flag |= sei->recovery_point.broken_link_flag; + picture->recovery_frame_cnt = sei->recovery_point.recovery_frame_cnt; + sei->recovery_point.present = 0; + } +} + +int h264_find_au_delimit_by_slice_info +( + h264_slice_info_t *slice, + h264_slice_info_t *prev_slice +) +{ + if( slice->frame_num != prev_slice->frame_num + || ((slice->pic_order_cnt_type == 0 && prev_slice->pic_order_cnt_type == 0) + && (slice->pic_order_cnt_lsb != prev_slice->pic_order_cnt_lsb + || slice->delta_pic_order_cnt_bottom != prev_slice->delta_pic_order_cnt_bottom)) + || ((slice->pic_order_cnt_type == 1 && prev_slice->pic_order_cnt_type == 1) + && (slice->delta_pic_order_cnt[0] != prev_slice->delta_pic_order_cnt[0] + || slice->delta_pic_order_cnt[1] != prev_slice->delta_pic_order_cnt[1])) + || slice->field_pic_flag != prev_slice->field_pic_flag + || slice->bottom_field_flag != prev_slice->bottom_field_flag + || slice->IdrPicFlag != prev_slice->IdrPicFlag + || slice->pic_parameter_set_id != prev_slice->pic_parameter_set_id + || ((slice->nal_ref_idc == 0 || prev_slice->nal_ref_idc == 0) + && (slice->nal_ref_idc != prev_slice->nal_ref_idc)) + || (slice->IdrPicFlag == 1 && prev_slice->IdrPicFlag == 1 + && slice->idr_pic_id != prev_slice->idr_pic_id) ) + return 1; + return 0; +} + +int h264_find_au_delimit_by_nalu_type +( + uint8_t nalu_type, + uint8_t prev_nalu_type +) +{ + return ((nalu_type >= H264_NALU_TYPE_SEI && nalu_type <= H264_NALU_TYPE_AUD) + || (nalu_type >= H264_NALU_TYPE_PREFIX && nalu_type <= H264_NALU_TYPE_RSV_NVCL18)) + && ((prev_nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && prev_nalu_type <= H264_NALU_TYPE_SLICE_IDR) + || prev_nalu_type == H264_NALU_TYPE_FD || prev_nalu_type == H264_NALU_TYPE_SLICE_AUX); +} + +int h264_supplement_buffer +( + h264_stream_buffer_t *sb, + h264_access_unit_t *au, + uint32_t size +) +{ + lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, size ); + if( !bank ) + return LSMASH_ERR_MEMORY_ALLOC; + sb->bank = bank; + sb->rbsp = lsmash_withdraw_buffer( bank, 1 ); + if( au && bank->number_of_buffers == 3 ) + { + au->data = lsmash_withdraw_buffer( bank, 2 ); + au->incomplete_data = lsmash_withdraw_buffer( bank, 3 ); + } + return 0; +} + +static void h264_bs_put_parameter_sets +( + lsmash_bs_t *bs, + lsmash_entry_list_t *ps_list, + uint32_t max_ps_count +) +{ + uint32_t ps_count = 0; + for( lsmash_entry_t *entry = ps_list->head; entry && ps_count < max_ps_count; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( ps && !ps->unused ) + { + lsmash_bs_put_be16( bs, ps->nalUnitLength ); + lsmash_bs_put_bytes( bs, ps->nalUnitLength, ps->nalUnit ); + } + else + continue; + ++ps_count; + } +} + +uint8_t *lsmash_create_h264_specific_info +( + lsmash_h264_specific_parameters_t *param, + uint32_t *data_length +) +{ + if( !param || !param->parameter_sets || !data_length ) + return NULL; + if( param->lengthSizeMinusOne != 0 && param->lengthSizeMinusOne != 1 && param->lengthSizeMinusOne != 3 ) + return NULL; + static const uint32_t max_ps_count[3] = { 31, 255, 255 }; + lsmash_entry_list_t *ps_list[3] = + { + param->parameter_sets->sps_list, /* SPS */ + param->parameter_sets->pps_list, /* PPS */ + param->parameter_sets->spsext_list /* SPSExt */ + }; + uint32_t ps_count[3] = { 0, 0, 0 }; + /* SPS and PPS are mandatory. */ + if( !ps_list[0] || !ps_list[0]->head || ps_list[0]->entry_count == 0 + || !ps_list[1] || !ps_list[1]->head || ps_list[1]->entry_count == 0 ) + return NULL; + /* Calculate enough buffer size. */ + uint32_t buffer_size = ISOM_BASEBOX_COMMON_SIZE + 11; + for( int i = 0; i < 3; i++ ) + if( ps_list[i] ) + for( lsmash_entry_t *entry = ps_list[i]->head; entry && ps_count[i] < max_ps_count[i]; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return NULL; + if( ps->unused ) + continue; + buffer_size += 2 + ps->nalUnitLength; + ++ps_count[i]; + } + /* Set up bytestream writer. */ + uint8_t buffer[buffer_size]; + lsmash_bs_t bs = { 0 }; + bs.buffer.data = buffer; + bs.buffer.alloc = buffer_size; + /* Create an AVCConfigurationBox */ + lsmash_bs_put_be32( &bs, 0 ); /* box size */ + lsmash_bs_put_be32( &bs, ISOM_BOX_TYPE_AVCC.fourcc ); /* box type: 'avcC' */ + lsmash_bs_put_byte( &bs, 1 ); /* configurationVersion */ + lsmash_bs_put_byte( &bs, param->AVCProfileIndication ); /* AVCProfileIndication */ + lsmash_bs_put_byte( &bs, param->profile_compatibility ); /* profile_compatibility */ + lsmash_bs_put_byte( &bs, param->AVCLevelIndication ); /* AVCLevelIndication */ + lsmash_bs_put_byte( &bs, param->lengthSizeMinusOne | 0xfc ); /* lengthSizeMinusOne */ + lsmash_bs_put_byte( &bs, ps_count[0] | 0xe0 ); /* numOfSequenceParameterSets */ + h264_bs_put_parameter_sets( &bs, ps_list[0], ps_count[0] ); /* sequenceParameterSetLength + * sequenceParameterSetNALUnit */ + lsmash_bs_put_byte( &bs, ps_count[1] ); /* numOfPictureParameterSets */ + h264_bs_put_parameter_sets( &bs, ps_list[1], ps_count[1] ); /* pictureParameterSetLength + * pictureParameterSetNALUnit */ + if( H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) + { + lsmash_bs_put_byte( &bs, param->chroma_format | 0xfc ); /* chroma_format */ + lsmash_bs_put_byte( &bs, param->bit_depth_luma_minus8 | 0xf8 ); /* bit_depth_luma_minus8 */ + lsmash_bs_put_byte( &bs, param->bit_depth_chroma_minus8 | 0xf8 ); /* bit_depth_chroma_minus8 */ + if( ps_list[2] ) + { + lsmash_bs_put_byte( &bs, ps_count[2] ); /* numOfSequenceParameterSetExt */ + h264_bs_put_parameter_sets( &bs, ps_list[2], ps_count[2] ); /* sequenceParameterSetExtLength + * sequenceParameterSetExtNALUnit */ + } + else /* no sequence parameter set extensions */ + lsmash_bs_put_byte( &bs, 0 ); /* numOfSequenceParameterSetExt */ + } + uint8_t *data = lsmash_bs_export_data( &bs, data_length ); + /* Update box size. */ + LSMASH_SET_BE32( data, *data_length ); + return data; +} + +static inline int h264_validate_ps_type +( + lsmash_h264_parameter_set_type ps_type, + void *ps_data, + uint32_t ps_length +) +{ + if( !ps_data || ps_length < 2 ) + return LSMASH_ERR_INVALID_DATA; + if( ps_type != H264_PARAMETER_SET_TYPE_SPS + && ps_type != H264_PARAMETER_SET_TYPE_PPS + && ps_type != H264_PARAMETER_SET_TYPE_SPSEXT ) + return LSMASH_ERR_INVALID_DATA; + uint8_t nalu_type = *((uint8_t *)ps_data) & 0x1f; + if( nalu_type != H264_NALU_TYPE_SPS + && nalu_type != H264_NALU_TYPE_PPS + && nalu_type != H264_NALU_TYPE_SPS_EXT ) + return LSMASH_ERR_INVALID_DATA; + if( (ps_type == H264_PARAMETER_SET_TYPE_SPS && nalu_type != H264_NALU_TYPE_SPS) + || (ps_type == H264_PARAMETER_SET_TYPE_PPS && nalu_type != H264_NALU_TYPE_PPS) + || (ps_type == H264_PARAMETER_SET_TYPE_SPSEXT && nalu_type != H264_NALU_TYPE_SPS_EXT) ) + return LSMASH_ERR_INVALID_DATA; + return 0; +} + +lsmash_dcr_nalu_appendable lsmash_check_h264_parameter_set_appendable +( + lsmash_h264_specific_parameters_t *param, + lsmash_h264_parameter_set_type ps_type, + void *_ps_data, + uint32_t ps_length +) +{ + uint8_t *ps_data = _ps_data; + if( !param ) + return DCR_NALU_APPEND_ERROR; + if( h264_validate_ps_type( ps_type, ps_data, ps_length ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( ps_type == H264_PARAMETER_SET_TYPE_SPSEXT + && !H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) + return DCR_NALU_APPEND_ERROR; + /* Check whether the same parameter set already exsits or not. */ + lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( param, ps_type ); + if( !ps_list || !ps_list->head ) + return DCR_NALU_APPEND_POSSIBLE; /* No parameter set */ + switch( nalu_check_same_ps_existence( ps_list, ps_data, ps_length ) ) + { + case 0 : break; + case 1 : return DCR_NALU_APPEND_DUPLICATED; /* The same parameter set already exists. */ + default : return DCR_NALU_APPEND_ERROR; /* An error occured. */ + } + uint32_t max_ps_length; + if( nalu_get_max_ps_length( ps_list, &max_ps_length ) < 0 ) + return DCR_NALU_APPEND_ERROR; + max_ps_length = LSMASH_MAX( max_ps_length, ps_length ); + uint32_t ps_count; + if( nalu_get_ps_count( ps_list, &ps_count ) ) + return DCR_NALU_APPEND_ERROR; + if( (ps_type == H264_PARAMETER_SET_TYPE_SPS && ps_count >= 31) + || (ps_type == H264_PARAMETER_SET_TYPE_PPS && ps_count >= 255) + || (ps_type == H264_PARAMETER_SET_TYPE_SPSEXT && ps_count >= 255) ) + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; /* No more appendable parameter sets. */ + if( ps_type == H264_PARAMETER_SET_TYPE_SPSEXT ) + return DCR_NALU_APPEND_POSSIBLE; + /* Check whether a new specific info is needed or not. */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[max_ps_length]; + uint8_t buffer [max_ps_length]; + bs.buffer.data = buffer; + bs.buffer.alloc = max_ps_length; + lsmash_bits_init( &bits, &bs ); + if( ps_type == H264_PARAMETER_SET_TYPE_PPS ) + { + /* PPS */ + uint8_t pps_id; + if( h264_get_pps_id( ps_data + 1, ps_length - 1, &pps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return DCR_NALU_APPEND_ERROR; + if( ps->unused ) + continue; + uint8_t param_pps_id; + if( h264_get_pps_id( ps->nalUnit + 1, ps->nalUnitLength - 1, ¶m_pps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( pps_id == param_pps_id ) + /* PPS that has the same pic_parameter_set_id already exists with different form. */ + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + } + return DCR_NALU_APPEND_POSSIBLE; + } + /* SPS */ + h264_sps_t sps; + if( h264_parse_sps_minimally( &bits, &sps, rbsp_buffer, ps_data + 1, ps_length - 1 ) < 0 ) + return DCR_NALU_APPEND_ERROR; + lsmash_bits_empty( &bits ); + /* FIXME; If the sequence parameter sets are marked with different profiles, + * and the relevant profile compatibility flags are all zero, + * then the stream may need examination to determine which profile, if any, the stream conforms to. + * If the stream is not examined, or the examination reveals that there is no profile to which the stream conforms, + * then the stream must be split into two or more sub-streams with separate configuration records in which these rules can be met. */ +#if 0 + if( sps.profile_idc != param->AVCProfileIndication && (sps->constraint_set_flags & param->profile_compatibility) ) +#else + if( sps.profile_idc != param->AVCProfileIndication ) +#endif + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + /* The values of chroma_format_idc, bit_depth_luma_minus8 and bit_depth_chroma_minus8 + * must be identical in all SPSs in a single AVC configuration record. */ + if( H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) + && (sps.chroma_format_idc != param->chroma_format + || sps.bit_depth_luma_minus8 != param->bit_depth_luma_minus8 + || sps.bit_depth_chroma_minus8 != param->bit_depth_chroma_minus8) ) + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + /* Forbidden to duplicate SPS that has the same seq_parameter_set_id with different form within the same configuration record. */ + uint8_t sps_id = sps.seq_parameter_set_id; + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return DCR_NALU_APPEND_ERROR; + if( ps->unused ) + continue; + uint8_t param_sps_id; + if( h264_get_sps_id( ps->nalUnit + 1, ps->nalUnitLength - 1, ¶m_sps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( sps_id == param_sps_id ) + /* SPS that has the same seq_parameter_set_id already exists with different form. */ + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + if( entry == ps_list->head ) + { + /* Check if the visual presentation sizes are different. */ + h264_sps_t first_sps; + if( h264_parse_sps_minimally( &bits, &first_sps, rbsp_buffer, + ps->nalUnit + 1, + ps->nalUnitLength - 1 ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( sps.cropped_width != first_sps.cropped_width + || sps.cropped_height != first_sps.cropped_height ) + return DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED; + } + } + return DCR_NALU_APPEND_POSSIBLE; +} + +static inline void h264_reorder_parameter_set_ascending_id +( + lsmash_h264_specific_parameters_t *param, + lsmash_h264_parameter_set_type ps_type, + lsmash_entry_list_t *ps_list, + uint8_t ps_id +) +{ + lsmash_entry_t *entry = NULL; + if( ps_id ) + for( int i = ps_id - 1; i; i-- ) + { + entry = h264_get_ps_entry_from_param( param, ps_type, i ); + if( entry ) + break; + } + int append_head = 0; + if( !entry ) + { + /* Couldn't find any parameter set with lower identifier. + * Next, find parameter set with upper identifier. */ + int max_ps_id = ps_type == H264_PARAMETER_SET_TYPE_SPS ? 31 : 255; + for( int i = ps_id + 1; i <= max_ps_id; i++ ) + { + entry = h264_get_ps_entry_from_param( param, ps_type, i ); + if( entry ) + break; + } + if( entry ) + append_head = 1; + } + if( !entry ) + return; /* The new entry was appended to the tail. */ + lsmash_entry_t *new_entry = ps_list->tail; + if( append_head ) + { + /* before: entry[i > ps_id] ... -> prev_entry -> new_entry[ps_id] + * after: new_entry[ps_id] -> entry[i > ps_id] -> ... -> prev_entry */ + if( new_entry->prev ) + new_entry->prev->next = NULL; + new_entry->prev = NULL; + entry->prev = new_entry; + new_entry->next = entry; + return; + } + /* before: entry[i < ps_id] -> next_entry -> ... -> prev_entry -> new_entry[ps_id] + * after: entry[i < ps_id] -> new_entry[ps_id] -> next_entry -> ... -> prev_entry */ + if( new_entry->prev ) + new_entry->prev->next = NULL; + new_entry->prev = entry; + new_entry->next = entry->next; + if( entry->next ) + entry->next->prev = new_entry; + entry->next = new_entry; +} + +int lsmash_append_h264_parameter_set +( + lsmash_h264_specific_parameters_t *param, + lsmash_h264_parameter_set_type ps_type, + void *_ps_data, + uint32_t ps_length +) +{ + uint8_t *ps_data = _ps_data; + if( !param || !ps_data || ps_length < 2 ) + return LSMASH_ERR_FUNCTION_PARAM; + if( ps_type != H264_PARAMETER_SET_TYPE_SPS + && ps_type != H264_PARAMETER_SET_TYPE_PPS + && ps_type != H264_PARAMETER_SET_TYPE_SPSEXT ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !param->parameter_sets ) + { + param->parameter_sets = lsmash_malloc_zero( sizeof(lsmash_h264_parameter_sets_t) ); + if( !param->parameter_sets ) + return LSMASH_ERR_MEMORY_ALLOC; + } + lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( param, ps_type ); + if( !ps_list ) + return LSMASH_ERR_NAMELESS; + if( ps_type == H264_PARAMETER_SET_TYPE_SPSEXT ) + { + if( !H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) + return 0; + isom_dcr_ps_entry_t *ps = isom_create_ps_entry( ps_data, ps_length ); + if( !ps ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( ps_list, ps ) < 0 ) + { + isom_remove_dcr_ps( ps ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; + } + /* Check if the same parameter set identifier already exists. */ + uint8_t ps_id; + int err = h264_get_ps_id( ps_data + 1, ps_length - 1, &ps_id, ps_type ); + if( err < 0 ) + return err; + lsmash_entry_t *entry = h264_get_ps_entry_from_param( param, ps_type, ps_id ); + isom_dcr_ps_entry_t *ps = entry ? (isom_dcr_ps_entry_t *)entry->data : NULL; + if( ps && !ps->unused ) + /* The same parameter set identifier already exists. */ + return LSMASH_ERR_FUNCTION_PARAM; + int invoke_reorder; + if( ps ) + { + /* Reuse an already existed parameter set in the list. */ + ps->unused = 0; + if( ps->nalUnit != ps_data ) + { + /* The same address could be given when called by h264_update_picture_info_for_slice(). */ + lsmash_free( ps->nalUnit ); + ps->nalUnit = ps_data; + } + ps->nalUnitLength = ps_length; + invoke_reorder = 0; + } + else + { + /* Create a new parameter set and append it into the list. */ + ps = isom_create_ps_entry( ps_data, ps_length ); + if( !ps ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( ps_list, ps ) < 0 ) + { + isom_remove_dcr_ps( ps ); + return LSMASH_ERR_MEMORY_ALLOC; + } + invoke_reorder = 1; + } + if( ps_type == H264_PARAMETER_SET_TYPE_SPS ) + { + /* Update specific info with SPS. */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[ps_length]; + uint8_t buffer [ps_length]; + bs.buffer.data = buffer; + bs.buffer.alloc = ps_length; + lsmash_bits_init( &bits, &bs ); + h264_sps_t sps; + if( (err = h264_parse_sps_minimally( &bits, &sps, rbsp_buffer, ps_data + 1, ps_length - 1 )) < 0 ) + { + lsmash_remove_entry_tail( ps_list, isom_remove_dcr_ps ); + return err; + } + if( ps_list->entry_count == 1 ) + param->profile_compatibility = 0xff; + param->AVCProfileIndication = sps.profile_idc; + param->profile_compatibility &= sps.constraint_set_flags; + param->AVCLevelIndication = LSMASH_MAX( param->AVCLevelIndication, sps.level_idc ); + param->chroma_format = sps.chroma_format_idc; + param->bit_depth_luma_minus8 = sps.bit_depth_luma_minus8; + param->bit_depth_chroma_minus8 = sps.bit_depth_chroma_minus8; + } + if( invoke_reorder ) + /* Add a new parameter set in order of ascending parameter set identifier. */ + h264_reorder_parameter_set_ascending_id( param, ps_type, ps_list, ps_id ); + return 0; +} + +int h264_try_to_append_parameter_set +( + h264_info_t *info, + lsmash_h264_parameter_set_type ps_type, + void *_ps_data, + uint32_t ps_length +) +{ + uint8_t *ps_data = _ps_data; + lsmash_dcr_nalu_appendable ret = lsmash_check_h264_parameter_set_appendable( &info->avcC_param, ps_type, ps_data, ps_length ); + lsmash_h264_specific_parameters_t *param; + switch( ret ) + { + case DCR_NALU_APPEND_ERROR : /* Error */ + return LSMASH_ERR_NAMELESS; + case DCR_NALU_APPEND_NEW_DCR_REQUIRED : /* Mulitiple sample description is needed. */ + case DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED : /* Mulitiple sample description is needed. */ + param = &info->avcC_param_next; + info->avcC_pending = 1; + break; + case DCR_NALU_APPEND_POSSIBLE : /* Appendable */ + param = info->avcC_pending ? &info->avcC_param_next : &info->avcC_param; + break; + default : /* No need to append */ + return 0; + } + int err; + switch( ps_type ) + { + case H264_PARAMETER_SET_TYPE_SPS : + if( (err = h264_parse_sps( info, info->buffer.rbsp, ps_data + 1, ps_length - 1 )) < 0 ) + return err; + break; + case H264_PARAMETER_SET_TYPE_PPS : + if( (err = h264_parse_pps( info, info->buffer.rbsp, ps_data + 1, ps_length - 1 )) < 0 ) + return err; + break; + default : + break; + } + return lsmash_append_h264_parameter_set( param, ps_type, ps_data, ps_length ); +} + +static inline int h264_move_dcr_nalu_entry +( + lsmash_h264_specific_parameters_t *dst_data, + lsmash_h264_specific_parameters_t *src_data, + lsmash_h264_parameter_set_type ps_type +) +{ + lsmash_entry_list_t *src_ps_list = h264_get_parameter_set_list( src_data, ps_type ); + lsmash_entry_list_t *dst_ps_list = h264_get_parameter_set_list( dst_data, ps_type ); + assert( src_ps_list && dst_ps_list ); + for( lsmash_entry_t *src_entry = src_ps_list->head; src_entry; src_entry = src_entry->next ) + { + isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)src_entry->data; + if( !src_ps ) + continue; + int err; + uint8_t src_ps_id; + if( (err = h264_get_ps_id( src_ps->nalUnit + 1, src_ps->nalUnitLength - 1, &src_ps_id, ps_type )) < 0 ) + return err; + lsmash_entry_t *dst_entry; + for( dst_entry = dst_ps_list->head; dst_entry; dst_entry = dst_entry->next ) + { + isom_dcr_ps_entry_t *dst_ps = (isom_dcr_ps_entry_t *)dst_entry->data; + if( !dst_ps ) + continue; + uint8_t dst_ps_id; + if( (err = h264_get_ps_id( dst_ps->nalUnit + 1, dst_ps->nalUnitLength - 1, &dst_ps_id, ps_type )) < 0 ) + return err; + if( dst_ps_id == src_ps_id ) + { + /* Replace the old parameter set with the new one. */ + assert( dst_entry->data != src_entry->data ); + isom_remove_dcr_ps( dst_ps ); + dst_entry->data = src_entry->data; + src_entry->data = NULL; + break; + } + } + if( !dst_entry ) + { + /* Move the parameter set. */ + if( lsmash_add_entry( dst_ps_list, src_ps ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + src_entry->data = NULL; + } + } + return 0; +} + +int h264_move_pending_avcC_param +( + h264_info_t *info +) +{ + assert( info ); + if( !info->avcC_pending ) + return 0; + /* Mark 'unused' on parameter sets within the decoder configuration record. */ + for( int i = 0; i < H264_PARAMETER_SET_TYPE_NUM; i++ ) + { + lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( &info->avcC_param, i ); + assert( ps_list ); + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + continue; + ps->unused = 1; + } + } + /* Move the new parameter sets. */ + int err; + if( (err = h264_move_dcr_nalu_entry( &info->avcC_param, &info->avcC_param_next, H264_PARAMETER_SET_TYPE_SPS )) < 0 + || (err = h264_move_dcr_nalu_entry( &info->avcC_param, &info->avcC_param_next, H264_PARAMETER_SET_TYPE_PPS )) < 0 ) + return err; + /* Move to the pending. */ + lsmash_h264_parameter_sets_t *parameter_sets = info->avcC_param.parameter_sets; /* Back up parameter sets. */ + info->avcC_param = info->avcC_param_next; + info->avcC_param.parameter_sets = parameter_sets; + /* No pending avcC. */ + lsmash_destroy_h264_parameter_sets( &info->avcC_param_next ); + memset( &info->avcC_param_next, 0, sizeof(lsmash_h264_specific_parameters_t) ); + info->avcC_pending = 0; + return 0; +} + +static int h264_parse_succeeded +( + h264_info_t *info, + lsmash_h264_specific_parameters_t *param +) +{ + int ret; + if( info->sps.present && info->pps.present ) + { + *param = info->avcC_param; + /* Avoid freeing parameter sets. */ + info->avcC_param.parameter_sets = NULL; + ret = 0; + } + else + ret = LSMASH_ERR_INVALID_DATA; + h264_cleanup_parser( info ); + return ret; +} + +static inline int h264_parse_failed +( + h264_info_t *info, + int ret +) +{ + h264_cleanup_parser( info ); + return ret; +} + +int lsmash_setup_h264_specific_parameters_from_access_unit +( + lsmash_h264_specific_parameters_t *param, + uint8_t *data, + uint32_t data_length +) +{ + if( !param || !data || data_length == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + h264_info_t *info = &(h264_info_t){ { 0 } }; + lsmash_bs_t *bs = &(lsmash_bs_t){ 0 }; + int err = lsmash_bs_set_empty_stream( bs, data, data_length ); + if( err < 0 ) + return err; + uint64_t sc_head_pos = nalu_find_first_start_code( bs ); + if( sc_head_pos == NALU_NO_START_CODE_FOUND ) + return LSMASH_ERR_INVALID_DATA; + if( (err = h264_setup_parser( info, 1 )) < 0 ) + return h264_parse_failed( info, err ); + h264_stream_buffer_t *sb = &info->buffer; + h264_slice_info_t *slice = &info->slice; + while( 1 ) + { + h264_nalu_header_t nuh; + uint64_t start_code_length; + uint64_t trailing_zero_bytes; + uint64_t nalu_length = h264_find_next_start_code( bs, &nuh, &start_code_length, &trailing_zero_bytes ); + if( start_code_length <= NALU_SHORT_START_CODE_LENGTH && lsmash_bs_is_end( bs, nalu_length ) ) + /* For the last NALU. This NALU already has been parsed. */ + return h264_parse_succeeded( info, param ); + uint8_t nalu_type = nuh.nal_unit_type; + uint64_t next_sc_head_pos = sc_head_pos + + start_code_length + + nalu_length + + trailing_zero_bytes; + if( nalu_type == H264_NALU_TYPE_FD ) + { + /* We don't support streams with both filler and HRD yet. + * Otherwise, just skip filler because elemental streams defined in 14496-15 are forbidden to use filler. */ + if( info->sps.vui.hrd.present ) + return h264_parse_failed( info, LSMASH_ERR_PATCH_WELCOME ); + } + else if( (nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SPS_EXT) + || nalu_type == H264_NALU_TYPE_SLICE_AUX ) + { + /* Increase the buffer if needed. */ + uint64_t possible_au_length = NALU_DEFAULT_NALU_LENGTH_SIZE + nalu_length; + if( sb->bank->buffer_size < possible_au_length + && (err = h264_supplement_buffer( sb, NULL, 2 * possible_au_length )) < 0 ) + return h264_parse_failed( info, err ); + /* Get the EBSP of the current NALU here. + * AVC elemental stream defined in 14496-15 can recognize from 0 to 13, and 19 of nal_unit_type. + * We don't support SVC and MVC elemental stream defined in 14496-15 yet. */ + uint8_t *nalu = lsmash_bs_get_buffer_data( bs ) + start_code_length; + if( nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SLICE_IDR ) + { + /* VCL NALU (slice) */ + h264_slice_info_t prev_slice = *slice; + if( (err = h264_parse_slice( info, &nuh, sb->rbsp, nalu + nuh.length, nalu_length - nuh.length )) < 0 ) + return h264_parse_failed( info, err ); + if( prev_slice.present ) + { + /* Check whether the AU that contains the previous VCL NALU completed or not. */ + if( h264_find_au_delimit_by_slice_info( slice, &prev_slice ) ) + /* The current NALU is the first VCL NALU of the primary coded picture of an new AU. + * Therefore, the previous slice belongs to that new AU. */ + return h264_parse_succeeded( info, param ); + } + slice->present = 1; + } + else + { + if( h264_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) + /* The last slice belongs to the AU you want at this time. */ + return h264_parse_succeeded( info, param ); + switch( nalu_type ) + { + case H264_NALU_TYPE_SPS : + if( (err = h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPS, nalu, nalu_length )) < 0 ) + return h264_parse_failed( info, err ); + break; + case H264_NALU_TYPE_PPS : + if( (err = h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_PPS, nalu, nalu_length )) < 0 ) + return h264_parse_failed( info, err ); + break; + case H264_NALU_TYPE_SPS_EXT : + if( (err = h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPSEXT, nalu, nalu_length )) < 0 ) + return h264_parse_failed( info, err ); + break; + default : + break; + } + } + } + /* Move to the first byte of the next start code. */ + info->prev_nalu_type = nalu_type; + if( lsmash_bs_read_seek( bs, next_sc_head_pos, SEEK_SET ) != next_sc_head_pos ) + return h264_parse_failed( info, LSMASH_ERR_NAMELESS ); + /* Check if no more data to read from the stream. */ + if( !lsmash_bs_is_end( bs, NALU_SHORT_START_CODE_LENGTH ) ) + sc_head_pos = next_sc_head_pos; + else + return h264_parse_succeeded( info, param ); + } +} + +int h264_construct_specific_parameters +( + lsmash_codec_specific_t *dst, + lsmash_codec_specific_t *src +) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_h264_specific_parameters_t *param = (lsmash_h264_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + if( !param->parameter_sets ) + { + param->parameter_sets = lsmash_malloc_zero( sizeof(lsmash_h264_parameter_sets_t) ); + if( !param->parameter_sets ) + return LSMASH_ERR_MEMORY_ALLOC; + } + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + int err = lsmash_bs_import_data( bs, data, src->size - (data - src->data.unstructured) ); + if( err < 0 ) + goto fail; + if( lsmash_bs_get_byte( bs ) != 1 ) + { + /* We don't support configurationVersion other than 1. */ + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + param->AVCProfileIndication = lsmash_bs_get_byte( bs ); + param->profile_compatibility = lsmash_bs_get_byte( bs ); + param->AVCLevelIndication = lsmash_bs_get_byte( bs ); + param->lengthSizeMinusOne = lsmash_bs_get_byte( bs ) & 0x03; + uint8_t numOfSequenceParameterSets = lsmash_bs_get_byte( bs ) & 0x1F; + if( numOfSequenceParameterSets + && (err = nalu_get_dcr_ps( bs, param->parameter_sets->sps_list, numOfSequenceParameterSets )) < 0 ) + goto fail; + uint8_t numOfPictureParameterSets = lsmash_bs_get_byte( bs ); + if( numOfPictureParameterSets + && (err = nalu_get_dcr_ps( bs, param->parameter_sets->pps_list, numOfPictureParameterSets )) < 0 ) + goto fail; + if( H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) + { + param->chroma_format = lsmash_bs_get_byte( bs ) & 0x03; + param->bit_depth_luma_minus8 = lsmash_bs_get_byte( bs ) & 0x07; + param->bit_depth_chroma_minus8 = lsmash_bs_get_byte( bs ) & 0x07; + uint8_t numOfSequenceParameterSetExt = lsmash_bs_get_byte( bs ); + if( numOfSequenceParameterSetExt + && (err = nalu_get_dcr_ps( bs, param->parameter_sets->spsext_list, numOfSequenceParameterSetExt )) < 0 ) + goto fail; + } + lsmash_bs_cleanup( bs ); + return 0; +fail: + lsmash_bs_cleanup( bs ); + return err; +} + +int h264_print_codec_specific +( + FILE *fp, + lsmash_file_t *file, + isom_box_t *box, + int level +) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: AVC Configuration Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + uint8_t *data = box->binary; + uint32_t offset = isom_skip_box_common( &data ); + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + int err = lsmash_bs_import_data( bs, data, box->size - offset ); + if( err < 0 ) + { + lsmash_bs_cleanup( bs ); + return err; + } + lsmash_ifprintf( fp, indent, "configurationVersion = %"PRIu8"\n", lsmash_bs_get_byte( bs ) ); + uint8_t AVCProfileIndication = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "AVCProfileIndication = %"PRIu8"\n", AVCProfileIndication ); + lsmash_ifprintf( fp, indent, "profile_compatibility = 0x%02"PRIx8"\n", lsmash_bs_get_byte( bs ) ); + lsmash_ifprintf( fp, indent, "AVCLevelIndication = %"PRIu8"\n", lsmash_bs_get_byte( bs ) ); + uint8_t temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); + lsmash_ifprintf( fp, indent, "lengthSizeMinusOne = %"PRIu8"\n", temp8 & 0x03 ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 5) & 0x07 ); + uint8_t numOfSequenceParameterSets = temp8 & 0x1f; + lsmash_ifprintf( fp, indent, "numOfSequenceParameterSets = %"PRIu8"\n", numOfSequenceParameterSets ); + for( uint8_t i = 0; i < numOfSequenceParameterSets; i++ ) + { + uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); + lsmash_bs_skip_bytes( bs, nalUnitLength ); + } + uint8_t numOfPictureParameterSets = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "numOfPictureParameterSets = %"PRIu8"\n", numOfPictureParameterSets ); + for( uint8_t i = 0; i < numOfPictureParameterSets; i++ ) + { + uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); + lsmash_bs_skip_bytes( bs, nalUnitLength ); + } + /* Note: there are too many files, in the world, that don't contain the following fields. */ + if( H264_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) + && (lsmash_bs_get_pos( bs ) < (box->size - offset)) ) + { + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); + lsmash_ifprintf( fp, indent, "chroma_format = %"PRIu8"\n", temp8 & 0x03 ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); + lsmash_ifprintf( fp, indent, "bit_depth_luma_minus8 = %"PRIu8"\n", temp8 & 0x7 ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); + lsmash_ifprintf( fp, indent, "bit_depth_chroma_minus8 = %"PRIu8"\n", temp8 & 0x7 ); + lsmash_ifprintf( fp, indent, "numOfSequenceParameterSetExt = %"PRIu8"\n", lsmash_bs_get_byte( bs ) ); + } + lsmash_bs_cleanup( bs ); + return 0; +} + +int h264_copy_codec_specific +( + lsmash_codec_specific_t *dst, + lsmash_codec_specific_t *src +) +{ + assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); + assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); + lsmash_h264_specific_parameters_t *src_data = (lsmash_h264_specific_parameters_t *)src->data.structured; + lsmash_h264_specific_parameters_t *dst_data = (lsmash_h264_specific_parameters_t *)dst->data.structured; + lsmash_destroy_h264_parameter_sets( dst_data ); + *dst_data = *src_data; + if( !src_data->parameter_sets ) + return 0; + dst_data->parameter_sets = lsmash_malloc_zero( sizeof(lsmash_h264_parameter_sets_t) ); + if( !dst_data->parameter_sets ) + return LSMASH_ERR_MEMORY_ALLOC; + for( int i = 0; i < 3; i++ ) + { + lsmash_entry_list_t *src_ps_list = h264_get_parameter_set_list( src_data, i ); + lsmash_entry_list_t *dst_ps_list = h264_get_parameter_set_list( dst_data, i ); + assert( src_ps_list && dst_ps_list ); + for( lsmash_entry_t *entry = src_ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)entry->data; + if( !src_ps || src_ps->unused ) + continue; + isom_dcr_ps_entry_t *dst_ps = isom_create_ps_entry( src_ps->nalUnit, src_ps->nalUnitLength ); + if( !dst_ps ) + { + lsmash_destroy_h264_parameter_sets( dst_data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( lsmash_add_entry( dst_ps_list, dst_ps ) < 0 ) + { + lsmash_destroy_h264_parameter_sets( dst_data ); + isom_remove_dcr_ps( dst_ps ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + } + return 0; +} + +int h264_print_bitrate +( + FILE *fp, + lsmash_file_t *file, + isom_box_t *box, + int level +) +{ + assert( fp && file && box ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: MPEG-4 Bit Rate Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + isom_btrt_t *btrt = (isom_btrt_t *)box; + lsmash_ifprintf( fp, indent, "bufferSizeDB = %"PRIu32"\n", btrt->bufferSizeDB ); + lsmash_ifprintf( fp, indent, "maxBitrate = %"PRIu32"\n", btrt->maxBitrate ); + lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32"\n", btrt->avgBitrate ); + return 0; +} diff -Nru l-smash-1.9.1/codecs/h264.h l-smash-2.3.0/codecs/h264.h --- l-smash-1.9.1/codecs/h264.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/h264.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,369 @@ +/***************************************************************************** + * h264.h: + ***************************************************************************** + * Copyright (C) 2012-2014 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. */ + +enum +{ + H264_NALU_TYPE_UNSPECIFIED0 = 0, /* Unspecified */ + H264_NALU_TYPE_SLICE_N_IDR = 1, /* Coded slice of a non-IDR picture */ + H264_NALU_TYPE_SLICE_DP_A = 2, /* Coded slice data partition A */ + H264_NALU_TYPE_SLICE_DP_B = 3, /* Coded slice data partition B */ + H264_NALU_TYPE_SLICE_DP_C = 4, /* Coded slice data partition C */ + H264_NALU_TYPE_SLICE_IDR = 5, /* Coded slice of an IDR picture */ + H264_NALU_TYPE_SEI = 6, /* Supplemental Enhancement Information */ + H264_NALU_TYPE_SPS = 7, /* Sequence Parameter Set */ + H264_NALU_TYPE_PPS = 8, /* Picture Parameter Set */ + H264_NALU_TYPE_AUD = 9, /* Access Unit Delimiter */ + H264_NALU_TYPE_EOS = 10, /* End of Sequence */ + H264_NALU_TYPE_EOB = 11, /* End of Bitstream */ + H264_NALU_TYPE_FD = 12, /* Filler Data */ + H264_NALU_TYPE_SPS_EXT = 13, /* Sequence Parameter Set Extension */ + H264_NALU_TYPE_PREFIX = 14, /* Prefix NAL unit */ + H264_NALU_TYPE_SUBSET_SPS = 15, /* Subset Sequence Parameter Set */ + H264_NALU_TYPE_RSV_NVCL16 = 16, /* Reserved */ + H264_NALU_TYPE_RSV_NVCL17 = 17, /* Reserved */ + H264_NALU_TYPE_RSV_NVCL18 = 18, /* Reserved */ + H264_NALU_TYPE_SLICE_AUX = 19, /* Coded slice of an auxiliary coded picture without partitioning */ + H264_NALU_TYPE_SLICE_EXT = 20, /* Coded slice extension */ + H264_NALU_TYPE_SLICE_EXT_DVC = 21, /* Coded slice extension for depth view components */ + H264_NALU_TYPE_RSV22 = 22, /* Reserved */ + H264_NALU_TYPE_RSV23 = 23, /* Reserved */ + H264_NALU_TYPE_UNSPECIFIED24 = 24, /* Unspecified */ + H264_NALU_TYPE_UNSPECIFIED31 = 31, /* Unspecified */ +}; + +struct lsmash_h264_parameter_sets_tag +{ + /* Each list contains entries as isom_dcr_ps_entry_t. */ + lsmash_entry_list_t sps_list [1]; + lsmash_entry_list_t pps_list [1]; + lsmash_entry_list_t spsext_list[1]; +}; + +typedef struct +{ + unsigned forbidden_zero_bit : 1; + unsigned nal_ref_idc : 2; + unsigned nal_unit_type : 5; + uint8_t length; +} h264_nalu_header_t; + +typedef struct +{ + uint8_t present; + uint8_t CpbDpbDelaysPresentFlag; + uint8_t cpb_removal_delay_length; + uint8_t dpb_output_delay_length; +} h264_hrd_t; + +typedef struct +{ + uint16_t sar_width; + uint16_t sar_height; + uint8_t video_full_range_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t fixed_frame_rate_flag; + uint8_t pic_struct_present_flag; + h264_hrd_t hrd; +} h264_vui_t; + +typedef struct +{ + uint8_t present; + uint8_t profile_idc; + uint8_t constraint_set_flags; + uint8_t level_idc; + uint8_t seq_parameter_set_id; + uint8_t chroma_format_idc; + uint8_t separate_colour_plane_flag; + uint8_t ChromaArrayType; + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + uint8_t pic_order_cnt_type; + uint8_t delta_pic_order_always_zero_flag; + uint8_t num_ref_frames_in_pic_order_cnt_cycle; + uint8_t frame_mbs_only_flag; + int32_t offset_for_non_ref_pic; + int32_t offset_for_top_to_bottom_field; + int32_t offset_for_ref_frame[255]; + int64_t ExpectedDeltaPerPicOrderCntCycle; + uint32_t max_num_ref_frames; + uint32_t log2_max_frame_num; + uint32_t MaxFrameNum; + uint32_t log2_max_pic_order_cnt_lsb; + uint32_t MaxPicOrderCntLsb; + uint32_t PicSizeInMapUnits; + uint32_t cropped_width; + uint32_t cropped_height; + h264_vui_t vui; +} h264_sps_t; + +typedef struct +{ + uint8_t present; + uint8_t pic_parameter_set_id; + uint8_t seq_parameter_set_id; + uint8_t entropy_coding_mode_flag; + uint8_t bottom_field_pic_order_in_frame_present_flag; + uint8_t num_slice_groups_minus1; + uint8_t slice_group_map_type; + uint8_t num_ref_idx_l0_default_active_minus1; + uint8_t num_ref_idx_l1_default_active_minus1; + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_idc; + uint8_t deblocking_filter_control_present_flag; + uint8_t redundant_pic_cnt_present_flag; + uint32_t SliceGroupChangeRate; +} h264_pps_t; + +typedef struct +{ + uint8_t present; + uint8_t pic_struct; +} h264_pic_timing_t; + +typedef struct +{ + uint8_t present; + uint8_t random_accessible; + uint8_t broken_link_flag; + uint32_t recovery_frame_cnt; +} h264_recovery_point_t; + +typedef struct +{ + h264_pic_timing_t pic_timing; + h264_recovery_point_t recovery_point; +} h264_sei_t; + +typedef struct +{ + uint8_t present; + uint8_t slice_id; /* only for slice data partition */ + uint8_t type; + uint8_t pic_order_cnt_type; + uint8_t nal_ref_idc; + uint8_t IdrPicFlag; + uint8_t seq_parameter_set_id; + uint8_t pic_parameter_set_id; + uint8_t field_pic_flag; + uint8_t bottom_field_flag; + uint8_t has_mmco5; + uint8_t has_redundancy; + uint16_t idr_pic_id; + uint32_t frame_num; + int32_t pic_order_cnt_lsb; + int32_t delta_pic_order_cnt_bottom; + int32_t delta_pic_order_cnt[2]; +} h264_slice_info_t; + +typedef enum +{ + H264_PICTURE_TYPE_IDR = 0, + H264_PICTURE_TYPE_I = 1, + H264_PICTURE_TYPE_I_P = 2, + H264_PICTURE_TYPE_I_P_B = 3, + H264_PICTURE_TYPE_SI = 4, + H264_PICTURE_TYPE_SI_SP = 5, + H264_PICTURE_TYPE_I_SI = 6, + H264_PICTURE_TYPE_I_SI_P_SP = 7, + H264_PICTURE_TYPE_I_SI_P_SP_B = 8, + H264_PICTURE_TYPE_NONE = 9, +} h264_picture_type; + +typedef struct +{ + h264_picture_type type; + uint8_t idr; + uint8_t random_accessible; + uint8_t independent; + uint8_t disposable; /* 1: nal_ref_idc == 0, 0: otherwise */ + uint8_t has_redundancy; + uint8_t has_primary; + uint8_t pic_parameter_set_id; + uint8_t field_pic_flag; + uint8_t bottom_field_flag; + uint8_t delta; + uint8_t broken_link_flag; + /* POC */ + uint8_t has_mmco5; + uint8_t ref_pic_has_mmco5; + uint8_t ref_pic_bottom_field_flag; + int32_t ref_pic_TopFieldOrderCnt; + int32_t ref_pic_PicOrderCntMsb; + int32_t ref_pic_PicOrderCntLsb; + int32_t pic_order_cnt_lsb; + int32_t delta_pic_order_cnt_bottom; + int32_t delta_pic_order_cnt[2]; + int32_t PicOrderCnt; + uint32_t FrameNumOffset; + /* */ + uint32_t recovery_frame_cnt; + uint32_t frame_num; +} h264_picture_info_t; + +typedef struct +{ + uint8_t *data; + uint8_t *incomplete_data; + uint32_t length; + uint32_t incomplete_length; + uint32_t number; + h264_picture_info_t picture; +} h264_access_unit_t; + +typedef struct h264_info_tag h264_info_t; + +typedef struct +{ + lsmash_multiple_buffers_t *bank; + uint8_t *rbsp; +} h264_stream_buffer_t; + +struct h264_info_tag +{ + lsmash_h264_specific_parameters_t avcC_param; + lsmash_h264_specific_parameters_t avcC_param_next; + lsmash_entry_list_t sps_list [1]; /* contains entries as h264_sps_t */ + lsmash_entry_list_t pps_list [1]; /* contains entries as h264_pps_t */ + lsmash_entry_list_t slice_list[1]; /* for slice data partition */ + h264_sps_t sps; /* active SPS */ + h264_pps_t pps; /* active PPS */ + h264_sei_t sei; /* active SEI */ + h264_slice_info_t slice; /* active slice */ + h264_access_unit_t au; + uint8_t prev_nalu_type; + uint8_t avcC_pending; + lsmash_bits_t *bits; + h264_stream_buffer_t buffer; +}; + +int h264_setup_parser +( + h264_info_t *info, + int parse_only +); + +void h264_cleanup_parser +( + h264_info_t *info +); + +uint64_t h264_find_next_start_code +( + lsmash_bs_t *bs, + h264_nalu_header_t *nuh, + uint64_t *start_code_length, + uint64_t *trailing_zero_bytes +); + +int h264_calculate_poc +( + h264_info_t *info, + h264_picture_info_t *picture, + h264_picture_info_t *prev_picture +); + +void h264_update_picture_info_for_slice +( + h264_info_t *info, + h264_picture_info_t *picture, + h264_slice_info_t *slice +); + +void h264_update_picture_info +( + h264_info_t *info, + h264_picture_info_t *picture, + h264_slice_info_t *slice, + h264_sei_t *sei +); + +int h264_find_au_delimit_by_slice_info +( + h264_slice_info_t *slice, + h264_slice_info_t *prev_slice +); + +int h264_find_au_delimit_by_nalu_type +( + uint8_t nalu_type, + uint8_t prev_nalu_type +); + +int h264_supplement_buffer +( + h264_stream_buffer_t *buffer, + h264_access_unit_t *au, + uint32_t size +); + +int h264_parse_sps +( + h264_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int h264_parse_pps +( + h264_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int h264_parse_sei +( + lsmash_bits_t *bits, + h264_sps_t *sps, + h264_sei_t *sei, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int h264_parse_slice +( + h264_info_t *info, + h264_nalu_header_t *nuh, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int h264_try_to_append_parameter_set +( + h264_info_t *info, + lsmash_h264_parameter_set_type ps_type, + void *ps_data, + uint32_t ps_length +); + +int h264_move_pending_avcC_param +( + h264_info_t *info +); diff -Nru l-smash-1.9.1/codecs/hevc.c l-smash-2.3.0/codecs/hevc.c --- l-smash-1.9.1/codecs/hevc.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/hevc.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,3055 @@ +/***************************************************************************** + * hevc.c: + ***************************************************************************** + * Copyright (C) 2013-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +/*************************************************************************** + ITU-T Recommendation H.265 (04/13) + ISO/IEC 14496-15:2014 +***************************************************************************/ +#include "hevc.h" +#include "nalu.h" + +#define IF_EXCEED_INT32( x ) if( (x) < INT32_MIN || (x) > INT32_MAX ) +#define HEVC_POC_DEBUG_PRINT 0 + +#define HEVC_MIN_NALU_HEADER_LENGTH 2 +#define HEVC_MAX_VPS_ID 15 +#define HEVC_MAX_SPS_ID 15 +#define HEVC_MAX_PPS_ID 63 +#define HEVC_MAX_DPB_SIZE 16 +#define HVCC_CONFIGURATION_VERSION 1 + +typedef enum +{ + HEVC_SLICE_TYPE_B = 0, + HEVC_SLICE_TYPE_P = 1, + HEVC_SLICE_TYPE_I = 2, +} hevc_slice_type; + +void lsmash_destroy_hevc_parameter_arrays +( + lsmash_hevc_specific_parameters_t *param +) +{ + if( !param || !param->parameter_arrays ) + return; + for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) + lsmash_remove_entries( param->parameter_arrays->ps_array[i].list, isom_remove_dcr_ps ); + lsmash_free( param->parameter_arrays ); + param->parameter_arrays = NULL; +} + +void hevc_destruct_specific_data +( + void *data +) +{ + if( !data ) + return; + lsmash_destroy_hevc_parameter_arrays( data ); + lsmash_free( data ); +} + +static void hevc_remove_pps +( + hevc_pps_t *pps +) +{ + if( !pps ) + return; + lsmash_free( pps->colWidth ); + lsmash_free( pps->rowHeight ); + lsmash_free( pps ); +} + +void hevc_cleanup_parser +( + hevc_info_t *info +) +{ + if( !info ) + return; + lsmash_remove_entries( info->vps_list, NULL ); + lsmash_remove_entries( info->sps_list, NULL ); + lsmash_remove_entries( info->pps_list, hevc_remove_pps ); + lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param ); + lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param_next ); + lsmash_destroy_multiple_buffers( info->buffer.bank ); + lsmash_bits_adhoc_cleanup( info->bits ); + info->bits = NULL; +} + +int hevc_setup_parser +( + hevc_info_t *info, + int parse_only +) +{ + assert( info ); + memset( info, 0, sizeof(hevc_info_t) ); + info->hvcC_param .lengthSizeMinusOne = NALU_DEFAULT_NALU_LENGTH_SIZE - 1; + info->hvcC_param_next.lengthSizeMinusOne = NALU_DEFAULT_NALU_LENGTH_SIZE - 1; + hevc_stream_buffer_t *sb = &info->buffer; + sb->bank = lsmash_create_multiple_buffers( parse_only ? 1 : 3, NALU_DEFAULT_BUFFER_SIZE ); + if( !sb->bank ) + return LSMASH_ERR_MEMORY_ALLOC; + sb->rbsp = lsmash_withdraw_buffer( sb->bank, 1 ); + if( !parse_only ) + { + info->au.data = lsmash_withdraw_buffer( sb->bank, 2 ); + info->au.incomplete_data = lsmash_withdraw_buffer( sb->bank, 3 ); + } + info->bits = lsmash_bits_adhoc_create(); + if( !info->bits ) + { + lsmash_destroy_multiple_buffers( sb->bank ); + return LSMASH_ERR_MEMORY_ALLOC; + } + lsmash_init_entry_list( info->vps_list ); + lsmash_init_entry_list( info->sps_list ); + lsmash_init_entry_list( info->pps_list ); + info->prev_nalu_type = HEVC_NALU_TYPE_UNKNOWN; + return 0; +} + +static int hevc_check_nalu_header +( + lsmash_bs_t *bs, + hevc_nalu_header_t *nuh, + int use_long_start_code +) +{ + /* Check if the enough length of NALU header on the buffer. */ + int start_code_length = use_long_start_code ? NALU_LONG_START_CODE_LENGTH : NALU_SHORT_START_CODE_LENGTH; + if( lsmash_bs_is_end( bs, start_code_length + 1 ) ) + return LSMASH_ERR_NAMELESS; + /* Read NALU header. */ + uint16_t temp16 = lsmash_bs_show_be16( bs, start_code_length ); + nuh->forbidden_zero_bit = (temp16 >> 15) & 0x01; + nuh->nal_unit_type = (temp16 >> 9) & 0x3f; + nuh->nuh_layer_id = (temp16 >> 3) & 0x3f; + uint8_t nuh_temporal_id_plus1 = temp16 & 0x07; + if( nuh->forbidden_zero_bit || nuh_temporal_id_plus1 == 0 ) + return LSMASH_ERR_INVALID_DATA; + nuh->TemporalId = nuh_temporal_id_plus1 - 1; + nuh->length = HEVC_MIN_NALU_HEADER_LENGTH; + /* nuh_layer_id shall be 0 in the specification we refer to. */ + if( nuh->nuh_layer_id ) + return LSMASH_ERR_NAMELESS; + if( nuh->TemporalId == 0 ) + { + /* For TSA_N, TSA_R, STSA_N and STSA_R, TemporalId shall not be equal to 0. */ + if( nuh->nal_unit_type >= HEVC_NALU_TYPE_TSA_N + && nuh->nal_unit_type <= HEVC_NALU_TYPE_STSA_R ) + return LSMASH_ERR_INVALID_DATA; + } + else + { + /* For BLA_W_LP to RSV_IRAP_VCL23, TemporalId shall be equal to 0. */ + if( nuh->nal_unit_type >= HEVC_NALU_TYPE_BLA_W_LP + && nuh->nal_unit_type <= HEVC_NALU_TYPE_RSV_IRAP_VCL23 ) + return LSMASH_ERR_INVALID_DATA; + /* For VPS, SPS, EOS and EOB, TemporalId shall be equal to 0. */ + if( nuh->nal_unit_type >= HEVC_NALU_TYPE_VPS + && nuh->nal_unit_type <= HEVC_NALU_TYPE_EOB + && nuh->nal_unit_type != HEVC_NALU_TYPE_PPS + && nuh->nal_unit_type != HEVC_NALU_TYPE_AUD ) + return LSMASH_ERR_INVALID_DATA; + } + /* VPS, SPS and PPS require long start code (0x00000001). + * Also AU delimiter requires it too because this type of NALU shall be the first NALU of any AU if present. */ + if( !use_long_start_code + && nuh->nal_unit_type >= HEVC_NALU_TYPE_VPS + && nuh->nal_unit_type <= HEVC_NALU_TYPE_AUD ) + return LSMASH_ERR_INVALID_DATA; + return 0; +} + +uint64_t hevc_find_next_start_code +( + lsmash_bs_t *bs, + hevc_nalu_header_t *nuh, + uint64_t *start_code_length, + uint64_t *trailing_zero_bytes +) +{ + uint64_t length = 0; /* the length of the latest NALU */ + uint64_t count = 0; /* the number of the trailing zero bytes after the latest NALU */ + /* Check the type of the current start code. */ + int long_start_code + = (!lsmash_bs_is_end( bs, NALU_LONG_START_CODE_LENGTH ) && 0x00000001 == lsmash_bs_show_be32( bs, 0 )) ? 1 + : (!lsmash_bs_is_end( bs, NALU_SHORT_START_CODE_LENGTH ) && 0x000001 == lsmash_bs_show_be24( bs, 0 )) ? 0 + : -1; + if( long_start_code >= 0 && hevc_check_nalu_header( bs, nuh, long_start_code ) == 0 ) + { + *start_code_length = long_start_code ? NALU_LONG_START_CODE_LENGTH : NALU_SHORT_START_CODE_LENGTH; + uint64_t distance = *start_code_length + nuh->length; + /* Find the start code of the next NALU and get the distance from the start code of the latest NALU. */ + if( !lsmash_bs_is_end( bs, distance + NALU_SHORT_START_CODE_LENGTH ) ) + { + uint32_t sync_bytes = lsmash_bs_show_be24( bs, distance ); + while( 0x000001 != sync_bytes ) + { + if( lsmash_bs_is_end( bs, ++distance + NALU_SHORT_START_CODE_LENGTH ) ) + { + distance = lsmash_bs_get_remaining_buffer_size( bs ); + break; + } + sync_bytes <<= 8; + sync_bytes |= lsmash_bs_show_byte( bs, distance + NALU_SHORT_START_CODE_LENGTH - 1 ); + sync_bytes &= 0xFFFFFF; + } + } + else + distance = lsmash_bs_get_remaining_buffer_size( bs ); + /* Any NALU has no consecutive zero bytes at the end. */ + while( 0x00 == lsmash_bs_show_byte( bs, distance - 1 ) ) + { + --distance; + ++count; + } + /* Remove the length of the start code. */ + length = distance - *start_code_length; + /* If there are one or more trailing zero bytes, we treat the last one byte as a part of the next start code. + * This makes the next start code a long start code. */ + if( count ) + --count; + } + else + { + /* No start code. */ + nuh->forbidden_zero_bit = 1; /* shall be 0, so invalid */ + nuh->nal_unit_type = HEVC_NALU_TYPE_UNKNOWN; + nuh->nuh_layer_id = 0; /* arbitrary */ + nuh->TemporalId = 0; /* arbitrary */ + nuh->length = 0; + *start_code_length = 0; + } + *trailing_zero_bytes = count; + return length; +} + +static hevc_vps_t *hevc_get_vps +( + lsmash_entry_list_t *vps_list, + uint8_t vps_id +) +{ + if( !vps_list || vps_id > HEVC_MAX_VPS_ID ) + return NULL; + for( lsmash_entry_t *entry = vps_list->head; entry; entry = entry->next ) + { + hevc_vps_t *vps = (hevc_vps_t *)entry->data; + if( !vps ) + return NULL; + if( vps->video_parameter_set_id == vps_id ) + return vps; + } + hevc_vps_t *vps = lsmash_malloc_zero( sizeof(hevc_vps_t) ); + if( !vps ) + return NULL; + vps->video_parameter_set_id = vps_id; + if( lsmash_add_entry( vps_list, vps ) < 0 ) + { + lsmash_free( vps ); + return NULL; + } + return vps; +} + +static hevc_sps_t *hevc_get_sps +( + lsmash_entry_list_t *sps_list, + uint8_t sps_id +) +{ + if( !sps_list || sps_id > HEVC_MAX_SPS_ID ) + return NULL; + for( lsmash_entry_t *entry = sps_list->head; entry; entry = entry->next ) + { + hevc_sps_t *sps = (hevc_sps_t *)entry->data; + if( !sps ) + return NULL; + if( sps->seq_parameter_set_id == sps_id ) + return sps; + } + hevc_sps_t *sps = lsmash_malloc_zero( sizeof(hevc_sps_t) ); + if( !sps ) + return NULL; + sps->seq_parameter_set_id = sps_id; + if( lsmash_add_entry( sps_list, sps ) < 0 ) + { + lsmash_free( sps ); + return NULL; + } + return sps; +} + +static hevc_pps_t *hevc_get_pps +( + lsmash_entry_list_t *pps_list, + uint8_t pps_id +) +{ + if( !pps_list || pps_id > HEVC_MAX_PPS_ID ) + return NULL; + for( lsmash_entry_t *entry = pps_list->head; entry; entry = entry->next ) + { + hevc_pps_t *pps = (hevc_pps_t *)entry->data; + if( !pps ) + return NULL; + if( pps->pic_parameter_set_id == pps_id ) + return pps; + } + hevc_pps_t *pps = lsmash_malloc_zero( sizeof(hevc_pps_t) ); + if( !pps ) + return NULL; + pps->pic_parameter_set_id = pps_id; + if( lsmash_add_entry( pps_list, pps ) < 0 ) + { + lsmash_free( pps ); + return NULL; + } + return pps; +} + +int hevc_calculate_poc +( + hevc_info_t *info, + hevc_picture_info_t *picture, + hevc_picture_info_t *prev_picture +) +{ +#if HEVC_POC_DEBUG_PRINT + fprintf( stderr, "PictureOrderCount\n" ); +#endif + hevc_pps_t *pps = hevc_get_pps( info->pps_list, picture->pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + hevc_sps_t *sps = hevc_get_sps( info->sps_list, pps->seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + /* 8.3.1 Decoding process for picture order count + * This process needs to be invoked only for the first slice segment of a picture. */ + int NoRaslOutputFlag; + if( picture->irap ) + { + /* 8.1 General decoding process + * If the current picture is an IDR picture, a BLA picture, the first picture in the + * bitstream in decoding order, or the first picture that follows an end of sequence + * NAL unit in decoding order, the variable NoRaslOutputFlag is set equal to 1. + * + * Note that not only the end of sequence NAL unit but the end of bistream NAL unit as + * well specify that the current access unit is the last access unit in the coded video + * sequence in decoding order. */ + NoRaslOutputFlag = picture->idr || picture->broken_link || info->eos; + if( info->eos ) + info->eos = 0; + } + else + NoRaslOutputFlag = 0; + int64_t poc_msb; + int32_t poc_lsb = picture->poc_lsb; + if( picture->irap && NoRaslOutputFlag ) + poc_msb = 0; + else + { + int32_t prev_poc_msb = picture->idr ? 0 : prev_picture->tid0_poc_msb; + int32_t prev_poc_lsb = picture->idr ? 0 : prev_picture->tid0_poc_lsb; + int32_t max_poc_lsb = 1 << sps->log2_max_pic_order_cnt_lsb; + if( (poc_lsb < prev_poc_lsb) + && ((prev_poc_lsb - poc_lsb) >= (max_poc_lsb / 2)) ) + poc_msb = prev_poc_msb + max_poc_lsb; + else if( (poc_lsb > prev_poc_lsb) + && ((poc_lsb - prev_poc_lsb) > (max_poc_lsb / 2)) ) + poc_msb = prev_poc_msb - max_poc_lsb; + else + poc_msb = prev_poc_msb; + } + picture->poc = poc_msb + poc_lsb; + if( picture->TemporalId == 0 && (!picture->radl || !picture->rasl || !picture->sublayer_nonref) ) + { + picture->tid0_poc_msb = poc_msb; + picture->tid0_poc_lsb = poc_lsb; + } +#if HEVC_POC_DEBUG_PRINT + fprintf( stderr, " prevPicOrderCntMsb: %"PRId32"\n", prev_poc_msb ); + fprintf( stderr, " prevPicOrderCntLsb: %"PRId32"\n", prev_poc_lsb ); + fprintf( stderr, " PicOrderCntMsb: %"PRId64"\n", poc_msb ); + fprintf( stderr, " pic_order_cnt_lsb: %"PRId32"\n", poc_lsb ); + fprintf( stderr, " MaxPicOrderCntLsb: %"PRIu64"\n", max_poc_lsb ); + fprintf( stderr, " POC: %"PRId32"\n", picture->poc ); +#endif + return 0; +} + +static inline int hevc_activate_vps +( + hevc_info_t *info, + uint8_t video_parameter_set_id +) +{ + hevc_vps_t *vps = hevc_get_vps( info->vps_list, video_parameter_set_id ); + if( !vps ) + return LSMASH_ERR_NAMELESS; + info->vps = *vps; + return 0; +} + +static inline int hevc_activate_sps +( + hevc_info_t *info, + uint8_t seq_parameter_set_id +) +{ + hevc_sps_t *sps = hevc_get_sps( info->sps_list, seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + info->sps = *sps; + return 0; +} + +static void hevc_parse_scaling_list_data +( + lsmash_bits_t *bits +) +{ + for( int sizeId = 0; sizeId < 4; sizeId++ ) + for( int matrixId = 0; matrixId < (sizeId == 3 ? 2 : 6); matrixId++ ) + { + if( !lsmash_bits_get( bits, 1 ) ) /* scaling_list_pred_mode_flag[sizeId][matrixId] */ + nalu_get_exp_golomb_ue( bits ); /* scaling_list_pred_matrix_id_delta[sizeId][matrixId] */ + else + { + int coefNum = LSMASH_MIN( 64, 1 << (4 + (sizeId << 1)) ); + if( sizeId > 1 ) + nalu_get_exp_golomb_se( bits ); /* scaling_list_dc_coef_minus8[sizeId - 2][matrixId] */ + for( int i = 0; i < coefNum; i++ ) + nalu_get_exp_golomb_se( bits ); /* scaling_list_delta_coef */ + } + } +} + +static int hevc_short_term_ref_pic_set +( + lsmash_bits_t *bits, + hevc_sps_t *sps, + int stRpsIdx +) +{ + int inter_ref_pic_set_prediction_flag = stRpsIdx != 0 ? lsmash_bits_get( bits, 1 ) : 0; + if( inter_ref_pic_set_prediction_flag ) + { + /* delta_idx_minus1 is always 0 in SPS since stRpsIdx must not be equal to num_short_term_ref_pic_sets. */ + uint64_t delta_idx_minus1 = stRpsIdx == sps->num_short_term_ref_pic_sets ? nalu_get_exp_golomb_ue( bits ) : 0; + int delta_rps_sign = lsmash_bits_get( bits, 1 ); + uint64_t abs_delta_rps_minus1 = nalu_get_exp_golomb_ue( bits ); + int RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1); + int deltaRps = (delta_rps_sign ? -1 : 1) * (abs_delta_rps_minus1 + 1); + hevc_st_rps_t *st_rps = &sps->st_rps[stRpsIdx]; + hevc_st_rps_t *ref_rps = &sps->st_rps[RefRpsIdx]; + uint8_t used_by_curr_pic_flag[32]; + uint8_t use_delta_flag [32]; + for( int j = 0; j <= ref_rps->NumDeltaPocs; j++ ) + { + used_by_curr_pic_flag[j] = lsmash_bits_get( bits, 1 ); + use_delta_flag [j] = !used_by_curr_pic_flag[j] ? lsmash_bits_get( bits, 1 ) : 1; + } + /* NumNegativePics */ + int i = 0; + for( int j = ref_rps->NumPositivePics - 1; j >= 0; j-- ) + { + int dPoc = ref_rps->DeltaPocS1[j] + deltaRps; + if( dPoc < 0 && use_delta_flag[ ref_rps->NumNegativePics + j ] ) + { + st_rps->DeltaPocS0 [i ] = dPoc; + st_rps->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[ ref_rps->NumNegativePics + j ]; + } + } + if( deltaRps < 0 && use_delta_flag[ ref_rps->NumDeltaPocs ] ) + { + st_rps->DeltaPocS0 [i ] = deltaRps; + st_rps->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[ ref_rps->NumDeltaPocs ]; + } + for( int j = 0; j < ref_rps->NumNegativePics; j++ ) + { + int dPoc = ref_rps->DeltaPocS0[j] + deltaRps; + if( dPoc < 0 && use_delta_flag[j] ) + { + st_rps->DeltaPocS0 [i ] = dPoc; + st_rps->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[j]; + } + } + st_rps->NumNegativePics = i; + /* NumPositivePics */ + i = 0; + for( int j = ref_rps->NumNegativePics - 1; j >= 0; j-- ) + { + int dPoc = ref_rps->DeltaPocS0[j] + deltaRps; + if( dPoc > 0 && use_delta_flag[j] ) + { + st_rps->DeltaPocS1 [i ] = dPoc; + st_rps->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[j]; + } + } + if( deltaRps > 0 && use_delta_flag[ ref_rps->NumDeltaPocs ] ) + { + st_rps->DeltaPocS1 [i ] = deltaRps; + st_rps->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[ ref_rps->NumDeltaPocs ]; + } + for( int j = 0; j < ref_rps->NumPositivePics; j++ ) + { + int dPoc = ref_rps->DeltaPocS1[j] + deltaRps; + if( dPoc > 0 && use_delta_flag[ ref_rps->NumNegativePics + j ] ) + { + st_rps->DeltaPocS1 [i ] = dPoc; + st_rps->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[ ref_rps->NumNegativePics + j ]; + } + } + st_rps->NumPositivePics = i; + /* NumDeltaPocs */ + st_rps->NumDeltaPocs = st_rps->NumNegativePics + st_rps->NumPositivePics; + } + else + { + uint64_t num_negative_pics = nalu_get_exp_golomb_ue( bits ); + uint64_t num_positive_pics = nalu_get_exp_golomb_ue( bits ); + if( num_negative_pics >= HEVC_MAX_DPB_SIZE || num_positive_pics >= HEVC_MAX_DPB_SIZE ) + return LSMASH_ERR_INVALID_DATA; + hevc_st_rps_t *st_rps = &sps->st_rps[stRpsIdx]; + st_rps->NumNegativePics = num_negative_pics; + st_rps->NumPositivePics = num_positive_pics; + st_rps->NumDeltaPocs = st_rps->NumNegativePics + st_rps->NumPositivePics; + for( int i = 0; i < num_negative_pics; i++ ) + { + uint64_t delta_poc_s0_minus1 = nalu_get_exp_golomb_ue( bits ); + if( i == 0 ) + st_rps->DeltaPocS0[i] = -(delta_poc_s0_minus1 + 1); + else + st_rps->DeltaPocS0[i] = st_rps->DeltaPocS0[i - 1] - (delta_poc_s0_minus1 + 1); + st_rps->UsedByCurrPicS0[i] = lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_s0_flag */ + } + for( int i = 0; i < num_positive_pics; i++ ) + { + uint64_t delta_poc_s1_minus1 = nalu_get_exp_golomb_ue( bits ); + if( i == 0 ) + st_rps->DeltaPocS1[i] = +(delta_poc_s1_minus1 + 1); + else + st_rps->DeltaPocS1[i] = st_rps->DeltaPocS1[i - 1] + (delta_poc_s1_minus1 + 1); + st_rps->UsedByCurrPicS0[i] = lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_s1_flag */ + } + } + return 0; +} + +static inline void hevc_parse_sub_layer_hrd_parameters +( + lsmash_bits_t *bits, + int CpbCnt, + int sub_pic_hrd_params_present_flag +) +{ + for( int i = 0; i <= CpbCnt; i++ ) + { + nalu_get_exp_golomb_ue( bits ); /* bit_rate_value_minus1[i] */ + nalu_get_exp_golomb_ue( bits ); /* cpb_size_value_minus1[i] */ + if( sub_pic_hrd_params_present_flag ) + { + nalu_get_exp_golomb_ue( bits ); /* cpb_size_du_value_minus1[i] */ + nalu_get_exp_golomb_ue( bits ); /* bit_rate_du_value_minus1[i] */ + } + lsmash_bits_get( bits, 1 ); /* cbr_flag[i] */ + } +} + +static void hevc_parse_hrd_parameters +( + lsmash_bits_t *bits, + hevc_hrd_t *hrd, + int commonInfPresentFlag, + int maxNumSubLayersMinus1 +) +{ + /* The specification we refer to doesn't define the implicit value of some fields. + * According to JCTVC-HM reference software, + * the implicit value of nal_hrd_parameters_present_flag is to be equal to 0, + * the implicit value of vcl_hrd_parameters_present_flag is to be equal to 0. */ + int nal_hrd_parameters_present_flag = 0; + int vcl_hrd_parameters_present_flag = 0; + memset( hrd, 0, sizeof(hevc_hrd_t) ); + if( commonInfPresentFlag ) + { + nal_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); + vcl_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); + if( nal_hrd_parameters_present_flag + || vcl_hrd_parameters_present_flag ) + { + hrd->CpbDpbDelaysPresentFlag = 1; + hrd->sub_pic_hrd_params_present_flag = lsmash_bits_get( bits, 1 ); + if( hrd->sub_pic_hrd_params_present_flag ) + { + lsmash_bits_get( bits, 8 ); /* tick_divisor_minus2 */ + hrd->du_cpb_removal_delay_increment_length = lsmash_bits_get( bits, 5 ) + 1; + hrd->sub_pic_cpb_params_in_pic_timing_sei_flag = lsmash_bits_get( bits, 1 ); + hrd->dpb_output_delay_du_length = lsmash_bits_get( bits, 5 ) + 1; + } + lsmash_bits_get( bits, 4 ); /* bit_rate_scale */ + lsmash_bits_get( bits, 4 ); /* cpb_size_scale */ + if( hrd->sub_pic_hrd_params_present_flag ) + lsmash_bits_get( bits, 4 ); /* cpb_size_du_scale */ + lsmash_bits_get( bits, 5 ); /* initial_cpb_removal_delay_length_minus1 */ + hrd->au_cpb_removal_delay_length = lsmash_bits_get( bits, 5 ) + 1; + hrd->dpb_output_delay_length = lsmash_bits_get( bits, 5 ) + 1; + } + } + for( int i = 0; i <= maxNumSubLayersMinus1; i++ ) + { + hrd->fixed_pic_rate_general_flag[i] = lsmash_bits_get( bits, 1 ); + uint8_t fixed_pic_rate_within_cvs_flag = !hrd->fixed_pic_rate_general_flag[i] ? lsmash_bits_get( bits, 1 ) : 1; + uint8_t low_delay_hrd_flag = !fixed_pic_rate_within_cvs_flag ? lsmash_bits_get( bits, 1 ) : 0; + hrd->elemental_duration_in_tc[i] = fixed_pic_rate_within_cvs_flag ? nalu_get_exp_golomb_ue( bits ) + 1 : 0; + uint8_t cpb_cnt_minus1 = !low_delay_hrd_flag ? nalu_get_exp_golomb_ue( bits ) : 0; + if( nal_hrd_parameters_present_flag ) + hevc_parse_sub_layer_hrd_parameters( bits, cpb_cnt_minus1, hrd->sub_pic_hrd_params_present_flag ); + if( vcl_hrd_parameters_present_flag ) + hevc_parse_sub_layer_hrd_parameters( bits, cpb_cnt_minus1, hrd->sub_pic_hrd_params_present_flag ); + } +} + +static inline void hevc_parse_profile_tier_level_common +( + lsmash_bits_t *bits, + hevc_ptl_common_t *ptlc, + int profile_present, + int level_present +) +{ + if( profile_present ) + { + ptlc->profile_space = lsmash_bits_get( bits, 2 ); + ptlc->tier_flag = lsmash_bits_get( bits, 1 ); + ptlc->profile_idc = lsmash_bits_get( bits, 5 ); + ptlc->profile_compatibility_flags = lsmash_bits_get( bits, 32 ); + ptlc->progressive_source_flag = lsmash_bits_get( bits, 1 ); + ptlc->interlaced_source_flag = lsmash_bits_get( bits, 1 ); + ptlc->non_packed_constraint_flag = lsmash_bits_get( bits, 1 ); + ptlc->frame_only_constraint_flag = lsmash_bits_get( bits, 1 ); + ptlc->reserved_zero_44bits = lsmash_bits_get( bits, 44 ); + } + if( level_present ) + ptlc->level_idc = lsmash_bits_get( bits, 8 ); +} + +static void hevc_parse_profile_tier_level +( + lsmash_bits_t *bits, + hevc_ptl_t *ptl, + int maxNumSubLayersMinus1 +) +{ + hevc_parse_profile_tier_level_common( bits, &ptl->general, 1, 1 ); + if( maxNumSubLayersMinus1 == 0 ) + return; + assert( maxNumSubLayersMinus1 <= 6 ); + int sub_layer_profile_present_flag[6] = { 0 }; + int sub_layer_level_present_flag [6] = { 0 }; + for( int i = 0; i < maxNumSubLayersMinus1; i++ ) + { + sub_layer_profile_present_flag[i] = lsmash_bits_get( bits, 1 ); + sub_layer_level_present_flag [i] = lsmash_bits_get( bits, 1 ); + } + for( int i = maxNumSubLayersMinus1; i < 8; i++ ) + lsmash_bits_get( bits, 2 ); /* reserved_zero_2bits[i] */ + for( int i = 0; i < maxNumSubLayersMinus1; i++ ) + hevc_parse_profile_tier_level_common( bits, &ptl->sub_layer[i], sub_layer_profile_present_flag[i], sub_layer_level_present_flag[i] ); +} + +static int hevc_parse_vps_minimally +( + lsmash_bits_t *bits, + hevc_vps_t *vps, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + memset( vps, 0, sizeof(hevc_vps_t) ); + vps->video_parameter_set_id = lsmash_bits_get( bits, 4 ); + /* vps_reserved_three_2bits shall be 3 in the specification we refer to. */ + if( lsmash_bits_get( bits, 2 ) != 3 ) + return LSMASH_ERR_NAMELESS; + /* vps_max_layers_minus1 shall be 0 in the specification we refer to. */ + if( lsmash_bits_get( bits, 6 ) != 0 ) + return LSMASH_ERR_NAMELESS; + vps->max_sub_layers_minus1 = lsmash_bits_get( bits, 3 ); + vps->temporal_id_nesting_flag = lsmash_bits_get( bits, 1 ); + /* When vps_max_sub_layers_minus1 is equal to 0, vps_temporal_id_nesting_flag shall be equal to 1. */ + if( (vps->max_sub_layers_minus1 | vps->temporal_id_nesting_flag) == 0 ) + return LSMASH_ERR_INVALID_DATA; + /* vps_reserved_0xffff_16bits shall be 0xFFFF in the specification we refer to. */ + if( lsmash_bits_get( bits, 16 ) != 0xFFFF ) + return LSMASH_ERR_NAMELESS; + hevc_parse_profile_tier_level( bits, &vps->ptl, vps->max_sub_layers_minus1 ); + vps->frame_field_info_present_flag = vps->ptl.general.progressive_source_flag + && vps->ptl.general.interlaced_source_flag; + int sub_layer_ordering_info_present_flag = lsmash_bits_get( bits, 1 ); + for( int i = sub_layer_ordering_info_present_flag ? 0 : vps->max_sub_layers_minus1; i <= vps->max_sub_layers_minus1; i++ ) + { + nalu_get_exp_golomb_ue( bits ); /* max_dec_pic_buffering_minus1[i] */ + nalu_get_exp_golomb_ue( bits ); /* max_num_reorder_pics [i] */ + nalu_get_exp_golomb_ue( bits ); /* max_latency_increase_plus1 [i] */ + } + uint8_t max_layer_id = lsmash_bits_get( bits, 6 ); + uint16_t num_layer_sets_minus1 = nalu_get_exp_golomb_ue( bits ); + for( int i = 1; i <= num_layer_sets_minus1; i++ ) + for( int j = 0; j <= max_layer_id; j++ ) + lsmash_bits_get( bits, 1 ); /* layer_id_included_flag[i][j] */ + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int hevc_parse_vps +( + hevc_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + lsmash_bits_t *bits = info->bits; + hevc_vps_t *vps; + { + /* Parse VPS minimally for configuration records. */ + hevc_vps_t min_vps; + int err = hevc_parse_vps_minimally( bits, &min_vps, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + vps = hevc_get_vps( info->vps_list, min_vps.video_parameter_set_id ); + if( !vps ) + return LSMASH_ERR_NAMELESS; + *vps = min_vps; + } + vps->timing_info_present_flag = lsmash_bits_get( bits, 1 ); + if( vps->timing_info_present_flag ) + { + lsmash_bits_get( bits, 32 ); /* num_units_in_tick */ + lsmash_bits_get( bits, 32 ); /* time_scale */ + if( lsmash_bits_get( bits, 1 ) ) /* poc_proportional_to_timing_flag */ + nalu_get_exp_golomb_ue( bits ); /* num_ticks_poc_diff_one_minus1 */ + vps->num_hrd_parameters = nalu_get_exp_golomb_ue( bits ); + for( int i = 0; i < vps->num_hrd_parameters; i++ ) + { + nalu_get_exp_golomb_ue( bits ); /* hrd_layer_set_idx[i] */ + int cprms_present_flag = i > 0 ? lsmash_bits_get( bits, 1 ) : 1; + /* Although the value of vps_num_hrd_parameters is required to be less than or equal to 1 in the spec + * we refer to, decoders shall allow other values of vps_num_hrd_parameters in the range of 0 to 1024, + * inclusive, to appear in the syntax. */ + if( i <= 1 ) + hevc_parse_hrd_parameters( bits, &vps->hrd[i], cprms_present_flag, vps->max_sub_layers_minus1 ); + else + { + hevc_hrd_t dummy_hrd; + hevc_parse_hrd_parameters( bits, &dummy_hrd, cprms_present_flag, vps->max_sub_layers_minus1 ); + } + } + } + /* Skip VPS extension. */ + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + vps->present = 1; + info->vps = *vps; + return 0; +} + +static int hevc_parse_sps_minimally +( + lsmash_bits_t *bits, + hevc_sps_t *sps, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + memset( sps, 0, sizeof(hevc_sps_t) ); + sps->video_parameter_set_id = lsmash_bits_get( bits, 4 ); + sps->max_sub_layers_minus1 = lsmash_bits_get( bits, 3 ); + sps->temporal_id_nesting_flag = lsmash_bits_get( bits, 1 ); + hevc_parse_profile_tier_level( bits, &sps->ptl, sps->max_sub_layers_minus1 ); + sps->seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + sps->chroma_format_idc = nalu_get_exp_golomb_ue( bits ); + if( sps->chroma_format_idc == 3 ) + sps->separate_colour_plane_flag = lsmash_bits_get( bits, 1 ); + static const int SubWidthC [] = { 1, 2, 2, 1 }; + static const int SubHeightC[] = { 1, 2, 1, 1 }; + uint64_t pic_width_in_luma_samples = nalu_get_exp_golomb_ue( bits ); + uint64_t pic_height_in_luma_samples = nalu_get_exp_golomb_ue( bits ); + sps->cropped_width = pic_width_in_luma_samples; + sps->cropped_height = pic_height_in_luma_samples; + if( lsmash_bits_get( bits, 1 ) ) /* conformance_window_flag */ + { + uint64_t conf_win_left_offset = nalu_get_exp_golomb_ue( bits ); + uint64_t conf_win_right_offset = nalu_get_exp_golomb_ue( bits ); + uint64_t conf_win_top_offset = nalu_get_exp_golomb_ue( bits ); + uint64_t conf_win_bottom_offset = nalu_get_exp_golomb_ue( bits ); + sps->cropped_width -= (conf_win_left_offset + conf_win_right_offset) * SubWidthC [ sps->chroma_format_idc ]; + sps->cropped_height -= (conf_win_top_offset + conf_win_bottom_offset) * SubHeightC[ sps->chroma_format_idc ]; + } + sps->bit_depth_luma_minus8 = nalu_get_exp_golomb_ue( bits ); + sps->bit_depth_chroma_minus8 = nalu_get_exp_golomb_ue( bits ); + sps->log2_max_pic_order_cnt_lsb = nalu_get_exp_golomb_ue( bits ) + 4; + int sub_layer_ordering_info_present_flag = lsmash_bits_get( bits, 1 ); + for( int i = sub_layer_ordering_info_present_flag ? 0 : sps->max_sub_layers_minus1; i <= sps->max_sub_layers_minus1; i++ ) + { + nalu_get_exp_golomb_ue( bits ); /* max_dec_pic_buffering_minus1[i] */ + nalu_get_exp_golomb_ue( bits ); /* max_num_reorder_pics [i] */ + nalu_get_exp_golomb_ue( bits ); /* max_latency_increase_plus1 [i] */ + } + uint64_t log2_min_luma_coding_block_size_minus3 = nalu_get_exp_golomb_ue( bits ); + uint64_t log2_diff_max_min_luma_coding_block_size = nalu_get_exp_golomb_ue( bits ); + nalu_get_exp_golomb_ue( bits ); /* log2_min_transform_block_size_minus2 */ + nalu_get_exp_golomb_ue( bits ); /* log2_diff_max_min_transform_block_size */ + nalu_get_exp_golomb_ue( bits ); /* max_transform_hierarchy_depth_inter */ + nalu_get_exp_golomb_ue( bits ); /* max_transform_hierarchy_depth_intra */ + { + int MinCbLog2SizeY = log2_min_luma_coding_block_size_minus3 + 3; + int MinCbSizeY = 1 << MinCbLog2SizeY; + if( pic_width_in_luma_samples == 0 || pic_width_in_luma_samples % MinCbSizeY + || pic_height_in_luma_samples == 0 || pic_height_in_luma_samples % MinCbSizeY ) + return LSMASH_ERR_INVALID_DATA; /* Both shall be an integer multiple of MinCbSizeY. */ + int CtbLog2SizeY = MinCbLog2SizeY + log2_diff_max_min_luma_coding_block_size; + int CtbSizeY = 1 << CtbLog2SizeY; + sps->PicWidthInCtbsY = (pic_width_in_luma_samples - 1) / CtbSizeY + 1; + sps->PicHeightInCtbsY = (pic_height_in_luma_samples - 1) / CtbSizeY + 1; + sps->PicSizeInCtbsY = sps->PicWidthInCtbsY * sps->PicHeightInCtbsY; + } + if( lsmash_bits_get( bits, 1 ) /* scaling_list_enabled_flag */ + && lsmash_bits_get( bits, 1 ) ) /* sps_scaling_list_data_present_flag */ + hevc_parse_scaling_list_data( bits ); + lsmash_bits_get( bits, 1 ); /* amp_enabled_flag */ + lsmash_bits_get( bits, 1 ); /* sample_adaptive_offset_enabled_flag */ + if( lsmash_bits_get( bits, 1 ) ) /* pcm_enabled_flag */ + { + lsmash_bits_get( bits, 4 ); /* pcm_sample_bit_depth_luma_minus1 */ + lsmash_bits_get( bits, 4 ); /* pcm_sample_bit_depth_chroma_minus1 */ + nalu_get_exp_golomb_ue( bits ); /* log2_min_pcm_luma_coding_block_size_minus3 */ + nalu_get_exp_golomb_ue( bits ); /* log2_diff_max_min_pcm_luma_coding_block_size */ + lsmash_bits_get( bits, 1 ); /* pcm_loop_filter_disabled_flag */ + } + sps->num_short_term_ref_pic_sets = nalu_get_exp_golomb_ue( bits ); + for( int i = 0; i < sps->num_short_term_ref_pic_sets; i++ ) + if( (err = hevc_short_term_ref_pic_set( bits, sps, i )) < 0 ) + return err; + sps->long_term_ref_pics_present_flag = lsmash_bits_get( bits, 1 ); + if( sps->long_term_ref_pics_present_flag ) + { + sps->num_long_term_ref_pics_sps = nalu_get_exp_golomb_ue( bits ); + for( int i = 0; i < sps->num_long_term_ref_pics_sps; i++ ) + { + lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); /* lt_ref_pic_poc_lsb_sps [i] */ + lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_lt_sps_flag[i] */ + } + } + sps->temporal_mvp_enabled_flag = lsmash_bits_get( bits, 1 ); + lsmash_bits_get( bits, 1 ); /* strong_intra_smoothing_enabled_flag */ + sps->vui.present = lsmash_bits_get( bits, 1 ); /* vui_parameters_present_flag */ + if( sps->vui.present ) + { + /* vui_parameters() */ + if( lsmash_bits_get( bits, 1 ) ) /* aspect_ratio_info_present_flag */ + { + uint8_t aspect_ratio_idc = lsmash_bits_get( bits, 8 ); + if( aspect_ratio_idc == 255 ) + { + /* EXTENDED_SAR */ + sps->vui.sar_width = lsmash_bits_get( bits, 16 ); + sps->vui.sar_height = lsmash_bits_get( bits, 16 ); + } + else + { + static const struct + { + uint16_t sar_width; + uint16_t sar_height; + } pre_defined_sar[] = + { + { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, + { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, { 80, 33 }, + { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, { 4, 3 }, + { 3, 2 }, { 2, 1 } + }; + if( aspect_ratio_idc < (sizeof(pre_defined_sar) / sizeof(pre_defined_sar[0])) ) + { + sps->vui.sar_width = pre_defined_sar[ aspect_ratio_idc ].sar_width; + sps->vui.sar_height = pre_defined_sar[ aspect_ratio_idc ].sar_height; + } + else + { + /* Behavior when unknown aspect_ratio_idc is detected is not specified in the specification. */ + sps->vui.sar_width = 0; + sps->vui.sar_height = 0; + } + } + } + else + { + sps->vui.sar_width = 0; + sps->vui.sar_height = 0; + } + if( lsmash_bits_get( bits, 1 ) ) /* overscan_info_present_flag */ + lsmash_bits_get( bits, 1 ); /* overscan_appropriate_flag */ + if( lsmash_bits_get( bits, 1 ) ) /* video_signal_type_present_flag */ + { + lsmash_bits_get( bits, 3 ); /* video_format */ + sps->vui.video_full_range_flag = lsmash_bits_get( bits, 1 ); + sps->vui.colour_description_present_flag = lsmash_bits_get( bits, 1 ); + if( sps->vui.colour_description_present_flag ) + { + sps->vui.colour_primaries = lsmash_bits_get( bits, 8 ); + sps->vui.transfer_characteristics = lsmash_bits_get( bits, 8 ); + sps->vui.matrix_coeffs = lsmash_bits_get( bits, 8 ); + } + else + { + sps->vui.colour_primaries = 2; + sps->vui.transfer_characteristics = 2; + sps->vui.matrix_coeffs = 2; + } + } + if( lsmash_bits_get( bits, 1 ) ) /* chroma_loc_info_present_flag */ + { + nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_top_field */ + nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_bottom_field */ + } + lsmash_bits_get( bits, 1 ); /* neutral_chroma_indication_flag */ + sps->vui.field_seq_flag = lsmash_bits_get( bits, 1 ); + sps->vui.frame_field_info_present_flag = lsmash_bits_get( bits, 1 ); + if( sps->vui.field_seq_flag ) + /* cropped_height indicates in a frame. */ + sps->cropped_height *= 2; + if( lsmash_bits_get( bits, 1 ) ) /* default_display_window_flag */ + { + /* default display window + * A rectangular region for display specified by these values is not considered + * as cropped visual presentation size which decoder delivers. + * Maybe, these values shall be indicated by the clean aperture on container level. */ + sps->vui.def_disp_win_offset.left = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubWidthC [ sps->chroma_format_idc ], 1 }; + sps->vui.def_disp_win_offset.right = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubWidthC [ sps->chroma_format_idc ], 1 }; + sps->vui.def_disp_win_offset.top = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubHeightC[ sps->chroma_format_idc ], 1 }; + sps->vui.def_disp_win_offset.bottom = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubHeightC[ sps->chroma_format_idc ], 1 }; + } + if( lsmash_bits_get( bits, 1 ) ) /* vui_timing_info_present_flag */ + { + sps->vui.num_units_in_tick = lsmash_bits_get( bits, 32 ); + sps->vui.time_scale = lsmash_bits_get( bits, 32 ); + if( lsmash_bits_get( bits, 1 ) ) /* vui_poc_proportional_to_timing_flag */ + nalu_get_exp_golomb_ue( bits ); /* vui_num_ticks_poc_diff_one_minus1 */ + if( lsmash_bits_get( bits, 1 ) ) /* vui_hrd_parameters_present_flag */ + hevc_parse_hrd_parameters( bits, &sps->vui.hrd, 1, sps->max_sub_layers_minus1 ); + } + else + { + sps->vui.num_units_in_tick = 1; /* arbitrary */ + sps->vui.time_scale = 25; /* arbitrary */ + } + if( lsmash_bits_get( bits, 1 ) ) /* bitstream_restriction_flag */ + { + lsmash_bits_get( bits, 1 ); /* tiles_fixed_structure_flag */ + lsmash_bits_get( bits, 1 ); /* motion_vectors_over_pic_boundaries_flag */ + lsmash_bits_get( bits, 1 ); /* restricted_ref_pic_lists_flag */ + sps->vui.min_spatial_segmentation_idc = nalu_get_exp_golomb_ue( bits ); + nalu_get_exp_golomb_ue( bits ); /* max_bytes_per_pic_denom */ + nalu_get_exp_golomb_ue( bits ); /* max_bits_per_min_cu_denom */ + nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_horizontal */ + nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_vertical */ + } + else + sps->vui.min_spatial_segmentation_idc = 0; + } + else + { + sps->vui.sar_width = 0; + sps->vui.sar_height = 0; + sps->vui.colour_primaries = 2; + sps->vui.transfer_characteristics = 2; + sps->vui.matrix_coeffs = 2; + sps->vui.field_seq_flag = 0; + sps->vui.frame_field_info_present_flag = sps->ptl.general.progressive_source_flag + && sps->ptl.general.interlaced_source_flag; + sps->vui.num_units_in_tick = 1; /* arbitrary */ + sps->vui.time_scale = 25; /* arbitrary */ + sps->vui.min_spatial_segmentation_idc = 0; + } + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int hevc_parse_sps +( + hevc_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + lsmash_bits_t *bits = info->bits; + hevc_sps_t *sps; + { + /* Parse SPS minimally for configuration records. */ + hevc_sps_t min_sps; + int err = hevc_parse_sps_minimally( bits, &min_sps, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + sps = hevc_get_sps( info->sps_list, min_sps.seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + *sps = min_sps; + } + /* Skip SPS extension. */ + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + sps->present = 1; + info->sps = *sps; + hevc_activate_vps( info, info->sps.video_parameter_set_id ); + return 0; +} + +static int hevc_allocate_tile_sizes +( + hevc_pps_t *pps, + uint32_t num_tile_columns, + uint32_t num_tile_rows +) +{ + /* Allocate columns and rows of tiles. */ + size_t col_alloc_size = 2 * num_tile_columns * sizeof(uint32_t); + if( col_alloc_size > pps->col_alloc_size ) + { + void *temp = lsmash_realloc( pps->colWidth, col_alloc_size ); + if( !temp ) + return LSMASH_ERR_MEMORY_ALLOC; + pps->col_alloc_size = col_alloc_size; + pps->colWidth = temp; + } + size_t row_alloc_size = 2 * num_tile_rows * sizeof(uint32_t); + if( row_alloc_size > pps->row_alloc_size ) + { + void *temp = lsmash_realloc( pps->rowHeight, row_alloc_size ); + if( !temp ) + return LSMASH_ERR_MEMORY_ALLOC; + pps->row_alloc_size = row_alloc_size; + pps->rowHeight = temp; + } + pps->colBd = pps->colWidth + num_tile_columns; + pps->rowBd = pps->rowHeight + num_tile_rows; + return 0; +} + +static int hevc_parse_pps_minimally +( + lsmash_bits_t *bits, + hevc_pps_t *pps, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + memset( pps, 0, SIZEOF_PPS_EXCLUDING_HEAP ); + pps->pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + pps->seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + pps->dependent_slice_segments_enabled_flag = lsmash_bits_get( bits, 1 ); + pps->output_flag_present_flag = lsmash_bits_get( bits, 1 ); + pps->num_extra_slice_header_bits = lsmash_bits_get( bits, 3 ); + lsmash_bits_get( bits, 1 ); /* sign_data_hiding_enabled_flag */ + lsmash_bits_get( bits, 1 ); /* cabac_init_present_flag */ + nalu_get_exp_golomb_ue( bits ); /* num_ref_idx_l0_default_active_minus1 */ + nalu_get_exp_golomb_ue( bits ); /* num_ref_idx_l1_default_active_minus1 */ + nalu_get_exp_golomb_se( bits ); /* init_qp_minus26 */ + lsmash_bits_get( bits, 1 ); /* constrained_intra_pred_flag */ + lsmash_bits_get( bits, 1 ); /* transform_skip_enabled_flag */ + if( lsmash_bits_get( bits, 1 ) ) /* cu_qp_delta_enabled_flag */ + nalu_get_exp_golomb_ue( bits ); /* diff_cu_qp_delta_depth */ + nalu_get_exp_golomb_se( bits ); /* cb_qp_offset */ + nalu_get_exp_golomb_se( bits ); /* cr_qp_offset */ + lsmash_bits_get( bits, 1 ); /* slice_chroma_qp_offsets_present_flag */ + lsmash_bits_get( bits, 1 ); /* weighted_pred_flag */ + lsmash_bits_get( bits, 1 ); /* weighted_bipred_flag */ + lsmash_bits_get( bits, 1 ) /* transquant_bypass_enabled_flag */; + pps->tiles_enabled_flag = lsmash_bits_get( bits, 1 ); + pps->entropy_coding_sync_enabled_flag = lsmash_bits_get( bits, 1 ); + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int hevc_parse_pps +( + hevc_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err; + lsmash_bits_t *bits = info->bits; + hevc_pps_t *pps; + { + /* Parse PPS minimally for configuration records. */ + hevc_pps_t min_pps; + if( (err = hevc_parse_pps_minimally( bits, &min_pps, rbsp_buffer, ebsp, ebsp_size )) < 0 ) + return err; + pps = hevc_get_pps( info->pps_list, min_pps.pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + memcpy( pps, &min_pps, SIZEOF_PPS_EXCLUDING_HEAP ); + } + hevc_sps_t temp_sps = info->sps; + if( (err = hevc_activate_sps( info, pps->seq_parameter_set_id )) < 0 ) + return err; + hevc_sps_t *sps = &info->sps; + if( pps->tiles_enabled_flag ) + { + pps->num_tile_columns_minus1 = nalu_get_exp_golomb_ue( bits ); + pps->num_tile_rows_minus1 = nalu_get_exp_golomb_ue( bits ); + if( pps->num_tile_columns_minus1 >= sps->PicWidthInCtbsY + || pps->num_tile_rows_minus1 >= sps->PicHeightInCtbsY ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + if( (err = hevc_allocate_tile_sizes( pps, pps->num_tile_columns_minus1 + 1, pps->num_tile_rows_minus1 + 1 )) < 0 ) + goto fail; + if( lsmash_bits_get( bits, 1 ) ) /* uniform_spacing_flag */ + { + for( int i = 0; i <= pps->num_tile_columns_minus1; i++ ) + pps->colWidth[i] = ((i + 1) * sps->PicWidthInCtbsY) / (pps->num_tile_columns_minus1 + 1) + - ( i * sps->PicWidthInCtbsY) / (pps->num_tile_columns_minus1 + 1); + for( int j = 0; j <= pps->num_tile_rows_minus1; j++ ) + pps->rowHeight[j] = ((j + 1) * sps->PicHeightInCtbsY) / (pps->num_tile_rows_minus1 + 1) + - ( j * sps->PicHeightInCtbsY) / (pps->num_tile_rows_minus1 + 1); + } + else + { + pps->colWidth[ pps->num_tile_columns_minus1 ] = sps->PicWidthInCtbsY; + for( uint64_t i = 0; i < pps->num_tile_columns_minus1; i++ ) + { + pps->colWidth[i] = nalu_get_exp_golomb_ue( bits ) + 1; /* column_width_minus1[i] */ + pps->colWidth[ pps->num_tile_columns_minus1 ] -= pps->colWidth[i]; + } + pps->rowHeight[ pps->num_tile_rows_minus1 ] = sps->PicHeightInCtbsY; + for( uint64_t j = 0; j < pps->num_tile_rows_minus1; j++ ) + { + pps->rowHeight[j] = nalu_get_exp_golomb_ue( bits ) + 1; /* row_height_minus1 [j] */ + pps->rowHeight[ pps->num_tile_rows_minus1 ] -= pps->rowHeight[j]; + } + } + pps->colBd[0] = 0; + for( uint64_t i = 0; i < pps->num_tile_columns_minus1; i++ ) + pps->colBd[i + 1] = pps->colBd[i] + pps->colWidth[i]; + pps->rowBd[0] = 0; + for( uint64_t j = 0; j < pps->num_tile_rows_minus1; j++ ) + pps->rowBd[j + 1] = pps->rowBd[j] + pps->rowHeight[j]; + lsmash_bits_get( bits, 1 ); /* loop_filter_across_tiles_enabled_flag */ + } + else + { + pps->num_tile_columns_minus1 = 0; + pps->num_tile_rows_minus1 = 0; + if( (err = hevc_allocate_tile_sizes( pps, 1, 1 )) < 0 ) + goto fail; + pps->colWidth [0] = sps->PicWidthInCtbsY; + pps->rowHeight[0] = sps->PicHeightInCtbsY; + pps->colBd [0] = 0; + pps->rowBd [0] = 0; + } + /* */ + /* Skip PPS extension. */ + lsmash_bits_empty( bits ); + if( bits->bs->error ) + goto fail; + pps->present = 1; + info->pps = *pps; + hevc_activate_vps( info, info->sps.video_parameter_set_id ); + return 0; +fail: + /* Revert SPS. */ + info->sps = temp_sps; + return err; +} + +int hevc_parse_sei +( + lsmash_bits_t *bits, + hevc_vps_t *vps, + hevc_sps_t *sps, + hevc_sei_t *sei, + hevc_nalu_header_t *nuh, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ); + if( err < 0 ) + return err; + uint8_t *rbsp_start = rbsp_buffer; + uint64_t rbsp_pos = 0; + do + { + /* sei_message() */ + uint32_t payloadType = 0; + for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) + { + /* 0xff : ff_byte + * otherwise: last_payload_type_byte */ + payloadType += temp; + ++rbsp_pos; + if( temp != 0xff ) + break; + } + uint32_t payloadSize = 0; + for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) + { + /* 0xff : ff_byte + * otherwise: last_payload_size_byte */ + payloadSize += temp; + ++rbsp_pos; + if( temp != 0xff ) + break; + } + if( nuh->nal_unit_type == HEVC_NALU_TYPE_PREFIX_SEI ) + { + if( payloadType == 1 ) + { + /* pic_timing */ + hevc_hrd_t *hrd = sps ? &sps->vui.hrd : vps ? &vps->hrd[0] : NULL; + if( !hrd ) + goto skip_sei_message; /* Any active VPS or SPS is not found. */ + sei->pic_timing.present = 1; + if( (sps && sps->vui.frame_field_info_present_flag) || vps->frame_field_info_present_flag ) + { + sei->pic_timing.pic_struct = lsmash_bits_get( bits, 4 ); + lsmash_bits_get( bits, 2 ); /* source_scan_type */ + lsmash_bits_get( bits, 1 ); /* duplicate_flag */ + } + if( hrd->CpbDpbDelaysPresentFlag ) + { + lsmash_bits_get( bits, hrd->au_cpb_removal_delay_length ); /* au_cpb_removal_delay_minus1 */ + lsmash_bits_get( bits, hrd->dpb_output_delay_length ); /* pic_dpb_output_delay */ + if( hrd->sub_pic_hrd_params_present_flag ) + { + lsmash_bits_get( bits, hrd->dpb_output_delay_du_length ); /* pic_dpb_output_du_delay */ + if( hrd->sub_pic_cpb_params_in_pic_timing_sei_flag ) + { + uint64_t num_decoding_units_minus1 = nalu_get_exp_golomb_ue( bits ); + int du_common_cpb_removal_delay_flag = lsmash_bits_get( bits, 1 ); + if( du_common_cpb_removal_delay_flag ) + /* du_common_cpb_removal_delay_increment_minus1 */ + lsmash_bits_get( bits, hrd->du_cpb_removal_delay_increment_length ); + for( uint64_t i = 0; i <= num_decoding_units_minus1; i++ ) + { + nalu_get_exp_golomb_ue( bits ); /* num_nalus_in_du_minus1 */ + if( !du_common_cpb_removal_delay_flag && i < num_decoding_units_minus1 ) + nalu_get_exp_golomb_ue( bits ); /* du_cpb_removal_delay_increment_minus1 */ + } + } + } + } + } + else if( payloadType == 3 ) + { + /* filler_payload + * FIXME: remove if array_completeness equal to 1. */ + return LSMASH_ERR_PATCH_WELCOME; + } + else if( payloadType == 6 ) + { + /* recovery_point */ + sei->recovery_point.present = 1; + sei->recovery_point.recovery_poc_cnt = nalu_get_exp_golomb_se( bits ); + lsmash_bits_get( bits, 1 ); /* exact_match_flag */ + sei->recovery_point.broken_link_flag = lsmash_bits_get( bits, 1 ); + } + else + goto skip_sei_message; + } + else if( nuh->nal_unit_type == HEVC_NALU_TYPE_SUFFIX_SEI ) + { + if( payloadType == 3 ) + { + /* filler_payload + * FIXME: remove if array_completeness equal to 1. */ + return LSMASH_ERR_PATCH_WELCOME; + } + else + goto skip_sei_message; + } + else + { +skip_sei_message: + lsmash_bits_get( bits, payloadSize * 8 ); + } + lsmash_bits_get_align( bits ); + rbsp_pos += payloadSize; + } while( *(rbsp_start + rbsp_pos) != 0x80 ); /* All SEI messages are byte aligned at their end. + * Therefore, 0x80 shall be rbsp_trailing_bits(). */ + lsmash_bits_empty( bits ); + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int hevc_parse_slice_segment_header +( + hevc_info_t *info, + hevc_nalu_header_t *nuh, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + lsmash_bits_t *bits = info->bits; + int err = nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, LSMASH_MIN( ebsp_size, 50 ) ); + if( err < 0 ) + return err; + hevc_slice_info_t *slice = &info->slice; + memset( slice, 0, sizeof(hevc_slice_info_t) ); + slice->nalu_type = nuh->nal_unit_type; + slice->TemporalId = nuh->TemporalId; + slice->first_slice_segment_in_pic_flag = lsmash_bits_get( bits, 1 ); + if( nuh->nal_unit_type >= HEVC_NALU_TYPE_BLA_W_LP + && nuh->nal_unit_type <= HEVC_NALU_TYPE_RSV_IRAP_VCL23 ) + lsmash_bits_get( bits, 1 ); /* no_output_of_prior_pics_flag */ + slice->pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); + /* Get PPS by slice_pic_parameter_set_id. */ + hevc_pps_t *pps = hevc_get_pps( info->pps_list, slice->pic_parameter_set_id ); + if( !pps ) + return LSMASH_ERR_NAMELESS; + /* Get SPS by pps_seq_parameter_set_id. */ + hevc_sps_t *sps = hevc_get_sps( info->sps_list, pps->seq_parameter_set_id ); + if( !sps ) + return LSMASH_ERR_NAMELESS; + slice->video_parameter_set_id = sps->video_parameter_set_id; + slice->seq_parameter_set_id = pps->seq_parameter_set_id; + if( !slice->first_slice_segment_in_pic_flag ) + { + slice->dependent_slice_segment_flag = pps->dependent_slice_segments_enabled_flag ? lsmash_bits_get( bits, 1 ) : 0; + slice->segment_address = lsmash_bits_get( bits, lsmash_ceil_log2( sps->PicSizeInCtbsY ) ); + } + else + { + slice->dependent_slice_segment_flag = 0; + slice->segment_address = 0; + } + if( !slice->dependent_slice_segment_flag ) + { + /* independent slice segment + * The values of the slice segment header of dependent slice segment are inferred from the values + * for the preceding independent slice segment in decoding order, if some of the values are not present. */ + for( int i = 0; i < pps->num_extra_slice_header_bits; i++ ) + lsmash_bits_get( bits, 1 ); /* slice_reserved_flag[i] */ + slice->type = nalu_get_exp_golomb_ue( bits ); + if( pps->output_flag_present_flag ) + lsmash_bits_get( bits, 1 ); /* pic_output_flag */ + if( sps->separate_colour_plane_flag ) + lsmash_bits_get( bits, 1 ); /* colour_plane_id */ + if( nuh->nal_unit_type != HEVC_NALU_TYPE_IDR_W_RADL + && nuh->nal_unit_type != HEVC_NALU_TYPE_IDR_N_LP ) + { + slice->pic_order_cnt_lsb = lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); + if( !lsmash_bits_get( bits, 1 ) ) /* short_term_ref_pic_set_sps_flag */ + { + if( (err = hevc_short_term_ref_pic_set( bits, sps, sps->num_short_term_ref_pic_sets )) < 0 ) + return err; + } + else + { + int length = lsmash_ceil_log2( sps->num_short_term_ref_pic_sets ); + if( length > 0 ) + lsmash_bits_get( bits, length ); /* short_term_ref_pic_set_idx */ + } + if( sps->long_term_ref_pics_present_flag ) + { + uint64_t num_long_term_sps = sps->num_long_term_ref_pics_sps > 0 ? nalu_get_exp_golomb_ue( bits ) : 0; + uint64_t num_long_term_pics = nalu_get_exp_golomb_ue( bits ); + for( uint64_t i = 0; i < num_long_term_sps + num_long_term_pics; i++ ) + { + if( i < num_long_term_sps ) + { + int length = lsmash_ceil_log2( sps->num_long_term_ref_pics_sps ); + if( length > 0 ) + lsmash_bits_get( bits, length ); /* lt_idx_sps[i] */ + } + else + { + lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); /* poc_lsb_lt [i] */ + lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_lt_flag[i] */ + } + if( lsmash_bits_get( bits, 1 ) ) /* delta_poc_msb_present_flag[i] */ + nalu_get_exp_golomb_ue( bits ); /* delta_poc_msb_cycle_lt [i] */ + } + } + if( sps->temporal_mvp_enabled_flag ) + lsmash_bits_get( bits, 1 ); /* slice_temporal_mvp_enabled_flag */ + } + else + /* For IDR-pictures, slice_pic_order_cnt_lsb is inferred to be 0. */ + slice->pic_order_cnt_lsb = 0; + } + lsmash_bits_empty( bits ); + if( bits->bs->error ) + return LSMASH_ERR_NAMELESS; + info->sps = *sps; + info->pps = *pps; + return 0; +} + +static int hevc_get_vps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id +) +{ + /* the number of bits of vps_id = 4 + * (4 - 1) / 8 + 1 = 1 bytes */ + *ps_id = (*ps_ebsp >> 4) & 0x0F; /* vps_video_parameter_set_id */ + return 0; +} + +static int hevc_get_sps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id +) +{ + /* the maximum number of bits of sps_id = 9: 0b00001XXXX + * (8 + 688 + 9 - 1) / 8 + 1 = 89 bytes + * Here more additional bytes because there might be emulation_prevention_three_byte(s). */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[128]; + uint8_t buffer [128]; + bs.buffer.data = buffer; + bs.buffer.alloc = 128; + lsmash_bits_init( &bits, &bs ); + int err = nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 128 ) ); + if( err < 0 ) + return err; + /* Skip sps_video_parameter_set_id and sps_temporal_id_nesting_flag. */ + uint8_t sps_max_sub_layers_minus1 = (lsmash_bits_get( &bits, 8 ) >> 1) & 0x07; + /* profile_tier_level() costs at most 688 bits. */ + hevc_ptl_t sps_ptl; + hevc_parse_profile_tier_level( &bits, &sps_ptl, sps_max_sub_layers_minus1 ); + uint64_t sps_seq_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); + if( sps_seq_parameter_set_id > HEVC_MAX_SPS_ID ) + return LSMASH_ERR_INVALID_DATA; + *ps_id = sps_seq_parameter_set_id; + return bs.error ? LSMASH_ERR_NAMELESS : 0; +} + +static int hevc_get_pps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id +) +{ + /* the maximum number of bits of pps_id = 13: 0b0000001XXXXXX + * (13 - 1) / 8 + 1 = 2 bytes + * Why +1? Because there might be an emulation_prevention_three_byte. */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[3]; + uint8_t buffer [3]; + bs.buffer.data = buffer; + bs.buffer.alloc = 3; + lsmash_bits_init( &bits, &bs ); + int err = nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 3 ) ); + if( err < 0 ) + return err; + uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); + if( pic_parameter_set_id > HEVC_MAX_PPS_ID ) + return LSMASH_ERR_INVALID_DATA; + *ps_id = pic_parameter_set_id; + return bs.error ? LSMASH_ERR_NAMELESS : 0; +} + +static inline int hevc_get_ps_id +( + uint8_t *ps_ebsp, + uint32_t ps_ebsp_length, + uint8_t *ps_id, + lsmash_hevc_dcr_nalu_type ps_type +) +{ + int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) + = ps_type == HEVC_DCR_NALU_TYPE_VPS ? hevc_get_vps_id + : ps_type == HEVC_DCR_NALU_TYPE_SPS ? hevc_get_sps_id + : ps_type == HEVC_DCR_NALU_TYPE_PPS ? hevc_get_pps_id + : NULL; + return get_ps_id ? get_ps_id( ps_ebsp, ps_ebsp_length, ps_id ) : LSMASH_ERR_INVALID_DATA; +} + +static inline hevc_parameter_array_t *hevc_get_parameter_set_array +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type +) +{ + if( !param->parameter_arrays ) + return NULL; + if( ps_type >= HEVC_DCR_NALU_TYPE_NUM ) + return NULL; + return ¶m->parameter_arrays->ps_array[ps_type]; +} + +static inline lsmash_entry_list_t *hevc_get_parameter_set_list +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type +) +{ + if( !param->parameter_arrays ) + return NULL; + if( ps_type >= HEVC_DCR_NALU_TYPE_NUM ) + return NULL; + return param->parameter_arrays->ps_array[ps_type].list; +} + +static lsmash_entry_t *hevc_get_ps_entry_from_param +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type, + uint8_t ps_id +) +{ + int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) + = ps_type == HEVC_DCR_NALU_TYPE_VPS ? hevc_get_vps_id + : ps_type == HEVC_DCR_NALU_TYPE_SPS ? hevc_get_sps_id + : ps_type == HEVC_DCR_NALU_TYPE_PPS ? hevc_get_pps_id + : NULL; + if( !get_ps_id ) + return NULL; + lsmash_entry_list_t *list = hevc_get_parameter_set_list( param, ps_type ); + if( !list ) + return NULL; + for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return NULL; + uint8_t param_ps_id; + if( get_ps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_ps_id ) < 0 ) + return NULL; + if( ps_id == param_ps_id ) + return entry; + } + return NULL; +} + +static inline void hevc_update_picture_type +( + hevc_picture_info_t *picture, + hevc_slice_info_t *slice +) +{ + if( picture->type == HEVC_PICTURE_TYPE_I_P ) + { + if( slice->type == HEVC_SLICE_TYPE_B ) + picture->type = HEVC_PICTURE_TYPE_I_P_B; + } + else if( picture->type == HEVC_PICTURE_TYPE_I ) + { + if( slice->type == HEVC_SLICE_TYPE_P ) + picture->type = HEVC_PICTURE_TYPE_I_P; + else if( slice->type == HEVC_SLICE_TYPE_B ) + picture->type = HEVC_PICTURE_TYPE_I_P_B; + } + else if( picture->type == HEVC_PICTURE_TYPE_NONE ) + { + if( slice->type == HEVC_SLICE_TYPE_P ) + picture->type = HEVC_PICTURE_TYPE_I_P; + else if( slice->type == HEVC_SLICE_TYPE_B ) + picture->type = HEVC_PICTURE_TYPE_I_P_B; + else if( slice->type == HEVC_SLICE_TYPE_I ) + picture->type = HEVC_PICTURE_TYPE_I; + } +#if 0 + fprintf( stderr, "Picture type = %s\n", picture->type == HEVC_PICTURE_TYPE_I_P ? "P" + : picture->type == HEVC_PICTURE_TYPE_I_P_B ? "B" + : picture->type == HEVC_PICTURE_TYPE_I ? "I" ); +#endif +} + +/* Shall be called at least once per picture. */ +void hevc_update_picture_info_for_slice +( + hevc_info_t *info, + hevc_picture_info_t *picture, + hevc_slice_info_t *slice +) +{ + assert( info ); + picture->has_primary |= !slice->dependent_slice_segment_flag; + hevc_update_picture_type( picture, slice ); + /* Mark 'used' on active parameter sets. */ + uint8_t ps_id[3] = { slice->video_parameter_set_id, slice->seq_parameter_set_id, slice->pic_parameter_set_id }; + for( int i = 0; i < 3; i++ ) + { + lsmash_hevc_dcr_nalu_type ps_type = (lsmash_hevc_dcr_nalu_type)i; + lsmash_entry_t *entry = hevc_get_ps_entry_from_param( &info->hvcC_param, ps_type, ps_id[i] ); + if( entry && entry->data ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( ps->unused ) + lsmash_append_hevc_dcr_nalu( &info->hvcC_param, ps_type, ps->nalUnit, ps->nalUnitLength ); + } + } + /* Discard this slice info. */ + slice->present = 0; +} + +/* Shall be called exactly once per picture. */ +void hevc_update_picture_info +( + hevc_info_t *info, + hevc_picture_info_t *picture, + hevc_slice_info_t *slice, + hevc_sps_t *sps, + hevc_sei_t *sei +) +{ + picture->irap = slice->nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && slice->nalu_type <= HEVC_NALU_TYPE_CRA; + picture->idr = slice->nalu_type == HEVC_NALU_TYPE_IDR_W_RADL || slice->nalu_type == HEVC_NALU_TYPE_IDR_N_LP; + picture->broken_link = slice->nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && slice->nalu_type <= HEVC_NALU_TYPE_BLA_N_LP; + picture->radl = slice->nalu_type == HEVC_NALU_TYPE_RADL_N || slice->nalu_type == HEVC_NALU_TYPE_RADL_R; + picture->rasl = slice->nalu_type == HEVC_NALU_TYPE_RASL_N || slice->nalu_type == HEVC_NALU_TYPE_RASL_R; + picture->sublayer_nonref = slice->nalu_type <= HEVC_NALU_TYPE_RSV_VCL_R15 && ((slice->nalu_type & 0x01) == 0); + picture->closed_rap = slice->nalu_type >= HEVC_NALU_TYPE_BLA_W_RADL && slice->nalu_type <= HEVC_NALU_TYPE_IDR_N_LP; + picture->random_accessible = picture->irap; + picture->TemporalId = slice->TemporalId; + picture->pic_parameter_set_id = slice->pic_parameter_set_id; + picture->poc_lsb = slice->pic_order_cnt_lsb; + hevc_update_picture_info_for_slice( info, picture, slice ); + picture->independent = (picture->type == HEVC_PICTURE_TYPE_I); + picture->field_coded = sps->vui.field_seq_flag; + if( sei->pic_timing.present ) + { + if( sei->pic_timing.pic_struct < 13 ) + { + static const uint8_t delta[13] = { 2, 1, 1, 2, 2, 3, 3, 4, 6, 1, 1, 1, 1 }; + picture->delta = delta[ sei->pic_timing.pic_struct ]; + } + else + /* Reserved values in the spec we refer to. */ + picture->delta = picture->field_coded ? 1 : 2; + sei->pic_timing.present = 0; + } + else + picture->delta = picture->field_coded ? 1 : 2; + if( sei->recovery_point.present ) + { + picture->random_accessible |= sei->recovery_point.present; + picture->recovery_poc_cnt = sei->recovery_point.recovery_poc_cnt; + picture->broken_link |= sei->recovery_point.broken_link_flag; + sei->recovery_point.present = 0; + } + else + picture->recovery_poc_cnt = 0; +} + +static uint64_t hevc_get_ctb_address_in_tile_scan +( + hevc_sps_t *sps, + hevc_pps_t *pps, + uint64_t segment_address, + uint64_t *TileId +) +{ + uint64_t tbX = segment_address % sps->PicWidthInCtbsY; + uint64_t tbY = segment_address / sps->PicWidthInCtbsY; + uint32_t tileX = pps->num_tile_columns_minus1; + for( uint32_t i = 0; i <= pps->num_tile_columns_minus1; i++ ) + if( tbX >= pps->colBd[i] ) + tileX = i; + uint32_t tileY = pps->num_tile_rows_minus1; + for( uint32_t j = 0; j <= pps->num_tile_rows_minus1; j++ ) + if( tbY >= pps->rowBd[j] ) + tileY = j; + uint64_t CtbAddrInTs = 0; + for( uint32_t i = 0; i < tileX; i++ ) + CtbAddrInTs += pps->rowHeight[tileY] * pps->colWidth[i]; + for( uint32_t j = 0; j < tileY; j++ ) + CtbAddrInTs += sps->PicWidthInCtbsY * pps->rowHeight[j]; + CtbAddrInTs += (tbY - pps->rowBd[tileY]) * pps->colWidth[tileX] + tbX - pps->colBd[tileX]; + *TileId = (uint64_t)tileY * (pps->num_tile_columns_minus1 + 1) + tileX; + return CtbAddrInTs; +} + +int hevc_find_au_delimit_by_slice_info +( + hevc_info_t *info, + hevc_slice_info_t *slice, + hevc_slice_info_t *prev_slice +) +{ + /* 7.4.2.4.5 Order of VCL NAL units and association to coded pictures + * - The first VCL NAL unit of the coded picture shall have first_slice_segment_in_pic_flag equal to 1. */ + if( slice->first_slice_segment_in_pic_flag ) + return 1; + /* The value of TemporalId shall be the same for all VCL NAL units of an access unit. */ + if( slice->TemporalId != prev_slice->TemporalId ) + return 1; + /* 7.4.2.4.5 Order of VCL NAL units and association to coded pictures + * - if( TileId[ CtbAddrRsToTs[ slice->segment_address ] ] <= TileId[ CtbAddrRsToTs[ prev_slice->segment_address ] ] + * || CtbAddrRsToTs[ slice->segment_address ] <= CtbAddrRsToTs[ prev_slice->segment_address ] ) + * return 1; + */ + hevc_pps_t *prev_pps = hevc_get_pps( info->pps_list, prev_slice->pic_parameter_set_id ); + if( !prev_pps ) + return 0; + hevc_sps_t *prev_sps = hevc_get_sps( info->sps_list, prev_pps->seq_parameter_set_id ); + if( !prev_sps ) + return 0; + uint64_t currTileId; + uint64_t prevTileId; + uint64_t currCtbAddrInTs = hevc_get_ctb_address_in_tile_scan( &info->sps, &info->pps, slice->segment_address, &currTileId ); + uint64_t prevCtbAddrInTs = hevc_get_ctb_address_in_tile_scan( prev_sps, prev_pps, prev_slice->segment_address, &prevTileId ); + if( currTileId <= prevTileId + || currCtbAddrInTs <= prevCtbAddrInTs ) + return 1; + return 0; +} + +int hevc_find_au_delimit_by_nalu_type +( + uint8_t nalu_type, + uint8_t prev_nalu_type +) +{ + /* 7.4.2.4.4 Order of NAL units and coded pictures and their association to access units */ + if( prev_nalu_type <= HEVC_NALU_TYPE_RSV_VCL31 ) + /* The first of any of the following NAL units after the last VCL NAL unit of a coded picture + * specifies the start of a new access unit: + * - access unit delimiter NAL unit (when present) + * - VPS NAL unit (when present) + * - SPS NAL unit (when present) + * - PPS NAL unit (when present) + * - Prefix SEI NAL unit (when present) + * - NAL units with nal_unit_type in the range of RSV_NVCL41..RSV_NVCL44 (when present) + * - NAL units with nal_unit_type in the range of UNSPEC48..UNSPEC55 (when present) + * - first VCL NAL unit of a coded picture (always present) */ + return (nalu_type >= HEVC_NALU_TYPE_VPS && nalu_type <= HEVC_NALU_TYPE_AUD) + || (nalu_type == HEVC_NALU_TYPE_PREFIX_SEI) + || (nalu_type >= HEVC_NALU_TYPE_RSV_NVCL41 && nalu_type <= HEVC_NALU_TYPE_RSV_NVCL44) + || (nalu_type >= HEVC_NALU_TYPE_UNSPEC48 && nalu_type <= HEVC_NALU_TYPE_UNSPEC55); + else if( prev_nalu_type == HEVC_NALU_TYPE_EOS ) + /* An end of sequence NAL unit shall be the last NAL unit in the access unit unless the next + * NAL unit is an end of bitstream NAL unit. */ + return (nalu_type != HEVC_NALU_TYPE_EOB); + else + /* An end of bitstream NAL unit shall be the last NAL unit in the access unit. + * Thus, the next NAL unit shall be the first NAL unit in the next access unit. */ + return (prev_nalu_type == HEVC_NALU_TYPE_EOB); +} + +int hevc_supplement_buffer +( + hevc_stream_buffer_t *sb, + hevc_access_unit_t *au, + uint32_t size +) +{ + lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, size ); + if( !bank ) + return LSMASH_ERR_MEMORY_ALLOC; + sb->bank = bank; + sb->rbsp = lsmash_withdraw_buffer( bank, 1 ); + if( au && bank->number_of_buffers == 3 ) + { + au->data = lsmash_withdraw_buffer( bank, 2 ); + au->incomplete_data = lsmash_withdraw_buffer( bank, 3 ); + } + return 0; +} + +static void hevc_bs_put_parameter_sets +( + lsmash_bs_t *bs, + lsmash_entry_list_t *dcr_ps_list, + uint32_t max_dcr_ps_count +) +{ + uint32_t dcr_ps_count = 0; + for( lsmash_entry_t *entry = dcr_ps_list->head; entry && dcr_ps_count < max_dcr_ps_count; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( ps && !ps->unused ) + { + lsmash_bs_put_be16( bs, ps->nalUnitLength ); + lsmash_bs_put_bytes( bs, ps->nalUnitLength, ps->nalUnit ); + } + else + continue; + ++dcr_ps_count; + } +} + +uint8_t *lsmash_create_hevc_specific_info +( + lsmash_hevc_specific_parameters_t *param, + uint32_t *data_length +) +{ + if( !param || !param->parameter_arrays || !data_length ) + return NULL; + if( param->lengthSizeMinusOne != 0 + && param->lengthSizeMinusOne != 1 + && param->lengthSizeMinusOne != 3 ) + return NULL; + hevc_parameter_array_t *param_arrays[HEVC_DCR_NALU_TYPE_NUM]; + lsmash_entry_list_t *dcr_ps_list [HEVC_DCR_NALU_TYPE_NUM]; + for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) + { + param_arrays[i] = ¶m->parameter_arrays->ps_array[i]; + dcr_ps_list [i] = param_arrays[i]->list; + } + /* VPS, SPS and PPS are mandatory. */ + if( !dcr_ps_list[0] || !dcr_ps_list[0]->head || dcr_ps_list[0]->entry_count == 0 + || !dcr_ps_list[1] || !dcr_ps_list[1]->head || dcr_ps_list[1]->entry_count == 0 + || !dcr_ps_list[2] || !dcr_ps_list[2]->head || dcr_ps_list[2]->entry_count == 0 ) + return NULL; + /* Calculate enough buffer size. */ + static const uint32_t max_dcr_ps_count[HEVC_DCR_NALU_TYPE_NUM] = + { + HEVC_MAX_VPS_ID + 1, + HEVC_MAX_SPS_ID + 1, + HEVC_MAX_PPS_ID + 1, + UINT16_MAX, + UINT16_MAX + }; + uint32_t ps_count[HEVC_DCR_NALU_TYPE_NUM] = { 0 }; + uint32_t buffer_size = ISOM_BASEBOX_COMMON_SIZE + 23; + for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) + if( dcr_ps_list[i] ) + { + for( lsmash_entry_t *entry = dcr_ps_list[i]->head; entry && ps_count[i] < max_dcr_ps_count[i]; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return NULL; + if( ps->unused ) + continue; + buffer_size += 2 + ps->nalUnitLength; + ++ps_count[i]; + } + buffer_size += 3; + } + /* Set up bytestream writer. */ + uint8_t buffer[buffer_size]; + lsmash_bs_t bs = { 0 }; + bs.buffer.data = buffer; + bs.buffer.alloc = buffer_size; + /* Create an HEVCConfigurationBox */ + lsmash_bs_put_be32( &bs, 0 ); /* box size */ + lsmash_bs_put_be32( &bs, ISOM_BOX_TYPE_HVCC.fourcc ); /* box type: 'hvcC' */ + lsmash_bs_put_byte( &bs, HVCC_CONFIGURATION_VERSION ); /* configurationVersion */ + uint8_t temp8 = (param->general_profile_space << 6) + | (param->general_tier_flag << 5) + | param->general_profile_idc; + lsmash_bs_put_byte( &bs, temp8 ); + lsmash_bs_put_be32( &bs, param->general_profile_compatibility_flags ); + lsmash_bs_put_be32( &bs, param->general_constraint_indicator_flags >> 16 ); + lsmash_bs_put_be16( &bs, param->general_constraint_indicator_flags ); + lsmash_bs_put_byte( &bs, param->general_level_idc ); + lsmash_bs_put_be16( &bs, param->min_spatial_segmentation_idc | 0xF000 ); + lsmash_bs_put_byte( &bs, param->parallelismType | 0xFC ); + lsmash_bs_put_byte( &bs, param->chromaFormat | 0xFC ); + lsmash_bs_put_byte( &bs, param->bitDepthLumaMinus8 | 0xF8 ); + lsmash_bs_put_byte( &bs, param->bitDepthChromaMinus8 | 0xF8 ); + lsmash_bs_put_be16( &bs, param->avgFrameRate ); + temp8 = (param->constantFrameRate << 6) + | (param->numTemporalLayers << 3) + | (param->temporalIdNested << 2) + | param->lengthSizeMinusOne; + lsmash_bs_put_byte( &bs, temp8 ); + uint8_t numOfArrays = !!ps_count[0] + + !!ps_count[1] + + !!ps_count[2] + + !!ps_count[3] + + !!ps_count[4]; + lsmash_bs_put_byte( &bs, numOfArrays ); + for( uint8_t i = 0; i < numOfArrays; i++ ) + { + temp8 = (param_arrays[i]->array_completeness << 7) | param_arrays[i]->NAL_unit_type; + lsmash_bs_put_byte( &bs, temp8 ); + lsmash_bs_put_be16( &bs, ps_count[i] ); + hevc_bs_put_parameter_sets( &bs, dcr_ps_list[i], ps_count[i] ); + } + uint8_t *data = lsmash_bs_export_data( &bs, data_length ); + /* Update box size. */ + LSMASH_SET_BE32( data, *data_length ); + return data; +} + +static inline int hevc_validate_dcr_nalu_type +( + lsmash_hevc_dcr_nalu_type ps_type, + void *ps_data, + uint32_t ps_length +) +{ + if( !ps_data || ps_length < 3 ) + return LSMASH_ERR_INVALID_DATA; + if( ps_type != HEVC_DCR_NALU_TYPE_VPS + && ps_type != HEVC_DCR_NALU_TYPE_SPS + && ps_type != HEVC_DCR_NALU_TYPE_PPS + && ps_type != HEVC_DCR_NALU_TYPE_PREFIX_SEI + && ps_type != HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) + return LSMASH_ERR_INVALID_DATA; + uint8_t nalu_type = (*((uint8_t *)ps_data) >> 1) & 0x3f; + if( nalu_type != HEVC_NALU_TYPE_VPS + && nalu_type != HEVC_NALU_TYPE_SPS + && nalu_type != HEVC_NALU_TYPE_PPS + && nalu_type != HEVC_NALU_TYPE_PREFIX_SEI + && nalu_type != HEVC_NALU_TYPE_SUFFIX_SEI ) + return LSMASH_ERR_INVALID_DATA; + if( (ps_type == HEVC_DCR_NALU_TYPE_VPS && nalu_type != HEVC_NALU_TYPE_VPS) + || (ps_type == HEVC_DCR_NALU_TYPE_SPS && nalu_type != HEVC_NALU_TYPE_SPS) + || (ps_type == HEVC_DCR_NALU_TYPE_PPS && nalu_type != HEVC_NALU_TYPE_PPS) + || (ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI && nalu_type != HEVC_NALU_TYPE_PREFIX_SEI) + || (ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI && nalu_type != HEVC_NALU_TYPE_SUFFIX_SEI) ) + return LSMASH_ERR_INVALID_DATA; + return 0; +} + +lsmash_dcr_nalu_appendable lsmash_check_hevc_dcr_nalu_appendable +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type, + void *_ps_data, + uint32_t ps_length +) +{ + uint8_t *ps_data = _ps_data; + if( !param ) + return DCR_NALU_APPEND_ERROR; + if( hevc_validate_dcr_nalu_type( ps_type, ps_data, ps_length ) < 0 ) + return DCR_NALU_APPEND_ERROR; + /* Check whether the same parameter set already exsits or not. */ + lsmash_entry_list_t *ps_list = hevc_get_parameter_set_list( param, ps_type ); + if( !ps_list || !ps_list->head ) + return DCR_NALU_APPEND_POSSIBLE; /* No parameter set */ + switch( nalu_check_same_ps_existence( ps_list, ps_data, ps_length ) ) + { + case 0 : break; + case 1 : return DCR_NALU_APPEND_DUPLICATED; /* The same parameter set already exists. */ + default : return DCR_NALU_APPEND_ERROR; /* An error occured. */ + } + /* Check the number of parameter sets in HEVC Decoder Configuration Record. */ + uint32_t ps_count; + if( nalu_get_ps_count( ps_list, &ps_count ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( (ps_type == HEVC_DCR_NALU_TYPE_VPS && ps_count >= HEVC_MAX_VPS_ID) + || (ps_type == HEVC_DCR_NALU_TYPE_SPS && ps_count >= HEVC_MAX_SPS_ID) + || (ps_type == HEVC_DCR_NALU_TYPE_PPS && ps_count >= HEVC_MAX_PPS_ID) + || (ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI && ps_count >= UINT16_MAX) + || (ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI && ps_count >= UINT16_MAX) ) + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; /* No more appendable parameter sets. */ + if( ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI + || ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) + return DCR_NALU_APPEND_POSSIBLE; + /* Check the maximum length of parameter sets in HEVC Decoder Configuration Record. */ + uint32_t max_ps_length; + if( nalu_get_max_ps_length( ps_list, &max_ps_length ) < 0 ) + return DCR_NALU_APPEND_ERROR; + max_ps_length = LSMASH_MAX( max_ps_length, ps_length ); + /* Check whether a new specific info is needed or not. */ + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[max_ps_length]; + uint8_t bs_buffer [max_ps_length]; + bs.buffer.data = bs_buffer; + bs.buffer.alloc = max_ps_length; + lsmash_bits_init( &bits, &bs ); + if( ps_type == HEVC_DCR_NALU_TYPE_PPS ) + { + /* PPS */ + uint8_t pps_id; + if( hevc_get_pps_id( ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH, &pps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return DCR_NALU_APPEND_ERROR; + if( ps->unused ) + continue; + uint8_t param_pps_id; + if( hevc_get_pps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_pps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( pps_id == param_pps_id ) + /* PPS that has the same pic_parameter_set_id already exists with different form. */ + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + } + return DCR_NALU_APPEND_POSSIBLE; + } + else if( ps_type == HEVC_DCR_NALU_TYPE_VPS ) + { + /* VPS */ + hevc_vps_t vps; + if( hevc_parse_vps_minimally( &bits, &vps, rbsp_buffer, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) < 0 ) + return DCR_NALU_APPEND_ERROR; + /* The value of profile_space must be identical in all the parameter sets in a single HEVC Decoder Configuration Record. */ + if( vps.ptl.general.profile_space != param->general_profile_space ) + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + /* FIXME */ + if( vps.ptl.general.profile_idc != param->general_profile_idc ) + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return DCR_NALU_APPEND_ERROR; + if( ps->unused ) + continue; + uint8_t param_vps_id; + if( hevc_get_vps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_vps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( param_vps_id == vps.video_parameter_set_id ) + /* VPS that has the same video_parameter_set_id already exists with different form. */ + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + } + return DCR_NALU_APPEND_POSSIBLE; + } + /* SPS */ + hevc_sps_t sps; + if( hevc_parse_sps_minimally( &bits, &sps, rbsp_buffer, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) < 0 ) + return DCR_NALU_APPEND_ERROR; + lsmash_bits_empty( &bits ); + /* The values of profile_space, chromaFormat, bitDepthLumaMinus8 and bitDepthChromaMinus8 + * must be identical in all the parameter sets in a single HEVC Decoder Configuration Record. */ + if( sps.ptl.general.profile_space != param->general_profile_space + || sps.chroma_format_idc != param->chromaFormat + || sps.bit_depth_luma_minus8 != param->bitDepthLumaMinus8 + || sps.bit_depth_chroma_minus8 != param->bitDepthChromaMinus8 ) + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + /* FIXME; If the sequence parameter sets are marked with different profiles, + * and the relevant profile compatibility flags are all zero, + * then the stream may need examination to determine which profile, if any, the stream conforms to. + * If the stream is not examined, or the examination reveals that there is no profile to which the stream conforms, + * then the stream must be split into two or more sub-streams with separate configuration records in which these rules can be met. */ +#if 0 + if( sps.ptl.general.profile_idc != param->general_profile_idc + && (sps.ptl.general.profile_compatibility_flags & param->general_profile_compatibility_flags) ) +#else + if( sps.ptl.general.profile_idc != param->general_profile_idc ) +#endif + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + /* Forbidden to duplicate SPS that has the same seq_parameter_set_id with different form within the same configuration record. */ + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return DCR_NALU_APPEND_ERROR; + if( ps->unused ) + continue; + uint8_t param_sps_id; + if( hevc_get_sps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_sps_id ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( param_sps_id == sps.seq_parameter_set_id ) + /* SPS that has the same seq_parameter_set_id already exists with different form. */ + return DCR_NALU_APPEND_NEW_DCR_REQUIRED; + if( entry == ps_list->head ) + { + /* Check if the cropped visual presentation sizes, the sample aspect ratios, the colour descriptions and + * the default display windows are different. */ + hevc_sps_t first_sps; + if( hevc_parse_sps_minimally( &bits, &first_sps, rbsp_buffer, + ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH ) < 0 ) + return DCR_NALU_APPEND_ERROR; + if( sps.cropped_width != first_sps.cropped_width + || sps.cropped_height != first_sps.cropped_height + || sps.vui.sar_width != first_sps.vui.sar_width + || sps.vui.sar_height != first_sps.vui.sar_height + || sps.vui.colour_primaries != first_sps.vui.colour_primaries + || sps.vui.transfer_characteristics != first_sps.vui.transfer_characteristics + || sps.vui.matrix_coeffs != first_sps.vui.matrix_coeffs + || sps.vui.video_full_range_flag != first_sps.vui.video_full_range_flag + || sps.vui.def_disp_win_offset.left .n != first_sps.vui.def_disp_win_offset.left .n + || sps.vui.def_disp_win_offset.right .n != first_sps.vui.def_disp_win_offset.right .n + || sps.vui.def_disp_win_offset.top .n != first_sps.vui.def_disp_win_offset.top .n + || sps.vui.def_disp_win_offset.bottom.n != first_sps.vui.def_disp_win_offset.bottom.n ) + return DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED; + } + } + return DCR_NALU_APPEND_POSSIBLE; +} + +static inline void hevc_specific_parameters_ready +( + lsmash_hevc_specific_parameters_t *param +) +{ + param->general_profile_compatibility_flags = ~UINT32_C(0); + param->general_constraint_indicator_flags = 0x0000FFFFFFFFFFFF; + param->min_spatial_segmentation_idc = 0x0FFF; + param->avgFrameRate = 0; /* unspecified average frame rate */ + param->constantFrameRate = 2; + param->numTemporalLayers = 0; + param->temporalIdNested = 1; +} + +static inline void hevc_specific_parameters_update_ptl +( + lsmash_hevc_specific_parameters_t *param, + hevc_ptl_t *ptl +) +{ + param->general_profile_space = ptl->general.profile_space; + param->general_tier_flag = LSMASH_MAX( param->general_tier_flag, ptl->general.tier_flag ); + param->general_profile_idc = ptl->general.profile_idc; + param->general_profile_compatibility_flags &= ptl->general.profile_compatibility_flags; + param->general_constraint_indicator_flags &= ((uint64_t)ptl->general.progressive_source_flag << 47) + | ((uint64_t)ptl->general.interlaced_source_flag << 46) + | ((uint64_t)ptl->general.non_packed_constraint_flag << 45) + | ((uint64_t)ptl->general.frame_only_constraint_flag << 44) + | ptl->general.reserved_zero_44bits; + param->general_level_idc = LSMASH_MAX( param->general_level_idc, ptl->general.level_idc ); +} + +static inline void hevc_reorder_parameter_set_ascending_id +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type, + lsmash_entry_list_t *ps_list, + uint8_t ps_id +) +{ + lsmash_entry_t *entry = NULL; + if( ps_id ) + for( int i = ps_id - 1; i; i-- ) + { + entry = hevc_get_ps_entry_from_param( param, ps_type, i ); + if( entry ) + break; + } + int append_head = 0; + if( !entry ) + { + /* Couldn't find any parameter set with lower identifier. + * Next, find parameter set with upper identifier. */ + int max_ps_id = ps_type == HEVC_DCR_NALU_TYPE_VPS ? HEVC_MAX_VPS_ID + : ps_type == HEVC_DCR_NALU_TYPE_SPS ? HEVC_MAX_SPS_ID + : HEVC_MAX_PPS_ID; + for( int i = ps_id + 1; i <= max_ps_id; i++ ) + { + entry = hevc_get_ps_entry_from_param( param, ps_type, i ); + if( entry ) + break; + } + if( entry ) + append_head = 1; + } + if( !entry ) + return; /* The new entry was appended to the tail. */ + lsmash_entry_t *new_entry = ps_list->tail; + if( append_head ) + { + /* before: entry[i > ps_id] ... -> prev_entry -> new_entry[ps_id] + * after: new_entry[ps_id] -> entry[i > ps_id] -> ... -> prev_entry */ + if( new_entry->prev ) + new_entry->prev->next = NULL; + new_entry->prev = NULL; + entry->prev = new_entry; + new_entry->next = entry; + return; + } + /* before: entry[i < ps_id] -> next_entry -> ... -> prev_entry -> new_entry[ps_id] + * after: entry[i < ps_id] -> new_entry[ps_id] -> next_entry -> ... -> prev_entry */ + if( new_entry->prev ) + new_entry->prev->next = NULL; + new_entry->prev = entry; + new_entry->next = entry->next; + if( entry->next ) + entry->next->prev = new_entry; + entry->next = new_entry; +} + +static inline int hevc_alloc_parameter_arrays +( + lsmash_hevc_specific_parameters_t *param +) +{ + assert( param ); + if( param->parameter_arrays ) + return 0; + lsmash_hevc_parameter_arrays_t *parameter_arrays = lsmash_malloc_zero( sizeof(lsmash_hevc_parameter_arrays_t) ); + if( !parameter_arrays ) + return LSMASH_ERR_MEMORY_ALLOC; + param->parameter_arrays = parameter_arrays; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_VPS ].array_completeness = 1; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_VPS ].NAL_unit_type = HEVC_NALU_TYPE_VPS; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SPS ].array_completeness = 1; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SPS ].NAL_unit_type = HEVC_NALU_TYPE_SPS; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PPS ].array_completeness = 1; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PPS ].NAL_unit_type = HEVC_NALU_TYPE_PPS; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PREFIX_SEI].array_completeness = 0; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PREFIX_SEI].NAL_unit_type = HEVC_NALU_TYPE_PREFIX_SEI; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SUFFIX_SEI].array_completeness = 0; + parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SUFFIX_SEI].NAL_unit_type = HEVC_NALU_TYPE_SUFFIX_SEI; + return 0; +} + +int lsmash_append_hevc_dcr_nalu +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type, + void *_ps_data, + uint32_t ps_length +) +{ + uint8_t *ps_data = _ps_data; + if( !param || !ps_data || ps_length < 2 ) + return LSMASH_ERR_FUNCTION_PARAM; + int err = hevc_alloc_parameter_arrays( param ); + if( err < 0 ) + return err; + hevc_parameter_array_t *ps_array = hevc_get_parameter_set_array( param, ps_type ); + if( !ps_array ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_entry_list_t *ps_list = ps_array->list; + if( ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI + || ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) + { + /* Append a SEI anyway. */ + isom_dcr_ps_entry_t *ps = isom_create_ps_entry( ps_data, ps_length ); + if( !ps ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( ps_list, ps ) < 0 ) + { + isom_remove_dcr_ps( ps ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; + } + if( ps_type != HEVC_DCR_NALU_TYPE_VPS + && ps_type != HEVC_DCR_NALU_TYPE_SPS + && ps_type != HEVC_DCR_NALU_TYPE_PPS ) + return LSMASH_ERR_FUNCTION_PARAM; + /* Check if the same parameter set identifier already exists. */ + uint8_t ps_id; + if( (err = hevc_get_ps_id( ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH, &ps_id, ps_type )) < 0 ) + return err; + lsmash_entry_t *entry = hevc_get_ps_entry_from_param( param, ps_type, ps_id ); + isom_dcr_ps_entry_t *ps = entry ? (isom_dcr_ps_entry_t *)entry->data : NULL; + if( ps && !ps->unused ) + /* The same parameter set identifier already exists. */ + return LSMASH_ERR_FUNCTION_PARAM; + int invoke_reorder; + if( ps ) + { + /* Reuse an already existed parameter set in the list. */ + ps->unused = 0; + if( ps->nalUnit != ps_data ) + { + /* The same address could be given when called by hevc_update_picture_info_for_slice(). */ + lsmash_free( ps->nalUnit ); + ps->nalUnit = ps_data; + } + ps->nalUnitLength = ps_length; + invoke_reorder = 0; + } + else + { + /* Create a new parameter set and append it into the list. */ + ps = isom_create_ps_entry( ps_data, ps_length ); + if( !ps ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( ps_list, ps ) < 0 ) + { + isom_remove_dcr_ps( ps ); + return LSMASH_ERR_MEMORY_ALLOC; + } + invoke_reorder = 1; + } + uint32_t ps_count; + if( (err = nalu_get_ps_count( ps_list, &ps_count )) < 0 ) + goto fail; + /* Update specific info with VPS, SPS or PPS. */ + { + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + uint8_t rbsp_buffer[ps_length]; + uint8_t buffer [ps_length]; + bs.buffer.data = buffer; + bs.buffer.alloc = ps_length; + lsmash_bits_init( &bits, &bs ); + if( ps_type == HEVC_DCR_NALU_TYPE_VPS ) + { + hevc_vps_t vps; + if( (err = hevc_parse_vps_minimally( &bits, &vps, rbsp_buffer, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH )) < 0 ) + goto fail; + if( ps_count == 1 ) + { + /* Initialize if not initialized yet. */ + lsmash_entry_list_t *sps_list = hevc_get_parameter_set_list( param, HEVC_DCR_NALU_TYPE_SPS ); + uint32_t sps_count; + if( (err = nalu_get_ps_count( sps_list, &sps_count )) < 0 ) + goto fail; + if( sps_count == 0 ) + hevc_specific_parameters_ready( param ); + } + hevc_specific_parameters_update_ptl( param, &vps.ptl ); + param->numTemporalLayers = LSMASH_MAX( param->numTemporalLayers, vps.max_sub_layers_minus1 + 1 ); + //param->temporalIdNested &= vps.temporal_id_nesting_flag; + } + else if( ps_type == HEVC_DCR_NALU_TYPE_SPS ) + { + hevc_sps_t sps; + if( (err = hevc_parse_sps_minimally( &bits, &sps, rbsp_buffer, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH )) < 0 ) + goto fail; + if( ps_count == 1 ) + { + /* Initialize if not initialized yet. */ + lsmash_entry_list_t *vps_list = hevc_get_parameter_set_list( param, HEVC_DCR_NALU_TYPE_VPS ); + uint32_t vps_count; + if( (err = nalu_get_ps_count( vps_list, &vps_count )) < 0 ) + goto fail; + if( vps_count == 0 ) + hevc_specific_parameters_ready( param ); + } + hevc_specific_parameters_update_ptl( param, &sps.ptl ); + param->min_spatial_segmentation_idc = LSMASH_MIN( param->min_spatial_segmentation_idc, sps.vui.min_spatial_segmentation_idc ); + param->chromaFormat = sps.chroma_format_idc; + param->bitDepthLumaMinus8 = sps.bit_depth_luma_minus8; + param->bitDepthChromaMinus8 = sps.bit_depth_chroma_minus8; + param->numTemporalLayers = LSMASH_MAX( param->numTemporalLayers, sps.max_sub_layers_minus1 + 1 ); + param->temporalIdNested &= sps.temporal_id_nesting_flag; + /* Check type of constant frame rate. */ + if( param->constantFrameRate ) + { + int cfr; + if( param->constantFrameRate == 2 ) + { + cfr = 1; + for( uint8_t i = 0; i <= sps.max_sub_layers_minus1; i++ ) + cfr &= sps.vui.hrd.fixed_pic_rate_general_flag[i]; + } + else + cfr = 0; + if( cfr ) + param->constantFrameRate = 2; + else + { + for( uint8_t i = 0; i <= sps.max_sub_layers_minus1; i++ ) + cfr |= sps.vui.hrd.fixed_pic_rate_general_flag[i]; + param->constantFrameRate = cfr; + } + } +#if 0 + /* FIXME: probably, we can get average frame rate according to C.3.3 Picture output. */ + if( param->constantFrameRate ) + { + uint64_t interval = 0; + for( uint8_t i = 0; i <= sps.max_sub_layers_minus1; i++ ) + interval += sps.vui.num_units_in_tick * sps.vui.hrd.elemental_duration_in_tc_minus1[i]; + uint64_t frame_rate; + if( interval ) + frame_rate = ((256 * (2 - sps.vui.field_seq_flag) * (uint64_t)sps.vui.time_scale) + * (sps.max_sub_layers_minus1 + 1)) / interval; + else + frame_rate = 0; + if( frame_rate != param->avgFrameRate && param->avgFrameRate ) + param->constantFrameRate = 0; + param->avgFrameRate = frame_rate; + } +#endif + } + else + { + hevc_pps_t pps; + if( (err = hevc_parse_pps_minimally( &bits, &pps, rbsp_buffer, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH )) < 0 ) + goto fail; + uint8_t parallelismType = 0; +#if 1 /* Replace 1 with 0 if parallelismType shall be set to 0 when min_spatial_segmentation_idc equal to 0. */ + + if( pps.entropy_coding_sync_enabled_flag ) + parallelismType = pps.tiles_enabled_flag ? 0 : 3; + else if( pps.tiles_enabled_flag ) + parallelismType = 2; + else + parallelismType = 1; +#else + /* Parse SPS and check the value of min_spatial_segmentation_idc is equal to zero or not. + * If the value is not equal to zero, update parallelismType appropriately. + * If corresponding SPS is not found, set 0 to parallelismType. */ + entry = hevc_get_ps_entry_from_param( param, HEVC_DCR_NALU_TYPE_SPS, pps.seq_parameter_set_id ); + if( entry && entry->data ) + { + ps = (isom_dcr_ps_entry_t *)entry->data; + lsmash_bits_t sps_bits = { 0 }; + lsmash_bs_t sps_bs = { 0 }; + uint8_t sps_rbsp_buffer[ ps->nalUnitLength ]; + uint8_t sps_buffer [ ps->nalUnitLength ]; + sps_bs.buffer.data = sps_buffer; + sps_bs.buffer.alloc = ps->nalUnitLength; + lsmash_bits_init( &sps_bits, &sps_bs ); + hevc_sps_t sps; + if( hevc_parse_sps_minimally( &sps_bits, &sps, sps_rbsp_buffer, + ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH ) == 0 ) + { + if( sps.vui.min_spatial_segmentation_idc ) + { + if( pps.entropy_coding_sync_enabled_flag ) + parallelismType = pps.tiles_enabled_flag ? 0 : 3; + else if( pps.tiles_enabled_flag ) + parallelismType = 2; + else + parallelismType = 1; + } + else + parallelismType = 0; + } + } +#endif + if( ps_count == 1 ) + param->parallelismType = parallelismType; + else if( param->parallelismType != parallelismType ) + param->parallelismType = 0; + } + } + if( invoke_reorder ) + /* Add a new parameter set in order of ascending parameter set identifier. */ + hevc_reorder_parameter_set_ascending_id( param, ps_type, ps_list, ps_id ); + return 0; +fail: + ps = (isom_dcr_ps_entry_t *)lsmash_get_entry_data( ps_list, ps_list->entry_count ); + if( ps ) + ps->unused = 1; + return err; +} + +int hevc_try_to_append_dcr_nalu +( + hevc_info_t *info, + lsmash_hevc_dcr_nalu_type ps_type, + void *_ps_data, + uint32_t ps_length +) +{ + uint8_t *ps_data = _ps_data; + lsmash_dcr_nalu_appendable ret = lsmash_check_hevc_dcr_nalu_appendable( &info->hvcC_param, ps_type, ps_data, ps_length ); + lsmash_hevc_specific_parameters_t *param; + switch( ret ) + { + case DCR_NALU_APPEND_ERROR : /* Error */ + return LSMASH_ERR_NAMELESS; + case DCR_NALU_APPEND_NEW_DCR_REQUIRED : /* Mulitiple sample description is needed. */ + case DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED : /* Mulitiple sample description is needed. */ + param = &info->hvcC_param_next; + info->hvcC_pending = 1; + break; + case DCR_NALU_APPEND_POSSIBLE : /* Appendable */ + param = info->hvcC_pending ? &info->hvcC_param_next : &info->hvcC_param; + break; + default : /* No need to append */ + return DCR_NALU_APPEND_DUPLICATED; + } + int err; + switch( ps_type ) + { + case HEVC_DCR_NALU_TYPE_VPS : + if( (err = hevc_parse_vps( info, info->buffer.rbsp, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH )) < 0 ) + return err; + break; + case HEVC_DCR_NALU_TYPE_SPS : + if( (err = hevc_parse_sps( info, info->buffer.rbsp, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH )) < 0 ) + return err; + break; + case HEVC_DCR_NALU_TYPE_PPS : + if( (err = hevc_parse_pps( info, info->buffer.rbsp, + ps_data + HEVC_MIN_NALU_HEADER_LENGTH, + ps_length - HEVC_MIN_NALU_HEADER_LENGTH )) < 0 ) + return err; + break; + default : + break; + } + return lsmash_append_hevc_dcr_nalu( param, ps_type, ps_data, ps_length ); +} + +static int hevc_move_dcr_nalu_entry +( + lsmash_hevc_specific_parameters_t *dst_data, + lsmash_hevc_specific_parameters_t *src_data, + lsmash_hevc_dcr_nalu_type ps_type +) +{ + lsmash_entry_list_t *src_ps_list = hevc_get_parameter_set_list( src_data, ps_type ); + lsmash_entry_list_t *dst_ps_list = hevc_get_parameter_set_list( dst_data, ps_type ); + assert( src_ps_list && dst_ps_list ); + for( lsmash_entry_t *src_entry = src_ps_list->head; src_entry; src_entry = src_entry->next ) + { + isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)src_entry->data; + if( !src_ps ) + continue; + uint8_t src_ps_id; + int err; + if( (err = hevc_get_ps_id( src_ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + src_ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, + &src_ps_id, ps_type )) < 0 ) + return err; + lsmash_entry_t *dst_entry; + for( dst_entry = dst_ps_list->head; dst_entry; dst_entry = dst_entry->next ) + { + isom_dcr_ps_entry_t *dst_ps = (isom_dcr_ps_entry_t *)dst_entry->data; + if( !dst_ps ) + continue; + uint8_t dst_ps_id; + if( (err = hevc_get_ps_id( dst_ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, + dst_ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, + &dst_ps_id, ps_type )) < 0 ) + return err; + if( dst_ps_id == src_ps_id ) + { + /* Replace the old parameter set with the new one. */ + assert( dst_entry->data != src_entry->data ); + isom_remove_dcr_ps( dst_ps ); + dst_entry->data = src_entry->data; + src_entry->data = NULL; + break; + } + } + if( !dst_entry ) + { + /* Move the parameter set. */ + if( lsmash_add_entry( dst_ps_list, src_ps ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + src_entry->data = NULL; + } + } + return 0; +} + +int hevc_move_pending_hvcC_param +( + hevc_info_t *info +) +{ + assert( info ); + if( !info->hvcC_pending ) + return 0; + /* Mark 'unused' on parameter sets within the decoder configuration record. */ + for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) + { + lsmash_entry_list_t *ps_list = hevc_get_parameter_set_list( &info->hvcC_param, i ); + assert( ps_list ); + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + continue; + ps->unused = 1; + } + } + /* Move the new parameter sets. */ + int err; + if( (err = hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_VPS )) < 0 + || (err = hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_SPS )) < 0 + || (err = hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_PPS )) < 0 + || (err = hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_PREFIX_SEI )) < 0 + || (err = hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_SUFFIX_SEI )) < 0 ) + return err; + /* Move to the pending. */ + lsmash_hevc_parameter_arrays_t *parameter_arrays = info->hvcC_param.parameter_arrays; /* Back up parameter arrays. */ + info->hvcC_param = info->hvcC_param_next; + info->hvcC_param.parameter_arrays = parameter_arrays; + /* No pending hvcC. */ + lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param_next ); + memset( &info->hvcC_param_next, 0, sizeof(lsmash_hevc_specific_parameters_t) ); + info->hvcC_pending = 0; + return 0; +} + +int lsmash_set_hevc_array_completeness +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type, + int array_completeness +) +{ + if( hevc_alloc_parameter_arrays( param ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + hevc_parameter_array_t *ps_array = hevc_get_parameter_set_array( param, ps_type ); + if( !ps_array ) + return LSMASH_ERR_FUNCTION_PARAM; + ps_array->array_completeness = array_completeness; + return 0; +} + +int lsmash_get_hevc_array_completeness +( + lsmash_hevc_specific_parameters_t *param, + lsmash_hevc_dcr_nalu_type ps_type, + int *array_completeness +) +{ + if( hevc_alloc_parameter_arrays( param ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + hevc_parameter_array_t *ps_array = hevc_get_parameter_set_array( param, ps_type ); + if( !ps_array ) + return LSMASH_ERR_FUNCTION_PARAM; + *array_completeness = ps_array->array_completeness; + return 0; +} + +static int hevc_parse_succeeded +( + hevc_info_t *info, + lsmash_hevc_specific_parameters_t *param +) +{ + int ret; + if( info->vps.present + && info->sps.present + && info->pps.present ) + { + *param = info->hvcC_param; + /* Avoid freeing parameter sets. */ + info->hvcC_param.parameter_arrays = NULL; + ret = 0; + } + else + ret = LSMASH_ERR_INVALID_DATA; + hevc_cleanup_parser( info ); + return ret; +} + +static inline int hevc_parse_failed +( + hevc_info_t *info, + int ret +) +{ + hevc_cleanup_parser( info ); + return ret; +} + +int lsmash_setup_hevc_specific_parameters_from_access_unit +( + lsmash_hevc_specific_parameters_t *param, + uint8_t *data, + uint32_t data_length +) +{ + if( !param || !data || data_length == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + hevc_info_t *info = &(hevc_info_t){ { 0 } }; + lsmash_bs_t *bs = &(lsmash_bs_t){ 0 }; + int err = lsmash_bs_set_empty_stream( bs, data, data_length ); + if( err < 0 ) + return err; + uint64_t sc_head_pos = nalu_find_first_start_code( bs ); + if( sc_head_pos == NALU_NO_START_CODE_FOUND ) + return LSMASH_ERR_INVALID_DATA; + if( (err = hevc_setup_parser( info, 1 )) < 0 ) + return hevc_parse_failed( info, err ); + hevc_stream_buffer_t *sb = &info->buffer; + hevc_slice_info_t *slice = &info->slice; + while( 1 ) + { + hevc_nalu_header_t nuh; + uint64_t start_code_length; + uint64_t trailing_zero_bytes; + uint64_t nalu_length = hevc_find_next_start_code( bs, &nuh, &start_code_length, &trailing_zero_bytes ); + if( start_code_length <= NALU_SHORT_START_CODE_LENGTH && lsmash_bs_is_end( bs, nalu_length ) ) + /* For the last NALU. This NALU already has been parsed. */ + return hevc_parse_succeeded( info, param ); + uint8_t nalu_type = nuh.nal_unit_type; + uint64_t next_sc_head_pos = sc_head_pos + + start_code_length + + nalu_length + + trailing_zero_bytes; + if( nalu_type == HEVC_NALU_TYPE_FD ) + { + /* We don't support streams with both filler and HRD yet. Otherwise, just skip filler. */ + if( info->sps.vui.hrd.present ) + return hevc_parse_failed( info, LSMASH_ERR_PATCH_WELCOME ); + } + else if( nalu_type <= HEVC_NALU_TYPE_RASL_R + || (nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && nalu_type <= HEVC_NALU_TYPE_CRA) + || (nalu_type >= HEVC_NALU_TYPE_VPS && nalu_type <= HEVC_NALU_TYPE_SUFFIX_SEI) ) + { + /* Increase the buffer if needed. */ + uint64_t possible_au_length = NALU_DEFAULT_NALU_LENGTH_SIZE + nalu_length; + if( sb->bank->buffer_size < possible_au_length + && (err = hevc_supplement_buffer( sb, NULL, 2 * possible_au_length )) < 0 ) + return hevc_parse_failed( info, err ); + /* Get the EBSP of the current NALU here. */ + uint8_t *nalu = lsmash_bs_get_buffer_data( bs ) + start_code_length; + if( nalu_type <= HEVC_NALU_TYPE_RSV_VCL31 ) + { + /* VCL NALU (slice) */ + hevc_slice_info_t prev_slice = *slice; + if( (err = hevc_parse_slice_segment_header( info, &nuh, sb->rbsp, nalu + nuh.length, nalu_length - nuh.length )) < 0 ) + return hevc_parse_failed( info, err ); + if( prev_slice.present ) + { + /* Check whether the AU that contains the previous VCL NALU completed or not. */ + if( hevc_find_au_delimit_by_slice_info( info, slice, &prev_slice ) ) + /* The current NALU is the first VCL NALU of the primary coded picture of a new AU. + * Therefore, the previous slice belongs to that new AU. */ + return hevc_parse_succeeded( info, param ); + } + slice->present = 1; + } + else + { + if( hevc_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) + /* The last slice belongs to the AU you want at this time. */ + return hevc_parse_succeeded( info, param ); + switch( nalu_type ) + { + case HEVC_NALU_TYPE_VPS : + if( (err = hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_VPS, nalu, nalu_length )) < 0 ) + return hevc_parse_failed( info, err ); + break; + case HEVC_NALU_TYPE_SPS : + if( (err = hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_SPS, nalu, nalu_length )) < 0 ) + return hevc_parse_failed( info, err ); + break; + case HEVC_NALU_TYPE_PPS : + if( (err = hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_PPS, nalu, nalu_length )) < 0 ) + return hevc_parse_failed( info, err ); + break; + default : + break; + } + } + } + /* Move to the first byte of the next start code. */ + info->prev_nalu_type = nalu_type; + if( lsmash_bs_read_seek( bs, next_sc_head_pos, SEEK_SET ) != next_sc_head_pos ) + return hevc_parse_failed( info, LSMASH_ERR_NAMELESS ); + /* Check if no more data to read from the stream. */ + if( !lsmash_bs_is_end( bs, NALU_SHORT_START_CODE_LENGTH ) ) + sc_head_pos = next_sc_head_pos; + else + return hevc_parse_succeeded( info, param ); + } +} + +int hevc_construct_specific_parameters +( + lsmash_codec_specific_t *dst, + lsmash_codec_specific_t *src +) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_hevc_specific_parameters_t *param = (lsmash_hevc_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + if( hevc_alloc_parameter_arrays( param ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + int err = lsmash_bs_import_data( bs, data, src->size - (data - src->data.unstructured) ); + if( err < 0 ) + goto fail; + if( lsmash_bs_get_byte( bs ) != HVCC_CONFIGURATION_VERSION ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; /* We don't support configurationVersion other than HVCC_CONFIGURATION_VERSION. */ + } + uint8_t temp8 = lsmash_bs_get_byte( bs ); + param->general_profile_space = (temp8 >> 6) & 0x03; + param->general_tier_flag = (temp8 >> 5) & 0x01; + param->general_profile_idc = temp8 & 0x1F; + param->general_profile_compatibility_flags = lsmash_bs_get_be32( bs ); + uint32_t temp32 = lsmash_bs_get_be32( bs ); + uint16_t temp16 = lsmash_bs_get_be16( bs ); + param->general_constraint_indicator_flags = ((uint64_t)temp32 << 16) | temp16; + param->general_level_idc = lsmash_bs_get_byte( bs ); + param->min_spatial_segmentation_idc = lsmash_bs_get_be16( bs ) & 0x0FFF; + param->parallelismType = lsmash_bs_get_byte( bs ) & 0x03; + param->chromaFormat = lsmash_bs_get_byte( bs ) & 0x03; + param->bitDepthLumaMinus8 = lsmash_bs_get_byte( bs ) & 0x07; + param->bitDepthChromaMinus8 = lsmash_bs_get_byte( bs ) & 0x07; + param->avgFrameRate = lsmash_bs_get_be16( bs ); + temp8 = lsmash_bs_get_byte( bs ); + param->constantFrameRate = (temp8 >> 6) & 0x03; + param->numTemporalLayers = (temp8 >> 3) & 0x07; + param->temporalIdNested = (temp8 >> 2) & 0x01; + param->lengthSizeMinusOne = temp8 & 0x03; + uint8_t numOfArrays = lsmash_bs_get_byte( bs ); + for( uint8_t i = 0; i < numOfArrays; i++ ) + { + hevc_parameter_array_t param_array; + memset( ¶m_array, 0, sizeof(hevc_parameter_array_t) ); + temp8 = lsmash_bs_get_byte( bs ); + param_array.array_completeness = (temp8 >> 7) & 0x01; + param_array.NAL_unit_type = temp8 & 0x3F; + param_array.list->entry_count = lsmash_bs_get_be16( bs ); + if( param_array.NAL_unit_type == HEVC_NALU_TYPE_VPS + || param_array.NAL_unit_type == HEVC_NALU_TYPE_SPS + || param_array.NAL_unit_type == HEVC_NALU_TYPE_PPS + || param_array.NAL_unit_type == HEVC_NALU_TYPE_PREFIX_SEI + || param_array.NAL_unit_type == HEVC_NALU_TYPE_SUFFIX_SEI ) + { + if( (err = nalu_get_dcr_ps( bs, param_array.list, param_array.list->entry_count )) < 0 ) + goto fail; + } + else + for( uint16_t j = 0; j < param_array.list->entry_count; j++ ) + { + uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); + lsmash_bs_skip_bytes( bs, nalUnitLength ); /* nalUnit */ + } + switch( param_array.NAL_unit_type ) + { + case HEVC_NALU_TYPE_VPS : + param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_VPS] = param_array; + break; + case HEVC_NALU_TYPE_SPS : + param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SPS] = param_array; + break; + case HEVC_NALU_TYPE_PPS : + param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PPS] = param_array; + break; + case HEVC_NALU_TYPE_PREFIX_SEI : + param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PREFIX_SEI] = param_array; + break; + case HEVC_NALU_TYPE_SUFFIX_SEI : + param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SUFFIX_SEI] = param_array; + break; + default : + /* Discard unknown NALUs. */ + break; + } + } + lsmash_bs_cleanup( bs ); + return 0; +fail: + lsmash_bs_cleanup( bs ); + return err; +} + +int hevc_print_codec_specific +( + FILE *fp, + lsmash_file_t *file, + isom_box_t *box, + int level +) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: HEVC Configuration Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + uint8_t *data = box->binary; + uint32_t offset = isom_skip_box_common( &data ); + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + int err = lsmash_bs_import_data( bs, data, box->size - offset ); + if( err < 0 ) + { + lsmash_bs_cleanup( bs ); + return err; + } + uint8_t configurationVersion = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "configurationVersion = %"PRIu8"\n", configurationVersion ); + if( configurationVersion != HVCC_CONFIGURATION_VERSION ) + { + lsmash_bs_cleanup( bs ); + return 0; + } + uint8_t temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "general_profile_space = %"PRIu8"\n", (temp8 >> 6) & 0x03 ); + lsmash_ifprintf( fp, indent, "general_tier_flag = %"PRIu8"\n", (temp8 >> 5) & 0x01 ); + lsmash_ifprintf( fp, indent, "general_profile_idc = %"PRIu8"\n", temp8 & 0x1F ); + lsmash_ifprintf( fp, indent, "general_profile_compatibility_flags = 0x%08"PRIx32"\n", lsmash_bs_get_be32( bs ) ); + uint32_t temp32 = lsmash_bs_get_be32( bs ); + uint16_t temp16 = lsmash_bs_get_be16( bs ); + lsmash_ifprintf( fp, indent, "general_constraint_indicator_flags = 0x%012"PRIx64"\n", ((uint64_t)temp32 << 16) | temp16 ); + uint8_t general_level_idc = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "general_level_idc = %"PRIu8" (Level %g)\n", general_level_idc, general_level_idc / 30.0 ); + temp16 = lsmash_bs_get_be16( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp16 >> 12) & 0x0F ); + lsmash_ifprintf( fp, indent, "min_spatial_segmentation_idc = %"PRIu16"\n", temp16 & 0x0FFF ); + temp8 = lsmash_bs_get_byte( bs ); + uint8_t parallelismType = temp8 & 0x03; + static const char *parallelism_table[4] = + { + "Mixed types or Unknown", + "Slice based", + "Tile based", + "Entropy coding synchronization based / WPP: Wavefront Parallel Processing" + }; + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); + lsmash_ifprintf( fp, indent, "parallelismType = %"PRIu8" (%s)\n", parallelismType, + parallelism_table[parallelismType] ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); + lsmash_ifprintf( fp, indent, "chromaFormat = %"PRIu8"\n", temp8 & 0x03 ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); + lsmash_ifprintf( fp, indent, "bitDepthLumaMinus8 = %"PRIu8"\n", temp8 & 0x07 ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); + lsmash_ifprintf( fp, indent, "bitDepthChromaMinus8 = %"PRIu8"\n", temp8 & 0x07 ); + lsmash_ifprintf( fp, indent, "avgFrameRate = %"PRIu16"\n", lsmash_bs_get_be16( bs ) ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "constantFrameRate = %"PRIu8"\n", (temp8 >> 6) & 0x03 ); + lsmash_ifprintf( fp, indent, "numTemporalLayers = %"PRIu8"\n", (temp8 >> 3) & 0x07 ); + lsmash_ifprintf( fp, indent, "temporalIdNested = %"PRIu8"\n", (temp8 >> 2) & 0x01 ); + lsmash_ifprintf( fp, indent, "lengthSizeMinusOne = %"PRIu8"\n", temp8 & 0x03 ); + uint8_t numOfArrays = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, indent, "numOfArrays = %"PRIu8"\n", numOfArrays ); + for( uint8_t i = 0; i < numOfArrays; i++ ) + { + int array_indent = indent + 1; + lsmash_ifprintf( fp, array_indent++, "array[%"PRIu8"]\n", i ); + temp8 = lsmash_bs_get_byte( bs ); + lsmash_ifprintf( fp, array_indent, "array_completeness = %"PRIu8"\n", (temp8 >> 7) & 0x01 ); + lsmash_ifprintf( fp, array_indent, "reserved = %"PRIu8"\n", (temp8 >> 6) & 0x01 ); + lsmash_ifprintf( fp, array_indent, "NAL_unit_type = %"PRIu8"\n", temp8 & 0x3F ); + uint16_t numNalus = lsmash_bs_get_be16( bs ); + lsmash_ifprintf( fp, array_indent, "numNalus = %"PRIu16"\n", numNalus ); + for( uint16_t j = 0; j < numNalus; j++ ) + { + uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); + lsmash_bs_skip_bytes( bs, nalUnitLength ); + } + } + lsmash_bs_cleanup( bs ); + return 0; +} + +static inline int hevc_copy_dcr_nalu_array +( + lsmash_hevc_specific_parameters_t *dst_data, + lsmash_hevc_specific_parameters_t *src_data, + lsmash_hevc_dcr_nalu_type ps_type +) +{ + hevc_parameter_array_t *src_ps_array = hevc_get_parameter_set_array( src_data, ps_type ); + hevc_parameter_array_t *dst_ps_array = hevc_get_parameter_set_array( dst_data, ps_type ); + assert( src_ps_array && dst_ps_array ); + dst_ps_array->array_completeness = src_ps_array->array_completeness; + dst_ps_array->NAL_unit_type = src_ps_array->NAL_unit_type; + lsmash_entry_list_t *src_ps_list = src_ps_array->list; + lsmash_entry_list_t *dst_ps_list = dst_ps_array->list; + for( lsmash_entry_t *entry = src_ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)entry->data; + if( !src_ps || src_ps->unused ) + continue; + isom_dcr_ps_entry_t *dst_ps = isom_create_ps_entry( src_ps->nalUnit, src_ps->nalUnitLength ); + if( !dst_ps ) + { + lsmash_destroy_hevc_parameter_arrays( dst_data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( lsmash_add_entry( dst_ps_list, dst_ps ) < 0 ) + { + lsmash_destroy_hevc_parameter_arrays( dst_data ); + isom_remove_dcr_ps( dst_ps ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + return 0; +} + +int hevc_copy_codec_specific +( + lsmash_codec_specific_t *dst, + lsmash_codec_specific_t *src +) +{ + assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); + assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); + lsmash_hevc_specific_parameters_t *src_data = (lsmash_hevc_specific_parameters_t *)src->data.structured; + lsmash_hevc_specific_parameters_t *dst_data = (lsmash_hevc_specific_parameters_t *)dst->data.structured; + lsmash_destroy_hevc_parameter_arrays( dst_data ); + *dst_data = *src_data; + if( !src_data->parameter_arrays ) + return 0; + dst_data->parameter_arrays = lsmash_malloc_zero( sizeof(lsmash_hevc_parameter_arrays_t) ); + if( !dst_data->parameter_arrays ) + return LSMASH_ERR_MEMORY_ALLOC; + for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) + { + int err = hevc_copy_dcr_nalu_array( dst_data, src_data, (lsmash_hevc_dcr_nalu_type)i ); + if( err < 0 ) + return err; + } + return 0; +} diff -Nru l-smash-1.9.1/codecs/hevc.h l-smash-2.3.0/codecs/hevc.h --- l-smash-1.9.1/codecs/hevc.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/hevc.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,447 @@ +/***************************************************************************** + * hevc.h: + ***************************************************************************** + * Copyright (C) 2013-2014 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. */ + +enum +{ + HEVC_NALU_TYPE_TRAIL_N = 0, + HEVC_NALU_TYPE_TRAIL_R = 1, + HEVC_NALU_TYPE_TSA_N = 2, + HEVC_NALU_TYPE_TSA_R = 3, + HEVC_NALU_TYPE_STSA_N = 4, + HEVC_NALU_TYPE_STSA_R = 5, + HEVC_NALU_TYPE_RADL_N = 6, + HEVC_NALU_TYPE_RADL_R = 7, + HEVC_NALU_TYPE_RASL_N = 8, + HEVC_NALU_TYPE_RASL_R = 9, + HEVC_NALU_TYPE_RSV_VCL_R15 = 15, + HEVC_NALU_TYPE_BLA_W_LP = 16, + HEVC_NALU_TYPE_BLA_W_RADL = 17, + HEVC_NALU_TYPE_BLA_N_LP = 18, + HEVC_NALU_TYPE_IDR_W_RADL = 19, + HEVC_NALU_TYPE_IDR_N_LP = 20, + HEVC_NALU_TYPE_CRA = 21, + HEVC_NALU_TYPE_RSV_IRAP_VCL22 = 22, + HEVC_NALU_TYPE_RSV_IRAP_VCL23 = 23, + HEVC_NALU_TYPE_RSV_VCL31 = 31, + HEVC_NALU_TYPE_VPS = 32, + HEVC_NALU_TYPE_SPS = 33, + HEVC_NALU_TYPE_PPS = 34, + HEVC_NALU_TYPE_AUD = 35, + HEVC_NALU_TYPE_EOS = 36, + HEVC_NALU_TYPE_EOB = 37, + HEVC_NALU_TYPE_FD = 38, + HEVC_NALU_TYPE_PREFIX_SEI = 39, + HEVC_NALU_TYPE_SUFFIX_SEI = 40, + HEVC_NALU_TYPE_RSV_NVCL41 = 41, + HEVC_NALU_TYPE_RSV_NVCL44 = 44, + HEVC_NALU_TYPE_RSV_NVCL47 = 47, + HEVC_NALU_TYPE_UNSPEC48 = 48, + HEVC_NALU_TYPE_UNSPEC55 = 55, + HEVC_NALU_TYPE_UNSPEC63 = 63, + HEVC_NALU_TYPE_UNKNOWN = 64 +}; + +typedef struct +{ + uint8_t array_completeness; + uint8_t NAL_unit_type; + lsmash_entry_list_t list[1]; +} hevc_parameter_array_t; + +struct lsmash_hevc_parameter_arrays_tag +{ + hevc_parameter_array_t ps_array[HEVC_DCR_NALU_TYPE_NUM]; +}; + +typedef struct +{ + unsigned forbidden_zero_bit : 1; + unsigned nal_unit_type : 7; /* +1 bit for HEVC_NALU_TYPE_UNKNOWN */ + unsigned nuh_layer_id : 6; + unsigned TemporalId : 3; + unsigned length : 15; +} hevc_nalu_header_t; + +/* Profile, Tier and Level */ +typedef struct +{ + uint8_t profile_space; + uint8_t tier_flag; + uint8_t profile_idc; + uint32_t profile_compatibility_flags; + uint8_t progressive_source_flag; + uint8_t interlaced_source_flag; + uint8_t non_packed_constraint_flag; + uint8_t frame_only_constraint_flag; + uint64_t reserved_zero_44bits; + uint8_t level_idc; +} hevc_ptl_common_t; + +typedef struct +{ + hevc_ptl_common_t general; + hevc_ptl_common_t sub_layer[6]; +} hevc_ptl_t; + +/* HRD (Hypothetical Reference Decoder) */ +typedef struct +{ + uint8_t present; + uint8_t CpbDpbDelaysPresentFlag; + uint8_t sub_pic_hrd_params_present_flag; + uint8_t du_cpb_removal_delay_increment_length; + uint8_t sub_pic_cpb_params_in_pic_timing_sei_flag; + uint8_t dpb_output_delay_du_length; + uint8_t au_cpb_removal_delay_length; + uint8_t dpb_output_delay_length; + uint8_t fixed_pic_rate_general_flag[7]; + uint16_t elemental_duration_in_tc [7]; +} hevc_hrd_t; + +/* VPS (Video Parameter Set) */ +typedef struct +{ + uint8_t present; + uint8_t video_parameter_set_id; + uint8_t max_sub_layers_minus1; + uint8_t temporal_id_nesting_flag; + uint8_t timing_info_present_flag; + uint8_t frame_field_info_present_flag; + uint16_t num_hrd_parameters; + hevc_ptl_t ptl; + hevc_hrd_t hrd[2]; +} hevc_vps_t; + +typedef struct +{ + uint8_t present; + uint16_t sar_width; + uint16_t sar_height; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coeffs; + uint8_t field_seq_flag; + uint8_t frame_field_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint16_t min_spatial_segmentation_idc; + lsmash_crop_t def_disp_win_offset; + hevc_hrd_t hrd; +} hevc_vui_t; + +/* Short term reference picture sets */ +typedef struct +{ + uint8_t NumNegativePics; + uint8_t NumPositivePics; + uint8_t NumDeltaPocs; + uint8_t UsedByCurrPicS0[16]; + uint8_t UsedByCurrPicS1[16]; + int32_t DeltaPocS0[16]; + int32_t DeltaPocS1[16]; +} hevc_st_rps_t; + +/* SPS (Sequence Parameter Set) */ +typedef struct +{ + uint8_t present; + uint8_t video_parameter_set_id; + uint8_t max_sub_layers_minus1; + uint8_t temporal_id_nesting_flag; + hevc_ptl_t ptl; + uint8_t seq_parameter_set_id; + uint8_t chroma_format_idc; + uint8_t separate_colour_plane_flag; + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + uint8_t log2_max_pic_order_cnt_lsb; + uint8_t num_short_term_ref_pic_sets; + uint8_t long_term_ref_pics_present_flag; + uint8_t num_long_term_ref_pics_sps; + uint8_t temporal_mvp_enabled_flag; + uint32_t cropped_width; + uint32_t cropped_height; + uint32_t PicWidthInCtbsY; + uint32_t PicHeightInCtbsY; + uint64_t PicSizeInCtbsY; + hevc_st_rps_t st_rps[65]; + hevc_vui_t vui; +} hevc_sps_t; + +/* PPS (Picture Parameter Set) */ +typedef struct +{ + uint8_t present; + uint8_t pic_parameter_set_id; + uint8_t seq_parameter_set_id; + uint8_t dependent_slice_segments_enabled_flag; + uint8_t output_flag_present_flag; + uint8_t num_extra_slice_header_bits; + uint8_t tiles_enabled_flag; + uint8_t entropy_coding_sync_enabled_flag; + uint32_t num_tile_columns_minus1; + uint32_t num_tile_rows_minus1; +#define SIZEOF_PPS_EXCLUDING_HEAP (sizeof(hevc_pps_t) - offsetof( hevc_pps_t, col_alloc_size )) + size_t col_alloc_size; + size_t row_alloc_size; + uint32_t *colWidth; + uint32_t *colBd; + uint32_t *rowHeight; + uint32_t *rowBd; +} hevc_pps_t; + +/* SEI (Supplemental Enhancement Information) */ +typedef struct +{ + uint8_t present; + uint8_t pic_struct; +} hevc_pic_timing_t; + +typedef struct +{ + uint8_t present; + uint8_t broken_link_flag; + int32_t recovery_poc_cnt; +} hevc_recovery_point_t; + +typedef struct +{ + hevc_pic_timing_t pic_timing; + hevc_recovery_point_t recovery_point; +} hevc_sei_t; + +/* Slice segment */ +typedef struct +{ + uint8_t present; + uint8_t nalu_type; + uint8_t TemporalId; + uint8_t type; + uint8_t video_parameter_set_id; + uint8_t seq_parameter_set_id; + uint8_t pic_parameter_set_id; + uint8_t first_slice_segment_in_pic_flag; + uint8_t dependent_slice_segment_flag; + uint64_t segment_address; + int32_t pic_order_cnt_lsb; +} hevc_slice_info_t; + +/* Picture */ +typedef enum +{ + HEVC_PICTURE_TYPE_I = 0, + HEVC_PICTURE_TYPE_I_P = 1, + HEVC_PICTURE_TYPE_I_P_B = 2, + HEVC_PICTURE_TYPE_IDR = 3, + HEVC_PICTURE_TYPE_CRA = 4, + HEVC_PICTURE_TYPE_BLA = 5, + HEVC_PICTURE_TYPE_NONE = 6, +} hevc_picture_type; + +typedef struct +{ + hevc_picture_type type; + uint8_t irap; /* 1: IDR, CRA or BLA picture */ + uint8_t idr; /* 1: IDR picture */ + uint8_t broken_link; /* 1: BLA picture or picture with broken link flag */ + uint8_t radl; /* 1: RADL picture */ + uint8_t rasl; /* 1: RASL picture */ + uint8_t sublayer_nonref; /* 1: sub-layer non-reference picture */ + uint8_t closed_rap; /* 1: no undecodable leading picture in CVS */ + uint8_t random_accessible;/* 1: RAP or starting point of GDR */ + uint8_t TemporalId; + uint8_t independent; /* 1: intra coded picture */ + uint8_t field_coded; /* 1: field coded picture */ + uint8_t pic_parameter_set_id; + uint8_t has_primary; /* 1: an independent slice segment is present. */ + uint8_t delta; + /* POC */ + uint16_t poc_lsb; + int32_t poc; + int32_t tid0_poc_msb; + int32_t tid0_poc_lsb; + /* */ + int32_t recovery_poc_cnt; +} hevc_picture_info_t; + +/* Access unit */ +typedef struct +{ + uint8_t *data; + uint8_t *incomplete_data; + uint32_t length; + uint32_t incomplete_length; + uint32_t number; + uint8_t TemporalId; + hevc_picture_info_t picture; +} hevc_access_unit_t; + +typedef struct hevc_info_tag hevc_info_t; + +typedef struct +{ + lsmash_multiple_buffers_t *bank; + uint8_t *rbsp; +} hevc_stream_buffer_t; + +struct hevc_info_tag +{ + lsmash_hevc_specific_parameters_t hvcC_param; + lsmash_hevc_specific_parameters_t hvcC_param_next; + hevc_nalu_header_t nuh; + lsmash_entry_list_t vps_list[1]; + lsmash_entry_list_t sps_list[1]; + lsmash_entry_list_t pps_list[1]; + hevc_vps_t vps; /* active VPS */ + hevc_sps_t sps; /* active SPS */ + hevc_pps_t pps; /* active PPS */ + hevc_sei_t sei; /* active SEI */ + hevc_slice_info_t slice; /* active slice */ + hevc_access_unit_t au; + uint8_t prev_nalu_type; + uint8_t hvcC_pending; + uint8_t eos; /* end of sequence */ + uint64_t ebsp_head_pos; + lsmash_bits_t *bits; + hevc_stream_buffer_t buffer; +}; + +int hevc_setup_parser +( + hevc_info_t *info, + int parse_only +); + +void hevc_cleanup_parser +( + hevc_info_t *info +); + +uint64_t hevc_find_next_start_code +( + lsmash_bs_t *bs, + hevc_nalu_header_t *nuh, + uint64_t *start_code_length, + uint64_t *trailing_zero_bytes +); + +int hevc_calculate_poc +( + hevc_info_t *info, + hevc_picture_info_t *picture, + hevc_picture_info_t *prev_picture +); + +void hevc_update_picture_info_for_slice +( + hevc_info_t *info, + hevc_picture_info_t *picture, + hevc_slice_info_t *slice +); + +void hevc_update_picture_info +( + hevc_info_t *info, + hevc_picture_info_t *picture, + hevc_slice_info_t *slice, + hevc_sps_t *sps, + hevc_sei_t *sei +); + +int hevc_find_au_delimit_by_slice_info +( + hevc_info_t *info, + hevc_slice_info_t *slice, + hevc_slice_info_t *prev_slice +); + +int hevc_find_au_delimit_by_nalu_type +( + uint8_t nalu_type, + uint8_t prev_nalu_type +); + +int hevc_supplement_buffer +( + hevc_stream_buffer_t *hb, + hevc_access_unit_t *au, + uint32_t size +); + +int hevc_parse_vps +( + hevc_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int hevc_parse_sps +( + hevc_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int hevc_parse_pps +( + hevc_info_t *info, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int hevc_parse_sei +( + lsmash_bits_t *bits, + hevc_vps_t *vps, + hevc_sps_t *sps, + hevc_sei_t *sei, + hevc_nalu_header_t *nuh, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); +int hevc_parse_slice_segment_header +( + hevc_info_t *info, + hevc_nalu_header_t *nuh, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +); + +int hevc_try_to_append_dcr_nalu +( + hevc_info_t *info, + lsmash_hevc_dcr_nalu_type ps_type, + void *ps_data, + uint32_t ps_length +); + +int hevc_move_pending_hvcC_param +( + hevc_info_t *info +); diff -Nru l-smash-1.9.1/codecs/mp4a.c l-smash-2.3.0/codecs/mp4a.c --- l-smash-1.9.1/codecs/mp4a.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/mp4a.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1131 @@ +/***************************************************************************** + * mp4a.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#define MP4A_INTERNAL +#include "core/box.h" + +#include "mp4a.h" +#include "description.h" + +#include +#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_sampling_frequency_table[13][5] = { + /* threshold, exact, idx_for_ga, idx_for_sbr, idx */ + { 92017, 96000, 0x0, 0xF, 0x0 }, /* SBR is not allowed */ + { 75132, 88200, 0x1, 0xF, 0x1 }, /* SBR is not allowed */ + { 55426, 64000, 0x2, 0xF, 0x2 }, /* SBR is not allowed */ + { 46009, 48000, 0x3, 0x0, 0x3 }, + { 37566, 44100, 0x4, 0x1, 0x4 }, + { 27713, 32000, 0x5, 0x2, 0x5 }, + { 23004, 24000, 0x6, 0x3, 0x6 }, + { 18783, 22050, 0x7, 0x4, 0x7 }, + { 13856, 16000, 0x8, 0x5, 0x8 }, + { 11502, 12000, 0x9, 0x6, 0x9 }, + { 9391, 11025, 0xA, 0x7, 0xA }, + { 8000, 8000, 0xB, 0x8, 0xB }, + { 0, 7350, 0xB, 0xF, 0xC } /* samplingFrequencyIndex for GASpecificConfig is 0xB (same as 8000Hz). */ +}; + +/* ISO/IEC 14496-3 Interface to ISO/IEC 14496-1 (MPEG-4 Systems), Syntax of AudioSpecificConfig(). */ +/* This structure is represent of regularized AudioSpecificConfig. */ +/* for actual definition, see Syntax of GetAudioObjectType() for audioObjectType and extensionAudioObjectType. */ +typedef struct +{ + lsmash_mp4a_aac_sbr_mode sbr_mode; /* L-SMASH's original, including sbrPresent flag. */ + lsmash_mp4a_AudioObjectType audioObjectType; + unsigned samplingFrequencyIndex : 4; + unsigned samplingFrequency : 24; + unsigned channelConfiguration : 4; + lsmash_mp4a_AudioObjectType extensionAudioObjectType; + unsigned extensionSamplingFrequencyIndex : 4; + unsigned extensionSamplingFrequency : 24; + unsigned extensionChannelConfiguration : 4; + /* 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 +{ + unsigned frameLengthFlag : 1; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */ + unsigned dependsOnCoreCoder : 1; /* FIXME: used if scalable AAC. */ + unsigned coreCoderDelay : 14; + unsigned extensionFlag : 1; /* 1bit, 1 if ErrorResilience */ + /* if( !channelConfiguration ){ */ + void* program_config_element; /* currently not supported. */ + /* } */ + /* + // we do not support AAC_scalable + if( (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_AAC_scalable) || (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable) ){ + uint8_t layerNr; // 3bits + } + */ + /* + // we do not support special AACs + if( extensionFlag ){ + if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_BSAC ){ + uint8_t numOfSubFrame; // 5bits + uint8_t layer_length; // 11bits + } + if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP + || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD + ){ + uint8_t aacSectionDataResilienceFlag; // 1bit + uint8_t aacScalefactorDataResilienceFlag; // 1bit + uint8_t aacSpectralDataResilienceFlag; // 1bit + } + uint8_t extensionFlag3; // 1bit + if( extensionFlag3 ){ + // tbd in version 3 + } + } + */ +} mp4a_GASpecificConfig_t; + +/* ISO/IEC 14496-3 MPEG_1_2_SpecificConfig */ +typedef struct +{ + uint8_t extension; /* shall be 0. */ +} mp4a_MPEG_1_2_SpecificConfig_t; + +/* ISO/IEC 14496-3 ALSSpecificConfig */ +typedef struct +{ + uint32_t size; + uint8_t *data; + uint32_t als_id; + uint32_t samp_freq; + uint32_t samples; + uint16_t channels; + unsigned file_type : 3; + unsigned resolution : 3; + unsigned floating : 1; + unsigned msb_first : 1; + uint16_t frame_length; + uint8_t random_access; + unsigned ra_flag : 2; + unsigned adapt_order : 1; + unsigned coef_table : 2; + unsigned long_term_prediction : 1; + unsigned max_order : 10; + unsigned block_switching : 2; + unsigned bgmc_mode : 1; + unsigned sb_part : 1; + unsigned joint_stereo : 1; + unsigned mc_coding : 1; + unsigned chan_config : 1; + unsigned chan_sort : 1; + unsigned crc_enabled : 1; + unsigned RLSLMS : 1; + unsigned reserved : 5; + unsigned aux_data_enabled : 1; +} mp4a_ALSSpecificConfig_t; + +static inline void mp4a_remove_GASpecificConfig( mp4a_GASpecificConfig_t* gasc ) +{ + debug_if( !gasc ) + return; + lsmash_free( gasc->program_config_element ); + lsmash_free( gasc ); +} + +static inline void mp4a_remove_MPEG_1_2_SpecificConfig( mp4a_MPEG_1_2_SpecificConfig_t* mpeg_1_2_sc ) +{ + lsmash_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: + lsmash_free( asc->deepAudioSpecificConfig ); + break; + } + lsmash_free( asc ); +} + +/* ADIF/PCE(program config element) style GASpecificConfig is not not supported. */ +/* channelConfig/samplingFrequencyIndex will be used when we support ADIF/PCE style GASpecificConfig. */ +static mp4a_GASpecificConfig_t* mp4a_create_GASpecificConfig( uint8_t samplingFrequencyIndex, uint8_t channelConfig, lsmash_mp4a_AudioObjectType aot ) +{ + debug_if( aot != MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC + && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_SSR && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LTP + && aot != MP4A_AUDIO_OBJECT_TYPE_TwinVQ ) + return NULL; + if( samplingFrequencyIndex > 0xB || channelConfig == 0 || channelConfig == 7 ) + return NULL; + mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_GASpecificConfig_t) ); + if( !gasc ) + return NULL; + gasc->frameLengthFlag = 0; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */ + gasc->dependsOnCoreCoder = 0; /* FIXME: used if scalable AAC. */ + switch( aot ){ + case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN: + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR: + case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP: + case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable: + case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: + gasc->extensionFlag = 0; + break; + /* currently never occures. */ + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable: + case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ: + case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD: + gasc->extensionFlag = 1; + break; + default: + gasc->extensionFlag = 0; + break; + } + return gasc; +} + +static mp4a_MPEG_1_2_SpecificConfig_t* mp4a_create_MPEG_1_2_SpecificConfig() +{ + mp4a_MPEG_1_2_SpecificConfig_t *mpeg_1_2_sc = (mp4a_MPEG_1_2_SpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_MPEG_1_2_SpecificConfig_t) ); + if( !mpeg_1_2_sc ) + return NULL; + mpeg_1_2_sc->extension = 0; /* shall be 0. */ + return mpeg_1_2_sc; +} + +static mp4a_ALSSpecificConfig_t *mp4a_create_ALSSpecificConfig( uint8_t *exdata, uint32_t exdata_length ) +{ + mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_ALSSpecificConfig_t) ); + if( !alssc ) + return NULL; + alssc->data = lsmash_memdup( exdata, exdata_length ); + if( !alssc->data ) + { + lsmash_free( alssc ); + return NULL; + } + alssc->size = exdata_length; + return alssc; +} + +/* Currently, only normal AAC, MPEG_1_2 are supported. + For AAC, other than normal AAC, such as AAC_scalable, ER_AAC_xxx, are not supported. + ADIF/PCE(program config element) style AudioSpecificConfig is not supported. + aot shall not be MP4A_AUDIO_OBJECT_TYPE_SBR even if you wish to signal SBR explicitly, use sbr_mode instead. + Frequency/channels shall be base AAC's one, even if SBR/PS. + If other than AAC with SBR, sbr_mode shall be MP4A_AAC_SBR_NOT_SPECIFIED. */ +mp4a_AudioSpecificConfig_t *mp4a_create_AudioSpecificConfig( + lsmash_mp4a_AudioObjectType aot, + uint32_t frequency, + uint32_t channels, + lsmash_mp4a_aac_sbr_mode sbr_mode, + uint8_t *exdata, + uint32_t exdata_length +) +{ + if( aot != MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC + && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_SSR && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LTP + && aot != MP4A_AUDIO_OBJECT_TYPE_TwinVQ && aot != MP4A_AUDIO_OBJECT_TYPE_Layer_1 + && aot != MP4A_AUDIO_OBJECT_TYPE_Layer_2 && aot != MP4A_AUDIO_OBJECT_TYPE_Layer_3 + && aot != MP4A_AUDIO_OBJECT_TYPE_ALS ) + return NULL; + if( frequency == 0 ) + return NULL; + + uint8_t channelConfig; + switch( channels ){ + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + channelConfig = channels; + break; + case 8: + channelConfig = 7; + break; + default: + return NULL; + } + + mp4a_AudioSpecificConfig_t *asc = (mp4a_AudioSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_AudioSpecificConfig_t) ); + if( !asc ) + return NULL; + + asc->sbr_mode = sbr_mode; + asc->audioObjectType = aot; + asc->channelConfiguration = channelConfig; + + uint8_t samplingFrequencyIndex = 0xF; + uint8_t i = 0x0; + if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED + || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN + || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_LC + || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_SSR + || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_LTP + || aot == MP4A_AUDIO_OBJECT_TYPE_SBR ) + { + while( frequency < mp4a_sampling_frequency_table[i][0] ) + i++; + asc->samplingFrequencyIndex = frequency == mp4a_sampling_frequency_table[i][1] ? i : 0xF; + asc->samplingFrequency = frequency; + samplingFrequencyIndex = mp4a_sampling_frequency_table[i][2]; + /* SBR settings */ + if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED ) + { + /* SBR limitation */ + /* see ISO/IEC 14496-3 Levels within the profiles / Levels for the High Efficiency AAC Profile */ + if( i < 0x3 ) + { + lsmash_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_HIERARCHICAL ) + { + asc->extensionSamplingFrequency = frequency * 2; + asc->extensionSamplingFrequencyIndex = i == 0xC ? 0xF : mp4a_sampling_frequency_table[i][3]; + } + else + { + asc->extensionSamplingFrequencyIndex = asc->samplingFrequencyIndex; + asc->extensionSamplingFrequency = asc->samplingFrequency; + } + } + else + { + while( i < 0xD && frequency != mp4a_sampling_frequency_table[i][1] ) + i++; + asc->samplingFrequencyIndex = i != 0xD ? i : 0xF; + asc->samplingFrequency = frequency; + asc->extensionAudioObjectType = MP4A_AUDIO_OBJECT_TYPE_NULL; + asc->extensionSamplingFrequencyIndex = asc->samplingFrequencyIndex; + asc->extensionSamplingFrequency = asc->samplingFrequency; + } + + switch( aot ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN: + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR: + case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP: + case MP4A_AUDIO_OBJECT_TYPE_SBR: +#if 0 /* FIXME: here, stop currently unsupported codecs. */ + case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable: + case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: /* NOTE: I think we already have a support for TwinVQ, but how to test this? */ + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable: + case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ: + case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD: +#endif + asc->deepAudioSpecificConfig = mp4a_create_GASpecificConfig( samplingFrequencyIndex, channelConfig, aot ); + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_1: + case MP4A_AUDIO_OBJECT_TYPE_Layer_2: + case MP4A_AUDIO_OBJECT_TYPE_Layer_3: + asc->deepAudioSpecificConfig = mp4a_create_MPEG_1_2_SpecificConfig(); + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS: + asc->deepAudioSpecificConfig = mp4a_create_ALSSpecificConfig( exdata, exdata_length ); + break; + default: + break; /* this case is trapped below. */ + } + if( !asc->deepAudioSpecificConfig ) + { + lsmash_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, 1, gasc->frameLengthFlag ); + lsmash_bits_put( bits, 1, gasc->dependsOnCoreCoder ); + lsmash_bits_put( bits, 1, gasc->extensionFlag ); +} + +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, 1, mpeg_1_2_sc->extension ); /* shall be 0 */ +} + +static void mp4a_put_ALSSpecificConfig( lsmash_bits_t *bits, mp4a_ALSSpecificConfig_t *alssc ) +{ + debug_if( !bits || !alssc ) + return; + lsmash_bits_import_data( bits, alssc->data, alssc->size ); +} + +static inline void mp4a_put_AudioObjectType( lsmash_bits_t* bits, lsmash_mp4a_AudioObjectType aot ) +{ + if( aot > MP4A_AUDIO_OBJECT_TYPE_ESCAPE ) + { + lsmash_bits_put( bits, 5, MP4A_AUDIO_OBJECT_TYPE_ESCAPE ); + lsmash_bits_put( bits, 6, aot - MP4A_AUDIO_OBJECT_TYPE_ESCAPE - 1 ); + } + else + lsmash_bits_put( bits, 5, aot ); +} + +static inline void mp4a_put_SamplingFrequencyIndex( lsmash_bits_t* bits, uint8_t samplingFrequencyIndex, uint32_t samplingFrequency ) +{ + lsmash_bits_put( bits, 4, samplingFrequencyIndex ); + if( samplingFrequencyIndex == 0xF ) + lsmash_bits_put( bits, 24, samplingFrequency ); +} + +/* 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, 4, asc->channelConfiguration ); + if( asc->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL ) + { + mp4a_put_SamplingFrequencyIndex( &bits, asc->extensionSamplingFrequencyIndex, asc->extensionSamplingFrequency ); + mp4a_put_AudioObjectType( &bits, asc->audioObjectType ); + } + switch( asc->audioObjectType ){ + case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN: + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR: + case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP: + case MP4A_AUDIO_OBJECT_TYPE_SBR: +#if 0 /* FIXME: here, stop currently unsupported codecs */ + case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable: + case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: /* NOTE: I think we already have a support for TwinVQ, but how to test this? */ + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable: + case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ: + case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC: + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD: +#endif + mp4a_put_GASpecificConfig( &bits, (mp4a_GASpecificConfig_t*)asc->deepAudioSpecificConfig ); + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_1: + case MP4A_AUDIO_OBJECT_TYPE_Layer_2: + case MP4A_AUDIO_OBJECT_TYPE_Layer_3: + mp4a_put_MPEG_1_2_SpecificConfig( &bits, (mp4a_MPEG_1_2_SpecificConfig_t*)asc->deepAudioSpecificConfig ); + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS: + lsmash_bits_put( &bits, 5, 0 ); /* fillBits for byte alignment */ + mp4a_put_ALSSpecificConfig( &bits, (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig ); + break; + default: + break; /* FIXME: do we have to return error? */ + } + + /* FIXME: Error Resiliant stuff omitted here. */ + + if( asc->sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE || asc->sbr_mode == MP4A_AAC_SBR_NONE ) + { + lsmash_bits_put( &bits, 11, 0x2b7 ); + 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, 1, 0x0 ); + else + { + lsmash_bits_put( &bits, 1, 0x1 ); + mp4a_put_SamplingFrequencyIndex( &bits, asc->extensionSamplingFrequencyIndex, asc->extensionSamplingFrequency ); + } + } + } + lsmash_bits_put_align( &bits ); +} + +static int mp4a_get_GASpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc ) +{ + mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_GASpecificConfig_t) ); + if( !gasc ) + return LSMASH_ERR_MEMORY_ALLOC; + asc->deepAudioSpecificConfig = gasc; + gasc->frameLengthFlag = lsmash_bits_get( bits, 1 ); + gasc->dependsOnCoreCoder = lsmash_bits_get( bits, 1 ); + if( gasc->dependsOnCoreCoder ) + gasc->coreCoderDelay = lsmash_bits_get( bits, 14 ); + gasc->extensionFlag = lsmash_bits_get( bits, 1 ); + return 0; +} + +static int mp4a_get_MPEG_1_2_SpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc ) +{ + mp4a_MPEG_1_2_SpecificConfig_t *mpeg_1_2_sc = (mp4a_MPEG_1_2_SpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_MPEG_1_2_SpecificConfig_t) ); + if( !mpeg_1_2_sc ) + return LSMASH_ERR_MEMORY_ALLOC; + mpeg_1_2_sc->extension = lsmash_bits_get( bits, 1 ); + return 0; +} + +static int mp4a_get_ALSSpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc ) +{ + mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_ALSSpecificConfig_t) ); + if( !alssc ) + return LSMASH_ERR_MEMORY_ALLOC; + asc->deepAudioSpecificConfig = alssc; + alssc->als_id = lsmash_bits_get( bits, 32 ); + alssc->samp_freq = lsmash_bits_get( bits, 32 ); + alssc->samples = lsmash_bits_get( bits, 32 ); + alssc->channels = lsmash_bits_get( bits, 16 ); + alssc->file_type = lsmash_bits_get( bits, 3 ); + alssc->resolution = lsmash_bits_get( bits, 3 ); + alssc->floating = lsmash_bits_get( bits, 1 ); + alssc->msb_first = lsmash_bits_get( bits, 1 ); + alssc->frame_length = lsmash_bits_get( bits, 16 ); + alssc->random_access = lsmash_bits_get( bits, 8 ); + alssc->ra_flag = lsmash_bits_get( bits, 2 ); + alssc->adapt_order = lsmash_bits_get( bits, 1 ); + alssc->coef_table = lsmash_bits_get( bits, 2 ); + alssc->long_term_prediction = lsmash_bits_get( bits, 1 ); + alssc->max_order = lsmash_bits_get( bits, 10 ); + alssc->block_switching = lsmash_bits_get( bits, 2 ); + alssc->bgmc_mode = lsmash_bits_get( bits, 1 ); + alssc->sb_part = lsmash_bits_get( bits, 1 ); + alssc->joint_stereo = lsmash_bits_get( bits, 1 ); + alssc->mc_coding = lsmash_bits_get( bits, 1 ); + alssc->chan_config = lsmash_bits_get( bits, 1 ); + alssc->chan_sort = lsmash_bits_get( bits, 1 ); + alssc->crc_enabled = lsmash_bits_get( bits, 1 ); + alssc->RLSLMS = lsmash_bits_get( bits, 1 ); + alssc->reserved = lsmash_bits_get( bits, 5 ); + alssc->aux_data_enabled = lsmash_bits_get( bits, 1 ); + return 0; +} + +static mp4a_AudioSpecificConfig_t *mp4a_get_AudioSpecificConfig( uint8_t *dsi_payload, uint32_t dsi_payload_length ) +{ + lsmash_bits_t *bits = lsmash_bits_adhoc_create(); + if( !bits ) + return NULL; + if( lsmash_bits_import_data( bits, dsi_payload, dsi_payload_length ) < 0 ) + { + lsmash_bits_adhoc_cleanup( bits ); + return NULL; + } + mp4a_AudioSpecificConfig_t *asc = (mp4a_AudioSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_AudioSpecificConfig_t) ); + if( !asc ) + return NULL; + asc->audioObjectType = lsmash_bits_get( bits, 5 ); + if( asc->audioObjectType == 31 ) + asc->extensionAudioObjectType = asc->audioObjectType += 1 + lsmash_bits_get( bits, 6 ); + asc->samplingFrequencyIndex = lsmash_bits_get( bits, 4 ); + if( asc->samplingFrequencyIndex == 0xf ) + asc->samplingFrequency = lsmash_bits_get( bits, 24 ); + asc->channelConfiguration = lsmash_bits_get( bits, 4 ); + int ret = 0; + switch( asc->audioObjectType ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN : + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC : + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : + case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP : + case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable : + case MP4A_AUDIO_OBJECT_TYPE_TwinVQ : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable : + case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ : + case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD : + ret = mp4a_get_GASpecificConfig( bits, asc ); + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : + ret = mp4a_get_MPEG_1_2_SpecificConfig( bits, asc ); + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS : + lsmash_bits_get( bits, 5 ); + ret = mp4a_get_ALSSpecificConfig( bits, asc ); + break; + default : + break; + } + if( ret < 0 ) + goto fail; + lsmash_bits_adhoc_cleanup( bits ); + return asc; +fail: + lsmash_bits_adhoc_cleanup( bits ); + lsmash_free( asc ); + return NULL; +} + +int mp4a_setup_summary_from_AudioSpecificConfig( lsmash_audio_summary_t *summary, uint8_t *dsi_payload, uint32_t dsi_payload_length ) +{ + mp4a_AudioSpecificConfig_t *asc = mp4a_get_AudioSpecificConfig( dsi_payload, dsi_payload_length ); + if( !asc ) + return LSMASH_ERR_NAMELESS; + summary->summary_type = LSMASH_SUMMARY_TYPE_AUDIO; + summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; + summary->aot = asc->audioObjectType; + switch( asc->audioObjectType ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN : + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC : + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : + case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP : + case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable : + case MP4A_AUDIO_OBJECT_TYPE_TwinVQ : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable : + case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ : + case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD : + case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : + if( asc->samplingFrequencyIndex == 0xf ) + summary->frequency = asc->samplingFrequency; + else + { + uint8_t i = 0x0; + while( i != 0xc ) + { + if( mp4a_sampling_frequency_table[i][2] == asc->samplingFrequencyIndex ) + { + summary->frequency = mp4a_sampling_frequency_table[i][1]; + break; + } + ++i; + } + if( i == 0xc ) + { + mp4a_remove_AudioSpecificConfig( asc ); + return LSMASH_ERR_INVALID_DATA; + } + } + if( asc->channelConfiguration < 8 ) + summary->channels = asc->channelConfiguration != 7 ? asc->channelConfiguration : 8; + else + summary->channels = 0; /* reserved */ + summary->sample_size = 16; + switch( asc->audioObjectType ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : + summary->samples_in_frame = 1024; + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : + summary->samples_in_frame = 384; + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : + summary->samples_in_frame = 1152; + break; + default : + summary->samples_in_frame = !((mp4a_GASpecificConfig_t *)asc->deepAudioSpecificConfig)->frameLengthFlag ? 1024 : 960; + break; + } + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS : + { + mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig; + summary->frequency = alssc->samp_freq; + summary->channels = alssc->channels + 1; + summary->sample_size = (alssc->resolution + 1) * 8; + summary->samples_in_frame = alssc->frame_length + 1; + break; + } + default : + break; + } + mp4a_remove_AudioSpecificConfig( asc ); + return 0; +} + +/* This function is very ad-hoc. */ +uint8_t *mp4a_export_AudioSpecificConfig( lsmash_mp4a_AudioObjectType aot, + uint32_t frequency, + uint32_t channels, + lsmash_mp4a_aac_sbr_mode sbr_mode, + uint8_t *exdata, + uint32_t exdata_length, + uint32_t *data_length ) +{ + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return NULL; + mp4a_AudioSpecificConfig_t *asc = mp4a_create_AudioSpecificConfig( aot, frequency, channels, sbr_mode, exdata, exdata_length ); + if( !asc ) + { + lsmash_bs_cleanup( bs ); + return NULL; + } + mp4a_put_AudioSpecificConfig( bs, asc ); + uint8_t *data = lsmash_bs_export_data( bs, data_length ); + mp4a_remove_AudioSpecificConfig( asc ); + lsmash_bs_cleanup( bs ); + if( !data ) + return NULL; + return data; +} + +#ifdef LSMASH_DEMUXER_ENABLED +static void mp4a_print_GASpecificConfig( FILE *fp, mp4a_AudioSpecificConfig_t *asc, int indent ) +{ + mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)asc->deepAudioSpecificConfig; + lsmash_ifprintf( fp, indent++, "[GASpecificConfig]\n" ); + lsmash_ifprintf( fp, indent, "frameLengthFlag = %"PRIu8"\n", gasc->frameLengthFlag ); + lsmash_ifprintf( fp, indent, "dependsOnCoreCoder = %"PRIu8"\n", gasc->dependsOnCoreCoder ); + if( gasc->dependsOnCoreCoder ) + lsmash_ifprintf( fp, indent, "coreCoderDelay = %"PRIu16"\n", gasc->coreCoderDelay ); + lsmash_ifprintf( fp, indent, "extensionFlag = %"PRIu8"\n", gasc->extensionFlag ); + if( !asc->channelConfiguration ) + lsmash_ifprintf( fp, indent, "program_config_element()\n" ); +} + +static void mp4a_print_MPEG_1_2_SpecificConfig( FILE *fp, mp4a_AudioSpecificConfig_t *asc, int indent ) +{ + mp4a_MPEG_1_2_SpecificConfig_t *mpeg_1_2_sc = (mp4a_MPEG_1_2_SpecificConfig_t *)asc->deepAudioSpecificConfig; + lsmash_ifprintf( fp, indent++, "[MPEG_1_2_SpecificConfig]\n" ); + lsmash_ifprintf( fp, indent, "extension = %"PRIu8"\n", mpeg_1_2_sc->extension ); +} + +static void mp4a_print_ALSSpecificConfig( FILE *fp, mp4a_AudioSpecificConfig_t *asc, int indent ) +{ + mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig; + const char *file_type [4] = { "raw", "wave", "aiff", "bwf" }; + const char *floating [2] = { "integer", "IEEE 32-bit floating-point" }; + const char *endian [2] = { "little", "big" }; + const char *ra_flag [4] = { "not stored", "stored at the beginning of frame_data()", "stored at the end of ALSSpecificConfig", "?" }; + lsmash_ifprintf( fp, indent++, "[ALSSpecificConfig]\n" ); + lsmash_ifprintf( fp, indent, "als_id = 0x%"PRIx32"\n", alssc->als_id ); + lsmash_ifprintf( fp, indent, "samp_freq = %"PRIu32" Hz\n", alssc->samp_freq ); + lsmash_ifprintf( fp, indent, "samples = %"PRIu32"\n", alssc->samples ); + lsmash_ifprintf( fp, indent, "channels = %"PRIu16"\n", alssc->channels ); + if( alssc->file_type <= 3 ) + lsmash_ifprintf( fp, indent, "file_type = %"PRIu8" (%s file)\n", alssc->file_type, file_type[ alssc->file_type ] ); + else + lsmash_ifprintf( fp, indent, "file_type = %"PRIu8"\n", alssc->file_type ); + if( alssc->resolution <= 3 ) + lsmash_ifprintf( fp, indent, "resolution = %"PRIu8" (%d-bit)\n", alssc->resolution, 8 * (1 + alssc->resolution) ); + else + lsmash_ifprintf( fp, indent, "resolution = %"PRIu8"\n", alssc->resolution ); + lsmash_ifprintf( fp, indent, "floating = %"PRIu8" (%s)\n", alssc->floating, floating[ alssc->floating ] ); + if( alssc->resolution ) + lsmash_ifprintf( fp, indent, "msb_first = %"PRIu8" (%s-endian)\n", alssc->msb_first, endian[ alssc->msb_first ] ); + else + lsmash_ifprintf( fp, indent, "msb_first = %"PRIu8" (%ssigned data)\n", alssc->msb_first, ((const char *[2]){ "un", "" })[ alssc->msb_first ] ); + lsmash_ifprintf( fp, indent, "frame_length = %"PRIu16"\n", alssc->frame_length ); + lsmash_ifprintf( fp, indent, "random_access = %"PRIu8"\n", alssc->random_access ); + lsmash_ifprintf( fp, indent, "ra_flag = %"PRIu8" (ra_unit_size is %s)\n", alssc->ra_flag, ra_flag[ alssc->ra_flag ] ); + lsmash_ifprintf( fp, indent, "adapt_order = %"PRIu8"\n", alssc->adapt_order ); + lsmash_ifprintf( fp, indent, "coef_table = %"PRIu8"\n", alssc->coef_table ); + lsmash_ifprintf( fp, indent, "long_term_prediction = %"PRIu8"\n", alssc->long_term_prediction ); + lsmash_ifprintf( fp, indent, "max_order = %"PRIu8"\n", alssc->max_order ); + lsmash_ifprintf( fp, indent, "block_switching = %"PRIu8"\n", alssc->block_switching ); + lsmash_ifprintf( fp, indent, "bgmc_mode = %"PRIu8"\n", alssc->bgmc_mode ); + lsmash_ifprintf( fp, indent, "sb_part = %"PRIu8"\n", alssc->sb_part ); + lsmash_ifprintf( fp, indent, "joint_stereo = %"PRIu8"\n", alssc->joint_stereo ); + lsmash_ifprintf( fp, indent, "mc_coding = %"PRIu8"\n", alssc->mc_coding ); + lsmash_ifprintf( fp, indent, "chan_config = %"PRIu8"\n", alssc->chan_config ); + lsmash_ifprintf( fp, indent, "chan_sort = %"PRIu8"\n", alssc->chan_sort ); + lsmash_ifprintf( fp, indent, "crc_enabled = %"PRIu8"\n", alssc->crc_enabled ); + lsmash_ifprintf( fp, indent, "RLSLMS = %"PRIu8"\n", alssc->RLSLMS ); + lsmash_ifprintf( fp, indent, "reserved = %"PRIu8"\n", alssc->reserved ); + lsmash_ifprintf( fp, indent, "aux_data_enabled = %"PRIu8"\n", alssc->aux_data_enabled ); +} + +void mp4a_print_AudioSpecificConfig( FILE *fp, uint8_t *dsi_payload, uint32_t dsi_payload_length, int indent ) +{ + assert( fp && dsi_payload && dsi_payload_length ); + mp4a_AudioSpecificConfig_t *asc = mp4a_get_AudioSpecificConfig( dsi_payload, dsi_payload_length ); + if( !asc ) + return; + const char *audio_object_type[] = + { + "NULL", + "AAC MAIN", + "AAC LC (Low Complexity)", + "AAC SSR (Scalable Sample Rate)", + "AAC LTP (Long Term Prediction)", + "SBR (Spectral Band Replication)", + "AAC scalable", + "TwinVQ", + "CELP (Code Excited Linear Prediction)", + "HVXC (Harmonic Vector Excitation Coding)", + "reserved", + "reserved", + "TTSI (Text-To-Speech Interface)", + "Main synthetic", + "Wavetable synthesis", + "General MIDI", + "Algorithmic Synthesis and Audio FX", + "ER AAC LC", + "reserved", + "ER AAC LTP", + "ER AAC scalable", + "ER Twin VQ", + "ER BSAC (Bit-Sliced Arithmetic Coding)", + "ER AAC LD", + "ER CELP", + "ER HVXC", + "ER HILN (Harmonic and Individual Lines plus Noise)", + "ER Parametric", + "SSC (SinuSoidal Coding)", + "PS (Parametric Stereo)", + "MPEG Surround", + "escape", + "Layer-1", + "Layer-2", + "Layer-3", + "DST (Direct Stream Transfer)", + "ALS (Audio Lossless Coding)", + "SLS (Scalable Lossless Coding)", + "SLS non-core", + "ER AAC ELD", + "SMR Simple", + "SMR Main", + "USAC (Unified Speech and Audio Coding)", + "SAOC", + "LD MPEG Surround", + "SAOC-DE" + }; + lsmash_ifprintf( fp, indent++, "[AudioSpecificConfig]\n" ); + if( asc->audioObjectType < sizeof(audio_object_type) / sizeof(audio_object_type[0]) ) + lsmash_ifprintf( fp, indent, "audioObjectType = %d (%s)\n", asc->audioObjectType, audio_object_type[ asc->audioObjectType ] ); + else + lsmash_ifprintf( fp, indent, "audioObjectType = %d\n", asc->audioObjectType ); + lsmash_ifprintf( fp, indent, "samplingFrequencyIndex = %"PRIu8"\n", asc->samplingFrequencyIndex ); + if( asc->samplingFrequencyIndex == 0xf ) + lsmash_ifprintf( fp, indent, "samplingFrequency = %"PRIu32"\n", asc->samplingFrequency ); + lsmash_ifprintf( fp, indent, "channelConfiguration = %"PRIu8"\n", asc->channelConfiguration ); + if( asc->extensionAudioObjectType == 5 ) + { + lsmash_ifprintf( fp, indent, "extensionSamplingFrequencyIndex = %"PRIu8"\n", asc->extensionSamplingFrequencyIndex ); + if( asc->extensionSamplingFrequencyIndex == 0xf ) + lsmash_ifprintf( fp, indent, "extensionSamplingFrequency = %"PRIu32"\n", asc->extensionSamplingFrequency ); + if( asc->audioObjectType == 22 ) + lsmash_ifprintf( fp, indent, "extensionChannelConfiguration = %"PRIu8"\n", asc->extensionChannelConfiguration ); + } + if( asc->deepAudioSpecificConfig ) + switch( asc->audioObjectType ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN : + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC : + case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : + case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP : + case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable : + case MP4A_AUDIO_OBJECT_TYPE_TwinVQ : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable : + case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ : + case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC : + case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD : + mp4a_print_GASpecificConfig( fp, asc, indent ); + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : + case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : + mp4a_print_MPEG_1_2_SpecificConfig( fp, asc, indent ); + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS : + mp4a_print_ALSSpecificConfig( fp, asc, indent ); + break; + default : + break; + } + mp4a_remove_AudioSpecificConfig( asc ); +} +#endif /* LSMASH_DEMUXER_ENABLED */ + +/*************************************************************************** + 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->summary_type != LSMASH_SUMMARY_TYPE_AUDIO ) + return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */ + if( lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ) != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 ) + return MP4A_AUDIO_PLI_NOT_SPECIFIED; /* This is of audio stream, but not described in ISO/IEC 14496-3. */ + if( summary->channels == 0 || summary->frequency == 0 ) + return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */ + mp4a_audioProfileLevelIndication pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + switch( summary->aot ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: + if( summary->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL ) + { + /* NOTE: This is not strictly preferable, but accurate; just possibly over-estimated. + We do not expect to use MP4A_AAC_SBR_HIERARCHICAL mode without SBR, nor downsampled mode with SBR. */ + if( summary->channels <= 2 && summary->frequency <= 24000 ) + pli = MP4A_AUDIO_PLI_HE_AAC_L2; + else if( summary->channels <= 5 && summary->frequency <= 48000 ) + pli = MP4A_AUDIO_PLI_HE_AAC_L5; + else + pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + break; + } + /* pretending plain AAC-LC, if actually HE-AAC. */ + static const uint32_t mp4sys_aac_pli_table[5][3] = { + /* channels, frequency, audioProfileLevelIndication */ + { 6, 96000, MP4A_AUDIO_PLI_AAC_L5 }, /* FIXME: 6ch is not strictly correct, but works in many case. */ + { 6, 48000, MP4A_AUDIO_PLI_AAC_L4 }, /* FIXME: 6ch is not strictly correct, but works in many case. */ + { 2, 48000, MP4A_AUDIO_PLI_AAC_L2 }, + { 2, 24000, MP4A_AUDIO_PLI_AAC_L1 }, + { 0, 0, MP4A_AUDIO_PLI_NOT_SPECIFIED } + }; + for( int i = 0; summary->channels <= mp4sys_aac_pli_table[i][0] && summary->frequency <= mp4sys_aac_pli_table[i][1] ; i++ ) + pli = mp4sys_aac_pli_table[i][2]; + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS: + /* FIXME: this is not stricly. Summary shall carry max_order, block_switching, bgmc_mode and RLSLMS. */ + if( summary->channels <= 2 && summary->frequency <= 48000 && summary->sample_size <= 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 -Nru l-smash-1.9.1/codecs/mp4a.h l-smash-2.3.0/codecs/mp4a.h --- l-smash-1.9.1/codecs/mp4a.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/mp4a.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,136 @@ +/***************************************************************************** + * mp4a.h: + ***************************************************************************** + * Copyright (C) 2010-2014 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 + +/*************************************************************************** + MPEG-4 Systems for MPEG-4 Audio +***************************************************************************/ + +/* 14496-3 audioProfileLevelIndication */ +typedef enum { + MP4A_AUDIO_PLI_Reserved = 0x00, /* Reserved for ISO use */ + MP4A_AUDIO_PLI_Main_L1 = 0x01, /* Main Audio Profile L1 */ + MP4A_AUDIO_PLI_Main_L2 = 0x02, /* Main Audio Profile L2 */ + MP4A_AUDIO_PLI_Main_L3 = 0x03, /* Main Audio Profile L3 */ + MP4A_AUDIO_PLI_Main_L4 = 0x04, /* Main Audio Profile L4 */ + MP4A_AUDIO_PLI_Scalable_L1 = 0x05, /* Scalable Audio Profile L1 */ + MP4A_AUDIO_PLI_Scalable_L2 = 0x06, /* Scalable Audio Profile L2 */ + MP4A_AUDIO_PLI_Scalable_L3 = 0x07, /* Scalable Audio Profile L3 */ + MP4A_AUDIO_PLI_Scalable_L4 = 0x08, /* Scalable Audio Profile L4 */ + MP4A_AUDIO_PLI_Speech_L1 = 0x09, /* Speech Audio Profile L1 */ + MP4A_AUDIO_PLI_Speech_L2 = 0x0A, /* Speech Audio Profile L2 */ + MP4A_AUDIO_PLI_Synthetic_L1 = 0x0B, /* Synthetic Audio Profile L1 */ + MP4A_AUDIO_PLI_Synthetic_L2 = 0x0C, /* Synthetic Audio Profile L2 */ + MP4A_AUDIO_PLI_Synthetic_L3 = 0x0D, /* Synthetic Audio Profile L3 */ + MP4A_AUDIO_PLI_HighQuality_L1 = 0x0E, /* High Quality Audio Profile L1 */ + MP4A_AUDIO_PLI_HighQuality_L2 = 0x0F, /* High Quality Audio Profile L2 */ + MP4A_AUDIO_PLI_HighQuality_L3 = 0x10, /* High Quality Audio Profile L3 */ + MP4A_AUDIO_PLI_HighQuality_L4 = 0x11, /* High Quality Audio Profile L4 */ + MP4A_AUDIO_PLI_HighQuality_L5 = 0x12, /* High Quality Audio Profile L5 */ + MP4A_AUDIO_PLI_HighQuality_L6 = 0x13, /* High Quality Audio Profile L6 */ + MP4A_AUDIO_PLI_HighQuality_L7 = 0x14, /* High Quality Audio Profile L7 */ + MP4A_AUDIO_PLI_HighQuality_L8 = 0x15, /* High Quality Audio Profile L8 */ + MP4A_AUDIO_PLI_LowDelay_L1 = 0x16, /* Low Delay Audio Profile L1 */ + MP4A_AUDIO_PLI_LowDelay_L2 = 0x17, /* Low Delay Audio Profile L2 */ + MP4A_AUDIO_PLI_LowDelay_L3 = 0x18, /* Low Delay Audio Profile L3 */ + MP4A_AUDIO_PLI_LowDelay_L4 = 0x19, /* Low Delay Audio Profile L4 */ + MP4A_AUDIO_PLI_LowDelay_L5 = 0x1A, /* Low Delay Audio Profile L5 */ + MP4A_AUDIO_PLI_LowDelay_L6 = 0x1B, /* Low Delay Audio Profile L6 */ + MP4A_AUDIO_PLI_LowDelay_L7 = 0x1C, /* Low Delay Audio Profile L7 */ + MP4A_AUDIO_PLI_LowDelay_L8 = 0x1D, /* Low Delay Audio Profile L8 */ + MP4A_AUDIO_PLI_Natural_L1 = 0x1E, /* Natural Audio Profile L1 */ + MP4A_AUDIO_PLI_Natural_L2 = 0x1F, /* Natural Audio Profile L2 */ + MP4A_AUDIO_PLI_Natural_L3 = 0x20, /* Natural Audio Profile L3 */ + MP4A_AUDIO_PLI_Natural_L4 = 0x21, /* Natural Audio Profile L4 */ + MP4A_AUDIO_PLI_MobileInternetworking_L1 = 0x22, /* Mobile Audio Internetworking Profile L1 */ + MP4A_AUDIO_PLI_MobileInternetworking_L2 = 0x23, /* Mobile Audio Internetworking Profile L2 */ + MP4A_AUDIO_PLI_MobileInternetworking_L3 = 0x24, /* Mobile Audio Internetworking Profile L3 */ + MP4A_AUDIO_PLI_MobileInternetworking_L4 = 0x25, /* Mobile Audio Internetworking Profile L4 */ + MP4A_AUDIO_PLI_MobileInternetworking_L5 = 0x26, /* Mobile Audio Internetworking Profile L5 */ + MP4A_AUDIO_PLI_MobileInternetworking_L6 = 0x27, /* Mobile Audio Internetworking Profile L6 */ + MP4A_AUDIO_PLI_AAC_L1 = 0x28, /* AAC Profile L1 */ + MP4A_AUDIO_PLI_AAC_L2 = 0x29, /* AAC Profile L2 */ + MP4A_AUDIO_PLI_AAC_L4 = 0x2A, /* AAC Profile L4 */ + MP4A_AUDIO_PLI_AAC_L5 = 0x2B, /* AAC Profile L5 */ + MP4A_AUDIO_PLI_HE_AAC_L2 = 0x2C, /* High Efficiency AAC Profile L2 */ + MP4A_AUDIO_PLI_HE_AAC_L3 = 0x2D, /* High Efficiency AAC Profile L3 */ + MP4A_AUDIO_PLI_HE_AAC_L4 = 0x2E, /* High Efficiency AAC Profile L4 */ + MP4A_AUDIO_PLI_HE_AAC_L5 = 0x2F, /* High Efficiency AAC Profile L5 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L2 = 0x30, /* High Efficiency AAC v2 Profile L2 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L3 = 0x31, /* High Efficiency AAC v2 Profile L3 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L4 = 0x32, /* High Efficiency AAC v2 Profile L4 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L5 = 0x33, /* High Efficiency AAC v2 Profile L5 */ + MP4A_AUDIO_PLI_LowDelay_AAC_L1 = 0x34, /* Low Delay AAC Profile L1 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L1= 0x35, /* Baseline MPEG Surround Profile L1 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L2= 0x36, /* Baseline MPEG Surround Profile L2 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L3= 0x37, /* Baseline MPEG Surround Profile L3 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L4= 0x38, /* Baseline MPEG Surround Profile L4 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L5= 0x39, /* Baseline MPEG Surround Profile L5 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L6= 0x3A, /* Baseline MPEG Surround Profile L6 */ + MP4A_AUDIO_PLI_HD_AAC_L1 = 0x3B, /* High Definition AAC Profile L1 */ + MP4A_AUDIO_PLI_ALS_Simple_L1 = 0x3C, /* ALS Simple Profile L1 */ + MP4A_AUDIO_PLI_NOT_SPECIFIED = 0xFE, /* no audio profile specified */ + MP4A_AUDIO_PLI_NONE_REQUIRED = 0xFF, /* no audio capability required */ +} mp4a_audioProfileLevelIndication; + +#ifndef MP4A_INTERNAL + +typedef void mp4a_AudioSpecificConfig_t; + +/* export for mp4sys / importer */ +mp4a_AudioSpecificConfig_t *mp4a_create_AudioSpecificConfig( + lsmash_mp4a_AudioObjectType aot, + uint32_t frequency, + uint32_t channels, + lsmash_mp4a_aac_sbr_mode sbr_mode, + uint8_t *exdata, + uint32_t exdata_length +); +void mp4a_put_AudioSpecificConfig( lsmash_bs_t* bs, mp4a_AudioSpecificConfig_t* asc ); +void mp4a_remove_AudioSpecificConfig( mp4a_AudioSpecificConfig_t* asc ); + +uint8_t *mp4a_export_AudioSpecificConfig( lsmash_mp4a_AudioObjectType aot, + uint32_t frequency, + uint32_t channels, + lsmash_mp4a_aac_sbr_mode sbr_mode, + uint8_t *exdata, + uint32_t exdata_length, + uint32_t *data_length ); + +/* export for importer */ +extern const uint32_t mp4a_sampling_frequency_table[13][5]; + +/* setup for summary */ +int mp4a_setup_summary_from_AudioSpecificConfig( lsmash_audio_summary_t *summary, uint8_t *dsi_payload, uint32_t dsi_payload_length ); + +/* profileLevelIndication relative functions. */ +mp4a_audioProfileLevelIndication mp4a_get_audioProfileLevelIndication( lsmash_audio_summary_t *summary ); +mp4a_audioProfileLevelIndication mp4a_max_audioProfileLevelIndication( + mp4a_audioProfileLevelIndication a, + mp4a_audioProfileLevelIndication b +); + +#endif + +#endif diff -Nru l-smash-1.9.1/codecs/mp4sys.c l-smash-2.3.0/codecs/mp4sys.c --- l-smash-1.9.1/codecs/mp4sys.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/mp4sys.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1593 @@ +/***************************************************************************** + * mp4sys.c: + ***************************************************************************** + * Copyright (C) 2010-2014 L-SMASH project + * + * Authors: Takashi Hirata + * 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +#include "description.h" +#include "mp4a.h" +#define MP4SYS_INTERNAL +#include "mp4sys.h" + +/*************************************************************************** + MPEG-4 Systems +***************************************************************************/ + +#define ALWAYS_28BITS_LENGTH_CODING 1 // for some weird (but originator's) devices + +static const lsmash_class_t lsmash_mp4sys_class = +{ + "mp4sys" +}; + +/* 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; + +typedef struct mp4sys_descriptor_tag mp4sys_descriptor_t; + +typedef void (*mp4sys_descriptor_destructor_t)( void * ); +typedef int (*mp4sys_descriptor_writer_t)( lsmash_bs_t *, void * ); + +#define MP4SYS_DESCRIPTOR_COMMON \ + const lsmash_class_t *class; \ + mp4sys_descriptor_t *parent; \ + mp4sys_descriptor_destructor_t destruct; \ + mp4sys_descriptor_writer_t write; \ + mp4sys_descriptor_head_t header; \ + lsmash_entry_list_t children + +struct mp4sys_descriptor_tag +{ + MP4SYS_DESCRIPTOR_COMMON; +}; + +typedef struct +{ + MP4SYS_DESCRIPTOR_COMMON; +} mp4sys_BaseDescriptor_t; + +/* DecoderSpecificInfo */ +/* contents varies depends on ObjectTypeIndication and StreamType. */ +typedef struct +{ + MP4SYS_DESCRIPTOR_COMMON; + uint8_t *data; +} mp4sys_DecoderSpecificInfo_t; + +/* DecoderConfigDescriptor */ +typedef struct +{ + MP4SYS_DESCRIPTOR_COMMON; + 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_COMMON; + uint8_t predefined; /* default the values from a set of predefined parameter sets as detailed below. + * 0x00 : Custom + * 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_ES_Descriptor_t +{ + MP4SYS_DESCRIPTOR_COMMON; + 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; + char URLstring[256]; + 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_COMMON; + uint32_t Track_ID; +} mp4sys_ES_ID_Inc_t; + +/* 14496-1 ObjectDescriptor / InitialObjectDescriptor */ +typedef struct +{ + MP4SYS_DESCRIPTOR_COMMON; + unsigned ObjectDescriptorID : 10; + unsigned URL_Flag : 1; + unsigned includeInlineProfileLevelFlag : 1; /* for OD, reserved and set to 1 */ + unsigned reserved : 4; /* 0b1111 */ + uint8_t URLlength; + char URLstring[256]; + /* IOD only */ + 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; + +static void mp4sys_remove_predefined_descriptor( void *opaque_descriptor, size_t offset_of_descriptor ) +{ + assert( opaque_descriptor ); + mp4sys_descriptor_t *descriptor = (mp4sys_descriptor_t *)opaque_descriptor; + if( descriptor->parent ) + { + mp4sys_descriptor_t **p = (mp4sys_descriptor_t **)(((int8_t *)descriptor->parent) + offset_of_descriptor); + if( *p == descriptor ) + *p = NULL; + } +} + +/* We always free descriptors through the children list of the parent descriptor. + * Therefore, don't free descriptors through any list other than the children list. */ +static void mp4sys_remove_descriptor_in_predefined_list( void *opaque_descriptor, size_t offset_of_list ) +{ + assert( opaque_descriptor ); + mp4sys_descriptor_t *descriptor = (mp4sys_descriptor_t *)opaque_descriptor; + if( descriptor->parent ) + { + lsmash_entry_list_t *list = (lsmash_entry_list_t *)(((int8_t *)descriptor->parent) + offset_of_list); + for( lsmash_entry_t *entry = list ? list->head : NULL; entry; entry = entry->next ) + if( descriptor == entry->data ) + { + /* We don't free this descriptor here. + * Because of freeing an entry of the list here, don't pass the list to free this descriptor. + * Or double free. */ + entry->data = NULL; + lsmash_remove_entry_direct( list, entry, NULL ); + break; + } + } +} + +static void mp4sys_remove_all_child_descriptors( lsmash_entry_list_t *children ); + +/* Free a descriptor and its children. */ +static void mp4sys_destruct_descriptor( mp4sys_descriptor_t *descriptor ) +{ + if( !descriptor ) + return; + if( descriptor->destruct ) + descriptor->destruct( descriptor ); + mp4sys_remove_all_child_descriptors( &descriptor->children ); + lsmash_free( descriptor ); +} + +static void mp4sys_remove_all_child_descriptors( lsmash_entry_list_t *children ) +{ + lsmash_remove_entries( children, mp4sys_destruct_descriptor ); +} + +/* Remove a descriptor by the pointer containing its address. + * In addition, remove from the children list of the parent descriptor if possible. + * Don't call this function within a function freeing one or more entries of any children list because of double free. + * Basically, don't use this function as a callback function. */ +void mp4sys_remove_descriptor( void *opaque_descriptor ) +{ + if( !opaque_descriptor ) + return; + mp4sys_descriptor_t *descriptor = (mp4sys_descriptor_t *)opaque_descriptor; + if( descriptor->parent ) + { + mp4sys_descriptor_t *parent = descriptor->parent; + for( lsmash_entry_t *entry = parent->children.head; entry; entry = entry->next ) + if( descriptor == entry->data ) + { + /* Free the corresponding entry here, therefore don't call this function as a callback function + * if a function frees the same entry later and calls this function. */ + lsmash_remove_entry_direct( &parent->children, entry, mp4sys_destruct_descriptor ); + return; + } + } + mp4sys_destruct_descriptor( descriptor ); +} + +static void mp4sys_remove_DecoderSpecificInfo( mp4sys_DecoderSpecificInfo_t *dsi ) +{ + if( !dsi ) + return; + lsmash_free( dsi->data ); + mp4sys_remove_predefined_descriptor( dsi, offsetof( mp4sys_DecoderConfigDescriptor_t, decSpecificInfo ) ); +} + +static void mp4sys_remove_DecoderConfigDescriptor( mp4sys_DecoderConfigDescriptor_t *dcd ) +{ + if( !dcd ) + return; + mp4sys_remove_predefined_descriptor( dcd, offsetof( mp4sys_ES_Descriptor_t, decConfigDescr ) ); +} + +static void mp4sys_remove_SLConfigDescriptor( mp4sys_SLConfigDescriptor_t *slcd ) +{ + if( !slcd ) + return; + mp4sys_remove_predefined_descriptor( slcd, offsetof( mp4sys_ES_Descriptor_t, slConfigDescr ) ); +} + +static void mp4sys_remove_ES_Descriptor( mp4sys_ES_Descriptor_t *esd ) +{ + if( !esd || (esd->parent && (esd->parent->header.tag == MP4SYS_DESCRIPTOR_TAG_ObjectDescrTag + || esd->parent->header.tag == MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag)) ) + return; + mp4sys_remove_descriptor_in_predefined_list( esd, offsetof( mp4sys_ObjectDescriptor_t, esDescr ) ); +} + +static void mp4sys_remove_ES_ID_Inc( mp4sys_ES_ID_Inc_t *es_id_inc ) +{ + if( !es_id_inc ) + return; + mp4sys_remove_descriptor_in_predefined_list( es_id_inc, offsetof( mp4sys_ObjectDescriptor_t, esDescr ) ); +} + +static void mp4sys_remove_ObjectDescriptor( mp4sys_ObjectDescriptor_t *od ) +{ +} + +static inline uint32_t mp4sys_get_descriptor_header_size( uint32_t payload_size_in_byte ) +{ +#if ALWAYS_28BITS_LENGTH_CODING + return 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 i + 1; /* +1 means tag's space */ +#endif +} + +/* returns total size of descriptor, including header, 2 at least */ +static inline uint32_t mp4sys_get_descriptor_size( uint32_t payload_size_in_byte ) +{ + return payload_size_in_byte + mp4sys_get_descriptor_header_size( payload_size_in_byte ); +} + +static inline void mp4sys_write_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header ) +{ + 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 ); +} + +int mp4sys_write_descriptor( lsmash_bs_t *bs, void *opaque_descriptor ); + +static int mp4sys_write_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_DecoderSpecificInfo_t *dsi ) +{ + if( dsi->data && dsi->header.size != 0 ) + lsmash_bs_put_bytes( bs, dsi->header.size, dsi->data ); + return 0; +} + +static int mp4sys_write_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_DecoderConfigDescriptor_t *dcd ) +{ + 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 ); + /* Here, profileLevelIndicationIndexDescriptor is omitted. */ + return 0; +} + +static int mp4sys_write_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_SLConfigDescriptor_t *slcd ) +{ + lsmash_bs_put_byte( bs, slcd->predefined ); + if( slcd->predefined == 0x00 ) + { + uint8_t temp8; + temp8 = slcd->useAccessUnitStartFlag << 7; + temp8 |= slcd->useAccessUnitEndFlag << 6; + temp8 |= slcd->useRandomAccessPointFlag << 5; + temp8 |= slcd->hasRandomAccessUnitsOnlyFlag << 4; + temp8 |= slcd->usePaddingFlag << 3; + temp8 |= slcd->useTimeStampsFlag << 2; + temp8 |= slcd->useIdleFlag << 1; + temp8 |= slcd->durationFlag; + lsmash_bs_put_byte( bs, temp8 ); + lsmash_bs_put_be32( bs, slcd->timeStampResolution ); + lsmash_bs_put_be32( bs, slcd->OCRResolution ); + lsmash_bs_put_byte( bs, slcd->timeStampLength ); + lsmash_bs_put_byte( bs, slcd->OCRLength ); + lsmash_bs_put_byte( bs, slcd->AU_Length ); + lsmash_bs_put_byte( bs, slcd->instantBitrateLength ); + uint16_t temp16; + temp16 = slcd->degradationPriorityLength << 12; + temp16 |= slcd->AU_seqNumLength << 7; + temp16 |= slcd->packetSeqNumLength << 2; + temp16 |= slcd->reserved; + lsmash_bs_put_be16( bs, temp16 ); + } + if( slcd->durationFlag ) + { + lsmash_bs_put_be32( bs, slcd->timeScale ); + lsmash_bs_put_be16( bs, slcd->accessUnitDuration ); + lsmash_bs_put_be16( bs, slcd->compositionUnitDuration ); + } + if( !slcd->useTimeStampsFlag ) + { + lsmash_bits_t *bits = lsmash_bits_create( bs ); + if( !bits ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bits_put( bits, slcd->timeStampLength, slcd->startDecodingTimeStamp ); + lsmash_bits_put( bits, slcd->timeStampLength, slcd->startCompositionTimeStamp ); + lsmash_bits_put_align( bits ); + lsmash_bits_cleanup( bits ); + } + return 0; +} + +static int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) +{ + 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->URLlength, esd->URLstring ); + } + if( esd->OCRstreamFlag ) + lsmash_bs_put_be16( bs, esd->OCR_ES_Id ); + /* Here, some syntax elements are omitted due to previous flags (all 0). */ + return 0; +} + +static int mp4sys_write_ES_ID_Inc( lsmash_bs_t *bs, mp4sys_ES_ID_Inc_t *es_id_inc ) +{ + lsmash_bs_put_be32( bs, es_id_inc->Track_ID ); + return 0; +} + +static int mp4sys_write_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t *od ) +{ + 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 ); + } + return 0; +} + +static int mp4sys_write_children( lsmash_bs_t *bs, mp4sys_descriptor_t *descriptor ) +{ + for( lsmash_entry_t *entry = descriptor->children.head; entry; entry = entry->next ) + { + mp4sys_descriptor_t *child = (mp4sys_descriptor_t *)entry->data; + if( !child ) + continue; + int ret = mp4sys_write_descriptor( bs, child ); + if( ret < 0 ) + return ret; + } + return 0; +} + +int mp4sys_write_descriptor( lsmash_bs_t *bs, void *opaque_descriptor ) +{ + if( !bs || !opaque_descriptor ) + return LSMASH_ERR_NAMELESS; + mp4sys_descriptor_t *descriptor = (mp4sys_descriptor_t *)opaque_descriptor; + mp4sys_write_descriptor_header( bs, &descriptor->header ); + if( !descriptor->write ) + return 0; + int err = descriptor->write( bs, descriptor ); + if( err < 0 ) + return err; + return mp4sys_write_children( bs, descriptor ); +} + +/* descriptor size updater */ +uint32_t mp4sys_update_descriptor_size( void *opaque_descriptor ) +{ + assert( opaque_descriptor ); + mp4sys_descriptor_t *descriptor = (mp4sys_descriptor_t *)opaque_descriptor; + uint64_t size = 0; + if( descriptor->write ) + { + uint32_t header_size = descriptor->header.size; + /* Calculate the size of this descriptor excluding its children with a fake bytestream writer. */ + { + lsmash_bs_t fake_bs = { NULL }; + mp4sys_write_descriptor_header( &fake_bs, &descriptor->header ); + if( descriptor->write( &fake_bs, descriptor ) == 0 ) + size = lsmash_bs_get_valid_data_size( &fake_bs ); + } + /* Calculate the size including the children if no error. */ + if( size >= mp4sys_get_descriptor_header_size( header_size ) ) + { + for( lsmash_entry_t *entry = descriptor->children.head; entry; entry = entry->next ) + if( entry->data ) + size += mp4sys_update_descriptor_size( entry->data ); + /* Calculate the size of this descriptor excluding its header. */ + size -= mp4sys_get_descriptor_header_size( header_size ); + descriptor->header.size = size; + /* Now, we get the actual size of this descriptor. */ + size += mp4sys_get_descriptor_header_size( size ); + } + else + { + /* Invalid descriptor */ + descriptor->header.size = 0; + size = 0; + } + } + else + descriptor->header.size = 0; + return size; +} + +static inline void *mp4sys_construct_descriptor_orig +( + size_t size, + mp4sys_descriptor_t *parent, + mp4sys_descriptor_destructor_t destructor, + mp4sys_descriptor_writer_t writer +) +{ + assert( size >= sizeof(mp4sys_BaseDescriptor_t) ); + mp4sys_descriptor_t *descriptor = lsmash_malloc_zero( size ); + if( !descriptor ) + return NULL; + descriptor->class = &lsmash_mp4sys_class; + descriptor->parent = parent; + descriptor->destruct = destructor; + descriptor->write = writer; + return descriptor; +} + +#define mp4sys_construct_descriptor( size, parent, destructor, writer ) \ + mp4sys_construct_descriptor_orig \ + ( \ + size, \ + (mp4sys_descriptor_t *)parent, \ + (mp4sys_descriptor_destructor_t)destructor, \ + (mp4sys_descriptor_writer_t)writer \ + ) + +#define MP4SYS_CONSTRUCT_DESCRIPTOR( var, descriptor_name, parent, ret ) \ + mp4sys_##descriptor_name##_t *var = \ + mp4sys_construct_descriptor \ + ( \ + sizeof(mp4sys_##descriptor_name##_t), \ + parent, \ + mp4sys_remove_##descriptor_name, \ + mp4sys_write_##descriptor_name \ + ); \ + if( !var ) \ + return ret + +static mp4sys_DecoderSpecificInfo_t *mp4sys_add_DecoderSpecificInfo( mp4sys_DecoderConfigDescriptor_t *dcd ) +{ + if( !dcd ) + return NULL; + MP4SYS_CONSTRUCT_DESCRIPTOR( dsi, DecoderSpecificInfo, dcd, NULL ); + dsi->header.tag = MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag; + if( lsmash_add_entry( &dcd->children, dsi ) < 0 ) + { + mp4sys_remove_descriptor( dsi ); + return NULL; + } + dcd->decSpecificInfo = dsi; + return dsi; +} + +/* + bufferSizeDB is byte unit, NOT bit unit. + avgBitrate is 0 if VBR +*/ +static mp4sys_DecoderConfigDescriptor_t *mp4sys_add_DecoderConfigDescriptor +( + mp4sys_ES_Descriptor_t *esd +) +{ + if( !esd ) + return NULL; + MP4SYS_CONSTRUCT_DESCRIPTOR( dcd, DecoderConfigDescriptor, esd, NULL ); + dcd->header.tag = MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag; + if( lsmash_add_entry( &esd->children, dcd ) < 0 ) + { + mp4sys_remove_descriptor( dcd ); + return NULL; + } + esd->decConfigDescr = dcd; + return dcd; +} + +static mp4sys_SLConfigDescriptor_t *mp4sys_add_SLConfigDescriptor( mp4sys_ES_Descriptor_t *esd ) +{ + if( !esd ) + return NULL; + MP4SYS_CONSTRUCT_DESCRIPTOR( slcd, SLConfigDescriptor, esd, NULL ); + slcd->header.tag = MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag; + if( lsmash_add_entry( &esd->children, slcd ) < 0 ) + { + mp4sys_remove_descriptor( slcd ); + return NULL; + } + esd->slConfigDescr = slcd; + return slcd; +} + +/* NOTE: This is only for MP4_IOD and MP4_OD, not for ISO Base Media's ObjectDescriptor and InitialObjectDescriptor */ +static mp4sys_ES_ID_Inc_t *mp4sys_add_ES_ID_Inc( mp4sys_ObjectDescriptor_t *od ) +{ + if( !od + || (od->header.tag != MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag + && od->header.tag != MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag) ) + return NULL; + MP4SYS_CONSTRUCT_DESCRIPTOR( es_id_inc, ES_ID_Inc, od, NULL ); + es_id_inc->header.tag = MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag; + if( lsmash_add_entry( &od->children, es_id_inc ) < 0 ) + { + mp4sys_remove_descriptor( es_id_inc ); + return NULL; + } + if( lsmash_add_entry( &od->esDescr, es_id_inc ) < 0 ) + { + lsmash_remove_entry_tail( &od->children, mp4sys_remove_ES_ID_Inc ); + return NULL; + } + return es_id_inc; +} + +int mp4sys_create_ES_ID_Inc( mp4sys_ObjectDescriptor_t *od, uint32_t Track_ID ) +{ + mp4sys_ES_ID_Inc_t *es_id_inc = mp4sys_add_ES_ID_Inc( od ); + if( !es_id_inc ) + return LSMASH_ERR_NAMELESS; + es_id_inc->Track_ID = Track_ID; + return 0; +} + +/* ES_ID of the ES Descriptor is stored as 0 when the ES Descriptor is built into sample descriptions in MP4 file format + * since the lower 16 bits of the track_ID is used, instead of ES_ID, for the identifier of the elemental stream within the track. */ +mp4sys_ES_Descriptor_t *mp4sys_create_ES_Descriptor( uint16_t ES_ID ) +{ + MP4SYS_CONSTRUCT_DESCRIPTOR( esd, ES_Descriptor, NULL, NULL ); + esd->header.tag = MP4SYS_DESCRIPTOR_TAG_ES_DescrTag; + esd->ES_ID = ES_ID; + return esd; +} + +/* NOTE: This is only for MP4_OD, not for ISO Base Media's ObjectDescriptor */ +mp4sys_ObjectDescriptor_t *mp4sys_create_ObjectDescriptor( uint16_t ObjectDescriptorID ) +{ + MP4SYS_CONSTRUCT_DESCRIPTOR( od, ObjectDescriptor, NULL, NULL ); + od->header.tag = MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag; + od->ObjectDescriptorID = ObjectDescriptorID; + od->includeInlineProfileLevelFlag = 1; /* 1 as part of reserved flag. */ + od->ODProfileLevelIndication = MP4SYS_OD_PLI_NONE_REQUIRED; + od->sceneProfileLevelIndication = MP4SYS_SCENE_PLI_NONE_REQUIRED; + od->audioProfileLevelIndication = MP4A_AUDIO_PLI_NONE_REQUIRED; + od->visualProfileLevelIndication = MP4SYS_VISUAL_PLI_NONE_REQUIRED; + od->graphicsProfileLevelIndication = MP4SYS_GRAPHICS_PLI_NONE_REQUIRED; + return od; +} + +/* NOTE: This is only for MP4_IOD, not for Iso Base Media's InitialObjectDescriptor */ +int mp4sys_to_InitialObjectDescriptor +( + mp4sys_ObjectDescriptor_t *od, + uint8_t include_inline_pli, + mp4sys_ODProfileLevelIndication od_pli, + mp4sys_sceneProfileLevelIndication scene_pli, + mp4a_audioProfileLevelIndication audio_pli, + mp4sys_visualProfileLevelIndication visual_pli, + mp4sys_graphicsProfileLevelIndication graph_pli +) +{ + if( !od ) + return LSMASH_ERR_NAMELESS; + 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; +} + +#ifdef LSMASH_DEMUXER_ENABLED +/* + 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 LSMASH_ERR_NAMELESS; + mp4sys_DecoderConfigDescriptor_t *dcd = esd->decConfigDescr; + dcd->bufferSizeDB = bufferSizeDB; + dcd->maxBitrate = maxBitrate; + dcd->avgBitrate = avgBitrate; + return 0; +} + +void mp4sys_print_descriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ); + +static void mp4sys_print_descriptor_header( FILE *fp, mp4sys_descriptor_head_t *header, int indent ) +{ + static const char *descriptor_names_table[256] = + { + "Forbidden", + "ObjectDescriptor", + "InitialObjectDescriptor", + "ES_Descriptor", + "DecoderConfigDescriptor", + "DecoderSpecificInfo", + "SLConfigDescriptor", + [0x0E] = "ES_ID_Inc", + [0x0F] = "ES_ID_Ref", + [0x10] = "MP4_IOD", + [0x11] = "MP4_OD" + }; + if( descriptor_names_table[ header->tag ] ) + lsmash_ifprintf( fp, indent, "[tag = 0x%02"PRIx8": %s]\n", header->tag, descriptor_names_table[ header->tag ] ); + else + lsmash_ifprintf( fp, indent, "[tag = 0x%02"PRIx8"]\n", header->tag ); + lsmash_ifprintf( fp, ++indent, "expandableClassSize = %"PRIu32"\n", header->size ); +} + +static void mp4sys_print_DecoderSpecificInfo( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + extern void mp4a_print_AudioSpecificConfig( FILE *, uint8_t *, uint32_t, int ); + if( !descriptor->parent || descriptor->parent->header.tag != MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag ) + return; + mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)descriptor->parent; + if( dcd->streamType != MP4SYS_STREAM_TYPE_AudioStream + || dcd->objectTypeIndication != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 ) + return; /* We support only AudioSpecificConfig here currently. */ + mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)descriptor; + mp4a_print_AudioSpecificConfig( fp, dsi->data, dsi->header.size, indent ); +} + +static void mp4sys_print_DecoderConfigDescriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)descriptor; + static const char *object_type_indication_descriptions_table[256] = + { + "Forbidden", + "Systems ISO/IEC 14496-1 (a)", + "Systems ISO/IEC 14496-1 (b)", + "Interaction Stream", + "Systems ISO/IEC 14496-1 Extended BIFS Configuration", + "Systems ISO/IEC 14496-1 AFX", + "Font Data Stream", + "Synthesized Texture Stream", + "Streaming Text Stream", + "LASeR Stream", + "Simple Aggregation Format (SAF) Stream", + [0x20] = "Visual ISO/IEC 14496-2", + [0x21] = "Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10", + [0x22] = "Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10", + [0x40] = "Audio ISO/IEC 14496-3", + [0x60] = "Visual ISO/IEC 13818-2 Simple Profile", + [0x61] = "Visual ISO/IEC 13818-2 Main Profile", + [0x62] = "Visual ISO/IEC 13818-2 SNR Profile", + [0x63] = "Visual ISO/IEC 13818-2 Spatial Profile", + [0x64] = "Visual ISO/IEC 13818-2 High Profile", + [0x65] = "Visual ISO/IEC 13818-2 422 Profile", + [0x66] = "Audio ISO/IEC 13818-7 Main Profile", + [0x67] = "Audio ISO/IEC 13818-7 LowComplexity Profile", + [0x68] = "Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile", + [0x69] = "Audio ISO/IEC 13818-3", + [0x6A] = "Visual ISO/IEC 11172-2", + [0x6B] = "Audio ISO/IEC 11172-3", + [0x6C] = "Visual ISO/IEC 10918-1", + [0x6D] = "Portable Network Graphics", + [0x6E] = "Visual ISO/IEC 15444-1 (JPEG 2000)", + [0xA0] = "EVRC Voice", + [0xA1] = "SMV Voice", + [0xA2] = "3GPP2 Compact Multimedia Format (CMF)", + [0xA3] = "SMPTE VC-1 Video", + [0xA4] = "Dirac Video Coder", + [0xA5] = "AC-3 Audio", + [0xA6] = "Enhanced AC-3 audio", + [0xA7] = "DRA Audio", + [0xA8] = "ITU G.719 Audio", + [0xA9] = "DTS Coherent Acoustics audio", + [0xAA] = "DTS-HD High Resolution Audio", + [0xAB] = "DTS-HD Master Audio", + [0xAC] = "DTS Express low bit rate audio", + [0xE1] = "13K Voice", + [0xFF] = "no object type specified" + }; + static const char *stream_type_descriptions_table[64] = + { + "Forbidden", + "ObjectDescriptorStream", + "ClockReferenceStream", + "SceneDescriptionStream", + "VisualStream", + "AudioStream", + "MPEG7Stream", + "IPMPStream", + "ObjectContentInfoStream", + "MPEGJStream", + "Interaction Stream", + "IPMPToolStream", + "FontDataStream", + "StreamingText" + }; + if( object_type_indication_descriptions_table[ dcd->objectTypeIndication ] ) + lsmash_ifprintf( fp, indent, "objectTypeIndication = 0x%02"PRIx8" (%s)\n", dcd->objectTypeIndication, object_type_indication_descriptions_table[ dcd->objectTypeIndication ] ); + else + lsmash_ifprintf( fp, indent, "objectTypeIndication = 0x%02"PRIx8"\n", dcd->objectTypeIndication ); + if( stream_type_descriptions_table[ dcd->streamType ] ) + lsmash_ifprintf( fp, indent, "streamType = 0x%02"PRIx8" (%s)\n", dcd->streamType, stream_type_descriptions_table[ dcd->streamType ] ); + else + lsmash_ifprintf( fp, indent, "streamType = 0x%02"PRIx8"\n", dcd->streamType ); + lsmash_ifprintf( fp, indent, "upStream = %"PRIu8"\n", dcd->upStream ); + lsmash_ifprintf( fp, indent, "reserved = %"PRIu8"\n", dcd->reserved ); + lsmash_ifprintf( fp, indent, "bufferSizeDB = %"PRIu32"\n", dcd->bufferSizeDB ); + lsmash_ifprintf( fp, indent, "maxBitrate = %"PRIu32"\n", dcd->maxBitrate ); + lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32"%s\n", dcd->avgBitrate, dcd->avgBitrate ? "" : " (variable bitrate)" ); +} + +static void mp4sys_print_SLConfigDescriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + mp4sys_SLConfigDescriptor_t *slcd = (mp4sys_SLConfigDescriptor_t *)descriptor; + lsmash_ifprintf( fp, indent, "predefined = %"PRIu8"\n", slcd->predefined ); + if( slcd->predefined == 0 ) + { + lsmash_ifprintf( fp, indent, "useAccessUnitStartFlag = %"PRIu8"\n", slcd->useAccessUnitStartFlag ); + lsmash_ifprintf( fp, indent, "useAccessUnitEndFlag = %"PRIu8"\n", slcd->useAccessUnitEndFlag ); + lsmash_ifprintf( fp, indent, "useRandomAccessPointFlag = %"PRIu8"\n", slcd->useRandomAccessPointFlag ); + lsmash_ifprintf( fp, indent, "hasRandomAccessUnitsOnlyFlag = %"PRIu8"\n", slcd->hasRandomAccessUnitsOnlyFlag ); + lsmash_ifprintf( fp, indent, "usePaddingFlag = %"PRIu8"\n", slcd->usePaddingFlag ); + lsmash_ifprintf( fp, indent, "useTimeStampsFlag = %"PRIu8"\n", slcd->useTimeStampsFlag ); + lsmash_ifprintf( fp, indent, "useIdleFlag = %"PRIu8"\n", slcd->useIdleFlag ); + lsmash_ifprintf( fp, indent, "durationFlag = %"PRIu8"\n", slcd->durationFlag ); + lsmash_ifprintf( fp, indent, "timeStampResolution = %"PRIu32"\n", slcd->timeStampResolution ); + lsmash_ifprintf( fp, indent, "OCRResolution = %"PRIu32"\n", slcd->OCRResolution ); + lsmash_ifprintf( fp, indent, "timeStampLength = %"PRIu8"\n", slcd->timeStampLength ); + lsmash_ifprintf( fp, indent, "OCRLength = %"PRIu8"\n", slcd->OCRLength ); + lsmash_ifprintf( fp, indent, "AU_Length = %"PRIu8"\n", slcd->AU_Length ); + lsmash_ifprintf( fp, indent, "instantBitrateLength = %"PRIu8"\n", slcd->instantBitrateLength ); + lsmash_ifprintf( fp, indent, "degradationPriorityLength = %"PRIu8"\n", slcd->degradationPriorityLength ); + lsmash_ifprintf( fp, indent, "AU_seqNumLength = %"PRIu8"\n", slcd->AU_seqNumLength ); + lsmash_ifprintf( fp, indent, "packetSeqNumLength = %"PRIu8"\n", slcd->packetSeqNumLength ); + lsmash_ifprintf( fp, indent, "reserved = 0x%01"PRIx8"\n", slcd->reserved ); + } + if( slcd->durationFlag ) + { + lsmash_ifprintf( fp, indent, "timeScale = %"PRIu32"\n", slcd->timeScale ); + lsmash_ifprintf( fp, indent, "accessUnitDuration = %"PRIu16"\n", slcd->accessUnitDuration ); + lsmash_ifprintf( fp, indent, "compositionUnitDuration = %"PRIu16"\n", slcd->compositionUnitDuration ); + } + if( !slcd->useTimeStampsFlag ) + { + lsmash_ifprintf( fp, indent, "startDecodingTimeStamp = %"PRIu64"\n", slcd->startDecodingTimeStamp ); + lsmash_ifprintf( fp, indent, "startCompositionTimeStamp = %"PRIu64"\n", slcd->startCompositionTimeStamp ); + } +} + +static void mp4sys_print_ES_Descriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)descriptor; + lsmash_ifprintf( fp, indent, "ES_ID = %"PRIu16"\n", esd->ES_ID ); + lsmash_ifprintf( fp, indent, "streamDependenceFlag = %"PRIu8"\n", esd->streamDependenceFlag ); + lsmash_ifprintf( fp, indent, "URL_Flag = %"PRIu8"\n", esd->URL_Flag ); + lsmash_ifprintf( fp, indent, "OCRstreamFlag = %"PRIu8"\n", esd->OCRstreamFlag ); + lsmash_ifprintf( fp, indent, "streamPriority = %"PRIu8"\n", esd->streamPriority ); + if( esd->streamDependenceFlag ) + lsmash_ifprintf( fp, indent, "dependsOn_ES_ID = %"PRIu16"\n", esd->dependsOn_ES_ID ); + if( esd->URL_Flag ) + { + lsmash_ifprintf( fp, indent, "URLlength = %"PRIu8"\n", esd->URLlength ); + lsmash_ifprintf( fp, indent, "URLstring = %s\n", esd->URLstring ); + } + if( esd->OCRstreamFlag ) + lsmash_ifprintf( fp, indent, "OCR_ES_Id = %"PRIu16"\n", esd->OCR_ES_Id ); +} + +static void mp4sys_print_ES_ID_Inc( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + mp4sys_ES_ID_Inc_t *es_id_inc = (mp4sys_ES_ID_Inc_t *)descriptor; + lsmash_ifprintf( fp, indent, "Track_ID = %"PRIu32"\n", es_id_inc->Track_ID ); +} + +static void mp4sys_print_ObjectDescriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + mp4sys_ObjectDescriptor_t *od = (mp4sys_ObjectDescriptor_t *)descriptor; + lsmash_ifprintf( fp, indent, "ObjectDescriptorID = %"PRIu16"\n", od->ObjectDescriptorID ); + lsmash_ifprintf( fp, indent, "URL_Flag = %"PRIu8"\n", od->URL_Flag ); + if( od->header.tag == MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag + || od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ) + { + lsmash_ifprintf( fp, indent, "includeInlineProfileLevelFlag = %"PRIu8"\n", od->includeInlineProfileLevelFlag ); + lsmash_ifprintf( fp, indent, "reserved = 0x%01"PRIx8"\n", od->reserved ); + } + else + lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", od->reserved | (od->includeInlineProfileLevelFlag << 4) ); + if( od->URL_Flag ) + { + lsmash_ifprintf( fp, indent, "URLlength = %"PRIu8"\n", od->URLlength ); + lsmash_ifprintf( fp, indent, "URLstring = %s\n", od->URLstring ); + } + else + { + if( od->header.tag == MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag + || od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ) + { + lsmash_ifprintf( fp, indent, "ODProfileLevelIndication = 0x%02"PRIx8"\n", od->ODProfileLevelIndication ); + lsmash_ifprintf( fp, indent, "sceneProfileLevelIndication = 0x%02"PRIx8"\n", od->sceneProfileLevelIndication ); + lsmash_ifprintf( fp, indent, "audioProfileLevelIndication = 0x%02"PRIx8"\n", od->audioProfileLevelIndication ); + lsmash_ifprintf( fp, indent, "visualProfileLevelIndication = 0x%02"PRIx8"\n", od->visualProfileLevelIndication ); + lsmash_ifprintf( fp, indent, "graphicsProfileLevelIndication = 0x%02"PRIx8"\n", od->graphicsProfileLevelIndication ); + } + } +} + +void mp4sys_print_descriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ) +{ + if( !descriptor ) + return; + mp4sys_print_descriptor_header( fp, &descriptor->header, indent++ ); + switch( descriptor->header.tag ) + { + case MP4SYS_DESCRIPTOR_TAG_ObjectDescrTag : + case MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag : + case MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag : + case MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag : + mp4sys_print_ObjectDescriptor( fp, descriptor, indent ); + break; + case MP4SYS_DESCRIPTOR_TAG_ES_DescrTag : + mp4sys_print_ES_Descriptor( fp, descriptor, indent ); + break; + case MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag : + mp4sys_print_DecoderConfigDescriptor( fp, descriptor, indent ); + break; + case MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag : + mp4sys_print_DecoderSpecificInfo( fp, descriptor, indent ); + break; + case MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag : + mp4sys_print_SLConfigDescriptor( fp, descriptor, indent ); + break; + case MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag : + mp4sys_print_ES_ID_Inc( fp, descriptor, indent ); + break; + default : + break; + } + for( lsmash_entry_t *entry = descriptor->children.head; entry; entry = entry->next ) + if( entry->data ) + mp4sys_print_descriptor( fp, entry->data, indent ); +} + +int mp4sys_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + assert( fp && file && box && !(box->manager & LSMASH_BINARY_CODED_BOX) ); + isom_esds_t *esds = (isom_esds_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: Elemental Stream Descriptor Box]\n", isom_4cc2str( esds->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", esds->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", esds->size ); + lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", esds->version ); + lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", esds->flags & 0x00ffffff ); + mp4sys_print_descriptor( fp, (mp4sys_descriptor_t *)esds->ES, indent ); + return 0; +} +#endif /* LSMASH_DEMUXER_ENABLED */ + +mp4sys_descriptor_t *mp4sys_get_descriptor( lsmash_bs_t *bs, void *parent ); + +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 mp4sys_DecoderSpecificInfo_t *mp4sys_get_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header, void *parent ) +{ + mp4sys_DecoderSpecificInfo_t *dsi = mp4sys_add_DecoderSpecificInfo( parent ); + if( !dsi ) + return NULL; + dsi->header.size = header->size; + if( dsi->header.size ) + { + dsi->data = lsmash_bs_get_bytes( bs, dsi->header.size ); + if( !dsi->data ) + { + mp4sys_remove_descriptor( dsi ); + return NULL; + } + } + return dsi; +} + +static mp4sys_DecoderConfigDescriptor_t *mp4sys_get_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header, void *parent ) +{ + mp4sys_DecoderConfigDescriptor_t *dcd = mp4sys_add_DecoderConfigDescriptor( parent ); + if( !dcd ) + return NULL; + uint64_t end_pos = header->size + lsmash_bs_count( bs ); + dcd->header.size = header->size; + 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 ); + while( lsmash_bs_count( bs ) < end_pos ) + { + mp4sys_descriptor_t *desc = mp4sys_get_descriptor( bs, dcd ); + if( desc ) + { + if( desc->header.tag == MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag ) + dcd->decSpecificInfo = (mp4sys_DecoderSpecificInfo_t *)desc; + else + mp4sys_remove_descriptor( desc ); + } + else + break; + } + return dcd; +} + +static mp4sys_SLConfigDescriptor_t *mp4sys_get_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header, void *parent ) +{ + mp4sys_SLConfigDescriptor_t *slcd = mp4sys_add_SLConfigDescriptor( parent ); + if( !slcd ) + return NULL; + slcd->header.size = header->size; + 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 ) + { + mp4sys_remove_descriptor( slcd ); + return NULL; + } + slcd->startDecodingTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength ); + slcd->startCompositionTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength ); + lsmash_bits_cleanup( bits ); + } + return slcd; +} + +static mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header, void *parent ) +{ + MP4SYS_CONSTRUCT_DESCRIPTOR( esd, ES_Descriptor, parent, NULL ); + if( parent && lsmash_add_entry( &((mp4sys_descriptor_t *)parent)->children, esd ) < 0 ) + { + mp4sys_remove_descriptor( esd ); + return NULL; + } + uint64_t end_pos = header->size + lsmash_bs_count( bs ); + esd->header = *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 ) + { + size_t length = lsmash_bs_get_byte( bs ); + lsmash_bs_read_data( bs, (uint8_t *)esd->URLstring, &length ); + esd->URLlength = length; + } + if( esd->OCRstreamFlag ) + esd->OCR_ES_Id = lsmash_bs_get_be16( bs ); + /* DecoderConfigDescriptor and SLConfigDescriptor are mandatory. */ + while( lsmash_bs_count( bs ) < end_pos ) + { + mp4sys_descriptor_t *desc = mp4sys_get_descriptor( bs, esd ); + if( desc ) + { + if( desc->header.tag == MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag ) + esd->decConfigDescr = (mp4sys_DecoderConfigDescriptor_t *)desc; + else if( desc->header.tag == MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag ) + esd->slConfigDescr = (mp4sys_SLConfigDescriptor_t *)desc; + else + mp4sys_remove_descriptor( desc ); + } + else + break; + } + if( !esd->decConfigDescr || !esd->slConfigDescr ) + { + mp4sys_remove_descriptor( esd ); + return NULL; + } + return esd; +} + +static mp4sys_ES_ID_Inc_t *mp4sys_get_ES_ID_Inc( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header, void *parent ) +{ + mp4sys_ES_ID_Inc_t *es_id_inc = mp4sys_add_ES_ID_Inc( parent ); + if( !es_id_inc ) + return NULL; + es_id_inc->header.size = header->size; + es_id_inc->Track_ID = lsmash_bs_get_be32( bs ); + return es_id_inc; +} + +static mp4sys_ObjectDescriptor_t *mp4sys_get_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_descriptor_head_t *header, void *parent ) +{ + MP4SYS_CONSTRUCT_DESCRIPTOR( od, ObjectDescriptor, parent, NULL ); + if( parent && lsmash_add_entry( &((mp4sys_descriptor_t *)parent)->children, od ) < 0 ) + { + mp4sys_remove_descriptor( od ); + return NULL; + } + od->header = *header; + uint64_t end_pos = header->size + lsmash_bs_count( bs ); + uint16_t temp16 = lsmash_bs_get_be16( bs ); + od->ObjectDescriptorID = (temp16 >> 6) & 0x03FF; + od->URL_Flag = (temp16 >> 5) & 0x0001; + od->includeInlineProfileLevelFlag = (temp16 >> 4) & 0x0001; + od->reserved = temp16 & 0x000F; + if( od->URL_Flag ) + { + size_t length = lsmash_bs_get_byte( bs ); + lsmash_bs_read_data( bs, (uint8_t *)od->URLstring, &length ); + od->URLlength = length; + } + else + { + if( od->header.tag == MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag + || od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ) + { + od->ODProfileLevelIndication = lsmash_bs_get_byte( bs ); + od->sceneProfileLevelIndication = lsmash_bs_get_byte( bs ); + od->audioProfileLevelIndication = lsmash_bs_get_byte( bs ); + od->visualProfileLevelIndication = lsmash_bs_get_byte( bs ); + od->graphicsProfileLevelIndication = lsmash_bs_get_byte( bs ); + } + const mp4sys_descriptor_tag at_least_one_descriptor_tag + = od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag + || od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag + ? MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag + : MP4SYS_DESCRIPTOR_TAG_ES_DescrTag; + while( lsmash_bs_count( bs ) < end_pos && od->esDescr.entry_count < 255 ) + { + mp4sys_descriptor_t *desc = mp4sys_get_descriptor( bs, od ); + if( !desc ) + break; + if( desc->header.tag != at_least_one_descriptor_tag ) + { + mp4sys_remove_descriptor( desc ); + break; + } + } + } + return od; +} + +mp4sys_descriptor_t *mp4sys_get_descriptor( lsmash_bs_t *bs, void *parent ) +{ + mp4sys_descriptor_head_t header; + mp4sys_get_descriptor_header( bs, &header ); + mp4sys_descriptor_t *desc; + switch( header.tag ) + { + case MP4SYS_DESCRIPTOR_TAG_ObjectDescrTag : + case MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag : + case MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag : + case MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag : + desc = (mp4sys_descriptor_t *)mp4sys_get_ObjectDescriptor( bs, &header, parent ); + break; + case MP4SYS_DESCRIPTOR_TAG_ES_DescrTag : + desc = (mp4sys_descriptor_t *)mp4sys_get_ES_Descriptor( bs, &header, parent ); + break; + case MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag : + desc = (mp4sys_descriptor_t *)mp4sys_get_DecoderConfigDescriptor( bs, &header, parent ); + break; + case MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag : + desc = (mp4sys_descriptor_t *)mp4sys_get_DecoderSpecificInfo( bs, &header, parent ); + break; + case MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag : + desc = (mp4sys_descriptor_t *)mp4sys_get_SLConfigDescriptor( bs, &header, parent ); + break; + case MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag : + desc = (mp4sys_descriptor_t *)mp4sys_get_ES_ID_Inc( bs, &header, parent ); + break; + default : + desc = lsmash_malloc_zero( sizeof(mp4sys_descriptor_t) ); + if( desc ) + { + desc->parent = parent; + desc->header = header; + } + break; + } + return desc; +} + +static uint8_t *mp4sys_export_DecoderSpecificInfo( mp4sys_ES_Descriptor_t *esd, uint32_t *dsi_payload_length ) +{ + if( !esd || !esd->decConfigDescr || !esd->decConfigDescr->decSpecificInfo ) + return NULL; + mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)esd->decConfigDescr->decSpecificInfo; + uint8_t *dsi_payload = NULL; + /* DecoderSpecificInfo can be absent. */ + if( dsi->header.size ) + { + dsi_payload = lsmash_memdup( dsi->data, dsi->header.size ); + if( !dsi_payload ) + return NULL; + } + if( dsi_payload_length ) + *dsi_payload_length = dsi->header.size; + return dsi_payload; +} + +/* Sumamry is needed to decide ProfileLevelIndication. + * Currently, support audio's only. */ +int mp4sys_setup_summary_from_DecoderSpecificInfo( lsmash_audio_summary_t *summary, mp4sys_ES_Descriptor_t *esd ) +{ + uint32_t dsi_payload_length = UINT32_MAX; /* arbitrary */ + uint8_t *dsi_payload = mp4sys_export_DecoderSpecificInfo( esd, &dsi_payload_length ); + if( !dsi_payload && dsi_payload_length ) + return LSMASH_ERR_NAMELESS; + int err = 0; + if( dsi_payload_length ) + { + lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !cs ) + { + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + lsmash_mp4sys_decoder_parameters_t *params = (lsmash_mp4sys_decoder_parameters_t *)cs->data.structured; + mp4sys_DecoderConfigDescriptor_t *dcd = esd->decConfigDescr; + params->objectTypeIndication = dcd->objectTypeIndication; + params->streamType = dcd->streamType; + params->bufferSizeDB = dcd->bufferSizeDB; + params->maxBitrate = dcd->maxBitrate; + params->avgBitrate = dcd->avgBitrate; + if( (err = mp4a_setup_summary_from_AudioSpecificConfig( summary, dsi_payload, dsi_payload_length )) < 0 + || (err = lsmash_set_mp4sys_decoder_specific_info( params, dsi_payload, dsi_payload_length )) < 0 + || (err = lsmash_add_entry( &summary->opaque->list, cs )) < 0 ) + { + lsmash_destroy_codec_specific_data( cs ); + goto fail; + } + } +fail: + lsmash_free( dsi_payload ); + return err; +} + +/**** 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; + /* DecoderConfigDescriptor */ + mp4sys_DecoderConfigDescriptor_t *dcd = mp4sys_add_DecoderConfigDescriptor( esd ); + if( !dcd ) + goto fail; + dcd->objectTypeIndication = params->objectTypeIndication; + dcd->streamType = params->streamType; + dcd->upStream = 0; + dcd->reserved = 1; + dcd->bufferSizeDB = params->bufferSizeDB; + dcd->maxBitrate = params->maxBitrate; + dcd->avgBitrate = params->avgBitrate; + /* DecoderSpecificInfo */ + if( params->dsi_payload && params->dsi_payload_length != 0 ) + { + mp4sys_DecoderSpecificInfo_t *dsi = mp4sys_add_DecoderSpecificInfo( dcd ); + if( !dsi ) + goto fail; + dsi->data = lsmash_memdup( params->dsi_payload, params->dsi_payload_length ); + if( !dsi->data ) + goto fail; + dsi->header.size = params->dsi_payload_length; + } + /* SLConfigDescriptor */ + { + mp4sys_SLConfigDescriptor_t *slcd = mp4sys_add_SLConfigDescriptor( esd ); + if( !slcd ) + goto fail; + slcd->predefined = 0x02; /* MP4 file which does not use URL_Flag shall have constant value 0x02 */ + slcd->useTimeStampsFlag = 1; /* set to 1 if predefined == 2 */ + } + return esd; +fail: + mp4sys_remove_descriptor( esd ); + return NULL; +} + +int lsmash_set_mp4sys_decoder_specific_info( lsmash_mp4sys_decoder_parameters_t *param, uint8_t *payload, uint32_t payload_length ) +{ + if( !param || !payload || payload_length == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !param->dsi ) + { + param->dsi = lsmash_malloc_zero( sizeof(lsmash_mp4sys_decoder_specific_info_t) ); + if( !param->dsi ) + return LSMASH_ERR_MEMORY_ALLOC; + } + else + { + lsmash_freep( ¶m->dsi->payload ); + param->dsi->payload_length = 0; + } + param->dsi->payload = lsmash_memdup( payload, payload_length ); + if( !param->dsi->payload ) + return LSMASH_ERR_MEMORY_ALLOC; + param->dsi->payload_length = payload_length; + return 0; +} + +void lsmash_destroy_mp4sys_decoder_specific_info( lsmash_mp4sys_decoder_parameters_t *param ) +{ + if( !param || !param->dsi ) + return; + lsmash_free( param->dsi->payload ); + lsmash_freep( ¶m->dsi ); +} + +void mp4sys_destruct_decoder_config( void *data ) +{ + if( !data ) + return; + lsmash_destroy_mp4sys_decoder_specific_info( data ); + lsmash_free( data ); +} + +uint8_t *lsmash_create_mp4sys_decoder_config( lsmash_mp4sys_decoder_parameters_t *param, uint32_t *data_length ) +{ + if( !param || !data_length ) + return NULL; + mp4sys_ES_Descriptor_params_t esd_param = { 0 }; + esd_param.ES_ID = 0; /* Within sample description, ES_ID is stored as 0. */ + esd_param.objectTypeIndication = param->objectTypeIndication; + esd_param.streamType = param->streamType; + esd_param.bufferSizeDB = param->bufferSizeDB; + esd_param.maxBitrate = param->maxBitrate; + esd_param.avgBitrate = param->avgBitrate; + if( param->dsi + && param->dsi->payload + && param->dsi->payload_length ) + { + esd_param.dsi_payload = param->dsi->payload; + esd_param.dsi_payload_length = param->dsi->payload_length; + } + mp4sys_ES_Descriptor_t *esd = mp4sys_setup_ES_Descriptor( &esd_param ); + if( !esd ) + return NULL; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + { + mp4sys_remove_descriptor( esd ); + return NULL; + } + lsmash_bs_put_be32( bs, 0 ); /* update later */ + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_ESDS.fourcc ); + lsmash_bs_put_be32( bs, 0 ); + mp4sys_update_descriptor_size( esd ); + mp4sys_write_descriptor( bs, esd ); + mp4sys_remove_descriptor( esd ); + uint8_t *data = lsmash_bs_export_data( bs, data_length ); + lsmash_bs_cleanup( bs ); + if( !data ) + return NULL; + /* Update box size. */ + LSMASH_SET_BE32( data, *data_length ); + return data; +} + +int mp4sys_construct_decoder_config( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < ISOM_FULLBOX_COMMON_SIZE + 23 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + data += 4; /* Skip version and flags. */ + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + int err = lsmash_bs_import_data( bs, data, src->size - (data - src->data.unstructured) ); + if( err < 0 ) + { + lsmash_bs_cleanup( bs ); + return err; + } + mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)mp4sys_get_descriptor( bs, NULL ); + lsmash_bs_cleanup( bs ); + if( !esd || esd->header.tag != MP4SYS_DESCRIPTOR_TAG_ES_DescrTag || !esd->decConfigDescr ) + return LSMASH_ERR_INVALID_DATA; + mp4sys_DecoderConfigDescriptor_t *dcd = esd->decConfigDescr; + param->objectTypeIndication = dcd->objectTypeIndication; + param->streamType = dcd->streamType; + param->bufferSizeDB = dcd->bufferSizeDB; + param->maxBitrate = dcd->maxBitrate; + param->avgBitrate = dcd->avgBitrate; + mp4sys_DecoderSpecificInfo_t *dsi = dcd->decSpecificInfo; + if( dsi + && dsi->header.size + && dsi->data + && (err = lsmash_set_mp4sys_decoder_specific_info( param, dsi->data, dsi->header.size )) < 0 ) + { + mp4sys_remove_descriptor( esd ); + return err; + } + mp4sys_remove_descriptor( esd ); + return 0; +} + +int mp4sys_copy_decoder_config( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); + assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); + lsmash_mp4sys_decoder_parameters_t *src_data = (lsmash_mp4sys_decoder_parameters_t *)src->data.structured; + lsmash_mp4sys_decoder_parameters_t *dst_data = (lsmash_mp4sys_decoder_parameters_t *)dst->data.structured; + lsmash_destroy_mp4sys_decoder_specific_info( dst_data ); + *dst_data = *src_data; + dst_data->dsi = NULL; + if( !src_data->dsi || !src_data->dsi->payload || src_data->dsi->payload_length == 0 ) + return 0; + return lsmash_set_mp4sys_decoder_specific_info( dst_data, src_data->dsi->payload, src_data->dsi->payload_length ); +} + +lsmash_mp4sys_object_type_indication lsmash_mp4sys_get_object_type_indication( lsmash_summary_t *summary ) +{ + if( !summary ) + return MP4SYS_OBJECT_TYPE_Forbidden; + lsmash_codec_specific_t *orig = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); + if( !orig ) + return MP4SYS_OBJECT_TYPE_Forbidden; + /* Found decoder configuration. + * Let's get objectTypeIndication. */ + lsmash_mp4sys_object_type_indication objectTypeIndication; + if( orig->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) + objectTypeIndication = ((lsmash_mp4sys_decoder_parameters_t *)orig->data.structured)->objectTypeIndication; + else + { + lsmash_codec_specific_t *conv = lsmash_convert_codec_specific_format( orig, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); + if( !conv ) + return MP4SYS_OBJECT_TYPE_Forbidden; + objectTypeIndication = ((lsmash_mp4sys_decoder_parameters_t *)conv->data.structured)->objectTypeIndication; + lsmash_destroy_codec_specific_data( conv ); + } + return objectTypeIndication; +} + +int lsmash_get_mp4sys_decoder_specific_info( lsmash_mp4sys_decoder_parameters_t *param, uint8_t **payload, uint32_t *payload_length ) +{ + if( !param || !payload || !payload_length ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !param->dsi || !param->dsi->payload || param->dsi->payload_length == 0 ) + { + *payload = NULL; + *payload_length = 0; + return 0; + } + uint8_t *temp = lsmash_memdup( param->dsi->payload, param->dsi->payload_length ); + if( !temp ) + return LSMASH_ERR_MEMORY_ALLOC; + *payload = temp; + *payload_length = param->dsi->payload_length; + return 0; +} diff -Nru l-smash-1.9.1/codecs/mp4sys.h l-smash-2.3.0/codecs/mp4sys.h --- l-smash-1.9.1/codecs/mp4sys.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/mp4sys.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,191 @@ +/***************************************************************************** + * mp4sys.h: + ***************************************************************************** + * Copyright (C) 2010-2014 L-SMASH project + * + * Authors: Takashi Hirata + * 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 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:2010 */ + /* 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; + +struct lsmash_mp4sys_decoder_specific_info_tag +{ + uint8_t *payload; + uint32_t payload_length; +}; + +#ifndef MP4SYS_INTERNAL + +typedef void mp4sys_descriptor_t; +typedef void mp4sys_ES_Descriptor_t; +typedef void mp4sys_ObjectDescriptor_t; + +void mp4sys_remove_descriptor( mp4sys_descriptor_t *descriptor ); + +/* ES_ID of the ES Descriptor is stored as 0 when the ES Descriptor is built into sample descriptions in MP4 file format + * since the lower 16 bits of the track_ID is used, instead of ES_ID, for the identifier of the elemental stream within the track. */ +mp4sys_ES_Descriptor_t *mp4sys_create_ES_Descriptor( uint16_t ES_ID ); +mp4sys_ObjectDescriptor_t *mp4sys_create_ObjectDescriptor( uint16_t ObjectDescriptorID ); +int mp4sys_create_ES_ID_Inc( mp4sys_ObjectDescriptor_t *od, uint32_t Track_ID ); + +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 +); + +int mp4sys_update_DecoderConfigDescriptor +( + mp4sys_ES_Descriptor_t *esd, + uint32_t bufferSizeDB, + uint32_t maxBitrate, + uint32_t avgBitrate +); + +uint32_t mp4sys_update_descriptor_size( mp4sys_descriptor_t *descriptor ); +int mp4sys_write_descriptor( lsmash_bs_t *bs, mp4sys_descriptor_t *descriptor ); +void mp4sys_print_descriptor( FILE *fp, mp4sys_descriptor_t *descriptor, int indent ); +mp4sys_descriptor_t *mp4sys_get_descriptor( lsmash_bs_t *bs, mp4sys_descriptor_t *parent ); + +int mp4sys_setup_summary_from_DecoderSpecificInfo( lsmash_audio_summary_t *summary, mp4sys_ES_Descriptor_t *esd ); + +/* 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 -Nru l-smash-1.9.1/codecs/nalu.h l-smash-2.3.0/codecs/nalu.h --- l-smash-1.9.1/codecs/nalu.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/nalu.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,253 @@ +/***************************************************************************** + * nalu.h: + ***************************************************************************** + * Copyright (C) 2013-2014 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. */ + +#define NALU_DEFAULT_BUFFER_SIZE (1<<16) +#define NALU_DEFAULT_NALU_LENGTH_SIZE 4 /* We always use 4 bytes length. */ +#define NALU_SHORT_START_CODE_LENGTH 3 +#define NALU_LONG_START_CODE_LENGTH 4 +#define NALU_NO_START_CODE_FOUND UINT64_MAX + +static inline uint64_t nalu_get_codeNum +( + lsmash_bits_t *bits +) +{ + uint32_t leadingZeroBits = 0; + for( int b = 0; !b; leadingZeroBits++ ) + b = lsmash_bits_get( bits, 1 ); + --leadingZeroBits; + return ((uint64_t)1 << leadingZeroBits) - 1 + lsmash_bits_get( bits, leadingZeroBits ); +} + +static inline uint64_t nalu_decode_exp_golomb_ue +( + uint64_t codeNum +) +{ + return codeNum; +} + +static inline int64_t nalu_decode_exp_golomb_se +( + uint64_t codeNum +) +{ + if( codeNum & 1 ) + return (int64_t)((codeNum >> 1) + 1); + return -1 * (int64_t)(codeNum >> 1); +} + +static inline uint64_t nalu_get_exp_golomb_ue +( + lsmash_bits_t *bits +) +{ + uint64_t codeNum = nalu_get_codeNum( bits ); + return nalu_decode_exp_golomb_ue( codeNum ); +} + +static inline uint64_t nalu_get_exp_golomb_se +( + lsmash_bits_t *bits +) +{ + uint64_t codeNum = nalu_get_codeNum( bits ); + return nalu_decode_exp_golomb_se( codeNum ); +} + +/* Convert EBSP (Encapsulated Byte Sequence Packets) to RBSP (Raw Byte Sequence Packets). */ +static inline uint8_t *nalu_remove_emulation_prevention +( + uint8_t *src, + uint64_t src_length, + uint8_t *dst +) +{ + uint8_t *src_end = src + src_length; + while( src < src_end ) + if( ((src + 2) < src_end) && !src[0] && !src[1] && (src[2] == 0x03) ) + { + /* 0x000003 -> 0x0000 */ + *dst++ = *src++; + *dst++ = *src++; + src++; /* Skip emulation_prevention_three_byte (0x03). */ + } + else + *dst++ = *src++; + return dst; +} + +static inline int nalu_import_rbsp_from_ebsp +( + lsmash_bits_t *bits, + uint8_t *rbsp_buffer, + uint8_t *ebsp, + uint64_t ebsp_size +) +{ + uint8_t *rbsp_start = rbsp_buffer; + uint8_t *rbsp_end = nalu_remove_emulation_prevention( ebsp, ebsp_size, rbsp_buffer ); + uint64_t rbsp_length = rbsp_end - rbsp_start; + return lsmash_bits_import_data( bits, rbsp_start, rbsp_length ); +} + +static inline int nalu_check_more_rbsp_data +( + lsmash_bits_t *bits +) +{ + lsmash_bs_t *bs = bits->bs; + lsmash_buffer_t *buffer = &bs->buffer; + if( buffer->pos < buffer->store && !(bits->store == 0 && (buffer->store == buffer->pos + 1)) ) + return 1; /* rbsp_trailing_bits will be placed at the next or later byte. + * Note: bs->pos points at the next byte if bits->store isn't empty. */ + if( bits->store == 0 ) + { + if( buffer->store == buffer->pos + 1 ) + return buffer->data[ buffer->pos ] != 0x80; + /* No rbsp_trailing_bits is present in RBSP data. */ + bs->error = 1; + return 0; + } + /* Check whether remainder of bits is identical to rbsp_trailing_bits. */ + uint8_t remainder_bits = bits->cache & ~(~0U << bits->store); + uint8_t rbsp_trailing_bits = 1U << (bits->store - 1); + return remainder_bits != rbsp_trailing_bits; +} + +static inline int nalu_get_max_ps_length +( + lsmash_entry_list_t *ps_list, + uint32_t *max_ps_length +) +{ + *max_ps_length = 0; + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return LSMASH_ERR_NAMELESS; + if( ps->unused ) + continue; + *max_ps_length = LSMASH_MAX( *max_ps_length, ps->nalUnitLength ); + } + return 0; +} + +static inline int nalu_get_ps_count +( + lsmash_entry_list_t *ps_list, + uint32_t *ps_count +) +{ + *ps_count = 0; + for( lsmash_entry_t *entry = ps_list ? ps_list->head : NULL; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return LSMASH_ERR_NAMELESS; + if( ps->unused ) + continue; + ++(*ps_count); + } + return 0; +} + +static inline int nalu_check_same_ps_existence +( + lsmash_entry_list_t *ps_list, + void *ps_data, + uint32_t ps_length +) +{ + for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) + { + isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; + if( !ps ) + return LSMASH_ERR_NAMELESS; + if( ps->unused ) + continue; + if( ps->nalUnitLength == ps_length && !memcmp( ps->nalUnit, ps_data, ps_length ) ) + return 1; /* The same parameter set already exists. */ + } + return 0; +} + +static inline int nalu_get_dcr_ps +( + lsmash_bs_t *bs, + lsmash_entry_list_t *list, + uint8_t entry_count +) +{ + for( uint8_t i = 0; i < entry_count; i++ ) + { + isom_dcr_ps_entry_t *data = lsmash_malloc( sizeof(isom_dcr_ps_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->nalUnitLength = lsmash_bs_get_be16( bs ); + data->nalUnit = lsmash_bs_get_bytes( bs, data->nalUnitLength ); + if( !data->nalUnit ) + { + lsmash_remove_entries( list, isom_remove_dcr_ps ); + return LSMASH_ERR_NAMELESS; + } + } + return 0; +} + +static inline int nalu_check_next_short_start_code +( + uint8_t *buf_pos, + uint8_t *buf_end +) +{ + return ((buf_pos + 2) < buf_end) && !buf_pos[0] && !buf_pos[1] && (buf_pos[2] == 0x01); +} + +/* Return the offset from the beginning of stream if a start code is found. + * Return NALU_NO_START_CODE_FOUND otherwise. */ +static inline uint64_t nalu_find_first_start_code +( + lsmash_bs_t *bs +) +{ + uint64_t first_sc_head_pos = 0; + while( 1 ) + { + if( lsmash_bs_is_end( bs, first_sc_head_pos + NALU_LONG_START_CODE_LENGTH ) ) + return NALU_NO_START_CODE_FOUND; + /* Invalid if encountered any value of non-zero before the first start code. */ + if( lsmash_bs_show_byte( bs, first_sc_head_pos ) ) + return NALU_NO_START_CODE_FOUND; + /* The first NALU of an AU in decoding order shall have long start code (0x00000001). */ + if( 0x00000001 == lsmash_bs_show_be32( bs, first_sc_head_pos ) ) + break; + ++first_sc_head_pos; + } + return first_sc_head_pos; +} diff -Nru l-smash-1.9.1/codecs/vc1.c l-smash-2.3.0/codecs/vc1.c --- l-smash-1.9.1/codecs/vc1.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/vc1.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,969 @@ +/***************************************************************************** + * vc1.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "core/box.h" + +/*************************************************************************** + SMPTE 421M-2006 + SMPTE RP 2025-2007 +***************************************************************************/ +#include "vc1.h" + +struct lsmash_vc1_header_tag +{ + uint8_t *ebdu; + uint32_t ebdu_size; +}; + +typedef enum +{ + VC1_ADVANCED_PICTURE_TYPE_P = 0x0, /* 0b0 */ + VC1_ADVANCED_PICTURE_TYPE_B = 0x2, /* 0b10 */ + VC1_ADVANCED_PICTURE_TYPE_I = 0x6, /* 0b110 */ + VC1_ADVANCED_PICTURE_TYPE_BI = 0xE, /* 0b1110 */ + VC1_ADVANCED_PICTURE_TYPE_SKIPPED = 0xF, /* 0b1111 */ +} vc1_picture_type; + +typedef enum +{ + VC1_ADVANCED_FIELD_PICTURE_TYPE_II = 0x0, /* 0b000 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_IP = 0x1, /* 0b001 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_PI = 0x2, /* 0b010 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_PP = 0x3, /* 0b011 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_BB = 0x4, /* 0b100 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_BBI = 0x5, /* 0b101 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_BIB = 0x6, /* 0b110 */ + VC1_ADVANCED_FIELD_PICTURE_TYPE_BIBI = 0x7, /* 0b111 */ +} vc1_field_picture_type; + +typedef enum +{ + VC1_FRAME_CODING_MODE_PROGRESSIVE = 0x0, /* 0b0 */ + VC1_FRAME_CODING_MODE_FRAME_INTERLACE = 0x2, /* 0b10 */ + VC1_FRAME_CODING_MODE_FIELD_INTERLACE = 0x3, /* 0b11 */ +} vc1_frame_coding_mode; + +static void vc1_destroy_header( lsmash_vc1_header_t *hdr ) +{ + if( !hdr ) + return; + lsmash_free( hdr->ebdu ); + lsmash_free( hdr ); +} + +void lsmash_destroy_vc1_headers( lsmash_vc1_specific_parameters_t *param ) +{ + if( !param ) + return; + vc1_destroy_header( param->seqhdr ); + vc1_destroy_header( param->ephdr ); + param->seqhdr = NULL; + param->ephdr = NULL; +} + +void vc1_destruct_specific_data( void *data ) +{ + if( !data ) + return; + lsmash_destroy_vc1_headers( data ); + lsmash_free( data ); +} + +void vc1_cleanup_parser( vc1_info_t *info ) +{ + if( !info ) + return; + lsmash_destroy_vc1_headers( &info->dvc1_param ); + lsmash_destroy_multiple_buffers( info->buffer.bank ); + lsmash_bits_adhoc_cleanup( info->bits ); + info->bits = NULL; +} + +int vc1_setup_parser +( + vc1_info_t *info, + int parse_only +) +{ + assert( info ); + memset( info, 0, sizeof(vc1_info_t) ); + vc1_stream_buffer_t *sb = &info->buffer; + sb->bank = lsmash_create_multiple_buffers( parse_only ? 1 : 3, VC1_DEFAULT_BUFFER_SIZE ); + if( !sb->bank ) + return LSMASH_ERR_MEMORY_ALLOC; + sb->rbdu = lsmash_withdraw_buffer( sb->bank, 1 ); + if( !parse_only ) + { + info->access_unit.data = lsmash_withdraw_buffer( sb->bank, 2 ); + info->access_unit.incomplete_data = lsmash_withdraw_buffer( sb->bank, 3 ); + } + info->bits = lsmash_bits_adhoc_create(); + if( !info->bits ) + { + lsmash_destroy_multiple_buffers( sb->bank ); + return LSMASH_ERR_MEMORY_ALLOC; + } + info->prev_bdu_type = 0xFF; /* 0xFF is a forbidden value. */ + return 0; +} + +uint64_t vc1_find_next_start_code_prefix +( + lsmash_bs_t *bs, + uint8_t *bdu_type, + uint64_t *trailing_zero_bytes +) +{ + uint64_t length = 0; /* the length of the latest EBDU */ + uint64_t count = 0; /* the number of the trailing zero bytes after the latest EBDU */ + if( !lsmash_bs_is_end( bs, VC1_START_CODE_LENGTH - 1 ) + && 0x000001 == lsmash_bs_show_be24( bs, 0 ) ) + { + *bdu_type = lsmash_bs_show_byte( bs, VC1_START_CODE_PREFIX_LENGTH ); + length = VC1_START_CODE_LENGTH; + /* Find the start code of the next EBDU and get the length of the latest EBDU. */ + int no_more = lsmash_bs_is_end( bs, length + VC1_START_CODE_LENGTH - 1 ); + if( !no_more ) + { + uint32_t sync_bytes = lsmash_bs_show_be24( bs, length ); + while( 0x000001 != sync_bytes ) + { + no_more = lsmash_bs_is_end( bs, ++length + VC1_START_CODE_LENGTH - 1 ); + if( no_more ) + break; + sync_bytes <<= 8; + sync_bytes |= lsmash_bs_show_byte( bs, length + VC1_START_CODE_PREFIX_LENGTH - 1 ); + sync_bytes &= 0xFFFFFF; + } + } + if( no_more ) + length = lsmash_bs_get_remaining_buffer_size( bs ); + /* Any EBDU has no consecutive zero bytes at the end. */ + while( 0x00 == lsmash_bs_show_byte( bs, length - 1 ) ) + { + --length; + ++count; + } + } + else + *bdu_type = 0xFF; /* 0xFF is a forbidden value. */ + *trailing_zero_bytes = count; + return length; +} + +int vc1_check_next_start_code_suffix +( + lsmash_bs_t *bs, + uint8_t *p_bdu_type +) +{ + uint8_t bdu_type = *((uint8_t *)lsmash_bs_get_buffer_data( bs ) + VC1_START_CODE_PREFIX_LENGTH); + if( (bdu_type >= 0x00 && bdu_type <= 0x09) + || (bdu_type >= 0x20 && bdu_type <= 0x7F) ) + return LSMASH_ERR_NAMELESS; /* SMPTE reserved */ + if( bdu_type >= 0x80 && bdu_type <= 0xFF ) + return LSMASH_ERR_INVALID_DATA; /* Forbidden */ + *p_bdu_type = bdu_type; + return 0; +} + +static inline uint8_t vc1_get_vlc( lsmash_bits_t *bits, int length ) +{ + uint8_t value = 0; + for( int i = 0; i < length; i++ ) + if( lsmash_bits_get( bits, 1 ) ) + value = (value << 1) | 1; + else + { + value = value << 1; + break; + } + return value; +} + +/* Convert EBDU (Encapsulated Byte Data Unit) to RBDU (Raw Byte Data Unit). */ +static uint8_t *vc1_remove_emulation_prevention( uint8_t *src, uint64_t src_length, uint8_t *dst ) +{ + uint8_t *src_end = src + src_length; + while( src < src_end ) + if( ((src + 2) < src_end) && !src[0] && !src[1] && (src[2] == 0x03) ) + { + /* 0x000003 -> 0x0000 */ + *dst++ = *src++; + *dst++ = *src++; + src++; /* Skip emulation_prevention_three_byte (0x03). */ + } + else + *dst++ = *src++; + return dst; +} + +static int vc1_import_rbdu_from_ebdu( lsmash_bits_t *bits, uint8_t *rbdu_buffer, uint8_t *ebdu, uint64_t ebdu_size ) +{ + uint8_t *rbdu_start = rbdu_buffer; + uint8_t *rbdu_end = vc1_remove_emulation_prevention( ebdu, ebdu_size, rbdu_buffer ); + uint64_t rbdu_length = rbdu_end - rbdu_start; + return lsmash_bits_import_data( bits, rbdu_start, rbdu_length ); +} + +static void vc1_parse_hrd_param( lsmash_bits_t *bits, vc1_hrd_param_t *hrd_param ) +{ + hrd_param->hrd_num_leaky_buckets = lsmash_bits_get( bits, 5 ); + lsmash_bits_get( bits, 4 ); /* bitrate_exponent */ + lsmash_bits_get( bits, 4 ); /* buffer_size_exponent */ + for( uint8_t i = 0; i < hrd_param->hrd_num_leaky_buckets; i++ ) + { + lsmash_bits_get( bits, 16 ); /* hrd_rate */ + lsmash_bits_get( bits, 16 ); /* hrd_buffer */ + } +} + +int vc1_parse_sequence_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int try_append ) +{ + lsmash_bits_t *bits = info->bits; + vc1_sequence_header_t *sequence = &info->sequence; + int err = vc1_import_rbdu_from_ebdu( bits, info->buffer.rbdu, ebdu + VC1_START_CODE_LENGTH, ebdu_size ); + if( err < 0 ) + return err; + memset( sequence, 0, sizeof(vc1_sequence_header_t) ); + sequence->profile = lsmash_bits_get( bits, 2 ); + if( sequence->profile != 3 ) + return LSMASH_ERR_NAMELESS; /* SMPTE Reserved */ + sequence->level = lsmash_bits_get( bits, 3 ); + if( sequence->level > 4 ) + return LSMASH_ERR_NAMELESS; /* SMPTE Reserved */ + sequence->colordiff_format = lsmash_bits_get( bits, 2 ); + if( sequence->colordiff_format != 1 ) + return LSMASH_ERR_NAMELESS; /* SMPTE Reserved */ + lsmash_bits_get( bits, 9 ); /* frmrtq_postproc (3) + * bitrtq_postproc (5) + * postproc_flag (1) */ + sequence->max_coded_width = lsmash_bits_get( bits, 12 ); + sequence->max_coded_height = lsmash_bits_get( bits, 12 ); + lsmash_bits_get( bits, 1 ); /* pulldown */ + sequence->interlace = lsmash_bits_get( bits, 1 ); + lsmash_bits_get( bits, 4 ); /* tfcntrflag (1) + * finterpflag (1) + * reserved (1) + * psf (1) */ + if( lsmash_bits_get( bits, 1 ) ) /* display_ext */ + { + sequence->disp_horiz_size = lsmash_bits_get( bits, 14 ) + 1; + sequence->disp_vert_size = lsmash_bits_get( bits, 14 ) + 1; + if( lsmash_bits_get( bits, 1 ) ) /* aspect_ratio_flag */ + { + uint8_t aspect_ratio = lsmash_bits_get( bits, 4 ); + if( aspect_ratio == 15 ) + { + sequence->aspect_width = lsmash_bits_get( bits, 8 ) + 1; /* aspect_horiz_size */ + sequence->aspect_height = lsmash_bits_get( bits, 8 ) + 1; /* aspect_vert_size */ + } + else + { + static const struct + { + uint32_t aspect_width; + uint32_t aspect_height; + } vc1_aspect_ratio[15] = + { + { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, { 40, 33 }, { 24, 11 }, + { 20, 11 }, { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, + { 0, 0 } /* SMPTE Reserved */ + }; + sequence->aspect_width = vc1_aspect_ratio[ aspect_ratio ].aspect_width; + sequence->aspect_height = vc1_aspect_ratio[ aspect_ratio ].aspect_height; + } + } + sequence->framerate_flag = lsmash_bits_get( bits, 1 ); + if( sequence->framerate_flag ) + { + if( lsmash_bits_get( bits, 1 ) ) /* framerateind */ + { + sequence->framerate_numerator = lsmash_bits_get( bits, 16 ) + 1; + sequence->framerate_denominator = 32; + } + else + { + static const uint32_t vc1_frameratenr_table[8] = { 0, 24, 25, 30, 50, 60, 48, 72 }; + uint8_t frameratenr = lsmash_bits_get( bits, 8 ); + if( frameratenr == 0 ) + return LSMASH_ERR_INVALID_DATA; /* Forbidden */ + if( frameratenr > 7 ) + return LSMASH_ERR_NAMELESS; /* SMPTE Reserved */ + uint8_t frameratedr = lsmash_bits_get( bits, 4 ); + if( frameratedr != 1 && frameratedr != 2 ) + /* 0: Forbidden, 3-15: SMPTE Reserved */ + return frameratedr == 0 + ? LSMASH_ERR_INVALID_DATA + : LSMASH_ERR_NAMELESS; + if( frameratedr == 1 ) + { + sequence->framerate_numerator = vc1_frameratenr_table[ frameratenr ]; + sequence->framerate_denominator = 1; + } + else + { + sequence->framerate_numerator = vc1_frameratenr_table[ frameratenr ] * 1000; + sequence->framerate_denominator = 1001; + } + } + } + if( lsmash_bits_get( bits, 1 ) ) /* color_format_flag */ + { + sequence->color_prim = lsmash_bits_get( bits, 8 ); + sequence->transfer_char = lsmash_bits_get( bits, 8 ); + sequence->matrix_coef = lsmash_bits_get( bits, 8 ); + } + sequence->hrd_param_flag = lsmash_bits_get( bits, 1 ); + if( sequence->hrd_param_flag ) + vc1_parse_hrd_param( bits, &sequence->hrd_param ); + } + /* '1' and stuffing bits ('0's) */ + if( !lsmash_bits_get( bits, 1 ) ) + return LSMASH_ERR_INVALID_DATA; + lsmash_bits_empty( bits ); + /* Preparation for creating VC1SpecificBox */ + if( try_append ) + { + /* Update some specific parameters. */ + lsmash_vc1_specific_parameters_t *param = &info->dvc1_param; + lsmash_vc1_header_t *seqhdr = param->seqhdr; + if( !seqhdr ) + { + seqhdr = lsmash_malloc( sizeof(lsmash_vc1_header_t) ); + if( !seqhdr ) + return LSMASH_ERR_MEMORY_ALLOC; + seqhdr->ebdu = lsmash_memdup( ebdu, ebdu_size ); + if( !seqhdr->ebdu ) + { + lsmash_free( seqhdr ); + return LSMASH_ERR_MEMORY_ALLOC; + } + seqhdr->ebdu_size = ebdu_size; + param->seqhdr = seqhdr; + } + else if( seqhdr && seqhdr->ebdu && (seqhdr->ebdu_size == ebdu_size) ) + param->multiple_sequence |= !!memcmp( ebdu, seqhdr->ebdu, seqhdr->ebdu_size ); + param->profile = sequence->profile << 2; + param->level = LSMASH_MAX( param->level, sequence->level ); + param->interlaced |= sequence->interlace; + uint32_t framerate = sequence->framerate_flag + ? ((double)sequence->framerate_numerator / sequence->framerate_denominator) + 0.5 + : 0xffffffff; /* 0xffffffff means framerate is unknown or unspecified. */ + if( param->framerate == 0 ) + param->framerate = framerate; + else if( param->framerate != framerate ) + param->framerate = 0xffffffff; + } + info->sequence.present = 1; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int vc1_parse_entry_point_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int try_append ) +{ + lsmash_bits_t *bits = info->bits; + vc1_sequence_header_t *sequence = &info->sequence; + vc1_entry_point_t *entry_point = &info->entry_point; + int err = vc1_import_rbdu_from_ebdu( bits, info->buffer.rbdu, ebdu + VC1_START_CODE_LENGTH, ebdu_size ); + if( err < 0 ) + return err; + memset( entry_point, 0, sizeof(vc1_entry_point_t) ); + uint8_t broken_link_flag = lsmash_bits_get( bits, 1 ); /* 0: no concatenation between the current and the previous entry points + * 1: concatenated and needed to discard B-pictures */ + entry_point->closed_entry_point = lsmash_bits_get( bits, 1 ); /* 0: Open RAP, 1: Closed RAP */ + if( broken_link_flag && entry_point->closed_entry_point ) + return LSMASH_ERR_INVALID_DATA; /* invalid combination */ + lsmash_bits_get( bits, 4 ); /* panscan_flag (1) + * refdist_flag (1) + * loopfilter (1) + * fastuvmc (1) */ + uint8_t extended_mv = lsmash_bits_get( bits, 1 ); + lsmash_bits_get( bits, 6 ); /* dquant (2) + * vstransform (1) + * overlap (1) + * quantizer (2) */ + if( sequence->hrd_param_flag ) + for( uint8_t i = 0; i < sequence->hrd_param.hrd_num_leaky_buckets; i++ ) + lsmash_bits_get( bits, 8 ); /* hrd_full */ + /* Decide coded size here. + * The correct formula is defined in Amendment 2:2011 to SMPTE ST 421M:2006. + * Don't use the formula specified in SMPTE 421M-2006. */ + uint16_t coded_width; + uint16_t coded_height; + if( lsmash_bits_get( bits, 1 ) ) /* coded_size_flag */ + { + coded_width = lsmash_bits_get( bits, 12 ); + coded_height = lsmash_bits_get( bits, 12 ); + } + else + { + coded_width = sequence->max_coded_width; + coded_height = sequence->max_coded_height; + } + coded_width = 2 * (coded_width + 1); /* corrected */ + coded_height = 2 * (coded_height + 1); /* corrected */ + if( sequence->disp_horiz_size == 0 || sequence->disp_vert_size == 0 ) + { + sequence->disp_horiz_size = coded_width; + sequence->disp_vert_size = coded_height; + } + /* */ + if( extended_mv ) + lsmash_bits_get( bits, 1 ); /* extended_dmv */ + if( lsmash_bits_get( bits, 1 ) ) /* range_mapy_flag */ + lsmash_bits_get( bits, 3 ); /* range_mapy */ + if( lsmash_bits_get( bits, 1 ) ) /* range_mapuv_flag */ + lsmash_bits_get( bits, 3 ); /* range_mapuv */ + /* '1' and stuffing bits ('0's) */ + if( !lsmash_bits_get( bits, 1 ) ) + return LSMASH_ERR_INVALID_DATA; + lsmash_bits_empty( bits ); + /* Preparation for creating VC1SpecificBox */ + if( try_append ) + { + lsmash_vc1_specific_parameters_t *param = &info->dvc1_param; + lsmash_vc1_header_t *ephdr = param->ephdr; + if( !ephdr ) + { + ephdr = lsmash_malloc( sizeof(lsmash_vc1_header_t) ); + if( !ephdr ) + return LSMASH_ERR_MEMORY_ALLOC; + ephdr->ebdu = lsmash_memdup( ebdu, ebdu_size ); + if( !ephdr->ebdu ) + { + lsmash_free( ephdr ); + return LSMASH_ERR_MEMORY_ALLOC; + } + ephdr->ebdu_size = ebdu_size; + param->ephdr = ephdr; + } + else if( ephdr && ephdr->ebdu && (ephdr->ebdu_size == ebdu_size) ) + param->multiple_entry |= !!memcmp( ebdu, ephdr->ebdu, ephdr->ebdu_size ); + } + info->entry_point.present = 1; + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +int vc1_parse_advanced_picture( lsmash_bits_t *bits, + vc1_sequence_header_t *sequence, vc1_picture_info_t *picture, + uint8_t *rbdu_buffer, uint8_t *ebdu, uint64_t ebdu_size ) +{ + int err = vc1_import_rbdu_from_ebdu( bits, rbdu_buffer, ebdu + VC1_START_CODE_LENGTH, ebdu_size ); + if( err < 0 ) + return err; + if( sequence->interlace ) + picture->frame_coding_mode = vc1_get_vlc( bits, 2 ); + else + picture->frame_coding_mode = 0; + if( picture->frame_coding_mode != 0x3 ) + picture->type = vc1_get_vlc( bits, 4 ); /* ptype (variable length) */ + else + picture->type = lsmash_bits_get( bits, 3 ); /* fptype (3) */ + picture->present = 1; + lsmash_bits_empty( bits ); + return bits->bs->error ? LSMASH_ERR_NAMELESS : 0; +} + +void vc1_update_au_property( vc1_access_unit_t *access_unit, vc1_picture_info_t *picture ) +{ + access_unit->random_accessible = picture->random_accessible; + access_unit->closed_gop = picture->closed_gop; + /* I-picture + * Be coded using information only from itself. (independent) + * All the macroblocks in an I-picture are intra-coded. + * P-picture + * Be coded using motion compensated prediction from past reference fields or frame. + * Can contain macroblocks that are inter-coded (i.e. coded using prediction) and macroblocks that are intra-coded. + * B-picture + * Be coded using motion compensated prediction from past and/or future reference fields or frames. (bi-predictive) + * Cannot be used for predicting any other picture. (disposable) + * BI-picture + * All the macroblocks in BI-picture are intra-coded. (independent) + * Cannot be used for predicting any other picture. (disposable) */ + if( picture->frame_coding_mode == 0x3 ) + { + /* field interlace */ + access_unit->independent = picture->type == VC1_ADVANCED_FIELD_PICTURE_TYPE_II || picture->type == VC1_ADVANCED_FIELD_PICTURE_TYPE_BIBI; + access_unit->non_bipredictive = picture->type < VC1_ADVANCED_FIELD_PICTURE_TYPE_BB || picture->type == VC1_ADVANCED_FIELD_PICTURE_TYPE_BIBI; + access_unit->disposable = picture->type >= VC1_ADVANCED_FIELD_PICTURE_TYPE_BB; + } + else + { + /* frame progressive/interlace */ + access_unit->independent = picture->type == VC1_ADVANCED_PICTURE_TYPE_I || picture->type == VC1_ADVANCED_PICTURE_TYPE_BI; + access_unit->non_bipredictive = picture->type != VC1_ADVANCED_PICTURE_TYPE_B; + access_unit->disposable = picture->type == VC1_ADVANCED_PICTURE_TYPE_B || picture->type == VC1_ADVANCED_PICTURE_TYPE_BI; + } + picture->present = 0; + picture->type = 0; + picture->closed_gop = 0; + picture->start_of_sequence = 0; + picture->random_accessible = 0; +} + +int vc1_find_au_delimit_by_bdu_type( uint8_t bdu_type, uint8_t prev_bdu_type ) +{ + /* In any access unit, EBDU with smaller least significant 8-bits of BDU type doesn't precede EBDU with larger one. + * Therefore, the condition: (bdu_type 0xF) > (prev_bdu_type & 0xF) is more precisely. + * No two or more frame start codes shall be in the same access unit. */ + return bdu_type > prev_bdu_type || (bdu_type == 0x0D && prev_bdu_type == 0x0D); +} + +int vc1_supplement_buffer( vc1_stream_buffer_t *sb, vc1_access_unit_t *access_unit, uint32_t size ) +{ + lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, size ); + if( !bank ) + return LSMASH_ERR_MEMORY_ALLOC; + sb->bank = bank; + sb->rbdu = lsmash_withdraw_buffer( bank, 1 ); + if( access_unit && bank->number_of_buffers == 3 ) + { + access_unit->data = lsmash_withdraw_buffer( bank, 2 ); + access_unit->incomplete_data = lsmash_withdraw_buffer( bank, 3 ); + } + return 0; +} + +uint8_t *lsmash_create_vc1_specific_info( lsmash_vc1_specific_parameters_t *param, uint32_t *data_length ) +{ + if( !param || !data_length ) + return NULL; + if( !param->seqhdr && !param->ephdr ) + return NULL; + /* Calculate enough buffer size. */ + lsmash_vc1_header_t *seqhdr = param->seqhdr; + lsmash_vc1_header_t *ephdr = param->ephdr; + uint32_t buffer_size = ISOM_BASEBOX_COMMON_SIZE + 7 + seqhdr->ebdu_size + ephdr->ebdu_size; + /* Set up bitstream writer. */ + uint8_t buffer[buffer_size]; + lsmash_bits_t bits = { 0 }; + lsmash_bs_t bs = { 0 }; + bs.buffer.data = buffer; + bs.buffer.alloc = buffer_size; + lsmash_bits_init( &bits, &bs ); + /* Create a VC1SpecificBox */ + lsmash_bits_put( &bits, 32, 0 ); /* box size */ + lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DVC1.fourcc ); /* box type: 'dvc1' */ + lsmash_bits_put( &bits, 4, param->profile ); /* profile */ + lsmash_bits_put( &bits, 3, param->level ); /* level */ + lsmash_bits_put( &bits, 1, 0 ); /* reserved */ + /* VC1AdvDecSpecStruc (for Advanced Profile) */ + lsmash_bits_put( &bits, 3, param->level ); /* level (identical to the previous level field) */ + lsmash_bits_put( &bits, 1, param->cbr ); /* cbr */ + lsmash_bits_put( &bits, 6, 0 ); /* reserved */ + lsmash_bits_put( &bits, 1, !param->interlaced ); /* no_interlace */ + lsmash_bits_put( &bits, 1, !param->multiple_sequence ); /* no_multiple_seq */ + lsmash_bits_put( &bits, 1, !param->multiple_entry ); /* no_multiple_entry */ + lsmash_bits_put( &bits, 1, !param->slice_present ); /* no_slice_code */ + lsmash_bits_put( &bits, 1, !param->bframe_present ); /* no_bframe */ + lsmash_bits_put( &bits, 1, 0 ); /* reserved */ + lsmash_bits_put( &bits, 32, param->framerate ); /* framerate */ + /* seqhdr_ephdr[] */ + for( uint32_t i = 0; i < seqhdr->ebdu_size; i++ ) + lsmash_bits_put( &bits, 8, *(seqhdr->ebdu + i) ); + for( uint32_t i = 0; i < ephdr->ebdu_size; i++ ) + lsmash_bits_put( &bits, 8, *(ephdr->ebdu + i) ); + /* */ + uint8_t *data = lsmash_bits_export_data( &bits, data_length ); + /* Update box size. */ + LSMASH_SET_BE32( data, *data_length ); + return data; +} + +static int vc1_try_to_put_header( lsmash_vc1_header_t **p_hdr, uint8_t *multiple_hdr, void *hdr_data, uint32_t hdr_length ) +{ + lsmash_vc1_header_t *hdr = *p_hdr; + if( !hdr ) + { + hdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); + if( !hdr ) + return LSMASH_ERR_MEMORY_ALLOC; + } + else if( hdr->ebdu ) + { + *multiple_hdr |= hdr->ebdu_size == hdr_length ? !!memcmp( hdr_data, hdr->ebdu, hdr->ebdu_size ) : 1; + return 0; + } + hdr->ebdu = lsmash_memdup( hdr_data, hdr_length ); + hdr->ebdu_size = hdr->ebdu ? hdr_length : 0; + *p_hdr = hdr; + return hdr->ebdu ? 0 : LSMASH_ERR_MEMORY_ALLOC; +} + +int lsmash_put_vc1_header( lsmash_vc1_specific_parameters_t *param, void *hdr_data, uint32_t hdr_length ) +{ + if( !param || !hdr_data || hdr_length < 5 ) + return LSMASH_ERR_FUNCTION_PARAM; + /* Check start code prefix (0x000001). */ + uint8_t *data = (uint8_t *)hdr_data; + if( data[0] != 0x00 || data[1] != 0x00 || data[2] != 0x01 ) + return LSMASH_ERR_INVALID_DATA; + if( data[3] == 0x0F ) /* sequence header */ + return vc1_try_to_put_header( ¶m->seqhdr, ¶m->multiple_sequence, hdr_data, hdr_length ); + else if( data[3] == 0x0E ) /* entry point header */ + return vc1_try_to_put_header( ¶m->ephdr, ¶m->multiple_entry, hdr_data, hdr_length ); + return LSMASH_ERR_INVALID_DATA; +} + +static int vc1_parse_succeeded +( + vc1_info_t *info, + lsmash_vc1_specific_parameters_t *param +) +{ + int ret; + if( info->sequence.present && info->entry_point.present ) + { + *param = info->dvc1_param; + /* Avoid freeing headers. */ + info->dvc1_param.seqhdr = NULL; + info->dvc1_param.ephdr = NULL; + ret = 0; + } + else + ret = LSMASH_ERR_INVALID_DATA; + vc1_cleanup_parser( info ); + return ret; +} + +static inline int vc1_parse_failed +( + vc1_info_t *info, + int ret +) +{ + vc1_cleanup_parser( info ); + return ret; +} + +int lsmash_setup_vc1_specific_parameters_from_access_unit( lsmash_vc1_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) +{ + if( !param || !data || data_length == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + vc1_info_t *info = &(vc1_info_t){ { 0 } }; + lsmash_bs_t *bs = &(lsmash_bs_t){ 0 }; + int err = lsmash_bs_set_empty_stream( bs, data, data_length ); + if( err < 0 ) + return err; + if( (err = vc1_setup_parser( info, 1 )) < 0 ) + return vc1_parse_failed( info, err ); + info->dvc1_param = *param; + vc1_stream_buffer_t *sb = &info->buffer; + while( 1 ) + { + uint8_t bdu_type; + uint64_t trailing_zero_bytes; + uint64_t ebdu_length = vc1_find_next_start_code_prefix( bs, &bdu_type, &trailing_zero_bytes ); + if( ebdu_length <= VC1_START_CODE_LENGTH && lsmash_bs_is_end( bs, ebdu_length ) ) + /* For the last EBDU. This EBDU already has been parsed. */ + return vc1_parse_succeeded( info, param ); + else if( bdu_type == 0xFF ) + return vc1_parse_failed( info, LSMASH_ERR_INVALID_DATA ); + uint64_t next_ebdu_head_pos = info->ebdu_head_pos + + ebdu_length + + trailing_zero_bytes; + if( bdu_type >= 0x0A && bdu_type <= 0x0F ) + { + /* Complete the current access unit if encountered delimiter of current access unit. */ + if( vc1_find_au_delimit_by_bdu_type( bdu_type, info->prev_bdu_type ) ) + /* The last video coded EBDU belongs to the access unit you want at this time. */ + return vc1_parse_succeeded( info, param ); + /* Increase the buffer if needed. */ + if( sb->bank->buffer_size < ebdu_length + && (err = vc1_supplement_buffer( sb, NULL, 2 * ebdu_length )) < 0 ) + return vc1_parse_failed( info, err ); + /* Process EBDU by its BDU type. */ + uint8_t *ebdu = lsmash_bs_get_buffer_data( bs ); + switch( bdu_type ) + { + /* FRM_SC: Frame start code + * FLD_SC: Field start code + * SLC_SC: Slice start code + * SEQ_SC: Sequence header start code + * EP_SC: Entry-point start code + * PIC_L: Picture layer + * SLC_L: Slice layer + * SEQ_L: Sequence layer + * EP_L: Entry-point layer */ + case 0x0D : /* Frame + * For the Progressive or Frame Interlace mode, shall signal the beginning of a new video frame. + * For the Field Interlace mode, shall signal the beginning of a sequence of two independently coded video fields. + * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][[SLC_SC][SLC_L] (optional)] ... */ + { + vc1_picture_info_t *picture = &info->picture; + if( (err = vc1_parse_advanced_picture( info->bits, &info->sequence, picture, info->buffer.rbdu, ebdu, ebdu_length )) < 0 ) + return vc1_parse_failed( info, err ); + info->dvc1_param.bframe_present |= picture->frame_coding_mode == 0x3 + ? picture->type >= VC1_ADVANCED_FIELD_PICTURE_TYPE_BB + : picture->type == VC1_ADVANCED_PICTURE_TYPE_B || picture->type == VC1_ADVANCED_PICTURE_TYPE_BI; + } + case 0x0C : /* Field + * Shall only be used for Field Interlaced frames + * and shall only be used to signal the beginning of the second field of the frame. + * [FRM_SC][PIC_L][FLD_SC][PIC_L][[SLC_SC][SLC_L] (optional)] ... + * Field start code is followed by INTERLACE_FIELD_PICTURE_FIELD2() which doesn't have info of its field picture type.*/ + break; + case 0x0B : /* Slice + * Shall not be used for start code of the first slice of a frame. + * Shall not be used for start code of the first slice of an interlace field coded picture. + * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][SLC_SC][SLC_L][[SLC_SC][SLC_L] (optional)] ... + * Slice layer may repeat frame header. We just ignore it. */ + info->dvc1_param.slice_present = 1; + break; + case 0x0E : /* Entry-point header + * Entry-point indicates the direct followed frame is a start of group of frames. + * Entry-point doesn't indicates the frame is a random access point when multiple sequence headers are present, + * since it is necessary to decode sequence header which subsequent frames belong to for decoding them. + * Entry point shall be followed by + * 1. I-picture - progressive or frame interlace + * 2. I/I-picture, I/P-picture, or P/I-picture - field interlace + * [[SEQ_SC][SEQ_L] (optional)][EP_SC][EP_L][FRM_SC][PIC_L] ... */ + if( (err = vc1_parse_entry_point_header( info, ebdu, ebdu_length, 1 )) < 0 ) + return vc1_parse_failed( info, err ); + break; + case 0x0F : /* Sequence header + * [SEQ_SC][SEQ_L][EP_SC][EP_L][FRM_SC][PIC_L] ... */ + if( (err = vc1_parse_sequence_header( info, ebdu, ebdu_length, 1 )) < 0 ) + return vc1_parse_failed( info, err ); + break; + default : /* End-of-sequence (0x0A) */ + break; + } + } + /* Move to the first byte of the next EBDU. */ + info->prev_bdu_type = bdu_type; + if( lsmash_bs_read_seek( bs, next_ebdu_head_pos, SEEK_SET ) != next_ebdu_head_pos ) + return vc1_parse_failed( info, LSMASH_ERR_NAMELESS ); + /* Check if no more data to read from the stream. */ + if( !lsmash_bs_is_end( bs, VC1_START_CODE_PREFIX_LENGTH ) ) + info->ebdu_head_pos = next_ebdu_head_pos; + else + return vc1_parse_succeeded( info, param ); + } +} + +static inline int vc1_check_next_start_code_prefix( uint8_t *buf_pos, uint8_t *buf_end ) +{ + return ((buf_pos + 2) < buf_end) && !buf_pos[0] && !buf_pos[1] && (buf_pos[2] == 0x01); +} + +int vc1_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( dst && dst->data.structured && src && src->data.unstructured ); + if( src->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) + return LSMASH_ERR_INVALID_DATA; + lsmash_vc1_specific_parameters_t *param = (lsmash_vc1_specific_parameters_t *)dst->data.structured; + uint8_t *data = src->data.unstructured; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + if( size != src->size ) + return LSMASH_ERR_INVALID_DATA; + param->profile = (data[0] >> 4) & 0x0F; + if( param->profile != 12 ) + return LSMASH_ERR_PATCH_WELCOME; /* We don't support profile other than 12 (Advanced profile). */ + param->level = (data[0] >> 1) & 0x07; + param->cbr = (data[1] >> 4) & 0x01; + param->interlaced = !((data[2] >> 5) & 0x01); + param->multiple_sequence = !((data[2] >> 4) & 0x01); + param->multiple_entry = !((data[2] >> 3) & 0x01); + param->slice_present = !((data[2] >> 2) & 0x01); + param->bframe_present = !((data[2] >> 1) & 0x01); + param->framerate = LSMASH_GET_BE32( &data[3] ); + /* Try to get seqhdr_ephdr[]. */ + if( !param->seqhdr ) + { + param->seqhdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); + if( !param->seqhdr ) + return LSMASH_ERR_MEMORY_ALLOC; + } + if( !param->ephdr ) + { + param->ephdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); + if( !param->ephdr ) + return LSMASH_ERR_MEMORY_ALLOC; + } + lsmash_vc1_header_t *seqhdr = param->seqhdr; + lsmash_vc1_header_t *ephdr = param->ephdr; + data += 7; + uint8_t *pos = data; + uint8_t *end = src->data.unstructured + src->size; + /* Find the start point of Sequence header EBDU. */ + while( pos < end ) + { + if( vc1_check_next_start_code_prefix( pos, end ) && (pos + 3 < end) && *(pos + 3) == 0x0F ) + { + seqhdr->ebdu_size = 4; + pos += 4; + break; + } + ++pos; + } + /* Find the end point of Sequence header EBDU. */ + while( pos < end ) + { + if( vc1_check_next_start_code_prefix( pos, end ) ) + break; + ++ seqhdr->ebdu_size; + } + /* Find the start point of Entry-point header EBDU. */ + while( pos < end ) + { + if( vc1_check_next_start_code_prefix( pos, end ) && (pos + 3 < end) && *(pos + 3) == 0x0E ) + { + ephdr->ebdu_size = 4; + pos += 4; + break; + } + ++pos; + } + /* Find the end point of Entry-point header EBDU. */ + while( pos < end ) + { + if( vc1_check_next_start_code_prefix( pos, end ) ) + break; + ++ ephdr->ebdu_size; + } + /* Append the Sequence header EBDU and Entry-point header EBDU if present. */ + if( seqhdr->ebdu_size ) + { + lsmash_free( seqhdr->ebdu ); + seqhdr->ebdu = lsmash_memdup( data, seqhdr->ebdu_size ); + if( !seqhdr->ebdu ) + return LSMASH_ERR_MEMORY_ALLOC; + } + if( ephdr->ebdu_size ) + { + lsmash_free( ephdr->ebdu ); + ephdr->ebdu = lsmash_memdup( data, ephdr->ebdu_size ); + if( !ephdr->ebdu ) + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +int vc1_copy_codec_specific( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) +{ + assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); + assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); + lsmash_vc1_specific_parameters_t *src_data = (lsmash_vc1_specific_parameters_t *)src->data.structured; + lsmash_vc1_specific_parameters_t *dst_data = (lsmash_vc1_specific_parameters_t *)dst->data.structured; + lsmash_destroy_vc1_headers( dst_data ); + *dst_data = *src_data; + if( !src_data->seqhdr && !src_data->ephdr ) + return 0; + if( src_data->seqhdr ) + { + dst_data->seqhdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); + if( !dst_data->seqhdr ) + return LSMASH_ERR_MEMORY_ALLOC; + if( src_data->seqhdr->ebdu_size ) + { + dst_data->seqhdr->ebdu = lsmash_memdup( src_data->seqhdr->ebdu, src_data->seqhdr->ebdu_size ); + if( !dst_data->seqhdr->ebdu ) + { + lsmash_destroy_vc1_headers( dst_data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + dst_data->seqhdr->ebdu_size = src_data->seqhdr->ebdu_size; + } + if( src_data->ephdr ) + { + dst_data->ephdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); + if( !dst_data->ephdr ) + { + lsmash_destroy_vc1_headers( dst_data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( src_data->ephdr->ebdu_size ) + { + dst_data->ephdr->ebdu = lsmash_memdup( src_data->ephdr->ebdu, src_data->ephdr->ebdu_size ); + if( !dst_data->ephdr->ebdu ) + { + lsmash_destroy_vc1_headers( dst_data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + dst_data->ephdr->ebdu_size = src_data->ephdr->ebdu_size; + } + return 0; +} + +int vc1_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: VC1 Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + if( box->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *data = box->binary; + isom_skip_box_common( &data ); + uint8_t profile = (data[0] >> 4) & 0x0F; + if( profile != 12 ) + return 0; /* We don't support profile other than 12 (Advanced profile). */ + lsmash_ifprintf( fp, indent, "profile = %"PRIu8"\n", profile ); + lsmash_ifprintf( fp, indent, "level = %"PRIu8"\n", (data[0] >> 1) & 0x07 ); + lsmash_ifprintf( fp, indent, "reserved = %"PRIu8"\n", data[0] & 0x01 ); + lsmash_ifprintf( fp, indent, "level = %"PRIu8"\n", (data[1] >> 5) & 0x07 ); + lsmash_ifprintf( fp, indent, "cbr = %"PRIu8"\n", (data[1] >> 4) & 0x01 ); + lsmash_ifprintf( fp, indent, "reserved1 = 0x%02"PRIx8"\n", (data[1] & 0x0F) | ((data[2] >> 6) & 0x03) ); + lsmash_ifprintf( fp, indent, "no_interlace = %"PRIu8"\n", (data[2] >> 5) & 0x01 ); + lsmash_ifprintf( fp, indent, "no_multiple_seq = %"PRIu8"\n", (data[2] >> 4) & 0x01 ); + lsmash_ifprintf( fp, indent, "no_multiple_entry = %"PRIu8"\n", (data[2] >> 3) & 0x01 ); + lsmash_ifprintf( fp, indent, "no_slice_code = %"PRIu8"\n", (data[2] >> 2) & 0x01 ); + lsmash_ifprintf( fp, indent, "no_bframe = %"PRIu8"\n", (data[2] >> 1) & 0x01 ); + lsmash_ifprintf( fp, indent, "reserved2 = %"PRIu8"\n", data[2] & 0x01 ); + uint32_t framerate = LSMASH_GET_BE32( &data[3] ); + lsmash_ifprintf( fp, indent, "framerate = %"PRIu32"\n", framerate ); + uint32_t seqhdr_ephdr_size = box->size - (data - box->binary + 7); + if( seqhdr_ephdr_size ) + { + lsmash_ifprintf( fp, indent, "seqhdr_ephdr[]\n" ); + data += 7; + for( uint32_t i = 0; i < seqhdr_ephdr_size; i += 8 ) + { + lsmash_ifprintf( fp, indent + 1, "" ); + for( uint32_t j = 0; ; j++ ) + if( j == 7 || (i + j == seqhdr_ephdr_size - 1) ) + { + fprintf( fp, "0x%02"PRIx8"\n", data[i + j] ); + break; + } + else + fprintf( fp, "0x%02"PRIx8" ", data[i + j] ); + } + } + return 0; +} diff -Nru l-smash-1.9.1/codecs/vc1.h l-smash-2.3.0/codecs/vc1.h --- l-smash-1.9.1/codecs/vc1.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/codecs/vc1.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,134 @@ +/***************************************************************************** + * vc1.h: + ***************************************************************************** + * Copyright (C) 2012-2014 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. */ + +#define VC1_DEFAULT_BUFFER_SIZE (1<<16) +#define VC1_START_CODE_PREFIX_LENGTH 3 /* 0x000001 */ +#define VC1_START_CODE_SUFFIX_LENGTH 1 /* BDU type */ +#define VC1_START_CODE_LENGTH (VC1_START_CODE_PREFIX_LENGTH + VC1_START_CODE_SUFFIX_LENGTH) /* = 4 */ + +typedef struct +{ + uint8_t hrd_num_leaky_buckets; +} vc1_hrd_param_t; + +typedef struct +{ + uint8_t present; + uint8_t profile; + uint8_t level; + uint8_t colordiff_format; /* currently 4:2:0 only */ + uint8_t interlace; + uint8_t color_prim; + uint8_t transfer_char; + uint8_t matrix_coef; + uint8_t hrd_param_flag; + uint8_t aspect_width; + uint8_t aspect_height; + uint8_t framerate_flag; + uint32_t framerate_numerator; + uint32_t framerate_denominator; + uint16_t max_coded_width; + uint16_t max_coded_height; + uint16_t disp_horiz_size; + uint16_t disp_vert_size; + vc1_hrd_param_t hrd_param; +} vc1_sequence_header_t; + +typedef struct +{ + uint8_t present; + uint8_t closed_entry_point; +} vc1_entry_point_t; + +typedef struct +{ + uint8_t present; + uint8_t frame_coding_mode; + uint8_t type; + uint8_t closed_gop; + uint8_t start_of_sequence; + uint8_t random_accessible; +} vc1_picture_info_t; + +typedef struct +{ + uint8_t random_accessible; + uint8_t closed_gop; + uint8_t independent; + uint8_t non_bipredictive; + uint8_t disposable; + uint8_t *data; + uint32_t data_length; + uint8_t *incomplete_data; + uint32_t incomplete_data_length; + uint32_t number; +} vc1_access_unit_t; + +typedef struct vc1_info_tag vc1_info_t; + +typedef struct +{ + lsmash_multiple_buffers_t *bank; + uint8_t *rbdu; +} vc1_stream_buffer_t; + +struct vc1_info_tag +{ + lsmash_vc1_specific_parameters_t dvc1_param; + vc1_sequence_header_t sequence; + vc1_entry_point_t entry_point; + vc1_picture_info_t picture; + vc1_access_unit_t access_unit; + uint8_t prev_bdu_type; + uint64_t ebdu_head_pos; + lsmash_bits_t *bits; + vc1_stream_buffer_t buffer; +}; + +int vc1_setup_parser +( + vc1_info_t *info, + int parse_only +); + +uint64_t vc1_find_next_start_code_prefix +( + lsmash_bs_t *bs, + uint8_t *bdu_type, + uint64_t *trailing_zero_bytes +); + +int vc1_check_next_start_code_suffix +( + lsmash_bs_t *bs, + uint8_t *p_bdu_type +); + +void vc1_cleanup_parser( vc1_info_t *info ); +int vc1_parse_sequence_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int probe ); +int vc1_parse_entry_point_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int probe ); +int vc1_parse_advanced_picture( lsmash_bits_t *bits, + vc1_sequence_header_t *sequence, vc1_picture_info_t *picture, + uint8_t *rbdu_buffer, uint8_t *ebdu, uint64_t ebdu_size ); +void vc1_update_au_property( vc1_access_unit_t *access_unit, vc1_picture_info_t *picture ); +int vc1_find_au_delimit_by_bdu_type( uint8_t bdu_type, uint8_t prev_bdu_type ); +int vc1_supplement_buffer( vc1_stream_buffer_t *buffer, vc1_access_unit_t *access_unit, uint32_t size ); diff -Nru l-smash-1.9.1/common/alloc.c l-smash-2.3.0/common/alloc.c --- l-smash-1.9.1/common/alloc.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/alloc.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,73 @@ +/***************************************************************************** + * alloc.c + ***************************************************************************** + * Copyright (C) 2011-2014 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 + +void *lsmash_malloc( size_t size ) +{ + return malloc( size ); +} + +void *lsmash_malloc_zero( size_t size ) +{ + if( !size ) + return NULL; + void *p = malloc( size ); + if( !p ) + return NULL; + memset( p, 0, size ); + return p; +} + +void *lsmash_realloc( void *ptr, size_t size ) +{ + return realloc( ptr, size ); +} + +void *lsmash_memdup( const void *ptr, size_t size ) +{ + if( !ptr || size == 0 ) + return NULL; + void *dst = malloc( size ); + if( !dst ) + return NULL; + memcpy( dst, ptr, size ); + return dst; +} + +void lsmash_free( void *ptr ) +{ + /* free() shall do nothing if a given address is NULL. */ + free( ptr ); +} + +void lsmash_freep( void *ptrptr ) +{ + if( !ptrptr ) + return; + void **ptr = (void **)ptrptr; + free( *ptr ); + *ptr = NULL; +} diff -Nru l-smash-1.9.1/common/bstream.c l-smash-2.3.0/common/bstream.c --- l-smash-1.9.1/common/bstream.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/bstream.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,954 @@ +/***************************************************************************** + * bstream.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 + +lsmash_bs_t *lsmash_bs_create( void ) +{ + lsmash_bs_t *bs = lsmash_malloc_zero( sizeof(lsmash_bs_t) ); + if( !bs ) + return NULL; + bs->unseekable = 1; + bs->buffer.internal = 1; + bs->buffer.max_size = BS_MAX_DEFAULT_READ_SIZE; + return bs; +} + +static void bs_buffer_free( lsmash_bs_t *bs ) +{ + if( bs->buffer.internal ) + lsmash_free( bs->buffer.data ); + bs->buffer.data = NULL; + bs->buffer.alloc = 0; + bs->buffer.store = 0; + bs->buffer.pos = 0; +} + +void lsmash_bs_cleanup( lsmash_bs_t *bs ) +{ + if( !bs ) + return; + bs_buffer_free( bs ); + lsmash_free( bs ); +} + +int lsmash_bs_set_empty_stream( lsmash_bs_t *bs, uint8_t *data, size_t size ) +{ + if( !bs ) + return LSMASH_ERR_FUNCTION_PARAM; + bs->stream = NULL; /* empty stream */ + bs->eof = 1; /* unreadable because of empty stream */ + bs->eob = 0; /* readable on the buffer */ + bs->error = 0; + bs->unseekable = 1; /* only seek on the buffer */ + bs->written = size; /* behave as if the size of the empty stream is 'size' */ + bs->offset = size; /* behave as if the poiter of the stream is at the end */ + bs->buffer.unseekable = 0; /* only seek on the buffer */ + bs->buffer.internal = 0; /* must not be allocated internally */ + bs->buffer.data = data; + bs->buffer.store = size; + bs->buffer.alloc = size; + bs->buffer.pos = 0; + bs->buffer.max_size = 0; /* make no sense */ + bs->buffer.count = 0; + bs->read = NULL; + bs->write = NULL; + bs->seek = NULL; + return 0; +} + +void lsmash_bs_empty( lsmash_bs_t *bs ) +{ + if( !bs ) + return; + if( bs->buffer.data ) + memset( bs->buffer.data, 0, bs->buffer.alloc ); + bs->buffer.store = 0; + bs->buffer.pos = 0; +} + +static void bs_alloc( lsmash_bs_t *bs, size_t alloc ) +{ + if( (bs->buffer.alloc >= alloc) || bs->error ) + return; + if( !bs->buffer.internal ) + { + /* We cannot re-allocate the memory block. */ + bs->error = 1; + return; + } + alloc += (1 << 16); + alloc = LSMASH_MAX( alloc, bs->buffer.max_size ); + uint8_t *data; + if( !bs->buffer.data ) + data = lsmash_malloc( alloc ); + else + data = lsmash_realloc( bs->buffer.data, alloc ); + if( !data ) + { + bs_buffer_free( bs ); + bs->error = 1; + return; + } + bs->buffer.internal = 1; + bs->buffer.data = data; + bs->buffer.alloc = alloc; +} + +static uint64_t bs_estimate_seek_offset( lsmash_bs_t *bs, int64_t offset, int whence ) +{ + /* Calculate the offset after the seek. */ + uint64_t dst_offset; + if( whence == SEEK_SET ) + { + assert( offset >= 0 ); + if( bs->written < offset ) + dst_offset = bs->written; + else + dst_offset = offset; + } + else if( whence == SEEK_CUR ) + { + if( offset < 0 && bs->offset < -offset ) + dst_offset = 0; + else if( offset > 0 && bs->written < bs->offset + offset ) + dst_offset = bs->written; + else + dst_offset = bs->offset + offset; + } + else /* if( whence == SEEK_END ) */ + { + assert( offset <= 0 ); + if( bs->written < -offset ) + dst_offset = 0; + else + dst_offset = bs->written + offset; + } + return dst_offset; +} + +/* TODO: Support offset > INT64_MAX */ +int64_t lsmash_bs_write_seek( lsmash_bs_t *bs, int64_t offset, int whence ) +{ + if( bs->unseekable ) + return LSMASH_ERR_NAMELESS; + if( whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END ) + return LSMASH_ERR_FUNCTION_PARAM; + /* Try to seek the stream. */ + int64_t ret = bs->seek( bs->stream, offset, whence ); + if( ret < 0 ) + return ret; + bs->offset = bs_estimate_seek_offset( bs, offset, whence ); + bs->eof = 0; + bs->eob = 0; + return ret; +} + +/* TODO: Support offset > INT64_MAX */ +int64_t lsmash_bs_read_seek( lsmash_bs_t *bs, int64_t offset, int whence ) +{ + if( whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END ) + return LSMASH_ERR_FUNCTION_PARAM; + if( whence == SEEK_CUR ) + offset -= lsmash_bs_get_remaining_buffer_size( bs ); + /* Check whether we can seek on the buffer. */ + if( !bs->buffer.unseekable ) + { + assert( bs->offset >= bs->buffer.store ); + uint64_t dst_offset = bs_estimate_seek_offset( bs, offset, whence ); + uint64_t offset_s = bs->offset - bs->buffer.store; + uint64_t offset_e = bs->offset; + if( bs->unseekable || (dst_offset >= offset_s && dst_offset < offset_e) ) + { + /* OK, we can. So, seek on the buffer. */ + bs->buffer.pos = dst_offset - offset_s; + bs->eob = 0; + return lsmash_bs_get_stream_pos( bs ); + } + } + if( bs->unseekable ) + return LSMASH_ERR_NAMELESS; + /* Try to seek the stream. */ + int64_t ret = bs->seek( bs->stream, offset, whence ); + if( ret < 0 ) + return ret; + bs->offset = ret; + bs->written = LSMASH_MAX( bs->written, bs->offset ); + bs->eof = 0; + bs->eob = 0; + /* The data on the buffer is invalid. */ + lsmash_bs_empty( bs ); + return ret; +} + +static void bs_dispose_past_data( lsmash_bs_t *bs ) +{ + /* Move remainder bytes. */ + assert( bs->buffer.store >= bs->buffer.pos ); + size_t remainder = lsmash_bs_get_remaining_buffer_size( bs ); + if( bs->buffer.pos && remainder ) + memmove( lsmash_bs_get_buffer_data_start( bs ), lsmash_bs_get_buffer_data( bs ), remainder ); + bs->buffer.store = remainder; + bs->buffer.pos = 0; +} + +/*---- bitstream writer ----*/ +void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value ) +{ + if( bs->buffer.internal + || bs->buffer.data ) + { + bs_alloc( bs, bs->buffer.store + 1 ); + if( bs->error ) + return; + bs->buffer.data[ bs->buffer.store ] = value; + } + ++ bs->buffer.store; +} + +void lsmash_bs_put_bytes( lsmash_bs_t *bs, uint32_t size, void *value ) +{ + if( size == 0 || !value ) + return; + if( bs->buffer.internal + || bs->buffer.data ) + { + bs_alloc( bs, bs->buffer.store + size ); + if( bs->error ) + return; + memcpy( lsmash_bs_get_buffer_data_end( bs ), value, size ); + } + bs->buffer.store += size; +} + +void lsmash_bs_put_be16( lsmash_bs_t *bs, uint16_t value ) +{ + lsmash_bs_put_byte( bs, value >> 8 ); + lsmash_bs_put_byte( bs, value ); +} + +void lsmash_bs_put_be24( lsmash_bs_t *bs, uint32_t value ) +{ + lsmash_bs_put_byte( bs, value >> 16 ); + lsmash_bs_put_be16( bs, value ); +} + +void lsmash_bs_put_be32( lsmash_bs_t *bs, uint32_t value ) +{ + lsmash_bs_put_be16( bs, value >> 16 ); + lsmash_bs_put_be16( bs, value ); +} + +void lsmash_bs_put_be64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be32( bs, value >> 32 ); + lsmash_bs_put_be32( bs, value ); +} + +void lsmash_bs_put_byte_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_byte( bs, value ); +} + +void lsmash_bs_put_be16_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be16( bs, value ); +} + +void lsmash_bs_put_be24_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be24( bs, value ); +} + +void lsmash_bs_put_be32_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be32( bs, value ); +} + +void lsmash_bs_put_le16( lsmash_bs_t *bs, uint16_t value ) +{ + lsmash_bs_put_byte( bs, value ); + lsmash_bs_put_byte( bs, value >> 8 ); +} + +void lsmash_bs_put_le32( lsmash_bs_t *bs, uint32_t value ) +{ + lsmash_bs_put_le16( bs, value ); + lsmash_bs_put_le16( bs, value >> 16 ); +} + +int lsmash_bs_flush_buffer( lsmash_bs_t *bs ) +{ + if( !bs ) + return LSMASH_ERR_FUNCTION_PARAM; + if( bs->buffer.store == 0 + || (bs->stream && bs->write && !bs->buffer.data) ) + return 0; + if( bs->error + || (bs->stream && bs->write && bs->write( bs->stream, lsmash_bs_get_buffer_data_start( bs ), bs->buffer.store ) != bs->buffer.store) ) + { + bs_buffer_free( bs ); + bs->error = 1; + return LSMASH_ERR_NAMELESS; + } + if( bs->write ) + { + bs->written += bs->buffer.store; + bs->offset += bs->buffer.store; + } + bs->buffer.store = 0; + return 0; +} + +int lsmash_bs_write_data( lsmash_bs_t *bs, uint8_t *buf, size_t size ) +{ + if( !bs || size > INT_MAX ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !buf || size == 0 ) + return 0; + if( bs->error || !bs->stream ) + { + bs_buffer_free( bs ); + bs->error = 1; + return LSMASH_ERR_NAMELESS; + } + int write_size = bs->write( bs->stream, buf, size ); + bs->written += write_size; + bs->offset += write_size; + return write_size != size ? LSMASH_ERR_NAMELESS : 0; +} + +void *lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t *length ) +{ + if( !bs || !bs->buffer.data || bs->buffer.store == 0 || bs->error ) + return NULL; + void *buf = lsmash_memdup( lsmash_bs_get_buffer_data_start( bs ), bs->buffer.store ); + if( !buf ) + return NULL; + if( length ) + *length = bs->buffer.store; + return buf; +} +/*---- ----*/ + +/*---- bitstream reader ----*/ +static void bs_fill_buffer( lsmash_bs_t *bs ) +{ + if( bs->eof || bs->error ) + return; + if( !bs->read || !bs->stream || bs->buffer.max_size == 0 ) + { + bs->eof = 1; + return; + } + if( !bs->buffer.data ) + { + bs_alloc( bs, bs->buffer.max_size ); + if( bs->error ) + return; + } + /* Read bytes from the stream to fill the buffer. */ + bs_dispose_past_data( bs ); + while( bs->buffer.alloc > bs->buffer.store ) + { + uint64_t invalid_buffer_size = bs->buffer.alloc - bs->buffer.store; + int max_read_size = LSMASH_MIN( invalid_buffer_size, bs->buffer.max_size ); + int read_size = bs->read( bs->stream, lsmash_bs_get_buffer_data_end( bs ), max_read_size ); + if( read_size == 0 ) + { + bs->eof = 1; + return; + } + else if( read_size < 0 ) + { + bs->error = 1; + return; + } + bs->buffer.unseekable = 0; + bs->buffer.store += read_size; + bs->offset += read_size; + bs->written = LSMASH_MAX( bs->written, bs->offset ); + } +} + +uint8_t lsmash_bs_show_byte( lsmash_bs_t *bs, uint32_t offset ) +{ + if( bs->error ) + return 0; + if( offset >= lsmash_bs_get_remaining_buffer_size( bs ) ) + { + bs_fill_buffer( bs ); + if( bs->error ) + return 0; + if( offset >= lsmash_bs_get_remaining_buffer_size( bs ) ) + { + if( bs->eof ) + /* No more read from both the stream and the buffer. */ + return 0; + /* We need increase the buffer size. */ + bs_alloc( bs, bs->buffer.pos + offset + 1 ); + bs_fill_buffer( bs ); + if( bs->error ) + return 0; + } + } + return bs->buffer.data[ bs->buffer.pos + offset ]; +} + +uint16_t lsmash_bs_show_be16( lsmash_bs_t *bs, uint32_t offset ) +{ + return ((uint16_t)lsmash_bs_show_byte( bs, offset ) << 8) + | ((uint16_t)lsmash_bs_show_byte( bs, offset + 1 )); +} + +uint32_t lsmash_bs_show_be24( lsmash_bs_t *bs, uint32_t offset ) +{ + return ((uint32_t)lsmash_bs_show_byte( bs, offset ) << 16) + | ((uint32_t)lsmash_bs_show_byte( bs, offset + 1 ) << 8) + | ((uint32_t)lsmash_bs_show_byte( bs, offset + 2 )); +} + +uint32_t lsmash_bs_show_be32( lsmash_bs_t *bs, uint32_t offset ) +{ + return ((uint32_t)lsmash_bs_show_byte( bs, offset ) << 24) + | ((uint32_t)lsmash_bs_show_byte( bs, offset + 1 ) << 16) + | ((uint32_t)lsmash_bs_show_byte( bs, offset + 2 ) << 8) + | ((uint32_t)lsmash_bs_show_byte( bs, offset + 3 )); +} + +uint64_t lsmash_bs_show_be64( lsmash_bs_t *bs, uint32_t offset ) +{ + return ((uint64_t)lsmash_bs_show_byte( bs, offset ) << 56) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 1 ) << 48) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 2 ) << 40) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 3 ) << 32) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 4 ) << 24) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 5 ) << 16) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 6 ) << 8) + | ((uint64_t)lsmash_bs_show_byte( bs, offset + 7 )); +} + +uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs ) +{ + if( bs->eob || bs->error ) + return 0; + assert( bs->buffer.pos <= bs->buffer.store ); + if( bs->buffer.pos == bs->buffer.store ) + { + bs_fill_buffer( bs ); + if( bs->error ) + return 0; + if( bs->buffer.pos == bs->buffer.store && bs->eof ) + { + /* No more read from both the stream and the buffer. */ + bs->eob = 1; + return 0; + } + } + ++ bs->buffer.count; /* increment counter */ + return bs->buffer.data[ bs->buffer.pos ++ ]; +} + +void lsmash_bs_skip_bytes( lsmash_bs_t *bs, uint32_t size ) +{ + if( bs->eob || bs->error || size == 0 ) + return; + uint64_t remainder; + uint64_t offset = 0; + while( size > lsmash_bs_get_remaining_buffer_size( bs ) ) + { + remainder = lsmash_bs_get_remaining_buffer_size( bs ); + offset += remainder; + size -= remainder; + bs->buffer.pos = bs->buffer.store; + if( bs->eof ) + { + /* No more read from both the stream and the buffer. */ + bs->eob = 1; + break; + } + else + { + bs_fill_buffer( bs ); + if( bs->error ) + break; + } + } + remainder = LSMASH_MIN( size, lsmash_bs_get_remaining_buffer_size( bs ) ); + offset += remainder; + bs->buffer.pos += remainder; + bs->buffer.count += offset; +} + +void lsmash_bs_skip_bytes_64( lsmash_bs_t *bs, uint64_t size ) +{ + while( size ) + { + uint64_t skip_bytes = LSMASH_MIN( size, UINT32_MAX ); + lsmash_bs_skip_bytes( bs, (uint32_t)skip_bytes ); + size -= skip_bytes; + if( bs->eob ) + return; + } +} + +static int64_t bs_get_bytes( lsmash_bs_t *bs, uint32_t size, uint8_t *buf ) +{ + size_t remainder; + size_t remain_size = size; + uintptr_t offset = 0; + while( remain_size > lsmash_bs_get_remaining_buffer_size( bs ) ) + { + remainder = lsmash_bs_get_remaining_buffer_size( bs ); + memcpy( buf + offset, lsmash_bs_get_buffer_data( bs ), remainder ); + offset += remainder; + remain_size -= remainder; + bs->buffer.pos = bs->buffer.store; + if( bs->eof ) + { + /* No more read from both the stream and the buffer. */ + bs->eob = 1; + break; + } + else + { + bs_fill_buffer( bs ); + if( bs->error ) + { + bs->buffer.count += offset; + return LSMASH_ERR_NAMELESS; + } + } + } + remainder = LSMASH_MIN( remain_size, lsmash_bs_get_remaining_buffer_size( bs ) ); + memcpy( buf + offset, lsmash_bs_get_buffer_data( bs ), remainder ); + offset += remainder; + bs->buffer.pos += remainder; + bs->buffer.count += offset; + if( offset < size ) + memset( buf + offset, 0, size - offset ); + return (int64_t)offset; +} + +uint8_t *lsmash_bs_get_bytes( lsmash_bs_t *bs, uint32_t size ) +{ + if( bs->eob || bs->error || size == 0 ) + return NULL; + uint8_t *value = lsmash_malloc( size ); + if( !value ) + { + bs->error = 1; + return NULL; + } + if( bs_get_bytes( bs, size, value ) < 0 ) + { + lsmash_free( value ); + return NULL; + } + return value; +} + +int64_t lsmash_bs_get_bytes_ex( lsmash_bs_t *bs, uint32_t size, uint8_t *value ) +{ + if( size == 0 ) + return 0; + if( bs->eob || bs->error ) + return LSMASH_ERR_NAMELESS; + return bs_get_bytes( bs, size, 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( lsmash_bs_t *bs, uint32_t size ) +{ + if( !bs || size > INT_MAX ) + return LSMASH_ERR_FUNCTION_PARAM; + if( size == 0 ) + return 0; + bs_alloc( bs, bs->buffer.store + size ); + if( bs->error || !bs->stream ) + { + bs->error = 1; + return LSMASH_ERR_NAMELESS; + } + int read_size = bs->read( bs->stream, lsmash_bs_get_buffer_data_end( bs ), size ); + if( read_size == 0 ) + { + bs->eof = 1; + return 0; + } + else if( read_size < 0 ) + { + bs->error = 1; + return LSMASH_ERR_NAMELESS; + } + bs->buffer.store += read_size; + bs->offset += read_size; + bs->written = LSMASH_MAX( bs->written, bs->offset ); + return read_size; +} + +int lsmash_bs_read_data( lsmash_bs_t *bs, uint8_t *buf, size_t *size ) +{ + if( !bs || !size || *size > INT_MAX ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !buf || *size == 0 ) + return 0; + if( bs->error || !bs->stream ) + { + bs->error = 1; + return LSMASH_ERR_NAMELESS; + } + int read_size = bs->read( bs->stream, buf, *size ); + if( read_size == 0 ) + bs->eof = 1; + else if( read_size < 0 ) + { + bs->error = 1; + return LSMASH_ERR_NAMELESS; + } + bs->buffer.unseekable = 1; + bs->offset += read_size; + *size = read_size; + bs->written = LSMASH_MAX( bs->written, bs->offset ); + return 0; +} + +int lsmash_bs_import_data( lsmash_bs_t *bs, void *data, uint32_t length ) +{ + if( !bs || !data || length == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + if( bs->error ) + return LSMASH_ERR_NAMELESS; + bs_alloc( bs, bs->buffer.store + length ); + if( bs->error || !bs->buffer.data ) /* means, failed to alloc. */ + { + bs_buffer_free( bs ); + return LSMASH_ERR_NAMELESS; + } + memcpy( lsmash_bs_get_buffer_data_end( bs ), data, length ); + bs->buffer.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 *)lsmash_malloc( sizeof(lsmash_bits_t) ); + if( !bits ) + return NULL; + lsmash_bits_init( bits, bs ); + return bits; +} + +void lsmash_bits_empty( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + lsmash_bs_empty( bits->bs ); + bits->store = 0; + bits->cache = 0; +} + +#define BITS_IN_BYTE 8 +void lsmash_bits_put_align( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + if( !bits->store ) + return; + lsmash_bs_put_byte( bits->bs, bits->cache << ( BITS_IN_BYTE - bits->store ) ); +} + +void lsmash_bits_get_align( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + bits->store = 0; + bits->cache = 0; +} + +/* Must be used ONLY for bits struct created with isom_create_bits. + Otherwise, just lsmash_free() the bits struct. */ +void lsmash_bits_cleanup( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + lsmash_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 ) ); +} + +void lsmash_bits_put( lsmash_bits_t *bits, uint32_t width, uint64_t value ) +{ + 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; + } +} + +uint64_t lsmash_bits_get( lsmash_bits_t *bits, uint32_t width ) +{ + debug_if( !bits || !width ) + return 0; + uint64_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(); + 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 ); +} +/*---- ----*/ + +/*---- basic I/O ----*/ +int lsmash_fread_wrapper( void *opaque, uint8_t *buf, int size ) +{ + int read_size = fread( buf, 1, size, (FILE *)opaque ); + return ferror( (FILE *)opaque ) ? LSMASH_ERR_NAMELESS : read_size; +} + +int lsmash_fwrite_wrapper( void *opaque, uint8_t *buf, int size ) +{ + return fwrite( buf, 1, size, (FILE *)opaque ); +} + +int64_t lsmash_fseek_wrapper( void *opaque, int64_t offset, int whence ) +{ + if( lsmash_fseek( (FILE *)opaque, offset, whence ) != 0 ) + return LSMASH_ERR_NAMELESS; + return lsmash_ftell( (FILE *)opaque ); +} + +lsmash_multiple_buffers_t *lsmash_create_multiple_buffers( uint32_t number_of_buffers, uint32_t buffer_size ) +{ + if( (uint64_t)number_of_buffers * buffer_size > UINT32_MAX ) + return NULL; + lsmash_multiple_buffers_t *multiple_buffer = lsmash_malloc( sizeof(lsmash_multiple_buffers_t) ); + if( !multiple_buffer ) + return NULL; + multiple_buffer->buffers = lsmash_malloc( number_of_buffers * buffer_size ); + if( !multiple_buffer->buffers ) + { + lsmash_free( multiple_buffer ); + return NULL; + } + multiple_buffer->number_of_buffers = number_of_buffers; + multiple_buffer->buffer_size = buffer_size; + return multiple_buffer; +} + +void *lsmash_withdraw_buffer( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_number ) +{ + if( !multiple_buffer || !buffer_number || buffer_number > multiple_buffer->number_of_buffers ) + return NULL; + return (uint8_t *)multiple_buffer->buffers + (buffer_number - 1) * multiple_buffer->buffer_size; +} + +lsmash_multiple_buffers_t *lsmash_resize_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_size ) +{ + if( !multiple_buffer ) + return NULL; + if( buffer_size == multiple_buffer->buffer_size ) + return multiple_buffer; + if( (uint64_t)multiple_buffer->number_of_buffers * buffer_size > UINT32_MAX ) + return NULL; + uint8_t *temp; + if( buffer_size > multiple_buffer->buffer_size ) + { + temp = lsmash_realloc( multiple_buffer->buffers, multiple_buffer->number_of_buffers * buffer_size ); + if( !temp ) + return NULL; + for( uint32_t i = multiple_buffer->number_of_buffers - 1; i ; i-- ) + memmove( temp + buffer_size, temp + i * multiple_buffer->buffer_size, multiple_buffer->buffer_size ); + } + else + { + for( uint32_t i = 1; i < multiple_buffer->number_of_buffers; i++ ) + memmove( (uint8_t *)multiple_buffer->buffers + buffer_size, + (uint8_t *)multiple_buffer->buffers + i * multiple_buffer->buffer_size, + multiple_buffer->buffer_size ); + temp = lsmash_realloc( multiple_buffer->buffers, multiple_buffer->number_of_buffers * buffer_size ); + if( !temp ) + return NULL; + } + multiple_buffer->buffers = temp; + multiple_buffer->buffer_size = buffer_size; + return multiple_buffer; +} + +void lsmash_destroy_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer ) +{ + if( !multiple_buffer ) + return; + lsmash_free( multiple_buffer->buffers ); + lsmash_free( multiple_buffer ); +} diff -Nru l-smash-1.9.1/common/bstream.h l-smash-2.3.0/common/bstream.h --- l-smash-1.9.1/common/bstream.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/bstream.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,281 @@ +/***************************************************************************** + * bstream.h + ***************************************************************************** + * Copyright (C) 2010-2014 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. */ + +/*---- bytestream ----*/ +#define BS_MAX_DEFAULT_READ_SIZE (4 * 1024 * 1024) + +typedef struct +{ + int unseekable; /* If set to 1, the buffer is unseekable. */ + int internal; /* If set to 1, the buffer is allocated on heap internally. + * The pointer to the buffer shall not be changed by any method other than internal allocation. */ + uint8_t *data; /* the pointer to the buffer for reading/writing */ + size_t store; /* valid data size on the buffer */ + size_t alloc; /* total buffer size including invalid area */ + size_t pos; /* the data position on the buffer to be read next */ + size_t max_size; /* the maximum number of bytes for reading from the stream at one time */ + uint64_t count; /* counter for arbitrary usage */ +} lsmash_buffer_t; + +typedef struct +{ + void *stream; /* I/O stream */ + uint8_t eof; /* If set to 1, the stream reached EOF. */ + uint8_t eob; /* if set to 1, we cannot read more bytes from the stream and the buffer until any seek. */ + uint8_t error; /* If set to 1, any error is detected. */ + uint8_t unseekable; /* If set to 1, the stream is unseekable. */ + uint64_t written; /* the number of bytes written into 'stream' already */ + uint64_t offset; /* the current position in the 'stream' + * the number of bytes from the beginning */ + lsmash_buffer_t buffer; + int (*read) ( void *opaque, uint8_t *buf, int size ); + int (*write)( void *opaque, uint8_t *buf, int size ); + int64_t (*seek) ( void *opaque, int64_t offset, int whence ); +} lsmash_bs_t; + +static inline void lsmash_bs_reset_counter( lsmash_bs_t *bs ) +{ + bs->buffer.count = 0; +} + +static inline uint64_t lsmash_bs_count( lsmash_bs_t *bs ) +{ + return bs->buffer.count; +} + +static inline uint64_t lsmash_bs_get_remaining_buffer_size( lsmash_bs_t *bs ) +{ + assert( bs->buffer.store >= bs->buffer.pos ); + return bs->buffer.store - bs->buffer.pos; +} + +static inline uint8_t *lsmash_bs_get_buffer_data( lsmash_bs_t *bs ) +{ + return bs->buffer.data + (uintptr_t)bs->buffer.pos; +} + +static inline uint8_t *lsmash_bs_get_buffer_data_start( lsmash_bs_t *bs ) +{ + return bs->buffer.data; +} + +static inline uint8_t *lsmash_bs_get_buffer_data_end( lsmash_bs_t *bs ) +{ + return bs->buffer.data + (uintptr_t)bs->buffer.store; +} + +static inline uint64_t lsmash_bs_get_pos( lsmash_bs_t *bs ) +{ + return bs->buffer.pos; +} + +static inline uint64_t lsmash_bs_get_stream_pos( lsmash_bs_t *bs ) +{ + assert( bs->buffer.store <= bs->offset ); + return bs->offset - lsmash_bs_get_remaining_buffer_size( bs ); +} + +static inline size_t lsmash_bs_get_valid_data_size( lsmash_bs_t *bs ) +{ + return bs->buffer.store; +} + +lsmash_bs_t *lsmash_bs_create( void ); +void lsmash_bs_cleanup( lsmash_bs_t *bs ); +int lsmash_bs_set_empty_stream( lsmash_bs_t *bs, uint8_t *data, size_t size ); +void lsmash_bs_empty( lsmash_bs_t *bs ); +int64_t lsmash_bs_write_seek( lsmash_bs_t *bs, int64_t offset, int whence ); +int64_t lsmash_bs_read_seek( lsmash_bs_t *bs, int64_t offset, int whence ); + +/*---- bytestream writer ----*/ +void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value ); +void lsmash_bs_put_bytes( lsmash_bs_t *bs, uint32_t size, void *value ); +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 ); +void lsmash_bs_put_le16( lsmash_bs_t *bs, uint16_t value ); +void lsmash_bs_put_le32( lsmash_bs_t *bs, uint32_t value ); +int lsmash_bs_flush_buffer( lsmash_bs_t *bs ); +int lsmash_bs_write_data( lsmash_bs_t *bs, uint8_t *buf, size_t size ); +void *lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t *length ); + +/*---- bytestream reader ----*/ +uint8_t lsmash_bs_show_byte( lsmash_bs_t *bs, uint32_t offset ); +uint16_t lsmash_bs_show_be16( lsmash_bs_t *bs, uint32_t offset ); +uint32_t lsmash_bs_show_be24( lsmash_bs_t *bs, uint32_t offset ); +uint32_t lsmash_bs_show_be32( lsmash_bs_t *bs, uint32_t offset ); +uint64_t lsmash_bs_show_be64( lsmash_bs_t *bs, uint32_t offset ); +uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs ); +void lsmash_bs_skip_bytes( lsmash_bs_t *bs, uint32_t size ); +void lsmash_bs_skip_bytes_64( lsmash_bs_t *bs, uint64_t size ); +uint8_t *lsmash_bs_get_bytes( lsmash_bs_t *bs, uint32_t size ); +int64_t lsmash_bs_get_bytes_ex( lsmash_bs_t *bs, uint32_t size, uint8_t *value ); +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( lsmash_bs_t *bs, uint32_t size ); +int lsmash_bs_read_data( lsmash_bs_t *bs, uint8_t *buf, size_t *size ); +int lsmash_bs_import_data( lsmash_bs_t *bs, void *data, uint32_t length ); + +/* Check if the given offset reaches both EOF of the stream and the end of the buffer. */ +static inline int lsmash_bs_is_end( lsmash_bs_t *bs, uint32_t offset ) +{ + lsmash_bs_show_byte( bs, offset ); + return bs->eof && (offset >= lsmash_bs_get_remaining_buffer_size( bs )); +} + +/*---- bitstream ----*/ +typedef struct { + lsmash_bs_t* bs; + uint8_t store; + uint8_t cache; +} lsmash_bits_t; + +void lsmash_bits_init( lsmash_bits_t* bits, lsmash_bs_t *bs ); +lsmash_bits_t *lsmash_bits_create( lsmash_bs_t *bs ); +void lsmash_bits_empty( lsmash_bits_t *bits ); +void lsmash_bits_put_align( lsmash_bits_t *bits ); +void lsmash_bits_get_align( lsmash_bits_t *bits ); +void lsmash_bits_cleanup( lsmash_bits_t *bits ); + +/*---- bitstream writer ----*/ +void lsmash_bits_put( lsmash_bits_t *bits, uint32_t width, uint64_t value ); +uint64_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 ); + +/*---- basic I/O ----*/ +int lsmash_fread_wrapper( void *opaque, uint8_t *buf, int size ); +int lsmash_fwrite_wrapper( void *opaque, uint8_t *buf, int size ); +int64_t lsmash_fseek_wrapper( void *opaque, int64_t offset, int whence ); + +/*---- multiple buffers ----*/ +typedef struct +{ + uint32_t number_of_buffers; + uint32_t buffer_size; + void *buffers; +} lsmash_multiple_buffers_t; + +lsmash_multiple_buffers_t *lsmash_create_multiple_buffers( uint32_t number_of_buffers, uint32_t buffer_size ); +void *lsmash_withdraw_buffer( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_number ); +lsmash_multiple_buffers_t *lsmash_resize_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_size ); +void lsmash_destroy_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer ); + +/*---- memory writers ----*/ +#define LSMASH_SET_BYTE( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x); \ + } while( 0 ) +#define LSMASH_SET_BE16( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x) >> 8; \ + ((uint8_t *)(p))[1] = (x); \ + } while( 0 ) +#define LSMASH_SET_BE24( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x) >> 16; \ + ((uint8_t *)(p))[1] = (x) >> 8; \ + ((uint8_t *)(p))[2] = (x); \ + } while( 0 ) +#define LSMASH_SET_BE32( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x) >> 24; \ + ((uint8_t *)(p))[1] = (x) >> 16; \ + ((uint8_t *)(p))[2] = (x) >> 8; \ + ((uint8_t *)(p))[3] = (x); \ + } while( 0 ) +#define LSMASH_SET_BE64( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x) >> 56; \ + ((uint8_t *)(p))[1] = (x) >> 48; \ + ((uint8_t *)(p))[2] = (x) >> 40; \ + ((uint8_t *)(p))[3] = (x) >> 32; \ + ((uint8_t *)(p))[4] = (x) >> 24; \ + ((uint8_t *)(p))[5] = (x) >> 16; \ + ((uint8_t *)(p))[6] = (x) >> 8; \ + ((uint8_t *)(p))[7] = (x); \ + } while( 0 ) +#define LSMASH_SET_LE16( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x); \ + ((uint8_t *)(p))[1] = (x) >> 8; \ + } while( 0 ) +#define LSMASH_SET_LE32( p, x ) \ + do \ + { \ + ((uint8_t *)(p))[0] = (x); \ + ((uint8_t *)(p))[1] = (x) >> 8; \ + ((uint8_t *)(p))[2] = (x) >> 16; \ + ((uint8_t *)(p))[3] = (x) >> 24; \ + } while( 0 ) + +/*---- memory readers ----*/ +#define LSMASH_GET_BYTE( p ) \ + (((const uint8_t *)(p))[0]) +#define LSMASH_GET_BE16( p ) \ + (((uint16_t)((const uint8_t *)(p))[0] << 8) \ + | ((uint16_t)((const uint8_t *)(p))[1])) +#define LSMASH_GET_BE24( p ) \ + (((uint32_t)((const uint8_t *)(p))[0] << 16) \ + | ((uint32_t)((const uint8_t *)(p))[1] << 8) \ + | ((uint32_t)((const uint8_t *)(p))[2])) +#define LSMASH_GET_BE32( p ) \ + (((uint32_t)((const uint8_t *)(p))[0] << 24) \ + | ((uint32_t)((const uint8_t *)(p))[1] << 16) \ + | ((uint32_t)((const uint8_t *)(p))[2] << 8) \ + | ((uint32_t)((const uint8_t *)(p))[3])) +#define LSMASH_GET_BE64( p ) \ + (((uint64_t)((const uint8_t *)(p))[0] << 56) \ + | ((uint64_t)((const uint8_t *)(p))[1] << 48) \ + | ((uint64_t)((const uint8_t *)(p))[2] << 40) \ + | ((uint64_t)((const uint8_t *)(p))[3] << 32) \ + | ((uint64_t)((const uint8_t *)(p))[4] << 24) \ + | ((uint64_t)((const uint8_t *)(p))[5] << 16) \ + | ((uint64_t)((const uint8_t *)(p))[6] << 8) \ + | ((uint64_t)((const uint8_t *)(p))[7])) +#define LSMASH_GET_LE16( p ) \ + (((uint16_t)((const uint8_t *)(p))[0]) \ + | ((uint16_t)((const uint8_t *)(p))[1] << 8)) +#define LSMASH_GET_LE32( p ) \ + (((uint32_t)((const uint8_t *)(p))[0]) \ + | ((uint32_t)((const uint8_t *)(p))[1] << 8) \ + | ((uint32_t)((const uint8_t *)(p))[2] << 16) \ + | ((uint32_t)((const uint8_t *)(p))[3] << 24)) diff -Nru l-smash-1.9.1/common/internal.h l-smash-2.3.0/common/internal.h --- l-smash-1.9.1/common/internal.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/internal.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,41 @@ +/***************************************************************************** + * internal.h: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "osdep.h" /* must be placed before stdio.h */ +#include +#include + +#ifndef lsmash_fseek +#define lsmash_fseek fseeko +#define lsmash_ftell ftello +#endif + +#include "lsmash.h" + +#include "utils.h" +#include "bstream.h" +#include "list.h" + +#endif diff -Nru l-smash-1.9.1/common/list.c l-smash-2.3.0/common/list.c --- l-smash-1.9.1/common/list.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/list.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,191 @@ +/***************************************************************************** + * list.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 + +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 = lsmash_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 LSMASH_ERR_FUNCTION_PARAM; + lsmash_entry_t *entry = lsmash_malloc( sizeof(lsmash_entry_t) ); + if( !entry ) + return LSMASH_ERR_MEMORY_ALLOC; + 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_orig( lsmash_entry_list_t *list, lsmash_entry_t *entry, lsmash_entry_data_eliminator eliminator ) +{ + if( !list || !entry ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !eliminator ) + eliminator = lsmash_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 ) + 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; + } + lsmash_free( entry ); + list->entry_count -= 1; + return 0; +} + +int lsmash_remove_entry_orig( lsmash_entry_list_t *list, uint32_t entry_number, lsmash_entry_data_eliminator eliminator ) +{ + lsmash_entry_t *entry = lsmash_get_entry( list, entry_number ); + return lsmash_remove_entry_direct( list, entry, eliminator ); +} + +int lsmash_remove_entry_tail_orig( lsmash_entry_list_t *list, lsmash_entry_data_eliminator eliminator ) +{ + return lsmash_remove_entry_direct( list, list->tail, eliminator ); +} + +void lsmash_remove_entries_orig( lsmash_entry_list_t *list, lsmash_entry_data_eliminator eliminator ) +{ + if( !list ) + return; + if( !eliminator ) + eliminator = lsmash_free; + for( lsmash_entry_t *entry = list->head; entry; ) + { + lsmash_entry_t *next = entry->next; + if( entry->data ) + eliminator( entry->data ); + lsmash_free( entry ); + entry = next; + } + lsmash_init_entry_list( list ); +} + +void lsmash_remove_list_orig( lsmash_entry_list_t *list, lsmash_entry_data_eliminator eliminator ) +{ + if( !list ) + return; + lsmash_remove_entries( list, eliminator ); + lsmash_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; +} diff -Nru l-smash-1.9.1/common/list.h l-smash-2.3.0/common/list.h --- l-smash-1.9.1/common/list.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/list.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,68 @@ +/***************************************************************************** + * list.h + ***************************************************************************** + * Copyright (C) 2010-2014 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. */ + +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 *); */ + +#define lsmash_remove_entry_direct( list, entry, eliminator ) \ + lsmash_remove_entry_direct_orig( list, entry, (lsmash_entry_data_eliminator)(eliminator) ) + +#define lsmash_remove_entry( list, entry_number, eliminator ) \ + lsmash_remove_entry_orig( list, entry_number, (lsmash_entry_data_eliminator)(eliminator) ) + +#define lsmash_remove_entry_tail( list, eliminator ) \ + lsmash_remove_entry_tail_orig( list, (lsmash_entry_data_eliminator)(eliminator) ) + +#define lsmash_remove_entries( list, eliminator ) \ + lsmash_remove_entries_orig( list, (lsmash_entry_data_eliminator)(eliminator) ) + +#define lsmash_remove_list( list, eliminator ) \ + lsmash_remove_list_orig( list, (lsmash_entry_data_eliminator)(eliminator) ) + +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_orig( lsmash_entry_list_t *list, lsmash_entry_t *entry, lsmash_entry_data_eliminator eliminator ); +int lsmash_remove_entry_orig( lsmash_entry_list_t *list, uint32_t entry_number, lsmash_entry_data_eliminator eliminator ); +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 ); + +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 -Nru l-smash-1.9.1/common/osdep.c l-smash-2.3.0/common/osdep.c --- l-smash-1.9.1/common/osdep.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/osdep.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,67 @@ +/***************************************************************************** + * osdep.c: + ***************************************************************************** + * Copyright (C) 2013-2014 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 + +#ifdef _WIN32 +#include +#endif + +#ifdef _WIN32 + +int lsmash_string_to_wchar( int cp, const char *from, wchar_t **to ) +{ + int nc = MultiByteToWideChar( cp, 0, from, -1, 0, 0 ); + if( nc == 0 ) + return 0; + *to = lsmash_malloc( nc * sizeof(wchar_t) ); + MultiByteToWideChar( cp, 0, from, -1, *to, nc ); + return nc; +} + +int lsmash_string_from_wchar( int cp, const wchar_t *from, char **to ) +{ + int nc = WideCharToMultiByte( cp, 0, from, -1, 0, 0, 0, 0 ); + if( nc == 0 ) + return 0; + *to = lsmash_malloc( nc * sizeof(char) ); + WideCharToMultiByte( cp, 0, from, -1, *to, nc, 0, 0 ); + return nc; +} + +FILE *lsmash_win32_fopen( const char *name, const char *mode ) +{ + wchar_t *wname = NULL, *wmode = NULL; + lsmash_string_to_wchar( CP_UTF8, name, &wname ); + lsmash_string_to_wchar( CP_UTF8, mode, &wmode ); + FILE *fp = _wfopen( wname, wmode ); + if( !fp ) + fp = fopen( name, mode ); + lsmash_free( wname ); + lsmash_free( wmode ); + return fp; +} + +#endif + diff -Nru l-smash-1.9.1/common/osdep.h l-smash-2.3.0/common/osdep.h --- l-smash-1.9.1/common/osdep.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/common/osdep.h 2014-11-06 14:12:27.000000000 +0000 @@ -25,6 +25,7 @@ #define OSDEP_H #define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE #ifdef __MINGW32__ #define lsmash_fseek fseeko64 diff -Nru l-smash-1.9.1/common/utils.c l-smash-2.3.0/common/utils.c --- l-smash-1.9.1/common/utils.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/utils.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,202 @@ +/***************************************************************************** + * utils.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +/*---- 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; +} +/*---- ----*/ + +/*---- others ----*/ +void lsmash_log +( + const void *class, + lsmash_log_level level, + const char *message, + ... +) +{ + /* Dereference lsmash_class_t pointer if 'class' is non-NULL. */ + lsmash_class_t *cls = class ? (lsmash_class_t *)*(intptr_t *)class : NULL; + if( cls && cls->log_level_offset ) + { + lsmash_log_level log_level = *(lsmash_log_level *)((int8_t *)class + cls->log_level_offset); + if( level > log_level ) + return; + } + char *prefix; + va_list args; + va_start( args, message ); + switch( level ) + { + case LSMASH_LOG_ERROR: + prefix = "Error"; + break; + case LSMASH_LOG_WARNING: + prefix = "Warning"; + break; + case LSMASH_LOG_INFO: + prefix = "Info"; + break; + default: + prefix = "Unknown"; + break; + } + if( cls ) + fprintf( stderr, "[%s: %s]: ", cls->name, prefix ); + else + fprintf( stderr, "[%s]: ", prefix ); + vfprintf( stderr, message, args ); + va_end( args ); +} + +void lsmash_log_refresh_line +( + const void *class /* unused, but for forward compatibility */ +) +{ + /* Assume 80 characters per line. */ + fprintf( stderr, "%80c", '\r' ); +} + +uint32_t lsmash_count_bits +( + uint32_t bits +) +{ + bits = (bits & 0x55555555) + ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + bits = (bits & 0x0f0f0f0f) + ((bits >> 4) & 0x0f0f0f0f); + bits = (bits & 0x00ff00ff) + ((bits >> 8) & 0x00ff00ff); + return (bits & 0x0000ffff) + ((bits >> 16) & 0x0000ffff); +} + +void lsmash_ifprintf +( + FILE *fp, + int indent, + const char *format, ... +) +{ + va_list args; + va_start( args, format ); + if( indent <= 10 ) + { + static const char *indent_string[] = + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + }; + fprintf( fp, "%s", indent_string[indent] ); + } + else + for( int i = 0; i < indent; i++ ) + fprintf( fp, " " ); + vfprintf( fp, format, args ); + va_end( args ); +} + +int lsmash_ceil_log2 +( + uint64_t value +) +{ + int length = 0; + while( value > (1ULL << length) ) + ++length; + return length; +} + +/* for qsort function */ +int lsmash_compare_dts +( + const lsmash_media_ts_t *a, + const lsmash_media_ts_t *b +) +{ + int64_t diff = (int64_t)(a->dts - b->dts); + return diff > 0 ? 1 : (diff == 0 ? 0 : -1); +} + +int lsmash_compare_cts +( + const lsmash_media_ts_t *a, + const lsmash_media_ts_t *b +) +{ + int64_t diff = (int64_t)(a->cts - b->cts); + return diff > 0 ? 1 : (diff == 0 ? 0 : -1); +} + +#ifdef _WIN32 +int lsmash_convert_ansi_to_utf8( const char *ansi, char *utf8, int length ) +{ + int len0 = MultiByteToWideChar( CP_THREAD_ACP, 0, ansi, -1, 0, 0 ); + wchar_t *buff = lsmash_malloc( len0 * sizeof(wchar_t) ); + if( !buff ) + return 0; + int len1 = MultiByteToWideChar( CP_THREAD_ACP, 0, ansi, -1, buff, len0 ); + if( len0 != len1 ) + goto convert_fail; + len0 = WideCharToMultiByte( CP_UTF8, 0, buff, -1, 0, 0, 0, 0 ); + if( len0 > length - 1 ) + goto convert_fail; + len1 = WideCharToMultiByte( CP_UTF8, 0, buff, -1, utf8, length, 0, 0 ); + lsmash_free( buff ); + if( len0 != len1 ) + return 0; + return len1; +convert_fail: + lsmash_free( buff ); + return 0; +} +#endif diff -Nru l-smash-1.9.1/common/utils.h l-smash-2.3.0/common/utils.h --- l-smash-1.9.1/common/utils.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/common/utils.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,197 @@ +/***************************************************************************** + * utils.h + ***************************************************************************** + * Copyright (C) 2010-2014 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)) + +/* default arguments + * Use only CALL_FUNC_DEFAULT_ARGS(). + * The defined macros can't be passed a macro argument requiring the empty parameter list. + * + * The following is an example. + * #define TEMPLATE_A( ... ) CALL_FUNC_DEFAULT_ARGS( TEMPLATE_A, __VA_ARGS__ ) + * #define TEMPLATE_A_1( _1 ) _1( 1 ) + * #define TEMPLATE_B( ... ) CALL_FUNC_DEFAULT_ARGS( TEMPLATE_B, __VA_ARGS__ ) + * #define TEMPLATE_B_2( _1, _2 ) ((_1) + (_2)) + * #define TEMPLATE_B_1( _1 ) TEMPLATE_B_2( _1, 0 ) + * #define TEMPLATE_B_0() + * int main( void ) + * { + * TEMPLATE_A( TEMPLATE_B_1 ); // OK + * TEMPLATE_A( TEMPLATE_B ); // NG + * TEMPLATE_B( 1, 2 ); // OK + * TEMPLATE_B(); // NG + * return 0; + * } + * */ +#define NUM_ARGS( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ... ) _10 +#define COUNT_NUM_ARGS( ... ) NUM_ARGS( __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0 ) +#define GET_FUNC_BY_NUM_ARGS_EXN( func_name, N ) func_name ## _ ## N +#define GET_FUNC_BY_NUM_ARGS_EX0( func_name, N ) GET_FUNC_BY_NUM_ARGS_EXN( func_name, N ) +#define GET_FUNC_BY_NUM_ARGS_EX1( func_name, ... ) GET_FUNC_BY_NUM_ARGS_EX0( func_name, COUNT_NUM_ARGS( __VA_ARGS__ ) ) +#define CALL_FUNC_DEFAULT_ARGS( func_name, ... ) GET_FUNC_BY_NUM_ARGS_EX1( func_name, __VA_ARGS__ ) ( __VA_ARGS__ ) + +/*---- class ----*/ +typedef struct +{ + char *name; + size_t log_level_offset; /* offset in the struct where 'log_level' is placed + * If set to 0, 'log_level' is unavailable and implicitly set to LSMASH_LOG_INFO. */ +} lsmash_class_t; + +/*---- type ----*/ +double lsmash_fixed2double( uint64_t value, int frac_width ); +float lsmash_int2float32( uint32_t value ); +double lsmash_int2float64( uint64_t value ); + +/*---- others ----*/ +typedef enum +{ + LSMASH_LOG_QUIET = 0, + LSMASH_LOG_ERROR, + LSMASH_LOG_WARNING, + LSMASH_LOG_INFO, +} lsmash_log_level; + +typedef struct +{ + uint64_t n; + uint64_t d; +} lsmash_rational_u64_t; + +typedef struct +{ + int64_t n; + uint64_t d; +} lsmash_rational_s64_t; + +void lsmash_log +( + const void *class, + lsmash_log_level level, + const char *message, + ... +); + +void lsmash_log_refresh_line +( + const void *class +); + +uint32_t lsmash_count_bits +( + uint32_t bits +); + +void lsmash_ifprintf +( + FILE *fp, + int indent, + const char *format, ... +); + +int lsmash_ceil_log2 +( + uint64_t value +); + +int lsmash_compare_dts +( + const lsmash_media_ts_t *a, + const lsmash_media_ts_t *b +); + +int lsmash_compare_cts +( + const lsmash_media_ts_t *a, + const lsmash_media_ts_t *b +); + +static inline uint64_t lsmash_get_gcd +( + uint64_t a, + uint64_t b +) +{ + if( !b ) + return a; + while( 1 ) + { + uint64_t c = a % b; + if( !c ) + return b; + a = b; + b = c; + } +} + +static inline uint64_t lsmash_get_lcm +( + uint64_t a, + uint64_t b +) +{ + if( !a ) + return 0; + return (a / lsmash_get_gcd( a, b )) * b; +} + +static inline void lsmash_reduce_fraction +( + uint64_t *a, + uint64_t *b +) +{ + if( !a || !b ) + return; + uint64_t gcd = lsmash_get_gcd( *a, *b ); + if( gcd ) + { + *a /= gcd; + *b /= gcd; + } +} + +static inline void lsmash_reduce_fraction_su +( + int64_t *a, + uint64_t *b +) +{ + if( !a || !b ) + return; + uint64_t c = *a > 0 ? *a : -(*a); + uint64_t gcd = lsmash_get_gcd( c, *b ); + if( gcd ) + { + c /= gcd; + *b /= gcd; + *a = *a > 0 ? c : -c; + } +} + +#endif diff -Nru l-smash-1.9.1/configure l-smash-2.3.0/configure --- l-smash-1.9.1/configure 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/configure 2014-11-06 14:12:27.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh #---------------------------------------------------------------------------- # configure script for L-SMASH @@ -66,7 +66,8 @@ echo -SRCDIR="$(cd $(dirname $0); pwd)" +SRCDIR=$(dirname "$0") +SRCDIR=$(cd "$SRCDIR"; pwd) test "$SRCDIR" = "$(pwd)" && SRCDIR=. test -n "$(echo $SRCDIR | grep ' ')" && \ error_exit "out-of-tree builds are impossible with whitespace in source path" @@ -103,7 +104,7 @@ TOOLS="" -CFLAGS="-Wshadow -Wall -std=gnu99 -I. -I$SRCDIR" +CFLAGS="-Wshadow -Wall -std=c99 -pedantic -I. -I$SRCDIR" LDFLAGS="-L." SO_LDFLAGS='-shared -Wl,-soname,$@' LIBS="-lm" @@ -164,7 +165,7 @@ --extra-libs=*) XLIBS="$optarg" ;; - # "--disable-demuxer" is the special option only for developpers. + # "--disable-demuxer" is the special option only for developers. --disable-demuxer) DEMUXER="" ;; @@ -191,6 +192,10 @@ done +MAJVER=$(grep -e '#define LSMASH_VERSION_MAJOR' $SRCDIR/lsmash.h | sed -e 's/#define LSMASH_VERSION_MAJOR //;s/ //g') +MINVER=$(grep -e '#define LSMASH_VERSION_MINOR' $SRCDIR/lsmash.h | sed -e 's/#define LSMASH_VERSION_MINOR //;s/ //g') +MICVER=$(grep -e '#define LSMASH_VERSION_MICRO' $SRCDIR/lsmash.h | sed -e 's/#define LSMASH_VERSION_MICRO //;s/ //g') + if test -n "$TARGET_OS"; then TARGET_OS=$(echo $TARGET_OS | tr '[A-Z]' '[a-z]') else @@ -202,6 +207,7 @@ SHARED_EXT=".dll" IMPLIB="liblsmash.dll.a" SO_LDFLAGS="-shared -Wl,--out-implib,$IMPLIB" + CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO=1" ;; *cygwin*) EXT=".exe" @@ -219,6 +225,8 @@ SHAREDLIB="" ;; *) + SHARED_NAME="liblsmash" + SHARED_EXT=".so.$MAJVER" if test -n "$SHAREDLIB"; then CFLAGS="$CFLAGS -fPIC" LDFLAGS="$LDFLAGS -fPIC" @@ -270,27 +278,48 @@ #============================================================================= # Notation for developpers. # Be sure to modified this block when you add/delete source files. -SRCS="isom.c fragment.c utils.c mp4sys.c mp4a.c dts.c a52.c h264.c hevc.c vc1.c alac.c importer.c \ - summary.c print.c read.c timeline.c chapter.c meta.c write.c description.c box.c osdep.c" +SRCS="common/alloc.c common/utils.c common/bstream.c common/list.c \ + common/osdep.c codecs/mp4sys.c codecs/mp4a.c codecs/dts.c codecs/a52.c \ + codecs/h264.c codecs/hevc.c codecs/vc1.c codecs/alac.c \ + codecs/description.c core/isom.c core/fragment.c core/summary.c \ + core/print.c core/read.c core/timeline.c core/chapter.c core/meta.c \ + core/write.c core/box.c core/file.c" +SRC_TOOLS="cli/cli.c cli/adts_imp.c cli/mp3_imp.c cli/amr_imp.c cli/a52_imp.c \ + cli/als_imp.c cli/dts_imp.c cli/vc1_imp.c cli/nalu_imp.c \ + cli/importer.c" +OBJ_TOOLS="" + TOOLS_ALL="muxer remuxer boxdumper timelineeditor" TOOLS_NAME="" -SRC_TOOLS="cli.c" + if test -n "$DEMUXER"; then CFLAGS="$CFLAGS -DLSMASH_DEMUXER_ENABLED" TOOLS="$TOOLS_ALL" else TOOLS="muxer" fi + +for tool in $SRC_TOOLS; do + OBJ_TOOLS="$OBJ_TOOLS ${tool%.c}.o" +done + for tool in $TOOLS; do - SRC_TOOLS="$SRC_TOOLS ${tool}.c" - TOOLS_NAME="$TOOLS_NAME ${tool}${EXT}" + SRC_TOOLS="$SRC_TOOLS cli/${tool}.c" + TOOLS_NAME="$TOOLS_NAME cli/${tool}${EXT}" done #============================================================================= -pushd $SRCDIR +CURDIR="$PWD" +cd $SRCDIR +VER=$MAJVER.$MINVER.$MICVER REV="$(git rev-list HEAD 2> /dev/null | wc -l)" +if test $REV -ne 0; then + VER_STRING="$VER rev.$REV" +else + VER_STRING="$VER" +fi HASH="$(git describe --always 2> /dev/null)" -popd +cd "$CURDIR" cat >> config.h << EOF #define LSMASH_REV "$REV" #define LSMASH_GIT_HASH "$HASH" @@ -305,9 +334,9 @@ Name: liblsmash Description: Loyal to Spec of MPEG4, and Ad-hock Simple Hackwork -Version: rev.$REV +Version: $VER_STRING Requires: -URL: http://code.google.com/p/l-smash/ +URL: https://github.com/l-smash/l-smash Libs: -L${libdir} -llsmash $LIBS Libs.private: Cflags: -I${includedir} @@ -345,13 +374,14 @@ SRC_TOOLS = $SRC_TOOLS TOOLS_ALL = $TOOLS_ALL TOOLS = $TOOLS_NAME +MAJVER = $MAJVER EOF for tool in $TOOLS; do cat >> config.mak2 << EOF -${tool}${EXT}: ${tool}.o cli.o $STATICLIB $SHAREDLIB - \$(CC) \$(CFLAGS) \$(LDFLAGS) -o \$@ \$< cli.o -llsmash \$(LIBS) +cli/${tool}${EXT}: cli/${tool}.o $OBJ_TOOLS $STATICLIB $SHAREDLIB + \$(CC) \$(CFLAGS) \$(LDFLAGS) -o \$@ \$< $OBJ_TOOLS -llsmash \$(LIBS) -@ \$(if \$(STRIP), \$(STRIP) \$@) EOF @@ -359,6 +389,7 @@ test "$SRCDIR" = "." || ln -sf ${SRCDIR}/Makefile . +mkdir -p cli codecs common core cat << EOF diff -Nru l-smash-1.9.1/core/box.c l-smash-2.3.0/core/box.c --- l-smash-1.9.1/core/box.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/box.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,2060 @@ +/***************************************************************************** + * box.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include + +#include "box.h" +#include "write.h" +#include "read.h" +#ifdef LSMASH_DEMUXER_ENABLED +#include "print.h" +#include "timeline.h" +#endif + +#include "codecs/mp4a.h" +#include "codecs/mp4sys.h" + +static const lsmash_class_t lsmash_box_class = +{ + "box" +}; + +void isom_init_box_common_orig +( + void *_box, + void *_parent, + lsmash_box_type_t box_type, + uint64_t precedence, + isom_extension_destructor_t destructor +) +{ + isom_box_t *box = (isom_box_t *)_box; + isom_box_t *parent = (isom_box_t *)_parent; + assert( box && parent && parent->root ); + box->class = &lsmash_box_class; + box->root = parent->root; + box->file = parent->file; + box->parent = parent; + box->precedence = precedence; + box->destruct = destructor; + box->size = 0; + box->type = box_type; + if( !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) && isom_is_fullbox( box ) ) + { + box->version = 0; + box->flags = 0; + } + isom_set_box_writer( box ); +} + +static void isom_reorder_tail_box( isom_box_t *parent ) +{ + /* Reorder the appended box by 'precedence'. */ + lsmash_entry_t *x = parent->extensions.tail; + assert( x && x->data ); + uint64_t precedence = ((isom_box_t *)x->data)->precedence; + for( lsmash_entry_t *y = x->prev; y; y = y->prev ) + { + isom_box_t *box = (isom_box_t *)y->data; + if( !box || precedence > box->precedence ) + { + /* Exchange the entity data of adjacent two entries. */ + y->data = x->data; + x->data = box; + x = y; + } + else + break; + } +} + +int isom_add_box_to_extension_list( void *parent_box, void *child_box ) +{ + assert( parent_box && child_box ); + isom_box_t *parent = (isom_box_t *)parent_box; + isom_box_t *child = (isom_box_t *)child_box; + /* Append at the end of the list. */ + if( lsmash_add_entry( &parent->extensions, child ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + /* Don't reorder the appended box when the file is opened for reading. */ + if( !parent->file || (parent->file->flags & LSMASH_FILE_MODE_READ) || parent->file->fake_file_mode ) + return 0; + isom_reorder_tail_box( parent ); + return 0; +} + +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.fourcc ); + 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.fourcc ); + } + if( box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ) + { + lsmash_bs_put_be32( bs, box->type.user.fourcc ); + lsmash_bs_put_bytes( bs, 12, box->type.user.id ); + } +} + +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 ); +} + +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 && lsmash_check_box_type_identical( 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 ); +} + +/* Return 1 if the box is fullbox, Otherwise return 0. */ +int isom_is_fullbox( void *box ) +{ + isom_box_t *current = (isom_box_t *)box; + lsmash_box_type_t type = current->type; + static lsmash_box_type_t fullbox_type_table[50] = { LSMASH_BOX_TYPE_INITIALIZER }; + if( !lsmash_check_box_type_specified( &fullbox_type_table[0] ) ) + { + /* Initialize the table. */ + int i = 0; + fullbox_type_table[i++] = ISOM_BOX_TYPE_SIDX; + fullbox_type_table[i++] = ISOM_BOX_TYPE_MVHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_TKHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_IODS; + fullbox_type_table[i++] = ISOM_BOX_TYPE_ESDS; + fullbox_type_table[i++] = QT_BOX_TYPE_ESDS; + fullbox_type_table[i++] = QT_BOX_TYPE_CLEF; + fullbox_type_table[i++] = QT_BOX_TYPE_PROF; + fullbox_type_table[i++] = QT_BOX_TYPE_ENOF; + fullbox_type_table[i++] = ISOM_BOX_TYPE_ELST; + fullbox_type_table[i++] = ISOM_BOX_TYPE_MDHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_HDLR; + fullbox_type_table[i++] = ISOM_BOX_TYPE_VMHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_SMHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_HMHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_NMHD; + fullbox_type_table[i++] = QT_BOX_TYPE_GMIN; + fullbox_type_table[i++] = ISOM_BOX_TYPE_DREF; + fullbox_type_table[i++] = ISOM_BOX_TYPE_STSD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_STSL; + fullbox_type_table[i++] = QT_BOX_TYPE_CHAN; + fullbox_type_table[i++] = ISOM_BOX_TYPE_SRAT; + fullbox_type_table[i++] = ISOM_BOX_TYPE_STTS; + fullbox_type_table[i++] = ISOM_BOX_TYPE_CTTS; + fullbox_type_table[i++] = ISOM_BOX_TYPE_CSLG; + fullbox_type_table[i++] = ISOM_BOX_TYPE_STSS; + fullbox_type_table[i++] = QT_BOX_TYPE_STPS; + 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_STCO; + fullbox_type_table[i++] = ISOM_BOX_TYPE_CO64; + fullbox_type_table[i++] = ISOM_BOX_TYPE_SGPD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_SBGP; + fullbox_type_table[i++] = ISOM_BOX_TYPE_CHPL; + fullbox_type_table[i++] = ISOM_BOX_TYPE_META; + fullbox_type_table[i++] = QT_BOX_TYPE_KEYS; + fullbox_type_table[i++] = ISOM_BOX_TYPE_MEAN; + fullbox_type_table[i++] = ISOM_BOX_TYPE_NAME; + fullbox_type_table[i++] = ISOM_BOX_TYPE_MEHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_TREX; + fullbox_type_table[i++] = ISOM_BOX_TYPE_MFHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_TFHD; + fullbox_type_table[i++] = ISOM_BOX_TYPE_TFDT; + fullbox_type_table[i++] = ISOM_BOX_TYPE_TRUN; + fullbox_type_table[i++] = ISOM_BOX_TYPE_TFRA; + fullbox_type_table[i++] = ISOM_BOX_TYPE_MFRO; + fullbox_type_table[i] = LSMASH_BOX_TYPE_UNSPECIFIED; + } + for( int i = 0; lsmash_check_box_type_specified( &fullbox_type_table[i] ); i++ ) + if( lsmash_check_box_type_identical( type, fullbox_type_table[i] ) ) + return 1; + if( current->parent ) + { + if( lsmash_check_box_type_identical( current->parent->type, ISOM_BOX_TYPE_DREF ) + || (lsmash_check_box_type_identical( type, ISOM_BOX_TYPE_CPRT ) + && lsmash_check_box_type_identical( current->parent->type, ISOM_BOX_TYPE_UDTA )) ) + return 1; + } + return 0; +} + +/* Return 1 if the sample type is LPCM audio, Otherwise return 0. */ +int isom_is_lpcm_audio( void *box ) +{ + isom_box_t *current = (isom_box_t *)box; + lsmash_box_type_t type = current->type; + return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_23NI_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NONE_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_LPCM_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SOWT_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TWOS_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL32_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL64_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN24_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN32_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NOT_SPECIFIED ) + || (lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RAW_AUDIO ) && (current->manager & LSMASH_AUDIO_DESCRIPTION)); +} + +int isom_is_qt_audio( lsmash_codec_type_t type ) +{ + return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_23NI_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MAC3_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MAC6_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NONE_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_QDM2_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_QDMC_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_QCLP_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_AC_3_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_AGSM_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ALAC_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ALAW_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CDX2_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CDX4_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVCA_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVI_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL32_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FL64_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IMA4_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN24_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_IN32_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_LPCM_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MP4A_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RAW_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SOWT_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TWOS_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULAW_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_VDVA_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FULLMP3_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MP3_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM2_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM17_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_GSM49_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_NOT_SPECIFIED ); +} + +/* Return 1 if the sample type is uncompressed Y'CbCr video, Otherwise return 0. */ +int isom_is_uncompressed_ycbcr( lsmash_codec_type_t type ) +{ + return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_2VUY_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V210_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V216_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V308_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V408_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V410_VIDEO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_YUV2_VIDEO ); +} + +int isom_is_waveform_audio( lsmash_box_type_t type ) +{ + return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM2_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ADPCM17_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_GSM49_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FULLMP3_AUDIO ) + || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MP3_AUDIO ); +} + +size_t isom_skip_box_common( uint8_t **p_data ) +{ + uint8_t *orig = *p_data; + uint8_t *data = *p_data; + uint64_t size = LSMASH_GET_BE32( data ); + data += ISOM_BASEBOX_COMMON_SIZE; + if( size == 1 ) + { + size = LSMASH_GET_BE64( data ); + data += 8; + } + *p_data = data; + return data - orig; +} + +static void isom_destruct_extension_binary( void *ext ) +{ + if( !ext ) + return; + isom_box_t *box = (isom_box_t *)ext; + lsmash_free( box->binary ); +} + +int isom_add_extension_binary +( + void *parent_box, + lsmash_box_type_t box_type, + uint64_t precedence, + uint8_t *box_data, + uint32_t box_size +) +{ + if( !parent_box || !box_data || box_size < ISOM_BASEBOX_COMMON_SIZE + || !lsmash_check_box_type_specified( &box_type ) ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_box_t *ext = lsmash_malloc_zero( sizeof(isom_box_t) ); + if( !ext ) + return LSMASH_ERR_MEMORY_ALLOC; + isom_box_t *parent = (isom_box_t *)parent_box; + ext->class = &lsmash_box_class; + ext->root = parent->root; + ext->file = parent->file; + ext->parent = parent; + ext->manager = LSMASH_BINARY_CODED_BOX; + ext->precedence = precedence; + ext->size = box_size; + ext->type = box_type; + ext->binary = box_data; + ext->destruct = isom_destruct_extension_binary; + if( isom_add_box_to_extension_list( parent, ext ) < 0 ) + { + lsmash_free( ext ); + return LSMASH_ERR_MEMORY_ALLOC; + } + isom_set_box_writer( ext ); + return 0; +} + +static void isom_remove_extension_box( isom_box_t *ext ) +{ + if( !ext ) + return; + if( ext->destruct ) + ext->destruct( ext ); + isom_remove_all_extension_boxes( &ext->extensions ); + lsmash_free( ext ); +} + +void isom_remove_all_extension_boxes( lsmash_entry_list_t *extensions ) +{ + lsmash_remove_entries( extensions, isom_remove_extension_box ); +} + +isom_box_t *isom_get_extension_box( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ) +{ + for( lsmash_entry_t *entry = extensions->head; entry; entry = entry->next ) + { + isom_box_t *ext = (isom_box_t *)entry->data; + if( !ext ) + continue; + if( lsmash_check_box_type_identical( ext->type, box_type ) ) + return ext; + } + return NULL; +} + +void *isom_get_extension_box_format( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ) +{ + for( lsmash_entry_t *entry = extensions->head; entry; entry = entry->next ) + { + isom_box_t *ext = (isom_box_t *)entry->data; + if( !ext || (ext->manager & LSMASH_BINARY_CODED_BOX) || !lsmash_check_box_type_identical( ext->type, box_type ) ) + continue; + return ext; + } + return NULL; +} + +lsmash_entry_t *isom_get_entry_of_box +( + lsmash_box_t *parent, + const lsmash_box_path_t box_path[] +) +{ + if( !parent ) + return NULL; + lsmash_entry_t *entry = NULL; + const lsmash_box_path_t *path = &box_path[0]; + while( lsmash_check_box_type_specified( &path->type ) ) + { + entry = parent->extensions.head; + if( !entry ) + return NULL; + parent = NULL; + uint32_t i = 1; + uint32_t number = path->number ? path->number : 1; + while( entry ) + { + isom_box_t *box = entry->data; + if( box && lsmash_check_box_type_identical( path->type, box->type ) ) + { + if( i == number ) + { + /* Found a box. Move to a child box. */ + parent = box; + ++path; + break; + } + ++i; + } + entry = entry->next; + } + if( !parent ) + return NULL; + } + return entry; +} + +/* box destructors */ +#define REMOVE_BOX( box_name, parent_type ) \ + isom_remove_predefined_box( box_name, offsetof( parent_type, box_name ) ) + +#define REMOVE_BOX_IN_LIST( box_name, parent_type ) \ + isom_remove_box_in_predefined_list( box_name, offsetof( parent_type, box_name##_list ) ) + +#define REMOVE_LIST_BOX_TEMPLATE( REMOVER, box_name, parent_type, eliminator ) \ + do \ + { \ + lsmash_remove_list( box_name->list, eliminator ); \ + REMOVER( box_name, parent_type ); \ + } while( 0 ) + +#define REMOVE_LIST_BOX( ... ) CALL_FUNC_DEFAULT_ARGS( REMOVE_LIST_BOX, __VA_ARGS__ ) +#define REMOVE_LIST_BOX_3( box_name, parent_type, eliminator ) \ + REMOVE_LIST_BOX_TEMPLATE( REMOVE_BOX, box_name, parent_type, eliminator ) +#define REMOVE_LIST_BOX_2( box_name, parent_type ) \ + REMOVE_LIST_BOX_3( box_name, parent_type, NULL ) + +#define REMOVE_LIST_BOX_IN_LIST( box_name, parent_type ) \ + REMOVE_LIST_BOX_TEMPLATE( REMOVE_BOX_IN_LIST, box_name, parent_type, NULL ) + +#define DEFINE_SIMPLE_BOX_REMOVER_TEMPLATE( REMOVER, box_name, ... ) \ + static void isom_remove_##box_name( isom_##box_name##_t *box_name ) \ + { \ + if( !box_name ) \ + return; \ + REMOVER( box_name, __VA_ARGS__ ); \ + } + +#define DEFINE_SIMPLE_BOX_REMOVER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_REMOVER_TEMPLATE( REMOVE_BOX, __VA_ARGS__ ) + +#define DEFINE_SIMPLE_LIST_BOX_REMOVER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_REMOVER_TEMPLATE( REMOVE_LIST_BOX, __VA_ARGS__ ) + +#define DEFINE_SIMPLE_BOX_IN_LIST_REMOVER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_REMOVER_TEMPLATE( REMOVE_BOX_IN_LIST, __VA_ARGS__ ) + +#define DEFINE_SIMPLE_LIST_BOX_IN_LIST_REMOVER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_REMOVER_TEMPLATE( REMOVE_LIST_BOX_IN_LIST, __VA_ARGS__ ) + +static void isom_remove_predefined_box( void *opaque_box, size_t offset_of_box ) +{ + assert( opaque_box ); + isom_box_t *box = (isom_box_t *)opaque_box; + if( box->parent ) + { + isom_box_t **p = (isom_box_t **)(((int8_t *)box->parent) + offset_of_box); + if( *p == box ) + *p = NULL; + } +} + +/* We always free boxes through the extension list of the parent box. + * Therefore, don't free boxes through any list other than the extension list. */ +static void isom_remove_box_in_predefined_list( void *opaque_box, size_t offset_of_list ) +{ + assert( opaque_box ); + isom_box_t *box = (isom_box_t *)opaque_box; + if( box->parent ) + { + lsmash_entry_list_t *list = (lsmash_entry_list_t *)(((int8_t *)box->parent) + offset_of_list); + if( list ) + for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) + if( box == entry->data ) + { + /* We don't free this box here. + * Because of freeing an entry of the list here, don't pass the list to free this box. + * Or double free. */ + entry->data = NULL; + lsmash_remove_entry_direct( list, entry, NULL ); + break; + } + } +} + +/* Remove a box by the pointer containing its address. + * In addition, remove from the extension list of the parent box if possible. + * Don't call this function within a function freeing one or more entries of any extension list because of double free. + * Basically, don't use this function as a callback function. */ +void isom_remove_box_by_itself( void *opaque_box ) +{ + if( !opaque_box ) + return; + isom_box_t *box = (isom_box_t *)opaque_box; + if( box->parent ) + { + isom_box_t *parent = box->parent; + for( lsmash_entry_t *entry = parent->extensions.head; entry; entry = entry->next ) + if( box == entry->data ) + { + /* Free the corresponding entry here, therefore don't call this function as a callback function + * if a function frees the same entry later and calls this function. */ + lsmash_remove_entry_direct( &parent->extensions, entry, isom_remove_extension_box ); + return; + } + } + isom_remove_extension_box( box ); +} + +void isom_remove_unknown_box( isom_unknown_box_t *unknown_box ) +{ + if( !unknown_box ) + return; + lsmash_free( unknown_box->unknown_field ); +} + +static void isom_remove_file( lsmash_file_t *file ) +{ + if( !file ) + return; +#ifdef LSMASH_DEMUXER_ENABLED + isom_remove_print_funcs( file ); + isom_remove_timelines( file ); +#endif + lsmash_free( file->compatible_brands ); + lsmash_bs_cleanup( file->bs ); + if( file->fragment ) + { + lsmash_remove_list( file->fragment->pool, isom_remove_sample_pool ); + lsmash_free( file->fragment ); + } + REMOVE_BOX_IN_LIST( file, lsmash_root_t ); +} + +static void isom_remove_ftyp( isom_ftyp_t *ftyp ) +{ + if( !ftyp ) + return; + lsmash_free( ftyp->compatible_brands ); + REMOVE_BOX( ftyp, lsmash_file_t ); +} + +static void isom_remove_iods( isom_iods_t *iods ) +{ + if( !iods ) + return; + mp4sys_remove_descriptor( iods->OD ); + REMOVE_BOX( iods, isom_moov_t ); +} + +static void isom_remove_trak( isom_trak_t *trak ) +{ + if( !trak ) + return; + if( trak->cache ) + { + isom_remove_sample_pool( trak->cache->chunk.pool ); + lsmash_remove_list( trak->cache->roll.pool, NULL ); + lsmash_free( trak->cache->rap ); + lsmash_free( trak->cache->fragment ); + lsmash_free( trak->cache ); + } + REMOVE_BOX_IN_LIST( trak, isom_moov_t ); +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_tkhd, tkhd, isom_trak_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_clef, clef, isom_tapt_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_prof, prof, isom_tapt_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_enof, enof, isom_tapt_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_tapt, tapt, isom_trak_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_edts, edts, isom_trak_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_tref, tref, isom_trak_t ) +DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_elst, elst, isom_edts_t ) + +static void isom_remove_track_reference_type( isom_tref_type_t *ref ) +{ + if( !ref ) + return; + lsmash_free( ref->track_ID ); + isom_remove_box_in_predefined_list( ref, offsetof( isom_tref_t, ref_list ) ); +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mdhd, mdhd, isom_mdia_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_vmhd, vmhd, isom_minf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_smhd, smhd, isom_minf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_hmhd, hmhd, isom_minf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_nmhd, nmhd, isom_minf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_gmhd, gmhd, isom_minf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_gmin, gmin, isom_gmhd_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_text, text, isom_gmhd_t ) + +static void isom_remove_hdlr( isom_hdlr_t *hdlr ) +{ + if( !hdlr ) + return; + lsmash_free( hdlr->componentName ); + if( hdlr->parent ) + { + if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_MDIA ) ) + REMOVE_BOX( hdlr, isom_mdia_t ); + else if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_META ) + || lsmash_check_box_type_identical( hdlr->parent->type, QT_BOX_TYPE_META ) ) + REMOVE_BOX( hdlr, isom_meta_t ); + else if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_MINF ) ) + REMOVE_BOX( hdlr, isom_minf_t ); + else + assert( 0 ); + return; + } +} + +static void isom_remove_glbl( isom_glbl_t *glbl ) +{ + if( !glbl ) + return; + lsmash_free( glbl->header_data ); +} + +static void isom_remove_esds( isom_esds_t *esds ) +{ + if( !esds ) + return; + mp4sys_remove_descriptor( esds->ES ); +} + +static void isom_remove_font_record( isom_font_record_t *font_record ) +{ + if( !font_record ) + return; + lsmash_free( font_record->font_name ); + lsmash_free( font_record ); +} +DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_ftab, ftab, isom_tx3g_entry_t, isom_remove_font_record ) + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_frma, frma, isom_wave_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_enda, enda, isom_wave_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mp4a, mp4a, isom_wave_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_terminator, terminator, isom_wave_t ) + +static void isom_remove_chan( isom_chan_t *chan ) +{ + if( !chan ) + return; + lsmash_free( chan->channelDescriptions ); +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_stsd, stsd, isom_stbl_t ) + +static void isom_remove_visual_description( isom_sample_entry_t *description ) +{ + isom_visual_entry_t *visual = (isom_visual_entry_t *)description; + lsmash_free( visual->color_table.array ); + isom_remove_box_in_predefined_list( visual, offsetof( isom_stsd_t, list ) ); +} + +static void isom_remove_audio_description( isom_sample_entry_t *description ) +{ + isom_remove_box_in_predefined_list( description, offsetof( isom_stsd_t, list ) ); +} + +static void isom_remove_hint_description( isom_sample_entry_t *description ) +{ + isom_hint_entry_t *hint = (isom_hint_entry_t *)description; + lsmash_free( hint->data ); + isom_remove_box_in_predefined_list( hint, offsetof( isom_stsd_t, list ) ); +} + +static void isom_remove_metadata_description( isom_sample_entry_t *description ) +{ + isom_remove_box_in_predefined_list( description, offsetof( isom_stsd_t, list ) ); +} + +static void isom_remove_tx3g_description( isom_sample_entry_t *description ) +{ + isom_remove_box_in_predefined_list( description, offsetof( isom_stsd_t, list ) ); +} + +static void isom_remove_qt_text_description( isom_sample_entry_t *description ) +{ + isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)description; + lsmash_free( text->font_name ); + isom_remove_box_in_predefined_list( text, offsetof( isom_stsd_t, list ) ); +} + +static void isom_remove_mp4s_description( isom_sample_entry_t *description ) +{ + isom_remove_box_in_predefined_list( description, offsetof( isom_stsd_t, list ) ); +} + +void isom_remove_sample_description( isom_sample_entry_t *sample ) +{ + if( !sample ) + return; + lsmash_codec_type_t sample_type = sample->type; + if( lsmash_check_box_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) ) + { + if( sample->manager & LSMASH_VIDEO_DESCRIPTION ) + { + isom_remove_visual_description( sample ); + return; + } + else if( sample->manager & LSMASH_AUDIO_DESCRIPTION ) + { + isom_remove_audio_description( sample ); + return; + } + } + static struct description_remover_table_tag + { + lsmash_codec_type_t type; + void (*func)( isom_sample_entry_t * ); + } description_remover_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } }; + if( !description_remover_table[0].func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( type, func ) \ + description_remover_table[i++] = (struct description_remover_table_tag){ type, func } + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_remove_visual_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_remove_audio_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_FDP_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M2TS_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_PM2T_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_PRTP_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RM2T_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RRTP_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RSRP_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RTP_HINT , isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SM2T_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SRTP_HINT, isom_remove_hint_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_IXSE_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_METT_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_METX_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLIX_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_OKSD_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVCM_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TEXT_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_URIM_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_XML_META, isom_remove_metadata_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_remove_tx3g_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_remove_qt_text_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_remove_mp4s_description ); + ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL ); + } + for( int i = 0; description_remover_table[i].func; i++ ) + if( lsmash_check_codec_type_identical( sample_type, description_remover_table[i].type ) ) + { + description_remover_table[i].func( sample ); + return; + } +} + +DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stts, stts, isom_stbl_t ) +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_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 ) + +static void isom_remove_sdtp( isom_sdtp_t *sdtp ) +{ + if( !sdtp ) + return; + lsmash_remove_list( sdtp->list, NULL ); + if( sdtp->parent ) + { + if( lsmash_check_box_type_identical( sdtp->parent->type, ISOM_BOX_TYPE_STBL ) ) + REMOVE_BOX( sdtp, isom_stbl_t ); + else if( lsmash_check_box_type_identical( sdtp->parent->type, ISOM_BOX_TYPE_TRAF ) ) + REMOVE_BOX( sdtp, isom_traf_t ); + else + assert( 0 ); + return; + } +} + +static void isom_remove_sgpd( isom_sgpd_t *sgpd ) +{ + if( !sgpd ) + return; + lsmash_remove_list( sgpd->list, NULL ); + if( sgpd->parent ) + { + if( lsmash_check_box_type_identical( sgpd->parent->type, ISOM_BOX_TYPE_STBL ) ) + REMOVE_BOX_IN_LIST( sgpd, isom_stbl_t ); + else if( lsmash_check_box_type_identical( sgpd->parent->type, ISOM_BOX_TYPE_TRAF ) ) + REMOVE_BOX_IN_LIST( sgpd, isom_traf_t ); + else + assert( 0 ); + return; + } +} + +static void isom_remove_sbgp( isom_sbgp_t *sbgp ) +{ + if( !sbgp ) + return; + lsmash_remove_list( sbgp->list, NULL ); + if( sbgp->parent ) + { + if( lsmash_check_box_type_identical( sbgp->parent->type, ISOM_BOX_TYPE_STBL ) ) + REMOVE_BOX_IN_LIST( sbgp, isom_stbl_t ); + else if( lsmash_check_box_type_identical( sbgp->parent->type, ISOM_BOX_TYPE_TRAF ) ) + REMOVE_BOX_IN_LIST( sbgp, isom_traf_t ); + else + assert( 0 ); + return; + } +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_stbl, stbl, isom_minf_t ) + +static void isom_remove_dref_entry( isom_dref_entry_t *data_entry ) +{ + if( !data_entry ) + return; + lsmash_free( data_entry->name ); + lsmash_free( data_entry->location ); + isom_remove_box_in_predefined_list( data_entry, offsetof( isom_dref_t, list ) ); +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_dref, dref, isom_dinf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_dinf, dinf, isom_minf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_minf, minf, isom_mdia_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mdia, mdia, isom_trak_t ) + +static void isom_remove_chpl_entry( isom_chpl_entry_t *data ) +{ + if( !data ) + return; + lsmash_free( data->chapter_name ); + lsmash_free( data ); +} +DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_chpl, chpl, isom_udta_t, isom_remove_chpl_entry ) + +static void isom_remove_keys_entry( isom_keys_entry_t *data ) +{ + if( !data ) + return; + lsmash_free( data->key_value ); + lsmash_free( data ); +} +DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_keys, keys, isom_meta_t, isom_remove_keys_entry ) + +static void isom_remove_mean( isom_mean_t *mean ) +{ + if( !mean ) + return; + lsmash_free( mean->meaning_string ); + REMOVE_BOX( mean, isom_metaitem_t ); +} + +static void isom_remove_name( isom_name_t *name ) +{ + if( !name ) + return; + lsmash_free( name->name ); + REMOVE_BOX( name, isom_metaitem_t ); +} + +static void isom_remove_data( isom_data_t *data ) +{ + if( !data ) + return; + lsmash_free( data->value ); + REMOVE_BOX( data, isom_metaitem_t ); +} + +DEFINE_SIMPLE_BOX_IN_LIST_REMOVER( isom_remove_metaitem, metaitem, isom_ilst_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_ilst, ilst, isom_meta_t ) + +static void isom_remove_meta( isom_meta_t *meta ) +{ + if( !meta ) + return; + if( meta->parent ) + { + if( lsmash_check_box_type_identical( meta->parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) + REMOVE_BOX( meta, lsmash_file_t ); + else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_MOOV ) ) + REMOVE_BOX( meta, isom_moov_t ); + else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_TRAK ) ) + REMOVE_BOX( meta, isom_trak_t ); + else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_UDTA ) ) + REMOVE_BOX( meta, isom_udta_t ); + else + assert( 0 ); + return; + } +} + +static void isom_remove_cprt( isom_cprt_t *cprt ) +{ + if( !cprt ) + return; + lsmash_free( cprt->notice ); + REMOVE_BOX_IN_LIST( cprt, isom_udta_t ); +} + +static void isom_remove_udta( isom_udta_t *udta ) +{ + if( !udta ) + return; + if( udta->parent ) + { + if( lsmash_check_box_type_identical( udta->parent->type, ISOM_BOX_TYPE_MOOV ) ) + REMOVE_BOX( udta, isom_moov_t ); + else if( lsmash_check_box_type_identical( udta->parent->type, ISOM_BOX_TYPE_TRAK ) ) + REMOVE_BOX( udta, isom_trak_t ); + else + assert( 0 ); + return; + } +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_WLOC, WLOC, isom_udta_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_LOOP, LOOP, isom_udta_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_SelO, SelO, isom_udta_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_AllF, AllF, isom_udta_t ) + +static void isom_remove_ctab( isom_ctab_t *ctab ) +{ + if( !ctab ) + return; + lsmash_free( ctab->color_table.array ); + if( ctab->parent && lsmash_check_box_type_identical( ctab->parent->type, ISOM_BOX_TYPE_MOOV ) ) + REMOVE_BOX( ctab, isom_moov_t ); +} + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mvex, mvex, isom_moov_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mvhd, mvhd, isom_moov_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mehd, mehd, isom_mvex_t ) +DEFINE_SIMPLE_BOX_IN_LIST_REMOVER( isom_remove_trex, trex, isom_mvex_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_moov, moov, lsmash_file_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mdat, mdat, lsmash_file_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mfhd, mfhd, isom_moof_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_tfhd, tfhd, isom_traf_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_tfdt, tfdt, isom_traf_t ) + +static void isom_remove_trun( isom_trun_t *trun ) +{ + if( !trun ) + return; + lsmash_remove_list( trun->optional, NULL ); + REMOVE_BOX_IN_LIST( trun, isom_traf_t ); +} + +DEFINE_SIMPLE_BOX_IN_LIST_REMOVER( isom_remove_traf, traf, isom_moof_t ) +DEFINE_SIMPLE_BOX_IN_LIST_REMOVER( isom_remove_moof, moof, lsmash_file_t ) + +static void isom_remove_free( isom_free_t *skip ) +{ + if( !skip ) + return; + lsmash_free( skip->data ); + isom_remove_predefined_box( skip, offsetof( lsmash_file_t, free ) ); +} +#define isom_remove_skip isom_remove_free + +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mfra, mfra, lsmash_file_t ) +DEFINE_SIMPLE_BOX_REMOVER( isom_remove_mfro, mfro, isom_mfra_t ) +DEFINE_SIMPLE_LIST_BOX_IN_LIST_REMOVER( isom_remove_tfra, tfra, isom_mfra_t ) +DEFINE_SIMPLE_LIST_BOX_IN_LIST_REMOVER( isom_remove_sidx, sidx, lsmash_file_t ) + +static void isom_remove_styp( isom_styp_t *styp ) +{ + if( !styp ) + return; + lsmash_free( styp->compatible_brands ); + REMOVE_BOX_IN_LIST( styp, lsmash_file_t ); +} + +/* box size updater */ +uint64_t isom_update_box_size( void *opaque_box ) +{ + assert( opaque_box ); + isom_box_t *box = (isom_box_t *)opaque_box; + if( box->manager & LSMASH_WRITTEN_BOX ) + /* No need to calculate the size of this box since the size is already decided and fixed. */ + return box->size; + uint64_t size = 0; + if( box->write ) + { + /* Calculate the size of this box excluding its children with a fake bytestream writer. */ + { + lsmash_bs_t fake_bs = { NULL }; + if( box->write( &fake_bs, box ) == 0 ) + size = lsmash_bs_get_valid_data_size( &fake_bs ); + } + /* Calculate the size of the children if no error. */ + if( size >= ISOM_BASEBOX_COMMON_SIZE ) + { + for( lsmash_entry_t *entry = box->extensions.head; entry; entry = entry->next ) + if( entry->data ) + size += isom_update_box_size( entry->data ); + /* Check large size. */ + if( size > UINT32_MAX ) + size += 8; + } + else + /* TODO: add error handling. */ + size = 0; + } + box->size = size; + return size; +} + +/* box adding functions */ +#define ATTACH_EXACTLY_ONE_BOX_TO_PARENT( box_name, parent_type ) \ + do \ + { \ + isom_box_t **p = (isom_box_t **)(((int8_t *)box_name->parent) \ + + offsetof( parent_type, box_name )); \ + if( *p == NULL ) \ + *p = (isom_box_t *)box_name; \ + } while( 0 ) + +#define INIT_BOX_COMMON0( box_name, parent, box_type, precedence ) \ + const isom_extension_destructor_t isom_remove_##box_name = NULL; \ + isom_init_box_common( box_name, parent, box_type, precedence, isom_remove_##box_name ) +#define INIT_BOX_COMMON1( box_name, parent, box_type, precedence ) \ + isom_init_box_common( box_name, parent, box_type, precedence, isom_remove_##box_name ) + +#define CREATE_BOX( box_name, parent, box_type, precedence, has_destructor ) \ + if( !(parent) ) \ + return NULL; \ + isom_##box_name##_t *box_name = lsmash_malloc_zero( sizeof(isom_##box_name##_t) ); \ + if( !box_name ) \ + return NULL; \ + INIT_BOX_COMMON ## has_destructor( box_name, parent, box_type, precedence ); \ + if( isom_add_box_to_extension_list( parent, box_name ) < 0 ) \ + { \ + lsmash_free( box_name ); \ + return NULL; \ + } +#define CREATE_LIST_BOX( box_name, parent, box_type, precedence, has_destructor ) \ + CREATE_BOX( box_name, parent, box_type, precedence, has_destructor ); \ + box_name->list = lsmash_create_entry_list(); \ + if( !box_name->list ) \ + { \ + lsmash_remove_entry_tail( &(parent)->extensions, isom_remove_##box_name ); \ + return NULL; \ + } + +#define ADD_BOX_TEMPLATE( box_name, parent, box_type, precedence, BOX_CREATOR ) \ + BOX_CREATOR( box_name, parent, box_type, precedence, 1 ); \ + if( !(parent)->box_name ) \ + (parent)->box_name = box_name +#define ADD_BOX_IN_LIST_TEMPLATE( box_name, parent, box_type, precedence, BOX_CREATOR ) \ + BOX_CREATOR( box_name, parent, box_type, precedence, 1 ); \ + if( lsmash_add_entry( &(parent)->box_name##_list, box_name ) < 0 ) \ + { \ + lsmash_remove_entry_tail( &(parent)->extensions, isom_remove_##box_name ); \ + return NULL; \ + } + +#define ADD_BOX( box_name, parent, box_type, precedence ) \ + ADD_BOX_TEMPLATE( box_name, parent, box_type, precedence, CREATE_BOX ) +#define ADD_BOX_IN_LIST( box_name, parent, box_type, precedence ) \ + ADD_BOX_IN_LIST_TEMPLATE( box_name, parent, box_type, precedence, CREATE_BOX ) +#define ADD_LIST_BOX( box_name, parent, box_type, precedence ) \ + ADD_BOX_TEMPLATE( box_name, parent, box_type, precedence, CREATE_LIST_BOX ) +#define ADD_LIST_BOX_IN_LIST( box_name, parent, box_type, precedence ) \ + ADD_BOX_IN_LIST_TEMPLATE( box_name, parent, box_type, precedence, CREATE_LIST_BOX ) + +#define DEFINE_SIMPLE_BOX_ADDER_TEMPLATE( ... ) CALL_FUNC_DEFAULT_ARGS( DEFINE_SIMPLE_BOX_ADDER_TEMPLATE, __VA_ARGS__ ) +#define DEFINE_SIMPLE_BOX_ADDER_TEMPLATE_6( ADDER, box_name, parent_name, box_type, precedence, parent_type ) \ + isom_##box_name##_t *isom_add_##box_name( parent_type *parent_name ) \ + { \ + ADDER( box_name, parent_name, box_type, precedence ); \ + return box_name; \ + } +#define DEFINE_SIMPLE_BOX_ADDER_TEMPLATE_5( ADDER, box_name, parent_name, box_type, precedence ) \ + DEFINE_SIMPLE_BOX_ADDER_TEMPLATE_6( ADDER, box_name, parent_name, box_type, precedence, isom_##parent_name##_t ) + +#define DEFINE_SIMPLE_BOX_ADDER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_ADDER_TEMPLATE( ADD_BOX, __VA_ARGS__ ) +#define DEFINE_SIMPLE_BOX_IN_LIST_ADDER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_ADDER_TEMPLATE( ADD_BOX_IN_LIST, __VA_ARGS__ ) +#define DEFINE_SIMPLE_LIST_BOX_ADDER( func_name, ... ) \ + DEFINE_SIMPLE_BOX_ADDER_TEMPLATE( ADD_LIST_BOX, __VA_ARGS__ ) + +#define DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( func_name, box_name, parent_name, box_type, precedence, has_destructor, parent_type ) \ + isom_##box_name##_t *isom_add_##box_name( parent_type *parent_name ) \ + { \ + CREATE_BOX( box_name, parent_name, box_type, precedence, has_destructor ); \ + return box_name; \ + } + +lsmash_file_t *isom_add_file( lsmash_root_t *root ) +{ + lsmash_file_t *file = lsmash_malloc_zero( sizeof(lsmash_file_t) ); + if( !file ) + return NULL; + file->class = &lsmash_box_class; + file->root = root; + file->file = file; + file->parent = (isom_box_t *)root; + file->destruct = (isom_extension_destructor_t)isom_remove_file; + file->size = 0; + file->type = LSMASH_BOX_TYPE_UNSPECIFIED; + if( isom_add_box_to_extension_list( root, file ) < 0 ) + { + lsmash_free( file ); + return NULL; + } + if( lsmash_add_entry( &root->file_list, file ) < 0 ) + { + lsmash_remove_entry_tail( &root->extensions, isom_remove_file ); + return NULL; + } + return file; +} + +isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type ) +{ + if( !tref ) + return NULL; + isom_tref_type_t *ref = lsmash_malloc_zero( sizeof(isom_tref_type_t) ); + if( !ref ) + return NULL; + /* Initialize common fields. */ + ref->root = tref->root; + ref->file = tref->file; + ref->parent = (isom_box_t *)tref; + ref->size = 0; + ref->type = lsmash_form_iso_box_type( type ); + ref->precedence = LSMASH_BOX_PRECEDENCE_ISOM_TREF_TYPE; + ref->destruct = (isom_extension_destructor_t)isom_remove_track_reference_type; + isom_set_box_writer( (isom_box_t *)ref ); + if( isom_add_box_to_extension_list( tref, ref ) < 0 ) + { + lsmash_free( ref ); + return NULL; + } + if( lsmash_add_entry( &tref->ref_list, ref ) < 0 ) + { + lsmash_remove_entry_tail( &tref->extensions, isom_remove_track_reference_type ); + return NULL; + } + return ref; +} + +DEFINE_SIMPLE_BOX_ADDER( isom_add_terminator, terminator, wave, QT_BOX_TYPE_TERMINATOR, LSMASH_BOX_PRECEDENCE_QTFF_TERMINATOR ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_frma, frma, wave, QT_BOX_TYPE_FRMA, LSMASH_BOX_PRECEDENCE_QTFF_FRMA ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_enda, enda, wave, QT_BOX_TYPE_ENDA, LSMASH_BOX_PRECEDENCE_QTFF_ENDA ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_mp4a, mp4a, wave, QT_BOX_TYPE_MP4A, LSMASH_BOX_PRECEDENCE_QTFF_MP4A ) +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_ftab, ftab, tx3g, ISOM_BOX_TYPE_FTAB, LSMASH_BOX_PRECEDENCE_ISOM_FTAB, isom_tx3g_entry_t ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_ftyp, ftyp, file, ISOM_BOX_TYPE_FTYP, LSMASH_BOX_PRECEDENCE_ISOM_FTYP, lsmash_file_t ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_moov, moov, file, ISOM_BOX_TYPE_MOOV, LSMASH_BOX_PRECEDENCE_ISOM_MOOV, lsmash_file_t ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_mvhd, mvhd, moov, ISOM_BOX_TYPE_MVHD, LSMASH_BOX_PRECEDENCE_ISOM_MVHD ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_iods, iods, moov, ISOM_BOX_TYPE_IODS, LSMASH_BOX_PRECEDENCE_ISOM_IODS ) + +isom_ctab_t *isom_add_ctab( void *parent_box ) +{ + /* According to QuickTime File Format Specification, this box is placed inside Movie Box if present. + * However, sometimes this box occurs inside an image description entry or the end of Sample Description Box. */ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + CREATE_BOX( ctab, parent, QT_BOX_TYPE_CTAB, LSMASH_BOX_PRECEDENCE_QTFF_CTAB, 1 ); + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( ctab, isom_moov_t ); + return ctab; +} + +isom_trak_t *isom_add_trak( isom_moov_t *moov ) +{ + if( !moov || !moov->file ) + return NULL; + CREATE_BOX( trak, moov, ISOM_BOX_TYPE_TRAK, LSMASH_BOX_PRECEDENCE_ISOM_TRAK, 1 ); + isom_fragment_t *fragment = NULL; + isom_cache_t *cache = lsmash_malloc_zero( sizeof(isom_cache_t) ); + if( !cache ) + goto fail; + if( moov->file->fragment ) + { + fragment = lsmash_malloc_zero( sizeof(isom_fragment_t) ); + if( !fragment ) + goto fail; + cache->fragment = fragment; + } + if( lsmash_add_entry( &moov->trak_list, trak ) < 0 ) + goto fail; + trak->cache = cache; + return trak; +fail: + lsmash_free( fragment ); + lsmash_free( cache ); + lsmash_remove_entry_tail( &moov->extensions, isom_remove_trak ); + return NULL; +} + +DEFINE_SIMPLE_BOX_ADDER ( isom_add_tkhd, tkhd, trak, ISOM_BOX_TYPE_TKHD, LSMASH_BOX_PRECEDENCE_ISOM_TKHD ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_tapt, tapt, trak, QT_BOX_TYPE_TAPT, LSMASH_BOX_PRECEDENCE_QTFF_TAPT ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_clef, clef, tapt, QT_BOX_TYPE_CLEF, LSMASH_BOX_PRECEDENCE_QTFF_CLEF ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_prof, prof, tapt, QT_BOX_TYPE_PROF, LSMASH_BOX_PRECEDENCE_QTFF_PROF ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_enof, enof, tapt, QT_BOX_TYPE_ENOF, LSMASH_BOX_PRECEDENCE_QTFF_ENOF ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_edts, edts, trak, ISOM_BOX_TYPE_EDTS, LSMASH_BOX_PRECEDENCE_ISOM_EDTS ) +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_elst, elst, edts, ISOM_BOX_TYPE_ELST, LSMASH_BOX_PRECEDENCE_ISOM_ELST ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_tref, tref, trak, ISOM_BOX_TYPE_TREF, LSMASH_BOX_PRECEDENCE_ISOM_TREF ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mdia, mdia, trak, ISOM_BOX_TYPE_MDIA, LSMASH_BOX_PRECEDENCE_ISOM_MDIA ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mdhd, mdhd, mdia, ISOM_BOX_TYPE_MDHD, LSMASH_BOX_PRECEDENCE_ISOM_MDHD ) + +isom_hdlr_t *isom_add_hdlr( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + CREATE_BOX( hdlr, parent, ISOM_BOX_TYPE_HDLR, LSMASH_BOX_PRECEDENCE_ISOM_HDLR, 1 ); + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( hdlr, isom_mdia_t ); + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) + || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( hdlr, isom_meta_t ); + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( hdlr, isom_minf_t ); + else + assert( 0 ); + return hdlr; +} + +DEFINE_SIMPLE_BOX_ADDER( isom_add_minf, minf, mdia, ISOM_BOX_TYPE_MINF, LSMASH_BOX_PRECEDENCE_ISOM_MINF ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_vmhd, vmhd, minf, ISOM_BOX_TYPE_VMHD, LSMASH_BOX_PRECEDENCE_ISOM_VMHD ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_smhd, smhd, minf, ISOM_BOX_TYPE_SMHD, LSMASH_BOX_PRECEDENCE_ISOM_SMHD ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_hmhd, hmhd, minf, ISOM_BOX_TYPE_HMHD, LSMASH_BOX_PRECEDENCE_ISOM_HMHD ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_nmhd, nmhd, minf, ISOM_BOX_TYPE_NMHD, LSMASH_BOX_PRECEDENCE_ISOM_NMHD ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_gmhd, gmhd, minf, QT_BOX_TYPE_GMHD, LSMASH_BOX_PRECEDENCE_QTFF_GMHD ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_gmin, gmin, gmhd, QT_BOX_TYPE_GMIN, LSMASH_BOX_PRECEDENCE_QTFF_GMIN ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_text, text, gmhd, QT_BOX_TYPE_TEXT, LSMASH_BOX_PRECEDENCE_QTFF_TEXT ) + +isom_dinf_t *isom_add_dinf( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + CREATE_BOX( dinf, parent, ISOM_BOX_TYPE_DINF, LSMASH_BOX_PRECEDENCE_ISOM_DINF, 1 ); + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( dinf, isom_minf_t ); + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) + || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( dinf, isom_meta_t ); + else + assert( 0 ); + return dinf; +} + +isom_dref_entry_t *isom_add_dref_entry( isom_dref_t *dref, lsmash_box_type_t type ) +{ + if( !dref ) + return NULL; + isom_dref_entry_t *data = lsmash_malloc_zero( sizeof(isom_dref_entry_t) ); + if( !data ) + return NULL; + isom_init_box_common( data, dref, type, LSMASH_BOX_PRECEDENCE_ISOM_DREF_ENTRY, isom_remove_dref_entry ); + if( isom_add_box_to_extension_list( dref, data ) < 0 ) + { + lsmash_free( data ); + return NULL; + } + if( lsmash_add_entry( &dref->list, data ) < 0 ) + { + lsmash_remove_entry_tail( &dref->extensions, isom_remove_dref_entry ); + return NULL; + } + return data; +} + +DEFINE_SIMPLE_BOX_ADDER( isom_add_dref, dref, dinf, ISOM_BOX_TYPE_DREF, LSMASH_BOX_PRECEDENCE_ISOM_DREF ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_stbl, stbl, minf, ISOM_BOX_TYPE_STBL, LSMASH_BOX_PRECEDENCE_ISOM_STBL ) +DEFINE_SIMPLE_BOX_ADDER( isom_add_stsd, stsd, stbl, ISOM_BOX_TYPE_STSD, LSMASH_BOX_PRECEDENCE_ISOM_STSD ) + +static int isom_add_sample_description_entry +( + isom_stsd_t *stsd, + void *description, + void (*destructor)( isom_sample_entry_t * ) +) +{ + if( isom_add_box_to_extension_list( stsd, description ) < 0 ) + { + lsmash_free( description ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( lsmash_add_entry( &stsd->list, description ) < 0 ) + { + lsmash_remove_entry_tail( &stsd->extensions, destructor ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +isom_visual_entry_t *isom_add_visual_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ) +{ + assert( stsd ); + isom_visual_entry_t *visual = lsmash_malloc_zero( sizeof(isom_visual_entry_t) ); + if( !visual ) + return NULL; + isom_init_box_common( visual, stsd, sample_type, LSMASH_BOX_PRECEDENCE_HM, isom_remove_visual_description ); + visual->manager |= LSMASH_VIDEO_DESCRIPTION; + return isom_add_sample_description_entry( stsd, visual, isom_remove_visual_description ) ? NULL : visual; +} + +isom_audio_entry_t *isom_add_audio_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ) +{ + assert( stsd ); + isom_audio_entry_t *audio = lsmash_malloc_zero( sizeof(isom_audio_entry_t) ); + if( !audio ) + return NULL; + isom_init_box_common( audio, stsd, sample_type, LSMASH_BOX_PRECEDENCE_HM, isom_remove_audio_description ); + audio->manager |= LSMASH_AUDIO_DESCRIPTION; + return isom_add_sample_description_entry( stsd, audio, isom_remove_audio_description ) ? NULL : audio; +} + +isom_qt_text_entry_t *isom_add_qt_text_description( isom_stsd_t *stsd ) +{ + assert( stsd ); + isom_qt_text_entry_t *text = lsmash_malloc_zero( sizeof(isom_qt_text_entry_t) ); + if( !text ) + return NULL; + isom_init_box_common( text, stsd, QT_CODEC_TYPE_TEXT_TEXT, LSMASH_BOX_PRECEDENCE_HM, isom_remove_qt_text_description ); + return isom_add_sample_description_entry( stsd, text, isom_remove_qt_text_description ) ? NULL : text; +} + +isom_tx3g_entry_t *isom_add_tx3g_description( isom_stsd_t *stsd ) +{ + assert( stsd ); + isom_tx3g_entry_t *tx3g = lsmash_malloc_zero( sizeof(isom_tx3g_entry_t) ); + if( !tx3g ) + return NULL; + isom_init_box_common( tx3g, stsd, ISOM_CODEC_TYPE_TX3G_TEXT, LSMASH_BOX_PRECEDENCE_HM, isom_remove_tx3g_description ); + return isom_add_sample_description_entry( stsd, tx3g, isom_remove_tx3g_description ) ? NULL : tx3g; +} + +isom_esds_t *isom_add_esds( void *parent_box ) +{ + isom_box_t *parent = (isom_box_t *)parent_box; + int is_qt = lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ); + lsmash_box_type_t box_type = is_qt ? QT_BOX_TYPE_ESDS : ISOM_BOX_TYPE_ESDS; + uint64_t precedence = is_qt ? LSMASH_BOX_PRECEDENCE_QTFF_ESDS : LSMASH_BOX_PRECEDENCE_ISOM_ESDS; + CREATE_BOX( esds, parent, box_type, precedence, 1 ); + return esds; +} + +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_glbl, glbl, parent_box, QT_BOX_TYPE_GLBL, LSMASH_BOX_PRECEDENCE_QTFF_GLBL, 1, void ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_clap, clap, visual, ISOM_BOX_TYPE_CLAP, LSMASH_BOX_PRECEDENCE_ISOM_CLAP, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_pasp, pasp, visual, ISOM_BOX_TYPE_PASP, LSMASH_BOX_PRECEDENCE_ISOM_PASP, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_colr, colr, visual, ISOM_BOX_TYPE_COLR, LSMASH_BOX_PRECEDENCE_ISOM_COLR, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_gama, gama, visual, QT_BOX_TYPE_GAMA, LSMASH_BOX_PRECEDENCE_QTFF_GAMA, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_fiel, fiel, visual, QT_BOX_TYPE_FIEL, LSMASH_BOX_PRECEDENCE_QTFF_FIEL, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_cspc, cspc, visual, QT_BOX_TYPE_CSPC, LSMASH_BOX_PRECEDENCE_QTFF_CSPC, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_sgbt, sgbt, visual, QT_BOX_TYPE_SGBT, LSMASH_BOX_PRECEDENCE_QTFF_SGBT, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_stsl, stsl, visual, ISOM_BOX_TYPE_STSL, LSMASH_BOX_PRECEDENCE_ISOM_STSL, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_btrt, btrt, visual, ISOM_BOX_TYPE_BTRT, LSMASH_BOX_PRECEDENCE_ISOM_BTRT, 0, isom_visual_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_wave, wave, audio, QT_BOX_TYPE_WAVE, LSMASH_BOX_PRECEDENCE_QTFF_WAVE, 0, isom_audio_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_chan, chan, audio, QT_BOX_TYPE_CHAN, LSMASH_BOX_PRECEDENCE_QTFF_CHAN, 1, isom_audio_entry_t ) +DEFINE_SIMPLE_SAMPLE_EXTENSION_ADDER( isom_add_srat, srat, audio, ISOM_BOX_TYPE_SRAT, LSMASH_BOX_PRECEDENCE_ISOM_SRAT, 0, isom_audio_entry_t ) + +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_stts, stts, stbl, ISOM_BOX_TYPE_STTS, LSMASH_BOX_PRECEDENCE_ISOM_STTS ) +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_ctts, ctts, stbl, ISOM_BOX_TYPE_CTTS, LSMASH_BOX_PRECEDENCE_ISOM_CTTS ) +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_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 ) + +isom_stco_t *isom_add_stco( isom_stbl_t *stbl ) +{ + ADD_LIST_BOX( stco, stbl, ISOM_BOX_TYPE_STCO, LSMASH_BOX_PRECEDENCE_ISOM_STCO ); + stco->large_presentation = 0; + return stco; +} + +isom_stco_t *isom_add_co64( isom_stbl_t *stbl ) +{ + ADD_LIST_BOX( stco, stbl, ISOM_BOX_TYPE_CO64, LSMASH_BOX_PRECEDENCE_ISOM_CO64 ); + stco->large_presentation = 1; + return stco; +} + +isom_sdtp_t *isom_add_sdtp( isom_box_t *parent ) +{ + if( !parent ) + return NULL; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) + { + isom_stbl_t *stbl = (isom_stbl_t *)parent; + ADD_LIST_BOX( sdtp, stbl, ISOM_BOX_TYPE_SDTP, LSMASH_BOX_PRECEDENCE_ISOM_SDTP ); + return sdtp; + } + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + { + isom_traf_t *traf = (isom_traf_t *)parent; + ADD_LIST_BOX( sdtp, traf, ISOM_BOX_TYPE_SDTP, LSMASH_BOX_PRECEDENCE_ISOM_SDTP ); + return sdtp; + } + assert( 0 ); + return NULL; +} + +isom_sgpd_t *isom_add_sgpd( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) + { + isom_stbl_t *stbl = (isom_stbl_t *)parent; + ADD_LIST_BOX_IN_LIST( sgpd, stbl, ISOM_BOX_TYPE_SGPD, LSMASH_BOX_PRECEDENCE_ISOM_SGPD ); + return sgpd; + } + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + { + isom_traf_t *traf = (isom_traf_t *)parent; + ADD_LIST_BOX_IN_LIST( sgpd, traf, ISOM_BOX_TYPE_SGPD, LSMASH_BOX_PRECEDENCE_ISOM_SGPD ); + return sgpd; + } + assert( 0 ); + return NULL; +} + +isom_sbgp_t *isom_add_sbgp( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) + { + isom_stbl_t *stbl = (isom_stbl_t *)parent; + ADD_LIST_BOX_IN_LIST( sbgp, stbl, ISOM_BOX_TYPE_SBGP, LSMASH_BOX_PRECEDENCE_ISOM_SBGP ); + return sbgp; + } + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + { + isom_traf_t *traf = (isom_traf_t *)parent; + ADD_LIST_BOX_IN_LIST( sbgp, traf, ISOM_BOX_TYPE_SBGP, LSMASH_BOX_PRECEDENCE_ISOM_SBGP ); + return sbgp; + } + assert( 0 ); + return NULL; +} + +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_chpl, chpl, udta, ISOM_BOX_TYPE_CHPL, LSMASH_BOX_PRECEDENCE_ISOM_CHPL ) + +isom_metaitem_t *isom_add_metaitem( isom_ilst_t *ilst, lsmash_itunes_metadata_item item ) +{ + if( !ilst ) + return NULL; + lsmash_box_type_t type = lsmash_form_iso_box_type( item ); + ADD_BOX_IN_LIST( metaitem, ilst, type, LSMASH_BOX_PRECEDENCE_ISOM_METAITEM ); + return metaitem; +} + +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mean, mean, metaitem, ISOM_BOX_TYPE_MEAN, LSMASH_BOX_PRECEDENCE_ISOM_MEAN ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_name, name, metaitem, ISOM_BOX_TYPE_NAME, LSMASH_BOX_PRECEDENCE_ISOM_NAME ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_data, data, metaitem, ISOM_BOX_TYPE_DATA, LSMASH_BOX_PRECEDENCE_ISOM_DATA ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_ilst, ilst, meta, ISOM_BOX_TYPE_ILST, LSMASH_BOX_PRECEDENCE_ISOM_ILST ) +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_keys, keys, meta, QT_BOX_TYPE_KEYS, LSMASH_BOX_PRECEDENCE_QTFF_KEYS ) + +isom_meta_t *isom_add_meta( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + CREATE_BOX( meta, parent, ISOM_BOX_TYPE_META, LSMASH_BOX_PRECEDENCE_ISOM_META, 1 ); + if( parent->file == (lsmash_file_t *)parent ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( meta, lsmash_file_t ); + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( meta, isom_moov_t ); + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( meta, isom_trak_t ); + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_UDTA ) ) + ATTACH_EXACTLY_ONE_BOX_TO_PARENT( meta, isom_udta_t ); + else + assert( 0 ); + return meta; +} + +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_cprt, cprt, udta, ISOM_BOX_TYPE_CPRT, LSMASH_BOX_PRECEDENCE_ISOM_CPRT ) + +isom_udta_t *isom_add_udta( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) ) + { + isom_moov_t *moov = (isom_moov_t *)parent; + ADD_BOX( udta, moov, ISOM_BOX_TYPE_UDTA, LSMASH_BOX_PRECEDENCE_ISOM_UDTA ); + return udta; + } + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) + { + isom_trak_t *trak = (isom_trak_t *)parent; + ADD_BOX( udta, trak, ISOM_BOX_TYPE_UDTA, LSMASH_BOX_PRECEDENCE_ISOM_UDTA ); + return udta; + } + assert( 0 ); + return NULL; +} + +DEFINE_SIMPLE_BOX_ADDER ( isom_add_WLOC, WLOC, udta, QT_BOX_TYPE_WLOC, LSMASH_BOX_PRECEDENCE_QTFF_WLOC ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_LOOP, LOOP, udta, QT_BOX_TYPE_LOOP, LSMASH_BOX_PRECEDENCE_QTFF_LOOP ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_SelO, SelO, udta, QT_BOX_TYPE_SELO, LSMASH_BOX_PRECEDENCE_QTFF_SELO ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_AllF, AllF, udta, QT_BOX_TYPE_ALLF, LSMASH_BOX_PRECEDENCE_QTFF_ALLF ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mvex, mvex, moov, ISOM_BOX_TYPE_MVEX, LSMASH_BOX_PRECEDENCE_ISOM_MVEX ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mehd, mehd, mvex, ISOM_BOX_TYPE_MEHD, LSMASH_BOX_PRECEDENCE_ISOM_MEHD ) +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_trex, trex, mvex, ISOM_BOX_TYPE_TREX, LSMASH_BOX_PRECEDENCE_ISOM_TREX ) +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_moof, moof, file, ISOM_BOX_TYPE_MOOF, LSMASH_BOX_PRECEDENCE_ISOM_MOOF, lsmash_file_t ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mfhd, mfhd, moof, ISOM_BOX_TYPE_MFHD, LSMASH_BOX_PRECEDENCE_ISOM_MFHD ) +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_traf, traf, moof, ISOM_BOX_TYPE_TRAF, LSMASH_BOX_PRECEDENCE_ISOM_TRAF ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_tfhd, tfhd, traf, ISOM_BOX_TYPE_TFHD, LSMASH_BOX_PRECEDENCE_ISOM_TFHD ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_tfdt, tfdt, traf, ISOM_BOX_TYPE_TFDT, LSMASH_BOX_PRECEDENCE_ISOM_TFDT ) +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_trun, trun, traf, ISOM_BOX_TYPE_TRUN, LSMASH_BOX_PRECEDENCE_ISOM_TRUN ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mfra, mfra, file, ISOM_BOX_TYPE_MFRA, LSMASH_BOX_PRECEDENCE_ISOM_MFRA, lsmash_file_t ) +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_tfra, tfra, mfra, ISOM_BOX_TYPE_TFRA, LSMASH_BOX_PRECEDENCE_ISOM_TFRA ) +DEFINE_SIMPLE_BOX_ADDER ( isom_add_mfro, mfro, mfra, ISOM_BOX_TYPE_MFRO, LSMASH_BOX_PRECEDENCE_ISOM_MFRO ) + +isom_mdat_t *isom_add_mdat( lsmash_file_t *file ) +{ + assert( !file->mdat ); + CREATE_BOX( mdat, file, ISOM_BOX_TYPE_MDAT, LSMASH_BOX_PRECEDENCE_ISOM_MDAT, 1 ); + file->mdat = mdat; + return mdat; +} + +isom_free_t *isom_add_free( void *parent_box ) +{ + if( !parent_box ) + return NULL; + isom_box_t *parent = (isom_box_t *)parent_box; + if( parent->file == (lsmash_file_t *)parent ) + { + lsmash_file_t *file = (lsmash_file_t *)parent; + CREATE_BOX( skip, file, ISOM_BOX_TYPE_FREE, LSMASH_BOX_PRECEDENCE_ISOM_FREE, 1 ); + if( !file->free ) + file->free = skip; + return skip; + } + CREATE_BOX( skip, parent, ISOM_BOX_TYPE_FREE, LSMASH_BOX_PRECEDENCE_ISOM_FREE, 1 ); + return skip; +} + +DEFINE_SIMPLE_BOX_IN_LIST_ADDER( isom_add_styp, styp, file, ISOM_BOX_TYPE_STYP, LSMASH_BOX_PRECEDENCE_ISOM_STYP, lsmash_file_t ) + +isom_sidx_t *isom_add_sidx( lsmash_file_t *file ) +{ + ADD_LIST_BOX_IN_LIST( sidx, file, ISOM_BOX_TYPE_SIDX, LSMASH_BOX_PRECEDENCE_ISOM_SIDX ); + return sidx; +} + +#undef ATTACH_EXACTLY_ONE_BOX_TO_PARENT +#undef CREATE_BOX +#undef CREATE_LIST_BOX +#undef ADD_BOX_TEMPLATE +#undef ADD_BOX_IN_LIST_TEMPLATE +#undef ADD_BOX +#undef ADD_BOX_IN_LIST +#undef ADD_LIST_BOX +#undef ADD_LIST_BOX_IN_LIST +#undef DEFINE_SIMPLE_BOX_ADDER_TEMPLATE +#undef DEFINE_SIMPLE_BOX_ADDER_TEMPLATE_6 +#undef DEFINE_SIMPLE_BOX_ADDER_TEMPLATE_5 +#undef DEFINE_SIMPLE_BOX_ADDER +#undef DEFINE_SIMPLE_BOX_IN_LIST_ADDER +#undef DEFINE_SIMPLE_LIST_BOX_ADDER + +static int fake_file_read +( + void *opaque, + uint8_t *buf, + int size +) +{ + fake_file_stream_t *stream = (fake_file_stream_t *)opaque; + int read_size; + if( stream->pos + size > stream->size ) + read_size = stream->size - stream->pos; + else + read_size = size; + memcpy( buf, stream->data + stream->pos, read_size ); + stream->pos += read_size; + return read_size; +} + +static int64_t fake_file_seek +( + void *opaque, + int64_t offset, + int whence +) +{ + fake_file_stream_t *stream = (fake_file_stream_t *)opaque; + if( whence == SEEK_SET ) + stream->pos = offset; + else if( whence == SEEK_CUR ) + stream->pos += offset; + else if( whence == SEEK_END ) + stream->pos = stream->size + offset; + return stream->pos; +} + +/* Public functions */ +lsmash_root_t *lsmash_create_root( void ) +{ + lsmash_root_t *root = lsmash_malloc_zero( sizeof(lsmash_root_t) ); + if( !root ) + return NULL; + root->root = root; + return root; +} + +void lsmash_destroy_root( lsmash_root_t *root ) +{ + isom_remove_box_by_itself( root ); +} + +lsmash_extended_box_type_t lsmash_form_extended_box_type( uint32_t fourcc, const uint8_t id[12] ) +{ + return (lsmash_extended_box_type_t){ fourcc, { id[0], id[1], id[2], id[3], id[4], id[5], + id[6], id[7], id[8], id[9], id[10], id[11] } }; +} + +lsmash_box_type_t lsmash_form_iso_box_type( uint32_t fourcc ) +{ + return (lsmash_box_type_t){ fourcc, lsmash_form_extended_box_type( fourcc, LSMASH_ISO_12_BYTES ) }; +} + +lsmash_box_type_t lsmash_form_qtff_box_type( uint32_t fourcc ) +{ + return (lsmash_box_type_t){ fourcc, lsmash_form_extended_box_type( fourcc, LSMASH_QTFF_12_BYTES ) }; +} + +#define CHECK_BOX_TYPE_IDENTICAL( a, b ) \ + a.fourcc == b.fourcc \ + && a.user.fourcc == b.user.fourcc \ + && a.user.id[0] == b.user.id[0] \ + && a.user.id[1] == b.user.id[1] \ + && a.user.id[2] == b.user.id[2] \ + && a.user.id[3] == b.user.id[3] \ + && a.user.id[4] == b.user.id[4] \ + && a.user.id[5] == b.user.id[5] \ + && a.user.id[6] == b.user.id[6] \ + && a.user.id[7] == b.user.id[7] \ + && a.user.id[8] == b.user.id[8] \ + && a.user.id[9] == b.user.id[9] \ + && a.user.id[10] == b.user.id[10] \ + && a.user.id[11] == b.user.id[11] + +int lsmash_check_box_type_identical( lsmash_box_type_t a, lsmash_box_type_t b ) +{ + return CHECK_BOX_TYPE_IDENTICAL( a, b ); +} + +int lsmash_check_codec_type_identical( lsmash_codec_type_t a, lsmash_codec_type_t b ) +{ + return CHECK_BOX_TYPE_IDENTICAL( a, b ); +} + +int lsmash_check_box_type_specified( const lsmash_box_type_t *box_type ) +{ + assert( box_type ); + if( !box_type ) + return 0; + return !!(box_type->fourcc + | box_type->user.fourcc + | box_type->user.id[0] | box_type->user.id[1] | box_type->user.id[2] | box_type->user.id[3] + | box_type->user.id[4] | box_type->user.id[5] | box_type->user.id[6] | box_type->user.id[7] + | box_type->user.id[8] | box_type->user.id[9] | box_type->user.id[10] | box_type->user.id[11]); +} + +lsmash_box_t *lsmash_get_box +( + lsmash_box_t *parent, + const lsmash_box_path_t box_path[] +) +{ + lsmash_entry_t *entry = isom_get_entry_of_box( parent, box_path ); + return (lsmash_box_t *)(entry ? entry->data : NULL); +} + +lsmash_box_t *lsmash_create_box +( + lsmash_box_type_t type, + uint8_t *data, + uint32_t size, + uint64_t precedence +) +{ + if( !lsmash_check_box_type_specified( &type ) ) + return NULL; + isom_unknown_box_t *box = lsmash_malloc_zero( sizeof(isom_unknown_box_t) ); + if( !box ) + return NULL; + if( size && data ) + { + box->unknown_size = size; + box->unknown_field = lsmash_memdup( data, size ); + if( !box->unknown_field ) + { + lsmash_free( box ); + return NULL; + } + } + else + { + box->unknown_size = 0; + box->unknown_field = NULL; + size = 0; + } + box->class = &lsmash_box_class; + box->root = NULL; + box->file = NULL; + box->parent = NULL; + box->destruct = (isom_extension_destructor_t)isom_remove_unknown_box; + box->manager = LSMASH_UNKNOWN_BOX; + box->precedence = precedence; + box->size = ISOM_BASEBOX_COMMON_SIZE + size + (type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ? 16 : 0); + box->type = type; + isom_set_box_writer( (isom_box_t *)box ); + return (lsmash_box_t *)box; +} + +int lsmash_add_box +( + lsmash_box_t *parent, + lsmash_box_t *box +) +{ + if( !parent ) + /* You cannot add any box without a box being its parent. */ + return LSMASH_ERR_FUNCTION_PARAM; + if( !box || box->size < ISOM_BASEBOX_COMMON_SIZE ) + return LSMASH_ERR_FUNCTION_PARAM; + if( parent->root == (lsmash_root_t *)parent ) + { + /* Only files can be added into any ROOT. + * For backward compatibility, use the active file as the parent. */ + if( parent->file ) + parent = (isom_box_t *)parent->file; + else + return LSMASH_ERR_FUNCTION_PARAM; + } + /* Add a box as a child box. */ + box->root = parent->root; + box->file = parent->file; + box->parent = parent; + return isom_add_box_to_extension_list( parent, box ); +} + +int lsmash_add_box_ex +( + lsmash_box_t *parent, + lsmash_box_t **p_box +) +{ + if( !parent ) + /* You cannot add any box without a box being its parent. */ + return LSMASH_ERR_FUNCTION_PARAM; + isom_unknown_box_t *box = (isom_unknown_box_t *)*p_box; + if( !box || box->size < ISOM_BASEBOX_COMMON_SIZE ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !(box->manager & LSMASH_UNKNOWN_BOX) ) + /* Simply add the box. */ + return lsmash_add_box( parent, *p_box ); + /* Check if the size of the box to be added is valid. */ + if( box->size != ISOM_BASEBOX_COMMON_SIZE + box->unknown_size + (box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ? 16 : 0) ) + return LSMASH_ERR_FUNCTION_PARAM; + if( !parent->file || parent->file == (lsmash_file_t *)box ) + return LSMASH_ERR_FUNCTION_PARAM; + if( parent->root == (lsmash_root_t *)parent ) + /* Only files can be added into any ROOT. + * For backward compatibility, use the active file as the parent. */ + parent = (isom_box_t *)parent->file; + /* Switch to the fake-file stream mode. */ + lsmash_file_t *file = parent->file; + lsmash_bs_t *bs_backup = file->bs; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + uint8_t *buf = lsmash_malloc( box->size ); + if( !buf ) + { + lsmash_bs_cleanup( bs ); + return LSMASH_ERR_MEMORY_ALLOC; + } + fake_file_stream_t fake_file = + { + .size = box->size, + .data = buf, + .pos = 0 + }; + bs->stream = &fake_file; + bs->read = fake_file_read; + bs->write = NULL; + bs->seek = fake_file_seek; + file->bs = bs; + file->fake_file_mode = 1; + /* Make the byte string representing the given box. */ + LSMASH_SET_BE32( &buf[0], box->size ); + LSMASH_SET_BE32( &buf[4], box->type.fourcc ); + if( box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ) + { + LSMASH_SET_BE32( &buf[8], box->type.user.fourcc ); + memcpy( &buf[12], box->type.user.id, 12 ); + } + memcpy( buf + (uintptr_t)(box->size - box->unknown_size), box->unknown_field, box->unknown_size ); + /* Add a box as a child box and try to expand into struct format. */ + lsmash_box_t dummy = { 0 }; + int ret = isom_read_box( file, &dummy, parent, 0, 0 ); + lsmash_free( buf ); + lsmash_bs_cleanup( bs ); + file->bs = bs_backup; /* Switch back to the normal file stream mode. */ + file->fake_file_mode = 0; + if( ret < 0 ) + return ret; + /* Reorder the added box by 'precedence'. */ + *p_box = (lsmash_box_t *)parent->extensions.tail->data; + (*p_box)->precedence = box->precedence; + isom_reorder_tail_box( parent ); + /* Do also its children by the same way. */ + lsmash_entry_list_t extensions = box->extensions; + lsmash_init_entry_list( &box->extensions ); /* to avoid freeing the children */ + isom_remove_box_by_itself( box ); + for( lsmash_entry_t *entry = extensions.head; entry; entry = entry->next ) + { + if( !entry->data ) + continue; + lsmash_box_t *child = (lsmash_box_t *)entry->data; + if( lsmash_add_box_ex( *p_box, &child ) == 0 ) + { + (*p_box)->size += child->size; + /* Avoid freeing at the end of this function. */ + entry->data = NULL; + } + } + isom_remove_all_extension_boxes( &extensions ); + return 0; +} + +void lsmash_destroy_box +( + lsmash_box_t *box +) +{ + isom_remove_box_by_itself( box ); +} + +void lsmash_destroy_children +( + lsmash_box_t *box +) +{ + if( box ) + isom_remove_all_extension_boxes( &box->extensions ); +} + +int lsmash_get_box_precedence +( + lsmash_box_t *box, + uint64_t *precedence +) +{ + if( !box || !precedence ) + return LSMASH_ERR_FUNCTION_PARAM; + *precedence = box->precedence; + return 0; +} + +lsmash_box_t *lsmash_root_as_box +( + lsmash_root_t *root +) +{ + return (lsmash_box_t *)root; +} + +lsmash_box_t *lsmash_file_as_box +( + lsmash_file_t *file +) +{ + return (lsmash_box_t *)file; +} + +int lsmash_write_top_level_box +( + lsmash_box_t *box +) +{ + if( !box || (isom_box_t *)box->file != box->parent ) + return LSMASH_ERR_FUNCTION_PARAM; + int ret = isom_write_box( box->file->bs, box ); + if( ret < 0 ) + return ret; + box->file->size += box->size; + return 0; +} + +uint8_t *lsmash_export_box +( + lsmash_box_t *box, + uint32_t *size +) +{ + if( !box || !size ) + return NULL; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + return NULL; + if( isom_write_box( bs, box ) < 0 ) + { + lsmash_bs_cleanup( bs ); + return NULL; + } + *size = bs->buffer.store; + uint8_t *data = bs->buffer.data; + bs->buffer.data = NULL; + lsmash_bs_cleanup( bs ); + return data; +} diff -Nru l-smash-1.9.1/core/box.h l-smash-2.3.0/core/box.h --- l-smash-1.9.1/core/box.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/box.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,2629 @@ +/***************************************************************************** + * box.h: + ***************************************************************************** + * Copyright (C) 2010-2014 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 + +typedef struct lsmash_box_tag isom_box_t; +typedef void (*isom_extension_destructor_t)( void *extension_data ); +typedef int (*isom_extension_writer_t)( lsmash_bs_t *bs, isom_box_t *box ); + +/* If size is 1, then largesize is actual size. + * If size is 0, then this box is the last one in the file. */ +#define ISOM_BASEBOX_COMMON \ + const lsmash_class_t *class; \ + lsmash_root_t *root; /* pointer of root */ \ + lsmash_file_t *file; /* pointer of file */ \ + isom_box_t *parent; /* pointer of the parent box of this box */ \ + uint8_t *binary; /* used only when LSMASH_BINARY_CODED_BOX */ \ + isom_extension_destructor_t destruct; /* box specific destructor */ \ + isom_extension_writer_t write; /* box specific writer */ \ + uint32_t manager; /* flags for L-SMASH */ \ + uint64_t precedence; /* precedence of the box position */ \ + uint64_t pos; /* starting position of this box in the file */ \ + lsmash_entry_list_t extensions; /* extension boxes */ \ + uint64_t size; /* the number of bytes in this box */ \ + lsmash_box_type_t type + +#define ISOM_FULLBOX_COMMON \ + ISOM_BASEBOX_COMMON; \ + uint8_t version; /* Basically, version is either 0 or 1 */ \ + uint32_t flags /* In the actual structure of box, flags is 24 bits. */ + +#define ISOM_BASEBOX_COMMON_SIZE 8 +#define ISOM_FULLBOX_COMMON_SIZE 12 +#define ISOM_LIST_FULLBOX_COMMON_SIZE 16 + +/* flags for L-SMASH */ +#define LSMASH_UNKNOWN_BOX 0x001 +#define LSMASH_ABSENT_IN_FILE 0x002 +#define LSMASH_QTFF_BASE 0x004 +#define LSMASH_VIDEO_DESCRIPTION 0x008 +#define LSMASH_AUDIO_DESCRIPTION 0x010 +#define LSMASH_FULLBOX 0x020 +#define LSMASH_LAST_BOX 0x040 +#define LSMASH_INCOMPLETE_BOX 0x080 +#define LSMASH_BINARY_CODED_BOX 0x100 +#define LSMASH_PLACEHOLDER 0x200 +#define LSMASH_WRITTEN_BOX 0x400 + +/* 12-byte ISO reserved value: + * 0xXXXXXXXX-0011-0010-8000-00AA00389B71 */ +static const uint8_t static_lsmash_iso_12_bytes[12] + = { 0x00, 0x11, 0x00, 0x10, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }; +#define LSMASH_ISO_12_BYTES static_lsmash_iso_12_bytes + +/* L-SMASH original 12-byte QuickTime file format value for CODEC discrimination mainly: + * 0xXXXXXXXX-0F11-4DA5-BF4E-F2C48C6AA11E */ +static const uint8_t static_lsmash_qtff_12_bytes[12] + = { 0x0F, 0x11, 0x4D, 0xA5, 0xBF, 0x4E, 0xF2, 0xC4, 0x8C, 0x6A, 0xA1, 0x1E }; +#define LSMASH_QTFF_12_BYTES static_lsmash_qtff_12_bytes + +struct lsmash_box_tag +{ + ISOM_FULLBOX_COMMON; +}; + +/* Unknown Box + * This structure is for boxes we don't know or define yet. + * This box must be always appended as an extension box. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t unknown_size; + uint8_t *unknown_field; +} isom_unknown_box_t; + +/* File Type Box + * This box identifies the specifications to which this file complies. + * This box shall occur before any variable-length box. + * In the absence of this box, the file is QuickTime file format or MP4 version 1 file format. + * In MP4 version 1 file format, Object Descriptor Box is mandatory. + * In QuickTime file format, Object Descriptor Box isn't defined. + * Therefore, if this box and an Object Descriptor Box are absent in the file, the file shall be QuickTime file format. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t major_brand; /* brand identifier */ + uint32_t minor_version; /* the minor version of the major brand */ + uint32_t *compatible_brands; /* a list, to the end of the box, of brands */ + + uint32_t brand_count; /* the number of factors in compatible_brands array */ +} isom_ftyp_t; + +/* Color Table Box + * This box defines a list of preferred colors for displaying the movie on devices that support only 256 colors. + * The list may contain up to 256 colors. This box contains a Macintosh color table data structure. + * This box is defined in QuickTime File Format Specification. + * The color table structure is also defined in struct ColorTable defined in Quickdraw.h. */ +typedef struct +{ + /* An array of colors. + * Each color is made of four unsigned 16-bit integers. */ + uint16_t value; /* index or other value + * Must be set to 0. */ + /* true color */ + uint16_t r; /* magnitude of red component */ + uint16_t g; /* magnitude of green component */ + uint16_t b; /* magnitude of blue component */ +} isom_qt_color_array_t; + +typedef struct +{ + uint32_t seed; /* unique identifier for table + * Must be set to 0. */ + uint16_t flags; /* high bit: 0 = PixMap; 1 = device + * Must be set to 0x8000. */ + uint16_t size; /* the number of colors in the following color array + * This is a zero-relative value; + * setting this field to 0 means that there is one color in the array. */ + isom_qt_color_array_t *array; +} isom_qt_color_table_t; + +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_qt_color_table_t color_table; +} isom_ctab_t; + +/* Track Header Box + * This box specifies the characteristics of a single track. */ +typedef struct +{ + /* version is either 0 or 1 + * flags + * 0x000001: Indicates that the track is enabled. + * A disabled track is treated as if it were not present. + * 0x000002: Indicates that the track is used in the presentation. + * 0x000004: Indicates that the track is used when previewing the presentation. + * 0x000008: Indicates that the track is used in the movie's poster. (only defined in QuickTime file format) + * ISOM: If in a presentation all tracks have neither track_in_movie nor track_in_preview set, + * then all tracks shall be treated as if both flags were set on all tracks. */ + ISOM_FULLBOX_COMMON; + /* version == 0: uint64_t -> uint32_t */ + uint64_t creation_time; /* the creation time of this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint64_t modification_time; /* the most recent time the track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint32_t track_ID; /* an integer that uniquely identifies the track + * Track IDs are never re-used and cannot be zero. */ + uint32_t reserved1; + uint64_t duration; /* the duration of this track expressed in the movie timescale units */ + /* The following fields are treated as + * ISOM: template fields. + * MP41: reserved fields. + * MP42: ignored fileds since compositions are done using BIFS system. + * 3GPP: ignored fields except for alternate_group. + * QTFF: usable fields. */ + uint32_t reserved2[2]; + int16_t layer; /* the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer. */ + int16_t alternate_group; /* an integer that specifies a group or collection of tracks + * If this field is not 0, it should be the same for tracks that contain alternate data for one another + * and different for tracks belonging to different such groups. + * Only one track within an alternate group should be played or streamed at any one time. */ + int16_t volume; /* fixed point 8.8 number. 0x0100 is full volume. */ + uint16_t reserved3; + int32_t matrix[9]; /* transformation matrix for the video */ + /* track's visual presentation size + * All images in the sequence are scaled to this size, before any overall transformation of the track represented by the matrix. + * Note: these fields are treated as reserved in MP4 version 1. */ + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ + /* */ +} isom_tkhd_t; + +/* Track Clean Aperture Dimensions Box + * A presentation mode where clap and pasp are reflected. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ +} isom_clef_t; + +/* Track Production Aperture Dimensions Box + * A presentation mode where pasp is reflected. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ +} isom_prof_t; + +/* Track Encoded Pixels Dimensions Box + * A presentation mode where clap and pasp are not reflected. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ +} isom_enof_t; + +/* Track Aperture Mode Dimensions Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_clef_t *clef; /* Track Clean Aperture Dimensions Box */ + isom_prof_t *prof; /* Track Production Aperture Dimensions Box */ + isom_enof_t *enof; /* Track Encoded Pixels Dimensions Box */ +} isom_tapt_t; + +/* Edit List Box + * This box contains an explicit timeline map. + * Each entry defines part of the track timeline: by mapping part of the media timeline, or by indicating 'empty' time, + * or by defining a 'dwell', where a single time-point in the media is held for a period. + * The last edit in a track shall never be an empty edit. + * Any difference between the duration in the Movie Header Box, and the track's duration is expressed as an implicit empty edit at the end. + * It is recommended that any edits, explicit or implied, not select any portion of the composition timeline that doesn't map to a sample. + * Therefore, if the first sample in the track has non-zero CTS, then this track should have at least one edit and the start time in it should + * correspond to the value of the CTS the first sample has or more not to exceed the largest CTS in this track. */ +typedef struct +{ + /* This entry is called Timeline Mapping Edit (TME) entry in UltraViolet Common File Format. + * version == 0: 64bits -> 32bits */ + uint64_t segment_duration; /* the duration of this edit expressed in the movie timescale units */ + int64_t media_time; /* the starting composition time within the media of this edit segment + * If this field is set to -1, it is an empty edit. */ + int32_t media_rate; /* the relative rate at which to play the media corresponding to this edit segment + * If this value is 0, then the edit is specifying a 'dwell': + * the media at media_time is presented for the segment_duration. + * This field is expressed as 16.16 fixed-point number. */ +} isom_elst_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + lsmash_entry_list_t *list; +} isom_elst_t; + +/* Edit Box + * This optional box maps the presentation time-line to the media time-line as it is stored in the file. + * In the absence of this box, there is an implicit one-to-one mapping of these time-lines, + * and the presentation of a track starts at the beginning of the presentation. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_elst_t *elst; /* Edit List Box */ +} isom_edts_t; + +/* Track Reference Box + * The Track Reference Box contains Track Reference Type Boxes. + * Track Reference Type Boxes define relationships between tracks. + * They allow one track to specify how it is related to other tracks. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t *track_ID; /* track_IDs of reference tracks / Zero value must not be used */ + + uint32_t ref_count; /* number of reference tracks */ +} isom_tref_type_t; + +typedef struct +{ + ISOM_BASEBOX_COMMON; + lsmash_entry_list_t ref_list; /* Track Reference Type Boxes */ +} isom_tref_t; + +/* Media Header Box + * This box declares overall information that is media-independent, and relevant to characteristics of the media in a track.*/ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + /* version == 0: uint64_t -> uint32_t */ + uint64_t creation_time; /* the creation time of the media in this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint64_t modification_time; /* the most recent time the media in this track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint32_t timescale; /* media timescale: timescale for this media */ + uint64_t duration; /* the duration of this media expressed in the timescale indicated in this box */ + /* */ + uint16_t language; /* ISOM: ISO-639-2/T language codes. Most significant 1-bit is 0. + * Each character is packed as the difference between its ASCII value and 0x60. + * QTFF: Macintosh language codes is usually used. + * Mac's value is less than 0x800 while ISO's value is 0x800 or greater. */ + int16_t quality; /* ISOM: pre_defined / QTFF: the media's playback quality */ +} isom_mdhd_t; + +/* Handler Reference Box + * In Media Box, this box is mandatory and (ISOM: should/QTFF: must) come before Media Information Box. + * ISOM: this box might be also in Meta Box. + * QTFF: this box might be also in Media Information Box. If this box is present there, it must come before Data Information Box. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t componentType; /* ISOM: pre_difined = 0 + * QTFF: 'mhlr' for Media Handler Reference Box and 'dhlr' for Data Handler Reference Box */ + uint32_t componentSubtype; /* Both ISOM and QT: when present in Media Handler Reference Box, this field defines the type of media data. + * ISOM: when present in Metadata Handler Reference Box, this field defines the format of the meta box contents. + * QTFF: when present in Data Handler Reference Box, this field defines the data reference type. */ + /* The following fields are defined in QTFF however these fields aren't mentioned in QuickTime SDK and are reserved in the specification. + * In ISOM, these fields are still defined as reserved. */ + uint32_t componentManufacturer; /* vendor indentification / A value of 0 matches any manufacturer. */ + uint32_t componentFlags; /* flags describing required component capabilities + * The high-order 8 bits should be set to 0. + * The low-order 24 bits are specific to each component type. */ + uint32_t componentFlagsMask; /* This field indicates which flags in the componentFlags field are relevant to this operation. */ + /* */ + uint8_t *componentName; /* ISOM: a null-terminated string in UTF-8 characters + * QTFF: Pascal string */ + + uint32_t componentName_length; +} isom_hdlr_t; + + +/** Media Information Header Boxes + ** There is a different media information header for each track type + ** (corresponding to the media handler-type); the matching header shall be present. **/ +/* Video Media Header Box + * This box contains general presentation information, independent of the coding, for video media. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* flags is 1 */ + uint16_t graphicsmode; /* template: graphicsmode = 0 */ + uint16_t opcolor[3]; /* template: opcolor = { 0, 0, 0 } */ +} isom_vmhd_t; + +/* Sound Media Header Box + * This box contains general presentation information, independent of the coding, for audio media. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + int16_t balance; /* a fixed-point 8.8 number that places mono audio tracks in a stereo space. template: balance = 0 */ + uint16_t reserved; +} isom_smhd_t; + +/* Hint Media Header Box + * This box contains general information, independent of the protocol, for hint tracks. (A PDU is a Protocol Data Unit.) */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint16_t maxPDUsize; /* the size in bytes of the largest PDU in this (hint) stream */ + uint16_t avgPDUsize; /* the average size of a PDU over the entire presentation */ + uint32_t maxbitrate; /* the maximum rate in bits/second over any window of one second */ + uint32_t avgbitrate; /* the average rate in bits/second over the entire presentation */ + uint32_t reserved; +} isom_hmhd_t; + +/* Null Media Header Box + * This box may be used for streams other than visual and audio (e.g., timed metadata streams). */ +typedef struct +{ + /* Streams other than visual and audio may use a Null Media Header Box */ + ISOM_FULLBOX_COMMON; /* flags is currently all zero */ +} isom_nmhd_t; + +/* Generic Media Information Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint16_t graphicsmode; + uint16_t opcolor[3]; + int16_t balance; /* This field is nomally set to 0. */ + uint16_t reserved; /* Reserved for use by Apple. Set this field to 0. */ +} isom_gmin_t; + +/* Text Media Information Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + int32_t matrix[9]; /* Unkown fields. Default values are probably: + * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */ +} isom_text_t; + +/* Generic Media Information Header Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_gmin_t *gmin; /* Generic Media Information Box */ + isom_text_t *text; /* Text Media Information Box */ +} isom_gmhd_t; +/** **/ + +/* Data Reference Box + * name and location fields are expressed in null-terminated string using UTF-8 characters. */ +typedef struct +{ + /* This box is DataEntryUrlBox or DataEntryUrnBox */ + ISOM_FULLBOX_COMMON; /* flags == 0x000001 means that the media data is in the same file + * as the Movie Box containing this data reference. */ + char *name; /* only for DataEntryUrnBox */ + char *location; /* a location to find the resource with the given name */ + + uint32_t name_length; + uint32_t location_length; + lsmash_file_t *ref_file; /* pointer to the handle of the referenced file */ +} 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; + +/* Parameter Set Entry within AVC/HEVC Decoder Configuration Record */ +typedef struct +{ + uint16_t nalUnitLength; + uint8_t *nalUnit; + /* */ + int unused; +} isom_dcr_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; + +/* Global Header Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t header_size; + uint8_t *header_data; +} isom_glbl_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; + * QTFF: this box is a mandatory extension for all uncompressed Y'CbCr data formats. */ +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; + +/* ISOM: Colour Information Box / QTFF: 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. + * If colour information is supplied in both this box, and also in the video bitstream, + * this box takes precedence, and over-rides the information in the bitstream. + * For QuickTime file format: + * This box ('colr') supersedes the Gamma Level Box ('gama'). + * Writers of QTFF should never write both into an Image Description, and readers of QTFF should ignore 'gama' if 'colr' is present. + * Note: this box is a mandatory extension for all uncompressed Y'CbCr data formats. + * For ISO Base Media file format: + * Colour information may be supplied in one or more Colour Information Boxes placed in a VisualSampleEntry. + * These should be placed in order in the sample entry starting with the most accurate (and potentially the most difficult to process), in progression to the least. + * These are advisory and concern rendering and colour conversion, and there is no normative behaviour associated with them; a reader may choose to use the most suitable. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t color_parameter_type; /* QTFF: 'nclc' or 'prof' + * ISOM: 'nclx', 'rICC' or 'prof' */ + /* for 'nclc' and 'nclx' */ + 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 */ + /* for 'nclx' */ + unsigned full_range_flag : 1; + unsigned reserved : 7; +} isom_colr_t; + +/* Gamma Level Box + * This box is used to indicate that the decompressor corrects gamma level at display time. + * This box is defined in QuickTime File Format Specification and ImageCompression.h. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t level; /* A fixed-point 16.16 number indicating the gamma level at which the image was captured. + * Zero value indicates platform's standard gamma. */ +} isom_gama_t; + +/* Field/Frame Information Box + * This box is used by applications to modify decompressed image data or by decompressor components to determine field display order. + * This box is defined in QuickTime File Format Specification, dispatch019 and ImageCodec.h. + * Note: this box is a mandatory extension for all uncompressed Y'CbCr data formats. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint8_t fields; /* the number of fields per frame + * 1: progressive scan + * 2: 2:1 interlaced */ + uint8_t detail; /* field ordering */ +} isom_fiel_t; + +/* Colorspace Box + * This box is defined in ImageCompression.h. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t pixel_format; /* the native pixel format of an image */ +} isom_cspc_t; + +/* Significant Bits Box + * This box is defined in Letters from the Ice Floe dispatch019. + * Note: this box is a mandatory extension for 'v216' (Uncompressed Y'CbCr, 10, 12, 14, or 16-bit-per-component 4:2:2). */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint8_t significantBits; /* the number of significant bits per component */ +} isom_sgbt_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. + * This box is defined in ISO Base Media file format. */ +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_mp4s_entry_t; + +/* ISOM: Visual Sample Entry / QTFF: Image Description + * For maximum compatibility, the following extension boxes should follow, not precede, + * any extension boxes defined in or required by derived specifications. + * Clean Aperture Box + * Pixel Aspect Ratio Box */ +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 -1, 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. */ + /* Color table follows color_table_ID only when color_table_ID is set to 0. */ + isom_qt_color_table_t color_table; /* a list of preferred colors for displaying the movie on devices that support only 256 colors */ +} 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 is defined in QuickTime file format. + * 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_terminator_t *terminator; /* Terminator Box */ +} isom_wave_t; + +/* Audio Channel Layout Box + * This box is defined in QuickTime file format or Apple Lossless Audio inside ISO Base Media. */ +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; + +/* Sampling Rate Box + * This box may be present only in an AudioSampleEntryV1, and when present, + * it overrides the samplerate field and documents the actual sampling rate. + * When this box is present, the media timescale should be the same as the + * sampling rate, or an integer division or multiple of it. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t sampling_rate; /* the actual sampling rate of the audio media, expressed as a 32-bit integer + * The value of this field overrides the samplerate field in the AudioSampleEntryV1 + * and documents the actual sampling rate. */ +} isom_srat_t; + +/* ISOM: Audio Sample Entry / QTFF: Sound Description */ +typedef struct +{ + ISOM_SAMPLE_ENTRY; + int16_t version; /* ISOM: version = 0 is used to support non-high samplerate audio format. + * version = 1, called AudioSampleEntryV1, is used to support high samplerate audio format. + * An AudioSampleEntryV1 requires that the enclosing Sample Description Box also takes the version 1. + * For maximum compatibility, an AudioSampleEntryV1 should only be used when needed. + * QTFF: 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 format. */ + 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 + * channelcount is a value greater than zero that indicates the maximum number of channels that the + * audio could deliver. + * A channelcount of 1 indicates mono audio, and 2 indicates stereo (left/right). + * When values greater than 2 are used, the codec configuration should identify the channel assignment. + * 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 + * When it is desired to indicate an audio sampling rate greater than the value that can be represented in + * this field, this field should contain a value left-shifted 16 bits that matches the media timescale, + * or be an integer division or multiple of it. + * 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. */ + /* QTFF-based 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 */ + /* QTFF-based 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 Audio Channel Layout 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 */ +} 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_qt_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; + +/* 3GPP 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; + uint32_t entry_count; /* print only */ + 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. */ +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: if version is set to 1, sample_offset is signed 32-bit integer. + * QTFF: sample_offset is always signed 32-bit integer. */ +} 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_t; + +/* Random Access Entry + * Samples marked by this group must be random access points, and may also be sync points. */ +typedef struct +{ + /* grouping_type is 'rap ' */ + uint32_t description_length; /* This field is available only if version == 1 and default_length == 0. */ + unsigned num_leading_samples_known : 1; /* the value of one indicates that the number of leading samples is known for each sample in this group, + * and the number is specified by num_leading_samples. */ + unsigned num_leading_samples : 7; /* the number of leading samples for each sample in this group + * Note: when num_leading_samples_known is equal to 0, this field should be ignored. */ +} isom_rap_entry_t; + +/* Roll Recovery Entry + * This grouping type is defined as that group of samples having the same roll distance. */ +typedef struct +{ + /* grouping_type is 'roll' */ + uint32_t description_length; /* This field is available only if version == 1 and default_length == 0. */ + int16_t roll_distance; /* the number of samples that must be decoded in order for a sample to be decoded correctly + * A positive value indicates post-roll, and a negative value indicates pre-roll. + * The value zero must not be used. */ +} isom_roll_entry_t; + +/* Sample to Group Box + * This box is used to find the group that a sample belongs to and the associated description of that sample group. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t grouping_type; /* Links it to its sample group description table with the same value for grouping type. */ + uint32_t grouping_type_parameter; /* an indication of the sub-type of the grouping + * This field is available only if version == 1. */ + lsmash_entry_list_t *list; +} isom_sbgp_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. + * Within the Sample to Group Box in movie fragment, the group description indexes for groups defined + * within the same fragment start at 0x10001, i.e. the index value 1, with the value 1 in the top 16 bits. */ +} isom_group_assignment_entry_t; + +/* Sample Table Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_stsd_t *stsd; /* Sample Description Box */ + isom_stts_t *stts; /* Decoding Time to Sample Box */ + isom_ctts_t *ctts; /* Composition Time to Sample Box */ + isom_cslg_t *cslg; /* ISOM: Composition to Decode Box / QTFF: Composition Shift Least Greatest Box */ + isom_stss_t *stss; /* Sync Sample Box */ + isom_stps_t *stps; /* ISOM: null / QTFF: Partial Sync Sample Box */ + isom_sdtp_t *sdtp; /* Independent and Disposable Samples Box */ + isom_stsc_t *stsc; /* Sample To Chunk Box */ + isom_stsz_t *stsz; /* Sample Size Box */ + isom_stco_t *stco; /* Chunk Offset Box */ + lsmash_entry_list_t sgpd_list; /* Sample Group Description Boxes */ + lsmash_entry_list_t sbgp_list; /* Sample To Group Boxes */ +} isom_stbl_t; + +/* Media Information Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + /* Media Information Header Boxes */ + isom_vmhd_t *vmhd; /* Video Media Header Box */ + isom_smhd_t *smhd; /* Sound Media Header Box */ + isom_hmhd_t *hmhd; /* ISOM: Hint Media Header Box / QTFF: null */ + isom_nmhd_t *nmhd; /* ISOM: Null Media Header Box / QTFF: null */ + isom_gmhd_t *gmhd; /* ISOM: null / QTFF: Generic Media Information Header Box */ + /* */ + isom_hdlr_t *hdlr; /* ISOM: null / QTFF: Data Handler Reference Box + * Note: this box must come before Data Information Box. */ + isom_dinf_t *dinf; /* Data Information Box */ + isom_stbl_t *stbl; /* Sample Table Box */ +} isom_minf_t; + +/* Media Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mdhd_t *mdhd; /* Media Header Box */ + isom_hdlr_t *hdlr; /* ISOM: Handler Reference Box / QTFF: Media Handler Reference Box + * Note: this box must come before Media Information Box. */ + isom_minf_t *minf; /* Media Information Box */ +} isom_mdia_t; + +/* Movie Header Box + * This box defines overall information which is media-independent, and relevant to the entire presentation considered as a whole. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + /* version == 0: uint64_t -> uint32_t */ + uint64_t creation_time; /* the creation time of the presentation (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint64_t modification_time; /* the most recent time the presentation was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint32_t timescale; /* movie timescale: timescale for the entire presentation */ + uint64_t duration; /* the duration, expressed in movie timescale, of the longest track */ + /* The following fields are treated as + * ISOM: template fields. + * MP41: reserved fields. + * MP42: ignored fileds since compositions are done using BIFS system. + * 3GPP: ignored fields. + * QTFF: usable fields. */ + int32_t rate; /* fixed point 16.16 number. 0x00010000 is normal forward playback. */ + int16_t volume; /* fixed point 8.8 number. 0x0100 is full volume. */ + int16_t reserved; + int32_t preferredLong[2]; /* ISOM: reserved / QTFF: unknown */ + int32_t matrix[9]; /* transformation matrix for the video */ + /* The following fields are defined in QuickTime file format. + * In ISO Base Media file format, these fields are treated as pre_defined. */ + int32_t previewTime; /* the time value in the movie at which the preview begins */ + int32_t previewDuration; /* the duration of the movie preview in movie timescale units */ + int32_t posterTime; /* the time value of the time of the movie poster */ + int32_t selectionTime; /* the time value for the start time of the current selection */ + int32_t selectionDuration; /* the duration of the current selection in movie timescale units */ + int32_t currentTime; /* the time value for current time position within the movie */ + /* */ + uint32_t next_track_ID; /* larger than the largest track-ID in use */ +} isom_mvhd_t; + +/* Object Descriptor Box + * Note that this box is mandatory under 14496-1:2001 (mp41) while not mandatory under 14496-14:2003 (mp42). */ +struct mp4sys_ObjectDescriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + struct mp4sys_ObjectDescriptor_t *OD; +} isom_iods_t; + +/* Media Data Box + * This box contains the media data. + * A presentation may contain zero or more Media Data Boxes.*/ +typedef struct +{ + ISOM_BASEBOX_COMMON; /* If size is 0, then this box is the last box. */ + + uint64_t media_size; /* the total media size already written in this box */ +} 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. + * Basically, this box exists in User Data Box inside Movie Box if present. */ +typedef struct +{ + uint64_t start_time; /* version = 0: expressed in movie timescale units + * version = 1: expressed in 100 nanoseconds */ + /* Chapter name is Pascal string */ + uint8_t chapter_name_length; + char *chapter_name; +} isom_chpl_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version = 0 is defined in F4V file format. */ + uint8_t unknown; /* only available under version = 1 */ + lsmash_entry_list_t *list; /* if version is set to 0, entry_count is uint8_t. */ +} isom_chpl_t; + +typedef struct +{ + char *chapter_name; + uint64_t start_time; +} isom_chapter_entry_t; + +/* Metadata Item Keys Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_keys_t; + +typedef struct +{ + uint32_t key_size; /* the size of the entire structure containing a key definition + * key_size = sizeof(key_size) + sizeof(key_namespace) + sizeof(key_value) */ + uint32_t key_namespace; /* a naming scheme used for metadata keys + * Location metadata keys, for example, use the 'mdta' key namespace. */ + uint8_t *key_value; /* the actual name of the metadata key + * Keys with the 'mdta' namespace use a reverse DNS naming convention. */ +} isom_keys_entry_t; + +/* Meaning Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint8_t *meaning_string; /* to fill the box */ + + uint32_t meaning_string_length; +} isom_mean_t; + +/* Name Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint8_t *name; /* to fill the box */ + + uint32_t name_length; +} isom_name_t; + +/* Data Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + /* type indicator */ + uint16_t reserved; /* always 0 */ + uint8_t type_set_identifier; /* 0: type set of the common basic data types */ + uint8_t type_code; /* type of data code */ + /* */ + uint32_t the_locale; /* reserved to be 0 */ + uint8_t *value; /* to fill the box */ + + uint32_t value_length; +} isom_data_t; + +/* Metadata Item Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mean_t *mean; /* Meaning Box */ + isom_name_t *name; /* Name Box */ + isom_data_t *data; /* Data Box */ +} isom_metaitem_t; + +/* Metadata Item List Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + lsmash_entry_list_t metaitem_list; /* Metadata Item Box List + * There is no entry_count field. */ +} isom_ilst_t; + +/* Meta Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* ISOM: FullBox / QTFF: BaseBox */ + isom_hdlr_t *hdlr; /* Metadata Handler Reference Box */ + isom_dinf_t *dinf; /* ISOM: Data Information Box / QTFF: null */ + isom_keys_t *keys; /* ISOM: null / QTFF: Metadata Item Keys Box */ + isom_ilst_t *ilst; /* Metadata Item List Box only defined in Apple MPEG-4 and QTFF */ +} isom_meta_t; + +/* Window Location Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + /* default window location for movie */ + uint16_t x; + uint16_t y; +} isom_WLOC_t; + +/* Looping Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t looping_mode; /* 0 for none, 1 for looping, 2 for palindromic looping */ +} isom_LOOP_t; + +/* Play Selection Only Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint8_t selection_only; /* whether only the selected area of the movie should be played */ +} isom_SelO_t; + +/* Play All Frames Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint8_t play_all_frames; /* whether all frames of video should be played, regardless of timing */ +} isom_AllF_t; + +/* Copyright Box + * The Copyright box contains a copyright declaration which applies to the entire presentation, + * when contained within the Movie Box, or, when contained in a track, to that entire track. + * There may be multiple copyright boxes using different language codes. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint16_t language; /* ISO-639-2/T language codes. Most significant 1-bit is 0. + * Each character is packed as the difference between its ASCII value and 0x60. */ + uint8_t *notice; /* a null-terminated string in either UTF-8 or UTF-16 characters, giving a copyright notice. + * If UTF-16 is used, the string shall start with the BYTE ORDER MARK (0xFEFF), to distinguish it from a UTF-8 string. + * This mark does not form part of the final string. */ + uint32_t notice_length; +} isom_cprt_t; + +/* User Data Box + * This box is a container box for informative user-data. + * This user data is formatted as a set of boxes with more specific box types, which declare more precisely their content. + * QTFF: for historical reasons, this box is optionally terminated by a 32-bit integer set to 0. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_chpl_t *chpl; /* Chapter List Box */ + isom_meta_t *meta; /* Meta Box extended by Apple for iTunes movie */ + /* QuickTime user data */ + isom_WLOC_t *WLOC; /* Window Location Box */ + isom_LOOP_t *LOOP; /* Looping Box */ + isom_SelO_t *SelO; /* Play Selection Only Box */ + isom_AllF_t *AllF; /* Play All Frames Box */ + /* Copyright Box List */ + lsmash_entry_list_t cprt_list; /* Copyright Boxes is defined in ISO Base Media and 3GPP file format */ +} isom_udta_t; + +/** Caches for handling tracks **/ +typedef struct +{ + uint64_t alloc; /* total buffer size for the pool */ + uint64_t size; /* total size of samples in the pool */ + uint32_t sample_count; /* number of samples in the pool */ + uint8_t *data; /* actual data of samples in the pool */ +} isom_sample_pool_t; + +typedef struct +{ + uint32_t chunk_number; /* chunk number */ + uint32_t sample_description_index; /* sample description index */ + uint64_t first_dts; /* the first DTS in chunk */ + isom_sample_pool_t *pool; /* samples pooled to interleave */ +} isom_chunk_t; + +typedef struct +{ + uint64_t dts; + uint64_t cts; + int32_t ctd_shift; +} isom_timestamp_t; + +typedef struct +{ + isom_group_assignment_entry_t *assignment; /* the address corresponding to the entry in Sample to Group Box */ + isom_group_assignment_entry_t *prev_assignment; /* the address of the previous assignment */ + isom_rap_entry_t *random_access; /* the address corresponding to the random access entry in Sample Group Description Box */ + uint8_t is_prev_rap; /* whether the previous sample is a random access point or not */ +} isom_rap_group_t; + +typedef struct +{ + isom_group_assignment_entry_t *assignment; /* the address corresponding to the entry in Sample to Group Box */ + isom_sgpd_t *sgpd; /* the address to the active Sample Group Description 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 */ + uint64_t rp_cts; /* the CTS of the recovery point */ + int16_t roll_distance; /* the current roll_distance + * The value may be updated when 'described' is set to ROLL_DISTANCE_INITIALIZED. */ +#define MAX_ROLL_WAIT_AND_SEE_COUNT 64 + uint8_t wait_and_see_count; /* Wait-and-see after initialization of roll_distance until reaching MAX_ROLL_WAIT_AND_SEE. */ + uint8_t is_fragment; /* the flag if the current group is in fragment */ + uint8_t prev_is_recovery_start; /* whether the previous sample is a starting point of recovery or not */ + uint8_t delimited; /* the flag if the sample_count is determined */ +#define ROLL_DISTANCE_INITIALIZED 1 +#define ROLL_DISTANCE_DETERMINED 2 + uint8_t described; /* the status of the group description */ +} isom_roll_group_t; + +typedef struct +{ + lsmash_entry_list_t *pool; /* grouping pooled to delimit and describe */ +} isom_grouping_t; + +typedef struct +{ + uint64_t largest_cts; /* the largest CTS of a subsegment of the reference stream */ + uint64_t smallest_cts; /* the smallest CTS of a subsegment of the reference stream */ + uint64_t first_cts; /* the CTS of the first sample of a subsegment of the reference stream */ + uint64_t segment_duration; /* the sum of the subsegment_duration of preceeding subsegments */ + /* SAP related info within the active subsegment of the reference stream */ + uint64_t first_ed_cts; /* the earliest CTS of decodable samples after the first recovery point */ + uint64_t first_rp_cts; /* the CTS of the first recovery point */ + uint32_t first_rp_number; /* the number of the first recovery point */ + uint32_t first_ra_number; /* the number of the first random accessible sample */ + lsmash_random_access_flag first_ra_flags; /* the flags of the first random accessible sample */ + int decodable; +} isom_subsegment_t; + +typedef struct +{ + uint8_t has_samples; + uint8_t roll_grouping; + uint8_t rap_grouping; + 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 fragment */ + uint64_t sample_count; /* the number of samples in this track fragment */ + isom_subsegment_t subsegment; +} isom_fragment_t; + +typedef struct +{ + uint8_t all_sync; /* if all samples are sync sample */ + uint8_t is_audio; + 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-present: + * This flag indicates the presence of the base_data_offset field. + * The base_data_offset is the base offset to use when calculating data offsets. + * Offsets are file offsets as like as chunk_offset in Chunk Offset Box. + * If this flag is set and default-base-is-moof is not set, 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. */ + ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT = 0x000002, /* sample-description-index-present + * This flag indicates the presence of the sample_description_index field. */ + ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT = 0x000008, /* default-sample-duration-present: + * This flag indicates the presence of the default_sample_duration field. */ + ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT = 0x000010, /* default-sample-size-present: + * This flag indicates the presence of the default_sample_size field. */ + ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT = 0x000020, /* default-sample-flags-present: + * This flag indicates the presence of the default_sample_flags field. */ + ISOM_TF_FLAGS_DURATION_IS_EMPTY = 0x010000, /* duration-is-empty: + * This flag indicates there are no samples for this time interval. */ + ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF = 0x020000, /* default-base-is-moof: + * If base-data-offset-present is not set, this flag indicates the implicit + * base_data_offset is always equal to the position of the first byte of the + * enclosing Movie Fragment BOX. + * This flag is only available under the 'iso5' or later brands and cannot be set + * when earlier brands are included in the File Type box. */ +} isom_tf_flags_code; + +/* Track Run Flags ('tr_flags') */ +typedef enum +{ + ISOM_TR_FLAGS_DATA_OFFSET_PRESENT = 0x000001, /* data-offset-present: + * This flag indicates the presence of the data_offset field. */ + ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT = 0x000004, /* first-sample-flags-present: + * This flag indicates the presence of the first_sample_flags field. */ + ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT = 0x000100, /* sample-duration-present: + * This flag indicates the presence of the sample_duration field. */ + ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT = 0x000200, /* sample-size-present: + * This flag indicates the presence of the sample_size field. */ + ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT = 0x000400, /* sample-flags-present: + * This flag indicates the presence of the sample_flags field. */ + ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT = 0x000800, /* sample-composition-time-offsets-present: + * This flag indicates the presence of the sample_composition_time_offset field. */ +} 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_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 */ +} 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 + * 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 Base Media Decode Time Box + * This box provides the absolute decode time, measured on the media timeline, of the first sample in decode order in the track fragment. + * This can be useful, for example, when performing random access in a file; + * it is not necessary to sum the sample durations of all preceding samples in previous fragments to find this value + * (where the sample durations are the deltas in the Decoding Time to Sample Box and the sample_durations in the preceding track runs). + * This box, if present, shall be positioned after the Track Fragment Header Box and before the first Track Fragment Run box. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + /* version == 0: 64bits -> 32bits */ + uint64_t baseMediaDecodeTime; /* an integer equal to the sum of the decode durations of all earlier samples in the media, expressed in the media's timescale + * It does not include the samples added in the enclosing track fragment. + * NOTE: the decode timeline is a media timeline, established before any explicit or implied mapping of media time to presentation time, + * for example by an edit list or similar structure. */ +} isom_tfdt_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_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 + * If version == 0, unsigned 32-bit integer. + * Otherwise, signed 32-bit integer. */ +} isom_trun_optional_row_t; + +/* Track Fragment Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_tfhd_t *tfhd; /* Track Fragment Header Box */ + isom_tfdt_t *tfdt; /* Track Fragment Base Media Decode Time 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_sdtp_t *sdtp; /* Independent and Disposable Samples Box (available under Protected Interoperable File Format) */ + lsmash_entry_list_t sgpd_list; /* Sample Group Description Boxes (available under ISO Base Media version 6 or later) */ + lsmash_entry_list_t sbgp_list; /* Sample To Group Boxes */ + + isom_cache_t *cache; /* taken over from corresponding 'trak' */ +} isom_traf_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_t; + +/* Track Fragment Random Access Box + * Each entry in this box contains the location and the presentation time of the sync sample. + * Note that not every sync 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 sync sample and no table entry follows. */ + lsmash_entry_list_t *list; /* entry_count corresponds to number_of_entry. */ +} isom_tfra_t; + +typedef struct +{ + /* version == 0: 64bits -> 32bits */ + uint64_t time; /* the presentation time of the sync sample in units defined in the Media Header Box of the associated track + * For segments based on movie sample tables or movie fragments, presentation times are in the movie timeline, + * that is they are composition times after the application of any edit list for the track. + * Note: the definition of segment is portion of an ISO base media file format file, consisting of either + * (a) a movie box, with its associated media data (if any) and other associated boxes + * or + * (b) one or more movie fragment boxes, with their associated media data, and other associated boxes. */ + 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 sync 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 sync sample + * The number ranges from 1 in each Track Fragment Box ('traf'). */ + uint32_t sample_number; /* the sample number that contains the sync 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 sync samples 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 +{ +#define FIRST_MOOF_POS_UNDETERMINED UINT64_MAX + isom_moof_t *movie; /* the address corresponding to the current Movie Fragment Box */ + uint64_t first_moof_pos; + uint64_t pool_size; /* the total sample size in the current movie fragment */ + uint64_t sample_count; /* the number of samples within the current movie fragment */ + lsmash_entry_list_t *pool; /* samples pooled to interleave for the current movie fragment */ +} isom_fragment_manager_t; + +/** **/ + +/* Track Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_tkhd_t *tkhd; /* Track Header Box */ + isom_tapt_t *tapt; /* ISOM: null / QTFF: Track Aperture Mode Dimensions Box */ + isom_edts_t *edts; /* Edit Box */ + isom_tref_t *tref; /* Track Reference Box */ + isom_mdia_t *mdia; /* Media Box */ + isom_udta_t *udta; /* User Data Box */ + isom_meta_t *meta; /* Meta Box */ + + isom_cache_t *cache; + uint32_t related_track_ID; + uint8_t is_chapter; +} isom_trak_t; + +/* Movie Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mvhd_t *mvhd; /* Movie Header Box */ + isom_iods_t *iods; /* MP4: Object Descriptor Box */ + lsmash_entry_list_t trak_list; /* Track Box List */ + isom_udta_t *udta; /* User Data Box */ + isom_ctab_t *ctab; /* ISOM: null / QTFF: Color Table Box */ + isom_meta_t *meta; /* Meta Box */ + isom_mvex_t *mvex; /* Movie Extends Box */ +} isom_moov_t; + +/** Segments + * segment + * portion of an ISO base media file format file, consisting of either (a) a movie box, with its associated media data + * (if any) and other associated boxes or (b) one or more movie fragment boxes, with their associated media data, and + * and other associated boxes + * subsegment + * time interval of a segment formed from movie fragment boxes, that is also a valid segment + * A subsegment is defined as a time interval of the containing (sub)segment, and corresponds to a single range of + * bytes of the containing (sub)segment. The durations of all the subsegments sum to the duration of the containing + * (sub)segment. + **/ +/* Segment Type Box + * Media presentations may be divided into segments for delivery, for example, it is possible (e.g. in HTTP streaming) to + * form files that contain a segment ? or concatenated segments ? which would not necessarily form ISO Base Media file + * format compliant files (e.g. they do not contain a Movie Box). + * If segments are stored in separate files (e.g. on a standard HTTP server) it is recommended that these 'segment files' + * contain a Segment Type Box, which must be first if present, to enable identification of those files, and declaration of + * the specifications with which they are compliant. + * Segment Type Boxes that are not first in a file may be ignored. + * Valid Segment Type Boxes shall be the first box in a segment. + * Note: + * The 'valid' here does not always mean that any brand of that segment has compatibility against other brands of it. + * After concatenations of segments, the result file might contain incompatibilities among brands. */ +typedef isom_ftyp_t isom_styp_t; + +/* Segment Index Box + * This box provides a compact index of one media stream within the media segment to which it applies. + * + * Each Segment Index Box documents how a (sub)segment is divided into one or more subsegments (which may themselves be + * further subdivided using Segment Index boxes). + * + * Each entry in the Segment Index Box contains a reference type that indicates whether the reference points directly to + * the media bytes of a referenced leaf subsegment, which is a subsegment that does not contain any indexing information + * that would enable its further division into subsegments, or to a Segment Index box that describes how the referenced + * subsegment is further subdivided; as a result, the segment may be indexed in a 'hierarchical' or 'daisy-chain' or + * other form by documenting time and byte offset information for other Segment Index Boxes applying to portions of the + * same (sub)segment. + * + * For segments based on ISO Base Media file format (i.e. based on movie sample tables or movie fragments): + * ! an access unit is a sample; + * ! a subsegment is a self-contained set of one or more consecutive movie fragments; a self-contained set contains + * one or more Movie Fragment Boxes with the corresponding Media Data Box(es), and a Media Data Box containing data + * referenced by a Movie Fragment Box must follow that Movie Fragment Box and precede the next Movie Fragment box + * containing information about the same track; + * ! Segment Index Boxes shall be placed before subsegment material they document, that is, before any Movie Fragment + * Box of the documented material of the subsegment; + * ! streams are tracks in the file format, and stream IDs are track IDs; + * ! a subsegment contains a stream access point if a track fragment within the subsegment for the track with track_ID + * equal to reference_ID contains a stream access point; + * ! initialisation data for SAPs consists of the Movie Box; + * ! presentation times are in the movie timeline, that is they are composition times after the application of any edit + * list for the track; + * ! the ISAP is a position exactly pointing to the start of a top-level box, such as a Movie Fragment Box; + * ! a SAP of type 1 or type 2 is indicated as a sync sample; + * ! a SAP of type 3 is marked as a member of a sample group of type 'rap '; + * ! a SAP of type 4 is marked as a member of a sample group of type 'roll' where the value of the roll_distance field + * is greater than 0. + * For SAPs of type 5 and 6, no specific signalling in the ISO Base Media file format is supported. */ +typedef struct +{ + unsigned int reference_type : 1; /* 1: the reference is to a Segment Index Box + * 0: the reference is to media content + * For files based on the ISO Base Media file format, the reference is to a + * Movie Fragment Box. + * If a separate index segment is used, then entries with reference type 1 are + * in the index segment, and entries with reference type 0 are in the media file. */ + unsigned int reference_size : 31; /* the distance in bytes from the first byte of the referenced item to the first + * byte of the next referenced item, or in the case of the last entry, the end of + * the referenced material */ + uint32_t subsegment_duration; /* when the reference is to Segment Index Box, i.e. reference_type is equal to 1: + * this field carries the sum of the subsegment_duration fields in that box; + * when the reference is to a subsegment: + * this field carries the difference between the earliest presentation time of + * any access unit of the reference stream in the next subsegment (or the first + * subsegment of the next segment, if this is the last subsegment of the segment, + * or the end presentation time of the reference stream if this is the last + * subsegment of the stream) and the earliest presentation time of any access + * unit of the reference stream in the referenced subsegment; + * The duration is expressed in the timescale of the enclosing Segment Index Box. */ + unsigned int starts_with_SAP : 1; /* whether the referenced subsegments start with a SAP */ + unsigned int SAP_type : 3; /* a SAP type or the value 0 + * When starting with a SAP, the value 0 means a SAP may be of an unknown type. + * Otherwise, the value 0 means no information of SAPs is provided. */ + unsigned int SAP_delta_time : 28; /* TSAP of the first SAP, in decoding order, in the referenced subsegment for + * the reference stream + * If the referenced subsegments do not contain a SAP, SAP_delta_time is + * reserved with the value 0, otherwise SAP_delta_time is the difference between + * the earliest presentation time of the subsegment, and the TSAP. + * Note that this difference may be zero, in the case that the subsegment starts + * with a SAP. */ +} isom_sidx_referenced_item_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t reference_ID; /* the stream ID for the reference stream + * If this Segment Index box is referenced from a "parent" Segment Index box, the value + * of the value of reference_ID shall be the same as the value of reference_ID of the + * "parent" Segment Index Box. */ + uint32_t timescale; /* the timescale, in ticks per second, for the time and duration fields within this box + * It is recommended that this match the timescale of the reference stream or track. + * For files based on the ISO Base Media file format, that is the timescale field of + * the Media Header Box of the track. */ + /* version == 0: 64bits -> 32bits */ + uint64_t earliest_presentation_time; /* the earliest presentation time of any access unit in the reference stream + * in the first subsegment, in the timescale indicated in the timescale field */ + uint64_t first_offset; /* the distance in bytes, in the file containing media, from the anchor point, + * to the first byte of the indexed material */ + /* */ + uint16_t reserved; /* 0 */ + uint16_t reference_count; /* the number of referenced items */ + lsmash_entry_list_t *list; /* entry_count corresponds to reference_count. */ +} isom_sidx_t; + +/** **/ + +/* File */ +struct lsmash_file_tag +{ + ISOM_FULLBOX_COMMON; /* The 'size' field indicates total file size. + * The 'flags' field indicates file mode. */ + isom_ftyp_t *ftyp; /* File Type Box */ + lsmash_entry_list_t styp_list; /* Segment Type Box List */ + isom_moov_t *moov; /* Movie Box */ + lsmash_entry_list_t sidx_list; /* Segment Index Box List */ + lsmash_entry_list_t moof_list; /* Movie Fragment Box List */ + isom_mdat_t *mdat; /* Media Data Box */ + isom_free_t *free; /* Free Space Box */ + isom_meta_t *meta; /* Meta Box */ + isom_mfra_t *mfra; /* Movie Fragment Random Access Box */ + + lsmash_bs_t *bs; /* bytestream manager */ + isom_fragment_manager_t *fragment; /* movie fragment manager */ + lsmash_entry_list_t *print; + lsmash_entry_list_t *timeline; + lsmash_file_t *initializer; + uint64_t fragment_count; /* the number of movie fragments we created */ + 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. */ + uint32_t brand_count; + uint32_t *compatible_brands; /* the backup of the compatible brands in the File Type Box or the valid Segment Type Box */ + uint8_t fake_file_mode; /* If set to 1, the bytestream manager handles fake-file stream. */ + /* flags for compatibility */ +#define COMPAT_FLAGS_OFFSET offsetof( lsmash_file_t, qt_compatible ) + uint8_t qt_compatible; /* compatibility with QuickTime file format */ + uint8_t isom_compatible; /* compatibility with ISO Base Media file format */ + uint8_t avc_extensions; /* compatibility with AVC extensions */ + uint8_t mp4_version1; /* compatibility with MP4 ver.1 file format */ + uint8_t mp4_version2; /* compatibility with MP4 ver.2 file format */ + uint8_t itunes_movie; /* compatibility with iTunes Movie */ + uint8_t max_3gpp_version; /* maximum 3GPP version */ + uint8_t max_isom_version; /* maximum ISO Base Media file format version */ + uint8_t min_isom_version; /* minimum ISO Base Media file format version */ + uint8_t forbid_tref; /* If set to 1, track reference is forbidden. */ + uint8_t undefined_64_ver; /* If set to 1, 64-bit version fields, e.g. duration, are undefined. */ + uint8_t allow_moof_base; /* If set to 1, default-base-is-moof is available for muxing. */ + uint8_t media_segment; /* If set to 1, this file is a media segment. */ +}; + +/* fake-file stream */ +typedef struct +{ + uint32_t size; + uint8_t *data; + uint32_t pos; +} fake_file_stream_t; + +/* ROOT */ +struct lsmash_root_tag +{ + ISOM_FULLBOX_COMMON; /* The 'file' field contains the address of the current active file. */ + lsmash_entry_list_t file_list; /* the list of all files the ROOT contains */ +}; + +/** **/ + +/* Box types + * + * For developers : + * Be careful about the use of the following defined BOX identifiers. + * Values of array XXXX_BOX_TYPE_YYYY.user.id may be invalid/disappeared value. + * For some systems, e.g., + * uint8_t id[12]; + * memcpy( id, ISOM_BOX_TYPE_MOOV.user.id, 12 * sizeof(uint8_t) ); + * brings about undefined behaviour. + * If you want to access values of array XXXX_BOX_TYPE_YYYY.user.id, you shall do like + * uint8_t id[12]; + * lsmash_box_type_t type = ISOM_BOX_TYPE_MOOV; + * memcpy( id, type.user.id, 12 * sizeof(uint8_t) ); + */ +#define ISOM_BOX_TYPE_ID32 lsmash_form_iso_box_type( LSMASH_4CC( 'I', 'D', '3', '2' ) ) +#define ISOM_BOX_TYPE_ALBM lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'l', 'b', 'm' ) ) +#define ISOM_BOX_TYPE_AUTH lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'u', 't', 'h' ) ) +#define ISOM_BOX_TYPE_BPCC lsmash_form_iso_box_type( LSMASH_4CC( 'b', 'p', 'c', 'c' ) ) +#define ISOM_BOX_TYPE_BUFF lsmash_form_iso_box_type( LSMASH_4CC( 'b', 'u', 'f', 'f' ) ) +#define ISOM_BOX_TYPE_BXML lsmash_form_iso_box_type( LSMASH_4CC( 'b', 'x', 'm', 'l' ) ) +#define ISOM_BOX_TYPE_CCID lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'c', 'i', 'd' ) ) +#define ISOM_BOX_TYPE_CDEF lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'd', 'e', 'f' ) ) +#define ISOM_BOX_TYPE_CLSF lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'l', 's', 'f' ) ) +#define ISOM_BOX_TYPE_CMAP lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'm', 'a', 'p' ) ) +#define ISOM_BOX_TYPE_CO64 lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'o', '6', '4' ) ) +#define ISOM_BOX_TYPE_COLR lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'o', 'l', 'r' ) ) +#define ISOM_BOX_TYPE_CPRT lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'p', 'r', 't' ) ) +#define ISOM_BOX_TYPE_CSLG lsmash_form_iso_box_type( LSMASH_4CC( 'c', 's', 'l', 'g' ) ) +#define ISOM_BOX_TYPE_CTTS lsmash_form_iso_box_type( LSMASH_4CC( 'c', 't', 't', 's' ) ) +#define ISOM_BOX_TYPE_CVRU lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'v', 'r', 'u' ) ) +#define ISOM_BOX_TYPE_DCFD lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'c', 'f', 'D' ) ) +#define ISOM_BOX_TYPE_DINF lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'i', 'n', 'f' ) ) +#define ISOM_BOX_TYPE_DREF lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'r', 'e', 'f' ) ) +#define ISOM_BOX_TYPE_DSCP lsmash_form_iso_box_type( LSMASH_4CC( 'd', 's', 'c', 'p' ) ) +#define ISOM_BOX_TYPE_DSGD lsmash_form_iso_box_type( LSMASH_4CC( 'd', 's', 'g', 'd' ) ) +#define ISOM_BOX_TYPE_DSTG lsmash_form_iso_box_type( LSMASH_4CC( 'd', 's', 't', 'g' ) ) +#define ISOM_BOX_TYPE_EDTS lsmash_form_iso_box_type( LSMASH_4CC( 'e', 'd', 't', 's' ) ) +#define ISOM_BOX_TYPE_ELST lsmash_form_iso_box_type( LSMASH_4CC( 'e', 'l', 's', 't' ) ) +#define ISOM_BOX_TYPE_FECI lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'e', 'c', 'i' ) ) +#define ISOM_BOX_TYPE_FECR lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'e', 'c', 'r' ) ) +#define ISOM_BOX_TYPE_FIIN lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'i', 'i', 'n' ) ) +#define ISOM_BOX_TYPE_FIRE lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'i', 'r', 'e' ) ) +#define ISOM_BOX_TYPE_FPAR lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'p', 'a', 'r' ) ) +#define ISOM_BOX_TYPE_FREE lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'r', 'e', 'e' ) ) +#define ISOM_BOX_TYPE_FRMA lsmash_form_iso_box_type( LSMASH_4CC( 'f', 'r', 'm', 'a' ) ) +#define ISOM_BOX_TYPE_FTYP lsmash_form_iso_box_type( LSMASH_4CC( 'f', 't', 'y', 'p' ) ) +#define ISOM_BOX_TYPE_GITN lsmash_form_iso_box_type( LSMASH_4CC( 'g', 'i', 't', 'n' ) ) +#define ISOM_BOX_TYPE_GNRE lsmash_form_iso_box_type( LSMASH_4CC( 'g', 'n', 'r', 'e' ) ) +#define ISOM_BOX_TYPE_GRPI lsmash_form_iso_box_type( LSMASH_4CC( 'g', 'r', 'p', 'i' ) ) +#define ISOM_BOX_TYPE_HDLR lsmash_form_iso_box_type( LSMASH_4CC( 'h', 'd', 'l', 'r' ) ) +#define ISOM_BOX_TYPE_HMHD lsmash_form_iso_box_type( LSMASH_4CC( 'h', 'm', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_ICNU lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'c', 'n', 'u' ) ) +#define ISOM_BOX_TYPE_IDAT lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'd', 'a', 't' ) ) +#define ISOM_BOX_TYPE_IHDR lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'h', 'd', 'r' ) ) +#define ISOM_BOX_TYPE_IINF lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'i', 'n', 'f' ) ) +#define ISOM_BOX_TYPE_ILOC lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'l', 'o', 'c' ) ) +#define ISOM_BOX_TYPE_IMIF lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'm', 'i', 'f' ) ) +#define ISOM_BOX_TYPE_INFU lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'n', 'f', 'u' ) ) +#define ISOM_BOX_TYPE_IODS lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'o', 'd', 's' ) ) +#define ISOM_BOX_TYPE_IPHD lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'p', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_IPMC lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'p', 'm', 'c' ) ) +#define ISOM_BOX_TYPE_IPRO lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'p', 'r', 'o' ) ) +#define ISOM_BOX_TYPE_IREF lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'r', 'e', 'f' ) ) +#define ISOM_BOX_TYPE_JP lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', ' ', ' ' ) ) +#define ISOM_BOX_TYPE_JP2C lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', '2', 'c' ) ) +#define ISOM_BOX_TYPE_JP2H lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', '2', 'h' ) ) +#define ISOM_BOX_TYPE_JP2I lsmash_form_iso_box_type( LSMASH_4CC( 'j', 'p', '2', 'i' ) ) +#define ISOM_BOX_TYPE_KYWD lsmash_form_iso_box_type( LSMASH_4CC( 'k', 'y', 'w', 'd' ) ) +#define ISOM_BOX_TYPE_LOCI lsmash_form_iso_box_type( LSMASH_4CC( 'l', 'o', 'c', 'i' ) ) +#define ISOM_BOX_TYPE_LRCU lsmash_form_iso_box_type( LSMASH_4CC( 'l', 'r', 'c', 'u' ) ) +#define ISOM_BOX_TYPE_MDAT lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'a', 't' ) ) +#define ISOM_BOX_TYPE_MDHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_MDIA lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'i', 'a' ) ) +#define ISOM_BOX_TYPE_MDRI lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'd', 'r', 'i' ) ) +#define ISOM_BOX_TYPE_MECO lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'c', 'o' ) ) +#define ISOM_BOX_TYPE_MEHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_M7HD lsmash_form_iso_box_type( LSMASH_4CC( 'm', '7', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_MERE lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'r', 'e' ) ) +#define ISOM_BOX_TYPE_META lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 't', 'a' ) ) +#define ISOM_BOX_TYPE_MFHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'f', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_MFRA lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'f', 'r', 'a' ) ) +#define ISOM_BOX_TYPE_MFRO lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'f', 'r', 'o' ) ) +#define ISOM_BOX_TYPE_MINF lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'i', 'n', 'f' ) ) +#define ISOM_BOX_TYPE_MJHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'j', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_MOOF lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'o', 'o', 'f' ) ) +#define ISOM_BOX_TYPE_MOOV lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'o', 'o', 'v' ) ) +#define ISOM_BOX_TYPE_MVCG lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'c', 'g' ) ) +#define ISOM_BOX_TYPE_MVCI lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'c', 'i' ) ) +#define ISOM_BOX_TYPE_MVEX lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'e', 'x' ) ) +#define ISOM_BOX_TYPE_MVHD lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_MVRA lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'v', 'r', 'a' ) ) +#define ISOM_BOX_TYPE_NMHD lsmash_form_iso_box_type( LSMASH_4CC( 'n', 'm', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_OCHD lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'c', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_ODAF lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'a', 'f' ) ) +#define ISOM_BOX_TYPE_ODDA lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'd', 'a' ) ) +#define ISOM_BOX_TYPE_ODHD lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_ODHE lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'h', 'e' ) ) +#define ISOM_BOX_TYPE_ODRB lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'r', 'b' ) ) +#define ISOM_BOX_TYPE_ODRM lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 'r', 'm' ) ) +#define ISOM_BOX_TYPE_ODTT lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'd', 't', 't' ) ) +#define ISOM_BOX_TYPE_OHDR lsmash_form_iso_box_type( LSMASH_4CC( 'o', 'h', 'd', 'r' ) ) +#define ISOM_BOX_TYPE_PADB lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'a', 'd', 'b' ) ) +#define ISOM_BOX_TYPE_PAEN lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'a', 'e', 'n' ) ) +#define ISOM_BOX_TYPE_PCLR lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'c', 'l', 'r' ) ) +#define ISOM_BOX_TYPE_PDIN lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'd', 'i', 'n' ) ) +#define ISOM_BOX_TYPE_PERF lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'e', 'r', 'f' ) ) +#define ISOM_BOX_TYPE_PITM lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'i', 't', 'm' ) ) +#define ISOM_BOX_TYPE_RES lsmash_form_iso_box_type( LSMASH_4CC( 'r', 'e', 's', ' ' ) ) +#define ISOM_BOX_TYPE_RESC lsmash_form_iso_box_type( LSMASH_4CC( 'r', 'e', 's', 'c' ) ) +#define ISOM_BOX_TYPE_RESD lsmash_form_iso_box_type( LSMASH_4CC( 'r', 'e', 's', 'd' ) ) +#define ISOM_BOX_TYPE_RTNG lsmash_form_iso_box_type( LSMASH_4CC( 'r', 't', 'n', 'g' ) ) +#define ISOM_BOX_TYPE_SBGP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'b', 'g', 'p' ) ) +#define ISOM_BOX_TYPE_SCHI lsmash_form_iso_box_type( LSMASH_4CC( 's', 'c', 'h', 'i' ) ) +#define ISOM_BOX_TYPE_SCHM lsmash_form_iso_box_type( LSMASH_4CC( 's', 'c', 'h', 'm' ) ) +#define ISOM_BOX_TYPE_SDEP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 'e', 'p' ) ) +#define ISOM_BOX_TYPE_SDHD lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_SDTP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 't', 'p' ) ) +#define ISOM_BOX_TYPE_SDVP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'd', 'v', 'p' ) ) +#define ISOM_BOX_TYPE_SEGR lsmash_form_iso_box_type( LSMASH_4CC( 's', 'e', 'g', 'r' ) ) +#define ISOM_BOX_TYPE_SGPD lsmash_form_iso_box_type( LSMASH_4CC( 's', 'g', 'p', 'd' ) ) +#define ISOM_BOX_TYPE_SIDX lsmash_form_iso_box_type( LSMASH_4CC( 's', 'i', 'd', 'x' ) ) +#define ISOM_BOX_TYPE_SINF lsmash_form_iso_box_type( LSMASH_4CC( 's', 'i', 'n', 'f' ) ) +#define ISOM_BOX_TYPE_SKIP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'k', 'i', 'p' ) ) +#define ISOM_BOX_TYPE_SMHD lsmash_form_iso_box_type( LSMASH_4CC( 's', 'm', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_SRMB lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'm', 'b' ) ) +#define ISOM_BOX_TYPE_SRMC lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'm', 'c' ) ) +#define ISOM_BOX_TYPE_SRPP lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'p', 'p' ) ) +#define ISOM_BOX_TYPE_STBL lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'b', 'l' ) ) +#define ISOM_BOX_TYPE_STCO lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'c', 'o' ) ) +#define ISOM_BOX_TYPE_STDP lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'd', 'p' ) ) +#define ISOM_BOX_TYPE_STSC lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'c' ) ) +#define ISOM_BOX_TYPE_STSD lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'd' ) ) +#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_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' ) ) +#define ISOM_BOX_TYPE_SUBS lsmash_form_iso_box_type( LSMASH_4CC( 's', 'u', 'b', 's' ) ) +#define ISOM_BOX_TYPE_SWTC lsmash_form_iso_box_type( LSMASH_4CC( 's', 'w', 't', 'c' ) ) +#define ISOM_BOX_TYPE_TFHD lsmash_form_iso_box_type( LSMASH_4CC( 't', 'f', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_TFDT lsmash_form_iso_box_type( LSMASH_4CC( 't', 'f', 'd', 't' ) ) +#define ISOM_BOX_TYPE_TFRA lsmash_form_iso_box_type( LSMASH_4CC( 't', 'f', 'r', 'a' ) ) +#define ISOM_BOX_TYPE_TIBR lsmash_form_iso_box_type( LSMASH_4CC( 't', 'i', 'b', 'r' ) ) +#define ISOM_BOX_TYPE_TIRI lsmash_form_iso_box_type( LSMASH_4CC( 't', 'i', 'r', 'i' ) ) +#define ISOM_BOX_TYPE_TITL lsmash_form_iso_box_type( LSMASH_4CC( 't', 'i', 't', 'l' ) ) +#define ISOM_BOX_TYPE_TKHD lsmash_form_iso_box_type( LSMASH_4CC( 't', 'k', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_TRAF lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'a', 'f' ) ) +#define ISOM_BOX_TYPE_TRAK lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'a', 'k' ) ) +#define ISOM_BOX_TYPE_TREF lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'e', 'f' ) ) +#define ISOM_BOX_TYPE_TREX lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'e', 'x' ) ) +#define ISOM_BOX_TYPE_TRGR lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'g', 'r' ) ) +#define ISOM_BOX_TYPE_TRUN lsmash_form_iso_box_type( LSMASH_4CC( 't', 'r', 'u', 'n' ) ) +#define ISOM_BOX_TYPE_TSEL lsmash_form_iso_box_type( LSMASH_4CC( 't', 's', 'e', 'l' ) ) +#define ISOM_BOX_TYPE_UDTA lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'd', 't', 'a' ) ) +#define ISOM_BOX_TYPE_UINF lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'i', 'n', 'f' ) ) +#define ISOM_BOX_TYPE_ULST lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'l', 's', 't' ) ) +#define ISOM_BOX_TYPE_URL lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'r', 'l', ' ' ) ) +#define ISOM_BOX_TYPE_URN lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'r', 'n', ' ' ) ) +#define ISOM_BOX_TYPE_UUID lsmash_form_iso_box_type( LSMASH_4CC( 'u', 'u', 'i', 'd' ) ) +#define ISOM_BOX_TYPE_VMHD lsmash_form_iso_box_type( LSMASH_4CC( 'v', 'm', 'h', 'd' ) ) +#define ISOM_BOX_TYPE_VWDI lsmash_form_iso_box_type( LSMASH_4CC( 'v', 'w', 'd', 'i' ) ) +#define ISOM_BOX_TYPE_XML lsmash_form_iso_box_type( LSMASH_4CC( 'x', 'm', 'l', ' ' ) ) +#define ISOM_BOX_TYPE_YRRC lsmash_form_iso_box_type( LSMASH_4CC( 'y', 'r', 'r', 'c' ) ) + +#define ISOM_BOX_TYPE_BTRT lsmash_form_iso_box_type( LSMASH_4CC( 'b', 't', 'r', 't' ) ) +#define ISOM_BOX_TYPE_CLAP lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'l', 'a', 'p' ) ) +#define ISOM_BOX_TYPE_PASP lsmash_form_iso_box_type( LSMASH_4CC( 'p', 'a', 's', 'p' ) ) +#define ISOM_BOX_TYPE_SRAT lsmash_form_iso_box_type( LSMASH_4CC( 's', 'r', 'a', 't' ) ) +#define ISOM_BOX_TYPE_STSL lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'l' ) ) + +#define ISOM_BOX_TYPE_FTAB lsmash_form_iso_box_type( LSMASH_4CC( 'f', 't', 'a', 'b' ) ) + +/* iTunes Metadata */ +#define ISOM_BOX_TYPE_DATA lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'a', 't', 'a' ) ) +#define ISOM_BOX_TYPE_ILST lsmash_form_iso_box_type( LSMASH_4CC( 'i', 'l', 's', 't' ) ) +#define ISOM_BOX_TYPE_MEAN lsmash_form_iso_box_type( LSMASH_4CC( 'm', 'e', 'a', 'n' ) ) +#define ISOM_BOX_TYPE_NAME lsmash_form_iso_box_type( LSMASH_4CC( 'n', 'a', 'm', 'e' ) ) + +/* Tyrant extension */ +#define ISOM_BOX_TYPE_CHPL lsmash_form_iso_box_type( LSMASH_4CC( 'c', 'h', 'p', 'l' ) ) + +/* Decoder Specific Info */ +#define ISOM_BOX_TYPE_ALAC lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'l', 'a', 'c' ) ) +#define ISOM_BOX_TYPE_AVCC lsmash_form_iso_box_type( LSMASH_4CC( 'a', 'v', 'c', 'C' ) ) +#define ISOM_BOX_TYPE_DAC3 lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'a', 'c', '3' ) ) +#define ISOM_BOX_TYPE_DAMR lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'a', 'm', 'r' ) ) +#define ISOM_BOX_TYPE_DDTS lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'd', 't', 's' ) ) +#define ISOM_BOX_TYPE_DEC3 lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'e', 'c', '3' ) ) +#define ISOM_BOX_TYPE_DVC1 lsmash_form_iso_box_type( LSMASH_4CC( 'd', 'v', 'c', '1' ) ) +#define ISOM_BOX_TYPE_ESDS lsmash_form_iso_box_type( LSMASH_4CC( 'e', 's', 'd', 's' ) ) +#define ISOM_BOX_TYPE_HVCC lsmash_form_iso_box_type( LSMASH_4CC( 'h', 'v', 'c', 'C' ) ) + +#define QT_BOX_TYPE_ALLF lsmash_form_qtff_box_type( LSMASH_4CC( 'A', 'l', 'l', 'F' ) ) +#define QT_BOX_TYPE_CLEF lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'l', 'e', 'f' ) ) +#define QT_BOX_TYPE_CLIP lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'l', 'i', 'p' ) ) +#define QT_BOX_TYPE_CRGN lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'r', 'g', 'n' ) ) +#define QT_BOX_TYPE_CTAB lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 't', 'a', 'b' ) ) +#define QT_BOX_TYPE_ENOF lsmash_form_qtff_box_type( LSMASH_4CC( 'e', 'n', 'o', 'f' ) ) +#define QT_BOX_TYPE_GMHD lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'm', 'h', 'd' ) ) +#define QT_BOX_TYPE_GMIN lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'm', 'i', 'n' ) ) +#define QT_BOX_TYPE_ILST lsmash_form_qtff_box_type( LSMASH_4CC( 'i', 'l', 's', 't' ) ) +#define QT_BOX_TYPE_IMAP lsmash_form_qtff_box_type( LSMASH_4CC( 'i', 'm', 'a', 'p' ) ) +#define QT_BOX_TYPE_KEYS lsmash_form_qtff_box_type( LSMASH_4CC( 'k', 'e', 'y', 's' ) ) +#define QT_BOX_TYPE_KMAT lsmash_form_qtff_box_type( LSMASH_4CC( 'k', 'm', 'a', 't' ) ) +#define QT_BOX_TYPE_LOAD lsmash_form_qtff_box_type( LSMASH_4CC( 'l', 'o', 'a', 'd' ) ) +#define QT_BOX_TYPE_LOOP lsmash_form_qtff_box_type( LSMASH_4CC( 'L', 'O', 'O', 'P' ) ) +#define QT_BOX_TYPE_MATT lsmash_form_qtff_box_type( LSMASH_4CC( 'm', 'a', 't', 't' ) ) +#define QT_BOX_TYPE_META lsmash_form_qtff_box_type( LSMASH_4CC( 'm', 'e', 't', 'a' ) ) +#define QT_BOX_TYPE_PNOT lsmash_form_qtff_box_type( LSMASH_4CC( 'p', 'n', 'o', 't' ) ) +#define QT_BOX_TYPE_PROF lsmash_form_qtff_box_type( LSMASH_4CC( 'p', 'r', 'o', 'f' ) ) +#define QT_BOX_TYPE_SELO lsmash_form_qtff_box_type( LSMASH_4CC( 'S', 'e', 'l', 'O' ) ) +#define QT_BOX_TYPE_STPS lsmash_form_qtff_box_type( LSMASH_4CC( 's', 't', 'p', 's' ) ) +#define QT_BOX_TYPE_TAPT lsmash_form_qtff_box_type( LSMASH_4CC( 't', 'a', 'p', 't' ) ) +#define QT_BOX_TYPE_TEXT lsmash_form_qtff_box_type( LSMASH_4CC( 't', 'e', 'x', 't' ) ) +#define QT_BOX_TYPE_WLOC lsmash_form_qtff_box_type( LSMASH_4CC( 'W', 'L', 'O', 'C' ) ) + +#define QT_BOX_TYPE_ALIS lsmash_form_qtff_box_type( LSMASH_4CC( 'a', 'l', 'i', 's' ) ) +#define QT_BOX_TYPE_RSRC lsmash_form_qtff_box_type( LSMASH_4CC( 'r', 's', 'r', 'c' ) ) + +#define QT_BOX_TYPE_CHAN lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'h', 'a', 'n' ) ) +#define QT_BOX_TYPE_COLR lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 'o', 'l', 'r' ) ) +#define QT_BOX_TYPE_CSPC lsmash_form_qtff_box_type( LSMASH_4CC( 'c', 's', 'p', 'c' ) ) +#define QT_BOX_TYPE_ENDA lsmash_form_qtff_box_type( LSMASH_4CC( 'e', 'n', 'd', 'a' ) ) +#define QT_BOX_TYPE_FIEL lsmash_form_qtff_box_type( LSMASH_4CC( 'f', 'i', 'e', 'l' ) ) +#define QT_BOX_TYPE_FRMA lsmash_form_qtff_box_type( LSMASH_4CC( 'f', 'r', 'm', 'a' ) ) +#define QT_BOX_TYPE_GAMA lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'a', 'm', 'a' ) ) +#define QT_BOX_TYPE_SGBT lsmash_form_qtff_box_type( LSMASH_4CC( 's', 'g', 'b', 't' ) ) +#define QT_BOX_TYPE_WAVE lsmash_form_qtff_box_type( LSMASH_4CC( 'w', 'a', 'v', 'e' ) ) +#define QT_BOX_TYPE_TERMINATOR lsmash_form_qtff_box_type( 0x00000000 ) + +/* Decoder Specific Info */ +#define QT_BOX_TYPE_ALAC lsmash_form_qtff_box_type( LSMASH_4CC( 'a', 'l', 'a', 'c' ) ) +#define QT_BOX_TYPE_ESDS lsmash_form_qtff_box_type( LSMASH_4CC( 'e', 's', 'd', 's' ) ) +#define QT_BOX_TYPE_GLBL lsmash_form_qtff_box_type( LSMASH_4CC( 'g', 'l', 'b', 'l' ) ) +#define QT_BOX_TYPE_MP4A lsmash_form_qtff_box_type( LSMASH_4CC( 'm', 'p', '4', 'a' ) ) + +/* Pre-defined precedence */ +#define LSMASH_BOX_PRECEDENCE_ISOM_FTYP (LSMASH_BOX_PRECEDENCE_H - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STYP (LSMASH_BOX_PRECEDENCE_H - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_SIDX (LSMASH_BOX_PRECEDENCE_N + 1 * LSMASH_BOX_PRECEDENCE_S) /* shall be placed before any 'moof' of the documented subsegments */ +#define LSMASH_BOX_PRECEDENCE_ISOM_MOOV (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MVHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_IODS (LSMASH_BOX_PRECEDENCE_HM - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TRAK (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TKHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_TAPT (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_CLEF (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_PROF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_ENOF (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_EDTS (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_ELST (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TREF (LSMASH_BOX_PRECEDENCE_N - 3 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TREF_TYPE (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MDIA (LSMASH_BOX_PRECEDENCE_N - 4 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MDHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_HDLR (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MINF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_VMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_SMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_HMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_NMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_GMHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_GMIN (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_TEXT (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_DINF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_DREF (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_DREF_ENTRY (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STBL (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STSD (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_GLBL (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_ESDS (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_ESDS (LSMASH_BOX_PRECEDENCE_HM - 1 * LSMASH_BOX_PRECEDENCE_S) /* preceded by 'frma' and 'mp4a' */ +#define LSMASH_BOX_PRECEDENCE_ISOM_BTRT (LSMASH_BOX_PRECEDENCE_HM - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_COLR (LSMASH_BOX_PRECEDENCE_LP + 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_COLR (LSMASH_BOX_PRECEDENCE_LP + 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_GAMA (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_FIEL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_CSPC (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_SGBT (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) /* 'v216' specific */ +#define LSMASH_BOX_PRECEDENCE_ISOM_CLAP (LSMASH_BOX_PRECEDENCE_LP + 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_PASP (LSMASH_BOX_PRECEDENCE_LP - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STSL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_CHAN (LSMASH_BOX_PRECEDENCE_LP - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_CHAN (LSMASH_BOX_PRECEDENCE_LP - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_WAVE (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_FRMA (LSMASH_BOX_PRECEDENCE_HM + 1 * LSMASH_BOX_PRECEDENCE_S) /* precede any as much as possible */ +#define LSMASH_BOX_PRECEDENCE_QTFF_ENDA (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_MP4A (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_TERMINATOR (LSMASH_BOX_PRECEDENCE_L - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_SRAT (LSMASH_BOX_PRECEDENCE_LP - 1 * LSMASH_BOX_PRECEDENCE_S) /* place at the end for maximum compatibility */ +#define LSMASH_BOX_PRECEDENCE_ISOM_FTAB (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STTS (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_CTTS (LSMASH_BOX_PRECEDENCE_N - 4 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_CSLG (LSMASH_BOX_PRECEDENCE_N - 6 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STSS (LSMASH_BOX_PRECEDENCE_N - 8 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_STPS (LSMASH_BOX_PRECEDENCE_N - 10 * LSMASH_BOX_PRECEDENCE_S) +#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_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) +#define LSMASH_BOX_PRECEDENCE_ISOM_SBGP (LSMASH_BOX_PRECEDENCE_N - 22 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_UDTA (LSMASH_BOX_PRECEDENCE_N - 5 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MEAN (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_NAME (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_DATA (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_KEYS (LSMASH_BOX_PRECEDENCE_N - 1 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_ILST (LSMASH_BOX_PRECEDENCE_N - 2 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_METAITEM (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_CHPL (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_META (LSMASH_BOX_PRECEDENCE_N - 7 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_WLOC (LSMASH_BOX_PRECEDENCE_N - 8 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_LOOP (LSMASH_BOX_PRECEDENCE_N - 9 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_SELO (LSMASH_BOX_PRECEDENCE_N - 10 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_ALLF (LSMASH_BOX_PRECEDENCE_N - 11 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_CPRT (LSMASH_BOX_PRECEDENCE_N - 12 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_QTFF_CTAB (LSMASH_BOX_PRECEDENCE_N - 6 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MVEX (LSMASH_BOX_PRECEDENCE_N - 8 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MEHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TREX (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MOOF (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MFHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TRAF (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TFHD (LSMASH_BOX_PRECEDENCE_HM - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TFDT (LSMASH_BOX_PRECEDENCE_HM - 1 * LSMASH_BOX_PRECEDENCE_S) /* shall be positioned after 'tfhd' and before 'trun' */ +#define LSMASH_BOX_PRECEDENCE_ISOM_TRUN (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MFRA (LSMASH_BOX_PRECEDENCE_L - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_TFRA (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MFRO (LSMASH_BOX_PRECEDENCE_L - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_MDAT (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_FREE (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_SKIP (LSMASH_BOX_PRECEDENCE_N - 0 * LSMASH_BOX_PRECEDENCE_S) + +/* Track reference types */ +typedef enum +{ + ISOM_TREF_TYPE_AVCP = LSMASH_4CC( 'a', 'v', 'c', 'p' ), /* AVC parameter set stream link */ + ISOM_TREF_TYPE_CDSC = LSMASH_4CC( 'c', 'd', 's', 'c' ), /* This track describes the referenced track. */ + ISOM_TREF_TYPE_DPND = LSMASH_4CC( 'd', 'p', 'n', 'd' ), /* This track has an MPEG-4 dependency on the referenced track. */ + ISOM_TREF_TYPE_HIND = LSMASH_4CC( 'h', 'i', 'n', 'd' ), /* Hint dependency */ + ISOM_TREF_TYPE_HINT = LSMASH_4CC( 'h', 'i', 'n', 't' ), /* Links hint track to original media track */ + ISOM_TREF_TYPE_IPIR = LSMASH_4CC( 'i', 'p', 'i', 'r' ), /* This track contains IPI declarations for the referenced track. */ + ISOM_TREF_TYPE_MPOD = LSMASH_4CC( 'm', 'p', 'o', 'd' ), /* This track is an OD track which uses the referenced track as an included elementary stream track. */ + ISOM_TREF_TYPE_SBAS = LSMASH_4CC( 's', 'b', 'a', 's' ), /* Scalable base */ + ISOM_TREF_TYPE_SCAL = LSMASH_4CC( 's', 'c', 'a', 'l' ), /* Scalable extraction */ + ISOM_TREF_TYPE_SWFR = LSMASH_4CC( 's', 'w', 'f', 'r' ), /* AVC Switch from */ + ISOM_TREF_TYPE_SWTO = LSMASH_4CC( 's', 'w', 't', 'o' ), /* AVC Switch to */ + ISOM_TREF_TYPE_SYNC = LSMASH_4CC( 's', 'y', 'n', 'c' ), /* This track uses the referenced track as its synchronization source. */ + ISOM_TREF_TYPE_VDEP = LSMASH_4CC( 'v', 'd', 'e', 'p' ), /* Auxiliary video depth */ + ISOM_TREF_TYPE_VPLX = LSMASH_4CC( 'v', 'p', 'l', 'x' ), /* Auxiliary video parallax */ + + QT_TREF_TYPE_CHAP = LSMASH_4CC( 'c', 'h', 'a', 'p' ), /* Chapter or scene list. Usually references a text track. */ + QT_TREF_TYPE_SCPT = LSMASH_4CC( 's', 'c', 'p', 't' ), /* Transcript. Usually references a text track. */ + QT_TREF_TYPE_SSRC = LSMASH_4CC( 's', 's', 'r', 'c' ), /* Nonprimary source. Indicates that the referenced track should send its data to this track, rather than presenting it. */ + QT_TREF_TYPE_TMCD = LSMASH_4CC( 't', 'm', 'c', 'd' ), /* Time code. Usually references a time code track. */ +} isom_track_reference_type; + +/* Handler types */ +enum isom_handler_type +{ + QT_HANDLER_TYPE_DATA = LSMASH_4CC( 'd', 'h', 'l', 'r' ), + QT_HANDLER_TYPE_MEDIA = LSMASH_4CC( 'm', 'h', 'l', 'r' ), +}; + +enum isom_meta_type +{ + ISOM_META_HANDLER_TYPE_ITUNES_METADATA = LSMASH_4CC( 'm', 'd', 'i', 'r' ), +}; + +/* Data reference types */ +enum isom_data_reference_type +{ + ISOM_REFERENCE_HANDLER_TYPE_URL = LSMASH_4CC( 'u', 'r', 'l', ' ' ), + ISOM_REFERENCE_HANDLER_TYPE_URN = LSMASH_4CC( 'u', 'r', 'n', ' ' ), + + QT_REFERENCE_HANDLER_TYPE_ALIAS = LSMASH_4CC( 'a', 'l', 'i', 's' ), + QT_REFERENCE_HANDLER_TYPE_RESOURCE = LSMASH_4CC( 'r', 's', 'r', 'c' ), + QT_REFERENCE_HANDLER_TYPE_URL = LSMASH_4CC( 'u', 'r', 'l', ' ' ), +}; + +/* Lanuage codes */ +typedef struct +{ + uint16_t mac_value; + uint16_t iso_name; +} isom_language_t; + +static const isom_language_t isom_languages[] = +{ + { 0, ISOM_LANGUAGE_CODE_ENGLISH }, + { 1, ISOM_LANGUAGE_CODE_FRENCH }, + { 2, ISOM_LANGUAGE_CODE_GERMAN }, + { 3, ISOM_LANGUAGE_CODE_ITALIAN }, + { 4, ISOM_LANGUAGE_CODE_DUTCH_M }, + { 5, ISOM_LANGUAGE_CODE_SWEDISH }, + { 6, ISOM_LANGUAGE_CODE_SPANISH }, + { 7, ISOM_LANGUAGE_CODE_DANISH }, + { 8, ISOM_LANGUAGE_CODE_PORTUGUESE }, + { 9, ISOM_LANGUAGE_CODE_NORWEGIAN }, + { 10, ISOM_LANGUAGE_CODE_HEBREW }, + { 11, ISOM_LANGUAGE_CODE_JAPANESE }, + { 12, ISOM_LANGUAGE_CODE_ARABIC }, + { 13, ISOM_LANGUAGE_CODE_FINNISH }, + { 14, ISOM_LANGUAGE_CODE_GREEK }, + { 15, ISOM_LANGUAGE_CODE_ICELANDIC }, + { 16, ISOM_LANGUAGE_CODE_MALTESE }, + { 17, ISOM_LANGUAGE_CODE_TURKISH }, + { 18, ISOM_LANGUAGE_CODE_CROATIAN }, + { 19, ISOM_LANGUAGE_CODE_CHINESE }, + { 20, ISOM_LANGUAGE_CODE_URDU }, + { 21, ISOM_LANGUAGE_CODE_HINDI }, + { 22, ISOM_LANGUAGE_CODE_THAI }, + { 23, ISOM_LANGUAGE_CODE_KOREAN }, + { 24, ISOM_LANGUAGE_CODE_LITHUANIAN }, + { 25, ISOM_LANGUAGE_CODE_POLISH }, + { 26, ISOM_LANGUAGE_CODE_HUNGARIAN }, + { 27, ISOM_LANGUAGE_CODE_ESTONIAN }, + { 28, ISOM_LANGUAGE_CODE_LATVIAN }, + { 29, ISOM_LANGUAGE_CODE_SAMI }, + { 30, ISOM_LANGUAGE_CODE_FAROESE }, + { 32, ISOM_LANGUAGE_CODE_RUSSIAN }, + { 33, ISOM_LANGUAGE_CODE_CHINESE }, + { 34, ISOM_LANGUAGE_CODE_DUTCH }, + { 35, ISOM_LANGUAGE_CODE_IRISH }, + { 36, ISOM_LANGUAGE_CODE_ALBANIAN }, + { 37, ISOM_LANGUAGE_CODE_ROMANIAN }, + { 38, ISOM_LANGUAGE_CODE_CZECH }, + { 39, ISOM_LANGUAGE_CODE_SLOVAK }, + { 40, ISOM_LANGUAGE_CODE_SLOVENIA }, + { 41, ISOM_LANGUAGE_CODE_YIDDISH }, + { 42, ISOM_LANGUAGE_CODE_SERBIAN }, + { 43, ISOM_LANGUAGE_CODE_MACEDONIAN }, + { 44, ISOM_LANGUAGE_CODE_BULGARIAN }, + { 45, ISOM_LANGUAGE_CODE_UKRAINIAN }, + { 46, ISOM_LANGUAGE_CODE_BELARUSIAN }, + { 47, ISOM_LANGUAGE_CODE_UZBEK }, + { 48, ISOM_LANGUAGE_CODE_KAZAKH }, + { 49, ISOM_LANGUAGE_CODE_AZERBAIJANI }, + { 51, ISOM_LANGUAGE_CODE_ARMENIAN }, + { 52, ISOM_LANGUAGE_CODE_GEORGIAN }, + { 53, ISOM_LANGUAGE_CODE_MOLDAVIAN }, + { 54, ISOM_LANGUAGE_CODE_KIRGHIZ }, + { 55, ISOM_LANGUAGE_CODE_TAJIK }, + { 56, ISOM_LANGUAGE_CODE_TURKMEN }, + { 57, ISOM_LANGUAGE_CODE_MONGOLIAN }, + { 59, ISOM_LANGUAGE_CODE_PASHTO }, + { 60, ISOM_LANGUAGE_CODE_KURDISH }, + { 61, ISOM_LANGUAGE_CODE_KASHMIRI }, + { 62, ISOM_LANGUAGE_CODE_SINDHI }, + { 63, ISOM_LANGUAGE_CODE_TIBETAN }, + { 64, ISOM_LANGUAGE_CODE_NEPALI }, + { 65, ISOM_LANGUAGE_CODE_SANSKRIT }, + { 66, ISOM_LANGUAGE_CODE_MARATHI }, + { 67, ISOM_LANGUAGE_CODE_BENGALI }, + { 68, ISOM_LANGUAGE_CODE_ASSAMESE }, + { 69, ISOM_LANGUAGE_CODE_GUJARATI }, + { 70, ISOM_LANGUAGE_CODE_PUNJABI }, + { 71, ISOM_LANGUAGE_CODE_ORIYA }, + { 72, ISOM_LANGUAGE_CODE_MALAYALAM }, + { 73, ISOM_LANGUAGE_CODE_KANNADA }, + { 74, ISOM_LANGUAGE_CODE_TAMIL }, + { 75, ISOM_LANGUAGE_CODE_TELUGU }, + { 76, ISOM_LANGUAGE_CODE_SINHALESE }, + { 77, ISOM_LANGUAGE_CODE_BURMESE }, + { 78, ISOM_LANGUAGE_CODE_KHMER }, + { 79, ISOM_LANGUAGE_CODE_LAO }, + { 80, ISOM_LANGUAGE_CODE_VIETNAMESE }, + { 81, ISOM_LANGUAGE_CODE_INDONESIAN }, + { 82, ISOM_LANGUAGE_CODE_TAGALOG }, + { 83, ISOM_LANGUAGE_CODE_MALAY_ROMAN }, + { 84, ISOM_LANGUAGE_CODE_MAYAY_ARABIC }, + { 85, ISOM_LANGUAGE_CODE_AMHARIC }, + { 87, ISOM_LANGUAGE_CODE_OROMO }, + { 88, ISOM_LANGUAGE_CODE_SOMALI }, + { 89, ISOM_LANGUAGE_CODE_SWAHILI }, + { 90, ISOM_LANGUAGE_CODE_KINYARWANDA }, + { 91, ISOM_LANGUAGE_CODE_RUNDI }, + { 92, ISOM_LANGUAGE_CODE_CHEWA }, + { 93, ISOM_LANGUAGE_CODE_MALAGASY }, + { 94, ISOM_LANGUAGE_CODE_ESPERANTO }, + { 128, ISOM_LANGUAGE_CODE_WELSH }, + { 129, ISOM_LANGUAGE_CODE_BASQUE }, + { 130, ISOM_LANGUAGE_CODE_CATALAN }, + { 131, ISOM_LANGUAGE_CODE_LATIN }, + { 132, ISOM_LANGUAGE_CODE_QUECHUA }, + { 133, ISOM_LANGUAGE_CODE_GUARANI }, + { 134, ISOM_LANGUAGE_CODE_AYMARA }, + { 135, ISOM_LANGUAGE_CODE_TATAR }, + { 136, ISOM_LANGUAGE_CODE_UIGHUR }, + { 137, ISOM_LANGUAGE_CODE_DZONGKHA }, + { 138, ISOM_LANGUAGE_CODE_JAVANESE }, + { UINT16_MAX, 0 } +}; + +/* Color parameters */ +enum isom_color_patameter_type +{ + ISOM_COLOR_PARAMETER_TYPE_NCLX = LSMASH_4CC( 'n', 'c', 'l', 'x' ), /* on-screen colours */ + ISOM_COLOR_PARAMETER_TYPE_RICC = LSMASH_4CC( 'r', 'I', 'C', 'C' ), /* restricted ICC profile */ + ISOM_COLOR_PARAMETER_TYPE_PROF = LSMASH_4CC( 'p', 'r', 'o', 'f' ), /* unrestricted ICC profile */ + + 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 */ +}; + +/* Sample grouping types */ +typedef enum +{ + ISOM_GROUP_TYPE_3GAG = LSMASH_4CC( '3', 'g', 'a', 'g' ), /* Text track3GPP PSS Annex G video buffer parameters */ + ISOM_GROUP_TYPE_ALST = LSMASH_4CC( 'a', 'l', 's', 't' ), /* Alternative startup sequence */ + ISOM_GROUP_TYPE_AVCB = LSMASH_4CC( 'a', 'v', 'c', 'b' ), /* AVC HRD parameters */ + ISOM_GROUP_TYPE_AVLL = LSMASH_4CC( 'a', 'v', 'l', 'l' ), /* AVC Layer */ + ISOM_GROUP_TYPE_AVSS = LSMASH_4CC( 'a', 'v', 's', 's' ), /* AVC Sub Sequence */ + ISOM_GROUP_TYPE_DTRT = LSMASH_4CC( 'd', 't', 'r', 't' ), /* Decode re-timing */ + ISOM_GROUP_TYPE_MVIF = LSMASH_4CC( 'm', 'v', 'i', 'f' ), /* MVC Scalability Information */ + ISOM_GROUP_TYPE_PROL = LSMASH_4CC( 'p', 'r', 'o', 'l' ), /* Pre-roll */ + ISOM_GROUP_TYPE_RAP = LSMASH_4CC( 'r', 'a', 'p', ' ' ), /* Random Access Point */ + ISOM_GROUP_TYPE_RASH = LSMASH_4CC( 'r', 'a', 's', 'h' ), /* Rate Share */ + ISOM_GROUP_TYPE_ROLL = LSMASH_4CC( 'r', 'o', 'l', 'l' ), /* Pre-roll/Post-roll */ + ISOM_GROUP_TYPE_SCIF = LSMASH_4CC( 's', 'c', 'i', 'f' ), /* SVC Scalability Information */ + ISOM_GROUP_TYPE_SCNM = LSMASH_4CC( 's', 'c', 'n', 'm' ), /* AVC/SVC/MVC map groups */ + ISOM_GROUP_TYPE_VIPR = LSMASH_4CC( 'v', 'i', 'p', 'r' ), /* View priority */ +} isom_grouping_type; + +/* wrapper to avoid boring cast */ +#define isom_init_box_common( box, parent, box_type, precedence, destructor ) \ + isom_init_box_common_orig( box, parent, box_type, precedence, (isom_extension_destructor_t)(destructor) ) + +void isom_init_box_common_orig +( + void *box, + void *parent, + lsmash_box_type_t box_type, + uint64_t precedence, + isom_extension_destructor_t destructor +); + +int isom_is_fullbox( void *box ); +int isom_is_lpcm_audio( void *box ); +int isom_is_qt_audio( lsmash_codec_type_t type ); +int isom_is_uncompressed_ycbcr( lsmash_codec_type_t type ); +int isom_is_waveform_audio( lsmash_box_type_t type ); + +size_t isom_skip_box_common( uint8_t **p_data ); + +void isom_bs_put_basebox_common( lsmash_bs_t *bs, isom_box_t *box ); +void isom_bs_put_fullbox_common( lsmash_bs_t *bs, isom_box_t *box ); +void isom_bs_put_box_common( lsmash_bs_t *bs, void *box ); + +#define isom_is_printable_char( c ) ((c) >= 32 && (c) < 128) +#define isom_is_printable_4cc( fourcc ) \ + (isom_is_printable_char( ((fourcc) >> 24) & 0xff ) \ + && isom_is_printable_char( ((fourcc) >> 16) & 0xff ) \ + && isom_is_printable_char( ((fourcc) >> 8) & 0xff ) \ + && isom_is_printable_char( (fourcc) & 0xff )) + +#define isom_4cc2str( fourcc ) (const char [5]){ (fourcc) >> 24, (fourcc) >> 16, (fourcc) >> 8, (fourcc), 0 } + +int isom_check_initializer_present( lsmash_root_t *root ); + +isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID ); +isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID ); +isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID ); +isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID ); +isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ); +isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ); +isom_sgpd_t *isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t *list ); +isom_sbgp_t *isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t *list ); +isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type ); +isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type ); + +isom_dcr_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size ); +void isom_remove_dcr_ps( isom_dcr_ps_entry_t *ps ); + +int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type ); +int isom_setup_iods( isom_moov_t *moov ); + +uint32_t isom_get_sample_count( isom_trak_t *trak ); +isom_sample_pool_t *isom_create_sample_pool( uint64_t size ); +int isom_update_sample_tables( isom_trak_t *trak, lsmash_sample_t *sample, uint32_t *samples_per_packet ); +int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet ); + +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 ); + +int isom_update_tkhd_duration( isom_trak_t *trak ); +int isom_update_bitrate_description( isom_mdia_t *mdia ); +int isom_complement_data_reference( isom_minf_t *minf ); +int isom_check_large_offset_requirement( isom_moov_t *moov, uint64_t meta_size ); +void isom_add_preceding_box_size( isom_moov_t *moov, uint64_t preceding_size ); +int isom_establish_movie( lsmash_file_t *file ); +int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment ); +int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ); + +lsmash_file_t *isom_add_file( lsmash_root_t *root ); +isom_ftyp_t *isom_add_ftyp( lsmash_file_t *file ); +isom_moov_t *isom_add_moov( lsmash_file_t *file ); +isom_mvhd_t *isom_add_mvhd( isom_moov_t *moov ); +isom_iods_t *isom_add_iods( isom_moov_t *moov ); +isom_ctab_t *isom_add_ctab( void *parent_box ); +isom_trak_t *isom_add_trak( isom_moov_t *moov ); +isom_tkhd_t *isom_add_tkhd( isom_trak_t *trak ); +isom_tapt_t *isom_add_tapt( isom_trak_t *trak ); +isom_clef_t *isom_add_clef( isom_tapt_t *tapt ); +isom_prof_t *isom_add_prof( isom_tapt_t *tapt ); +isom_enof_t *isom_add_enof( isom_tapt_t *tapt ); +isom_edts_t *isom_add_edts( isom_trak_t *trak ); +isom_elst_t *isom_add_elst( isom_edts_t *edts ); +isom_tref_t *isom_add_tref( isom_trak_t *trak ); +isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type ); +isom_mdia_t *isom_add_mdia( isom_trak_t *trak ); +isom_mdhd_t *isom_add_mdhd( isom_mdia_t *mdia ); +isom_hdlr_t *isom_add_hdlr( void *parent_box ); +isom_minf_t *isom_add_minf( isom_mdia_t *mdia ); +isom_vmhd_t *isom_add_vmhd( isom_minf_t *minf ); +isom_smhd_t *isom_add_smhd( isom_minf_t *minf ); +isom_hmhd_t *isom_add_hmhd( isom_minf_t *minf ); +isom_nmhd_t *isom_add_nmhd( isom_minf_t *minf ); +isom_gmhd_t *isom_add_gmhd( isom_minf_t *minf ); +isom_gmin_t *isom_add_gmin( isom_gmhd_t *gmhd ); +isom_text_t *isom_add_text( isom_gmhd_t *gmhd ); +isom_dinf_t *isom_add_dinf( void *parent_box ); +isom_dref_t *isom_add_dref( isom_dinf_t *dinf ); +isom_dref_entry_t *isom_add_dref_entry( isom_dref_t *dref, lsmash_box_type_t type ); +isom_stbl_t *isom_add_stbl( isom_minf_t *minf ); +isom_stsd_t *isom_add_stsd( isom_stbl_t *stbl ); +isom_visual_entry_t *isom_add_visual_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ); +isom_audio_entry_t *isom_add_audio_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type ); +isom_qt_text_entry_t *isom_add_qt_text_description( isom_stsd_t *stsd ); +isom_tx3g_entry_t *isom_add_tx3g_description( isom_stsd_t *stsd ); +isom_esds_t *isom_add_esds( void *parent_box ); +isom_glbl_t *isom_add_glbl( void *parent_box ); +isom_clap_t *isom_add_clap( isom_visual_entry_t *visual ); +isom_pasp_t *isom_add_pasp( isom_visual_entry_t *visual ); +isom_colr_t *isom_add_colr( isom_visual_entry_t *visual ); +isom_gama_t *isom_add_gama( isom_visual_entry_t *visual ); +isom_fiel_t *isom_add_fiel( isom_visual_entry_t *visual ); +isom_cspc_t *isom_add_cspc( isom_visual_entry_t *visual ); +isom_sgbt_t *isom_add_sgbt( isom_visual_entry_t *visual ); +isom_stsl_t *isom_add_stsl( isom_visual_entry_t *visual ); +isom_btrt_t *isom_add_btrt( isom_visual_entry_t *visual ); +isom_wave_t *isom_add_wave( isom_audio_entry_t *audio ); +isom_frma_t *isom_add_frma( isom_wave_t *wave ); +isom_enda_t *isom_add_enda( isom_wave_t *wave ); +isom_mp4a_t *isom_add_mp4a( isom_wave_t *wave ); +isom_terminator_t *isom_add_terminator( isom_wave_t *wave ); +isom_chan_t *isom_add_chan( isom_audio_entry_t *audio ); +isom_srat_t *isom_add_srat( isom_audio_entry_t *audio ); +isom_ftab_t *isom_add_ftab( isom_tx3g_entry_t *tx3g ); +isom_stts_t *isom_add_stts( isom_stbl_t *stbl ); +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_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 ); +isom_sgpd_t *isom_add_sgpd( void *parent_box ); +isom_sbgp_t *isom_add_sbgp( void *parent_box ); +isom_stco_t *isom_add_stco( isom_stbl_t *stbl ); +isom_stco_t *isom_add_co64( isom_stbl_t *stbl ); +isom_udta_t *isom_add_udta( void *parent_box ); +isom_cprt_t *isom_add_cprt( isom_udta_t *udta ); +isom_WLOC_t *isom_add_WLOC( isom_udta_t *udta ); +isom_LOOP_t *isom_add_LOOP( isom_udta_t *udta ); +isom_SelO_t *isom_add_SelO( isom_udta_t *udta ); +isom_AllF_t *isom_add_AllF( isom_udta_t *udta ); +isom_chpl_t *isom_add_chpl( isom_udta_t *udta ); +isom_meta_t *isom_add_meta( void *parent_box ); +isom_keys_t *isom_add_keys( isom_meta_t *meta ); +isom_ilst_t *isom_add_ilst( isom_meta_t *meta ); +isom_metaitem_t *isom_add_metaitem( isom_ilst_t *ilst, lsmash_itunes_metadata_item item ); +isom_mean_t *isom_add_mean( isom_metaitem_t *metaitem ); +isom_name_t *isom_add_name( isom_metaitem_t *metaitem ); +isom_data_t *isom_add_data( isom_metaitem_t *metaitem ); +isom_mvex_t *isom_add_mvex( isom_moov_t *moov ); +isom_mehd_t *isom_add_mehd( isom_mvex_t *mvex ); +isom_trex_t *isom_add_trex( isom_mvex_t *mvex ); +isom_moof_t *isom_add_moof( lsmash_file_t *file ); +isom_mfhd_t *isom_add_mfhd( isom_moof_t *moof ); +isom_traf_t *isom_add_traf( isom_moof_t *moof ); +isom_tfhd_t *isom_add_tfhd( isom_traf_t *traf ); +isom_tfdt_t *isom_add_tfdt( isom_traf_t *traf ); +isom_trun_t *isom_add_trun( isom_traf_t *traf ); +isom_mfra_t *isom_add_mfra( lsmash_file_t *file ); +isom_tfra_t *isom_add_tfra( isom_mfra_t *mfra ); +isom_mfro_t *isom_add_mfro( isom_mfra_t *mfra ); +isom_mdat_t *isom_add_mdat( lsmash_file_t *file ); +isom_free_t *isom_add_free( void *parent_box ); +isom_styp_t *isom_add_styp( lsmash_file_t *file ); +isom_sidx_t *isom_add_sidx( lsmash_file_t *file ); + +void isom_remove_sample_description( isom_sample_entry_t *sample ); +void isom_remove_unknown_box( isom_unknown_box_t *unknown_box ); +void isom_remove_sample_pool( isom_sample_pool_t *pool ); + +uint64_t isom_update_box_size( void *box ); + +int isom_add_extension_binary( void *parent_box, lsmash_box_type_t box_type, uint64_t precedence, uint8_t *box_data, uint32_t box_size ); +void isom_remove_all_extension_boxes( lsmash_entry_list_t *extensions ); +isom_box_t *isom_get_extension_box( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ); +void *isom_get_extension_box_format( lsmash_entry_list_t *extensions, lsmash_box_type_t box_type ); +void isom_remove_box_by_itself( void *opaque_box ); + +#endif diff -Nru l-smash-1.9.1/core/chapter.c l-smash-2.3.0/core/chapter.c --- l-smash-1.9.1/core/chapter.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/chapter.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,450 @@ +/***************************************************************************** + * chapter.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include +#include + +#include "box.h" + +#define CHAPTER_BUFSIZE 512 +#define UTF8_BOM "\xEF\xBB\xBF" +#define UTF8_BOM_LENGTH 3 + +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 LSMASH_ERR_INVALID_DATA; + /* check overflow */ + if( hh >= 5124095 + || mm >= 60 + || ss >= 60 ) + return LSMASH_ERR_INVALID_DATA; + /* 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 LSMASH_ERR_NAMELESS; + 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]; + /* get start_time */ + if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) < 0 ) + return LSMASH_ERR_NAMELESS; + char *chapter_time = strchr( buff, '=' ); /* find separator */ + if( !chapter_time++ ) + return LSMASH_ERR_INVALID_DATA; + if( isom_get_start_time( chapter_time, data ) < 0 ) + return LSMASH_ERR_INVALID_DATA; + if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) < 0 ) /* get chapter_name */ + return LSMASH_ERR_NAMELESS; + char *chapter_name = strchr( buff, '=' ); /* find separator */ + if( !chapter_name++ ) + return LSMASH_ERR_INVALID_DATA; + int len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */ + data->chapter_name = (char *)lsmash_malloc( len + 1 ); + if( !data->chapter_name ) + return LSMASH_ERR_MEMORY_ALLOC; + 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]; + if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) < 0 ) /* read newline */ + return LSMASH_ERR_NAMELESS; + char *p_buff = &buff[ !memcmp( buff, UTF8_BOM, UTF8_BOM_LENGTH ) ? UTF8_BOM_LENGTH : 0 ]; /* BOM detection */ + if( isom_get_start_time( p_buff, data ) < 0 ) /* get start_time */ + return LSMASH_ERR_INVALID_DATA; + /* get chapter_name */ + char *chapter_name = strchr( buff, ' ' ); /* find separator */ + if( !chapter_name++ ) + return LSMASH_ERR_INVALID_DATA; + int len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */ + data->chapter_name = (char *)lsmash_malloc( len + 1 ); + if( !data->chapter_name ) + return LSMASH_ERR_MEMORY_ALLOC; + 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 ) +{ + FILE *fp = lsmash_fopen( file_name, "rb" ); + if( !fp ) + { + lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name ); + return NULL; + } + char buff[CHAPTER_BUFSIZE]; + fn_get_chapter_data fnc = NULL; + if( fgets( buff, CHAPTER_BUFSIZE, fp ) != NULL ) + { + char *p_buff = &buff[ !memcmp( buff, UTF8_BOM, UTF8_BOM_LENGTH ) ? UTF8_BOM_LENGTH : 0 ]; /* BOM detection */ + if( !strncmp( p_buff, "CHAPTER", 7 ) ) + fnc = isom_read_simple_chapter; + else if( isdigit( (unsigned char)p_buff[0] ) && isdigit( (unsigned char)p_buff[1] ) && p_buff[2] == ':' + && isdigit( (unsigned char)p_buff[3] ) && isdigit( (unsigned char)p_buff[4] ) && p_buff[5] == ':' ) + fnc = isom_read_minimum_chapter; + else + lsmash_log( NULL, LSMASH_LOG_ERROR, "the chapter file is malformed.\n" ); + } + fclose( fp ); + return fnc; +} + +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 LSMASH_ERR_FUNCTION_PARAM; + isom_chpl_entry_t *data = lsmash_malloc( sizeof(isom_chpl_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->start_time = chap_data->start_time; + data->chapter_name_length = strlen( chap_data->chapter_name ); + data->chapter_name = (char *)lsmash_malloc( data->chapter_name_length + 1 ); + if( !data->chapter_name ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + 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 ) < 0 ) + { + lsmash_free( data->chapter_name ); + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name, int add_bom ) +{ + if( isom_check_initializer_present( root ) < 0 ) + goto error_message; + /* This function should be called after updating of the latest movie duration. */ + lsmash_file_t *file = root->file; + if( !file + || !file->moov + || !file->moov->mvhd + || file->moov->mvhd->timescale == 0 + || file->moov->mvhd->duration == 0 ) + goto error_message; + /* check each line format */ + fn_get_chapter_data fnc = isom_check_chap_line( file_name ); + if( !fnc ) + goto error_message; + FILE *chapter = lsmash_fopen( file_name, "rb" ); + if( !chapter ) + { + lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name ); + goto error_message; + } + if( (!file->moov->udta && !isom_add_udta( file->moov )) + || (!file->moov->udta->chpl && !isom_add_chpl( file->moov->udta )) ) + goto fail; + file->moov->udta->chpl->version = 1; /* version = 1 is popular. */ + isom_chapter_entry_t data = { 0 }; + while( !fnc( chapter, &data ) ) + { + if( add_bom ) + { + char *chapter_name_with_bom = (char *)lsmash_malloc( strlen( data.chapter_name ) + 1 + UTF8_BOM_LENGTH ); + if( !chapter_name_with_bom ) + goto fail2; + sprintf( chapter_name_with_bom, "%s%s", UTF8_BOM, data.chapter_name ); + lsmash_free( data.chapter_name ); + data.chapter_name = chapter_name_with_bom; + } + data.start_time = (data.start_time + 50) / 100; /* convert to 100ns unit */ + if( data.start_time / 1e7 > (double)file->moov->mvhd->duration / file->moov->mvhd->timescale ) + { + lsmash_log( NULL, LSMASH_LOG_WARNING, + "a chapter point exceeding the actual duration detected." + "This chapter point and the following ones (if any) will be cut off.\n" ); + lsmash_free( data.chapter_name ); + break; + } + if( isom_add_chpl_entry( file->moov->udta->chpl, &data ) < 0 ) + goto fail2; + lsmash_freep( &data.chapter_name ); + } + fclose( chapter ); + return 0; +fail2: + lsmash_free( data.chapter_name ); +fail: + fclose( chapter ); +error_message: + lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to set chapter list.\n" ); + return LSMASH_ERR_NAMELESS; +} + +int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name ) +{ + if( isom_check_initializer_present( root ) < 0 ) + goto error_message; + lsmash_file_t *file = root->file; + if( !file + || !file->moov + || !file->moov->mvhd ) + goto error_message; + if( file->forbid_tref || (!file->qt_compatible && !file->itunes_movie) ) + { + lsmash_log( NULL, LSMASH_LOG_ERROR, "reference chapter is not available for this file.\n" ); + goto error_message; + } + FILE *chapter = NULL; /* shut up 'uninitialized' warning */ + /* Create a Track Reference Box. */ + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak ) + { + lsmash_log( NULL, LSMASH_LOG_ERROR, "the specified track ID to apply the chapter doesn't exist.\n" ); + goto error_message; + } + if( !trak->tref && !isom_add_tref( trak ) ) + goto error_message; + /* Create a track_ID for a new chapter track. */ + uint32_t *id = (uint32_t *)lsmash_malloc( sizeof(uint32_t) ); + if( !id ) + goto error_message; + uint32_t chapter_track_ID = *id = file->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 ); + if( !chap ) + goto error_message; /* no need to free id */ + chap->ref_count = 1; + chap->track_ID = id; + /* Create a reference chapter track. */ + if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) ) + goto error_message; + /* 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 ) < 0 ) + 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 = file->max_3gpp_version >= 6 || file->itunes_movie ? ISOM_LANGUAGE_CODE_UNDEFINED : 0; + media_param.MAC_language = 0; + if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) < 0 ) + goto fail; + /* Create a sample description. */ + lsmash_codec_type_t sample_type = file->max_3gpp_version >= 6 || file->itunes_movie + ? ISOM_CODEC_TYPE_TX3G_TEXT + : QT_CODEC_TYPE_TEXT_TEXT; + lsmash_summary_t summary = { .sample_type = sample_type, .data_ref_index = 1 }; + uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, &summary ); + if( !sample_entry ) + goto fail; + /* Check each line format. */ + fn_get_chapter_data fnc = isom_check_chap_line( file_name ); + if( !fnc ) + goto fail; + /* Open chapter format file. */ + chapter = lsmash_fopen( file_name, "rb" ); + if( !chapter ) + { + lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name ); + 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 */ + int is_qt_text = lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TEXT_TEXT ); + uint16_t name_length = strlen( data.chapter_name ); + lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * is_qt_text ); + if( !sample ) + { + lsmash_free( data.chapter_name ); + goto fail; + } + sample->data[0] = (name_length >> 8) & 0xff; + sample->data[1] = name_length & 0xff; + memcpy( sample->data + 2, data.chapter_name, name_length ); + if( is_qt_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 = data.start_time; + sample->cts = data.start_time; + sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + sample->index = sample_entry; + if( lsmash_append_sample( root, chapter_track_ID, sample ) < 0 ) + { + lsmash_free( data.chapter_name ); + goto fail; + } + lsmash_freep( &data.chapter_name ); + } + if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) < 0 ) + goto fail; + isom_trak_t *chapter_trak = isom_get_trak( file, chapter_track_ID ); + if( !chapter_trak ) + goto fail; + fclose( chapter ); + chapter_trak->is_chapter = 1; + chapter_trak->related_track_ID = track_ID; + return 0; +fail: + if( chapter ) + fclose( chapter ); + /* Remove chapter track reference. */ + if( trak->tref->ref_list.tail ) + isom_remove_box_by_itself( trak->tref->ref_list.tail->data ); + if( trak->tref->ref_list.entry_count == 0 ) + isom_remove_box_by_itself( trak->tref ); + /* Remove the reference chapter track attached at tail of the list. */ + if( file->moov->trak_list.tail ) + isom_remove_box_by_itself( file->moov->trak_list.tail->data ); +error_message: + lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to set reference chapter.\n" ); + return LSMASH_ERR_NAMELESS; +} + +uint32_t lsmash_count_tyrant_chapter( lsmash_root_t *root ) +{ + if( isom_check_initializer_present( root ) < 0 + && root->file->initializer->moov + && root->file->initializer->moov->udta + && root->file->initializer->moov->udta->chpl + && root->file->initializer->moov->udta->chpl->list ) + return root->file->initializer->moov->udta->chpl->list->entry_count; + return 0; +} + +char *lsmash_get_tyrant_chapter( lsmash_root_t *root, uint32_t index, double *timestamp ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return NULL; + lsmash_file_t *file = root->file->initializer; + if( !file->moov + || !file->moov->mvhd + || !file->moov->udta + || !file->moov->udta->chpl ) + return NULL; + isom_chpl_t *chpl = file->moov->udta->chpl; + isom_chpl_entry_t *data = (isom_chpl_entry_t *)lsmash_get_entry_data( chpl->list, index ); + if( !data ) + return NULL; + double timescale = chpl->version ? 10000000.0 : file->moov->mvhd->timescale; + *timestamp = data->start_time / timescale; + if( !memcmp( data->chapter_name, UTF8_BOM, UTF8_BOM_LENGTH ) ) + return data->chapter_name + UTF8_BOM_LENGTH; + return data->chapter_name; +} + + +int lsmash_print_chapter_list( lsmash_root_t *root ) +{ + if( isom_check_initializer_present( root ) < 0 + || !(root->file->initializer->flags & LSMASH_FILE_MODE_READ) ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file->initializer; + if( file->moov + && file->moov->udta + && file->moov->udta->chpl ) + { + isom_chpl_t *chpl = file->moov->udta->chpl; + uint32_t timescale; + if( !chpl->version ) + { + if( !file->moov + && !file->moov->mvhd ) + return LSMASH_ERR_NAMELESS; + timescale = file->moov->mvhd->timescale; + } + else + timescale = 10000000; + uint32_t i = 1; + for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next ) + { + isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data; + int64_t start_time = data->start_time / timescale; + int hh = start_time / 3600; + int mm = (start_time / 60) % 60; + int ss = start_time % 60; + int ms = ((data->start_time / (double)timescale) - hh * 3600 - mm * 60 - ss) * 1e3 + 0.5; + if( !memcmp( data->chapter_name, UTF8_BOM, UTF8_BOM_LENGTH ) ) /* detect BOM */ + { + data->chapter_name += UTF8_BOM_LENGTH; +#ifdef _WIN32 + if( i == 1 ) + printf( UTF8_BOM ); /* add BOM on Windows */ +#endif + } + printf( "CHAPTER%02"PRIu32"=%02d:%02d:%02d.%03d\n", i, hh, mm, ss, ms ); + printf( "CHAPTER%02"PRIu32"NAME=%s\n", i++, data->chapter_name ); + } + return 0; + } + lsmash_log( NULL, LSMASH_LOG_ERROR, "this file doesn't have a chapter list.\n" ); + return LSMASH_ERR_NAMELESS; +} diff -Nru l-smash-1.9.1/core/file.c l-smash-2.3.0/core/file.c --- l-smash-1.9.1/core/file.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/file.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,685 @@ +/***************************************************************************** + * file.c + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include + +#include "box.h" +#include "read.h" +#include "fragment.h" + +static void isom_clear_compat_flags +( + lsmash_file_t *file +) +{ + /* Clear flags for compatibility. */ + memset( (int8_t *)file + COMPAT_FLAGS_OFFSET, 0, sizeof(lsmash_file_t) - COMPAT_FLAGS_OFFSET ); + file->min_isom_version = UINT8_MAX; /* undefined value */ +} + +int isom_check_compatibility +( + lsmash_file_t *file +) +{ + if( !file ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_clear_compat_flags( file ); + /* Get the brand container. */ + isom_ftyp_t *ftyp = file->ftyp ? file->ftyp : (isom_ftyp_t *)lsmash_get_entry_data( &file->styp_list, 1 ); + /* Check brand to decide mandatory boxes. */ + if( !ftyp ) + { + /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */ + if( file->moov + && file->moov->iods ) + { + file->mp4_version1 = 1; + file->isom_compatible = 1; + } + else + { + file->qt_compatible = 1; + file->undefined_64_ver = 1; + } + return 0; + } + for( uint32_t i = 0; i <= ftyp->brand_count; i++ ) + { + uint32_t brand = (i == ftyp->brand_count ? ftyp->major_brand : ftyp->compatible_brands[i]); + switch( brand ) + { + case ISOM_BRAND_TYPE_QT : + file->qt_compatible = 1; + break; + case ISOM_BRAND_TYPE_MP41 : + file->mp4_version1 = 1; + break; + case ISOM_BRAND_TYPE_MP42 : + file->mp4_version2 = 1; + break; + case ISOM_BRAND_TYPE_AVC1 : + case ISOM_BRAND_TYPE_ISOM : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 1 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 1 ); + break; + case ISOM_BRAND_TYPE_ISO2 : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 2 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 2 ); + break; + case ISOM_BRAND_TYPE_ISO3 : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 3 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 3 ); + break; + case ISOM_BRAND_TYPE_ISO4 : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 4 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 4 ); + break; + case ISOM_BRAND_TYPE_ISO5 : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 5 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 5 ); + break; + case ISOM_BRAND_TYPE_ISO6 : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 6 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 6 ); + break; + case ISOM_BRAND_TYPE_ISO7 : + file->max_isom_version = LSMASH_MAX( file->max_isom_version, 7 ); + file->min_isom_version = LSMASH_MIN( file->min_isom_version, 7 ); + break; + case ISOM_BRAND_TYPE_M4A : + case ISOM_BRAND_TYPE_M4B : + case ISOM_BRAND_TYPE_M4P : + case ISOM_BRAND_TYPE_M4V : + file->itunes_movie = 1; + break; + case ISOM_BRAND_TYPE_3GP4 : + file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 4 ); + break; + case ISOM_BRAND_TYPE_3GP5 : + file->max_3gpp_version = LSMASH_MAX( file->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 : + file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 6 ); + break; + case ISOM_BRAND_TYPE_3GP7 : + file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 7 ); + break; + case ISOM_BRAND_TYPE_3GP8 : + file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 8 ); + break; + case ISOM_BRAND_TYPE_3GE9 : + case ISOM_BRAND_TYPE_3GF9 : + case ISOM_BRAND_TYPE_3GG9 : + case ISOM_BRAND_TYPE_3GH9 : + case ISOM_BRAND_TYPE_3GM9 : + case ISOM_BRAND_TYPE_3GP9 : + case ISOM_BRAND_TYPE_3GR9 : + case ISOM_BRAND_TYPE_3GS9 : + case ISOM_BRAND_TYPE_3GT9 : + file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 9 ); + break; + default : + break; + } + switch( brand ) + { + 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 : + file->avc_extensions = 1; + break; + case ISOM_BRAND_TYPE_3GP4 : + case ISOM_BRAND_TYPE_3GP5 : + case ISOM_BRAND_TYPE_3GP6 : + case ISOM_BRAND_TYPE_3GP7 : + case ISOM_BRAND_TYPE_3GP8 : + case ISOM_BRAND_TYPE_3GP9 : + file->forbid_tref = 1; + break; + case ISOM_BRAND_TYPE_3GH9 : + case ISOM_BRAND_TYPE_3GM9 : + case ISOM_BRAND_TYPE_DASH : + case ISOM_BRAND_TYPE_DSMS : + case ISOM_BRAND_TYPE_LMSG : + case ISOM_BRAND_TYPE_MSDH : + case ISOM_BRAND_TYPE_MSIX : + case ISOM_BRAND_TYPE_SIMS : + file->media_segment = 1; + break; + default : + break; + } + } + file->isom_compatible = !file->qt_compatible + || file->mp4_version1 + || file->mp4_version2 + || file->itunes_movie + || file->max_3gpp_version; + file->undefined_64_ver = file->qt_compatible || file->itunes_movie; + if( file->flags & LSMASH_FILE_MODE_WRITE ) + { + /* Media Segment is incompatible with ISO Base Media File Format version 4 or former must be compatible with + * version 6 or later since it requires default-base-is-moof and Track Fragment Base Media Decode Time Box. */ + if( file->media_segment && (file->min_isom_version < 5 || (file->max_isom_version && file->max_isom_version < 6)) ) + return LSMASH_ERR_INVALID_DATA; + file->allow_moof_base = (file->max_isom_version >= 5 && file->min_isom_version >= 5) + || (file->max_isom_version == 0 && file->min_isom_version == UINT8_MAX && file->media_segment); + } + return 0; +} + +int isom_check_mandatory_boxes +( + lsmash_file_t *file +) +{ + if( !file + || !file->moov + || !file->moov->mvhd ) + return LSMASH_ERR_INVALID_DATA; + /* A movie requires at least one track. */ + if( !file->moov->trak_list.head ) + return LSMASH_ERR_INVALID_DATA; + for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_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 LSMASH_ERR_INVALID_DATA; + if( file->qt_compatible && !trak->mdia->minf->hdlr ) + return LSMASH_ERR_INVALID_DATA; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stsd->list.head ) + return LSMASH_ERR_INVALID_DATA; + if( !file->fragment + && (!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 LSMASH_ERR_INVALID_DATA; + } + if( !file->fragment ) + return 0; + if( !file->moov->mvex ) + return LSMASH_ERR_INVALID_DATA; + for( lsmash_entry_t *entry = file->moov->mvex->trex_list.head; entry; entry = entry->next ) + if( !entry->data ) /* trex */ + return LSMASH_ERR_INVALID_DATA; + return 0; +} + +int isom_rearrange_data +( + lsmash_file_t *file, + lsmash_adhoc_remux_t *remux, + uint8_t *buf[2], + size_t read_num, + size_t size, + uint64_t read_pos, + uint64_t write_pos, + uint64_t file_size +) +{ + assert( remux ); + /* Copy-pastan */ + int buf_switch = 1; + lsmash_bs_t *bs = file->bs; + int ret; + int64_t ret64; + while( read_num == size ) + { + ret64 = lsmash_bs_write_seek( bs, read_pos, SEEK_SET ); + if( ret64 < 0 ) + return ret64; + ret = lsmash_bs_read_data( bs, buf[buf_switch], &read_num ); + if( ret < 0 ) + return ret; + read_pos = bs->offset; + buf_switch ^= 0x1; + ret64 = lsmash_bs_write_seek( bs, write_pos, SEEK_SET ); + if( ret64 < 0 ) + return ret64; + ret = lsmash_bs_write_data( bs, buf[buf_switch], size ); + if( ret < 0 ) + return ret; + write_pos = bs->offset; + if( remux->func ) + remux->func( remux->param, write_pos, file_size ); // FIXME: + } + ret = lsmash_bs_write_data( bs, buf[buf_switch ^ 0x1], read_num ); + if( ret < 0 ) + return ret; + if( remux->func ) + remux->func( remux->param, file_size, file_size ); // FIXME: + return 0; +} + +static int isom_set_brands +( + lsmash_file_t *file, + lsmash_brand_type major_brand, + uint32_t minor_version, + lsmash_brand_type *brands, + uint32_t brand_count +) +{ + if( brand_count > 50 ) + return LSMASH_ERR_FUNCTION_PARAM; /* We support setting brands up to 50. */ + if( major_brand == 0 || brand_count == 0 ) + { + if( file->flags & LSMASH_FILE_MODE_INITIALIZATION ) + { + /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */ + isom_remove_box_by_itself( file->ftyp ); + /* Anyway we use QTFF as a default file format. */ + isom_clear_compat_flags( file ); + file->qt_compatible = 1; + } + else + { + /* The absence of the Segment Type Box is allowed. + * We set brands from the initialization segment after switching to this segment. */ + for( lsmash_entry_t *entry = file->styp_list.head; entry; entry = entry->next ) + isom_remove_box_by_itself( entry->data ); + if( file->initializer ) + { + /* Copy flags for compatibility. */ + memcpy( (int8_t *)file + COMPAT_FLAGS_OFFSET, file->initializer, sizeof(lsmash_file_t) - COMPAT_FLAGS_OFFSET ); + file->isom_compatible = 1; + file->allow_moof_base = 1; + file->media_segment = 1; + if( file->min_isom_version < 5 ) + file->min_isom_version = 5; + if( file->max_isom_version < 6 ) + file->max_isom_version = 6; + } + } + return 0; + } + isom_ftyp_t *ftyp; + if( file->flags & LSMASH_FILE_MODE_INITIALIZATION ) + { + /* Add File Type Box if absent yet. */ + if( !file->ftyp && !isom_add_ftyp( file ) ) + return LSMASH_ERR_NAMELESS; + ftyp = file->ftyp; + } + else + { + /* Add Segment Type Box if absent yet. */ + ftyp = file->styp_list.head && file->styp_list.head->data + ? (isom_styp_t *)file->styp_list.head->data + : isom_add_styp( file ); + if( !ftyp ) + return LSMASH_ERR_NAMELESS; + } + /* Allocate an array of compatible brands. + * ISO/IEC 14496-12 doesn't forbid the absence of brands in the compatible brand list. + * For a reason of safety, however, we set at least one brand in the list. */ + size_t alloc_size = (brand_count ? brand_count : 1) * sizeof(uint32_t); + lsmash_brand_type *compatible_brands; + if( !file->compatible_brands ) + compatible_brands = lsmash_malloc( alloc_size ); + else + compatible_brands = lsmash_realloc( file->compatible_brands, alloc_size ); + if( !compatible_brands ) + return LSMASH_ERR_MEMORY_ALLOC; + /* Set compatible brands. */ + if( brand_count ) + for( uint32_t i = 0; i < brand_count; i++ ) + compatible_brands[i] = brands[i]; + else + { + /* At least one compatible brand. */ + compatible_brands[0] = major_brand; + brand_count = 1; + } + file->compatible_brands = compatible_brands; + /* Duplicate an array of compatible brands. */ + lsmash_free( ftyp->compatible_brands ); + ftyp->compatible_brands = lsmash_memdup( compatible_brands, alloc_size ); + if( !ftyp->compatible_brands ) + { + lsmash_freep( &file->compatible_brands ); + return LSMASH_ERR_MEMORY_ALLOC; + } + ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8 + brand_count * 4; + ftyp->major_brand = major_brand; + ftyp->minor_version = minor_version; + ftyp->brand_count = brand_count; + file->brand_count = brand_count; + return isom_check_compatibility( file ); +} + +/******************************* + public interfaces +*******************************/ + +void lsmash_discard_boxes +( + lsmash_root_t *root +) +{ + if( !root || !root->file ) + return; + isom_remove_all_extension_boxes( &root->file->extensions ); +} + +int lsmash_open_file +( + const char *filename, + int open_mode, + lsmash_file_parameters_t *param +) +{ + if( !filename || !param ) + return LSMASH_ERR_FUNCTION_PARAM; + char mode[4] = { 0 }; + lsmash_file_mode file_mode = 0; + if( open_mode == 0 ) + { + memcpy( mode, "w+b", 4 ); + file_mode = LSMASH_FILE_MODE_WRITE + | LSMASH_FILE_MODE_BOX + | LSMASH_FILE_MODE_INITIALIZATION + | LSMASH_FILE_MODE_MEDIA; + } +#ifdef LSMASH_DEMUXER_ENABLED + else if( open_mode == 1 ) + { + memcpy( mode, "rb", 3 ); + file_mode = LSMASH_FILE_MODE_READ; + } +#endif + if( file_mode == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + FILE *stream = NULL; + int seekable = 1; + if( !strcmp( filename, "-" ) ) + { + if( file_mode & LSMASH_FILE_MODE_READ ) + { + stream = stdin; + seekable = 0; + } + else if( file_mode & LSMASH_FILE_MODE_WRITE ) + { + stream = stdout; + seekable = 0; + file_mode |= LSMASH_FILE_MODE_FRAGMENTED; + } + } + else + stream = lsmash_fopen( filename, mode ); + if( !stream ) + return LSMASH_ERR_NAMELESS; + memset( param, 0, sizeof(lsmash_file_parameters_t) ); + param->mode = file_mode; + param->opaque = (void *)stream; + param->read = lsmash_fread_wrapper; + param->write = lsmash_fwrite_wrapper; + param->seek = seekable ? lsmash_fseek_wrapper : NULL; + param->major_brand = 0; + param->brands = NULL; + param->brand_count = 0; + param->minor_version = 0; + 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; + return 0; +} + +int lsmash_close_file +( + lsmash_file_parameters_t *param +) +{ + if( !param ) + return LSMASH_ERR_NAMELESS; + if( !param->opaque ) + return 0; + int ret = fclose( (FILE *)param->opaque ); + param->opaque = NULL; + return ret == 0 ? 0 : LSMASH_ERR_UNKNOWN; +} + +lsmash_file_t *lsmash_set_file +( + lsmash_root_t *root, + lsmash_file_parameters_t *param +) +{ + if( !root || !param ) + return NULL; + lsmash_file_t *file = isom_add_file( root ); + if( !file ) + return NULL; + lsmash_bs_t *bs = lsmash_bs_create(); + if( !bs ) + goto fail; + file->bs = bs; + file->flags = param->mode; + file->bs->stream = param->opaque; + file->bs->read = param->read; + file->bs->write = param->write; + file->bs->seek = param->seek; + file->bs->unseekable = (param->seek == NULL); + file->bs->buffer.max_size = param->max_read_size; + file->max_chunk_duration = param->max_chunk_duration; + file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration ); + file->max_chunk_size = param->max_chunk_size; + if( (file->flags & LSMASH_FILE_MODE_WRITE) + && (file->flags & LSMASH_FILE_MODE_BOX) ) + { + /* Construction of Segment Index Box requires seekability at our current implementation. + * If segment is not so large, data rearrangement can be avoided by buffering i.e. the + * seekability is not essential, but at present we don't support buffering of all materials + * within segment. */ + if( (file->flags & LSMASH_FILE_MODE_INDEX) && file->bs->unseekable ) + goto fail; + /* Establish the fragment handler if required. */ + if( file->flags & LSMASH_FILE_MODE_FRAGMENTED ) + { + file->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) ); + if( !file->fragment ) + goto fail; + file->fragment->first_moof_pos = FIRST_MOOF_POS_UNDETERMINED; + file->fragment->pool = lsmash_create_entry_list(); + if( !file->fragment->pool ) + goto fail; + } + else if( file->bs->unseekable ) + /* For unseekable output operations, LSMASH_FILE_MODE_FRAGMENTED shall be set. */ + goto fail; + /* Establish file types. */ + if( isom_set_brands( file, param->major_brand, + param->minor_version, + param->brands, param->brand_count ) < 0 ) + goto fail; + /* Create the movie header if the initialization of the streams is required. */ + if( file->flags & LSMASH_FILE_MODE_INITIALIZATION ) + { + if( !isom_add_moov( file ) + || !isom_add_mvhd( file->moov ) ) + goto fail; + /* Default Movie Header Box. */ + isom_mvhd_t *mvhd = file->moov->mvhd; + mvhd->rate = 0x00010000; + mvhd->volume = 0x0100; + mvhd->matrix[0] = 0x00010000; + mvhd->matrix[4] = 0x00010000; + mvhd->matrix[8] = 0x40000000; + mvhd->next_track_ID = 1; + file->initializer = file; + } + } + if( !root->file ) + root->file = file; + return file; +fail: + isom_remove_box_by_itself( file ); + return NULL; +} + +int64_t lsmash_read_file +( + lsmash_file_t *file, + lsmash_file_parameters_t *param +) +{ +#ifdef LSMASH_DEMUXER_ENABLED + if( !file ) + return (int64_t)LSMASH_ERR_FUNCTION_PARAM; + if( !file->bs ) + return (int64_t)LSMASH_ERR_NAMELESS; + int64_t ret = LSMASH_ERR_NAMELESS; + if( file->flags & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP) ) + { + /* Get the file size if seekable when reading. */ + if( !file->bs->unseekable ) + { + ret = lsmash_bs_read_seek( file->bs, 0, SEEK_END ); + if( ret < 0 ) + return ret; + file->bs->written = ret; + lsmash_bs_read_seek( file->bs, 0, SEEK_SET ); + } + else + ret = 0; + /* Read whole boxes. */ + ret = isom_read_file( file ); + if( ret < 0 ) + return ret; + if( param ) + { + if( file->ftyp ) + { + /* file types */ + isom_ftyp_t *ftyp = file->ftyp; + param->major_brand = ftyp->major_brand ? ftyp->major_brand : ISOM_BRAND_TYPE_QT; + param->minor_version = ftyp->minor_version; + param->brands = file->compatible_brands; + param->brand_count = file->brand_count; + } + else if( file->styp_list.head && file->styp_list.head->data ) + { + /* segment types */ + isom_styp_t *styp = (isom_styp_t *)file->styp_list.head->data; + param->major_brand = styp->major_brand ? styp->major_brand : ISOM_BRAND_TYPE_QT; + param->minor_version = styp->minor_version; + param->brands = file->compatible_brands; + param->brand_count = file->brand_count; + } + else + { + param->major_brand = file->mp4_version1 ? ISOM_BRAND_TYPE_MP41 : ISOM_BRAND_TYPE_QT; + param->minor_version = 0; + param->brands = NULL; + param->brand_count = 0; + } + } + } + return ret; +#else + return (int64_t)LSMASH_ERR_NAMELESS; +#endif +} + +int lsmash_activate_file +( + lsmash_root_t *root, + lsmash_file_t *file +) +{ + if( !root || !file || file->root != root ) + return LSMASH_ERR_FUNCTION_PARAM; + root->file = file; + return 0; +} + +int lsmash_switch_media_segment +( + lsmash_root_t *root, + lsmash_file_t *successor, + lsmash_adhoc_remux_t *remux +) +{ + if( !root || !remux ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *predecessor = root->file; + if( !predecessor || !successor + || predecessor == successor + || predecessor->root != successor->root + || !predecessor->root || !successor->root + || predecessor->root != root || successor->root != root + || (successor->flags & LSMASH_FILE_MODE_INITIALIZATION) + || !(successor->flags & LSMASH_FILE_MODE_MEDIA) + || !(predecessor->flags & LSMASH_FILE_MODE_WRITE) || !(successor->flags & LSMASH_FILE_MODE_WRITE) + || !(predecessor->flags & LSMASH_FILE_MODE_BOX) || !(successor->flags & LSMASH_FILE_MODE_BOX) + || !(predecessor->flags & LSMASH_FILE_MODE_FRAGMENTED) || !(successor->flags & LSMASH_FILE_MODE_FRAGMENTED) + || !(predecessor->flags & LSMASH_FILE_MODE_SEGMENT) || !(successor->flags & LSMASH_FILE_MODE_SEGMENT) + || (!(predecessor->flags & LSMASH_FILE_MODE_MEDIA) && !(predecessor->flags & LSMASH_FILE_MODE_INITIALIZATION)) ) + return LSMASH_ERR_FUNCTION_PARAM; + int ret = isom_finish_final_fragment_movie( predecessor, remux ); + if( ret < 0 ) + return ret; + if( predecessor->flags & LSMASH_FILE_MODE_INITIALIZATION ) + { + if( predecessor->initializer != predecessor ) + return LSMASH_ERR_INVALID_DATA; + successor->initializer = predecessor; + } + else + successor->initializer = predecessor->initializer; + if( !lsmash_get_entry_data( &successor->styp_list, 1 ) ) + { + ret = isom_set_brands( successor, 0, 0, NULL, 0 ); + if( ret < 0 ) + return LSMASH_ERR_NAMELESS; + } + successor->fragment_count = predecessor->fragment_count; + root->file = successor; + return 0; +} diff -Nru l-smash-1.9.1/core/file.h l-smash-2.3.0/core/file.h --- l-smash-1.9.1/core/file.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/file.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,43 @@ +/***************************************************************************** + * file.h + ***************************************************************************** + * Copyright (C) 2014 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. */ + +int isom_check_compatibility +( + lsmash_file_t *file +); + +int isom_check_mandatory_boxes +( + lsmash_file_t *file +); + +int isom_rearrange_data +( + lsmash_file_t *file, + lsmash_adhoc_remux_t *remux, + uint8_t *buf[2], + size_t read_num, + size_t size, + uint64_t read_pos, + uint64_t write_pos, + uint64_t file_size +); diff -Nru l-smash-1.9.1/core/fragment.c l-smash-2.3.0/core/fragment.c --- l-smash-1.9.1/core/fragment.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/fragment.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1751 @@ +/***************************************************************************** + * fragment.c + ***************************************************************************** + * Copyright (C) 2011-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include "box.h" +#include "file.h" +#include "write.h" +#include "fragment.h" + +static isom_sidx_t *isom_get_sidx( lsmash_file_t *file, uint32_t reference_ID ) +{ + if( reference_ID == 0 || !file ) + return NULL; + for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) + { + isom_sidx_t *sidx = (isom_sidx_t *)entry->data; + if( !sidx ) + return NULL; + if( sidx->reference_ID == reference_ID ) + return sidx; + } + return NULL; +} + +static int isom_finish_fragment_movie( lsmash_file_t *file ); + +/* 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( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file + || !file->bs + || !file->fragment ) + return LSMASH_ERR_NAMELESS; + /* Finish and write the current movie fragment before starting a new one. */ + int ret = isom_finish_fragment_movie( file ); + if( ret < 0 ) + return ret; + /* Add a new movie fragment if the current one is not present or not written. */ + if( !file->fragment->movie || (file->fragment->movie->manager & LSMASH_WRITTEN_BOX) ) + { + /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */ + if( file->fragment->movie && file->moof_list.entry_count != 1 ) + return LSMASH_ERR_NAMELESS; + isom_moof_t *moof = isom_add_moof( file ); + if( !isom_add_mfhd( moof ) ) + return LSMASH_ERR_NAMELESS; + file->fragment->movie = moof; + moof->mfhd->sequence_number = ++ file->fragment_count; + if( file->moof_list.entry_count == 1 ) + return 0; + /* Remove the previous movie fragment. */ + if( file->moof_list.head ) + isom_remove_box_by_itself( file->moof_list.head->data ); + } + return 0; +} + +static int isom_set_fragment_overall_duration( lsmash_file_t *file ) +{ + assert( file == file->initializer ); + /* Get the longest duration of the tracks. */ + uint64_t longest_duration = 0; + for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + if( !trak + || !trak->cache + || !trak->cache->fragment + || !trak->mdia + || !trak->mdia->mdhd + || trak->mdia->mdhd->timescale == 0 ) + return LSMASH_ERR_NAMELESS; + 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) * file->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 LSMASH_ERR_NAMELESS; + if( data->segment_duration == ISOM_EDIT_DURATION_IMPLICIT ) + { + uint64_t segment_duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration; + duration += (uint64_t)(((double)segment_duration / trak->mdia->mdhd->timescale) * file->moov->mvhd->timescale); + } + else + duration += data->segment_duration; + } + } + longest_duration = LSMASH_MAX( duration, longest_duration ); + } + isom_mehd_t *mehd = file->moov->mvex->mehd; + mehd->fragment_duration = longest_duration; + mehd->version = 1; + mehd->manager &= ~(LSMASH_PLACEHOLDER | LSMASH_WRITTEN_BOX); /* Update per media segment. */ + isom_update_box_size( mehd ); + /* Write Movie Extends Header Box here. */ + lsmash_bs_t *bs = file->bs; + uint64_t current_pos = bs->offset; + lsmash_bs_write_seek( bs, mehd->pos, SEEK_SET ); + int ret = isom_write_box( bs, (isom_box_t *)mehd ); + lsmash_bs_write_seek( bs, current_pos, SEEK_SET ); + return ret; +} + +static int isom_write_fragment_random_access_info( lsmash_file_t *file ) +{ + assert( file == file->initializer ); + if( !file->mfra ) + return 0; + if( !file->moov->mvex ) + return LSMASH_ERR_NAMELESS; + /* Reconstruct the Movie Fragment Random Access Box. + * All 'time' field in the Track Fragment Random Access Boxes shall reflect edit list. */ + uint32_t movie_timescale = lsmash_get_movie_timescale( file->root ); + if( movie_timescale == 0 ) + return LSMASH_ERR_NAMELESS; /* Division by zero will occur. */ + for( lsmash_entry_t *trex_entry = file->moov->mvex->trex_list.head; trex_entry; trex_entry = trex_entry->next ) + { + isom_trex_t *trex = (isom_trex_t *)trex_entry->data; + if( !trex ) + return LSMASH_ERR_NAMELESS; + /* Get the edit list of the track associated with the trex->track_ID. + * If failed or absent, implicit timeline mapping edit is used, and skip this operation for the track. */ + isom_trak_t *trak = isom_get_trak( file, trex->track_ID ); + if( !trak ) + return LSMASH_ERR_NAMELESS; + if( !trak->edts + || !trak->edts->elst + || !trak->edts->elst->list + || !trak->edts->elst->list->head + || !trak->edts->elst->list->head->data ) + continue; + isom_elst_t *elst = trak->edts->elst; + /* Get the Track Fragment Random Access Boxes of the track associated with the trex->track_ID. + * If failed or absent, skip reconstructing the Track Fragment Random Access Box of the track. */ + isom_tfra_t *tfra = isom_get_tfra( file->mfra, trex->track_ID ); + if( !tfra ) + continue; + /* Reconstruct the Track Fragment Random Access Box. */ + lsmash_entry_t *edit_entry = elst->list->head; + isom_elst_entry_t *edit = edit_entry->data; + uint64_t edit_offset = 0; /* units in media timescale */ + uint32_t media_timescale = lsmash_get_media_timescale( file->root, trex->track_ID ); + for( lsmash_entry_t *rap_entry = tfra->list->head; rap_entry; ) + { + isom_tfra_location_time_entry_t *rap = (isom_tfra_location_time_entry_t *)rap_entry->data; + if( !rap ) + { + /* Irregular case. Drop this entry. */ + lsmash_entry_t *next = rap_entry->next; + lsmash_remove_entry_direct( tfra->list, rap_entry, NULL ); + rap_entry = next; + continue; + } + uint64_t composition_time = rap->time; + /* Skip edits that doesn't need the current sync sample indicated in the Track Fragment Random Access Box. */ + while( edit ) + { + uint64_t segment_duration = edit->segment_duration == ISOM_EDIT_DURATION_IMPLICIT + ? trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration + : ((edit->segment_duration - 1) / movie_timescale + 1) * media_timescale; + if( edit->media_time != ISOM_EDIT_MODE_EMPTY + && composition_time < edit->media_time + segment_duration ) + break; /* This Timeline Mapping Edit might require the current sync sample. + * Note: this condition doesn't cover all cases. + * For instance, matching the both following conditions + * 1. A sync sample isn't in the presentation. + * 2. The other samples, which precede it in the composition timeline, is in the presentation. */ + edit_offset += segment_duration; + edit_entry = edit_entry->next; + if( !edit_entry ) + { + /* No more presentation. */ + edit = NULL; + break; + } + edit = edit_entry->data; + } + if( !edit ) + { + /* No more presentation. + * Drop the rest of sync samples since they are generally absent in the whole presentation. + * Though the exceptions are sync samples with earlier composition time, we ignore them. (SAP type 2: TEPT = TDEC = TSAP < TPTF) + * To support this exception, we need sorting entries of the list by composition times. */ + while( rap_entry ) + { + lsmash_entry_t *next = rap_entry->next; + lsmash_remove_entry_direct( tfra->list, rap_entry, NULL ); + rap_entry = next; + } + break; + } + /* If the sync sample isn't in the presentation, + * we pick the earliest presentation time of the current edit as its presentation time. */ + rap->time = edit_offset; + if( composition_time >= edit->media_time ) + rap->time += composition_time - edit->media_time; + rap_entry = rap_entry->next; + } + tfra->number_of_entry = tfra->list->entry_count; + } + /* Decide the size of the Movie Fragment Random Access Box. */ + if( isom_update_box_size( file->mfra ) == 0 ) + return LSMASH_ERR_NAMELESS; + /* Write the Movie Fragment Random Access Box. */ + return isom_write_box( file->bs, (isom_box_t *)file->mfra ); +} + +static int isom_update_indexed_material_offset +( + lsmash_file_t *file, + isom_sidx_t *last_sidx +) +{ + /* Update the size of each Segment Index Box. */ + for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) + { + isom_sidx_t *sidx = (isom_sidx_t *)entry->data; + if( !sidx ) + continue; + if( isom_update_box_size( sidx ) == 0 ) + return LSMASH_ERR_NAMELESS; + } + /* first_offset: the sum of the size of subsequent Segment Index Boxes + * Be careful about changing the size of them. */ + last_sidx->first_offset = 0; + for( lsmash_entry_t *a_entry = file->sidx_list.head; a_entry && a_entry->data != last_sidx; a_entry = a_entry->next ) + { + isom_sidx_t *a = (isom_sidx_t *)a_entry->data; + a->first_offset = 0; + for( lsmash_entry_t *b_entry = a_entry->next; b_entry; b_entry = b_entry->next ) + { + isom_sidx_t *b = (isom_sidx_t *)b_entry->data; + a->first_offset += b->size; + } + } + return 0; +} + +static int isom_write_segment_indexes +( + lsmash_file_t *file, + lsmash_adhoc_remux_t *remux +) +{ + /* Update the size of each Segment Index Box and establish the offset from the anchor point to the indexed material. */ + int ret; + if( (ret = isom_update_indexed_material_offset( file, (isom_sidx_t *)file->sidx_list.tail->data )) < 0 ) + return ret; + /* Get the total size of all Segment Index Boxes. */ + uint64_t total_sidx_size = 0; + for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) + { + isom_sidx_t *sidx = (isom_sidx_t *)entry->data; + if( !sidx ) + continue; + total_sidx_size += sidx->size; + } + /* The buffer size must be at least total_sidx_size * 2. */ + size_t buffer_size = total_sidx_size * 2; + if( remux->buffer_size > buffer_size ) + buffer_size = remux->buffer_size; + /* Split to 2 buffers. */ + uint8_t *buf[2] = { NULL, NULL }; + if( (buf[0] = (uint8_t *)lsmash_malloc( buffer_size )) == NULL ) + return LSMASH_ERR_MEMORY_ALLOC; + size_t size = buffer_size / 2; + buf[1] = buf[0] + size; + /* Seek to the beginning of the first Movie Fragment Box i.e. the first subsegment within this media segment. */ + lsmash_bs_t *bs = file->bs; + int64_t ret64; + if( (ret64 = lsmash_bs_write_seek( bs, file->fragment->first_moof_pos, SEEK_SET )) < 0 ) + { + ret = ret64; + goto fail; + } + size_t read_num = size; + lsmash_bs_read_data( bs, buf[0], &read_num ); + uint64_t read_pos = bs->offset; + /* Write the Segment Index Boxes actually here. */ + if( (ret64 = lsmash_bs_write_seek( bs, file->fragment->first_moof_pos, SEEK_SET )) < 0 ) + { + ret = ret64; + goto fail; + } + for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) + { + isom_sidx_t *sidx = (isom_sidx_t *)entry->data; + if( !sidx ) + continue; + if( (ret = isom_write_box( file->bs, (isom_box_t *)sidx )) < 0 ) + goto fail; + } + /* Rearrange subsequent data. */ + uint64_t write_pos = bs->offset; + uint64_t total = file->size + total_sidx_size; + if( (ret = isom_rearrange_data( file, remux, buf, read_num, size, read_pos, write_pos, total )) < 0 ) + goto fail; + file->size += total_sidx_size; + lsmash_freep( &buf[0] ); + /* Update 'moof_offset' of each entry within the Track Fragment Random Access Boxes. */ + if( file->mfra ) + for( lsmash_entry_t *entry = file->mfra->tfra_list.head; entry; entry = entry->next ) + { + isom_tfra_t *tfra = (isom_tfra_t *)entry->data; + if( !tfra ) + continue; + for( lsmash_entry_t *rap_entry = tfra->list->head; rap_entry; rap_entry = rap_entry->next ) + { + isom_tfra_location_time_entry_t *rap = (isom_tfra_location_time_entry_t *)rap_entry->data; + if( !rap ) + continue; + rap->moof_offset += total_sidx_size; + } + } + return 0; +fail: + lsmash_free( buf[0] ); + return ret; +} + +int isom_finish_final_fragment_movie +( + lsmash_file_t *file, + lsmash_adhoc_remux_t *remux +) +{ + /* Output the final movie fragment. */ + int ret; + if( (ret = isom_finish_fragment_movie( file )) < 0 ) + return ret; + if( file->bs->unseekable ) + return 0; + /* Write Segment Index Boxes. + * This occurs only when the initial movie has no samples. + * We don't consider updating of chunk offsets within initial movie sample table here. + * This is reasonable since DASH requires no samples in the initial movie. + * This implementation is not suitable for live-streaming. + + To support live-streaming, it is good to use daisy-chained index. */ + if( (file->flags & LSMASH_FILE_MODE_MEDIA) + && (file->flags & LSMASH_FILE_MODE_INDEX) + && (file->flags & LSMASH_FILE_MODE_SEGMENT) ) + { + if( !remux ) + return LSMASH_ERR_FUNCTION_PARAM; + if( (ret = isom_write_segment_indexes( file, remux )) < 0 ) + return ret; + } + /* Write the overall random access information at the tail of the movie if this file is self-contained. */ + if( (ret = isom_write_fragment_random_access_info( file->initializer )) < 0 ) + return ret; + /* Set overall duration of the movie. */ + return isom_set_fragment_overall_duration( file->initializer ); +} + +#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_file_t *file ) +{ + assert( file == file->initializer ); + if( !isom_add_mvex( file->moov ) ) + return LSMASH_ERR_NAMELESS; + if( !file->bs->unseekable ) + { + if( !isom_add_mehd( file->moov->mvex ) ) + return LSMASH_ERR_NAMELESS; + file->moov->mvex->mehd->manager |= LSMASH_PLACEHOLDER; + } + for( lsmash_entry_t *trak_entry = file->moov->trak_list.head; trak_entry; trak_entry = trak_entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)trak_entry->data; + if( !trak + || !trak->cache + || !trak->tkhd + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return LSMASH_ERR_NAMELESS; + 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 LSMASH_ERR_NAMELESS; + isom_trex_t *trex = isom_add_trex( file->moov->mvex ); + if( !trex ) + return LSMASH_ERR_NAMELESS; + 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 LSMASH_ERR_NAMELESS; + ++ 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_file_t *file ) +{ + assert( file == file->initializer ); + /* Don't write the random access info at the end of the file if unseekable or not self-contained. */ + if( file->bs->unseekable + || !(file->flags & LSMASH_FILE_MODE_BOX) + || !(file->flags & LSMASH_FILE_MODE_INITIALIZATION) + || !(file->flags & LSMASH_FILE_MODE_MEDIA) + || (file->flags & LSMASH_FILE_MODE_SEGMENT) ) + return 0; + if( !isom_add_mfra( file ) + || !isom_add_mfro( file->mfra ) ) + return LSMASH_ERR_NAMELESS; + return 0; +} + +static int isom_output_fragment_media_data( lsmash_file_t *file ) +{ + isom_fragment_manager_t *fragment = file->fragment; + /* If there is no available Media Data Box to write samples, add and write a new one. */ + if( fragment->sample_count ) + { + if( !file->mdat && !isom_add_mdat( file ) ) + return LSMASH_ERR_NAMELESS; + file->mdat->manager &= ~(LSMASH_INCOMPLETE_BOX | LSMASH_WRITTEN_BOX); + int ret = isom_write_box( file->bs, (isom_box_t *)file->mdat ); + if( ret < 0 ) + return ret; + file->size += file->mdat->size; + file->mdat->size = 0; + file->mdat->media_size = 0; + } + lsmash_remove_entries( fragment->pool, isom_remove_sample_pool ); + fragment->pool_size = 0; + fragment->sample_count = 0; + return 0; +} + +static int isom_finish_fragment_initial_movie( lsmash_file_t *file ) +{ + assert( file == file->initializer ); + if( !file->moov ) + return LSMASH_ERR_NAMELESS; + isom_moov_t *moov = file->moov; + int ret; + for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + if( !trak + || !trak->cache + || !trak->tkhd + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return LSMASH_ERR_NAMELESS; + if( (ret = isom_complement_data_reference( trak->mdia->minf )) < 0 ) + return ret; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( isom_get_sample_count( trak ) ) + { + /* Add stss box if any samples aren't sync sample. */ + if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) ) + return -1; + if( (ret = isom_update_tkhd_duration( trak )) < 0 ) + return ret; + } + else + trak->tkhd->duration = 0; + if( (ret = isom_update_bitrate_description( trak->mdia )) < 0 ) + return ret; + /* Complete the last sample groups within tracks in the initial movie. */ + if( trak->cache->rap ) + { + isom_sgpd_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); + if( !sgpd ) + return LSMASH_ERR_NAMELESS; + if( (ret = isom_rap_grouping_established( trak->cache->rap, 1, sgpd, 0 )) < 0 ) + return ret; + lsmash_freep( &trak->cache->rap ); + } + if( trak->cache->roll.pool ) + { + isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &stbl->sbgp_list ); + if( !sbgp ) + return LSMASH_ERR_NAMELESS; + if( (ret = isom_all_recovery_completed( sbgp, trak->cache->roll.pool )) < 0 ) + return ret; + } + } + if( file->mp4_version1 == 1 && (ret = isom_setup_iods( moov )) < 0 ) + return ret; + if( (ret = isom_create_fragment_overall_default_settings( file )) < 0 + || (ret = isom_prepare_random_access_info ( file )) < 0 + || (ret = isom_establish_movie ( file )) < 0 ) + return ret; + /* stco->co64 conversion, depending on last chunk's offset */ + uint64_t meta_size = file->meta ? file->meta->size : 0; + if( (ret = isom_check_large_offset_requirement( moov, meta_size )) < 0 ) + return ret; + /* Now, the amount of the offset is fixed. apply it to stco/co64 */ + uint64_t preceding_size = moov->size + meta_size; + isom_add_preceding_box_size( moov, preceding_size ); + /* Write File Type Box here if it was not written yet. */ + if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) ) + { + if( isom_write_box( file->bs, (isom_box_t *)file->ftyp ) < 0 ) + return -1; + file->size += file->ftyp->size; + } + /* Write Movie Box. */ + if( (ret = isom_write_box( file->bs, (isom_box_t *)file->moov )) < 0 + || (ret = isom_write_box( file->bs, (isom_box_t *)file->meta )) < 0 ) + return ret; + file->size += preceding_size; + /* Output samples. */ + if( (ret = isom_output_fragment_media_data( file )) < 0 ) + return ret; + /* Revert the number of samples in tracks to 0. */ + for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + if( trak->cache->fragment ) + trak->cache->fragment->sample_count = 0; + } + return 0; +} + +/* Return 1 if there is diffrence, otherwise return 0. */ +static int isom_compare_sample_flags( isom_sample_flags_t *a, isom_sample_flags_t *b ) +{ + return (a->reserved != b->reserved) + || (a->is_leading != b->is_leading) + || (a->sample_depends_on != b->sample_depends_on) + || (a->sample_is_depended_on != b->sample_is_depended_on) + || (a->sample_has_redundancy != b->sample_has_redundancy) + || (a->sample_padding_value != b->sample_padding_value) + || (a->sample_is_non_sync_sample != b->sample_is_non_sync_sample) + || (a->sample_degradation_priority != b->sample_degradation_priority); +} + +static int isom_make_segment_index_entry +( + lsmash_file_t *file, + isom_moof_t *moof +) +{ + /* Make the index of this subsegment. */ + for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)entry->data; + isom_tfhd_t *tfhd = traf->tfhd; + isom_fragment_t *track_fragment = traf->cache->fragment; + isom_subsegment_t *subsegment = &track_fragment->subsegment; + isom_sidx_t *sidx = isom_get_sidx( file, tfhd->track_ID ); + isom_trak_t *trak = isom_get_trak( file->initializer, tfhd->track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd ) + return LSMASH_ERR_NAMELESS; + assert( traf->tfdt ); + if( !sidx ) + { + sidx = isom_add_sidx( file ); + if( !sidx ) + return LSMASH_ERR_NAMELESS; + sidx->reference_ID = tfhd->track_ID; + sidx->timescale = trak->mdia->mdhd->timescale; + sidx->reserved = 0; + sidx->reference_count = 0; + int ret = isom_update_indexed_material_offset( file, sidx ); + if( ret < 0 ) + return ret; + } + /* One pair of a Movie Fragment Box with an associated Media Box per subsegment. */ + isom_sidx_referenced_item_t *data = lsmash_malloc( sizeof(isom_sidx_referenced_item_t) ); + if( !data ) + return LSMASH_ERR_NAMELESS; + if( lsmash_add_entry( sidx->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + sidx->reference_count = sidx->list->entry_count; + data->reference_type = 0; /* media */ + data->reference_size = file->size - moof->pos; + /* presentation */ + uint64_t TSAP; + uint64_t TDEC; + uint64_t TEPT; + uint64_t TPTF; + uint64_t composition_duration = subsegment->largest_cts - subsegment->smallest_cts + track_fragment->last_duration; + int subsegment_in_presentation; /* If set to 1, TEPT is available. */ + int first_rp_in_presentation; /* If set to 1, both TSAP and TDEC are available. */ + int first_sample_in_presentation; /* If set to 1, TPTF is available. */ + if( trak->edts && trak->edts->elst && trak->edts->elst->list ) + { + /**-- Explicit edits --**/ + const isom_elst_t *elst = trak->edts->elst; + const isom_elst_entry_t *edit = NULL; + uint32_t movie_timescale = file->initializer->moov->mvhd->timescale; + uint64_t pts = subsegment->segment_duration; + /* This initialization is redundant since these are unused when uninitialized + * and are initialized always in used cases, but unclever compilers may + * complain that these variables may be uninitialized. */ + TSAP = 0; + TDEC = 0; + TEPT = 0; + TPTF = 0; + /* */ + subsegment_in_presentation = 0; + first_rp_in_presentation = 0; + first_sample_in_presentation = 0; + for( lsmash_entry_t *elst_entry = elst->list->head; elst_entry; elst_entry = elst_entry->next ) + { + edit = (isom_elst_entry_t *)elst_entry->data; + if( !edit ) + continue; + uint64_t edit_end_pts; + uint64_t edit_end_cts; + if( edit->segment_duration == ISOM_EDIT_DURATION_IMPLICIT + || (elst->version == 0 && edit->segment_duration == ISOM_EDIT_DURATION_UNKNOWN32) + || (elst->version == 1 && edit->segment_duration == ISOM_EDIT_DURATION_UNKNOWN64) ) + { + edit_end_cts = UINT64_MAX; + edit_end_pts = UINT64_MAX; + } + else + { + if( edit->segment_duration ) + { + double segment_duration = edit->segment_duration * ((double)sidx->timescale / movie_timescale); + edit_end_cts = edit->media_time + (uint64_t)(segment_duration * ((double)edit->media_rate / (1 << 16))); + edit_end_pts = pts + (uint64_t)segment_duration; + } + else + { + uint64_t segment_duration = composition_duration; + if( edit->media_time > subsegment->smallest_cts ) + { + if( subsegment->largest_cts + track_fragment->last_duration > edit->media_time ) + segment_duration -= edit->media_time - subsegment->smallest_cts; + else + segment_duration = 0; + } + edit_end_cts = edit->media_time + (uint64_t)(segment_duration * ((double)edit->media_rate / (1 << 16))); + edit_end_pts = pts + (uint64_t)segment_duration; + } + } + if( edit->media_time == ISOM_EDIT_MODE_EMPTY ) + { + pts = edit_end_pts; + continue; + } + if( (subsegment->smallest_cts >= edit->media_time && subsegment->smallest_cts < edit_end_cts) + || (subsegment->largest_cts >= edit->media_time && subsegment->largest_cts < edit_end_cts) ) + { + /* This subsegment is present in this edit. */ + double rate = (double)edit->media_rate / (1 << 16); + uint64_t start_time = LSMASH_MAX( subsegment->smallest_cts, edit->media_time ); + if( sidx->reference_count == 1 ) + sidx->earliest_presentation_time = pts; + if( subsegment_in_presentation == 0 ) + { + subsegment_in_presentation = 1; + if( subsegment->smallest_cts >= edit->media_time ) + TEPT = pts + (uint64_t)((subsegment->smallest_cts - start_time) / rate); + else + TEPT = pts; + } + if( first_rp_in_presentation == 0 + && ((subsegment->first_ed_cts >= edit->media_time && subsegment->first_ed_cts < edit_end_cts) + || (subsegment->first_rp_cts >= edit->media_time && subsegment->first_rp_cts < edit_end_cts)) ) + { + /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */ + first_rp_in_presentation = 1; + if( subsegment->first_ed_cts >= edit->media_time && subsegment->first_ed_cts < edit_end_cts ) + TSAP = pts + (uint64_t)((subsegment->first_ed_cts - start_time) / rate); + else + TSAP = pts; + TDEC = TSAP; + } + if( first_sample_in_presentation == 0 + && subsegment->first_cts >= edit->media_time && subsegment->first_cts < edit_end_cts ) + { + first_sample_in_presentation = 1; + TPTF = pts + (uint64_t)((subsegment->first_cts - start_time) / rate); + } + uint64_t subsegment_end_pts = pts + (uint64_t)(composition_duration / rate); + pts = LSMASH_MIN( edit_end_pts, subsegment_end_pts ); + /* Update subsegment_duration. */ + data->subsegment_duration = pts - subsegment->segment_duration; + } + else + /* This subsegment is not present in this edit. */ + pts = edit_end_pts; + } + } + else + { + /**-- Implicit edit --**/ + if( sidx->reference_count == 1 ) + sidx->earliest_presentation_time = subsegment->smallest_cts; + data->subsegment_duration = composition_duration; + /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */ + TSAP = subsegment->first_rp_cts; + TDEC = subsegment->first_rp_cts; + TEPT = subsegment->smallest_cts; + TPTF = subsegment->first_cts; + subsegment_in_presentation = 1; + first_rp_in_presentation = 1; + first_sample_in_presentation = 1; + } + if( subsegment->first_ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE + || subsegment->first_ra_number == 0 + || subsegment->first_rp_number == 0 + || subsegment_in_presentation == 0 + || first_rp_in_presentation == 0 ) + { + /* No SAP in this subsegment. */ + data->starts_with_SAP = 0; + data->SAP_type = 0; + data->SAP_delta_time = 0; + } + else + { + data->starts_with_SAP = (subsegment->first_ra_number == 1); + data->SAP_type = 0; + data->SAP_delta_time = TSAP - TEPT; + /* Decide SAP_type. */ + if( first_sample_in_presentation ) + { + if( TEPT == TDEC && TDEC == TSAP && TSAP == TPTF ) + data->SAP_type = 1; + else if( TEPT == TDEC && TDEC == TSAP && TSAP < TPTF ) + data->SAP_type = 2; + else if( TEPT < TDEC && TDEC == TSAP && TSAP <= TPTF ) + data->SAP_type = 3; + else if( TEPT <= TPTF && TPTF < TDEC && TDEC == TSAP ) + data->SAP_type = 4; + } + if( data->SAP_type == 0 ) + { + if( TEPT == TDEC && TDEC < TSAP ) + data->SAP_type = 5; + else if( TEPT < TDEC && TDEC < TSAP ) + data->SAP_type = 6; + } + } + subsegment->segment_duration += data->subsegment_duration; + subsegment->first_ed_cts = UINT64_MAX; + subsegment->first_rp_cts = UINT64_MAX; + subsegment->first_rp_number = 0; + subsegment->first_ra_number = 0; + subsegment->first_ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE; + subsegment->decodable = 0; + } + return 0; +} + +static int isom_finish_fragment_movie +( + lsmash_file_t *file +) +{ + if( !file->fragment + || !file->fragment->pool ) + return LSMASH_ERR_NAMELESS; + isom_moof_t *moof = file->fragment->movie; + if( !moof ) + { + if( file == file->initializer ) + return isom_finish_fragment_initial_movie( file ); + else + return 0; /* No movie fragment to be finished. */ + } + /* Don't write the current movie fragment if containing no track fragments. + * This is a requirement of DASH Media Segment. */ + if( !moof->traf_list.head + || !moof->traf_list.head->data ) + return 0; + /* 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_t *traf = (isom_traf_t *)entry->data; + if( !traf + || !traf->tfhd + || !traf->file + || !traf->file->initializer + || !traf->file->initializer->moov + || !traf->file->initializer->moov->mvex ) + return LSMASH_ERR_NAMELESS; + isom_tfhd_t *tfhd = traf->tfhd; + isom_trex_t *trex = isom_get_trex( file->initializer->moov->mvex, tfhd->track_ID ); + if( !trex ) + return LSMASH_ERR_NAMELESS; + 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_t *trun = (isom_trun_t *)trun_entry->data; + if( !trun || trun->sample_count == 0 ) + return LSMASH_ERR_NAMELESS; + isom_sample_flags_t *sample_flags; + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) + { + if( !trun->optional ) + return LSMASH_ERR_NAMELESS; + 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 LSMASH_ERR_NAMELESS; + 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_t *trun = (isom_trun_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( !isom_compare_sample_flags( &trun->first_sample_flags, &tfhd->default_sample_flags ) ) + 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( isom_compare_sample_flags( &tfhd->default_sample_flags, &representative_sample_flags ) ) + useful_default_sample_flags = 0; + if( !isom_compare_sample_flags( &trun->first_sample_flags, &representative_sample_flags ) ) + 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( isom_compare_sample_flags( &representative_sample_flags, &row->sample_flags ) ) + { + 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( !isom_compare_sample_flags( &tfhd->default_sample_flags, &trex->default_sample_flags ) ) + tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT; + } + /* Complete the last sample groups in the previous track fragments. */ + int ret; + for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)entry->data; + if( traf->cache->rap ) + { + isom_sgpd_t *sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP ); + if( !sgpd ) + return LSMASH_ERR_NAMELESS; + if( (ret = isom_rap_grouping_established( traf->cache->rap, 1, sgpd, 1 )) < 0 ) + return ret; + lsmash_freep( &traf->cache->rap ); + } + if( traf->cache->roll.pool ) + { + isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &traf->sbgp_list ); + if( !sbgp ) + return LSMASH_ERR_NAMELESS; + if( (ret = isom_all_recovery_completed( sbgp, traf->cache->roll.pool )) < 0 ) + return ret; + } + } + /* Establish Movie Fragment Box. + * We write exactly one Media Data Box starting immediately after the corresponding Movie Fragment Box. */ + if( file->allow_moof_base ) + { + /* In this branch, we use default-base-is-moof flag, which indicates implicit base_data_offsets originate in the + * first byte of each enclosing Movie Fragment Box. + * We use the sum of the size of the Movie Fragment Box and the offset from the size field of the Media Data Box to + * the type field of it as the data_offset of the first track run like the following. + * + * _____________ _ offset := 0 + * | | | + * | m | s i z e | + * | |_________| + * | o | | + * | | t y p e | + * | o |_________| + * | | | + * | f | d a t a | + * |___|_________|_ offset := the size of the Movie Fragment Box + * | | | + * | m | s i z e | + * | |_________| + * | d | | + * | | t y p e | + * | a |_________|_ offset := the data_offset of the first track run + * | | | + * | t | d a t a | + * |___|_________|_ offset := the size of a subsegment containing exactly one movie fragment + * + * For a pair of one Movie Fragment Box and one Media Data Box, placed in this order, implicit base_data_offsets + * indicated by the absence of both base-data-offset-present and default-base-is-moof are somewhat complicated + * since the implicit base_data_offset of the current track fragment is defined by the end of the data of the + * previous track fragment and the data_offset of the track runs could be negative value because of interleaving + * track runs or something other reasons. + * In contrast, implicit base_data_offsets indicated by default-base-is-moof are simple since the base_data_offset + * of each track fragment is always constant for that pair and has no dependency on other track fragments. + */ + for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)entry->data; + traf->tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF; + traf->tfhd->base_data_offset = file->size; /* not written actually though */ + for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next ) + { + /* Here, data_offset is always greater than zero. */ + isom_trun_t *trun = trun_entry->data; + trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; + } + } + /* Consider the update of tr_flags here. */ + if( isom_update_box_size( moof ) == 0 ) + return LSMASH_ERR_NAMELESS; + /* 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_t *traf = (isom_traf_t *)entry->data; + for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next ) + { + isom_trun_t *trun = trun_entry->data; + trun->data_offset += moof->size + ISOM_BASEBOX_COMMON_SIZE; + } + } + } + else + { + /* In this branch, we use explicit base_data_offset. */ + for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)entry->data; + traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT; + } + /* Consider the update of tf_flags here. */ + if( isom_update_box_size( moof ) == 0 ) + return LSMASH_ERR_NAMELESS; + /* 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_t *traf = (isom_traf_t *)entry->data; + traf->tfhd->base_data_offset = file->size + moof->size + ISOM_BASEBOX_COMMON_SIZE; + } + } + /* Write Movie Fragment Box and its children. */ + moof->pos = file->size; + if( (ret = isom_write_box( file->bs, (isom_box_t *)moof )) < 0 ) + return ret; + if( file->fragment->first_moof_pos == FIRST_MOOF_POS_UNDETERMINED ) + file->fragment->first_moof_pos = moof->pos; + file->size += moof->size; + /* Output samples. */ + if( (ret = isom_output_fragment_media_data( file )) < 0 ) + return ret; + /* Revert the number of samples in track fragments to 0. */ + for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)entry->data; + if( traf->cache->fragment ) + traf->cache->fragment->sample_count = 0; + } + if( !(file->flags & LSMASH_FILE_MODE_INDEX) || file->max_isom_version < 6 ) + return 0; + return isom_make_segment_index_entry( file, moof ); +} + +#undef GET_MOST_USED + +static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_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 = lsmash_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 ) < 0 ) + { + lsmash_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( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file->fragment + || !file->fragment->movie + || !file->initializer->moov ) + return LSMASH_ERR_NAMELESS; + isom_trak_t *trak = isom_get_trak( file->initializer, track_ID ); + if( !trak + || !trak->tkhd ) + return LSMASH_ERR_NAMELESS; + isom_trex_t *trex = isom_get_trex( file->initializer->moov->mvex, track_ID ); + if( !trex ) + return LSMASH_ERR_NAMELESS; + isom_moof_t *moof = file->fragment->movie; + isom_traf_t *traf = isom_get_traf( moof, track_ID ); + if( traf ) + return LSMASH_ERR_NAMELESS; + traf = isom_add_traf( moof ); + if( !isom_add_tfhd( traf ) ) + return LSMASH_ERR_NAMELESS; + 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; +} + +int isom_set_fragment_last_duration +( + isom_traf_t *traf, + uint32_t last_duration +) +{ + isom_tfhd_t *tfhd = traf->tfhd; + if( !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_t *trex = isom_get_trex( traf->file->initializer->moov->mvex, tfhd->track_ID ); + if( !trex ) + return LSMASH_ERR_NAMELESS; + 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_t *trun = (isom_trun_t *)traf->trun_list.tail->data; + if( trun->sample_count == 1 + && traf->trun_list.entry_count == 1 ) + { + isom_trex_t *trex = isom_get_trex( traf->file->initializer->moov->mvex, tfhd->track_ID ); + if( !trex ) + return LSMASH_ERR_NAMELESS; + 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 LSMASH_ERR_NAMELESS; + row->sample_duration = last_duration; + } + traf->cache->fragment->last_duration = last_duration; + return 0; +} + +int isom_append_fragment_track_run +( + lsmash_file_t *file, + isom_chunk_t *chunk +) +{ + if( !chunk->pool || chunk->pool->size == 0 ) + return 0; + isom_fragment_manager_t *fragment = file->fragment; + /* Move data in the pool of the current track fragment to the pool of the current movie fragment. + * Empty the pool of current track. We don't delete data of samples here. */ + if( lsmash_add_entry( fragment->pool, chunk->pool ) < 0 ) + return LSMASH_ERR_MEMORY_ALLOC; + fragment->sample_count += chunk->pool->sample_count; + fragment->pool_size += chunk->pool->size; + chunk->pool = isom_create_sample_pool( chunk->pool->size ); + return chunk->pool ? 0 : LSMASH_ERR_MEMORY_ALLOC; +} + +static int isom_output_fragment_cache( isom_traf_t *traf ) +{ + isom_cache_t *cache = traf->cache; + int ret = isom_append_fragment_track_run( traf->file, &cache->chunk ); + if( ret < 0 ) + return ret; + for( lsmash_entry_t *entry = traf->sgpd_list.head; entry; entry = entry->next ) + { + isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data; + if( !sgpd ) + return LSMASH_ERR_NAMELESS; + switch( sgpd->grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + { + isom_rap_group_t *group = cache->rap; + if( !group ) + { + if( traf->file->fragment ) + continue; + else + return LSMASH_ERR_NAMELESS; + } + if( !group->random_access ) + continue; + group->random_access->num_leading_samples_known = 1; + break; + } + case ISOM_GROUP_TYPE_ROLL : + case ISOM_GROUP_TYPE_PROL : + if( !cache->roll.pool ) + { + if( traf->file->fragment ) + continue; + else + return LSMASH_ERR_NAMELESS; + } + isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &traf->sbgp_list ); + if( !sbgp ) + return LSMASH_ERR_NAMELESS; + if( (ret = isom_all_recovery_completed( sbgp, cache->roll.pool )) < 0 ) + return ret; + break; + default : + break; + } + } + return 0; +} + +int isom_flush_fragment_pooled_samples +( + lsmash_file_t *file, + uint32_t track_ID, + uint32_t last_sample_duration +) +{ + isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID ); + if( !traf ) + /* No samples. We don't return as an error here since user might call the flushing function even if the + * current movie fragment has no track fragment with this track_ID. */ + return 0; + if( !traf->cache + || !traf->cache->fragment ) + return LSMASH_ERR_NAMELESS; + if( 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_t *trun = (isom_trun_t *)traf->trun_list.tail->data; + if( file->fragment->pool_size ) + trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; + trun->data_offset = file->fragment->pool_size; + } + int ret = isom_output_fragment_cache( traf ); + if( ret < 0 ) + return ret; + return isom_set_fragment_last_duration( traf, last_sample_duration ); +} + +/* 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_t *traf, isom_trex_t *trex, uint32_t duration ) +{ + isom_tfhd_t *tfhd = traf->tfhd; + isom_trun_t *trun = (isom_trun_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 LSMASH_ERR_NAMELESS; + /* OK. The previous sample exists in the previous track run in the same track fragment. */ + trun = (isom_trun_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 LSMASH_ERR_NAMELESS; + 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.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC); + flags.sample_degradation_priority = 0; + return flags; +} + +static int isom_update_fragment_sample_tables( isom_traf_t *traf, lsmash_sample_t *sample ) +{ + isom_tfhd_t *tfhd = traf->tfhd; + isom_trex_t *trex = isom_get_trex( traf->file->initializer->moov->mvex, tfhd->track_ID ); + if( !trex ) + return LSMASH_ERR_NAMELESS; + lsmash_file_t *file = traf->file; + isom_cache_t *cache = traf->cache; + isom_chunk_t *current = &cache->chunk; + if( !current->pool ) + { + /* Very initial settings, just once per track */ + current->pool = isom_create_sample_pool( 0 ); + if( !current->pool ) + return LSMASH_ERR_MEMORY_ALLOC; + } + /* Create a new track run if the duration exceeds max_chunk_duration. + * Old one will be appended to the pool of this movie fragment. */ + uint32_t media_timescale = lsmash_get_media_timescale( file->root, tfhd->track_ID ); + if( !media_timescale ) + return LSMASH_ERR_NAMELESS; + int delimit = (file->max_chunk_duration < ((double)(sample->dts - current->first_dts) / media_timescale)) + || (file->max_chunk_size < (current->pool->size + sample->length)); + isom_trun_t *trun = NULL; + if( !traf->trun_list.entry_count || delimit ) + { + if( delimit + && 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_t *)traf->trun_list.tail->data; + if( file->fragment->pool_size ) + trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; + trun->data_offset = file->fragment->pool_size; + } + trun = isom_add_trun( traf ); + if( !trun ) + return LSMASH_ERR_NAMELESS; + } + else + { + if( !traf->trun_list.tail + || !traf->trun_list.tail->data ) + return LSMASH_ERR_NAMELESS; + trun = (isom_trun_t *)traf->trun_list.tail->data; + } + isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample ); + if( ++trun->sample_count == 1 ) + { + if( traf->trun_list.entry_count == 1 ) + { + /* This track fragment isn't empty-duration-fragment any more. */ + tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY; + /* Set up sample_description_index in this track fragment. */ + if( sample->index != trex->default_sample_description_index ) + tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT; + tfhd->sample_description_index = current->sample_description_index = sample->index; + /* Set up default_sample_size used in this track fragment. */ + tfhd->default_sample_size = sample->length; + /* Set up default_sample_flags used in this track fragment. + * Note: we decide an appropriate default value at the end of this movie fragment. */ + tfhd->default_sample_flags = sample_flags; + /* Set up random access information if this sample is a sync sample. + * We inform only the first sample in each movie fragment. */ + if( file->mfra && (sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) + { + isom_tfra_t *tfra = isom_get_tfra( file->mfra, tfhd->track_ID ); + if( !tfra ) + { + tfra = isom_add_tfra( file->mfra ); + if( !tfra ) + return LSMASH_ERR_NAMELESS; + tfra->track_ID = tfhd->track_ID; + } + if( !tfra->list ) + { + tfra->list = lsmash_create_entry_list(); + if( !tfra->list ) + return LSMASH_ERR_MEMORY_ALLOC; + } + isom_tfra_location_time_entry_t *rap = lsmash_malloc( sizeof(isom_tfra_location_time_entry_t) ); + if( !rap ) + return LSMASH_ERR_MEMORY_ALLOC; + rap->time = sample->cts; /* Set composition timestamp temporally. + * At the end of the whole movie, this will be reset as presentation time. */ + rap->moof_offset = file->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 ) < 0 ) + { + lsmash_free( rap ); + return LSMASH_ERR_MEMORY_ALLOC; + } + 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 ); + } + /* Set up the base media decode time of this track fragment. + * This feature is available under ISO Base Media version 6 or later. + * For DASH Media Segment, each Track Fragment Box shall contain a Track Fragment Base Media Decode Time Box. */ + if( file->max_isom_version >= 6 || file->media_segment ) + { + assert( !traf->tfdt ); + if( !isom_add_tfdt( traf ) ) + return LSMASH_ERR_NAMELESS; + if( sample->dts > UINT32_MAX ) + traf->tfdt->version = 1; + traf->tfdt->baseMediaDecodeTime = sample->dts; + } + } + trun->first_sample_flags = sample_flags; + current->first_dts = sample->dts; + } + /* Update the optional rows in the current track run except for sample_duration if needed. */ + if( sample->length != tfhd->default_sample_size ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT; + if( isom_compare_sample_flags( &sample_flags, &tfhd->default_sample_flags ) ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT; + uint32_t sample_composition_time_offset = sample->cts - sample->dts; + if( sample_composition_time_offset ) + { + trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT; + /* Check if negative composition time offset is present. */ + isom_timestamp_t *ts_cache = &cache->timestamp; + if( (sample->cts + ts_cache->ctd_shift) < sample->dts ) + { + if( file->max_isom_version < 6 ) + return LSMASH_ERR_INVALID_DATA; /* Negative composition time offset is invalid. */ + if( (sample->dts - sample->cts) > INT32_MAX ) + return LSMASH_ERR_INVALID_DATA; /* Overflow */ + ts_cache->ctd_shift = sample->dts - sample->cts; + if( trun->version == 0 && file->max_isom_version >= 6 ) + trun->version = 1; + } + } + if( trun->flags ) + { + isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count ); + if( !row ) + return LSMASH_ERR_NAMELESS; + row->sample_size = sample->length; + row->sample_flags = sample_flags; + row->sample_composition_time_offset = sample_composition_time_offset; + } + /* Set up the sample groupings for random access. */ + int ret; + if( (ret = isom_group_random_access( (isom_box_t *)traf, sample )) < 0 + || (ret = isom_group_roll_recovery( (isom_box_t *)traf, sample )) < 0 ) + return ret; + /* 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 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 LSMASH_ERR_INVALID_DATA; + uint32_t sample_duration = sample->dts - prev_dts; + if( (ret = isom_update_fragment_previous_sample_duration( traf, trex, sample_duration )) < 0 ) + return ret; + } + /* Cache */ + cache->timestamp.dts = sample->dts; + cache->timestamp.cts = sample->cts; + cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts ); + isom_subsegment_t *subsegment = &cache->fragment->subsegment; + if( trun->sample_count == 1 && traf->trun_list.entry_count == 1 ) + { + subsegment->first_cts = sample->cts; + subsegment->largest_cts = sample->cts; + subsegment->smallest_cts = sample->cts; + } + else + { + subsegment->largest_cts = LSMASH_MAX( sample->cts, subsegment->largest_cts ); + subsegment->smallest_cts = LSMASH_MIN( sample->cts, subsegment->smallest_cts ); + } + if( subsegment->first_ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + { + subsegment->first_ra_flags = sample->prop.ra_flags; + subsegment->first_ra_number = cache->fragment->sample_count + 1; + if( sample->prop.ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP) ) + { + subsegment->first_rp_number = subsegment->first_ra_number; + subsegment->first_rp_cts = sample->cts; + subsegment->first_ed_cts = sample->cts; + subsegment->decodable = 1; + } + } + else if( subsegment->decodable ) + { + if( (subsegment->first_ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)) + ? (sample->prop.leading == ISOM_SAMPLE_IS_DECODABLE_LEADING) + : (subsegment->first_ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START) ) + subsegment->first_ed_cts = LSMASH_MIN( sample->cts, subsegment->first_ed_cts ); + else + subsegment->decodable = 0; + } + return delimit; +} + +static int isom_append_fragment_sample_internal_initial( isom_trak_t *trak, lsmash_sample_t *sample ) +{ + /* 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. */ + uint32_t samples_per_packet; + int ret = isom_update_sample_tables( trak, sample, &samples_per_packet ); + if( ret < 0 ) + return ret; + else if( ret == 1 ) + isom_append_fragment_track_run( trak->file, &trak->cache->chunk ); + /* Add a new sample into the pool of this track fragment. */ + if( (ret = isom_pool_sample( trak->cache->chunk.pool, sample, samples_per_packet )) < 0 ) + return ret; + trak->cache->fragment->has_samples = 1; + trak->cache->fragment->sample_count += 1; + return 0; +} + +static int isom_append_fragment_sample_internal( isom_traf_t *traf, lsmash_sample_t *sample ) +{ + /* 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. */ + int ret = isom_update_fragment_sample_tables( traf, sample ); + if( ret < 0 ) + return ret; + else if( ret == 1 ) + isom_append_fragment_track_run( traf->file, &traf->cache->chunk ); + /* Add a new sample into the pool of this track fragment. */ + if( (ret = isom_pool_sample( traf->cache->chunk.pool, sample, 1 )) < 0 ) + return ret; + traf->cache->fragment->has_samples = 1; + traf->cache->fragment->sample_count += 1; + return 0; +} + +int isom_append_fragment_sample +( + lsmash_file_t *file, + uint32_t track_ID, + lsmash_sample_t *sample +) +{ + isom_fragment_manager_t *fragment = file->fragment; + assert( fragment && fragment->pool ); + isom_trak_t *trak = isom_get_trak( file->initializer, track_ID ); + if( !trak + || !trak->file + || !trak->cache + || !trak->cache->fragment + || !trak->tkhd + || !trak->mdia + || !trak->mdia->mdhd + || trak->mdia->mdhd->timescale == 0 + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd + || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list ) + return LSMASH_ERR_NAMELESS; + /* Write the Segment Type Box here if required and if it was not written yet. */ + if( !(file->flags & LSMASH_FILE_MODE_INITIALIZATION) + && file->styp_list.head && file->styp_list.head->data ) + { + isom_styp_t *styp = (isom_styp_t *)file->styp_list.head->data; + if( !(styp->manager & LSMASH_WRITTEN_BOX) ) + { + int ret = isom_write_box( file->bs, (isom_box_t *)styp ); + if( ret < 0 ) + return ret; + file->size += styp->size; + } + } + int (*append_sample_func)( void *, lsmash_sample_t * ) = NULL; + void *track_fragment = NULL; + if( !fragment->movie ) + { + /* Forbid adding a sample into the initial movie if requiring compatibility with Media Segment. */ + if( file->media_segment ) + return LSMASH_ERR_NAMELESS; + append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial; + track_fragment = trak; + } + else + { + isom_traf_t *traf = isom_get_traf( fragment->movie, track_ID ); + if( !traf ) + { + traf = isom_add_traf( fragment->movie ); + if( !isom_add_tfhd( traf ) ) + return LSMASH_ERR_NAMELESS; + 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; + int ret; + if( (traf->cache->fragment->rap_grouping && (ret = isom_add_sample_grouping( (isom_box_t *)traf, ISOM_GROUP_TYPE_RAP )) < 0) + || (traf->cache->fragment->roll_grouping && (ret = isom_add_sample_grouping( (isom_box_t *)traf, ISOM_GROUP_TYPE_ROLL )) < 0) ) + return ret; + } + else if( !traf->file + || !traf->file->initializer + || !traf->file->initializer->moov + || !traf->file->initializer->moov->mvex + || !traf->cache + || !traf->tfhd ) + return LSMASH_ERR_NAMELESS; + 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 LSMASH_ERR_NAMELESS; + if( isom_is_lpcm_audio( sample_entry ) ) + { + uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket; + if( sample->length == frame_size ) + return append_sample_func( track_fragment, sample ); + else if( sample->length < frame_size ) + return LSMASH_ERR_INVALID_DATA; + /* Append samples splitted into each LPCMFrame. */ + uint64_t dts = sample->dts; + uint64_t cts = sample->cts; + for( uint32_t offset = 0; offset < sample->length; offset += frame_size ) + { + lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size ); + if( !lpcm_sample ) + return LSMASH_ERR_MEMORY_ALLOC; + 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; + int ret = append_sample_func( track_fragment, lpcm_sample ); + if( ret < 0 ) + { + lsmash_delete_sample( lpcm_sample ); + return ret; + } + } + lsmash_delete_sample( sample ); + return 0; + } + return append_sample_func( track_fragment, sample ); +} diff -Nru l-smash-1.9.1/core/fragment.h l-smash-2.3.0/core/fragment.h --- l-smash-1.9.1/core/fragment.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/fragment.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,53 @@ +/***************************************************************************** + * fragment.h + ***************************************************************************** + * Copyright (C) 2011-2014 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. */ + +int isom_finish_final_fragment_movie +( + lsmash_file_t *file, + lsmash_adhoc_remux_t *remux +); + +int isom_set_fragment_last_duration +( + isom_traf_t *traf, + uint32_t last_duration +); + +int isom_append_fragment_track_run +( + lsmash_file_t *file, + isom_chunk_t *chunk +); + +int isom_flush_fragment_pooled_samples +( + lsmash_file_t *file, + uint32_t track_ID, + uint32_t last_sample_duration +); + +int isom_append_fragment_sample +( + lsmash_file_t *file, + uint32_t track_ID, + lsmash_sample_t *sample +); diff -Nru l-smash-1.9.1/core/isom.c l-smash-2.3.0/core/isom.c --- l-smash-1.9.1/core/isom.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/isom.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,4301 @@ +/***************************************************************************** + * isom.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "box.h" +#include "file.h" +#include "write.h" +#include "fragment.h" +#include "read.h" + +#include "codecs/mp4a.h" +#include "codecs/mp4sys.h" +#include "codecs/description.h" + +/*---- ----*/ +int isom_check_initializer_present( lsmash_root_t *root ) +{ + if( !root + || !root->file + || !root->file->initializer ) + return LSMASH_ERR_NAMELESS; + return 0; +} + +isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID ) +{ + if( track_ID == 0 + || !file + || file != file->initializer + || !file->moov ) + return NULL; + for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + if( !trak + || !trak->tkhd ) + return NULL; + if( trak->tkhd->track_ID == track_ID ) + return trak; + } + return NULL; +} + +isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID ) +{ + if( track_ID == 0 || !mvex ) + return NULL; + for( lsmash_entry_t *entry = mvex->trex_list.head; entry; entry = entry->next ) + { + isom_trex_t *trex = (isom_trex_t *)entry->data; + if( !trex ) + return NULL; + if( trex->track_ID == track_ID ) + return trex; + } + return NULL; +} + +isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID ) +{ + if( track_ID == 0 || !moof ) + return NULL; + for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)entry->data; + if( !traf + || !traf->tfhd ) + return NULL; + if( traf->tfhd->track_ID == track_ID ) + return traf; + } + return NULL; +} + +isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID ) +{ + if( track_ID == 0 || !mfra ) + return NULL; + for( lsmash_entry_t *entry = mfra->tfra_list.head; entry; entry = entry->next ) + { + isom_tfra_t *tfra = (isom_tfra_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 ) +{ + assert( elst->file ); + isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->segment_duration = segment_duration; + data->media_time = media_time; + data->media_rate = media_rate; + if( lsmash_add_entry( elst->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( !elst->file->undefined_64_ver + && (data->segment_duration > UINT32_MAX + || data->media_time > INT32_MAX + || data->media_time < INT32_MIN) ) + elst->version = 1; + return 0; +} + +isom_dcr_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size ) +{ + isom_dcr_ps_entry_t *entry = lsmash_malloc( sizeof(isom_dcr_ps_entry_t) ); + if( !entry ) + return NULL; + entry->nalUnit = lsmash_memdup( ps, ps_size ); + if( !entry->nalUnit ) + { + lsmash_free( entry ); + return NULL; + } + entry->nalUnitLength = ps_size; + entry->unused = 0; + return entry; +} + +void isom_remove_dcr_ps( isom_dcr_ps_entry_t *ps ) +{ + if( !ps ) + return; + lsmash_free( ps->nalUnit ); + lsmash_free( ps ); +} + +/* This function returns 0 if failed, sample_entry_number if succeeded. */ +int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary ) +{ + if( !root || !summary + || ((lsmash_summary_t *)summary)->data_ref_index == 0 + || ((lsmash_summary_t *)summary)->data_ref_index > UINT16_MAX ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file, track_ID ); + if( !trak + || !trak->file + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd ) + return 0; + isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd; + lsmash_entry_list_t *list = &stsd->list; + int ret = LSMASH_ERR_NAMELESS; + lsmash_codec_type_t sample_type = ((lsmash_summary_t *)summary)->sample_type; + if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) ) + { + if( trak->mdia->minf->vmhd ) + ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary ); + else if( trak->mdia->minf->smhd ) + ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary ); + return ret < 0 ? 0 : list->entry_count; + } +typedef void (*opaque_func_t)( void ); + static struct description_setup_table_tag + { + lsmash_codec_type_t type; + opaque_func_t func; + } description_setup_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } }; + if( !description_setup_table[0].func ) + { + int i = 0; +#define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \ + description_setup_table[i++] = (struct description_setup_table_tag){ type, (opaque_func_t)func } + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_setup_visual_description ); +#if 0 + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_setup_visual_description ); +#endif + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_setup_visual_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_setup_audio_description ); +#if 0 + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description ); +#endif + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_setup_audio_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_add_tx3g_description ); + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_add_qt_text_description ); +#if 0 + ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_add_mp4s_entry ); +#endif + } + for( int i = 0; description_setup_table[i].func; i++ ) + if( lsmash_check_codec_type_identical( sample_type, description_setup_table[i].type ) ) + { + if( (opaque_func_t)isom_setup_visual_description == description_setup_table[i].func ) + ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary ); + else if( (opaque_func_t)isom_setup_audio_description == description_setup_table[i].func ) + ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary ); + else if( (opaque_func_t)isom_add_tx3g_description == description_setup_table[i].func ) + ret = isom_setup_tx3g_description( stsd, (lsmash_summary_t *)summary ); + else if( (opaque_func_t)isom_add_qt_text_description == description_setup_table[i].func ) + { + isom_qt_text_entry_t *text = isom_add_qt_text_description( stsd ); + if( text ) + { + text->data_reference_index = ((lsmash_summary_t *)summary)->data_ref_index; + ret = 0; + } + } + break; + } + return ret < 0 ? 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 LSMASH_ERR_NAMELESS; + isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->sample_count = 1; + data->sample_delta = sample_delta; + if( lsmash_add_entry( stbl->stts->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset ) +{ + if( !stbl + || !stbl->ctts + || !stbl->ctts->list ) + return LSMASH_ERR_NAMELESS; + isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->sample_count = 1; + data->sample_offset = sample_offset; + if( lsmash_add_entry( stbl->ctts->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + 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 LSMASH_ERR_NAMELESS; + isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + 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 ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size ) +{ + if( !stbl + || !stbl->stsz ) + return LSMASH_ERR_NAMELESS; + isom_stsz_t *stsz = stbl->stsz; + /* 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( !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 LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; i < stsz->sample_count; i++ ) + { + isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->entry_size = stsz->sample_size; + if( lsmash_add_entry( stsz->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + stsz->sample_size = 0; + } + isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->entry_size = entry_size; + if( lsmash_add_entry( stsz->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + ++ 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 LSMASH_ERR_NAMELESS; + isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->sample_number = sample_number; + if( lsmash_add_entry( stbl->stss->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number ) +{ + if( !stbl + || !stbl->stps + || !stbl->stps->list ) + return LSMASH_ERR_NAMELESS; + isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->sample_number = sample_number; + if( lsmash_add_entry( stbl->stps->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +/* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning. + * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with + * both ISOBMFF with AVCFF extensions and QTFF. + * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible + * compatibility == 1 -> AVCFF extensions compatible + * compatibility == 2 -> QTFF compatible + * compatibility == 3 -> both AVCFF extensions and QTFF compatible */ +static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, int compatibility ) +{ + if( !prop || !parent ) + return LSMASH_ERR_NAMELESS; + isom_sdtp_t *sdtp = NULL; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) + sdtp = ((isom_stbl_t *)parent)->sdtp; + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + sdtp = ((isom_traf_t *)parent)->sdtp; + else + assert( 0 ); + if( !sdtp + || !sdtp->list ) + return LSMASH_ERR_NAMELESS; + isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( compatibility == 1 ) + data->is_leading = prop->leading & 0x03; + else if( compatibility == 2 ) + data->is_leading = prop->allow_earlier & 0x03; + else + { + data->is_leading = 0; + assert( compatibility == 3 ); + } + 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( sdtp->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset ) +{ + if( !stbl + || !stbl->stco + || !stbl->stco->list ) + return LSMASH_ERR_NAMELESS; + isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->chunk_offset = chunk_offset; + if( lsmash_add_entry( stbl->stco->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_convert_stco_to_co64( isom_stbl_t *stbl ) +{ + /* backup stco */ + int err = 0; + isom_stco_t *stco = stbl->stco; + stbl->stco = NULL; + if( !isom_add_co64( stbl ) ) + { + err = LSMASH_ERR_NAMELESS; + goto fail; + } + /* 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( (err = isom_add_co64_entry( stbl, data->chunk_offset )) < 0 ) + goto fail; + } +fail: + isom_remove_box_by_itself( stco ); + return err; +} + +static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset ) +{ + if( !stbl + || !stbl->stco + || !stbl->stco->list ) + return LSMASH_ERR_NAMELESS; + if( stbl->stco->large_presentation ) + return isom_add_co64_entry( stbl, chunk_offset ); + if( chunk_offset > UINT32_MAX ) + { + int err = isom_convert_stco_to_co64( stbl ); + if( err < 0 ) + return err; + return isom_add_co64_entry( stbl, chunk_offset ); + } + isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + data->chunk_offset = (uint32_t)chunk_offset; + if( lsmash_add_entry( stbl->stco->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static isom_sgpd_t *isom_get_sample_group_description_common( lsmash_entry_list_t *list, uint32_t grouping_type ) +{ + for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) + { + isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data; + if( !sgpd + || !sgpd->list ) + return NULL; + if( sgpd->grouping_type == grouping_type ) + return sgpd; + } + return NULL; +} + +static isom_sbgp_t *isom_get_sample_to_group_common( lsmash_entry_list_t *list, uint32_t grouping_type ) +{ + for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) + { + isom_sbgp_t *sbgp = (isom_sbgp_t *)entry->data; + if( !sbgp + || !sbgp->list ) + return NULL; + if( sbgp->grouping_type == grouping_type ) + return sbgp; + } + return NULL; +} + +isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + return isom_get_sample_group_description_common( &stbl->sgpd_list, grouping_type ); +} + +isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + return isom_get_sample_to_group_common( &stbl->sbgp_list, grouping_type ); +} + +isom_sgpd_t *isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t *list ) +{ + isom_sgpd_t *sgpd; + if( (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_ROLL )) + || (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_PROL )) ) + return sgpd; + return NULL; +} + +isom_sbgp_t *isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t *list ) +{ + isom_sbgp_t *sbgp; + if( (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_ROLL )) + || (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_PROL )) ) + return sbgp; + return NULL; +} + +isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type ) +{ + return isom_get_sample_group_description_common( &traf->sgpd_list, grouping_type ); +} + +isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type ) +{ + return isom_get_sample_to_group_common( &traf->sbgp_list, grouping_type ); +} + +static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_t *sgpd ) +{ + if( !sgpd ) + return NULL; + isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) ); + if( !data ) + return NULL; + data->description_length = 0; + data->num_leading_samples_known = 0; + data->num_leading_samples = 0; + if( lsmash_add_entry( sgpd->list, data ) < 0 ) + { + lsmash_free( data ); + return NULL; + } + return data; +} + +static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_t *sgpd, int16_t roll_distance ) +{ + if( !sgpd ) + return NULL; + isom_roll_entry_t *data = lsmash_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 ) < 0 ) + { + lsmash_free( data ); + return NULL; + } + return data; +} + +static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index ) +{ + if( !sbgp ) + return NULL; + isom_group_assignment_entry_t *data = lsmash_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 ) < 0 ) + { + lsmash_free( data ); + return NULL; + } + return data; +} + +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 ) + 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 LSMASH_ERR_NAMELESS; + 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; + int err = isom_add_stts_entry( stbl, sample_delta ); + if( err < 0 ) + return err; + } + else + last_stts_data->sample_delta = sample_delta; + } + return 0; +} + +static int isom_update_mdhd_duration( isom_trak_t *trak, uint32_t last_sample_delta ) +{ + if( !trak + || !trak->file + || !trak->cache + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stts + || !trak->mdia->minf->stbl->stts->list ) + return LSMASH_ERR_INVALID_DATA; + lsmash_file_t *file = trak->file; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_stts_t *stts = stbl->stts; + isom_ctts_t *ctts = stbl->ctts; + isom_cslg_t *cslg = stbl->cslg; + mdhd->duration = 0; + uint32_t sample_count = isom_get_sample_count( trak ); + if( sample_count == 0 ) + { + /* Return error if non-fragmented movie has no samples. */ + if( !file->fragment && !stts->list->entry_count ) + return LSMASH_ERR_INVALID_DATA; + 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 ); + int err; + if( last_sample_delta ) + { + mdhd->duration += last_sample_delta; + if( (err = isom_replace_last_sample_delta( stbl, last_sample_delta )) < 0 ) + return err; + } + 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( (err = lsmash_remove_entry_tail( stts->list, NULL )) < 0 ) + return err; + /* 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 LSMASH_ERR_INVALID_DATA; + uint64_t dts = 0; + uint64_t max_cts = 0; + uint64_t max2_cts = 0; + uint64_t min_cts = UINT64_MAX; + uint32_t max_offset = 0; + uint32_t min_offset = UINT32_MAX; + int32_t ctd_shift = trak->cache->timestamp.ctd_shift; + uint32_t j = 0; + uint32_t k = 0; + lsmash_entry_t *stts_entry = stts->list->head; + lsmash_entry_t *ctts_entry = ctts->list->head; + for( uint32_t i = 0; i < sample_count; i++ ) + { + if( !ctts_entry || !stts_entry ) + return LSMASH_ERR_INVALID_DATA; + 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 LSMASH_ERR_INVALID_DATA; + uint64_t cts; + if( ctd_shift ) + { + /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */ + int32_t sample_offset = (int32_t)ctts_data->sample_offset; + cts = dts + sample_offset + ctd_shift; + max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset ); + min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset ); + } + else + { + cts = dts + ctts_data->sample_offset; + max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset ); + min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset ); + } + min_cts = LSMASH_MIN( min_cts, cts ); + if( max_cts < cts ) + { + max2_cts = max_cts; + max_cts = cts; + } + else if( max2_cts < cts ) + max2_cts = cts; + dts += stts_data->sample_delta; + /* If finished sample_count of current entry, move to next. */ + if( ++j == ctts_data->sample_count ) + { + ctts_entry = ctts_entry->next; + j = 0; + } + if( ++k == stts_data->sample_count ) + { + stts_entry = stts_entry->next; + k = 0; + } + } + dts -= last_stts_data->sample_delta; + if( file->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. */ + } + int err = isom_replace_last_sample_delta( stbl, last_sample_delta ); + if( err < 0 ) + return err; + /* Explicit composition information and timeline shifting */ + if( cslg || file->qt_compatible || file->max_isom_version >= 4 ) + { + if( ctd_shift ) + { + /* Remove composition to decode timeline shift. */ + max_cts -= ctd_shift; + max2_cts -= ctd_shift; + min_cts -= ctd_shift; + } + int64_t composition_end_time = max_cts + (max_cts - max2_cts); + if( !file->fragment + && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX) + && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) ) + { + if( !cslg ) + { + if( !isom_add_cslg( trak->mdia->minf->stbl ) ) + return LSMASH_ERR_NAMELESS; + cslg = stbl->cslg; + } + cslg->compositionToDTSShift = ctd_shift; + cslg->leastDecodeToDisplayDelta = min_offset; + cslg->greatestDecodeToDisplayDelta = max_offset; + cslg->compositionStartTime = min_cts; + cslg->compositionEndTime = composition_end_time; + } + else + isom_remove_box_by_itself( cslg ); + } + } + if( mdhd->duration > UINT32_MAX && !file->undefined_64_ver ) + mdhd->version = 1; + return 0; +} + +static int isom_update_mvhd_duration( isom_moov_t *moov ) +{ + if( !moov + || !moov->mvhd + || !moov->mvhd->file ) + return LSMASH_ERR_INVALID_DATA; + 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_t *data = (isom_trak_t *)entry->data; + if( !data + || !data->tkhd ) + return LSMASH_ERR_INVALID_DATA; + mvhd->duration = entry != moov->trak_list.head + ? LSMASH_MAX( mvhd->duration, data->tkhd->duration ) + : data->tkhd->duration; + } + if( mvhd->duration > UINT32_MAX && !mvhd->file->undefined_64_ver ) + mvhd->version = 1; + return 0; +} + +int isom_update_tkhd_duration( isom_trak_t *trak ) +{ + if( !trak + || !trak->tkhd + || !trak->file + || !trak->file->moov ) + return LSMASH_ERR_INVALID_DATA; + lsmash_file_t *file = trak->file; + isom_tkhd_t *tkhd = trak->tkhd; + tkhd->duration = 0; + if( file->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 + || !file->moov->mvhd + || trak->mdia->mdhd->timescale == 0 ) + return LSMASH_ERR_INVALID_DATA; + if( trak->mdia->mdhd->duration == 0 ) + { + int err = isom_update_mdhd_duration( trak, 0 ); + if( err < 0 ) + return err; + } + tkhd->duration = trak->mdia->mdhd->duration * ((double)file->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 LSMASH_ERR_INVALID_DATA; + tkhd->duration += data->segment_duration; + } + } + if( tkhd->duration > UINT32_MAX && !file->undefined_64_ver ) + tkhd->version = 1; + if( !file->fragment && tkhd->duration == 0 ) + tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff; + return isom_update_mvhd_duration( file->moov ); +} + +int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak ) + return LSMASH_ERR_NAMELESS; + int err = isom_update_mdhd_duration( trak, last_sample_delta ); + if( err < 0 ) + return err; + /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */ + if( !file->fragment && trak->edts && trak->edts->elst ) + err = isom_update_mvhd_duration( file->moov ); /* Only update movie duration. */ + else + err = isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */ + return err; +} + +static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number_in_entry, uint32_t sample_count_in_entry, lsmash_entry_t **entry ) +{ + if( *sample_number_in_entry != sample_count_in_entry ) + { + *sample_number_in_entry += 1; + return 0; + } + /* Precede the next entry. */ + *sample_number_in_entry = 1; + if( *entry ) + { + *entry = (*entry)->next; + if( *entry && !(*entry)->data ) + return LSMASH_ERR_INVALID_DATA; + } + return 0; +} + +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; + 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; + isom_stts_entry_t *stts_data = NULL; + isom_stsc_entry_t *stsc_data = NULL; + if( next_stsc_entry && !next_stsc_entry->data ) + return LSMASH_ERR_INVALID_DATA; + uint32_t rate = 0; + uint64_t dts = 0; + uint32_t time_wnd = 0; + uint32_t timescale = mdia->mdhd->timescale; + uint32_t chunk_number = 0; + uint32_t sample_number_in_stts = 1; + uint32_t sample_number_in_chunk = 1; + *bufferSizeDB = 0; + *maxBitrate = 0; + *avgBitrate = 0; + while( stts_entry ) + { + int err; + if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk ) + { + /* Move the next chunk. */ + sample_number_in_chunk = 1; + ++chunk_number; + /* Check if the next entry is broken. */ + while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number ) + { + /* Just skip broken next entry. */ + next_stsc_entry = next_stsc_entry->next; + if( next_stsc_entry && !next_stsc_entry->data ) + return LSMASH_ERR_INVALID_DATA; + } + /* Check if the next chunk belongs to the next sequence of chunks. */ + if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number ) + { + stsc_entry = next_stsc_entry; + next_stsc_entry = next_stsc_entry->next; + if( next_stsc_entry && !next_stsc_entry->data ) + return LSMASH_ERR_INVALID_DATA; + stsc_data = (isom_stsc_entry_t *)stsc_entry->data; + /* Check if the next contiguous chunks belong to given sample description. */ + if( stsc_data->sample_description_index != sample_description_index ) + { + /* Skip chunks which don't belong to given sample description. */ + uint32_t number_of_skips = 0; + uint32_t first_chunk = stsc_data->first_chunk; + uint32_t samples_per_chunk = stsc_data->samples_per_chunk; + while( next_stsc_entry ) + { + if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index ) + { + stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data; + number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk; + first_chunk = stsc_data->first_chunk; + samples_per_chunk = stsc_data->samples_per_chunk; + } + else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk ) + ; /* broken entry */ + else + break; + /* Just skip the next entry. */ + next_stsc_entry = next_stsc_entry->next; + if( next_stsc_entry && !next_stsc_entry->data ) + return LSMASH_ERR_INVALID_DATA; + } + if( !next_stsc_entry ) + break; /* There is no more chunks which don't belong to given sample description. */ + 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_entry ) + break; + stsz_entry = stsz_entry->next; + } + if( !stts_entry ) + break; + if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts, + ((isom_stts_entry_t *)stts_entry->data)->sample_count, + &stts_entry )) < 0 ) + return err; + } + if( (stsz->list && !stsz_entry) || !stts_entry ) + break; + chunk_number = stsc_data->first_chunk; + } + } + } + else + ++sample_number_in_chunk; + /* Get current sample's size. */ + 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 LSMASH_ERR_INVALID_DATA; + size = stsz_data->entry_size; + stsz_entry = stsz_entry->next; + } + else + size = stsz->sample_size; + /* Get current sample's DTS. */ + if( stts_data ) + dts += stts_data->sample_delta; + stts_data = (isom_stts_entry_t *)stts_entry->data; + if( !stts_data ) + return LSMASH_ERR_INVALID_DATA; + if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry )) < 0 ) + return err; + /* Calculate bitrate description. */ + if( *bufferSizeDB < size ) + *bufferSizeDB = size; + *avgBitrate += size; + rate += size; + if( dts > time_wnd + timescale ) + { + if( rate > *maxBitrate ) + *maxBitrate = rate; + time_wnd = dts; + rate = 0; + } + } + double duration = (double)mdia->mdhd->duration / timescale; + *avgBitrate = (uint32_t)(*avgBitrate / duration); + if( *maxBitrate == 0 ) + *maxBitrate = *avgBitrate; + /* Convert to bits per second. */ + *maxBitrate *= 8; + *avgBitrate *= 8; + return 0; +} + +int isom_update_bitrate_description( isom_mdia_t *mdia ) +{ + if( !mdia + || !mdia->mdhd + || !mdia->minf + || !mdia->minf->stbl ) + return LSMASH_ERR_INVALID_DATA; + isom_stbl_t *stbl = mdia->minf->stbl; + if( !stbl->stsd + || !stbl->stsz + || !stbl->stsc || !stbl->stsc->list + || !stbl->stts || !stbl->stts->list ) + return LSMASH_ERR_INVALID_DATA; + uint32_t sample_description_index = 0; + for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next ) + { + isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data; + if( !sample_entry ) + return LSMASH_ERR_INVALID_DATA; + ++sample_description_index; + int err; + uint32_t bufferSizeDB; + uint32_t maxBitrate; + uint32_t avgBitrate; + /* set bitrate info */ + lsmash_codec_type_t sample_type = sample_entry->type; + if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HEV1_VIDEO ) ) + { + isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry; + if( !stsd_data ) + return LSMASH_ERR_INVALID_DATA; + isom_btrt_t *btrt = (isom_btrt_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_BTRT ); + if( btrt ) + { + if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) + return err; + btrt->bufferSizeDB = bufferSizeDB; + btrt->maxBitrate = maxBitrate; + btrt->avgBitrate = avgBitrate; + } + } + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) ) + { + isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry; + if( !stsd_data ) + return LSMASH_ERR_INVALID_DATA; + isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS ); + if( !esds || !esds->ES ) + return LSMASH_ERR_INVALID_DATA; + if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) + return err; + /* FIXME: avgBitrate is 0 only if VBR in proper. */ + if( (err = mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 )) < 0 ) + return err; + } + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) ) + { + isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry; + if( !stsd_data ) + return LSMASH_ERR_INVALID_DATA; + isom_esds_t *esds = NULL; + if( ((isom_audio_entry_t *)sample_entry)->version ) + { + /* MPEG-4 Audio in QTFF */ + isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box_format( &stsd_data->extensions, QT_BOX_TYPE_WAVE ); + if( !wave ) + return LSMASH_ERR_INVALID_DATA; + esds = (isom_esds_t *)isom_get_extension_box_format( &wave->extensions, ISOM_BOX_TYPE_ESDS ); + } + else + esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS ); + if( !esds || !esds->ES ) + return LSMASH_ERR_INVALID_DATA; + if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) + return err; + /* FIXME: avgBitrate is 0 only if VBR in proper. */ + if( (err = mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 )) < 0 ) + return err; + } + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) ) + { + isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry; + if( !alac ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *exdata = NULL; + uint32_t exdata_size = 0; + isom_box_t *alac_ext = isom_get_extension_box( &alac->extensions, QT_BOX_TYPE_WAVE ); + if( alac_ext ) + { + /* Apple Lossless Audio inside QuickTime file format + * Though average bitrate field we found is always set to 0 apparently, + * we set up maxFrameBytes and avgBitRate fields. */ + if( alac_ext->manager & LSMASH_BINARY_CODED_BOX ) + exdata = isom_get_child_box_position( alac_ext->binary, alac_ext->size, QT_BOX_TYPE_ALAC, &exdata_size ); + else + { + isom_wave_t *wave = (isom_wave_t *)alac_ext; + isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, QT_BOX_TYPE_ALAC ); + if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) ) + return LSMASH_ERR_INVALID_DATA; + exdata = wave_ext->binary; + exdata_size = wave_ext->size; + } + } + else + { + /* Apple Lossless Audio inside ISO Base Media file format */ + isom_box_t *ext = isom_get_extension_box( &alac->extensions, ISOM_BOX_TYPE_ALAC ); + if( !ext || !(ext->manager & LSMASH_BINARY_CODED_BOX) ) + return LSMASH_ERR_INVALID_DATA; + exdata = ext->binary; + exdata_size = ext->size; + } + if( !exdata || exdata_size < 36 ) + return LSMASH_ERR_INVALID_DATA; + if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) + return err; + exdata += 24; + /* maxFrameBytes */ + LSMASH_SET_BE32( &exdata[0], bufferSizeDB ); + /* avgBitRate */ + LSMASH_SET_BE32( &exdata[4], avgBitrate ); + } + else if( isom_is_waveform_audio( sample_type ) ) + { + isom_box_t *ext = isom_get_extension_box( &sample_entry->extensions, QT_BOX_TYPE_WAVE ); + if( !ext ) + return LSMASH_ERR_INVALID_DATA; + uint8_t *exdata = NULL; + uint32_t exdata_size = 0; + if( ext->manager & LSMASH_BINARY_CODED_BOX ) + exdata = isom_get_child_box_position( ext->binary, ext->size, sample_type, &exdata_size ); + else + { + isom_wave_t *wave = (isom_wave_t *)ext; + isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, sample_type ); + if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) ) + return LSMASH_ERR_INVALID_DATA; + exdata = wave_ext->binary; + exdata_size = wave_ext->size; + } + /* Check whether exdata is valid or not. */ + if( !exdata || exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 ) + return LSMASH_ERR_INVALID_DATA; + exdata += ISOM_BASEBOX_COMMON_SIZE; + uint16_t cbSize = LSMASH_GET_LE16( &exdata[16] ); + if( exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 + cbSize ) + return LSMASH_ERR_INVALID_DATA; + /* WAVEFORMATEX.nAvgBytesPerSec */ + if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) + return err; + uint32_t nAvgBytesPerSec = avgBitrate / 8; + LSMASH_SET_LE32( &exdata[8], nAvgBytesPerSec ); + if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FULLMP3_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_MP3_AUDIO ) ) + { + /* MPEGLAYER3WAVEFORMAT.nBlockSize */ + uint32_t nSamplesPerSec = LSMASH_GET_LE32( &exdata[ 4] ); + uint16_t nFramesPerBlock = LSMASH_GET_LE16( &exdata[26] ); + uint16_t padding = 0; /* FIXME? */ + uint16_t nBlockSize = (144 * (avgBitrate / nSamplesPerSec) + padding) * nFramesPerBlock; + LSMASH_SET_LE16( &exdata[24], nBlockSize ); + } + } + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) + { + isom_audio_entry_t *dts_audio = (isom_audio_entry_t *)sample_entry; + if( !dts_audio ) + return LSMASH_ERR_INVALID_DATA; + isom_box_t *ext = isom_get_extension_box( &dts_audio->extensions, ISOM_BOX_TYPE_DDTS ); + if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 28) ) + 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 ) + maxBitrate = avgBitrate; + uint8_t *exdata = ext->binary + 12; + LSMASH_SET_BE32( &exdata[0], maxBitrate ); + LSMASH_SET_BE32( &exdata[4], avgBitrate ); + } + else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) ) + { + isom_audio_entry_t *eac3 = (isom_audio_entry_t *)sample_entry; + if( !eac3 ) + return LSMASH_ERR_INVALID_DATA; + isom_box_t *ext = isom_get_extension_box( &eac3->extensions, ISOM_BOX_TYPE_DEC3 ); + 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( (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 */ + uint8_t *exdata = ext->binary + 8; + exdata[0] = (bitrate >> 5) & 0xff; + exdata[1] = (bitrate & 0x1f) << 3; + } + } + return sample_description_index ? 0 : LSMASH_ERR_INVALID_DATA; +} + +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_t *trak, uint64_t current_mp4time ) +{ + if( !trak->mdia + || !trak->mdia->mdhd ) + return LSMASH_ERR_NAMELESS; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + if( mdhd->creation_time == 0 ) + mdhd->creation_time = mdhd->modification_time = current_mp4time; + return 0; +} + +static int isom_set_track_creation_time( isom_trak_t *trak, uint64_t current_mp4time ) +{ + if( !trak + || !trak->tkhd ) + return LSMASH_ERR_NAMELESS; + isom_tkhd_t *tkhd = trak->tkhd; + if( tkhd->creation_time == 0 ) + tkhd->creation_time = tkhd->modification_time = current_mp4time; + return isom_set_media_creation_time( trak, current_mp4time ); +} + +static int isom_set_movie_creation_time( lsmash_file_t *file ) +{ + if( !file + || !file->moov + || !file->moov->mvhd ) + return LSMASH_ERR_NAMELESS; + uint64_t current_mp4time = isom_get_current_mp4time(); + for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) + { + int err = isom_set_track_creation_time( (isom_trak_t *)entry->data, current_mp4time ); + if( err < 0 ) + return err; + } + isom_mvhd_t *mvhd = file->moov->mvhd; + if( mvhd->creation_time == 0 ) + mvhd->creation_time = mvhd->modification_time = current_mp4time; + return 0; +} + +int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type ) +{ + isom_box_t *parent = hdlr->parent; + lsmash_file_t *file = hdlr->file; + if( !parent || !file ) + return LSMASH_ERR_NAMELESS; + isom_mdia_t *mdia = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ? (isom_mdia_t *)parent : NULL; + isom_meta_t *meta = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ? (isom_meta_t *)parent + : lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ? (isom_meta_t *)parent : NULL; + uint32_t type = mdia ? (file->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA); + uint32_t subtype = media_type; + hdlr->componentType = type; + hdlr->componentSubtype = subtype; + char *type_name = NULL; + char *subtype_name = NULL; + uint8_t type_name_length = 0; + uint8_t subtype_name_length = 0; + if( mdia ) + type_name = "Media "; + else if( meta ) + type_name = "Metadata "; + else /* if( minf ) */ + type_name = "Data "; + type_name_length = strlen( type_name ); + struct + { + uint32_t subtype; + char *subtype_name; + uint8_t subtype_name_length; + } subtype_table[] = + { + { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 }, + { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video ", 6 }, + { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 }, + { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 }, + { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 }, + { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 }, + { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 }, + { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 }, + { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 }, + { subtype, "Unknown ", 8 } + }; + for( int i = 0; subtype_table[i].subtype; i++ ) + if( subtype == subtype_table[i].subtype ) + { + subtype_name = subtype_table[i].subtype_name; + subtype_name_length = subtype_table[i].subtype_name_length; + break; + } + uint32_t name_length = 15 + subtype_name_length + type_name_length + file->isom_compatible + file->qt_compatible; + uint8_t *name = lsmash_malloc( name_length ); + if( !name ) + return LSMASH_ERR_MEMORY_ALLOC; + if( file->qt_compatible ) + name[0] = name_length & 0xff; + memcpy( name + file->qt_compatible, "L-SMASH ", 8 ); + memcpy( name + file->qt_compatible + 8, subtype_name, subtype_name_length ); + memcpy( name + file->qt_compatible + 8 + subtype_name_length, type_name, type_name_length ); + memcpy( name + file->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 ); + if( file->isom_compatible ) + name[name_length - 1] = 0; + hdlr->componentName = name; + hdlr->componentName_length = name_length; + return 0; +} + +/******************************* + public interfaces +*******************************/ + +/*---- track manipulators ----*/ + +void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 + || !root->file->initializer->moov ) + return; + for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + if( !trak + || !trak->tkhd ) + return; + if( trak->tkhd->track_ID == track_ID ) + { + isom_remove_box_by_itself( trak ); + return; + } + } +} + +uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + lsmash_file_t *file = root->file; + /* Don't allow to create a new track if the initial movie is already written. */ + if( (file->fragment && file->fragment->movie) + || (file->moov && (file->moov->manager & LSMASH_WRITTEN_BOX)) ) + return 0; + isom_trak_t *trak = isom_add_trak( file->moov ); + if( !trak + || !trak->file + || !trak->file->moov + || !trak->file->moov->mvhd ) + goto fail; + if( !isom_add_tkhd( trak ) + || !isom_add_mdia( trak ) + || !isom_add_mdhd( trak->mdia ) + || !isom_add_minf( trak->mdia ) + || !isom_add_dinf( trak->mdia->minf ) + || !isom_add_dref( trak->mdia->minf->dinf ) + || !isom_add_stbl( trak->mdia->minf ) + || !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 ) ) + goto fail; + if( !isom_add_hdlr( trak->mdia ) + || isom_setup_handler_reference( trak->mdia->hdlr, media_type ) < 0 ) + goto fail; + if( file->qt_compatible ) + { + if( !isom_add_hdlr( trak->mdia->minf ) + || isom_setup_handler_reference( trak->mdia->minf->hdlr, QT_REFERENCE_HANDLER_TYPE_URL ) < 0 ) + goto fail; + } + switch( media_type ) + { + case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK : + if( !isom_add_vmhd( trak->mdia->minf ) ) + goto fail; + trak->mdia->minf->vmhd->flags = 0x000001; + break; + case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK : + if( !isom_add_smhd( trak->mdia->minf ) ) + goto fail; + trak->cache->is_audio = 1; + break; + case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK : + if( !isom_add_hmhd( trak->mdia->minf ) ) + goto fail; + break; + case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK : + if( file->qt_compatible || file->itunes_movie ) + { + if( !isom_add_gmhd( trak->mdia->minf ) + || !isom_add_gmin( trak->mdia->minf->gmhd ) + || !isom_add_text( trak->mdia->minf->gmhd ) ) + return 0; + /* Default Text Media Information Box. */ + { + isom_text_t *text = trak->mdia->minf->gmhd->text; + text->matrix[0] = 0x00010000; + text->matrix[4] = 0x00010000; + text->matrix[8] = 0x40000000; + } + } + else + goto fail; /* We support only reference text media track for chapter yet. */ + break; + default : + if( !isom_add_nmhd( trak->mdia->minf ) ) + goto fail; + break; + } + /* Default Track Header Box. */ + { + isom_tkhd_t *tkhd = trak->tkhd; + if( media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ) + tkhd->volume = 0x0100; + tkhd->matrix[0] = 0x00010000; + tkhd->matrix[4] = 0x00010000; + tkhd->matrix[8] = 0x40000000; + tkhd->duration = 0xffffffff; + tkhd->track_ID = trak->file->moov->mvhd->next_track_ID ++; + } + trak->mdia->mdhd->language = file->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED; + return trak->tkhd->track_ID; +fail: + isom_remove_box_by_itself( trak ); + return 0; +} + +uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number ) +{ + if( isom_check_initializer_present( root ) < 0 + || !root->file->initializer->moov ) + return 0; + isom_trak_t *trak = (isom_trak_t *)lsmash_get_entry_data( &root->file->initializer->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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->hdlr + || !file->moov->mvhd ) + return LSMASH_ERR_NAMELESS; + /* Prepare Track Aperture Modes if required. */ + if( file->qt_compatible && param->aperture_modes ) + { + if( !trak->tapt && !isom_add_tapt( trak ) ) + return LSMASH_ERR_NAMELESS; + 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 LSMASH_ERR_NAMELESS; + } + else + isom_remove_box_by_itself( trak->tapt ); + /* Set up Track Header. */ + uint32_t media_type = trak->mdia->hdlr->componentSubtype; + isom_tkhd_t *tkhd = trak->tkhd; + tkhd->flags = param->mode; + tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID; + tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration; + /* Template fields + * alternate_group, layer, volume and matrix + * According to 14496-14, these value are all set to defaut values in 14496-12. + * And when a file is read as an MPEG-4 file, these values shall be ignored. + * If a file complies with other specifications, then those fields may have non-default values + * as required by those other specifications. */ + if( param->alternate_group ) + { + if( file->qt_compatible || file->itunes_movie || file->max_3gpp_version >= 4 ) + tkhd->alternate_group = param->alternate_group; + else + { + tkhd->alternate_group = 0; + lsmash_log( NULL, LSMASH_LOG_WARNING, + "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" ); + } + } + else + tkhd->alternate_group = 0; + if( file->qt_compatible || file->itunes_movie ) + { + tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0; + tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0; + if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ) + for( int i = 0; i < 9; i++ ) + tkhd->matrix[i] = param->matrix[i]; + else + for( int i = 0; i < 9; i++ ) + tkhd->matrix[i] = 0; + } + else + { + tkhd->layer = 0; + tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0; + tkhd->matrix[0] = 0x00010000; + tkhd->matrix[1] = 0; + tkhd->matrix[2] = 0; + tkhd->matrix[3] = 0; + tkhd->matrix[4] = 0x00010000; + tkhd->matrix[5] = 0; + tkhd->matrix[6] = 0; + tkhd->matrix[7] = 0; + tkhd->matrix[8] = 0x40000000; + } + /* visual presentation size */ + tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0; + tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0; + /* Update next_track_ID if needed. */ + if( file->moov->mvhd->next_track_ID <= tkhd->track_ID ) + file->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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak ) + return LSMASH_ERR_NAMELESS; + 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 inline int check_dref_presence( isom_trak_t *trak ) +{ + if( !trak + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->dinf + || !trak->mdia->minf->dinf->dref ) + return LSMASH_ERR_NAMELESS; + return 0; +} + +uint32_t lsmash_count_data_reference +( + lsmash_root_t *root, + uint32_t track_ID +) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( check_dref_presence( trak ) < 0 ) + return 0; + return trak->mdia->minf->dinf->dref->list.entry_count; +} + +int lsmash_get_data_reference +( + lsmash_root_t *root, + uint32_t track_ID, + lsmash_data_reference_t *data_ref +) +{ + if( isom_check_initializer_present( root ) < 0 || !data_ref ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( check_dref_presence( trak ) < 0 ) + return LSMASH_ERR_NAMELESS; + isom_dref_entry_t *url = lsmash_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref->index ); + if( !url ) + return LSMASH_ERR_NAMELESS; + if( !(url->flags & 0x000001) && url->location ) + { + int length = strlen( url->location ); + char *location = lsmash_malloc( length + 1 ); + if( !location ) + return LSMASH_ERR_MEMORY_ALLOC; + memcpy( location, url->location, length ); + location[length] = '\0'; + data_ref->location = location; + } + else + data_ref->location = NULL; + return 0; +} + +void lsmash_cleanup_data_reference +( + lsmash_data_reference_t *data_ref +) +{ + if( !data_ref ) + return; + lsmash_freep( &data_ref->location ); +} + +int lsmash_create_data_reference +( + lsmash_root_t *root, + uint32_t track_ID, + lsmash_data_reference_t *data_ref, + lsmash_file_t *file +) +{ + /* At present, we don't support external data references for movie fragments. + * Note that, for external media data, default-base-is-moof is meaningless since relative + * offsets from Movie Fragment Boxes make no sense. + * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed + * for the implementation which does not write actually and does reference read-only file. */ + if( !root || !file || file->root != root + || (!(file->flags & LSMASH_FILE_MODE_MEDIA) && !(file->flags & LSMASH_FILE_MODE_INITIALIZATION)) + || !(file->flags & LSMASH_FILE_MODE_WRITE) + || (root->file != file && ((file->flags & LSMASH_FILE_MODE_FRAGMENTED) || file->fragment)) + || !data_ref ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file, track_ID ); + if( check_dref_presence( trak ) < 0 ) + return LSMASH_ERR_NAMELESS; + isom_dref_entry_t *url = isom_add_dref_entry( trak->mdia->minf->dinf->dref, ISOM_BOX_TYPE_URL ); + if( !url ) + return LSMASH_ERR_NAMELESS; + if( !data_ref->location || root->file == file ) + { + /* Media data is in the same file. */ + url->flags = 0x000001; + url->ref_file = root->file; + } + else + { + /* Set the location of the file. */ + int length = strlen( data_ref->location ); + url->location = lsmash_malloc( length + 1 ); + if( !url->location ) + { + isom_remove_box_by_itself( url ); + return LSMASH_ERR_MEMORY_ALLOC; + } + memcpy( url->location, data_ref->location, length ); + url->location[length] = '\0'; + url->location_length = length + 1; + url->ref_file = file; + } + data_ref->index = trak->mdia->minf->dinf->dref->list.entry_count; + return 0; +} + +int lsmash_assign_data_reference +( + lsmash_root_t *root, + uint32_t track_ID, + uint32_t data_ref_index, + lsmash_file_t *file +) +{ + if( isom_check_initializer_present( root ) < 0 + || !file || file->root != root + || !(file->flags & LSMASH_FILE_MODE_MEDIA) + || !(file->flags & LSMASH_FILE_MODE_READ) + || data_ref_index == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( check_dref_presence( trak ) < 0 ) + return LSMASH_ERR_NAMELESS; + isom_dref_entry_t *url = (isom_dref_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref_index ); + if( !url ) + return LSMASH_ERR_NAMELESS; + if( !(url->flags & 0x000001) ) + /* Reference an external media data. */ + url->ref_file = file; + return 0; +} + +static int isom_set_media_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name ) +{ + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->hdlr ) + return LSMASH_ERR_NAMELESS; + isom_hdlr_t *hdlr = trak->mdia->hdlr; + uint8_t *name = NULL; + uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible; + if( file->qt_compatible ) + name_length = LSMASH_MIN( name_length, 255 ); + if( name_length > hdlr->componentName_length && hdlr->componentName ) + name = lsmash_realloc( hdlr->componentName, name_length ); + else if( !hdlr->componentName ) + name = lsmash_malloc( name_length ); + else + name = hdlr->componentName; + if( !name ) + return LSMASH_ERR_MEMORY_ALLOC; + if( file->qt_compatible ) + name[0] = name_length & 0xff; + memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) ); + if( file->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_file_t *file, uint32_t track_ID, char *handler_name ) +{ + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->hdlr ) + return LSMASH_ERR_NAMELESS; + isom_hdlr_t *hdlr = trak->mdia->minf->hdlr; + uint8_t *name = NULL; + uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible; + if( file->qt_compatible ) + name_length = LSMASH_MIN( name_length, 255 ); + if( name_length > hdlr->componentName_length && hdlr->componentName ) + name = lsmash_realloc( hdlr->componentName, name_length ); + else if( !hdlr->componentName ) + name = lsmash_malloc( name_length ); + else + name = hdlr->componentName; + if( !name ) + return LSMASH_ERR_MEMORY_ALLOC; + if( file->qt_compatible ) + name[0] = name_length & 0xff; + memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) ); + if( file->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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file->initializer, 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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file->initializer, 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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file->initializer, 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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file, 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->tail + || !trak->mdia->minf->stbl->stts->list->tail->data ) + return 0; + return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta; +} + +uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file, 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; +} + +uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return 0; + lsmash_file_t *file = root->file->initializer; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return 0; + uint32_t sample_count = isom_get_sample_count( trak ); + if( sample_count == 0 ) + return 0; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stts || !stbl->stts->list + || !stbl->ctts || !stbl->ctts->list ) + return 0; + if( !(file->max_isom_version >= 4 && stbl->ctts->version == 1) && !file->qt_compatible ) + return 0; /* This movie shall not have composition to decode timeline shift. */ + lsmash_entry_t *stts_entry = stbl->stts->list->head; + lsmash_entry_t *ctts_entry = stbl->ctts->list->head; + if( !stts_entry || !ctts_entry ) + return 0; + uint64_t dts = 0; + uint64_t cts = 0; + uint32_t ctd_shift = 0; + uint32_t i = 0; + uint32_t j = 0; + for( uint32_t k = 0; k < sample_count; k++ ) + { + 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 0; + cts = dts + (int32_t)ctts_data->sample_offset; + if( dts > cts + ctd_shift ) + ctd_shift = dts - cts; + dts += stts_data->sample_delta; + if( ++i == stts_data->sample_count ) + { + stts_entry = stts_entry->next; + if( !stts_entry ) + return 0; + i = 0; + } + if( ++j == ctts_data->sample_count ) + { + ctts_entry = ctts_entry->next; + if( !ctts_entry ) + return 0; + j = 0; + } + } + return ctd_shift; +} + +uint16_t lsmash_pack_iso_language( char *iso_language ) +{ + if( !iso_language || strlen( iso_language ) != 3 ) + return 0; + return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] ); +} + +static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language ) +{ + assert( MAC_language ); + 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 LSMASH_ERR_NAMELESS; + *MAC_language = isom_languages[i].mac_value; + return 0; +} + +static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language ) +{ + assert( ISO_language ); + 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_file_t *file, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language ) +{ + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd ) + return LSMASH_ERR_NAMELESS; + uint16_t language = 0; + if( file->isom_compatible ) + { + if( ISO_language ) + language = ISO_language; + else if( MAC_language ) + { + int err = isom_mac2iso_language( MAC_language, &language ); + if( err ) + return err; + } + else + language = ISOM_LANGUAGE_CODE_UNDEFINED; + } + else if( file->qt_compatible ) + { + if( ISO_language ) + { + int err = isom_iso2mac_language( ISO_language, &language ); + if( err ) + return err; + } + else + language = MAC_language; + } + else + return LSMASH_ERR_INVALID_DATA; + trak->mdia->mdhd->language = language; + return 0; +} + +int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type ) +{ + isom_sgpd_t *sgpd; + isom_sbgp_t *sbgp; + if( NULL == (sgpd = isom_add_sgpd( parent )) + || NULL == (sbgp = isom_add_sbgp( parent )) ) + return LSMASH_ERR_NAMELESS; + sbgp->grouping_type = grouping_type; + sgpd->grouping_type = grouping_type; + sgpd->version = 1; /* We use version 1 for Sample Group Description Box 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 : + case ISOM_GROUP_TYPE_PROL : + sgpd->default_length = 2; + break; + default : + /* We don't consider other grouping types currently. */ + break; + } + return 0; +} + +static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type grouping_type ) +{ + lsmash_file_t *file = trak->file; + switch( grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + assert( file->max_isom_version >= 6 ); + break; + case ISOM_GROUP_TYPE_ROLL : + case ISOM_GROUP_TYPE_PROL : + assert( file->avc_extensions || file->qt_compatible ); + break; + default : + assert( 0 ); + break; + } + int err = isom_add_sample_grouping( (isom_box_t *)trak->mdia->minf->stbl, grouping_type ); + if( err < 0 ) + return err; + if( trak->cache->fragment && file->max_isom_version >= 6 ) + switch( grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + trak->cache->fragment->rap_grouping = 1; + break; + case ISOM_GROUP_TYPE_ROLL : + case ISOM_GROUP_TYPE_PROL : + trak->cache->fragment->roll_grouping = 1; + break; + default : + /* We don't consider other grouping types currently. */ + break; + } + 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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return LSMASH_ERR_NAMELESS; + trak->mdia->mdhd->timescale = param->timescale; + int err = isom_set_media_language( file, track_ID, param->ISO_language, param->MAC_language ); + if( err < 0 ) + return err; + if( param->media_handler_name + && (err = isom_set_media_handler_name( file, track_ID, param->media_handler_name )) < 0 ) + return err; + if( file->qt_compatible && param->data_handler_name + && (err = isom_set_data_handler_name( file, track_ID, param->data_handler_name )) < 0 ) + return err; + if( (file->avc_extensions || file->qt_compatible) && param->roll_grouping + && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_ROLL )) < 0 ) + return err; + if( (file->max_isom_version >= 6) && param->rap_grouping + && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_RAP )) < 0 ) + return err; + return 0; +} + +int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file->initializer; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->hdlr + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return LSMASH_ERR_NAMELESS; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + param->timescale = mdhd->timescale; + param->handler_type = trak->mdia->hdlr->componentSubtype; + param->duration = mdhd->duration; + /* Whether sample grouping present. */ + { + isom_sbgp_t *sbgp; + isom_sgpd_t *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; + sbgp = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list ); + sgpd = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list ); + param->roll_grouping = sbgp && sgpd; + } + /* Get media language. */ + if( mdhd->language >= 0x800 ) + { + param->MAC_language = 0; + param->ISO_language = mdhd->language; + } + else + { + param->MAC_language = mdhd->language; + param->ISO_language = 0; + } + /* Get handler name(s). */ + isom_hdlr_t *hdlr = trak->mdia->hdlr; + int length = LSMASH_MIN( 255, hdlr->componentName_length ); + if( length ) + { + memcpy( param->media_handler_name_shadow, hdlr->componentName + file->qt_compatible, length ); + param->media_handler_name_shadow[length - 2 + file->isom_compatible + file->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 + file->qt_compatible, length ); + param->data_handler_name_shadow[length - 2 + file->isom_compatible + file->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 ----*/ + +void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param ) +{ + memset( param, 0, sizeof(lsmash_movie_parameters_t) ); + 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 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file + || !file->moov + || !file->moov->mvhd ) + return LSMASH_ERR_NAMELESS; + isom_mvhd_t *mvhd = file->moov->mvhd; + mvhd->timescale = param->timescale; + if( file->qt_compatible || file->itunes_movie ) + { + mvhd->rate = param->playback_rate; + mvhd->volume = param->playback_volume; + mvhd->previewTime = param->preview_time; + mvhd->previewDuration = param->preview_duration; + mvhd->posterTime = param->poster_time; + } + else + { + mvhd->rate = 0x00010000; + mvhd->volume = 0x0100; + mvhd->previewTime = 0; + mvhd->previewDuration = 0; + mvhd->posterTime = 0; + } + return 0; +} + +int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file->initializer; + if( !file->moov + || !file->moov->mvhd ) + return LSMASH_ERR_NAMELESS; + isom_mvhd_t *mvhd = file->moov->mvhd; + 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 = file->moov->trak_list.entry_count; + return 0; +} + +uint32_t lsmash_get_movie_timescale( lsmash_root_t *root ) +{ + if( isom_check_initializer_present( root ) < 0 + || !root->file->initializer->moov + || !root->file->initializer->moov->mvhd ) + return 0; + return root->file->initializer->moov->mvhd->timescale; +} + +static int isom_scan_trak_profileLevelIndication +( + isom_trak_t *trak, + mp4a_audioProfileLevelIndication *audio_pli, + mp4sys_visualProfileLevelIndication *visual_pli +) +{ + if( !trak + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return LSMASH_ERR_INVALID_DATA; + isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd; + if( !stsd + || !stsd->list.head ) + return LSMASH_ERR_INVALID_DATA; + 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 LSMASH_ERR_INVALID_DATA; + lsmash_codec_type_t sample_type = sample_entry->type; + if( trak->mdia->minf->vmhd ) + { + if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO ) + || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) ) + { + /* FIXME: Do we have to arbitrate like audio? */ + if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED ) + *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC; + } + else + *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED; + } + else if( trak->mdia->minf->smhd ) + { + if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) ) + { + isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry; + isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &audio->extensions, ISOM_BOX_TYPE_ESDS ); + if( !esds || !esds->ES ) + return LSMASH_ERR_INVALID_DATA; + lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); + if( !summary ) + continue; + if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 ) + *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + else + *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( summary ) ); + lsmash_cleanup_summary( (lsmash_summary_t *)summary ); + } + else + /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */ + *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + } + else + ; /* FIXME: Do we have to set OD_profileLevelIndication? */ + } + return 0; +} + +int isom_setup_iods( isom_moov_t *moov ) +{ + if( !moov->iods && !isom_add_iods( moov ) ) + return LSMASH_ERR_NAMELESS; + isom_iods_t *iods = moov->iods; + int err = LSMASH_ERR_NAMELESS; + iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */ + if( !iods->OD ) + goto fail; + 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_t *trak = (isom_trak_t *)entry->data; + if( !trak + || !trak->tkhd ) + goto fail; + if( (err = isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli )) < 0 ) + goto fail; + if( (err = mp4sys_create_ES_ID_Inc( iods->OD, trak->tkhd->track_ID )) < 0 ) + goto fail; + } + if( (err = 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 )) < 0 ) + goto fail; + return 0; +fail: + isom_remove_box_by_itself( iods ); + return err; +} + +int lsmash_create_object_descriptor( lsmash_root_t *root ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + /* Return error if this file is not compatible with MP4 file format. */ + if( !file->mp4_version1 + && !file->mp4_version2 ) + return LSMASH_ERR_FUNCTION_PARAM; + return isom_setup_iods( file->moov ); +} + +/*---- finishing functions ----*/ + +int isom_complement_data_reference( isom_minf_t *minf ) +{ + if( !minf->dinf + || !minf->dinf->dref ) + return LSMASH_ERR_INVALID_DATA; + /* Complement data referece if absent. */ + if( !minf->dinf->dref->list.head ) + { + isom_dref_entry_t *url = isom_add_dref_entry( minf->dinf->dref, ISOM_BOX_TYPE_URL ); + if( !url ) + return LSMASH_ERR_NAMELESS; + url->flags = 0x000001; /* Media data is in the same file. */ + } + return 0; +} + +static lsmash_file_t *isom_get_written_media_file +( + isom_trak_t *trak, + uint32_t sample_description_index +) +{ + isom_minf_t *minf = trak->mdia->minf; + isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &minf->stbl->stsd->list, sample_description_index ); + isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &minf->dinf->dref->list, description->data_reference_index ); + lsmash_file_t *file = (!dref_entry || !dref_entry->ref_file) ? trak->file : dref_entry->ref_file; + if( !(file->flags & LSMASH_FILE_MODE_MEDIA) + || !(file->flags & LSMASH_FILE_MODE_WRITE) ) + return trak->file; + return file; +} + +int isom_check_large_offset_requirement +( + isom_moov_t *moov, + uint64_t meta_size +) +{ + for( lsmash_entry_t *entry = moov->trak_list.head; entry; ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + isom_stco_t *stco = trak->mdia->minf->stbl->stco; + if( !stco->list->tail /* no samples */ + || stco->large_presentation + || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX ) + { + entry = entry->next; + continue; /* no need to convert stco into co64 */ + } + /* stco->co64 conversion */ + int err = isom_convert_stco_to_co64( trak->mdia->minf->stbl ); + if( err < 0 ) + return err; + if( isom_update_box_size( moov ) == 0 ) + return LSMASH_ERR_INVALID_DATA; + entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */ + } + return 0; +} + +void isom_add_preceding_box_size +( + isom_moov_t *moov, + uint64_t preceding_size +) +{ + for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) + { + /* Apply to the chunks in the same file. */ + isom_trak_t *trak = (isom_trak_t *)entry->data; + isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc; + isom_stco_t *stco = trak->mdia->minf->stbl->stco; + lsmash_entry_t *stsc_entry = stsc->list->head; + isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL; + uint32_t chunk_number = 1; + for( lsmash_entry_t *stco_entry = stco->list->head; stco_entry; ) + { + if( stsc_data + && stsc_data->first_chunk == chunk_number ) + { + lsmash_file_t *ref_file = isom_get_written_media_file( trak, stsc_data->sample_description_index ); + stsc_entry = stsc_entry->next; + stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL; + if( ref_file != trak->file ) + { + /* The chunks are not contained in the same file. Skip applying the offset. + * If no more stsc entries, the rest of the chunks is not contained in the same file. */ + if( !stsc_entry || !stsc_data ) + break; + while( stco_entry && chunk_number < stsc_data->first_chunk ) + { + stco_entry = stco_entry->next; + ++chunk_number; + } + continue; + } + } + if( stco->large_presentation ) + ((isom_co64_entry_t *)stco_entry->data)->chunk_offset += preceding_size; + else + ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += preceding_size; + stco_entry = stco_entry->next; + ++chunk_number; + } + } +} + +int isom_establish_movie( lsmash_file_t *file ) +{ + assert( file == file->initializer ); + int err; + if( (err = isom_check_mandatory_boxes( file )) < 0 + || (err = isom_set_movie_creation_time( file )) < 0 ) + return err; + if( isom_update_box_size( file->moov ) == 0 ) + return LSMASH_ERR_INVALID_DATA; + return 0; +} + +int lsmash_finish_movie +( + lsmash_root_t *root, + lsmash_adhoc_remux_t *remux +) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file + || !file->bs + || !file->initializer->moov ) + return LSMASH_ERR_INVALID_DATA; + if( file->fragment ) + return isom_finish_final_fragment_movie( file, remux ); + if( file != file->initializer ) + return LSMASH_ERR_INVALID_DATA; + int err; + isom_moov_t *moov = file->moov; + for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *trak = (isom_trak_t *)entry->data; + if( !trak + || !trak->cache + || !trak->tkhd + || !trak->mdia + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stco + || !trak->mdia->minf->stbl->stco->list + || !trak->mdia->minf->stbl->stco->list->tail ) + return LSMASH_ERR_INVALID_DATA; + if( (err = isom_complement_data_reference( trak->mdia->minf )) < 0 ) + return err; + 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. */ + lsmash_edit_t edit; + edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) ); + edit.start_time = 0; + edit.rate = ISOM_EDIT_MODE_NORMAL; + 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; + if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) ) + return LSMASH_ERR_NAMELESS; + if( (err = isom_update_tkhd_duration( trak )) < 0 + || (err = isom_update_bitrate_description( trak->mdia )) < 0 ) + return err; + } + if( file->mp4_version1 == 1 && (err = isom_setup_iods( moov )) < 0 ) + return err; + if( (err = isom_establish_movie( file )) < 0 ) + return err; + /* Write the size of Media Data Box here. */ + lsmash_bs_t *bs = file->bs; + file->mdat->manager &= ~LSMASH_INCOMPLETE_BOX; + if( (err = isom_write_box( bs, (isom_box_t *)file->mdat )) < 0 ) + return err; + /* Write the Movie Box and a Meta Box if no optimization for progressive download. */ + uint64_t meta_size = file->meta ? file->meta->size : 0; + if( !remux ) + { + if( (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0 + || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 ) + return err; + file->size += moov->size + meta_size; + return 0; + } + /* stco->co64 conversion, depending on last chunk's offset */ + if( (err = isom_check_large_offset_requirement( moov, meta_size )) < 0 ) + return err; + /* now the amount of offset is fixed. */ + uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */ + /* buffer size must be at least mtf_size * 2 */ + remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 ); + /* Split to 2 buffers. */ + uint8_t *buf[2] = { NULL, NULL }; + if( (buf[0] = (uint8_t*)lsmash_malloc( remux->buffer_size )) == NULL ) + return LSMASH_ERR_MEMORY_ALLOC; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */ + size_t size = remux->buffer_size / 2; + buf[1] = buf[0] + size; + /* Now, the amount of the offset is fixed. apply it to stco/co64 */ + isom_add_preceding_box_size( moov, mtf_size ); + /* Backup starting area of mdat and write moov + meta there instead. */ + isom_mdat_t *mdat = file->mdat; + uint64_t total = file->size + mtf_size; + uint64_t placeholder_pos = file->free ? file->free->pos : mdat->pos; + if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0 ) + goto fail; + size_t read_num = size; + lsmash_bs_read_data( bs, buf[0], &read_num ); + uint64_t read_pos = bs->offset; + /* Write moov + meta there instead. */ + if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0 + || (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0 + || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 ) + goto fail; + uint64_t write_pos = bs->offset; + /* Update the positions */ + mdat->pos += mtf_size; + if( file->free ) + file->free->pos += mtf_size; + /* Move Media Data Box. */ + if( (err = isom_rearrange_data( file, remux, buf, read_num, size, read_pos, write_pos, total )) < 0 ) + goto fail; + file->size += mtf_size; + lsmash_free( buf[0] ); + return 0; +fail: + lsmash_free( buf[0] ); + return err; +} + +int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta ) +{ + if( isom_check_initializer_present( root ) < 0 || track_ID == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( file->fragment + && file->fragment->movie ) + { + isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID ); + if( !traf + || !traf->cache + || !traf->tfhd ) + return LSMASH_ERR_NAMELESS; + return isom_set_fragment_last_duration( traf, sample_delta ); + } + if( file != file->initializer ) + return LSMASH_ERR_INVALID_DATA; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->cache + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd + || !trak->mdia->minf->stbl->stsz + || !trak->mdia->minf->stbl->stts + || !trak->mdia->minf->stbl->stts->list ) + return LSMASH_ERR_NAMELESS; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_stts_t *stts = stbl->stts; + uint32_t sample_count = isom_get_sample_count( trak ); + int err; + if( !stts->list->tail ) + { + if( !sample_count ) + return 0; /* no samples */ + if( sample_count > 1 ) + return LSMASH_ERR_INVALID_DATA; /* irregular sample_count */ + /* Set the duration of the first sample. + * This duration is also the duration of the last sample. */ + if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 ) + return err; + 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 LSMASH_ERR_INVALID_DATA; + int no_last = (sample_count > i); + isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data; + if( !last_stts_data ) + return LSMASH_ERR_INVALID_DATA; + /* Consider QuikcTime fixed compression audio. */ + isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, + trak->cache->chunk.sample_description_index ); + if( !audio ) + return LSMASH_ERR_INVALID_DATA; + if( (audio->manager & LSMASH_AUDIO_DESCRIPTION) + && (audio->manager & LSMASH_QTFF_BASE) + && (audio->version == 1) + && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) ) + { + if( audio->samplesPerPacket == 0 ) + return LSMASH_ERR_INVALID_DATA; + int exclude_last_sample = no_last ? 0 : 1; + uint32_t j = audio->samplesPerPacket; + for( lsmash_entry_t *entry = stts->list->tail; entry && j > 1; entry = entry->prev ) + { + isom_stts_entry_t *stts_data = (isom_stts_entry_t *)entry->data; + if( !stts_data ) + return LSMASH_ERR_INVALID_DATA; + for( uint32_t k = exclude_last_sample; k < stts_data->sample_count && j > 1; k++ ) + { + sample_delta -= stts_data->sample_delta; + --j; + } + exclude_last_sample = 0; + } + } + /* Set sample_delta. */ + if( no_last ) + { + /* The duration of the last sample is not set yet. */ + if( sample_count - i > 1 ) + return LSMASH_ERR_INVALID_DATA; + /* Add a sample_delta. */ + if( sample_delta == last_stts_data->sample_delta ) + ++ last_stts_data->sample_count; + else if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 ) + return err; + } + /* The duration of the last sample is already set. Replace it with a new one. */ + else if( (err = isom_replace_last_sample_delta( stbl, sample_delta )) < 0 ) + return err; + return lsmash_update_track_duration( root, track_ID, sample_delta ); +} + +/*---- timeline manipulator ----*/ + +int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit ) +{ + if( isom_check_initializer_present( root ) < 0 + || edit.start_time < -1 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file->initializer; + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->edts + || !trak->edts->elst + || !trak->edts->elst->list ) + return LSMASH_ERR_NAMELESS; + isom_elst_t *elst = trak->edts->elst; + isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, edit_number ); + if( !data ) + return LSMASH_ERR_NAMELESS; + data->segment_duration = edit.duration; + data->media_time = edit.start_time; + data->media_rate = edit.rate; + if( elst->pos == 0 || !file->fragment || file->bs->unseekable ) + 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 = file->bs; + uint64_t current_pos = bs->offset; + uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12); + lsmash_bs_write_seek( bs, 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)LSMASH_MIN( data->segment_duration, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, (uint32_t)data->media_time ); + } + lsmash_bs_put_be32( bs, data->media_rate ); + int ret = lsmash_bs_flush_buffer( bs ); + lsmash_bs_write_seek( bs, current_pos, SEEK_SET ); + return ret; +} + +int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit ) +{ + if( isom_check_initializer_present( root ) < 0 || edit.start_time < -1 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file, track_ID ); + if( !trak + || !trak->tkhd ) + return LSMASH_ERR_NAMELESS; + edit.duration = (edit.duration || root->file->fragment) ? edit.duration + : trak->tkhd->duration ? trak->tkhd->duration + : isom_update_tkhd_duration( trak ) < 0 ? 0 + : trak->tkhd->duration; + if( (!trak->edts && !isom_add_edts( trak )) + || (!trak->edts->elst && !isom_add_elst( trak->edts )) ) + return LSMASH_ERR_NAMELESS; + int err = isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate ); + if( err < 0 ) + return err; + return isom_update_tkhd_duration( trak ); +} + +int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit ) +{ + if( isom_check_initializer_present( root ) < 0 || !edit ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak ) + return LSMASH_ERR_NAMELESS; + if( !trak->edts + || !trak->edts->elst ) + { + /* no edits */ + edit->duration = 0; + edit->start_time = 0; + edit->rate = 0; + return 0; + } + isom_elst_entry_t *elst = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, edit_number ); + if( !elst ) + return LSMASH_ERR_NAMELESS; + edit->duration = elst->segment_duration; + edit->start_time = elst->media_time; + edit->rate = elst->media_rate; + return 0; +} + +uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak + || !trak->edts + || !trak->edts->elst + || !trak->edts->elst->list ) + return 0; + return trak->edts->elst->list->entry_count; +} + +/*---- create / modification time fields manipulators ----*/ + +int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd ) + return LSMASH_ERR_NAMELESS; + 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 ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak + || !trak->tkhd ) + return LSMASH_ERR_NAMELESS; + 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( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file->initializer; + if( !file->moov + || !file->moov->mvhd ) + return LSMASH_ERR_INVALID_DATA; + isom_mvhd_t *mvhd = file->moov->mvhd; + mvhd->modification_time = isom_get_current_mp4time(); + /* overwrite strange creation_time */ + if( mvhd->creation_time > mvhd->modification_time ) + mvhd->creation_time = mvhd->modification_time; + return 0; +} + +/*---- sample manipulators ----*/ +lsmash_sample_t *lsmash_create_sample( uint32_t size ) +{ + lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) ); + if( !sample ) + return NULL; + if( size == 0 ) + return sample; + sample->data = lsmash_malloc( size ); + if( !sample->data ) + { + lsmash_free( sample ); + return NULL; + } + sample->length = size; + return sample; +} + +int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size ) +{ + if( !sample ) + return LSMASH_ERR_FUNCTION_PARAM; + if( size == 0 ) + { + lsmash_free( sample->data ); + sample->data = NULL; + sample->length = 0; + return 0; + } + if( size == sample->length ) + return 0; + uint8_t *data; + if( !sample->data ) + data = lsmash_malloc( size ); + else + data = lsmash_realloc( sample->data, size ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + sample->data = data; + sample->length = size; + return 0; +} + +void lsmash_delete_sample( lsmash_sample_t *sample ) +{ + if( !sample ) + return; + lsmash_free( sample->data ); + lsmash_free( sample ); +} + +isom_sample_pool_t *isom_create_sample_pool( uint64_t size ) +{ + isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) ); + if( !pool ) + return NULL; + if( size == 0 ) + return pool; + pool->data = lsmash_malloc( size ); + if( !pool->data ) + { + lsmash_free( pool ); + return NULL; + } + pool->alloc = size; + return pool; +} + +void isom_remove_sample_pool( isom_sample_pool_t *pool ) +{ + if( !pool ) + return; + lsmash_free( pool->data ); + lsmash_free( pool ); +} + +static uint32_t isom_add_size( isom_trak_t *trak, uint32_t sample_size ) +{ + if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) < 0 ) + return 0; + return isom_get_sample_count( trak ); +} + +static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts ) +{ + isom_stts_t *stts = stbl->stts; + if( stts->list->entry_count == 0 ) + { + if( isom_add_stts_entry( stbl, dts ) < 0 ) + 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 ) < 0 ) + return 0; + cache->dts = dts; + return sample_delta; +} + +static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts ) +{ + int err; + isom_ctts_t *ctts = stbl->ctts; + if( !ctts ) + { + if( cts == cache->dts ) + { + cache->cts = cts; + return 0; + } + /* Add ctts box and the first ctts entry. */ + if( !isom_add_ctts( stbl ) ) + return LSMASH_ERR_NAMELESS; + if( (err = isom_add_ctts_entry( stbl, 0 )) < 0 ) + 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; + if( sample_count != 1 ) + { + data->sample_count = sample_count - 1; + if( (err = isom_add_ctts_entry( stbl, cts - cache->dts )) < 0 ) + return err; + } + else + data->sample_offset = cts; + cache->cts = cts; + return 0; + } + if( !ctts->list ) + return LSMASH_ERR_INVALID_DATA; + 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( (err = isom_add_ctts_entry( stbl, sample_offset )) < 0 ) + return err; + cache->cts = cts; + return 0; +} + +static int isom_add_timestamp( isom_trak_t *trak, uint64_t dts, uint64_t cts ) +{ + if( !trak->cache + || !trak->mdia->minf->stbl->stts + || !trak->mdia->minf->stbl->stts->list ) + return LSMASH_ERR_INVALID_DATA; + lsmash_file_t *file = trak->file; + if( file->isom_compatible && file->qt_compatible && (cts - dts) > INT32_MAX ) + return LSMASH_ERR_INVALID_DATA; /* sample_offset is not compatible. */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_timestamp_t *ts_cache = &trak->cache->timestamp; + uint32_t sample_count = isom_get_sample_count( trak ); + uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0; + if( sample_count > 1 && sample_delta == 0 ) + return LSMASH_ERR_INVALID_DATA; + int err = isom_add_cts( stbl, ts_cache, cts ); + if( err < 0 ) + return err; + if( (cts + ts_cache->ctd_shift) < dts ) + { + if( (file->max_isom_version < 4 && !file->qt_compatible) /* Negative sample offset is not supported. */ + || (file->max_isom_version >= 4 && file->qt_compatible) /* ctts version 1 is not defined in QTFF. */ + || ((dts - cts) > INT32_MAX) ) /* Overflow */ + return LSMASH_ERR_INVALID_DATA; + ts_cache->ctd_shift = dts - cts; + if( stbl->ctts->version == 0 && !file->qt_compatible ) + stbl->ctts->version = 1; + } + if( trak->cache->fragment ) + { + isom_fragment_t *fragment_cache = trak->cache->fragment; + fragment_cache->last_duration = sample_delta; + fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts ); + } + return 0; +} + +static int isom_add_sync_point( isom_trak_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->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) /* no null check for prop */ + { + if( !cache->all_sync ) + return 0; + if( !stbl->stss && !isom_add_stss( stbl ) ) + return LSMASH_ERR_NAMELESS; + int err = isom_add_stss_entry( stbl, 1 ); + if( err < 0 ) /* Declare here the first sample is a sync sample. */ + return err; + 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 LSMASH_ERR_NAMELESS; + } + return isom_add_stss_entry( stbl, sample_number ); +} + +static int isom_add_partial_sync( isom_trak_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop ) +{ + if( !trak->file->qt_compatible ) + return 0; + if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) ) + return 0; + /* This sample is a partial sync sample. */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stps && !isom_add_stps( stbl ) ) + return LSMASH_ERR_NAMELESS; + return isom_add_stps_entry( stbl, sample_number ); +} + +static int isom_add_dependency_type( isom_trak_t *trak, lsmash_sample_property_t *prop ) +{ + if( !trak->file->qt_compatible && !trak->file->avc_extensions ) + return 0; + int compatibility = trak->file->avc_extensions && trak->file->qt_compatible ? 3 + : trak->file->qt_compatible ? 2 + : trak->file->avc_extensions ? 1 + : 0; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( stbl->sdtp ) + return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility ); + /* no null check for prop */ + if( !prop->allow_earlier + && !prop->leading + && !prop->independent + && !prop->disposable + && !prop->redundant ) + return 0; + if( !isom_add_sdtp( (isom_box_t *)stbl ) ) + return LSMASH_ERR_NAMELESS; + 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++ ) + { + int err = isom_add_sdtp_entry( (isom_box_t *)stbl, &null_prop, compatibility ); + if( err < 0 ) + return err; + } + return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility ); +} + +int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment ) +{ + isom_rap_entry_t *rap = group->random_access; + if( !rap ) + return 0; + assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data ); + rap->num_leading_samples_known = num_leading_samples_known; + /* Avoid duplication of sample group descriptions. */ + uint32_t group_description_index = is_fragment ? 0x10001 : 1; + for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next ) + { + isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_INVALID_DATA; + if( rap->num_leading_samples_known == data->num_leading_samples_known + && rap->num_leading_samples == data->num_leading_samples ) + { + /* The same description already exists. + * Remove the latest random access entry. */ + lsmash_remove_entry_tail( sgpd->list, NULL ); + /* Replace assigned group_description_index with the one corresponding the same description. */ + if( group->assignment->group_description_index == 0 ) + { + /* We don't create consecutive sample groups not assigned to 'rap '. + * So the previous sample group shall be a group of 'rap ' if any. */ + if( group->prev_assignment ) + { + assert( group->prev_assignment->group_description_index ); + group->prev_assignment->group_description_index = group_description_index; + } + } + else + group->assignment->group_description_index = group_description_index; + break; + } + ++group_description_index; + } + group->random_access = NULL; + return 0; +} + +int isom_group_random_access( isom_box_t *parent, lsmash_sample_t *sample ) +{ + if( parent->file->max_isom_version < 6 ) + return 0; + isom_sbgp_t *sbgp; + isom_sgpd_t *sgpd; + isom_cache_t *cache; + uint32_t sample_count; + int is_fragment; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) + { + isom_trak_t *trak = (isom_trak_t *)parent; + sbgp = isom_get_sample_to_group ( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP ); + sgpd = isom_get_sample_group_description( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP ); + cache = trak->cache; + sample_count = isom_get_sample_count( trak ); + is_fragment = 0; + } + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + { + isom_traf_t *traf = (isom_traf_t *)parent; + sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP ); + sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP ); + cache = traf->cache; + sample_count = cache->fragment->sample_count + 1; + is_fragment = 1; + } + else + { + assert( 0 ); + sbgp = NULL; + sgpd = NULL; + /* redundant initializations to suppress warnings from unclever compilers */ + cache = NULL; + sample_count = 0; + is_fragment = 0; + } + if( !sbgp || !sgpd ) + return 0; + lsmash_sample_property_t *prop = &sample->prop; + uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) + || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) + || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP) + || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete); + isom_rap_group_t *group = cache->rap; + if( !group ) + { + /* This sample is the first sample, create a grouping cache. */ + assert( sample_count == 1 ); + group = lsmash_malloc( sizeof(isom_rap_group_t) ); + if( !group ) + return LSMASH_ERR_MEMORY_ALLOC; + 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 + (is_fragment ? 0x10000 : 0) ); + } + else + { + /* The first sample is not always a random access point. */ + group->random_access = NULL; + group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); + } + if( !group->assignment ) + { + lsmash_free( group ); + return LSMASH_ERR_MEMORY_ALLOC; + } + group->prev_assignment = NULL; + group->is_prev_rap = is_rap; + cache->rap = group; + return 0; + } + int err; + if( group->is_prev_rap ) + { + /* OK. here, the previous sample is a menber of 'rap '. */ + if( !is_rap ) + { + /* This sample isn't a member of 'rap ' and the previous sample is. + * So we create a new group and set 0 on its group_description_index. */ + group->prev_assignment = group->assignment; + group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); + if( !group->assignment ) + { + lsmash_free( group ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) ) + { + /* 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( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 ) + return err; + group->random_access = isom_add_rap_group_entry( sgpd ); + group->prev_assignment = group->assignment; + group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) ); + if( !group->assignment ) + { + lsmash_free( group ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + 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( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 ) + return err; + group->random_access = isom_add_rap_group_entry( sgpd ); + group->prev_assignment = group->assignment; + group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) ); + if( !group->assignment ) + { + lsmash_free( group ); + return LSMASH_ERR_MEMORY_ALLOC; + } + } + else /* The previous and current sample aren't a member of 'rap '. */ + ++ group->assignment->sample_count; + /* Obtain the property of the latest random access point group. */ + if( !is_rap && group->random_access ) + { + if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN ) + { + /* We can no longer know num_leading_samples in this group. */ + if( (err = isom_rap_grouping_established( group, 0, sgpd, is_fragment )) < 0 ) + return err; + } + else + { + if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING + || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING ) + ++ group->random_access->num_leading_samples; + /* no more consecutive leading samples in this group */ + else if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 ) + return err; + } + } + group->is_prev_rap = is_rap; + return 0; +} + +static int isom_roll_grouping_established( isom_roll_group_t *group ) +{ + /* Avoid duplication of sample group descriptions. */ + isom_sgpd_t *sgpd = group->sgpd; + uint32_t group_description_index = group->is_fragment ? 0x10001 : 1; + for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) + { + isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_INVALID_DATA; + if( group->roll_distance == data->roll_distance ) + { + /* The same description already exists. + * Set the group_description_index corresponding the same description. */ + group->assignment->group_description_index = group_description_index; + return 0; + } + ++group_description_index; + } + /* Add a new roll recovery description. */ + if( !isom_add_roll_group_entry( sgpd, group->roll_distance ) ) + return LSMASH_ERR_MEMORY_ALLOC; + group->assignment->group_description_index = sgpd->list->entry_count + (group->is_fragment ? 0x10000 : 0); + return 0; +} + +static int isom_deduplicate_roll_group( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) +{ + /* Deduplication */ + uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1; + isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_get_entry_data( sbgp->list, current_group_number - 1 ); + for( lsmash_entry_t *entry = pool->head; entry; ) + { + isom_roll_group_t *group = (isom_roll_group_t *)entry->data; + if( !group + || !group->assignment ) + return LSMASH_ERR_INVALID_DATA; + if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED ) + return 0; + if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index ) + { + /* Merge the current group with the previous. */ + lsmash_entry_t *next_entry = entry->next; + prev_assignment->sample_count += group->assignment->sample_count; + int err; + if( (err = lsmash_remove_entry( sbgp->list, current_group_number, NULL )) < 0 + || (err = lsmash_remove_entry_direct( pool, entry, NULL )) < 0 ) + return err; + entry = next_entry; + } + else + { + entry = entry->next; + prev_assignment = group->assignment; + ++current_group_number; + } + } + return 0; +} + +/* Remove pooled caches that has become unnecessary. */ +static int isom_clean_roll_pool( lsmash_entry_list_t *pool ) +{ + for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head ) + { + isom_roll_group_t *group = (isom_roll_group_t *)entry->data; + if( !group ) + return LSMASH_ERR_INVALID_DATA; + if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED ) + return 0; + int err = lsmash_remove_entry_direct( pool, entry, NULL ); + if( err < 0 ) + return err; + } + return 0; +} + +static int isom_flush_roll_pool( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) +{ + int err; + for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) + { + isom_roll_group_t *group = (isom_roll_group_t *)entry->data; + if( !group ) + return LSMASH_ERR_INVALID_DATA; + if( group->delimited + && group->described == ROLL_DISTANCE_DETERMINED + && group->roll_distance != 0 + && (err = isom_roll_grouping_established( group )) < 0 ) + return err; + } + if( (err = isom_deduplicate_roll_group( sbgp, pool )) < 0 ) + return err; + return isom_clean_roll_pool( pool ); +} + +static int isom_all_recovery_described( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) +{ + for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) + { + isom_roll_group_t *group = (isom_roll_group_t *)entry->data; + if( !group ) + return LSMASH_ERR_INVALID_DATA; + group->described = ROLL_DISTANCE_DETERMINED; + } + return isom_flush_roll_pool( sbgp, pool ); +} + +int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) +{ + for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) + { + isom_roll_group_t *group = (isom_roll_group_t *)entry->data; + if( !group ) + return LSMASH_ERR_INVALID_DATA; + group->described = ROLL_DISTANCE_DETERMINED; + group->delimited = 1; + } + return isom_flush_roll_pool( sbgp, pool ); +} + +static isom_roll_entry_t *isom_get_roll_description +( + isom_roll_group_t *group +) +{ + uint32_t group_description_index = group->assignment->group_description_index; + if( group_description_index && group->is_fragment ) + { + assert( group_description_index > 0x10000 ); + group_description_index -= 0x10000; + } + return (isom_roll_entry_t *)lsmash_get_entry_data( group->sgpd->list, group_description_index ); +} + +int isom_group_roll_recovery( isom_box_t *parent, lsmash_sample_t *sample ) +{ + if( !parent->file->avc_extensions + && !parent->file->qt_compatible ) + return 0; + uint32_t sample_count; + int is_fragment; + isom_cache_t *cache; + lsmash_entry_list_t *sbgp_list; + lsmash_entry_list_t *sgpd_list; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) + { + isom_trak_t *trak = (isom_trak_t *)parent; + cache = trak->cache; + sbgp_list = &trak->mdia->minf->stbl->sbgp_list; + sgpd_list = &trak->mdia->minf->stbl->sgpd_list; + sample_count = isom_get_sample_count( trak ); + is_fragment = 0; + } + else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + { + if( parent->file->max_isom_version < 6 ) + return 0; + isom_traf_t *traf = (isom_traf_t *)parent; + cache = traf->cache; + sbgp_list = &traf->sbgp_list; + sgpd_list = &traf->sgpd_list; + sample_count = cache->fragment->sample_count + 1; + is_fragment = 1; + } + else + { + assert( 0 ); + return LSMASH_ERR_INVALID_DATA; + } + isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group ( sbgp_list ); + isom_sgpd_t *sgpd = isom_get_roll_recovery_sample_group_description( sgpd_list ); + if( !sbgp || !sgpd || sbgp->grouping_type != sgpd->grouping_type ) + return 0; + /* Check if 'roll' -> 'prol' conversion is needed. */ + if( cache->is_audio + && sbgp->grouping_type == ISOM_GROUP_TYPE_ROLL + && !(sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) + { + /* Since not every samples is a sync sample, change grouping_type into 'prol'. */ + sbgp->grouping_type = ISOM_GROUP_TYPE_PROL; + sgpd->grouping_type = ISOM_GROUP_TYPE_PROL; + } + lsmash_entry_list_t *pool = cache->roll.pool; + if( !pool ) + { + pool = lsmash_create_entry_list(); + if( !pool ) + return LSMASH_ERR_MEMORY_ALLOC; + cache->roll.pool = pool; + } + lsmash_sample_property_t *prop = &sample->prop; + isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count ); + int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags ); + int valid_pre_roll = !is_recovery_start + && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) + && (prop->pre_roll.distance > 0) + && (prop->pre_roll.distance <= -INT16_MIN); + int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start); + if( !new_group ) + { + /* Check pre-roll distance. */ + assert( group->assignment && group->sgpd ); + isom_roll_entry_t *prev_roll = isom_get_roll_description( group ); + if( !prev_roll ) + new_group = valid_pre_roll; + else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) ) + /* Pre-roll distance is different from the previous. */ + new_group = 1; + } + if( new_group ) + { + if( group ) + group->delimited = 1; + else + assert( sample_count == 1 ); + /* Create a new group. */ + group = lsmash_malloc_zero( sizeof(isom_roll_group_t) ); + if( !group ) + return LSMASH_ERR_MEMORY_ALLOC; + group->sgpd = sgpd; + group->prev_is_recovery_start = is_recovery_start; + group->is_fragment = is_fragment; + group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); + if( !group->assignment || lsmash_add_entry( pool, group ) < 0 ) + { + lsmash_free( group ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( is_recovery_start ) + { + /* a member of non-roll or post-roll group */ + group->first_sample = sample_count; + group->recovery_point = prop->post_roll.complete; + } + else + { + group->described = ROLL_DISTANCE_DETERMINED; + if( valid_pre_roll ) + { + /* a member of pre-roll group */ + group->roll_distance = -prop->pre_roll.distance; + int err = isom_roll_grouping_established( group ); + if( err < 0 ) + return err; + } + else + /* a member of non-roll group */ + group->roll_distance = 0; + } + } + else + { + group->prev_is_recovery_start = is_recovery_start; + group->assignment->sample_count += 1; + } + /* If encountered a RAP, all recovery is completed here. */ + if( prop->ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC + | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP + | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) ) + return isom_all_recovery_described( sbgp, pool ); + /* Check whether this sample is a random access recovery point or not. */ + for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) + { + group = (isom_roll_group_t *)entry->data; + if( !group ) + return LSMASH_ERR_INVALID_DATA; + if( group->described == ROLL_DISTANCE_DETERMINED ) + continue; + if( group->described == ROLL_DISTANCE_INITIALIZED ) + { + /* Let's consider the following picture sequence. + * coded order : P[0] P[1] P[2] P[3] P[4] P[5] + * DTS : 0 1 2 3 4 5 + * CTS : 2 4 3 6 7 5 + * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point. + * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC. + * Therefore, as follows, + * output order : P[0] P[2] P[1] P[5]|P[3] P[4] + * ---(incorrect?)--->| + * there is no guarantee that P[5] is decoded and output correctly. + * From this, it can be said that the roll_distance of this sequence is equal to 5. */ + isom_roll_entry_t *post_roll = isom_get_roll_description( group ); + if( post_roll && post_roll->roll_distance > 0 ) + { + if( group->rp_cts > sample->cts ) + /* Updated roll_distance for composition reordering. */ + post_roll->roll_distance = sample_count - group->first_sample; + if( ++ group->wait_and_see_count >= MAX_ROLL_WAIT_AND_SEE_COUNT ) + group->described = ROLL_DISTANCE_DETERMINED; + } + } + else if( prop->post_roll.identifier == group->recovery_point ) + { + int16_t distance = sample_count - group->first_sample; + group->rp_cts = sample->cts; + group->roll_distance = distance; + /* 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'. + * The roll_distance may be updated later because of composition reordering. */ + group->described = ROLL_DISTANCE_INITIALIZED; + group->wait_and_see_count = 0; + /* All groups with uninitialized roll_distance 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 || group->described != ROLL_DISTANCE_INITIALIZED ) + continue; + group->described = ROLL_DISTANCE_DETERMINED; + } + /* Cache the CTS of the first recovery point in a subsegment. */ + if( cache->fragment + && cache->fragment->subsegment.first_rp_number == 0 ) + { + cache->fragment->subsegment.first_rp_number = sample_count; + cache->fragment->subsegment.first_rp_cts = sample->cts; + cache->fragment->subsegment.first_ed_cts = sample->cts; + cache->fragment->subsegment.decodable = 1; + } + } + else + /* Random Accessible Point */ + return isom_all_recovery_described( sbgp, pool ); + } + } + return isom_flush_roll_pool( sbgp, pool ); +} + +/* returns 1 if pooled samples must be flushed. */ +/* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk. + see lsmash_append_sample for detail. */ +static int isom_add_chunk( isom_trak_t *trak, lsmash_sample_t *sample ) +{ + if( !trak->file + || !trak->cache + || !trak->mdia->mdhd + || trak->mdia->mdhd->timescale == 0 + || !trak->mdia->minf->dinf + || !trak->mdia->minf->dinf->dref + || !trak->mdia->minf->stbl->stsd + || !trak->mdia->minf->stbl->stsc + || !trak->mdia->minf->stbl->stsc->list ) + return LSMASH_ERR_INVALID_DATA; + isom_chunk_t *current = &trak->cache->chunk; + if( !current->pool ) + { + /* Very initial settings, just once per track */ + current->pool = isom_create_sample_pool( 0 ); + if( !current->pool ) + return LSMASH_ERR_MEMORY_ALLOC; + } + if( current->pool->sample_count == 0 ) + { + /* Cannot decide whether we should flush the current sample or not here yet. */ + current->chunk_number += 1; + current->sample_description_index = sample->index; + current->first_dts = sample->dts; + return 0; + } + if( sample->dts < current->first_dts ) + return LSMASH_ERR_INVALID_DATA; /* easy error check. */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + lsmash_file_t *file = isom_get_written_media_file( trak, current->sample_description_index ); + if( (current->sample_description_index == sample->index) + && (file->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale)) + && (file->max_chunk_size >= current->pool->size + sample->length) ) + 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 file after a chunk is fully determined with its contents. */ + /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */ + isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL; + /* Create a new chunk sequence in this track if needed. */ + int err; + if( (!last_stsc_data + || current->pool->sample_count != last_stsc_data->samples_per_chunk + || current->sample_description_index != last_stsc_data->sample_description_index) + && (err = isom_add_stsc_entry( stbl, current->chunk_number, + current->pool->sample_count, + current->sample_description_index )) < 0 ) + return err; + /* Add a new chunk offset in this track. */ + uint64_t offset = file->size; + if( file->fragment ) + offset += ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size; + if( (err = isom_add_stco_entry( stbl, offset )) < 0 ) + return err; + /* Update and re-initialize cache, using the current sample */ + current->chunk_number += 1; + 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_file_t *file, isom_sample_pool_t *pool ) +{ + if( !file + || !file->bs + || !file->bs->stream + || !(file->flags & LSMASH_FILE_MODE_WRITE) + || !(file->flags & LSMASH_FILE_MODE_MEDIA) + || ((file->flags & LSMASH_FILE_MODE_BOX) && !file->mdat) ) + return LSMASH_ERR_INVALID_DATA; + lsmash_bs_put_bytes( file->bs, pool->size, pool->data ); + int err = lsmash_bs_flush_buffer( file->bs ); + if( err < 0 ) + return err; + if( file->mdat ) + file->mdat->media_size += pool->size; + file->size += pool->size; + pool->sample_count = 0; + pool->size = 0; + return 0; +} + +int isom_update_sample_tables( isom_trak_t *trak, lsmash_sample_t *sample, uint32_t *samples_per_packet ) +{ + int err; + isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index ); + if( (audio->manager & LSMASH_AUDIO_DESCRIPTION) + && (audio->manager & LSMASH_QTFF_BASE) + && (audio->version == 1) + && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) ) + { + /* Add entries of the sample table for each uncompressed sample. */ + uint64_t sample_duration = trak->mdia->mdhd->timescale / (audio->samplerate >> 16); + if( audio->samplesPerPacket == 0 || sample_duration == 0 ) + return LSMASH_ERR_INVALID_DATA; + uint64_t sample_dts = sample->dts; + uint64_t sample_cts = sample->cts; + for( uint32_t i = 0; i < audio->samplesPerPacket; i++ ) + { + /* Add a size of uncomressed audio and increment sample_count. + * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */ + uint32_t sample_count = isom_add_size( trak, 1 ); + if( sample_count == 0 ) + return LSMASH_ERR_NAMELESS; + /* Add a decoding timestamp and a composition timestamp. */ + if( (err = isom_add_timestamp( trak, sample_dts, sample_cts )) < 0 ) + return err; + sample_dts += sample_duration; + sample_cts += sample_duration; + } + *samples_per_packet = audio->samplesPerPacket; + } + else + { + /* Add a sample_size and increment sample_count. */ + uint32_t sample_count = isom_add_size( trak, sample->length ); + if( sample_count == 0 ) + return LSMASH_ERR_NAMELESS; + /* Add a decoding timestamp and a composition timestamp. */ + if( (err = isom_add_timestamp( trak, sample->dts, sample->cts )) < 0 ) + return err; + /* Add a sync point if needed. */ + if( (err = isom_add_sync_point( trak, sample_count, &sample->prop )) < 0 ) + return err; + /* Add a partial sync point if needed. */ + if( (err = isom_add_partial_sync( trak, sample_count, &sample->prop )) < 0 ) + return err; + /* Add leading, independent, disposable and redundant information if needed. */ + if( (err = isom_add_dependency_type( trak, &sample->prop )) < 0 ) + return err; + /* Group samples into random access point type if needed. */ + if( (err = isom_group_random_access( (isom_box_t *)trak, sample )) < 0 ) + return err; + /* Group samples into random access recovery point type if needed. */ + if( (err = isom_group_roll_recovery( (isom_box_t *)trak, sample )) < 0 ) + return err; + *samples_per_packet = 1; + } + /* Add a chunk if needed. */ + return isom_add_chunk( trak, sample ); +} + +static int isom_output_cached_chunk( isom_trak_t *trak ) +{ + isom_chunk_t *chunk = &trak->cache->chunk; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL; + /* Create a new chunk sequence in this track if needed. */ + int err; + if( (!last_stsc_data + || chunk->pool->sample_count != last_stsc_data->samples_per_chunk + || chunk->sample_description_index != last_stsc_data->sample_description_index) + && (err = isom_add_stsc_entry( stbl, chunk->chunk_number, + chunk->pool->sample_count, + chunk->sample_description_index )) < 0 ) + return err; + lsmash_file_t *file = isom_get_written_media_file( trak, chunk->sample_description_index ); + if( file->fragment ) + { + /* Add a new chunk offset in this track. */ + if( (err = isom_add_stco_entry( stbl, file->size + ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size )) < 0 ) + return err; + return isom_append_fragment_track_run( file, chunk ); + } + /* Add a new chunk offset in this track. */ + if( (err = isom_add_stco_entry( stbl, file->size )) < 0 ) + return err; + /* Output pooled samples in this track. */ + return isom_write_pooled_samples( file, chunk->pool ); +} + +int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet ) +{ + uint64_t pool_size = pool->size + sample->length; + if( pool->alloc < pool_size ) + { + uint8_t *data; + uint64_t alloc = pool_size + (1<<16); + if( !pool->data ) + data = lsmash_malloc( alloc ); + else + data = lsmash_realloc( pool->data, alloc ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + pool->data = data; + pool->alloc = alloc; + } + memcpy( pool->data + pool->size, sample->data, sample->length ); + pool->size = pool_size; + pool->sample_count += samples_per_packet; + lsmash_delete_sample( sample ); + return 0; +} + +static int isom_append_sample_internal( isom_trak_t *trak, lsmash_sample_t *sample ) +{ + uint32_t samples_per_packet; + int ret = isom_update_sample_tables( trak, sample, &samples_per_packet ); + if( ret < 0 ) + return ret; + /* ret == 1 means pooled samples must be flushed. */ + isom_sample_pool_t *current_pool = trak->cache->chunk.pool; + if( ret == 1 ) + { + /* The sample_description_index in the cache is one of the next written chunk. + * Therefore, it cannot be referenced here. */ + lsmash_entry_list_t *stsc_list = trak->mdia->minf->stbl->stsc->list; + isom_stsc_entry_t *last_stsc_data = (isom_stsc_entry_t *)stsc_list->tail->data; + lsmash_file_t *file = isom_get_written_media_file( trak, last_stsc_data->sample_description_index ); + if( (ret = isom_write_pooled_samples( file, current_pool )) < 0 ) + return ret; + } + /* 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. */ + lsmash_file_t *file = trak->file; + double tolerance = file->max_async_tolerance; + for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) + { + isom_trak_t *other = (isom_trak_t *)entry->data; + if( trak == other ) + continue; + if( !other + || !other->cache + || !other->mdia + || !other->mdia->mdhd + || other->mdia->mdhd->timescale == 0 + || !other->mdia->minf + || !other->mdia->minf->stbl + || !other->mdia->minf->stbl->stsc + || !other->mdia->minf->stbl->stsc->list ) + return LSMASH_ERR_INVALID_DATA; + isom_chunk_t *chunk = &other->cache->chunk; + if( !chunk->pool || chunk->pool->sample_count == 0 ) + continue; + double diff = ((double)sample->dts / trak->mdia->mdhd->timescale) + - ((double)chunk->first_dts / other->mdia->mdhd->timescale); + if( diff > tolerance && (ret = isom_output_cached_chunk( other )) < 0 ) + return ret; + /* Note: we don't flush the cached chunk in the current track and the current sample here + * even if the conditional expression of '-diff > tolerance' meets. + * That's useless because appending a sample to another track would be a good equivalent. + * It's even harmful because it causes excess chunk division by calling + * isom_output_cached_chunk() which always generates a new chunk. + * Anyway some excess chunk division will be there, but rather less without it. + * To completely avoid this, we need to observe at least whether the current sample will be placed + * right next to the previous chunk of the same track or not. */ + } + /* anyway the current sample must be pooled. */ + return isom_pool_sample( current_pool, sample, samples_per_packet ); +} + +/* This function is for non-fragmented movie. */ +static int isom_append_sample( lsmash_file_t *file, uint32_t track_ID, lsmash_sample_t *sample ) +{ + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->file + || !trak->cache + || !trak->mdia + || !trak->mdia->mdhd + || trak->mdia->mdhd->timescale == 0 + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd + || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list ) + return LSMASH_ERR_NAMELESS; + /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */ + int err; + if( !file->mdat ) + { + if( !isom_add_mdat( file ) ) + return LSMASH_ERR_NAMELESS; + file->mdat->manager |= LSMASH_PLACEHOLDER; + if( (err = isom_write_box( file->bs, (isom_box_t *)file->mdat )) < 0 ) + return err; + assert( file->free ); + file->size += file->free->size + file->mdat->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 LSMASH_ERR_NAMELESS; + if( isom_is_lpcm_audio( sample_entry ) ) + { + uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket; + if( sample->length == frame_size ) + return isom_append_sample_internal( trak, sample ); + else if( sample->length < frame_size ) + return LSMASH_ERR_INVALID_DATA; + /* Append samples splitted into each LPCMFrame. */ + uint64_t dts = sample->dts; + uint64_t cts = sample->cts; + for( uint32_t offset = 0; offset < sample->length; offset += frame_size ) + { + lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size ); + if( !lpcm_sample ) + return LSMASH_ERR_MEMORY_ALLOC; + 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( (err = isom_append_sample_internal( trak, lpcm_sample )) < 0 ) + { + lsmash_delete_sample( lpcm_sample ); + return err; + } + } + lsmash_delete_sample( sample ); + return 0; + } + return isom_append_sample_internal( trak, sample ); +} + +static int isom_output_cache( isom_trak_t *trak ) +{ + int err; + isom_cache_t *cache = trak->cache; + if( cache->chunk.pool + && cache->chunk.pool->sample_count + && (err = isom_output_cached_chunk( trak )) < 0 ) + return err; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + for( lsmash_entry_t *entry = stbl->sgpd_list.head; entry; entry = entry->next ) + { + isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data; + if( !sgpd ) + return LSMASH_ERR_INVALID_DATA; + switch( sgpd->grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + { + isom_rap_group_t *group = cache->rap; + if( !group ) + { + if( stbl->file->fragment ) + continue; + else + return LSMASH_ERR_NAMELESS; + } + if( !group->random_access ) + continue; + group->random_access->num_leading_samples_known = 1; + break; + } + case ISOM_GROUP_TYPE_ROLL : + case ISOM_GROUP_TYPE_PROL : + if( !cache->roll.pool ) + { + if( stbl->file->fragment ) + continue; + else + return LSMASH_ERR_NAMELESS; + } + isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &stbl->sbgp_list ); + if( !sbgp ) + return LSMASH_ERR_NAMELESS; + if( (err = isom_all_recovery_completed( sbgp, cache->roll.pool )) < 0 ) + return err; + break; + default : + break; + } + } + return 0; +} + +int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( file->fragment + && file->fragment->movie ) + return isom_flush_fragment_pooled_samples( file, track_ID, last_sample_delta ); + if( file != file->initializer ) + return LSMASH_ERR_INVALID_DATA; + isom_trak_t *trak = isom_get_trak( file, 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 LSMASH_ERR_NAMELESS; + int err = isom_output_cache( trak ); + if( err < 0 ) + return err; + return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta ); +} + +int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ) +{ + if( isom_check_initializer_present( root ) < 0 + || track_ID == 0 + || !sample + || !sample->data ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + /* 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( !file + || !file->bs + || !(file->flags & LSMASH_FILE_MODE_BOX) + || file->max_chunk_duration == 0 + || file->max_async_tolerance == 0 ) + return LSMASH_ERR_NAMELESS; + /* Write File Type Box here if it was not written yet. */ + if( file->flags & LSMASH_FILE_MODE_INITIALIZATION ) + { + if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) ) + { + int err = isom_write_box( file->bs, (isom_box_t *)file->ftyp ); + if( err < 0 ) + return err; + file->size += file->ftyp->size; + } + } + if( (file->flags & LSMASH_FILE_MODE_FRAGMENTED) + && file->fragment + && file->fragment->pool ) + return isom_append_fragment_sample( file, track_ID, sample ); + if( file != file->initializer ) + return LSMASH_ERR_INVALID_DATA; + return isom_append_sample( file, track_ID, sample ); +} + +/*---- misc functions ----*/ + +int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak ) + return LSMASH_ERR_NAMELESS; + isom_remove_box_by_itself( trak->edts ); + return isom_update_tkhd_duration( trak ); +} + +void lsmash_delete_tyrant_chapter( lsmash_root_t *root ) +{ + if( isom_check_initializer_present( root ) < 0 + || !root->file->initializer->moov + || !root->file->initializer->moov->udta ) + return; + isom_remove_box_by_itself( root->file->moov->udta->chpl ); +} + +int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice ) +{ + if( isom_check_initializer_present( root ) < 0 + || (ISO_language && ISO_language < 0x800) + || !notice ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file->moov + || !file->isom_compatible ) + return LSMASH_ERR_NAMELESS; + isom_udta_t *udta; + if( track_ID ) + { + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak || (!trak->udta && !isom_add_udta( trak )) ) + return LSMASH_ERR_NAMELESS; + udta = trak->udta; + } + else + { + if( !file->moov->udta && !isom_add_udta( file->moov ) ) + return LSMASH_ERR_NAMELESS; + udta = file->moov->udta; + } + assert( udta ); + for( lsmash_entry_t *entry = udta->cprt_list.head; entry; entry = entry->next ) + { + isom_cprt_t *cprt = (isom_cprt_t *)entry->data; + if( !cprt || cprt->language == ISO_language ) + return LSMASH_ERR_NAMELESS; + } + if( !isom_add_cprt( udta ) ) + return LSMASH_ERR_NAMELESS; + isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list.tail->data; + cprt->language = ISO_language; + cprt->notice_length = strlen( notice ) + 1; + cprt->notice = lsmash_memdup( notice, cprt->notice_length ); + return 0; +} diff -Nru l-smash-1.9.1/core/meta.c l-smash-2.3.0/core/meta.c --- l-smash-1.9.1/core/meta.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/meta.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,501 @@ +/***************************************************************************** + * meta.c: + ***************************************************************************** + * Copyright (C) 2012-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include + +#include "box.h" + +static isom_data_t *isom_add_metadata( lsmash_file_t *file, + lsmash_itunes_metadata_item item, + char *meaning_string, char *name_string ) +{ + assert( file && file->moov ); + if( ((item == ITUNES_METADATA_ITEM_CUSTOM) && (!meaning_string || !meaning_string[0]) ) + || (!file->moov->udta && !isom_add_udta( file->moov )) + || (!file->moov->udta->meta && !isom_add_meta( file->moov->udta )) + || (!file->moov->udta->meta->ilst && !isom_add_ilst( file->moov->udta->meta )) ) + return NULL; + if( !file->moov->udta->meta->hdlr ) + { + if( !isom_add_hdlr( file->moov->udta->meta ) + || isom_setup_handler_reference( file->moov->udta->meta->hdlr, ISOM_META_HANDLER_TYPE_ITUNES_METADATA ) < 0 ) + return NULL; + } + isom_ilst_t *ilst = file->moov->udta->meta->ilst; + if( !isom_add_metaitem( ilst, item ) ) + return NULL; + isom_metaitem_t *metaitem = (isom_metaitem_t *)ilst->metaitem_list.tail->data; + if( item == ITUNES_METADATA_ITEM_CUSTOM ) + { + if( !isom_add_mean( metaitem ) ) + goto fail; + isom_mean_t *mean = metaitem->mean; + mean->meaning_string_length = strlen( meaning_string ); /* No null terminator */ + mean->meaning_string = lsmash_memdup( meaning_string, mean->meaning_string_length ); + if( !mean->meaning_string ) + goto fail; + if( name_string && name_string[0] ) + { + if( !isom_add_name( metaitem ) ) + goto fail; + isom_name_t *name = metaitem->name; + name->name_length = strlen( name_string ); /* No null terminator */ + name->name = lsmash_memdup( name_string, name->name_length ); + if( !name->name ) + goto fail; + } + } + if( !isom_add_data( metaitem ) ) + goto fail; + return metaitem->data; +fail: + isom_remove_box_by_itself( metaitem ); + return NULL; +} + +static int isom_set_itunes_metadata_string( lsmash_file_t *file, + lsmash_itunes_metadata_item item, + lsmash_itunes_metadata_value_t value, char *meaning, char *name ) +{ + uint32_t value_length = strlen( value.string ); + if( item == ITUNES_METADATA_ITEM_DESCRIPTION && value_length > 255 ) + item = ITUNES_METADATA_ITEM_LONG_DESCRIPTION; + isom_data_t *data = isom_add_metadata( file, item, meaning, name ); + if( !data ) + return LSMASH_ERR_NAMELESS; + data->type_code = ITUNES_METADATA_SUBTYPE_UTF8; + data->value_length = value_length; /* No null terminator */ + data->value = lsmash_memdup( value.string, data->value_length ); + if( !data->value ) + { + isom_ilst_t *ilst = file->moov->udta->meta->ilst; + isom_remove_box_by_itself( ilst->metaitem_list.tail->data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_set_itunes_metadata_integer( lsmash_file_t *file, + lsmash_itunes_metadata_item item, + lsmash_itunes_metadata_value_t value, char *meaning, char *name ) +{ + static const struct + { + lsmash_itunes_metadata_item item; + int length; + } metadata_code_type_table[] = + { + { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, 1 }, + { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, 2 }, + { ITUNES_METADATA_ITEM_CONTENT_RATING, 1 }, + { ITUNES_METADATA_ITEM_MEDIA_TYPE, 1 }, + { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, 2 }, + { ITUNES_METADATA_ITEM_TV_EPISODE, 4 }, + { ITUNES_METADATA_ITEM_TV_SEASON, 4 }, + { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, 1 }, + { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, 4 }, + { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, 4 }, + { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, 4 }, + { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, 4 }, + { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, 8 }, + { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, 4 }, + { ITUNES_METADATA_ITEM_CUSTOM, 8 }, + { 0, 0 } + }; + int i; + for( i = 0; metadata_code_type_table[i].item; i++ ) + if( item == metadata_code_type_table[i].item ) + break; + if( metadata_code_type_table[i].length == 0 ) + return LSMASH_ERR_NAMELESS; + isom_data_t *data = isom_add_metadata( file, item, meaning, name ); + if( !data ) + return LSMASH_ERR_NAMELESS; + if( item == ITUNES_METADATA_ITEM_PREDEFINED_GENRE ) + data->type_code = ITUNES_METADATA_SUBTYPE_IMPLICIT; + else + data->type_code = ITUNES_METADATA_SUBTYPE_INTEGER; + data->value_length = metadata_code_type_table[i].length; + uint8_t temp[8]; + for( i = 0; i < data->value_length; i++ ) + { + int shift = (data->value_length - i - 1) * 8; + temp[i] = (value.integer >> shift) & 0xff; + } + data->value = lsmash_memdup( temp, data->value_length ); + if( !data->value ) + { + isom_ilst_t *ilst = file->moov->udta->meta->ilst; + isom_remove_box_by_itself( ilst->metaitem_list.tail->data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_set_itunes_metadata_boolean( lsmash_file_t *file, + lsmash_itunes_metadata_item item, + lsmash_itunes_metadata_value_t value, char *meaning, char *name ) +{ + isom_data_t *data = isom_add_metadata( file, item, meaning, name ); + if( !data ) + return LSMASH_ERR_NAMELESS; + data->type_code = ITUNES_METADATA_SUBTYPE_INTEGER; + data->value_length = 1; + uint8_t temp = (uint8_t)value.boolean; + data->value = lsmash_memdup( &temp, 1 ); + if( !data->value ) + { + isom_ilst_t *ilst = file->moov->udta->meta->ilst; + isom_remove_box_by_itself( ilst->metaitem_list.tail->data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static int isom_set_itunes_metadata_binary( lsmash_file_t *file, + lsmash_itunes_metadata_item item, + lsmash_itunes_metadata_value_t value, char *meaning, char *name ) +{ + isom_data_t *data = isom_add_metadata( file, item, meaning, name ); + if( !data ) + return LSMASH_ERR_NAMELESS; + switch( item ) + { + case ITUNES_METADATA_ITEM_COVER_ART : + if( value.binary.subtype != ITUNES_METADATA_SUBTYPE_JPEG + && value.binary.subtype != ITUNES_METADATA_SUBTYPE_PNG + && value.binary.subtype != ITUNES_METADATA_SUBTYPE_BMP ) + return LSMASH_ERR_FUNCTION_PARAM; + break; + case ITUNES_METADATA_ITEM_DISC_NUMBER : + case ITUNES_METADATA_ITEM_TRACK_NUMBER : + value.binary.subtype = ITUNES_METADATA_SUBTYPE_IMPLICIT; + break; + default : + break; + } + switch( value.binary.subtype ) + { + case ITUNES_METADATA_SUBTYPE_UUID : + if( value.binary.size != 16 ) + return LSMASH_ERR_FUNCTION_PARAM; + break; + case ITUNES_METADATA_SUBTYPE_DURATION : + if( value.binary.size != 4 ) + return LSMASH_ERR_FUNCTION_PARAM; + break; + case ITUNES_METADATA_SUBTYPE_TIME : + if( value.binary.size != 4 && value.binary.size != 8 ) + return LSMASH_ERR_FUNCTION_PARAM; + break; + case ITUNES_METADATA_SUBTYPE_INTEGER : + if( value.binary.size != 1 && value.binary.size != 2 + && value.binary.size != 3 && value.binary.size != 4 + && value.binary.size != 8 ) + return LSMASH_ERR_FUNCTION_PARAM; + break; + case ITUNES_METADATA_SUBTYPE_RIAAPA : + if( value.binary.size != 1 ) + return LSMASH_ERR_FUNCTION_PARAM; + break; + default : + break; + } + data->type_code = value.binary.subtype; + data->value_length = value.binary.size; + data->value = lsmash_memdup( value.binary.data, value.binary.size ); + if( !data->value ) + { + isom_ilst_t *ilst = file->moov->udta->meta->ilst; + isom_remove_box_by_itself( ilst->metaitem_list.tail->data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +int lsmash_set_itunes_metadata( lsmash_root_t *root, lsmash_itunes_metadata_t metadata ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + static const struct + { + lsmash_itunes_metadata_item item; + int (*func_set_itunes_metadata)( lsmash_file_t *, lsmash_itunes_metadata_item, lsmash_itunes_metadata_value_t, char *, char * ); + } itunes_metadata_function_mapping[] = + { + { ITUNES_METADATA_ITEM_ALBUM_NAME, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ARTIST, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_USER_COMMENT, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_RELEASE_DATE, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ENCODED_BY, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_USER_GENRE, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_GROUPING, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_LYRICS, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_TITLE, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_TRACK_SUBTITLE, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ENCODING_TOOL, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_COMPOSER, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ALBUM_ARTIST, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_PODCAST_CATEGORY, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_COPYRIGHT, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_DESCRIPTION, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_GROUPING_DRAFT, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_PODCAST_KEYWORD, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_LONG_DESCRIPTION, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_PURCHASE_DATE, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_TV_EPISODE_ID, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_TV_NETWORK, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_TV_SHOW_NAME, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_PURCHASE_ACCOUNT_ID, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ARTIST, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM_ARTIST, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_COMPOSER, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_NAME, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_SHOW, isom_set_itunes_metadata_string }, + { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_CONTENT_RATING, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_MEDIA_TYPE, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_TV_EPISODE, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_TV_SEASON, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, isom_set_itunes_metadata_integer }, + { ITUNES_METADATA_ITEM_DISC_COMPILATION, isom_set_itunes_metadata_boolean }, + { ITUNES_METADATA_ITEM_HIGH_DEFINITION_VIDEO, isom_set_itunes_metadata_boolean }, + { ITUNES_METADATA_ITEM_PODCAST, isom_set_itunes_metadata_boolean }, + { ITUNES_METADATA_ITEM_GAPLESS_PLAYBACK, isom_set_itunes_metadata_boolean }, + { ITUNES_METADATA_ITEM_COVER_ART, isom_set_itunes_metadata_binary }, + { ITUNES_METADATA_ITEM_DISC_NUMBER, isom_set_itunes_metadata_binary }, + { ITUNES_METADATA_ITEM_TRACK_NUMBER, isom_set_itunes_metadata_binary }, + { 0, NULL } + }; + lsmash_file_t *file = root->file; + for( int i = 0; itunes_metadata_function_mapping[i].func_set_itunes_metadata; i++ ) + if( metadata.item == itunes_metadata_function_mapping[i].item ) + return itunes_metadata_function_mapping[i].func_set_itunes_metadata( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); + if( metadata.item == ITUNES_METADATA_ITEM_CUSTOM ) + switch( metadata.type ) + { + case ITUNES_METADATA_TYPE_STRING : + return isom_set_itunes_metadata_string( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); + case ITUNES_METADATA_TYPE_INTEGER : + return isom_set_itunes_metadata_integer( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); + case ITUNES_METADATA_TYPE_BOOLEAN : + return isom_set_itunes_metadata_boolean( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); + case ITUNES_METADATA_TYPE_BINARY : + return isom_set_itunes_metadata_binary( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); + default : + break; + } + return LSMASH_ERR_FUNCTION_PARAM; +} + +static lsmash_itunes_metadata_type isom_get_itunes_metadata_type( lsmash_itunes_metadata_item item ) +{ + static const struct + { + lsmash_itunes_metadata_item item; + lsmash_itunes_metadata_type type; + } itunes_metadata_item_type_mapping[] = + { + { ITUNES_METADATA_ITEM_ALBUM_NAME, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ARTIST, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_USER_COMMENT, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_RELEASE_DATE, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ENCODED_BY, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_USER_GENRE, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_GROUPING, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_LYRICS, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_TITLE, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_TRACK_SUBTITLE, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ENCODING_TOOL, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_COMPOSER, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ALBUM_ARTIST, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_PODCAST_CATEGORY, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_COPYRIGHT, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_DESCRIPTION, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_GROUPING_DRAFT, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_PODCAST_KEYWORD, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_LONG_DESCRIPTION, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_PURCHASE_DATE, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_TV_EPISODE_ID, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_TV_NETWORK, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_TV_SHOW_NAME, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_PURCHASE_ACCOUNT_ID, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ARTIST, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM_ARTIST, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_COMPOSER, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_NAME, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_SHOW, ITUNES_METADATA_TYPE_STRING }, + { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_CONTENT_RATING, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_MEDIA_TYPE, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_TV_EPISODE, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_TV_SEASON, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, ITUNES_METADATA_TYPE_INTEGER }, + { ITUNES_METADATA_ITEM_DISC_COMPILATION, ITUNES_METADATA_TYPE_BOOLEAN }, + { ITUNES_METADATA_ITEM_HIGH_DEFINITION_VIDEO, ITUNES_METADATA_TYPE_BOOLEAN }, + { ITUNES_METADATA_ITEM_PODCAST, ITUNES_METADATA_TYPE_BOOLEAN }, + { ITUNES_METADATA_ITEM_GAPLESS_PLAYBACK, ITUNES_METADATA_TYPE_BOOLEAN }, + { ITUNES_METADATA_ITEM_COVER_ART, ITUNES_METADATA_TYPE_BINARY }, + { ITUNES_METADATA_ITEM_DISC_NUMBER, ITUNES_METADATA_TYPE_BINARY }, + { ITUNES_METADATA_ITEM_TRACK_NUMBER, ITUNES_METADATA_TYPE_BINARY }, + { 0, ITUNES_METADATA_TYPE_NONE } + }; + for( int i = 0; itunes_metadata_item_type_mapping[i].type != ITUNES_METADATA_TYPE_NONE; i++ ) + if( item == itunes_metadata_item_type_mapping[i].item ) + return itunes_metadata_item_type_mapping[i].type; + return ITUNES_METADATA_TYPE_NONE; +} + +int lsmash_get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata ) +{ + if( isom_check_initializer_present( root ) < 0 || !metadata ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file->initializer; + if( !file->moov + || !file->moov->udta + || !file->moov->udta->meta + || !file->moov->udta->meta->ilst ) + return LSMASH_ERR_NAMELESS; + isom_ilst_t *ilst = file->moov->udta->meta->ilst; + isom_metaitem_t *metaitem = (isom_metaitem_t *)lsmash_get_entry_data( &ilst->metaitem_list, metadata_number ); + if( !metaitem || !metaitem->data || !metaitem->data->value || metaitem->data->value_length == 0 ) + return LSMASH_ERR_NAMELESS; + /* Get 'item'. */ + metadata->item = metaitem->type.fourcc; + /* Get 'type'. */ + metadata->type = isom_get_itunes_metadata_type( metadata->item ); + /* Get 'meaning'. */ + int err = LSMASH_ERR_MEMORY_ALLOC; + isom_mean_t *mean = metaitem->mean; + if( mean ) + { + uint8_t *temp = lsmash_malloc( mean->meaning_string_length + 1 ); + if( !temp ) + goto fail; + memcpy( temp, mean->meaning_string, mean->meaning_string_length ); + temp[ mean->meaning_string_length ] = 0; + metadata->meaning = (char *)temp; + } + else + metadata->meaning = NULL; + /* Get 'name'. */ + isom_name_t *name = metaitem->name; + if( name ) + { + uint8_t *temp = lsmash_malloc( name->name_length + 1 ); + if( !temp ) + goto fail; + memcpy( temp, name->name, name->name_length ); + temp[ name->name_length ] = 0; + metadata->name = (char *)temp; + } + else + metadata->name = NULL; + /* Get 'value'. */ + isom_data_t *data = metaitem->data; + switch( metadata->type ) + { + case ITUNES_METADATA_TYPE_STRING : + { + uint8_t *temp = lsmash_malloc( data->value_length + 1 ); + if( !temp ) + goto fail; + memcpy( temp, data->value, data->value_length ); + temp[ data->value_length ] = 0; + metadata->value.string = (char *)temp; + break; + } + case ITUNES_METADATA_TYPE_INTEGER : + if( data->value_length > 8 ) + { + err = LSMASH_ERR_INVALID_DATA; + goto fail; + } + metadata->value.integer = 0; + for( uint32_t i = 0; i < data->value_length; i++ ) + { + int shift = (data->value_length - i - 1) * 8; + metadata->value.integer |= (uint64_t)data->value[i] << shift; + } + break; + case ITUNES_METADATA_TYPE_BOOLEAN : + metadata->value.boolean = !!data->value[0]; + break; + default : + metadata->type = ITUNES_METADATA_TYPE_BINARY; + metadata->value.binary.subtype = data->type_code; + metadata->value.binary.size = data->value_length; + metadata->value.binary.data = lsmash_memdup( data->value, data->value_length ); + if( !metadata->value.binary.data ) + goto fail; + break; + } + return 0; +fail: + lsmash_freep( &metadata->meaning ); + lsmash_freep( &metadata->name ); + return err; +} + +uint32_t lsmash_count_itunes_metadata( lsmash_root_t *root ) +{ + if( isom_check_initializer_present( root ) < 0 + || !root->file->initializer->moov + || !root->file->initializer->moov->udta + || !root->file->initializer->moov->udta->meta + || !root->file->initializer->moov->udta->meta->ilst ) + return 0; + return root->file->initializer->moov->udta->meta->ilst->metaitem_list.entry_count; +} + +void lsmash_cleanup_itunes_metadata( lsmash_itunes_metadata_t *metadata ) +{ + if( !metadata ) + return; + lsmash_freep( &metadata->meaning ); + lsmash_freep( &metadata->name ); + if( metadata->type == ITUNES_METADATA_TYPE_STRING ) + lsmash_freep( &metadata->value.string ); + else if( metadata->type == ITUNES_METADATA_TYPE_BINARY ) + lsmash_freep( &metadata->value.binary.data ); +} diff -Nru l-smash-1.9.1/core/print.c l-smash-2.3.0/core/print.c --- l-smash-1.9.1/core/print.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/print.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,2683 @@ +/***************************************************************************** + * print.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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. */ + +#ifdef LSMASH_DEMUXER_ENABLED + +#include "common/internal.h" /* must be placed first */ + +#include +#include +#include +#include /* for isom_iprintf */ + +#include "box.h" + + +typedef int (*isom_print_box_t)( FILE *, lsmash_file_t *, isom_box_t *, int ); + +typedef struct +{ + int level; + isom_box_t *box; + isom_print_box_t func; +} isom_print_entry_t; + +static void isom_ifprintf_duration( FILE *fp, int indent, char *field_name, uint64_t duration, uint32_t timescale ) +{ + if( !timescale ) + { + lsmash_ifprintf( fp, indent, "duration = %"PRIu64"\n", duration ); + return; + } + int dur = duration / timescale; + int hour = (dur / 3600) % 24; + int min = (dur / 60) % 60; + int sec = dur % 60; + int ms = ((double)duration / timescale - (hour * 3600 + min * 60 + sec)) * 1e3 + 0.5; + static char str[32]; + sprintf( str, "%02d:%02d:%02d.%03d", hour, min, sec, ms ); + lsmash_ifprintf( fp, indent, "%s = %"PRIu64" (%s)\n", field_name, duration, str ); +} + +static char *isom_mp4time2utc( uint64_t mp4time ) +{ + int year_offset = mp4time / 31536000; + int leap_years = year_offset / 4 + ((mp4time / 86400) > 366); /* 1904 itself is leap year */ + int day = (mp4time / 86400) - (year_offset * 365) - leap_years + 1; + while( day < 1 ) + { + --year_offset; + leap_years = year_offset / 4 + ((mp4time / 86400) > 366); + day = (mp4time / 86400) - (year_offset * 365) - leap_years + 1; + } + int year = 1904 + year_offset; + int is_leap = (!(year % 4) && (year % 100)) || !(year % 400); + static const int month_days[13] = { 29, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int month; + for( month = 1; month <= 12; month++ ) + { + int i = (month == 2 && is_leap) ? 0 : month; + if( day <= month_days[i] ) + break; + day -= month_days[i]; + } + int hour = (mp4time / 3600) % 24; + int min = (mp4time / 60) % 60; + int sec = mp4time % 60; + static char utc[64]; + sprintf( utc, "UTC %d/%02d/%02d, %02d:%02d:%02d\n", year, month, day, hour, min, sec ); + return utc; +} + +static void isom_ifprintf_matrix( FILE *fp, int indent, int32_t *matrix ) +{ + lsmash_ifprintf( fp, indent, "| a, b, u | | %f, %f, %f |\n", lsmash_fixed2double( matrix[0], 16 ), + lsmash_fixed2double( matrix[1], 16 ), + lsmash_fixed2double( matrix[2], 30 ) ); + lsmash_ifprintf( fp, indent, "| c, d, v | = | %f, %f, %f |\n", lsmash_fixed2double( matrix[3], 16 ), + lsmash_fixed2double( matrix[4], 16 ), + lsmash_fixed2double( matrix[5], 30 ) ); + lsmash_ifprintf( fp, indent, "| x, y, w | | %f, %f, %f |\n", lsmash_fixed2double( matrix[6], 16 ), + lsmash_fixed2double( matrix[7], 16 ), + lsmash_fixed2double( matrix[8], 30 ) ); +} + +static void isom_ifprintf_rgb_color( FILE *fp, int indent, uint16_t *color ) +{ + lsmash_ifprintf( fp, indent, "{ R, G, B } = { %"PRIu16", %"PRIu16", %"PRIu16" }\n", color[0], color[1], color[2] ); +} + +static void isom_ifprintf_rgba_color( FILE *fp, int indent, uint8_t *color ) +{ + lsmash_ifprintf( fp, indent, "{ R, G, B, A } = { %"PRIu8", %"PRIu8", %"PRIu8", %"PRIu8" }\n", color[0], color[1], color[2], color[3] ); +} + +static char *isom_unpack_iso_language( uint16_t language ) +{ + static char unpacked[4]; + unpacked[0] = ((language >> 10) & 0x1f) + 0x60; + unpacked[1] = ((language >> 5) & 0x1f) + 0x60; + unpacked[2] = ( language & 0x1f) + 0x60; + unpacked[3] = 0; + return unpacked; +} + +static void isom_ifprintf_sample_description_common_reserved( FILE *fp, int indent, uint8_t *reserved ) +{ + uint64_t temp = ((uint64_t)reserved[0] << 40) + | ((uint64_t)reserved[1] << 32) + | ((uint64_t)reserved[2] << 24) + | ((uint64_t)reserved[3] << 16) + | ((uint64_t)reserved[4] << 8) + | (uint64_t)reserved[5]; + lsmash_ifprintf( fp, indent, "reserved = 0x%012"PRIx64"\n", temp ); +} + +static void isom_ifprintf_sample_flags( FILE *fp, int indent, char *field_name, 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_ifprintf( fp, indent++, "%s = 0x%08"PRIx32"\n", field_name, temp ); + if( flags->is_leading & ISOM_SAMPLE_IS_UNDECODABLE_LEADING ) lsmash_ifprintf( fp, indent, "undecodable leading\n" ); + else if( flags->is_leading & ISOM_SAMPLE_IS_NOT_LEADING ) lsmash_ifprintf( fp, indent, "non-leading\n" ); + else if( flags->is_leading & ISOM_SAMPLE_IS_DECODABLE_LEADING ) lsmash_ifprintf( fp, indent, "decodable leading\n" ); + if( flags->sample_depends_on & ISOM_SAMPLE_IS_INDEPENDENT ) lsmash_ifprintf( fp, indent, "independent\n" ); + else if( flags->sample_depends_on & ISOM_SAMPLE_IS_NOT_INDEPENDENT ) lsmash_ifprintf( fp, indent, "dependent\n" ); + if( flags->sample_is_depended_on & ISOM_SAMPLE_IS_NOT_DISPOSABLE ) lsmash_ifprintf( fp, indent, "non-disposable\n" ); + else if( flags->sample_is_depended_on & ISOM_SAMPLE_IS_DISPOSABLE ) lsmash_ifprintf( fp, indent, "disposable\n" ); + if( flags->sample_has_redundancy & ISOM_SAMPLE_HAS_REDUNDANCY ) lsmash_ifprintf( fp, indent, "redundant\n" ); + else if( flags->sample_has_redundancy & ISOM_SAMPLE_HAS_NO_REDUNDANCY ) lsmash_ifprintf( fp, indent, "non-redundant\n" ); + if( flags->sample_padding_value ) + lsmash_ifprintf( fp, indent, "padding_bits = %"PRIu8"\n", flags->sample_padding_value ); + lsmash_ifprintf( fp, indent, flags->sample_is_non_sync_sample ? "non-sync sample\n" : "sync sample\n" ); + lsmash_ifprintf( fp, indent, "degradation_priority = %"PRIu16"\n", flags->sample_degradation_priority ); +} + +static inline int isom_print_simple( FILE *fp, isom_box_t *box, int level, char *name ) +{ + int indent = level; + if( box->type.fourcc != ISOM_BOX_TYPE_UUID.fourcc ) + { + lsmash_ifprintf( fp, indent++, "[%s: %s]\n", isom_4cc2str( box->type.fourcc ), name ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + } + else + { + lsmash_ifprintf( fp, indent++, "[uuid: UUID Box]\n" ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + lsmash_ifprintf( fp, indent++, "usertype\n" ); + if( isom_is_printable_4cc( box->type.user.fourcc ) ) + lsmash_ifprintf( fp, indent, "type = %s\n", isom_4cc2str( box->type.user.fourcc ) ); + lsmash_ifprintf( fp, indent, "name = %s\n", name ); + lsmash_ifprintf( fp, indent, "uuid = 0x%08"PRIx32"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"0x%08"PRIx32"\n", + box->type.user.fourcc, + (box->type.user.id[0] << 8) | box->type.user.id[1], (box->type.user.id[2] << 8) | box->type.user.id[3], + (box->type.user.id[4] << 8) | box->type.user.id[5], (box->type.user.id[6] << 8) | box->type.user.id[7], + (box->type.user.id[8] << 24) | (box->type.user.id[9] << 16) | (box->type.user.id[10] << 8) | box->type.user.id[11] ); + } + return 0; +} + +static void isom_print_basebox_common( FILE *fp, int indent, isom_box_t *box, char *name ) +{ + isom_print_simple( fp, box, indent, name ); +} + +static void isom_print_fullbox_common( FILE *fp, int indent, isom_box_t *box, char *name ) +{ + isom_print_simple( fp, box, indent++, name ); + lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", box->version ); + lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", box->flags & 0x00ffffff ); +} + +static void isom_print_box_common( FILE *fp, int indent, isom_box_t *box, char *name ) +{ + isom_box_t *parent = box->parent; + if( parent && lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) + { + isom_print_basebox_common( fp, indent, box, name ); + return; + } + if( isom_is_fullbox( box ) ) + isom_print_fullbox_common( fp, indent, box, name ); + else + isom_print_basebox_common( fp, indent, box, name ); +} + +static int isom_print_unknown( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + int indent = level; + if( box->type.fourcc != ISOM_BOX_TYPE_UUID.fourcc ) + { + lsmash_ifprintf( fp, indent++, "[%s]\n", isom_4cc2str( box->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + } + else + { + lsmash_ifprintf( fp, indent++, "[uuid: UUID Box]\n" ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + lsmash_ifprintf( fp, indent++, "usertype\n" ); + if( isom_is_printable_4cc( box->type.user.fourcc ) ) + lsmash_ifprintf( fp, indent, "type = %s\n", isom_4cc2str( box->type.user.fourcc ) ); + lsmash_ifprintf( fp, indent, "uuid = 0x%08"PRIx32"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"%08"PRIx32"\n", + box->type.user.fourcc, + (box->type.user.id[0] << 8) | box->type.user.id[1], (box->type.user.id[2] << 8) | box->type.user.id[3], + (box->type.user.id[4] << 8) | box->type.user.id[5], (box->type.user.id[6] << 8) | box->type.user.id[7], + (box->type.user.id[8] << 24) | (box->type.user.id[9] << 16) | (box->type.user.id[10] << 8) | box->type.user.id[11] ); + } + return 0; +} + +static void isom_print_brand_description( FILE *fp, lsmash_brand_type brand ) +{ + if( brand == 0 ) + return; + static const struct + { + lsmash_brand_type brand; + char *description; + } brand_description_table[] = + { + { ISOM_BRAND_TYPE_3G2A, "3GPP2" }, + { ISOM_BRAND_TYPE_3GE6, "3GPP Release 6 Extended Presentation Profile" }, + { ISOM_BRAND_TYPE_3GE9, "3GPP Release 9 Extended Presentation Profile" }, + { ISOM_BRAND_TYPE_3GF9, "3GPP Release 9 File-delivery Server Profile" }, + { ISOM_BRAND_TYPE_3GG6, "3GPP Release 6 General Profile" }, + { ISOM_BRAND_TYPE_3GG9, "3GPP Release 9 General Profile" }, + { ISOM_BRAND_TYPE_3GH9, "3GPP Release 9 Adaptive Streaming Profile" }, + { ISOM_BRAND_TYPE_3GM9, "3GPP Release 9 Media Segment Profile" }, + { ISOM_BRAND_TYPE_3GP4, "3GPP Release 4" }, + { ISOM_BRAND_TYPE_3GP5, "3GPP Release 5" }, + { ISOM_BRAND_TYPE_3GP6, "3GPP Release 6 Basic Profile" }, + { ISOM_BRAND_TYPE_3GP7, "3GPP Release 7" }, + { ISOM_BRAND_TYPE_3GP8, "3GPP Release 8" }, + { ISOM_BRAND_TYPE_3GP9, "3GPP Release 9 Basic Profile" }, + { ISOM_BRAND_TYPE_3GR6, "3GPP Release 6 Progressive Download Profile" }, + { ISOM_BRAND_TYPE_3GR9, "3GPP Release 9 Progressive Download Profile" }, + { ISOM_BRAND_TYPE_3GS6, "3GPP Release 6 Streaming Server Profile" }, + { ISOM_BRAND_TYPE_3GS9, "3GPP Release 9 Streaming Server Profile" }, + { ISOM_BRAND_TYPE_3GT9, "3GPP Release 9 Media Stream Recording Profile" }, + { ISOM_BRAND_TYPE_ARRI, "ARRI Digital Camera" }, + { ISOM_BRAND_TYPE_CAEP, "Canon Digital Camera" }, + { ISOM_BRAND_TYPE_CDES, "Convergent Designs" }, + { ISOM_BRAND_TYPE_LCAG, "Leica digital camera" }, + { ISOM_BRAND_TYPE_M4A , "iTunes MPEG-4 audio protected or not" }, + { ISOM_BRAND_TYPE_M4B , "iTunes AudioBook protected or not" }, + { ISOM_BRAND_TYPE_M4P , "MPEG-4 protected audio" }, + { ISOM_BRAND_TYPE_M4V , "MPEG-4 protected audio+video" }, + { ISOM_BRAND_TYPE_MFSM, "Media File for Samsung video Metadata" }, + { ISOM_BRAND_TYPE_MPPI, "Photo Player Multimedia Application Format" }, + { ISOM_BRAND_TYPE_ROSS, "Ross Video" }, + { ISOM_BRAND_TYPE_AVC1, "Advanced Video Coding extensions" }, + { ISOM_BRAND_TYPE_BBXM, "Blinkbox Master File" }, + { ISOM_BRAND_TYPE_CAQV, "Casio Digital Camera" }, + { ISOM_BRAND_TYPE_CCFF, "Common container file format" }, + { ISOM_BRAND_TYPE_DA0A, "DMB AF" }, + { ISOM_BRAND_TYPE_DA0B, "DMB AF" }, + { ISOM_BRAND_TYPE_DA1A, "DMB AF" }, + { ISOM_BRAND_TYPE_DA1B, "DMB AF" }, + { ISOM_BRAND_TYPE_DA2A, "DMB AF" }, + { ISOM_BRAND_TYPE_DA2B, "DMB AF" }, + { ISOM_BRAND_TYPE_DA3A, "DMB AF" }, + { ISOM_BRAND_TYPE_DA3B, "DMB AF" }, + { ISOM_BRAND_TYPE_DASH, "Indexed self-initializing Media Segment" }, + { ISOM_BRAND_TYPE_DBY1, "MP4 files with Dolby content" }, + { ISOM_BRAND_TYPE_DMB1, "DMB AF" }, + { ISOM_BRAND_TYPE_DSMS, "Self-initializing Media Segment" }, + { ISOM_BRAND_TYPE_DV1A, "DMB AF" }, + { ISOM_BRAND_TYPE_DV1B, "DMB AF" }, + { ISOM_BRAND_TYPE_DV2A, "DMB AF" }, + { ISOM_BRAND_TYPE_DV2B, "DMB AF" }, + { ISOM_BRAND_TYPE_DV3A, "DMB AF" }, + { ISOM_BRAND_TYPE_DV3B, "DMB AF" }, + { ISOM_BRAND_TYPE_DVR1, "DVB RTP" }, + { ISOM_BRAND_TYPE_DVT1, "DVB Transport Stream" }, + { ISOM_BRAND_TYPE_IFRM, "Apple iFrame" }, + { ISOM_BRAND_TYPE_ISC2, "Files encrypted according to ISMACryp 2.0" }, + { ISOM_BRAND_TYPE_ISO2, "ISO Base Media file format version 2" }, + { ISOM_BRAND_TYPE_ISO3, "ISO Base Media file format version 3" }, + { ISOM_BRAND_TYPE_ISO4, "ISO Base Media file format version 4" }, + { ISOM_BRAND_TYPE_ISO5, "ISO Base Media file format version 5" }, + { ISOM_BRAND_TYPE_ISO6, "ISO Base Media file format version 6" }, + { ISOM_BRAND_TYPE_ISO7, "ISO Base Media file format version 7" }, + { ISOM_BRAND_TYPE_ISOM, "ISO Base Media file format version 1" }, + { ISOM_BRAND_TYPE_JPSI, "The JPSearch data interchange format" }, + { ISOM_BRAND_TYPE_LMSG, "last Media Segment indicator" }, + { ISOM_BRAND_TYPE_MJ2S, "Motion JPEG 2000 simple profile" }, + { ISOM_BRAND_TYPE_MJP2, "Motion JPEG 2000, general profile" }, + { ISOM_BRAND_TYPE_MP21, "MPEG-21" }, + { ISOM_BRAND_TYPE_MP41, "MP4 version 1" }, + { ISOM_BRAND_TYPE_MP42, "MP4 version 2" }, + { ISOM_BRAND_TYPE_MP71, "MPEG-7 file-level metadata" }, + { ISOM_BRAND_TYPE_MSDH, "Media Segment" }, + { ISOM_BRAND_TYPE_MSIX, "Indexed Media Segment" }, + { ISOM_BRAND_TYPE_NIKO, "Nikon Digital Camera" }, + { ISOM_BRAND_TYPE_ODCF, "OMA DCF" }, + { ISOM_BRAND_TYPE_OPF2, "OMA PDCF" }, + { ISOM_BRAND_TYPE_OPX2, "OMA Adapted PDCF" }, + { ISOM_BRAND_TYPE_PANA, "Panasonic Digital Camera" }, + { ISOM_BRAND_TYPE_PIFF, "Protected Interoperable File Format" }, + { ISOM_BRAND_TYPE_PNVI, "Panasonic Video Intercom" }, + { ISOM_BRAND_TYPE_QT , "QuickTime file format" }, + { ISOM_BRAND_TYPE_RISX, "Representation Index Segment" }, + { ISOM_BRAND_TYPE_SDV , "SD Video" }, + { ISOM_BRAND_TYPE_SIMS, "Sub-Indexed Media Segment" }, + { ISOM_BRAND_TYPE_SISX, "Single Index Segment" }, + { ISOM_BRAND_TYPE_SSSS, "Subsegment Index Segment" }, + { 0, NULL } + }; + for( int i = 0; brand_description_table[i].description; i++ ) + if( brand == brand_description_table[i].brand ) + { + fprintf( fp, " : %s\n", brand_description_table[i].description ); + return; + } + fprintf( fp, "\n" ); +} + +static void isom_print_file_type +( + FILE *fp, + int indent, + uint32_t major_brand, + uint32_t minor_version, + uint32_t brand_count, + uint32_t *compatible_brands +) +{ + lsmash_ifprintf( fp, indent, "major_brand = %s", isom_4cc2str( major_brand ) ); + isom_print_brand_description( fp, major_brand ); + lsmash_ifprintf( fp, indent, "minor_version = %"PRIu32"\n", minor_version ); + lsmash_ifprintf( fp, indent++, "compatible_brands\n" ); + for( uint32_t i = 0; i < brand_count; i++ ) + { + if( compatible_brands[i] ) + { + lsmash_ifprintf( fp, indent, "brand[%"PRIu32"] = %s", i, isom_4cc2str( compatible_brands[i] ) ); + isom_print_brand_description( fp, compatible_brands[i] ); + } + else + lsmash_ifprintf( fp, indent, "brand[%"PRIu32"] = (void)\n", i ); + } +} + +static int isom_print_ftyp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_ftyp_t *ftyp = (isom_ftyp_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "File Type Box" ); + isom_print_file_type( fp, indent, ftyp->major_brand, ftyp->minor_version, ftyp->brand_count, ftyp->compatible_brands ); + return 0; +} + +static int isom_print_styp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + /* Print 'valid' if this box is the first box in a file. */ + int valid; + if( file + && file->print + && file->print->head + && file->print->head->data ) + valid = (box == ((isom_print_entry_t *)file->print->head->data)->box); + else + valid = 0; + char *name = valid ? "Segment Type Box (valid)" : "Segment Type Box"; + isom_styp_t *styp = (isom_styp_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, name ); + isom_print_file_type( fp, indent, styp->major_brand, styp->minor_version, styp->brand_count, styp->compatible_brands ); + return 0; +} + +static int isom_print_sidx( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_sidx_t *)box)->list ) + return -1; + isom_sidx_t *sidx = (isom_sidx_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Segment Index Box" ); + lsmash_ifprintf( fp, indent, "reference_ID = %"PRIu32"\n", sidx->reference_ID ); + lsmash_ifprintf( fp, indent, "timescale = %"PRIu32"\n", sidx->timescale ); + lsmash_ifprintf( fp, indent, "earliest_presentation_time = %"PRIu64"\n", sidx->earliest_presentation_time ); + lsmash_ifprintf( fp, indent, "first_offset = %"PRIu64"\n", sidx->first_offset ); + lsmash_ifprintf( fp, indent, "reserved = %"PRIu16"\n", sidx->reserved ); + lsmash_ifprintf( fp, indent, "reference_count = %"PRIu16"\n", sidx->reference_count ); + uint32_t i = 0; + for( lsmash_entry_t *entry = sidx->list->head; entry; entry = entry->next ) + { + isom_sidx_referenced_item_t *data = (isom_sidx_referenced_item_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "reference_type = %"PRIu8" (%s)\n", data->reference_type, data->reference_type ? "index" : "media" ); + lsmash_ifprintf( fp, indent, "reference_size = %"PRIu32"\n", data->reference_size ); + lsmash_ifprintf( fp, indent, "subsegment_duration = %"PRIu32"\n", data->subsegment_duration ); + lsmash_ifprintf( fp, indent, "starts_with_SAP = %"PRIu8"%s\n", data->starts_with_SAP, data->starts_with_SAP ? " (yes)" : "" ); + lsmash_ifprintf( fp, indent, "SAP_type = %"PRIu8"%s\n", data->SAP_type, data->SAP_type == 0 ? " (unknown)" : "" ); + lsmash_ifprintf( fp, indent--, "SAP_delta_time = %"PRIu32"\n", data->SAP_delta_time ); + } + return 0; +} + +static int isom_print_moov( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Movie Box" ); +} + +static int isom_print_mvhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mvhd_t *mvhd = (isom_mvhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Movie Header Box" ); + lsmash_ifprintf( fp, indent, "creation_time = %s", isom_mp4time2utc( mvhd->creation_time ) ); + lsmash_ifprintf( fp, indent, "modification_time = %s", isom_mp4time2utc( mvhd->modification_time ) ); + lsmash_ifprintf( fp, indent, "timescale = %"PRIu32"\n", mvhd->timescale ); + isom_ifprintf_duration( fp, indent, "duration", mvhd->duration, mvhd->timescale ); + lsmash_ifprintf( fp, indent, "rate = %f\n", lsmash_fixed2double( mvhd->rate, 16 ) ); + lsmash_ifprintf( fp, indent, "volume = %f\n", lsmash_fixed2double( mvhd->volume, 8 ) ); + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", mvhd->reserved ); + if( file->qt_compatible ) + { + lsmash_ifprintf( fp, indent, "preferredLong1 = 0x%08"PRIx32"\n", mvhd->preferredLong[0] ); + lsmash_ifprintf( fp, indent, "preferredLong2 = 0x%08"PRIx32"\n", mvhd->preferredLong[1] ); + lsmash_ifprintf( fp, indent, "transformation matrix\n" ); + isom_ifprintf_matrix( fp, indent + 1, mvhd->matrix ); + lsmash_ifprintf( fp, indent, "previewTime = %"PRId32"\n", mvhd->previewTime ); + lsmash_ifprintf( fp, indent, "previewDuration = %"PRId32"\n", mvhd->previewDuration ); + lsmash_ifprintf( fp, indent, "posterTime = %"PRId32"\n", mvhd->posterTime ); + lsmash_ifprintf( fp, indent, "selectionTime = %"PRId32"\n", mvhd->selectionTime ); + lsmash_ifprintf( fp, indent, "selectionDuration = %"PRId32"\n", mvhd->selectionDuration ); + lsmash_ifprintf( fp, indent, "currentTime = %"PRId32"\n", mvhd->currentTime ); + } + else + { + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", mvhd->preferredLong[0] ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", mvhd->preferredLong[1] ); + lsmash_ifprintf( fp, indent, "transformation matrix\n" ); + isom_ifprintf_matrix( fp, indent + 1, mvhd->matrix ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->previewTime ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->previewDuration ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->posterTime ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->selectionTime ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->selectionDuration ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->currentTime ); + } + lsmash_ifprintf( fp, indent, "next_track_ID = %"PRIu32"\n", mvhd->next_track_ID ); + return 0; +} + +static void isom_pring_qt_color_table( FILE *fp, int indent, isom_qt_color_table_t *color_table ) +{ + isom_qt_color_array_t *array = color_table->array; + if( !array ) + return; + lsmash_ifprintf( fp, indent, "ctSeed = %"PRIu32"\n", color_table->seed ); + lsmash_ifprintf( fp, indent, "ctFlags = 0x%04"PRIx16"\n", color_table->flags ); + lsmash_ifprintf( fp, indent, "ctSize = %"PRIu16"\n", color_table->size ); + lsmash_ifprintf( fp, indent++, "ctTable\n" ); + for( uint16_t i = 0; i <= color_table->size; i++ ) + lsmash_ifprintf( fp, indent, + "color[%"PRIu16"] = { 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16" }\n", + i, array[i].value, array[i].r, array[i].g, array[i].b ); +} + +static int isom_print_ctab( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_ctab_t *ctab = (isom_ctab_t *)box; + int indent = level; + isom_print_box_common( fp, indent, box, "Color Table Box" ); + isom_pring_qt_color_table( fp, indent + 1, &ctab->color_table ); + return 0; +} + +static int isom_print_iods( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + extern void mp4sys_print_descriptor( FILE *, void *, int ); + isom_iods_t *iods = (isom_iods_t *)box; + int indent = level; + isom_print_box_common( fp, indent, box, "Object Descriptor Box" ); + mp4sys_print_descriptor( fp, iods->OD, indent + 1 ); + return 0; +} + +static int isom_print_trak( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Track Box" ); +} + +static int isom_print_tkhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_tkhd_t *tkhd = (isom_tkhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Header Box" ); + ++indent; + if( tkhd->flags & ISOM_TRACK_ENABLED ) + lsmash_ifprintf( fp, indent, "Track enabled\n" ); + else + lsmash_ifprintf( fp, indent, "Track disabled\n" ); + if( tkhd->flags & ISOM_TRACK_IN_MOVIE ) + lsmash_ifprintf( fp, indent, "Track in movie\n" ); + if( tkhd->flags & ISOM_TRACK_IN_PREVIEW ) + lsmash_ifprintf( fp, indent, "Track in preview\n" ); + if( file->qt_compatible && (tkhd->flags & QT_TRACK_IN_POSTER) ) + lsmash_ifprintf( fp, indent, "Track in poster\n" ); + lsmash_ifprintf( fp, --indent, "creation_time = %s", isom_mp4time2utc( tkhd->creation_time ) ); + lsmash_ifprintf( fp, indent, "modification_time = %s", isom_mp4time2utc( tkhd->modification_time ) ); + lsmash_ifprintf( fp, indent, "track_ID = %"PRIu32"\n", tkhd->track_ID ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved1 ); + if( file && file->moov && file->moov->mvhd ) + isom_ifprintf_duration( fp, indent, "duration", tkhd->duration, file->moov->mvhd->timescale ); + else + isom_ifprintf_duration( fp, indent, "duration", tkhd->duration, 0 ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved2[0] ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved2[1] ); + lsmash_ifprintf( fp, indent, "layer = %"PRId16"\n", tkhd->layer ); + lsmash_ifprintf( fp, indent, "alternate_group = %"PRId16"\n", tkhd->alternate_group ); + lsmash_ifprintf( fp, indent, "volume = %f\n", lsmash_fixed2double( tkhd->volume, 8 ) ); + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", tkhd->reserved3 ); + lsmash_ifprintf( fp, indent, "transformation matrix\n" ); + isom_ifprintf_matrix( fp, indent + 1, tkhd->matrix ); + lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( tkhd->width, 16 ) ); + lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( tkhd->height, 16 ) ); + return 0; +} + +static int isom_print_tapt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Track Aperture Mode Dimensions Box" ); +} + +static int isom_print_clef( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_clef_t *clef = (isom_clef_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Clean Aperture Dimensions Box" ); + lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( clef->width, 16 ) ); + lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( clef->height, 16 ) ); + return 0; +} + +static int isom_print_prof( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_prof_t *prof = (isom_prof_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Production Aperture Dimensions Box" ); + lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( prof->width, 16 ) ); + lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( prof->height, 16 ) ); + return 0; +} + +static int isom_print_enof( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_enof_t *enof = (isom_enof_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Encoded Pixels Dimensions Box" ); + lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( enof->width, 16 ) ); + lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( enof->height, 16 ) ); + return 0; +} + +static int isom_print_edts( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Edit Box" ); +} + +static int isom_print_elst( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_elst_t *elst = (isom_elst_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Edit List Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", elst->list->entry_count ); + for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next ) + { + isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "segment_duration = %"PRIu64"\n", data->segment_duration ); + lsmash_ifprintf( fp, indent, "media_time = %"PRId64"\n", data->media_time ); + lsmash_ifprintf( fp, indent--, "media_rate = %f\n", lsmash_fixed2double( data->media_rate, 16 ) ); + } + return 0; +} + +static int isom_print_tref( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Track Reference Box" ); +} + +static int isom_print_track_reference_type( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_tref_type_t *ref = (isom_tref_type_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Reference Type Box" ); + for( uint32_t i = 0; i < ref->ref_count; i++ ) + lsmash_ifprintf( fp, indent, "track_ID[%"PRIu32"] = %"PRIu32"\n", i, ref->track_ID[i] ); + return 0; +} + +static int isom_print_mdia( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Media Box" ); +} + +static int isom_print_mdhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mdhd_t *mdhd = (isom_mdhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Media Header Box" ); + lsmash_ifprintf( fp, indent, "creation_time = %s", isom_mp4time2utc( mdhd->creation_time ) ); + lsmash_ifprintf( fp, indent, "modification_time = %s", isom_mp4time2utc( mdhd->modification_time ) ); + lsmash_ifprintf( fp, indent, "timescale = %"PRIu32"\n", mdhd->timescale ); + isom_ifprintf_duration( fp, indent, "duration", mdhd->duration, mdhd->timescale ); + if( mdhd->language >= 0x800 ) + lsmash_ifprintf( fp, indent, "language = %s\n", isom_unpack_iso_language( mdhd->language ) ); + else + lsmash_ifprintf( fp, indent, "language = %"PRIu16"\n", mdhd->language ); + if( file->qt_compatible ) + lsmash_ifprintf( fp, indent, "quality = %"PRId16"\n", mdhd->quality ); + else + lsmash_ifprintf( fp, indent, "pre_defined = 0x%04"PRIx16"\n", mdhd->quality ); + return 0; +} + +static int isom_print_hdlr( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_hdlr_t *hdlr = (isom_hdlr_t *)box; + int indent = level; + char str[hdlr->componentName_length + 1]; + memcpy( str, hdlr->componentName, hdlr->componentName_length ); + str[hdlr->componentName_length] = 0; + isom_print_box_common( fp, indent++, box, "Handler Reference Box" ); + if( file->qt_compatible ) + { + lsmash_ifprintf( fp, indent, "componentType = %s\n", isom_4cc2str( hdlr->componentType ) ); + lsmash_ifprintf( fp, indent, "componentSubtype = %s\n", isom_4cc2str( hdlr->componentSubtype ) ); + lsmash_ifprintf( fp, indent, "componentManufacturer = %s\n", isom_4cc2str( hdlr->componentManufacturer ) ); + lsmash_ifprintf( fp, indent, "componentFlags = 0x%08"PRIx32"\n", hdlr->componentFlags ); + lsmash_ifprintf( fp, indent, "componentFlagsMask = 0x%08"PRIx32"\n", hdlr->componentFlagsMask ); + if( hdlr->componentName_length ) + lsmash_ifprintf( fp, indent, "componentName = %s\n", &str[1] ); + else + lsmash_ifprintf( fp, indent, "componentName = \n" ); + } + else + { + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", hdlr->componentType ); + lsmash_ifprintf( fp, indent, "handler_type = %s\n", isom_4cc2str( hdlr->componentSubtype ) ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentManufacturer ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentFlags ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentFlagsMask ); + lsmash_ifprintf( fp, indent, "name = %s\n", str ); + } + return 0; +} + +static int isom_print_minf( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Media Information Box" ); +} + +static int isom_print_vmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_vmhd_t *vmhd = (isom_vmhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Video Media Header Box" ); + lsmash_ifprintf( fp, indent, "graphicsmode = %"PRIu16"\n", vmhd->graphicsmode ); + lsmash_ifprintf( fp, indent, "opcolor\n" ); + isom_ifprintf_rgb_color( fp, indent + 1, vmhd->opcolor ); + return 0; +} + +static int isom_print_smhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_smhd_t *smhd = (isom_smhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Sound Media Header Box" ); + lsmash_ifprintf( fp, indent, "balance = %f\n", lsmash_fixed2double( smhd->balance, 8 ) ); + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", smhd->reserved ); + return 0; +} + +static int isom_print_hmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_hmhd_t *hmhd = (isom_hmhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Hint Media Header Box" ); + lsmash_ifprintf( fp, indent, "maxPDUsize = %"PRIu16"\n", hmhd->maxPDUsize ); + lsmash_ifprintf( fp, indent, "avgPDUsize = %"PRIu16"\n", hmhd->avgPDUsize ); + lsmash_ifprintf( fp, indent, "maxbitrate = %"PRIu32"\n", hmhd->maxbitrate ); + lsmash_ifprintf( fp, indent, "avgbitrate = %"PRIu32"\n", hmhd->avgbitrate ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hmhd->reserved ); + return 0; +} + +static int isom_print_nmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_print_box_common( fp, level, box, "Null Media Header Box" ); + return 0; +} + +static int isom_print_gmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Generic Media Information Header Box" ); +} + +static int isom_print_gmin( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_gmin_t *gmin = (isom_gmin_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Generic Media Information Box" ); + lsmash_ifprintf( fp, indent, "graphicsmode = %"PRIu16"\n", gmin->graphicsmode ); + lsmash_ifprintf( fp, indent, "opcolor\n" ); + isom_ifprintf_rgb_color( fp, indent + 1, gmin->opcolor ); + lsmash_ifprintf( fp, indent, "balance = %f\n", lsmash_fixed2double( gmin->balance, 8 ) ); + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", gmin->reserved ); + return 0; +} + +static int isom_print_text( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_text_t *text = (isom_text_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Text Media Information Box" ); + lsmash_ifprintf( fp, indent, "Unknown matrix\n" ); + isom_ifprintf_matrix( fp, indent + 1, text->matrix ); + return 0; +} + +static int isom_print_dinf( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Data Information Box" ); +} + +static int isom_print_dref( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_dref_t *dref = (isom_dref_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Data Reference Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu16"\n", dref->list.entry_count ); + return 0; +} + +static int isom_print_url( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_dref_entry_t *url = (isom_dref_entry_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Data Entry Url Box" ); + if( url->flags & 0x000001 ) + lsmash_ifprintf( fp, indent, "location = in the same file\n" ); + else + lsmash_ifprintf( fp, indent, "location = %s\n", url->location ); + return 0; +} + +static int isom_print_stbl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Sample Table Box" ); +} + +static int isom_print_stsd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_stsd_t *stsd = (isom_stsd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Sample Description Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stsd->entry_count ); + return 0; +} + +static int isom_print_visual_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_visual_entry_t *visual = (isom_visual_entry_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: Visual Description]\n", isom_4cc2str( visual->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", visual->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", visual->size ); + isom_ifprintf_sample_description_common_reserved( fp, indent, visual->reserved ); + lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", visual->data_reference_index ); + if( file->qt_compatible ) + { + lsmash_ifprintf( fp, indent, "version = %"PRId16"\n", visual->version ); + lsmash_ifprintf( fp, indent, "revision_level = %"PRId16"\n", visual->revision_level ); + lsmash_ifprintf( fp, indent, "vendor = %s\n", isom_4cc2str( visual->vendor ) ); + lsmash_ifprintf( fp, indent, "temporalQuality = %"PRIu32"\n", visual->temporalQuality ); + lsmash_ifprintf( fp, indent, "spatialQuality = %"PRIu32"\n", visual->spatialQuality ); + lsmash_ifprintf( fp, indent, "width = %"PRIu16"\n", visual->width ); + lsmash_ifprintf( fp, indent, "height = %"PRIu16"\n", visual->height ); + lsmash_ifprintf( fp, indent, "horizresolution = %f\n", lsmash_fixed2double( visual->horizresolution, 16 ) ); + lsmash_ifprintf( fp, indent, "vertresolution = %f\n", lsmash_fixed2double( visual->vertresolution, 16 ) ); + lsmash_ifprintf( fp, indent, "dataSize = %"PRIu32"\n", visual->dataSize ); + lsmash_ifprintf( fp, indent, "frame_count = %"PRIu16"\n", visual->frame_count ); + lsmash_ifprintf( fp, indent, "compressorname_length = %"PRIu8"\n", visual->compressorname[0] ); + lsmash_ifprintf( fp, indent, "compressorname = %s\n", visual->compressorname + 1 ); + lsmash_ifprintf( fp, indent, "depth = 0x%04"PRIx16, visual->depth ); + if( visual->depth == 32 ) + fprintf( fp, " (colour with alpha)\n" ); + else if( visual->depth >= 33 && visual->depth <= 40 ) + fprintf( fp, " (grayscale with no alpha)\n" ); + else + fprintf( fp, "\n" ); + lsmash_ifprintf( fp, indent, "color_table_ID = %"PRId16"\n", visual->color_table_ID ); + if( visual->color_table_ID == 0 ) + isom_pring_qt_color_table( fp, indent, &visual->color_table ); + } + else + { + lsmash_ifprintf( fp, indent, "pre_defined = 0x%04"PRIx16"\n", visual->version ); + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", visual->revision_level ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", visual->vendor ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", visual->temporalQuality ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", visual->spatialQuality ); + lsmash_ifprintf( fp, indent, "width = %"PRIu16"\n", visual->width ); + lsmash_ifprintf( fp, indent, "height = %"PRIu16"\n", visual->height ); + lsmash_ifprintf( fp, indent, "horizresolution = %f\n", lsmash_fixed2double( visual->horizresolution, 16 ) ); + lsmash_ifprintf( fp, indent, "vertresolution = %f\n", lsmash_fixed2double( visual->vertresolution, 16 ) ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", visual->dataSize ); + lsmash_ifprintf( fp, indent, "frame_count = %"PRIu16"\n", visual->frame_count ); + lsmash_ifprintf( fp, indent, "compressorname_length = %"PRIu8"\n", visual->compressorname[0] ); + lsmash_ifprintf( fp, indent, "compressorname = %s\n", visual->compressorname + 1 ); + lsmash_ifprintf( fp, indent, "depth = 0x%04"PRIx16, visual->depth ); + if( visual->depth == 0x0018 ) + fprintf( fp, " (colour with no alpha)\n" ); + else if( visual->depth == 0x0028 ) + fprintf( fp, " (grayscale with no alpha)\n" ); + else if( visual->depth == 0x0020 ) + fprintf( fp, " (gray or colour with alpha)\n" ); + else + fprintf( fp, "\n" ); + lsmash_ifprintf( fp, indent, "pre_defined = 0x%04"PRIx16"\n", visual->color_table_ID ); + } + return 0; +} + +static int isom_print_glbl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_glbl_t *glbl = (isom_glbl_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Global Header Box" ); + if( glbl->header_data ) + { + lsmash_ifprintf( fp, indent, "global_header[]\n" ); + for( uint32_t i = 0; i < glbl->header_size; i += 8 ) + { + lsmash_ifprintf( fp, indent + 1, "" ); + for( uint32_t j = 0; ; j++ ) + if( j == 7 || (i + j == glbl->header_size - 1) ) + { + fprintf( fp, "0x%02"PRIx8"\n", glbl->header_data[i + j] ); + break; + } + else + fprintf( fp, "0x%02"PRIx8" ", glbl->header_data[i + j] ); + } + } + return 0; +} + +static int isom_print_clap( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_clap_t *clap = (isom_clap_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Clean Aperture Box" ); + lsmash_ifprintf( fp, indent, "cleanApertureWidthN = %"PRIu32"\n", clap->cleanApertureWidthN ); + lsmash_ifprintf( fp, indent, "cleanApertureWidthD = %"PRIu32"\n", clap->cleanApertureWidthD ); + lsmash_ifprintf( fp, indent, "cleanApertureHeightN = %"PRIu32"\n", clap->cleanApertureHeightN ); + lsmash_ifprintf( fp, indent, "cleanApertureHeightD = %"PRIu32"\n", clap->cleanApertureHeightD ); + lsmash_ifprintf( fp, indent, "horizOffN = %"PRId32"\n", clap->horizOffN ); + lsmash_ifprintf( fp, indent, "horizOffD = %"PRIu32"\n", clap->horizOffD ); + lsmash_ifprintf( fp, indent, "vertOffN = %"PRId32"\n", clap->vertOffN ); + lsmash_ifprintf( fp, indent, "vertOffD = %"PRIu32"\n", clap->vertOffD ); + return 0; +} + +static int isom_print_pasp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_pasp_t *pasp = (isom_pasp_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Pixel Aspect Ratio Box" ); + lsmash_ifprintf( fp, indent, "hSpacing = %"PRIu32"\n", pasp->hSpacing ); + lsmash_ifprintf( fp, indent, "vSpacing = %"PRIu32"\n", pasp->vSpacing ); + return 0; +} + +static int isom_print_colr( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_colr_t *colr = (isom_colr_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, colr->manager & LSMASH_QTFF_BASE ? "Color Parameter Box" : "Colour Information Box" ); + lsmash_ifprintf( fp, indent, "color_parameter_type = %s\n", isom_4cc2str( colr->color_parameter_type ) ); + if( colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_NCLC + || colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) + { + lsmash_ifprintf( fp, indent, "primaries_index = %"PRIu16"\n", colr->primaries_index ); + lsmash_ifprintf( fp, indent, "transfer_function_index = %"PRIu16"\n", colr->transfer_function_index ); + lsmash_ifprintf( fp, indent, "matrix_index = %"PRIu16"\n", colr->matrix_index ); + if( colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) + { + if( colr->manager & LSMASH_INCOMPLETE_BOX ) + { + lsmash_ifprintf( fp, indent, "full_range_flag = N/A\n" ); + lsmash_ifprintf( fp, indent, "reserved = N/A\n" ); + } + else + { + lsmash_ifprintf( fp, indent, "full_range_flag = %"PRIu8"\n", colr->full_range_flag ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx8"\n", colr->reserved ); + } + } + } + return 0; +} + +static int isom_print_gama( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_gama_t *gama = (isom_gama_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Gamma Level Box" ); + if( gama->level == 0x00023333 ) + lsmash_ifprintf( fp, indent, "level = 2.2 (standard television video gamma)\n" ); + else + { + lsmash_ifprintf( fp, indent, "level = %f", lsmash_fixed2double( gama->level, 16 ) ); + if( gama->level == 0 ) + fprintf( fp, " (platform's standard gamma)" ); + else if( gama->level == 0xffffffff ) + fprintf( fp, " (no gamma-correction)" ); + fprintf( fp, "\n" ); + } + return 0; +} + +static int isom_print_fiel( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_fiel_t *fiel = (isom_fiel_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Field/Frame Information Box" ); + lsmash_ifprintf( fp, indent, "fields = %"PRIu8" (%s)\n", fiel->fields, fiel->fields > 1 ? "interlaced" : "progressive scan" ); + lsmash_ifprintf( fp, indent, "detail = %"PRIu8, fiel->detail ); + if( fiel->fields > 1 ) + { + static const char *field_orderings[5] = + { "unknown", "temporal top first", "temporal bottom first", "spatial first line early", "spatial first line late" }; + int ordering = 0; + if( fiel->fields == 2 ) + { + if( fiel->detail == QT_FIELD_ORDERINGS_TEMPORAL_TOP_FIRST ) + ordering = 1; + else if( fiel->detail == QT_FIELD_ORDERINGS_TEMPORAL_BOTTOM_FIRST ) + ordering = 2; + else if( fiel->detail == QT_FIELD_ORDERINGS_SPATIAL_FIRST_LINE_EARLY ) + ordering = 3; + else if( fiel->detail == QT_FIELD_ORDERINGS_SPATIAL_FIRST_LINE_LATE ) + ordering = 4; + } + fprintf( fp, " (%s)\n", field_orderings[ordering] ); + } + else + fprintf( fp, "\n" ); + return 0; +} + +static int isom_print_cspc( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_cspc_t *cspc = (isom_cspc_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Colorspace Box" ); + static const struct + { + lsmash_qt_pixel_format pixel_format; + char *description; + } unprintable_pixel_format_table[] = + { + { QT_PIXEL_FORMAT_TYPE_1_MONOCHROME, "1 bit indexed" }, + { QT_PIXEL_FORMAT_TYPE_2_INDEXED, "2 bit indexed" }, + { QT_PIXEL_FORMAT_TYPE_4_INDEXED, "4 bit indexed" }, + { QT_PIXEL_FORMAT_TYPE_8_INDEXED, "8 bit indexed" }, + { QT_PIXEL_FORMAT_TYPE_1_INDEXED_GRAY_WHITE_IS_ZERO, "1 bit indexed gray, white is zero" }, + { QT_PIXEL_FORMAT_TYPE_2_INDEXED_GRAY_WHITE_IS_ZERO, "2 bit indexed gray, white is zero" }, + { QT_PIXEL_FORMAT_TYPE_4_INDEXED_GRAY_WHITE_IS_ZERO, "4 bit indexed gray, white is zero" }, + { QT_PIXEL_FORMAT_TYPE_8_INDEXED_GRAY_WHITE_IS_ZERO, "8 bit indexed gray, white is zero" }, + { QT_PIXEL_FORMAT_TYPE_16BE555, "16 bit BE RGB 555" }, + { QT_PIXEL_FORMAT_TYPE_24RGB, "24 bit RGB" }, + { QT_PIXEL_FORMAT_TYPE_32ARGB, "32 bit ARGB" }, + { 0, NULL } + }; + for( int i = 0; unprintable_pixel_format_table[i].pixel_format; i++ ) + if( cspc->pixel_format == unprintable_pixel_format_table[i].pixel_format ) + { + lsmash_ifprintf( fp, indent, "pixel_format = 0x%08"PRIx32" (%s)\n", cspc->pixel_format, unprintable_pixel_format_table[i].description ); + return 0; + } + lsmash_ifprintf( fp, indent, "pixel_format = %s\n", isom_4cc2str( cspc->pixel_format ) ); + return 0; +} + +static int isom_print_sgbt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_sgbt_t *sgbt = (isom_sgbt_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Significant Bits Box" ); + lsmash_ifprintf( fp, indent, "significantBits = %"PRIu8"\n", sgbt->significantBits ); + return 0; +} + +static int isom_print_stsl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_stsl_t *stsl = (isom_stsl_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Sample Scale Box" ); + lsmash_ifprintf( fp, indent, "constraint_flag = %s\n", (stsl->constraint_flag & 0x01) ? "on" : "off" ); + lsmash_ifprintf( fp, indent, "scale_method = " ); + if( stsl->scale_method == ISOM_SCALE_METHOD_FILL ) + fprintf( fp, "'fill'\n" ); + else if( stsl->scale_method == ISOM_SCALE_METHOD_HIDDEN ) + fprintf( fp, "'hidden'\n" ); + else if( stsl->scale_method == ISOM_SCALE_METHOD_MEET ) + fprintf( fp, "'meet'\n" ); + else if( stsl->scale_method == ISOM_SCALE_METHOD_SLICE_X ) + fprintf( fp, "'slice' in the x-coodinate\n" ); + else if( stsl->scale_method == ISOM_SCALE_METHOD_SLICE_Y ) + fprintf( fp, "'slice' in the y-coodinate\n" ); + lsmash_ifprintf( fp, indent, "display_center_x = %"PRIu16"\n", stsl->display_center_x ); + lsmash_ifprintf( fp, indent, "display_center_y = %"PRIu16"\n", stsl->display_center_y ); + return 0; +} + +static int isom_print_audio_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_audio_entry_t *audio = (isom_audio_entry_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: Audio Description]\n", isom_4cc2str( audio->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", audio->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", audio->size ); + isom_ifprintf_sample_description_common_reserved( fp, indent, audio->reserved ); + lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", audio->data_reference_index ); + if( file->qt_compatible ) + { + lsmash_ifprintf( fp, indent, "version = %"PRId16"\n", audio->version ); + lsmash_ifprintf( fp, indent, "revision_level = %"PRId16"\n", audio->revision_level ); + lsmash_ifprintf( fp, indent, "vendor = %s\n", isom_4cc2str( audio->vendor ) ); + lsmash_ifprintf( fp, indent, "channelcount = %"PRIu16"\n", audio->channelcount ); + lsmash_ifprintf( fp, indent, "samplesize = %"PRIu16"\n", audio->samplesize ); + lsmash_ifprintf( fp, indent, "compression_ID = %"PRId16"\n", audio->compression_ID ); + lsmash_ifprintf( fp, indent, "packet_size = %"PRIu16"\n", audio->packet_size ); + } + else + { + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", audio->version ); + lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", audio->revision_level ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", audio->vendor ); + lsmash_ifprintf( fp, indent, "channelcount = %"PRIu16"\n", audio->channelcount ); + lsmash_ifprintf( fp, indent, "samplesize = %"PRIu16"\n", audio->samplesize ); + lsmash_ifprintf( fp, indent, "pre_defined = %"PRId16"\n", audio->compression_ID ); + lsmash_ifprintf( fp, indent, "reserved = %"PRIu16"\n", audio->packet_size ); + } + lsmash_ifprintf( fp, indent, "samplerate = %f\n", lsmash_fixed2double( audio->samplerate, 16 ) ); + if( audio->version == 1 && (audio->manager & LSMASH_QTFF_BASE) ) + { + lsmash_ifprintf( fp, indent, "samplesPerPacket = %"PRIu32"\n", audio->samplesPerPacket ); + lsmash_ifprintf( fp, indent, "bytesPerPacket = %"PRIu32"\n", audio->bytesPerPacket ); + lsmash_ifprintf( fp, indent, "bytesPerFrame = %"PRIu32"\n", audio->bytesPerFrame ); + lsmash_ifprintf( fp, indent, "bytesPerSample = %"PRIu32"\n", audio->bytesPerSample ); + } + else if( audio->version == 2 ) + { + lsmash_ifprintf( fp, indent, "sizeOfStructOnly = %"PRIu32"\n", audio->sizeOfStructOnly ); + lsmash_ifprintf( fp, indent, "audioSampleRate = %lf\n", lsmash_int2float64( audio->audioSampleRate ) ); + lsmash_ifprintf( fp, indent, "numAudioChannels = %"PRIu32"\n", audio->numAudioChannels ); + lsmash_ifprintf( fp, indent, "always7F000000 = 0x%08"PRIx32"\n", audio->always7F000000 ); + lsmash_ifprintf( fp, indent, "constBitsPerChannel = %"PRIu32"\n", audio->constBitsPerChannel ); + lsmash_ifprintf( fp, indent++, "formatSpecificFlags = 0x%08"PRIx32"\n", audio->formatSpecificFlags ); + if( isom_is_lpcm_audio( audio ) ) + { + lsmash_ifprintf( fp, indent, "sample format: " ); + if( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_FLOAT ) + fprintf( fp, "floating point\n" ); + else + { + fprintf( fp, "integer\n" ); + lsmash_ifprintf( fp, indent, "signedness: " ); + fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER ? "signed\n" : "unsigned\n" ); + } + if( audio->constBytesPerAudioPacket != 1 ) + { + lsmash_ifprintf( fp, indent, "endianness: " ); + fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN ? "big\n" : "little\n" ); + } + lsmash_ifprintf( fp, indent, "packed: " ); + if( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_PACKED ) + fprintf( fp, "yes\n" ); + else + { + fprintf( fp, "no\n" ); + lsmash_ifprintf( fp, indent, "alignment: " ); + fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH ? "high\n" : "low\n" ); + } + if( audio->numAudioChannels > 1 ) + { + lsmash_ifprintf( fp, indent, "interleved: " ); + fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED ? "no\n" : "yes\n" ); + } + } + lsmash_ifprintf( fp, --indent, "constBytesPerAudioPacket = %"PRIu32"\n", audio->constBytesPerAudioPacket ); + lsmash_ifprintf( fp, indent, "constLPCMFramesPerAudioPacket = %"PRIu32"\n", audio->constLPCMFramesPerAudioPacket ); + } + return 0; +} + +static int isom_print_wave( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Sound Information Decompression Parameters Box" ); +} + +static int isom_print_frma( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_frma_t *frma = (isom_frma_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Format Box" ); + lsmash_ifprintf( fp, indent, "data_format = %s\n", isom_4cc2str( frma->data_format ) ); + return 0; +} + +static int isom_print_enda( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_enda_t *enda = (isom_enda_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Audio Endian Box" ); + lsmash_ifprintf( fp, indent, "littleEndian = %s\n", enda->littleEndian ? "yes" : "no" ); + return 0; +} + +static int isom_print_terminator( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_terminator_t *terminator = (isom_terminator_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[0x00000000: Terminator Box]\n" ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", terminator->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", terminator->size ); + return 0; +} + +static int isom_print_chan( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_chan_t *chan = (isom_chan_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Audio Channel Layout Box" ); + lsmash_ifprintf( fp, indent, "channelLayoutTag = 0x%08"PRIx32"\n", chan->channelLayoutTag ); + lsmash_ifprintf( fp, indent, "channelBitmap = 0x%08"PRIx32"\n", chan->channelBitmap ); + lsmash_ifprintf( fp, indent, "numberChannelDescriptions = %"PRIu32"\n", chan->numberChannelDescriptions ); + if( chan->numberChannelDescriptions ) + { + isom_channel_description_t *desc = chan->channelDescriptions; + for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) + { + lsmash_ifprintf( fp, indent++, "ChannelDescriptions[%"PRIu32"]\n", i ); + lsmash_ifprintf( fp, indent, "channelLabel = 0x%08"PRIx32"\n", desc->channelLabel ); + lsmash_ifprintf( fp, indent, "channelFlags = 0x%08"PRIx32"\n", desc->channelFlags ); + for( int j = 0; j < 3; j++ ) + lsmash_ifprintf( fp, indent, "coordinates[%d] = %f\n", j, lsmash_int2float32( desc->coordinates[j] ) ); + --indent; + } + } + return 0; +} + +static int isom_print_srat( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_srat_t *srat = (isom_srat_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Sampling Rate Box" ); + lsmash_ifprintf( fp, indent, "sampling_rate = %"PRIu32"\n", srat->sampling_rate ); + return 0; +} + +static int isom_print_text_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[text: QuickTime Text Description]\n" ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", text->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", text->size ); + isom_ifprintf_sample_description_common_reserved( fp, indent, text->reserved ); + lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", text->data_reference_index ); + lsmash_ifprintf( fp, indent, "displayFlags = 0x%08"PRId32"\n", text->displayFlags ); + lsmash_ifprintf( fp, indent, "textJustification = %"PRId32"\n", text->textJustification ); + lsmash_ifprintf( fp, indent, "bgColor\n" ); + isom_ifprintf_rgb_color( fp, indent + 1, text->bgColor ); + lsmash_ifprintf( fp, indent, "top = %"PRId16"\n", text->top ); + lsmash_ifprintf( fp, indent, "left = %"PRId16"\n", text->left ); + lsmash_ifprintf( fp, indent, "bottom = %"PRId16"\n", text->bottom ); + lsmash_ifprintf( fp, indent, "right = %"PRId16"\n", text->right ); + lsmash_ifprintf( fp, indent, "scrpStartChar = %"PRId32"\n", text->scrpStartChar ); + lsmash_ifprintf( fp, indent, "scrpHeight = %"PRId16"\n", text->scrpHeight ); + lsmash_ifprintf( fp, indent, "scrpAscent = %"PRId16"\n", text->scrpAscent ); + lsmash_ifprintf( fp, indent, "scrpFont = %"PRId16"\n", text->scrpFont ); + lsmash_ifprintf( fp, indent, "scrpFace = %"PRIu16"\n", text->scrpFace ); + lsmash_ifprintf( fp, indent, "scrpSize = %"PRId16"\n", text->scrpSize ); + lsmash_ifprintf( fp, indent, "scrpColor\n" ); + isom_ifprintf_rgb_color( fp, indent + 1, text->scrpColor ); + if( text->font_name_length ) + lsmash_ifprintf( fp, indent, "font_name = %s\n", text->font_name ); + return 0; +} + +static int isom_print_tx3g_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[tx3g: Timed Text Description]\n" ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", tx3g->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", tx3g->size ); + isom_ifprintf_sample_description_common_reserved( fp, indent, tx3g->reserved ); + lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", tx3g->data_reference_index ); + lsmash_ifprintf( fp, indent, "displayFlags = 0x%08"PRId32"\n", tx3g->displayFlags ); + lsmash_ifprintf( fp, indent, "horizontal_justification = %"PRId8"\n", tx3g->horizontal_justification ); + lsmash_ifprintf( fp, indent, "vertical_justification = %"PRId8"\n", tx3g->vertical_justification ); + lsmash_ifprintf( fp, indent, "background_color_rgba\n" ); + isom_ifprintf_rgba_color( fp, indent + 1, tx3g->background_color_rgba ); + lsmash_ifprintf( fp, indent, "top = %"PRId16"\n", tx3g->top ); + lsmash_ifprintf( fp, indent, "left = %"PRId16"\n", tx3g->left ); + lsmash_ifprintf( fp, indent, "bottom = %"PRId16"\n", tx3g->bottom ); + lsmash_ifprintf( fp, indent, "right = %"PRId16"\n", tx3g->right ); + lsmash_ifprintf( fp, indent, "startChar = %"PRIu16"\n", tx3g->startChar ); + lsmash_ifprintf( fp, indent, "endChar = %"PRIu16"\n", tx3g->endChar ); + lsmash_ifprintf( fp, indent, "font_ID = %"PRIu16"\n", tx3g->font_ID ); + lsmash_ifprintf( fp, indent, "face_style_flags = %"PRIu8"\n", tx3g->face_style_flags ); + lsmash_ifprintf( fp, indent, "font_size = %"PRIu8"\n", tx3g->font_size ); + lsmash_ifprintf( fp, indent, "text_color_rgba\n" ); + isom_ifprintf_rgba_color( fp, indent + 1, tx3g->text_color_rgba ); + return 0; +} + +static int isom_print_ftab( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_ftab_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_ftab_t *ftab = (isom_ftab_t *)box; + int indent = level; + uint16_t i = 0; + isom_print_box_common( fp, indent++, box, "Font Table Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu16"\n", ftab->list->entry_count ); + for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next ) + { + isom_font_record_t *data = (isom_font_record_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu16"]\n", i++ ); + lsmash_ifprintf( fp, indent, "font_ID = %"PRIu16"\n", data->font_ID ); + if( data->font_name_length ) + lsmash_ifprintf( fp, indent, "font_name = %s\n", data->font_name ); + --indent; + } + return 0; +} + +static int isom_print_mp4s_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)box; + int indent = level; + lsmash_ifprintf( fp, indent++, "[%s: MPEG-4 Systems Description]\n", isom_4cc2str( mp4s->type.fourcc ) ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", mp4s->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", mp4s->size ); + isom_ifprintf_sample_description_common_reserved( fp, indent, mp4s->reserved ); + lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", mp4s->data_reference_index ); + return 0; +} + +static int isom_print_sample_description_extesion( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + extern int mp4sys_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int h264_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int hevc_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int h264_print_bitrate( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int vc1_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int ac3_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int eac3_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int dts_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + extern int alac_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); + static struct print_description_extension_table_tag + { + lsmash_box_type_t type; + int (*print_func)( FILE *, lsmash_file_t *, isom_box_t *, int ); + } print_description_extension_table[32] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } }; + if( !print_description_extension_table[0].print_func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( type, func ) \ + print_description_extension_table[i++] = (struct print_description_extension_table_tag){ type, func } + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_print_clap ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_print_pasp ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_print_stsl ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_print_colr ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_print_colr ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_print_gama ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_print_fiel ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_print_cspc ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_print_sgbt ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, isom_print_ctab ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_print_glbl ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_print_wave ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_print_chan ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, mp4sys_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_AVCC, h264_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, h264_print_bitrate ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_HVCC, hevc_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DVC1, vc1_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DAC3, ac3_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DEC3, eac3_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DDTS, dts_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_ALAC, alac_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, isom_print_ftab ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_ESDS, mp4sys_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_ALAC, alac_print_codec_specific ); + ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); +#undef ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT + } + for( int i = 0; print_description_extension_table[i].print_func; i++ ) + if( lsmash_check_box_type_identical( box->type, print_description_extension_table[i].type ) ) + return print_description_extension_table[i].print_func( fp, file, box, level ); + return isom_print_unknown( fp, file, box, level ); +} + +static int isom_print_stts( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_stts_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_stts_t *stts = (isom_stts_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Decoding Time to Sample Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stts->list->entry_count ); + for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next ) + { + isom_stts_entry_t *data = (isom_stts_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); + lsmash_ifprintf( fp, indent--, "sample_delta = %"PRIu32"\n", data->sample_delta ); + } + return 0; +} + +static int isom_print_ctts( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_ctts_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_ctts_t *ctts = (isom_ctts_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Composition Time to Sample Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", ctts->list->entry_count ); + if( file->qt_compatible || ctts->version == 1 ) + for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) + { + isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); + lsmash_ifprintf( fp, indent--, "sample_offset = %"PRId32"\n", (union {uint32_t ui; int32_t si;}){ data->sample_offset }.si ); + } + else + for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) + { + isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); + lsmash_ifprintf( fp, indent--, "sample_offset = %"PRIu32"\n", data->sample_offset ); + } + return 0; +} + +static int isom_print_cslg( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_cslg_t *cslg = (isom_cslg_t *)box; + int indent = level; + if( file->qt_compatible ) + { + isom_print_box_common( fp, indent++, box, "Composition Shift Least Greatest Box" ); + lsmash_ifprintf( fp, indent, "compositionOffsetToDTDDeltaShift = %"PRId32"\n", cslg->compositionToDTSShift ); + lsmash_ifprintf( fp, indent, "leastDecodeToDisplayDelta = %"PRId32"\n", cslg->leastDecodeToDisplayDelta ); + lsmash_ifprintf( fp, indent, "greatestDecodeToDisplayDelta = %"PRId32"\n", cslg->greatestDecodeToDisplayDelta ); + lsmash_ifprintf( fp, indent, "displayStartTime = %"PRId32"\n", cslg->compositionStartTime ); + lsmash_ifprintf( fp, indent, "displayEndTime = %"PRId32"\n", cslg->compositionEndTime ); + } + else + { + isom_print_box_common( fp, indent++, box, "Composition to Decode Box" ); + lsmash_ifprintf( fp, indent, "compositionToDTSShift = %"PRId32"\n", cslg->compositionToDTSShift ); + lsmash_ifprintf( fp, indent, "leastDecodeToDisplayDelta = %"PRId32"\n", cslg->leastDecodeToDisplayDelta ); + lsmash_ifprintf( fp, indent, "greatestDecodeToDisplayDelta = %"PRId32"\n", cslg->greatestDecodeToDisplayDelta ); + lsmash_ifprintf( fp, indent, "compositionStartTime = %"PRId32"\n", cslg->compositionStartTime ); + lsmash_ifprintf( fp, indent, "compositionEndTime = %"PRId32"\n", cslg->compositionEndTime ); + } + return 0; +} + +static int isom_print_stss( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_stss_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_stss_t *stss = (isom_stss_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Sync Sample Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stss->list->entry_count ); + for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next ) + lsmash_ifprintf( fp, indent, "sample_number[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stss_entry_t *)entry->data)->sample_number ); + return 0; +} + +static int isom_print_stps( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_stps_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_stps_t *stps = (isom_stps_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Partial Sync Sample Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stps->list->entry_count ); + for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next ) + lsmash_ifprintf( fp, indent, "sample_number[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stps_entry_t *)entry->data)->sample_number ); + return 0; +} + +static int isom_print_sdtp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_sdtp_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_sdtp_t *sdtp = (isom_sdtp_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Independent and Disposable Samples Box" ); + for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next ) + { + isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + if( data->is_leading || data->sample_depends_on || data->sample_is_depended_on || data->sample_has_redundancy ) + { + if( file->avc_extensions ) + { + if( data->is_leading & ISOM_SAMPLE_IS_UNDECODABLE_LEADING ) + lsmash_ifprintf( fp, indent, "undecodable leading\n" ); + else if( data->is_leading & ISOM_SAMPLE_IS_NOT_LEADING ) + lsmash_ifprintf( fp, indent, "non-leading\n" ); + else if( data->is_leading & ISOM_SAMPLE_IS_DECODABLE_LEADING ) + lsmash_ifprintf( fp, indent, "decodable leading\n" ); + } + else if( data->is_leading & QT_SAMPLE_EARLIER_PTS_ALLOWED ) + lsmash_ifprintf( fp, indent, "early display times allowed\n" ); + if( data->sample_depends_on & ISOM_SAMPLE_IS_INDEPENDENT ) + lsmash_ifprintf( fp, indent, "independent\n" ); + else if( data->sample_depends_on & ISOM_SAMPLE_IS_NOT_INDEPENDENT ) + lsmash_ifprintf( fp, indent, "dependent\n" ); + if( data->sample_is_depended_on & ISOM_SAMPLE_IS_NOT_DISPOSABLE ) + lsmash_ifprintf( fp, indent, "non-disposable\n" ); + else if( data->sample_is_depended_on & ISOM_SAMPLE_IS_DISPOSABLE ) + lsmash_ifprintf( fp, indent, "disposable\n" ); + if( data->sample_has_redundancy & ISOM_SAMPLE_HAS_REDUNDANCY ) + lsmash_ifprintf( fp, indent, "redundant\n" ); + else if( data->sample_has_redundancy & ISOM_SAMPLE_HAS_NO_REDUNDANCY ) + lsmash_ifprintf( fp, indent, "non-redundant\n" ); + } + else + lsmash_ifprintf( fp, indent, "no description\n" ); + --indent; + } + return 0; +} + +static int isom_print_stsc( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_stsc_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_stsc_t *stsc = (isom_stsc_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Sample To Chunk Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stsc->list->entry_count ); + for( lsmash_entry_t *entry = stsc->list->head; entry; entry = entry->next ) + { + isom_stsc_entry_t *data = (isom_stsc_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "first_chunk = %"PRIu32"\n", data->first_chunk ); + lsmash_ifprintf( fp, indent, "samples_per_chunk = %"PRIu32"\n", data->samples_per_chunk ); + lsmash_ifprintf( fp, indent--, "sample_description_index = %"PRIu32"\n", data->sample_description_index ); + } + return 0; +} + +static int isom_print_stsz( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_stsz_t *stsz = (isom_stsz_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Sample Size Box" ); + if( !stsz->sample_size ) + lsmash_ifprintf( fp, indent, "sample_size = 0 (variable)\n" ); + else + lsmash_ifprintf( fp, indent, "sample_size = %"PRIu32" (constant)\n", stsz->sample_size ); + lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", stsz->sample_count ); + if( !stsz->sample_size && stsz->list ) + for( lsmash_entry_t *entry = stsz->list->head; entry; entry = entry->next ) + { + isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; + 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 ) + return LSMASH_ERR_INVALID_DATA; + isom_stco_t *stco = (isom_stco_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Chunk Offset Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stco->list->entry_count ); + if( lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_STCO ) ) + { + for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) + lsmash_ifprintf( fp, indent, "chunk_offset[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stco_entry_t *)entry->data)->chunk_offset ); + } + else + { + for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) + lsmash_ifprintf( fp, indent, "chunk_offset[%"PRIu32"] = %"PRIu64"\n", i++, ((isom_co64_entry_t *)entry->data)->chunk_offset ); + } + return 0; +} + +static int isom_print_sgpd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_sgpd_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_sgpd_t *sgpd = (isom_sgpd_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Sample Group Description Box" ); + lsmash_ifprintf( fp, indent, "grouping_type = %s\n", isom_4cc2str( sgpd->grouping_type ) ); + if( sgpd->version == 1 ) + { + lsmash_ifprintf( fp, indent, "default_length = %"PRIu32, sgpd->default_length ); + fprintf( fp, " %s\n", sgpd->default_length ? "(constant)" : "(variable)" ); + } + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", sgpd->list->entry_count ); + switch( sgpd->grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) + { + if( sgpd->version == 1 && !sgpd->default_length ) + lsmash_ifprintf( fp, indent, "description_length[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_rap_entry_t *)entry->data)->description_length ); + else + { + isom_rap_entry_t *rap = (isom_rap_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "num_leading_samples_known = %"PRIu8"\n", rap->num_leading_samples_known ); + lsmash_ifprintf( fp, indent--, "num_leading_samples = %"PRIu8"\n", rap->num_leading_samples ); + } + } + break; + case ISOM_GROUP_TYPE_ROLL : + case ISOM_GROUP_TYPE_PROL : + for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) + { + if( sgpd->version == 1 && !sgpd->default_length ) + lsmash_ifprintf( fp, indent, "description_length[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_roll_entry_t *)entry->data)->description_length ); + else + lsmash_ifprintf( fp, indent, "roll_distance[%"PRIu32"] = %"PRId16"\n", i++, ((isom_roll_entry_t *)entry->data)->roll_distance ); + } + break; + default : + break; + } + return 0; +} + +static int isom_print_sbgp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_sbgp_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_sbgp_t *sbgp = (isom_sbgp_t *)box; + int indent = level; + int is_fragment = sbgp->parent && lsmash_check_box_type_identical( sbgp->parent->type, ISOM_BOX_TYPE_TRAF ); + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Sample to Group Box" ); + lsmash_ifprintf( fp, indent, "grouping_type = %s\n", isom_4cc2str( sbgp->grouping_type ) ); + if( sbgp->version == 1 ) + lsmash_ifprintf( fp, indent, "grouping_type_parameter = %s\n", isom_4cc2str( sbgp->grouping_type_parameter ) ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", 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; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); + lsmash_ifprintf( fp, indent--, "group_description_index = %"PRIu32, data->group_description_index ); + if( is_fragment && data->group_description_index >= 0x10000 ) + fprintf( fp, " (i.e. %"PRIu32" for this fragment-local group)", data->group_description_index - 0x10000 ); + if( !data->group_description_index ) + fprintf( fp, " (not in this grouping type)\n" ); + else + fprintf( fp, "\n" ); + } + return 0; +} + +static int isom_print_udta( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "User Data Box" ); +} + +static int isom_print_chpl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_chpl_t *chpl = (isom_chpl_t *)box; + uint32_t timescale; + if( !chpl->version ) + { + if( !file || !file->moov || !file->moov->mvhd ) + return LSMASH_ERR_INVALID_DATA; + timescale = file->moov->mvhd->timescale; + } + else + timescale = 10000000; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Chapter List Box" ); + if( chpl->version == 1 ) + { + lsmash_ifprintf( fp, indent, "unknown = 0x%02"PRIx8"\n", chpl->unknown ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", chpl->list->entry_count ); + } + else + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu8"\n", (uint8_t)chpl->list->entry_count ); + for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next ) + { + isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data; + int64_t start_time = data->start_time / timescale; + int hh = start_time / 3600; + int mm = (start_time / 60) % 60; + int ss = start_time % 60; + int ms = ((data->start_time / (double)timescale) - hh * 3600 - mm * 60 - ss) * 1e3 + 0.5; + int with_bom = 0; + if( !memcmp( data->chapter_name, "\xEF\xBB\xBF", 3 ) ) /* detect BOM */ + { + data->chapter_name += 3; + with_bom = 1; + } + lsmash_ifprintf( fp, indent++, "chapter[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "start_time = %02d:%02d:%02d.%03d\n", hh, mm, ss, ms ); + lsmash_ifprintf( fp, indent--, with_bom ? "chapter_name = %s ( it has BOM in it )\n" : "chapter_name = %s\n", data->chapter_name ); + } + return 0; +} + +static int isom_print_meta( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + int indent = level; + if( !(box->manager & LSMASH_QTFF_BASE) ) + { + isom_print_basebox_common( fp, indent++, box, "Meta Box" ); + lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", box->version ); + lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", box->flags & 0x00ffffff ); + } + else + isom_print_basebox_common( fp, indent, box, "Metadata Box" ); + return 0; +} + +static int isom_print_keys( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + if( !((isom_keys_t *)box)->list ) + return LSMASH_ERR_INVALID_DATA; + isom_keys_t *keys = (isom_keys_t *)box; + int indent = level; + uint32_t i = 1; + isom_print_box_common( fp, indent++, box, "Metadata Item Keys Box" ); + lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", keys->list->entry_count ); + for( lsmash_entry_t *entry = keys->list->head; entry; entry = entry->next ) + { + isom_keys_entry_t *data = (isom_keys_entry_t *)entry->data; + lsmash_ifprintf( fp, indent++, "[key %"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "key_size = %"PRIu32"\n", data->key_size ); + lsmash_ifprintf( fp, indent, "key_namespace = %s\n", isom_4cc2str( data->key_namespace ) ); + uint32_t value_length = data->key_size - 8; + char str[value_length + 1]; + memcpy( str, data->key_value, value_length ); + str[value_length] = 0; + lsmash_ifprintf( fp, indent--, "key_value = %s\n", str ); + } + return 0; +} + +static int isom_print_ilst( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Metadata Item List Box" ); +} + +static int isom_print_metaitem( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_metaitem_t *metaitem = (isom_metaitem_t *)box; + if( box->parent && box->parent->parent && (box->parent->parent->manager & LSMASH_QTFF_BASE) ) + { + int indent = level; + lsmash_ifprintf( fp, indent++, "[key_index %"PRIu32": Metadata Item Box]\n", box->type.fourcc ); + lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); + lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); + return 0; + } + static const struct + { + lsmash_itunes_metadata_item item; + char *name; + } metaitem_table[] = + { + { ITUNES_METADATA_ITEM_ALBUM_NAME, "Album Name" }, + { ITUNES_METADATA_ITEM_ARTIST, "Artist" }, + { ITUNES_METADATA_ITEM_USER_COMMENT, "User Comment" }, + { ITUNES_METADATA_ITEM_RELEASE_DATE, "Release Date" }, + { ITUNES_METADATA_ITEM_ENCODED_BY, "Encoded By" }, + { ITUNES_METADATA_ITEM_USER_GENRE, "User Genre" }, + { ITUNES_METADATA_ITEM_GROUPING, "Grouping" }, + { ITUNES_METADATA_ITEM_LYRICS, "Lyrics" }, + { ITUNES_METADATA_ITEM_TITLE, "Title" }, + { ITUNES_METADATA_ITEM_ENCODING_TOOL, "Encoding Tool" }, + { ITUNES_METADATA_ITEM_COMPOSER, "Composer" }, + { ITUNES_METADATA_ITEM_ALBUM_ARTIST, "Album Artist" }, + { ITUNES_METADATA_ITEM_PODCAST_CATEGORY, "Podcast Category" }, + { ITUNES_METADATA_ITEM_COVER_ART, "Cover Art" }, + { ITUNES_METADATA_ITEM_DISC_COMPILATION, "Disc Compilation" }, + { ITUNES_METADATA_ITEM_COPYRIGHT, "Copyright" }, + { ITUNES_METADATA_ITEM_DESCRIPTION, "Description" }, + { ITUNES_METADATA_ITEM_DISC_NUMBER, "Disc Number" }, + { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, "Episode Global Unique ID" }, + { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, "Pre-defined Genre" }, + { ITUNES_METADATA_ITEM_GROUPING_DRAFT, "Grouping (Overall work like TIT1 in ID3)" }, + { ITUNES_METADATA_ITEM_HIGH_DEFINITION_VIDEO, "High Definition Video" }, + { ITUNES_METADATA_ITEM_PODCAST_KEYWORD, "Podcast Keyword" }, + { ITUNES_METADATA_ITEM_LONG_DESCRIPTION, "Long Description" }, + { ITUNES_METADATA_ITEM_PODCAST, "Podcast" }, + { ITUNES_METADATA_ITEM_GAPLESS_PLAYBACK, "Gapless Playback" }, + { ITUNES_METADATA_ITEM_PURCHASE_DATE, "Purchase Date" }, + { ITUNES_METADATA_ITEM_PODCAST_URL, "Podcast URL" }, + { ITUNES_METADATA_ITEM_CONTENT_RATING, "Content Rating" }, + { ITUNES_METADATA_ITEM_MEDIA_TYPE, "Media Type" }, + { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, "Beats Per Minute" }, + { ITUNES_METADATA_ITEM_TRACK_NUMBER, "Track Number" }, + { ITUNES_METADATA_ITEM_TV_EPISODE_ID, "TV Episode ID" }, + { ITUNES_METADATA_ITEM_TV_EPISODE, "TV Episode" }, + { ITUNES_METADATA_ITEM_TV_NETWORK, "TV Network" }, + { ITUNES_METADATA_ITEM_TV_SHOW_NAME, "TV Show Name" }, + { ITUNES_METADATA_ITEM_TV_SEASON, "TV Season" }, + { ITUNES_METADATA_ITEM_ITUNES_PURCHASE_ACCOUNT_ID, "iTunes Account Used for Purchase" }, + { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, "iTunes Account Type" }, + { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, "iTunes Artist ID" }, + { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, "iTunes Composer ID" }, + { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, "iTunes Catalog ID" }, + { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, "iTunes TV Genre ID" }, + { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, "iTunes Playlist ID" }, + { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, "iTunes Country Code" }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM, "Sort Album" }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ARTIST, "Sort Artist" }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM_ARTIST, "Sort Album Artist" }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_COMPOSER, "Sort Composer" }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_NAME, "Sort Name" }, + { ITUNES_METADATA_ITEM_ITUNES_SORT_SHOW, "Sort Show" }, + { ITUNES_METADATA_ITEM_CUSTOM, "Custom Metadata Item" }, + { 0, NULL } + }; + char *name = NULL; + int i; + for( i = 0; metaitem_table[i].name; i++ ) + if( metaitem->type.fourcc == metaitem_table[i].item ) + { + name = metaitem_table[i].name; + break; + } + if( !name ) + name = "Unknown"; + uint32_t name_length = strlen( name ); + uint32_t display_name_length = name_length + 20; + char display_name[display_name_length + 1]; + memcpy( display_name, "Metadata Item Box (", 19 ); + memcpy( display_name + 19, name, name_length ); + display_name[display_name_length - 1] = ')'; + display_name[display_name_length] = 0; + return isom_print_simple( fp, box, level, display_name ); +} + +static int isom_print_name( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_name_t *name = (isom_name_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Name Box" ); + char str[name->name_length + 1]; + memcpy( str, name->name, name->name_length ); + str[name->name_length] = 0; + lsmash_ifprintf( fp, indent, "name = %s\n", str ); + return 0; +} + +static int isom_print_mean( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mean_t *mean = (isom_mean_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Mean Box" ); + char str[mean->meaning_string_length + 1]; + memcpy( str, mean->meaning_string, mean->meaning_string_length ); + str[mean->meaning_string_length] = 0; + lsmash_ifprintf( fp, indent, "meaning_string = %s\n", str ); + return 0; +} + +static int isom_print_data( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_data_t *data = (isom_data_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Data Box" ); + if( box->parent && box->parent->parent && box->parent->parent->parent + && (box->parent->parent->parent->manager & LSMASH_QTFF_BASE) ) + { + uint32_t type_set_indicator = data->reserved >> 8; + uint32_t well_known_type = ((data->reserved << 16) | (data->type_set_identifier << 8) | data->type_code) & 0xffffff; + char *well_known_type_name; + static const struct + { + uint32_t type; + char *name; + } well_known_type_table[] = + { + { 0, "reserved" }, + { 1, "UTF-8" }, + { 2, "UTF-16 BE" }, + { 3, "S/JIS" }, + { 4, "UTF-8 sort" }, + { 5, "UTF-16 sort" }, + { 13, "JPEG in a JFIF wrapper" }, + { 14, "PNG in a PNG wrapper" }, + { 21, "BE Signed Integer" }, + { 22, "BE Unsigned Integer" }, + { 23, "BE Float32" }, + { 24, "BE Float64" }, + { 27, "BMP (Windows bitmap format graphics)" }, + { 28, "QuickTime Metadata box" }, + { UINT32_MAX } + }; + int table_index; + for( table_index = 0; well_known_type_table[table_index].type != UINT32_MAX; table_index++ ) + if( well_known_type == well_known_type_table[table_index].type ) + { + well_known_type_name = well_known_type_table[table_index].name; + break; + } + if( well_known_type_table[table_index].type == UINT32_MAX ) + well_known_type_name = "Unknown"; + lsmash_ifprintf( fp, indent, "type_set_indicator = %"PRIu8"\n", type_set_indicator ); + lsmash_ifprintf( fp, indent, "well_known_type = %"PRIu32" (%s)\n", well_known_type, well_known_type_name ); + lsmash_ifprintf( fp, indent, "locale_indicator = %"PRIu32"\n", data->the_locale ); + if( data->value_length == 0 ) + { + lsmash_ifprintf( fp, indent, "value = (null)\n" ); + return 0; + } + if( well_known_type == 1 ) + { + /* UTF-8 without any count or null terminator */ + char str[data->value_length + 1]; + memcpy( str, data->value, data->value_length ); + str[data->value_length] = 0; + lsmash_ifprintf( fp, indent, "value = %s\n", str ); + } + else if( well_known_type == 13 || well_known_type == 14 || well_known_type == 27 ) + lsmash_ifprintf( fp, indent, "value = (binary data)\n" ); + else if( well_known_type == 21 && data->value_length <= 4 ) + /* a big-endian signed integer in 1,2,3 or 4 bytes */ + goto show_in_signed_integer; + else if( well_known_type == 22 && data->value_length <= 4 ) + { + /* a big-endian unsigned integer in 1,2,3 or 4 bytes */ + uint32_t integer = data->value[0]; + for( uint32_t i = 1; i < data->value_length; i++ ) + integer = (integer << 8) | data->value[i]; + lsmash_ifprintf( fp, indent, "value = %"PRIu32"\n", integer ); + } + else if( well_known_type == 23 && data->value_length == 4 ) + { + /* a big-endian 32-bit floating point value (IEEE754) */ + uint32_t float32 = LSMASH_GET_BE32( data->value ); + lsmash_ifprintf( fp, indent, "value = %f\n", lsmash_int2float32( float32 ) ); + } + else if( well_known_type == 24 && data->value_length == 8 ) + { + /* a big-endian 64-bit floating point value (IEEE754) */ + uint64_t float64 = LSMASH_GET_BE64( data->value ); + lsmash_ifprintf( fp, indent, "value = %lf\n", lsmash_int2float64( float64 ) ); + } + else + goto show_in_binary; + } + else + { + char *basic_data_type_name; + static const struct + { + uint32_t type; + char *name; + } basic_data_type_table[] = + { + { 0, "Implicit" }, + { 1, "UTF-8" }, + { 2, "UTF-16 BE" }, + { 3, "S/JIS" }, + { 6, "HTML" }, + { 7, "XML" }, + { 8, "UUID" }, + { 9, "ISRC" }, + { 10, "MI3P" }, + { 12, "GIF" }, + { 13, "JPEG in a JFIF wrapper" }, + { 14, "PNG in a PNG wrapper" }, + { 15, "URL" }, + { 16, "duration" }, + { 17, "date/time" }, + { 18, "Genres" }, + { 21, "BE Signed Integer" }, + { 24, "RIAA-PA (RIAA Parental advisory)" }, + { 25, "UPC (Universal Product Code)" }, + { 27, "BMP (Windows bitmap format graphics)" }, + { UINT32_MAX } + }; + int table_index; + for( table_index = 0; basic_data_type_table[table_index].type != UINT32_MAX; table_index++ ) + if( data->type_code == basic_data_type_table[table_index].type ) + { + basic_data_type_name = basic_data_type_table[table_index].name; + break; + } + if( basic_data_type_table[table_index].type == UINT32_MAX ) + basic_data_type_name = "Unknown"; + lsmash_ifprintf( fp, indent, "reserved = %"PRIu16"\n", data->reserved ); + lsmash_ifprintf( fp, indent, "type_set_identifier = %"PRIu8"%s\n", + data->type_set_identifier, + data->type_set_identifier ? "" : " (basic type set)" ); + lsmash_ifprintf( fp, indent, "type_code = %"PRIu8" (%s)\n", data->type_code, basic_data_type_name ); + lsmash_ifprintf( fp, indent, "the_locale = %"PRIu32"\n", data->the_locale ); + if( data->value_length == 0 ) + { + lsmash_ifprintf( fp, indent, "value = (null)\n" ); + return 0; + } + if( data->type_code == 6 || data->type_code == 7 + || data->type_code == 12 || data->type_code == 13 + || data->type_code == 14 || data->type_code == 27 ) + lsmash_ifprintf( fp, indent, "value = (binary data)\n" ); + else if( data->type_code == 8 && data->value_length == 16 ) + /* UUID */ + lsmash_ifprintf( fp, indent, "value = 0x%08"PRIx32"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"0x%08"PRIx32"\n", + LSMASH_GET_BE32( &data->value[ 0] ), + LSMASH_GET_BE16( &data->value[ 4] ), + LSMASH_GET_BE16( &data->value[ 6] ), + LSMASH_GET_BE16( &data->value[ 8] ), + LSMASH_GET_BE16( &data->value[10] ), + LSMASH_GET_BE32( &data->value[12] ) ); + else if( data->type_code == 16 && data->value_length == 4 ) + { + /* duration in milliseconds */ + uint32_t duration = LSMASH_GET_BE32( data->value ); + lsmash_ifprintf( fp, indent, "value = %"PRIu32" milliseconds\n", duration ); + } + else if( data->type_code == 17 && (data->value_length == 4 || data->value_length == 8) ) + { + /* UTC, counting seconds since midnight on 1 January, 1904 */ + uint64_t mp4time = data->value_length == 8 + ? LSMASH_GET_BE64( data->value ) + : LSMASH_GET_BE32( data->value ); + isom_mp4time2utc( mp4time ); + } + else if( data->type_code == 21 && data->value_length <= 8 ) + /* a big-endian signed integer in 1,2,3,4 or 8 bytes */ + goto show_in_signed_integer; + else if( data->type_code == 24 ) + { + /* RIAA-PA (RIAA Parental advisory) 8-bit integer */ + lsmash_ifprintf( fp, indent, "value = %"PRIu8, data->value[0] ); + if( (signed)data->value[0] == -1 ) + fprintf( fp, " (no)" ); + else if( data->value[0] == 1 ) + fprintf( fp, " (yes)" ); + else if( data->value[0] == 0 ) + fprintf( fp, " (unspecified)" ); + fprintf( fp, "\n" ); + } + else if( data->type_code == 1 || data->type_code == 2 || data->type_code == 3 + || data->type_code == 9 || data->type_code == 10 || data->type_code == 15 + || data->type_code == 25 ) + { + /* String */ + char str[data->value_length + 1]; + memcpy( str, data->value, data->value_length ); + str[data->value_length] = 0; + lsmash_ifprintf( fp, indent, "value = %s\n", str ); + } + else + goto show_in_binary; + } + return 0; +show_in_signed_integer:; + uint64_t integer = data->value[0]; + uint64_t max_value = 0xff; + for( uint32_t i = 1; i < data->value_length; i++ ) + { + integer = (integer << 8) | data->value[i]; + max_value = (max_value << 8) | 0xff; + } + lsmash_ifprintf( fp, indent, "value = %"PRId64"\n", (int64_t)(integer | (integer > (max_value >> 1) ? ~max_value : 0)) ); + return 0; +show_in_binary: + lsmash_ifprintf( fp, indent, "value = " ); + if( data->value_length ) + { + fprintf( fp, "0x" ); + for( uint32_t i = 0; i < data->value_length; i++ ) + fprintf( fp, "%02"PRIx8, data->value[i] ); + } + fprintf( fp, "\n" ); + return 0; +} + +static int isom_print_WLOC( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_WLOC_t *WLOC = (isom_WLOC_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Window Location Box" ); + lsmash_ifprintf( fp, indent, "x = %"PRIu16"\n", WLOC->x ); + lsmash_ifprintf( fp, indent, "y = %"PRIu16"\n", WLOC->y ); + return 0; +} + +static int isom_print_LOOP( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_LOOP_t *LOOP = (isom_LOOP_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Looping Box" ); + lsmash_ifprintf( fp, indent, "looping_mode = %"PRIu32, LOOP->looping_mode ); + switch( LOOP->looping_mode ) + { + case 0 : + fprintf( fp, " (none)\n" ); + break; + case 1 : + fprintf( fp, " (looping)\n" ); + break; + case 2 : + fprintf( fp, " (palindromic looping)\n" ); + break; + default : + fprintf( fp, "\n" ); + break; + } + return 0; +} + +static int isom_print_SelO( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_SelO_t *SelO = (isom_SelO_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Play Selection Only Box" ); + lsmash_ifprintf( fp, indent, "selection_only = %"PRIu8"\n", SelO->selection_only ); + return 0; +} + +static int isom_print_AllF( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_AllF_t *AllF = (isom_AllF_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Play All Frames Box" ); + lsmash_ifprintf( fp, indent, "play_all_frames = %"PRIu8"\n", AllF->play_all_frames ); + return 0; +} + +static int isom_print_cprt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_cprt_t *cprt = (isom_cprt_t *)box; + int indent = level; + char str[cprt->notice_length + 1]; + memcpy( str, cprt->notice, cprt->notice_length ); + str[cprt->notice_length] = 0; + isom_print_box_common( fp, indent++, box, "Copyright Box" ); + lsmash_ifprintf( fp, indent, "language = %s\n", isom_unpack_iso_language( cprt->language ) ); + lsmash_ifprintf( fp, indent, "notice = %s\n", str ); + return 0; +} + +static int isom_print_mvex( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Movie Extends Box" ); +} + +static int isom_print_mehd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mehd_t *mehd = (isom_mehd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Movie Extends Header Box" ); + if( file && file->moov && file->moov->mvhd ) + isom_ifprintf_duration( fp, indent, "fragment_duration", mehd->fragment_duration, file->moov->mvhd->timescale ); + else + isom_ifprintf_duration( fp, indent, "fragment_duration", mehd->fragment_duration, 0 ); + return 0; +} + +static int isom_print_trex( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_trex_t *trex = (isom_trex_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Extends Box" ); + lsmash_ifprintf( fp, indent, "track_ID = %"PRIu32"\n", trex->track_ID ); + lsmash_ifprintf( fp, indent, "default_sample_description_index = %"PRIu32"\n", trex->default_sample_description_index ); + lsmash_ifprintf( fp, indent, "default_sample_duration = %"PRIu32"\n", trex->default_sample_duration ); + lsmash_ifprintf( fp, indent, "default_sample_size = %"PRIu32"\n", trex->default_sample_size ); + isom_ifprintf_sample_flags( fp, indent, "default_sample_flags", &trex->default_sample_flags ); + return 0; +} + +static int isom_print_moof( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Movie Fragment Box" ); +} + +static int isom_print_mfhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mfhd_t *mfhd = (isom_mfhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Movie Fragment Header Box" ); + lsmash_ifprintf( fp, indent, "sequence_number = %"PRIu32"\n", mfhd->sequence_number ); + return 0; +} + +static int isom_print_traf( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Track Fragment Box" ); +} + +static int isom_print_tfhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_tfhd_t *tfhd = (isom_tfhd_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Fragment Header Box" ); + ++indent; + if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) lsmash_ifprintf( fp, indent, "base-data-offset-present\n" ); + if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) lsmash_ifprintf( fp, indent, "sample-description-index-present\n" ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) lsmash_ifprintf( fp, indent, "default-sample-duration-present\n" ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) lsmash_ifprintf( fp, indent, "default-sample-size-present\n" ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) lsmash_ifprintf( fp, indent, "default-sample-flags-present\n" ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF ) lsmash_ifprintf( fp, indent, "default-base-is-moof\n" ); + lsmash_ifprintf( fp, --indent, "track_ID = %"PRIu32"\n", tfhd->track_ID ); + if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) + lsmash_ifprintf( fp, indent, "base_data_offset = %"PRIu64"\n", tfhd->base_data_offset ); + if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) + lsmash_ifprintf( fp, indent, "sample_description_index = %"PRIu32"\n", tfhd->sample_description_index ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) + lsmash_ifprintf( fp, indent, "default_sample_duration = %"PRIu32"\n", tfhd->default_sample_duration ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) + lsmash_ifprintf( fp, indent, "default_sample_size = %"PRIu32"\n", tfhd->default_sample_size ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) + isom_ifprintf_sample_flags( fp, indent, "default_sample_flags", &tfhd->default_sample_flags ); + return 0; +} + +static int isom_print_tfdt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_tfdt_t *tfdt = (isom_tfdt_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Fragment Base Media Decode Time Box" ); + lsmash_ifprintf( fp, indent, "baseMediaDecodeTime = %"PRIu64"\n", tfdt->baseMediaDecodeTime ); + return 0; +} + +static int isom_print_trun( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_trun_t *trun = (isom_trun_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Fragment Run Box" ); + ++indent; + if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) lsmash_ifprintf( fp, indent, "data-offset-present\n" ); + if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) lsmash_ifprintf( fp, indent, "first-sample-flags-present\n" ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) lsmash_ifprintf( fp, indent, "sample-duration-present\n" ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) lsmash_ifprintf( fp, indent, "sample-size-present\n" ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) lsmash_ifprintf( fp, indent, "sample-flags-present\n" ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) lsmash_ifprintf( fp, indent, "sample-composition-time-offsets-present\n" ); + lsmash_ifprintf( fp, --indent, "sample_count = %"PRIu32"\n", trun->sample_count ); + if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) + lsmash_ifprintf( fp, indent, "data_offset = %"PRId32"\n", trun->data_offset ); + if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) + isom_ifprintf_sample_flags( fp, indent, "first_sample_flags", &trun->first_sample_flags ); + if( trun->optional ) + { + uint32_t i = 0; + for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next ) + { + isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)entry->data; + lsmash_ifprintf( fp, indent++, "sample[%"PRIu32"]\n", i++ ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) + lsmash_ifprintf( fp, indent, "sample_duration = %"PRIu32"\n", row->sample_duration ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) + lsmash_ifprintf( fp, indent, "sample_size = %"PRIu32"\n", row->sample_size ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) + isom_ifprintf_sample_flags( fp, indent, "sample_flags", &row->sample_flags ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) + { + if( trun->version == 0 ) + lsmash_ifprintf( fp, indent, "sample_composition_time_offset = %"PRIu32"\n", + row->sample_composition_time_offset ); + else + lsmash_ifprintf( fp, indent, "sample_composition_time_offset = %"PRId32"\n", + (union {uint32_t ui; int32_t si;}){ row->sample_composition_time_offset }.si ); + } + --indent; + } + } + return 0; +} + +static int isom_print_free( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Free Space Box" ); +} + +static int isom_print_mdat( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Media Data Box" ); +} + +static int isom_print_mfra( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + return isom_print_simple( fp, box, level, "Movie Fragment Random Access Box" ); +} + +static int isom_print_tfra( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_tfra_t *tfra = (isom_tfra_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Track Fragment Random Access Box" ); + lsmash_ifprintf( fp, indent, "track_ID = %"PRIu32"\n", tfra->track_ID ); + lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tfra->reserved ); + lsmash_ifprintf( fp, indent, "length_size_of_traf_num = %"PRIu8"\n", tfra->length_size_of_traf_num ); + lsmash_ifprintf( fp, indent, "length_size_of_trun_num = %"PRIu8"\n", tfra->length_size_of_trun_num ); + lsmash_ifprintf( fp, indent, "length_size_of_sample_num = %"PRIu8"\n", tfra->length_size_of_sample_num ); + lsmash_ifprintf( fp, indent, "number_of_entry = %"PRIu32"\n", tfra->number_of_entry ); + if( tfra->list ) + { + uint32_t i = 0; + 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; + lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); + lsmash_ifprintf( fp, indent, "time = %"PRIu64"\n", data->time ); + lsmash_ifprintf( fp, indent, "moof_offset = %"PRIu64"\n", data->moof_offset ); + lsmash_ifprintf( fp, indent, "traf_number = %"PRIu32"\n", data->traf_number ); + lsmash_ifprintf( fp, indent, "trun_number = %"PRIu32"\n", data->trun_number ); + lsmash_ifprintf( fp, indent, "sample_number = %"PRIu32"\n", data->sample_number ); + --indent; + } + } + return 0; +} + +static int isom_print_mfro( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_mfro_t *mfro = (isom_mfro_t *)box; + int indent = level; + isom_print_box_common( fp, indent++, box, "Movie Fragment Random Access Offset Box" ); + lsmash_ifprintf( fp, indent, "size = %"PRIu32"\n", mfro->length ); + return 0; +} + +int lsmash_print_movie( lsmash_root_t *root, const char *filename ) +{ + if( !root ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file + || !file->print + || !(file->flags & LSMASH_FILE_MODE_DUMP) ) + return LSMASH_ERR_FUNCTION_PARAM; + FILE *destination; + if( !strcmp( filename, "-" ) ) + destination = stdout; + else + destination = lsmash_fopen( filename, "wb" ); + fprintf( destination, "[File]\n" ); + fprintf( destination, " size = %"PRIu64"\n", file->size ); + for( lsmash_entry_t *entry = file->print->head; entry; entry = entry->next ) + { + isom_print_entry_t *data = (isom_print_entry_t *)entry->data; + if( !data || !data->box ) + { + fclose( destination ); + return LSMASH_ERR_NAMELESS; + } + int ret = data->func( destination, file, data->box, data->level ); + if( ret < 0 ) + { + fclose( destination ); + return ret; + } + } + fclose( destination ); + return 0; +} + +static isom_print_box_t isom_select_print_func( isom_box_t *box ) +{ + if( box->manager & LSMASH_UNKNOWN_BOX ) + return isom_print_unknown; + if( box->parent ) + { + isom_box_t *parent = box->parent; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) + { + lsmash_codec_type_t sample_type = box->type; + if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) ) + { + if( box->manager & LSMASH_VIDEO_DESCRIPTION ) + return isom_print_visual_description; + else if( box->manager & LSMASH_AUDIO_DESCRIPTION ) + return isom_print_audio_description; + } + static struct print_description_table_tag + { + lsmash_codec_type_t type; + isom_print_box_t func; + } print_description_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } }; + if( !print_description_table[0].func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( type, func ) print_description_table[i++] = (struct print_description_table_tag){ type, func } + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_print_visual_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_QDM2_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_QDMC_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CDX2_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CDX4_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVCA_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVI_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_IMA4_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_VDVA_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MP3_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_print_audio_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_print_text_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_print_tx3g_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_print_mp4s_description ); + ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL ); +#undef ADD_PRINT_DESCRIPTION_TABLE_ELEMENT + } + for( int i = 0; print_description_table[i].func; i++ ) + if( lsmash_check_codec_type_identical( sample_type, print_description_table[i].type ) ) + return print_description_table[i].func; + return isom_print_unknown; + } + if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) + { + if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_FRMA ) ) + return isom_print_frma; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ENDA ) ) + return isom_print_enda; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_TERMINATOR ) ) + return isom_print_terminator; + else + return isom_print_sample_description_extesion; + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TREF ) ) + return isom_print_track_reference_type; + if( parent->parent ) + { + if( lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_STSD ) ) + return isom_print_sample_description_extesion; + else if( lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_ILST ) + || lsmash_check_box_type_identical( parent->parent->type, QT_BOX_TYPE_ILST ) ) + { + if( parent->type.fourcc == LSMASH_4CC( '-', '-', '-', '-' ) ) + { + if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_MEAN ) ) + return isom_print_mean; + if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_NAME ) ) + return isom_print_name; + } + if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_DATA ) ) + return isom_print_data; + } + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_ILST ) + || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) + return isom_print_metaitem; + } + static struct print_box_table_tag + { + lsmash_box_type_t type; + isom_print_box_t func; + } print_box_table[128] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } }; + if( !print_box_table[0].func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_PRINT_BOX_TABLE_ELEMENT( type, func ) print_box_table[i++] = (struct print_box_table_tag){ type, func } + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_FTYP, isom_print_ftyp ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STYP, isom_print_styp ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SIDX, isom_print_sidx ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOV, isom_print_moov ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MVHD, isom_print_mvhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_IODS, isom_print_iods ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, isom_print_ctab ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAK, isom_print_trak ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TKHD, isom_print_tkhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_TAPT, isom_print_tapt ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CLEF, isom_print_clef ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_PROF, isom_print_prof ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_ENOF, isom_print_enof ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_EDTS, isom_print_edts ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_ELST, isom_print_elst ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TREF, isom_print_tref ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MDIA, isom_print_mdia ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MDHD, isom_print_mdhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_HDLR, isom_print_hdlr ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MINF, isom_print_minf ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_VMHD, isom_print_vmhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SMHD, isom_print_smhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_HMHD, isom_print_hmhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_NMHD, isom_print_nmhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GMHD, isom_print_gmhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GMIN, isom_print_gmin ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_TEXT, isom_print_text ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_DINF, isom_print_dinf ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_DREF, isom_print_dref ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_URL, isom_print_url ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STBL, isom_print_stbl ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSD, isom_print_stsd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_print_clap ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_print_pasp ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_print_colr ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_print_colr ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_print_glbl ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_print_gama ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_print_fiel ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_print_cspc ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_print_sgbt ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_print_stsl ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_print_wave ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_print_chan ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SRAT, isom_print_srat ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, isom_print_ftab ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STTS, isom_print_stts ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CTTS, isom_print_ctts ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CSLG, isom_print_cslg ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSS, isom_print_stss ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_STPS, isom_print_stps ); + 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_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 ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SBGP, isom_print_sbgp ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_UDTA, isom_print_udta ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CHPL, isom_print_chpl ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_WLOC, isom_print_WLOC ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_LOOP, isom_print_LOOP ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_SELO, isom_print_SelO ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_ALLF, isom_print_AllF ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CPRT, isom_print_cprt ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MVEX, isom_print_mvex ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MEHD, isom_print_mehd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TREX, isom_print_trex ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOF, isom_print_moof ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MFHD, isom_print_mfhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAF, isom_print_traf ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TFHD, isom_print_tfhd ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TFDT, isom_print_tfdt ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TRUN, isom_print_trun ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_FREE, isom_print_free ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SKIP, isom_print_free ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MDAT, isom_print_mdat ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_KEYS, isom_print_keys ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_META, isom_print_meta ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_ILST, isom_print_ilst ); + ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_ILST, isom_print_ilst ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRA, isom_print_mfra ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TFRA, isom_print_tfra ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRO, isom_print_mfro ); + ADD_PRINT_BOX_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); +#undef ADD_PRINT_BOX_TABLE_ELEMENT + } + for( int i = 0; print_box_table[i].func; i++ ) + if( lsmash_check_box_type_identical( box->type, print_box_table[i].type ) ) + return print_box_table[i].func; + return isom_print_unknown; +} + +static inline void isom_print_remove_plastic_box( isom_box_t *box ) +{ + if( box->manager & LSMASH_ABSENT_IN_FILE ) + /* free flagged box */ + isom_remove_box_by_itself( box ); +} + +int isom_add_print_func( lsmash_file_t *file, void *box, int level ) +{ + if( !(file->flags & LSMASH_FILE_MODE_DUMP) ) + { + isom_print_remove_plastic_box( box ); + return 0; + } + isom_print_entry_t *data = lsmash_malloc( sizeof(isom_print_entry_t) ); + if( !data ) + { + isom_print_remove_plastic_box( box ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->level = level; + data->box = (isom_box_t *)box; + data->func = isom_select_print_func( (isom_box_t *)box ); + assert( data->func ); + if( lsmash_add_entry( file->print, data ) < 0 ) + { + isom_print_remove_plastic_box( data->box ); + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +static void isom_remove_print_func( isom_print_entry_t *data ) +{ + if( !data || !data->box ) + return; + isom_print_remove_plastic_box( data->box ); + lsmash_free( data ); +} + +void isom_remove_print_funcs( lsmash_file_t *file ) +{ + lsmash_remove_list( file->print, isom_remove_print_func ); + file->print = NULL; +} + +#endif /* LSMASH_DEMUXER_ENABLED */ diff -Nru l-smash-1.9.1/core/print.h l-smash-2.3.0/core/print.h --- l-smash-1.9.1/core/print.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/print.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,29 @@ +/***************************************************************************** + * print.h: + ***************************************************************************** + * Copyright (C) 2010-2014 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_PRINT_H +#define LSMASH_PRINT_H + +int isom_add_print_func( lsmash_file_t *file, void *box, int level ); +void isom_remove_print_funcs( lsmash_file_t *file ); + +#endif /* LSMASH_PRINT_H */ diff -Nru l-smash-1.9.1/core/read.c l-smash-2.3.0/core/read.c --- l-smash-1.9.1/core/read.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/read.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,2907 @@ +/***************************************************************************** + * read.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "box.h" +#include "file.h" +#include "print.h" +#include "read.h" +#include "write.h" + +#include "codecs/mp4a.h" +#include "codecs/mp4sys.h" +#include "codecs/description.h" + +static int isom_bs_read_box_common( lsmash_bs_t *bs, isom_box_t *box ) +{ + assert( bs && box && box->file ); + /* Reset the counter so that we can use it to get position within the box. */ + lsmash_bs_reset_counter( bs ); + /* Read the common fields of box. */ + box->pos = lsmash_bs_get_stream_pos( bs ); + if( bs->eob ) + /* No more read. */ + return 1; + /* Read size and type. */ + box->size = lsmash_bs_get_be32( bs ); + box->type.fourcc = lsmash_bs_get_be32( bs ); + /* If size is set to 1, the actual size is repersented in the next 8 bytes. + * If size is set to 0, this box ends at the end of the stream. */ + if( box->size == 1 ) + box->size = lsmash_bs_get_be64( bs ); + if( box->size == 0 ) + { + /* This box is the last box in the stream. */ + box->manager |= LSMASH_LAST_BOX; + if( !bs->unseekable ) + box->size = bs->written - (lsmash_bs_get_stream_pos( bs ) - lsmash_bs_count( bs )); + else + /* We haven't known the box size yet. + * To get the box size, read the stream until the end of the stream. */ + while( 1 ) + { + int ret = lsmash_bs_read( bs, 1 ); + if( bs->eof || ret < 0 ) + { + /* OK, now we know the box size. */ + box->size = lsmash_bs_count( bs ) + lsmash_bs_get_remaining_buffer_size( bs ); + if( ret < 0 ) + /* This box may end incompletely at the end of the stream. */ + box->manager |= LSMASH_INCOMPLETE_BOX; + break; + } + } + } + /* Here, we don't set up extended box type fields if this box is not a UUID Box. */ + if( box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc + && box->size >= lsmash_bs_count( bs ) + 16 ) + { + /* Get UUID. */ + lsmash_box_type_t *type = &box->type; + uint64_t temp64 = lsmash_bs_get_be64( bs ); + type->user.fourcc = (temp64 >> 32) & 0xffffffff; + LSMASH_SET_BE32( &type->user.id[0], temp64 ); + temp64 = lsmash_bs_get_be64( bs ); + LSMASH_SET_BE64( &type->user.id[4], temp64 ); + } + return bs->eob; +} + +static int isom_read_fullbox_common_extension( lsmash_bs_t *bs, isom_box_t *box ) +{ + if( !isom_is_fullbox( box ) ) + return 0; + /* Get version and flags. */ + box->version = lsmash_bs_get_byte( bs ); + box->flags = lsmash_bs_get_be24( bs ); + box->manager |= LSMASH_FULLBOX; + return 0; +} + +/* Don't copy destructor since a destructor is defined as box specific. */ +static void isom_basebox_common_copy( isom_box_t *dst, isom_box_t *src ) +{ + dst->root = src->root; + dst->file = src->file; + dst->parent = src->parent; + dst->manager = src->manager; + dst->pos = src->pos; + dst->size = src->size; + dst->type = src->type; +} + +static void isom_fullbox_common_copy( isom_box_t *dst, isom_box_t *src ) +{ + dst->root = src->root; + dst->file = src->file; + dst->parent = src->parent; + dst->manager = src->manager; + dst->pos = src->pos; + dst->size = src->size; + dst->type = src->type; + dst->version = src->version; + dst->flags = src->flags; +} + +static void isom_box_common_copy( void *dst, void *src ) +{ + if( src && lsmash_check_box_type_identical( ((isom_box_t *)src)->type, ISOM_BOX_TYPE_STSD ) ) + { + isom_basebox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); + return; + } + if( isom_is_fullbox( src ) ) + isom_fullbox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); + else + isom_basebox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); +} + +static void isom_skip_box_rest( lsmash_bs_t *bs, isom_box_t *box ) +{ + if( box->manager & LSMASH_LAST_BOX ) + { + box->size = (box->manager & LSMASH_FULLBOX) ? ISOM_FULLBOX_COMMON_SIZE : ISOM_BASEBOX_COMMON_SIZE; + uint64_t start = lsmash_bs_get_stream_pos( bs ); + if( !bs->unseekable ) + lsmash_bs_read_seek( bs, 0, SEEK_END ); + else + while( !bs->eob ) + lsmash_bs_skip_bytes( bs, UINT32_MAX ); + uint64_t end = lsmash_bs_get_stream_pos( bs ); + box->size += end - start; + return; + } + uint64_t skip_bytes = box->size - lsmash_bs_count( bs ); + if( !bs->unseekable ) + { + /* The stream is seekable. So, skip by seeking the stream. */ + uint64_t start = lsmash_bs_get_stream_pos( bs ); + lsmash_bs_read_seek( bs, skip_bytes, SEEK_CUR ); + uint64_t end = lsmash_bs_get_stream_pos( bs ); + if( end - start != skip_bytes ) + /* not match size */ + box->manager |= LSMASH_INCOMPLETE_BOX; + return; + } + /* The stream is unseekable. So, skip by reading the stream. */ + lsmash_bs_skip_bytes_64( bs, skip_bytes ); + if( box->size > lsmash_bs_count( bs ) ) + box->manager |= LSMASH_INCOMPLETE_BOX; +} + +static void isom_check_box_size( lsmash_bs_t *bs, isom_box_t *box ) +{ + uint64_t pos = lsmash_bs_count( bs ); + if( box->manager & LSMASH_LAST_BOX ) + { + box->size = pos; + return; + } + if( box->size < pos ) + { + printf( "[%s] box has more bytes: %"PRId64"\n", isom_4cc2str( box->type.fourcc ), pos - box->size ); + box->size = pos; + } +} + +static int isom_read_children( lsmash_file_t *file, isom_box_t *box, void *parent, int level ) +{ + int ret; + lsmash_bs_t *bs = file->bs; + isom_box_t *parent_box = (isom_box_t *)parent; + uint64_t parent_pos = lsmash_bs_count( bs ); + while( !(ret = isom_read_box( file, box, parent_box, parent_pos, level )) ) + { + parent_pos += box->size; + if( parent_box->size <= parent_pos || bs->eob || bs->error ) + break; + } + box->size = parent_pos; /* for file size */ + return ret; +} + +static int isom_read_leaf_box_common_last_process( lsmash_file_t *file, isom_box_t *box, int level, void *instance ) +{ + isom_check_box_size( file->bs, box ); + isom_box_common_copy( instance, box ); + return isom_add_print_func( file, instance, level ); +} + +static int isom_read_unknown_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + lsmash_bs_t *bs = file->bs; + uint64_t read_size = box->size - lsmash_bs_count( bs ); + if( box->manager & LSMASH_INCOMPLETE_BOX ) + return LSMASH_ERR_INVALID_DATA; + isom_unknown_box_t *unknown = lsmash_malloc_zero( sizeof(isom_unknown_box_t) ); + if( !unknown ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( &parent->extensions, unknown ) < 0 ) + { + lsmash_free( unknown ); + return LSMASH_ERR_MEMORY_ALLOC; + } + isom_box_common_copy( unknown, box ); + unknown->manager |= LSMASH_UNKNOWN_BOX; + unknown->destruct = (isom_extension_destructor_t)isom_remove_unknown_box; + isom_set_box_writer( (isom_box_t *)unknown ); + if( read_size ) + { + unknown->unknown_field = lsmash_bs_get_bytes( bs, read_size ); + if( unknown->unknown_field ) + unknown->unknown_size = read_size; + else + unknown->manager |= LSMASH_INCOMPLETE_BOX; + } + if( !(file->flags & LSMASH_FILE_MODE_DUMP) ) + return 0; + /* Create a dummy for dump. */ + isom_box_t *dummy = lsmash_malloc_zero( sizeof(isom_box_t) ); + if( !dummy ) + return LSMASH_ERR_MEMORY_ALLOC; + box->manager |= LSMASH_ABSENT_IN_FILE; + isom_box_common_copy( dummy, box ); + int ret = isom_add_print_func( file, dummy, level ); + if( ret < 0 ) + { + lsmash_free( dummy ); + return ret; + } + return 0; +} + +#define ADD_BOX( box_name, parent_type ) \ + isom_##box_name##_t *box_name = isom_add_##box_name( (parent_type *)parent ); \ + if( !box_name ) \ + return LSMASH_ERR_NAMELESS + +static int isom_read_ftyp( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) || ((lsmash_file_t *)parent)->ftyp ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( ftyp, lsmash_file_t ); + lsmash_bs_t *bs = file->bs; + ftyp->major_brand = lsmash_bs_get_be32( bs ); + ftyp->minor_version = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_count( bs ); + ftyp->brand_count = box->size > pos ? (box->size - pos) / sizeof(uint32_t) : 0; + size_t alloc_size = ftyp->brand_count * sizeof(uint32_t); + ftyp->compatible_brands = ftyp->brand_count ? lsmash_malloc( alloc_size ) : NULL; + if( ftyp->brand_count && !ftyp->compatible_brands ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; i < ftyp->brand_count; i++ ) + ftyp->compatible_brands[i] = lsmash_bs_get_be32( bs ); + if( !file->compatible_brands && ftyp->compatible_brands ) + { + file->compatible_brands = lsmash_memdup( ftyp->compatible_brands, alloc_size ); + if( !file->compatible_brands ) + return LSMASH_ERR_MEMORY_ALLOC; + file->brand_count = ftyp->brand_count; + } + return isom_read_leaf_box_common_last_process( file, box, level, ftyp ); +} + +static int isom_read_styp( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( styp, lsmash_file_t ); + lsmash_bs_t *bs = file->bs; + styp->major_brand = lsmash_bs_get_be32( bs ); + styp->minor_version = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_count( bs ); + styp->brand_count = box->size > pos ? (box->size - pos) / sizeof(uint32_t) : 0; + size_t alloc_size = styp->brand_count * sizeof(uint32_t); + styp->compatible_brands = styp->brand_count ? lsmash_malloc( alloc_size ) : NULL; + if( styp->brand_count && !styp->compatible_brands ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; i < styp->brand_count; i++ ) + styp->compatible_brands[i] = lsmash_bs_get_be32( bs ); + if( !file->compatible_brands && styp->compatible_brands ) + { + file->compatible_brands = lsmash_memdup( styp->compatible_brands, alloc_size ); + if( !file->compatible_brands ) + return LSMASH_ERR_MEMORY_ALLOC; + file->brand_count = styp->brand_count; + } + file->flags |= LSMASH_FILE_MODE_SEGMENT; + return isom_read_leaf_box_common_last_process( file, box, level, styp ); +} + +static int isom_read_sidx( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( sidx, lsmash_file_t ); + lsmash_bs_t *bs = file->bs; + sidx->reference_ID = lsmash_bs_get_be32( bs ); + sidx->timescale = lsmash_bs_get_be32( bs ); + if( box->version == 0 ) + { + sidx->earliest_presentation_time = lsmash_bs_get_be32( bs ); + sidx->first_offset = lsmash_bs_get_be32( bs ); + } + else + { + sidx->earliest_presentation_time = lsmash_bs_get_be64( bs ); + sidx->first_offset = lsmash_bs_get_be64( bs ); + } + sidx->reserved = lsmash_bs_get_be16( bs ); + sidx->reference_count = lsmash_bs_get_be16( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); + pos < box->size && sidx->list->entry_count < sidx->reference_count; + pos = lsmash_bs_count( bs ) ) + { + isom_sidx_referenced_item_t *data = lsmash_malloc( sizeof(isom_sidx_referenced_item_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( sidx->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + uint32_t temp32; + temp32 = lsmash_bs_get_be32( bs ); + data->reference_type = (temp32 >> 31) & 0x00000001; + data->reference_size = temp32 & 0x7FFFFFFF; + data->subsegment_duration = lsmash_bs_get_be32( bs ); + temp32 = lsmash_bs_get_be32( bs ); + data->starts_with_SAP = (temp32 >> 31) & 0x00000001; + data->SAP_type = (temp32 >> 28) & 0x00000007; + data->SAP_delta_time = temp32 & 0x0FFFFFFF; + } + file->flags |= LSMASH_FILE_MODE_INDEX; + return isom_read_leaf_box_common_last_process( file, box, level, sidx ); +} + +static int isom_read_moov( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) || ((lsmash_file_t *)parent)->moov ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( moov, lsmash_file_t ); + file->flags |= LSMASH_FILE_MODE_INITIALIZATION; + file->initializer = file; + isom_box_common_copy( moov, box ); + int ret = isom_add_print_func( file, moov, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, moov, level ); +} + +static int isom_read_mvhd( 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_MOOV ) || ((isom_moov_t *)parent)->mvhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mvhd, isom_moov_t ); + lsmash_bs_t *bs = file->bs; + if( box->version ) + { + mvhd->creation_time = lsmash_bs_get_be64( bs ); + mvhd->modification_time = lsmash_bs_get_be64( bs ); + mvhd->timescale = lsmash_bs_get_be32( bs ); + mvhd->duration = lsmash_bs_get_be64( bs ); + } + else + { + mvhd->creation_time = lsmash_bs_get_be32( bs ); + mvhd->modification_time = lsmash_bs_get_be32( bs ); + mvhd->timescale = lsmash_bs_get_be32( bs ); + mvhd->duration = lsmash_bs_get_be32( bs ); + } + mvhd->rate = lsmash_bs_get_be32( bs ); + mvhd->volume = lsmash_bs_get_be16( bs ); + mvhd->reserved = lsmash_bs_get_be16( bs ); + mvhd->preferredLong[0] = lsmash_bs_get_be32( bs ); + mvhd->preferredLong[1] = lsmash_bs_get_be32( bs ); + for( int i = 0; i < 9; i++ ) + mvhd->matrix[i] = lsmash_bs_get_be32( bs ); + mvhd->previewTime = lsmash_bs_get_be32( bs ); + mvhd->previewDuration = lsmash_bs_get_be32( bs ); + mvhd->posterTime = lsmash_bs_get_be32( bs ); + mvhd->selectionTime = lsmash_bs_get_be32( bs ); + mvhd->selectionDuration = lsmash_bs_get_be32( bs ); + mvhd->currentTime = lsmash_bs_get_be32( bs ); + mvhd->next_track_ID = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, mvhd ); +} + +static int isom_read_iods( 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_MOOV ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( iods, isom_moov_t ); + lsmash_bs_t *bs = file->bs; + iods->OD = mp4sys_get_descriptor( bs, NULL ); + if( !iods->OD ) + return LSMASH_ERR_INVALID_DATA; + isom_skip_box_rest( file->bs, box ); + return isom_read_leaf_box_common_last_process( file, box, level, iods ); +} + +static int isom_read_qt_color_table( lsmash_bs_t *bs, isom_qt_color_table_t *color_table ) +{ + color_table->seed = lsmash_bs_get_be32( bs ); + color_table->flags = lsmash_bs_get_be16( bs ); + color_table->size = lsmash_bs_get_be16( bs ); + if( bs->eob ) + return LSMASH_ERR_INVALID_DATA; + isom_qt_color_array_t *array = lsmash_malloc_zero( (color_table->size + 1) * sizeof(isom_qt_color_array_t) ); + if( !array ) + return LSMASH_ERR_MEMORY_ALLOC; + color_table->array = array; + for( uint16_t i = 0; i <= color_table->size; i++ ) + { + uint64_t color = lsmash_bs_get_be64( bs ); + array[i].value = (color >> 48) & 0xffff; + array[i].r = (color >> 32) & 0xffff; + array[i].g = (color >> 16) & 0xffff; + array[i].b = color & 0xffff; + } + return 0; +} + +static int isom_read_ctab( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( ctab, isom_moov_t ); + lsmash_bs_t *bs = file->bs; + int ret = isom_read_qt_color_table( bs, &ctab->color_table ); + if( ret < 0 ) + return ret; + return isom_read_leaf_box_common_last_process( file, box, level, ctab ); +} + +static int isom_read_trak( 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_MOOV ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( trak, isom_moov_t ); + box->parent = parent; + box->root = file->root; + box->file = file; + isom_box_common_copy( trak, box ); + int ret = isom_add_print_func( file, trak, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, trak, level ); +} + +static int isom_read_tkhd( 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_TRAK ) || ((isom_trak_t *)parent)->tkhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( tkhd, isom_trak_t ); + lsmash_bs_t *bs = file->bs; + if( box->version ) + { + tkhd->creation_time = lsmash_bs_get_be64( bs ); + tkhd->modification_time = lsmash_bs_get_be64( bs ); + tkhd->track_ID = lsmash_bs_get_be32( bs ); + tkhd->reserved1 = lsmash_bs_get_be32( bs ); + tkhd->duration = lsmash_bs_get_be64( bs ); + } + else + { + tkhd->creation_time = lsmash_bs_get_be32( bs ); + tkhd->modification_time = lsmash_bs_get_be32( bs ); + tkhd->track_ID = lsmash_bs_get_be32( bs ); + tkhd->reserved1 = lsmash_bs_get_be32( bs ); + tkhd->duration = lsmash_bs_get_be32( bs ); + } + tkhd->reserved2[0] = lsmash_bs_get_be32( bs ); + tkhd->reserved2[1] = lsmash_bs_get_be32( bs ); + tkhd->layer = lsmash_bs_get_be16( bs ); + tkhd->alternate_group = lsmash_bs_get_be16( bs ); + tkhd->volume = lsmash_bs_get_be16( bs ); + tkhd->reserved3 = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 9; i++ ) + tkhd->matrix[i] = lsmash_bs_get_be32( bs ); + tkhd->width = lsmash_bs_get_be32( bs ); + tkhd->height = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, tkhd ); +} + +static int isom_read_tapt( 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_TRAK ) || ((isom_trak_t *)parent)->tapt ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( tapt, isom_trak_t ); + isom_box_common_copy( tapt, box ); + int ret = isom_add_print_func( file, tapt, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, tapt, level ); +} + +static int isom_read_clef( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_TAPT ) || ((isom_tapt_t *)parent)->clef ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( clef, isom_tapt_t ); + lsmash_bs_t *bs = file->bs; + clef->width = lsmash_bs_get_be32( bs ); + clef->height = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, clef ); +} + +static int isom_read_prof( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_TAPT ) || ((isom_tapt_t *)parent)->prof ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( prof, isom_tapt_t ); + lsmash_bs_t *bs = file->bs; + prof->width = lsmash_bs_get_be32( bs ); + prof->height = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, prof ); +} + +static int isom_read_enof( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_TAPT ) || ((isom_tapt_t *)parent)->enof ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( enof, isom_tapt_t ); + lsmash_bs_t *bs = file->bs; + enof->width = lsmash_bs_get_be32( bs ); + enof->height = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, enof ); +} + +static int isom_read_edts( 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_TRAK ) || ((isom_trak_t *)parent)->edts ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( edts, isom_trak_t ); + isom_box_common_copy( edts, box ); + if( isom_add_print_func( file, edts, level ) < 0 ) + return -1; + return isom_read_children( file, box, edts, level ); +} + +static int isom_read_elst( 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_EDTS ) || ((isom_edts_t *)parent)->elst ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( elst, isom_edts_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && elst->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( elst->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( box->version == 1 ) + { + data->segment_duration = lsmash_bs_get_be64( bs ); + data->media_time = (int64_t)lsmash_bs_get_be64( bs ); + } + else + { + data->segment_duration = lsmash_bs_get_be32( bs ); + data->media_time = (int32_t)lsmash_bs_get_be32( bs ); + } + data->media_rate = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, elst ); +} + +static int isom_read_tref( 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_TRAK ) || ((isom_trak_t *)parent)->tref ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( tref, isom_trak_t ); + isom_box_common_copy( tref, box ); + int ret = isom_add_print_func( file, tref, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, tref, level ); +} + +static int isom_read_track_reference_type( 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_TREF ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_tref_type_t *ref = isom_add_track_reference_type( (isom_tref_t *)parent, box->type.fourcc ); + if( !ref ) + return LSMASH_ERR_NAMELESS; + lsmash_bs_t *bs = file->bs; + ref->ref_count = (box->size - lsmash_bs_count( bs ) ) / sizeof(uint32_t); + if( ref->ref_count ) + { + ref->track_ID = lsmash_malloc( ref->ref_count * sizeof(uint32_t) ); + if( !ref->track_ID ) + { + ref->ref_count = 0; + return LSMASH_ERR_MEMORY_ALLOC; + } + for( uint32_t i = 0; i < ref->ref_count; i++ ) + ref->track_ID[i] = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, ref ); +} + +static int isom_read_mdia( 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_TRAK ) || ((isom_trak_t *)parent)->mdia ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mdia, isom_trak_t ); + isom_box_common_copy( mdia, box ); + int ret = isom_add_print_func( file, mdia, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, mdia, level ); +} + +static int isom_read_mdhd( 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_MDIA ) || ((isom_mdia_t *)parent)->mdhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mdhd, isom_mdia_t ); + lsmash_bs_t *bs = file->bs; + if( box->version ) + { + mdhd->creation_time = lsmash_bs_get_be64( bs ); + mdhd->modification_time = lsmash_bs_get_be64( bs ); + mdhd->timescale = lsmash_bs_get_be32( bs ); + mdhd->duration = lsmash_bs_get_be64( bs ); + } + else + { + mdhd->creation_time = lsmash_bs_get_be32( bs ); + mdhd->modification_time = lsmash_bs_get_be32( bs ); + mdhd->timescale = lsmash_bs_get_be32( bs ); + mdhd->duration = lsmash_bs_get_be32( bs ); + } + mdhd->language = lsmash_bs_get_be16( bs ); + mdhd->quality = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, mdhd ); +} + +static int isom_read_hdlr( 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_MDIA ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) + && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF )) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) && ((isom_mdia_t *)parent)->hdlr) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) && ((isom_meta_t *)parent)->hdlr) + || (lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) && ((isom_meta_t *)parent)->hdlr) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) && ((isom_minf_t *)parent)->hdlr) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( hdlr, void ); + lsmash_bs_t *bs = file->bs; + hdlr->componentType = lsmash_bs_get_be32( bs ); + hdlr->componentSubtype = lsmash_bs_get_be32( bs ); + hdlr->componentManufacturer = lsmash_bs_get_be32( bs ); + hdlr->componentFlags = lsmash_bs_get_be32( bs ); + hdlr->componentFlagsMask = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_count( bs ); + hdlr->componentName_length = box->size - pos; + if( hdlr->componentName_length ) + { + hdlr->componentName = lsmash_malloc( hdlr->componentName_length ); + if( !hdlr->componentName ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; pos < box->size; pos = lsmash_bs_count( bs ) ) + hdlr->componentName[i++] = lsmash_bs_get_byte( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, hdlr ); +} + +static int isom_read_minf( 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_MDIA ) || ((isom_mdia_t *)parent)->minf ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( minf, isom_mdia_t ); + isom_box_common_copy( minf, box ); + int ret = isom_add_print_func( file, minf, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, minf, level ); +} + +static int isom_read_vmhd( 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_MINF ) || ((isom_minf_t *)parent)->vmhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( vmhd, isom_minf_t ); + lsmash_bs_t *bs = file->bs; + vmhd->graphicsmode = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 3; i++ ) + vmhd->opcolor[i] = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, vmhd ); +} + +static int isom_read_smhd( 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_MINF ) || ((isom_minf_t *)parent)->smhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( smhd, isom_minf_t ); + lsmash_bs_t *bs = file->bs; + smhd->balance = lsmash_bs_get_be16( bs ); + smhd->reserved = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, smhd ); +} + +static int isom_read_hmhd( 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_MINF ) || ((isom_minf_t *)parent)->hmhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( hmhd, isom_minf_t ); + lsmash_bs_t *bs = file->bs; + hmhd->maxPDUsize = lsmash_bs_get_be16( bs ); + hmhd->avgPDUsize = lsmash_bs_get_be16( bs ); + hmhd->maxbitrate = lsmash_bs_get_be32( bs ); + hmhd->avgbitrate = lsmash_bs_get_be32( bs ); + hmhd->reserved = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, hmhd ); +} + +static int isom_read_nmhd( 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_MINF ) || ((isom_minf_t *)parent)->nmhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( nmhd, isom_minf_t ); + return isom_read_leaf_box_common_last_process( file, box, level, nmhd ); +} + +static int isom_read_gmhd( 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_MINF ) || ((isom_minf_t *)parent)->gmhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( gmhd, isom_minf_t ); + isom_box_common_copy( gmhd, box ); + int ret = isom_add_print_func( file, gmhd, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, gmhd, level ); +} + +static int isom_read_gmin( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_GMHD ) || ((isom_gmhd_t *)parent)->gmin ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( gmin, isom_gmhd_t ); + lsmash_bs_t *bs = file->bs; + gmin->graphicsmode = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 3; i++ ) + gmin->opcolor[i] = lsmash_bs_get_be16( bs ); + gmin->balance = lsmash_bs_get_be16( bs ); + gmin->reserved = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, gmin ); +} + +static int isom_read_text( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_GMHD ) || ((isom_gmhd_t *)parent)->text ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( text, isom_gmhd_t ); + lsmash_bs_t *bs = file->bs; + for( int i = 0; i < 9; i++ ) + text->matrix[i] = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, text ); +} + +static int isom_read_dinf( 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_MINF ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) + && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META )) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) && ((isom_minf_t *)parent)->dinf) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) && ((isom_meta_t *)parent)->dinf) + || (lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) && ((isom_meta_t *)parent)->dinf) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( dinf, void ); + isom_box_common_copy( dinf, box ); + int ret = isom_add_print_func( file, dinf, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, dinf, level ); +} + +static int isom_read_dref( 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_DINF ) || ((isom_dinf_t *)parent)->dref ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( dref, isom_dinf_t ); + lsmash_bs_t *bs = file->bs; + dref->list.entry_count = lsmash_bs_get_be32( bs ); + isom_box_common_copy( dref, box ); + int ret = isom_add_print_func( file, dref, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, dref, level ); +} + +static int isom_read_dref_entry( 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_DREF ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_dref_t *dref = (isom_dref_t *)parent; + if( !dref->list.head ) + dref->list.entry_count = 0; /* discard entry_count gotten from the file */ + isom_dref_entry_t *ref = isom_add_dref_entry( dref, box->type ); + if( !ref ) + return LSMASH_ERR_NAMELESS; + lsmash_bs_t *bs = file->bs; + if( lsmash_check_box_type_identical( ref->type, ISOM_BOX_TYPE_URL ) ) + { + uint64_t pos = lsmash_bs_count( bs ); + ref->location_length = box->size - pos; + if( ref->location_length ) + { + ref->location = lsmash_malloc( ref->location_length ); + if( !ref->location ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; pos < box->size; pos = lsmash_bs_count( bs ) ) + ref->location[i++] = lsmash_bs_get_byte( bs ); + } + } + else + isom_skip_box_rest( bs, box ); + if( box->flags & 0x000001 ) + ref->ref_file = ref->file; + box->parent = parent; + return isom_read_leaf_box_common_last_process( file, box, level, ref ); +} + +static int isom_read_stbl( 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_MINF ) || ((isom_minf_t *)parent)->stbl ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stbl, isom_minf_t ); + isom_box_common_copy( stbl, box ); + int ret = isom_add_print_func( file, stbl, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, stbl, level ); +} + +static int isom_read_stsd( 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)->stsd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stsd, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + stsd->entry_count = lsmash_bs_get_be32( bs ); + isom_box_common_copy( stsd, box ); + int ret = isom_add_print_func( file, stsd, level ); + if( ret < 0 ) + return ret; + uint64_t stsd_pos = lsmash_bs_count( bs ); + for( uint32_t i = 0; i < stsd->entry_count || (stsd_pos + ISOM_BASEBOX_COMMON_SIZE) <= stsd->size; i++ ) + { + if( (ret = isom_read_box( file, box, (isom_box_t *)stsd, stsd_pos, level )) != 0 ) + break; + stsd_pos += box->size; + if( stsd->size <= stsd_pos || bs->eob || bs->error ) + break; + } + if( stsd->size < stsd_pos ) + { + printf( "[stsd] box has extra bytes: %"PRId64"\n", stsd_pos - stsd->size ); + stsd->size = stsd_pos; + } + box->size = stsd->size; + return ret; +} + +static int isom_read_codec_specific( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + lsmash_bs_t *bs = file->bs; + uint64_t opaque_pos = lsmash_bs_count( bs ); + uint64_t exdata_length = box->size - opaque_pos; + if( exdata_length > UINT32_MAX ) + return LSMASH_ERR_MEMORY_ALLOC; + uint8_t *exdata = lsmash_malloc( box->size ); + if( !exdata ) + return LSMASH_ERR_MEMORY_ALLOC; + int ret = lsmash_bs_get_bytes_ex( bs, exdata_length, exdata + (uintptr_t)opaque_pos ); + if( ret < 0 ) + goto fail; + LSMASH_SET_BE32( &exdata[0], box->size ); + LSMASH_SET_BE32( &exdata[4], box->type.fourcc ); + uintptr_t i = 8; + if( box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc ) + { + LSMASH_SET_BE32( &exdata[8], box->type.user.fourcc ); + memcpy( &exdata[12], box->type.user.id, 12 ); + i += 16; + } + if( box->manager & LSMASH_FULLBOX ) + { + LSMASH_SET_BYTE( &exdata[i], box->version ); + i += 1; + LSMASH_SET_BE24( &exdata[i], box->flags ); + i += 3; + } + if( i != opaque_pos ) + { + ret = LSMASH_ERR_INVALID_DATA; + goto fail; + } + if( (ret = isom_add_extension_binary( parent, box->type, LSMASH_BOX_PRECEDENCE_N, exdata, box->size )) < 0 ) + goto fail; + isom_box_t *ext = (isom_box_t *)parent->extensions.tail->data; + box->manager |= ext->manager; + isom_check_box_size( file->bs, box ); + isom_basebox_common_copy( ext, box ); + return isom_add_print_func( file, ext, level ); +fail: + lsmash_free( exdata ); + return ret; +} + +static void *isom_sample_description_alloc( lsmash_codec_type_t sample_type ) +{ + static struct description_alloc_table_tag + { + lsmash_codec_type_t type; + size_t alloc_size; + } description_alloc_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, 0 } }; + if( description_alloc_table[0].alloc_size == 0 ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( type, alloc_size ) \ + description_alloc_table[i++] = (struct description_alloc_table_tag){ type, alloc_size } + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, sizeof(isom_visual_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_QDM2_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_QDMC_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CDX2_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CDX4_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVCA_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVI_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_IMA4_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_RAW_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_VDVA_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MP3_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, sizeof(isom_audio_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, sizeof(isom_tx3g_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, sizeof(isom_qt_text_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, sizeof(isom_mp4s_entry_t) ); + ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, 0 ); +#undef ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT + } + for( int i = 0; description_alloc_table[i].alloc_size; i++ ) + if( lsmash_check_codec_type_identical( sample_type, description_alloc_table[i].type ) ) + return lsmash_malloc_zero( description_alloc_table[i].alloc_size ); + return NULL; +} + +static void *isom_add_description( lsmash_codec_type_t sample_type, isom_stsd_t *stsd ) +{ + void *sample = isom_sample_description_alloc( sample_type ); + if( !sample ) + return NULL; + if( lsmash_add_entry( &stsd->list, sample ) < 0 ) + { + lsmash_free( sample ); + return NULL; + } + if( lsmash_add_entry( &stsd->extensions, sample ) < 0 ) + { + lsmash_remove_entry_tail( &stsd->list, lsmash_free ); + return NULL; + } + ((isom_box_t *)sample)->destruct = (isom_extension_destructor_t)isom_remove_sample_description; + return sample; +} + +static int isom_read_visual_description( 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_STSD ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_visual_entry_t *visual = (isom_visual_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); + if( !visual ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = file->bs; + for( int i = 0; i < 6; i++ ) + visual->reserved[i] = lsmash_bs_get_byte( bs ); + visual->data_reference_index = lsmash_bs_get_be16( bs ); + visual->version = lsmash_bs_get_be16( bs ); + visual->revision_level = lsmash_bs_get_be16( bs ); + visual->vendor = lsmash_bs_get_be32( bs ); + visual->temporalQuality = lsmash_bs_get_be32( bs ); + visual->spatialQuality = lsmash_bs_get_be32( bs ); + visual->width = lsmash_bs_get_be16( bs ); + visual->height = lsmash_bs_get_be16( bs ); + visual->horizresolution = lsmash_bs_get_be32( bs ); + visual->vertresolution = lsmash_bs_get_be32( bs ); + visual->dataSize = lsmash_bs_get_be32( bs ); + visual->frame_count = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 32; i++ ) + visual->compressorname[i] = lsmash_bs_get_byte( bs ); + visual->depth = lsmash_bs_get_be16( bs ); + visual->color_table_ID = lsmash_bs_get_be16( bs ); + int ret; + if( visual->color_table_ID == 0 + && lsmash_bs_get_pos( bs ) < box->size + && (ret = isom_read_qt_color_table( bs, &visual->color_table )) < 0 ) + return ret; + box->parent = parent; + box->manager |= LSMASH_VIDEO_DESCRIPTION; + isom_box_common_copy( visual, box ); + if( (ret = isom_add_print_func( file, visual, level )) < 0 ) + return ret; + return isom_read_children( file, box, visual, level ); +} + +static int isom_read_esds( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, ISOM_CODEC_TYPE_MP4V_VIDEO ) + && !lsmash_check_box_type_identical( parent->type, ISOM_CODEC_TYPE_MP4A_AUDIO ) + && !lsmash_check_box_type_identical( parent->type, ISOM_CODEC_TYPE_M4AE_AUDIO ) + && !lsmash_check_box_type_identical( parent->type, ISOM_CODEC_TYPE_MP4S_SYSTEM ) + && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) + return isom_read_unknown_box( file, box, parent, level ); + if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) + { + box->type = QT_BOX_TYPE_ESDS; + if( parent->parent && lsmash_check_box_type_identical( parent->parent->type, ISOM_CODEC_TYPE_MP4A_AUDIO ) ) + parent->parent->type = QT_CODEC_TYPE_MP4A_AUDIO; + } + else + box->type = ISOM_BOX_TYPE_ESDS; + ADD_BOX( esds, void ); + lsmash_bs_t *bs = file->bs; + esds->ES = mp4sys_get_descriptor( bs, NULL ); + if( !esds->ES ) + return LSMASH_ERR_INVALID_DATA; + return isom_read_leaf_box_common_last_process( file, box, level, esds ); +} + +static int isom_read_btrt( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( btrt, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + btrt->bufferSizeDB = lsmash_bs_get_be32( bs ); + btrt->maxBitrate = lsmash_bs_get_be32( bs ); + btrt->avgBitrate = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, btrt ); +} + +static int isom_read_glbl( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( glbl, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + uint32_t header_size = box->size - ISOM_BASEBOX_COMMON_SIZE; + if( header_size ) + { + glbl->header_data = lsmash_malloc( header_size ); + if( !glbl->header_data ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; i < header_size; i++ ) + glbl->header_data[i] = lsmash_bs_get_byte( bs ); + } + glbl->header_size = header_size; + return isom_read_leaf_box_common_last_process( file, box, level, glbl ); +} + +static int isom_read_clap( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( clap, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + clap->cleanApertureWidthN = lsmash_bs_get_be32( bs ); + clap->cleanApertureWidthD = lsmash_bs_get_be32( bs ); + clap->cleanApertureHeightN = lsmash_bs_get_be32( bs ); + clap->cleanApertureHeightD = lsmash_bs_get_be32( bs ); + clap->horizOffN = lsmash_bs_get_be32( bs ); + clap->horizOffD = lsmash_bs_get_be32( bs ); + clap->vertOffN = lsmash_bs_get_be32( bs ); + clap->vertOffD = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, clap ); +} + +static int isom_read_pasp( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( pasp, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + pasp->hSpacing = lsmash_bs_get_be32( bs ); + pasp->vSpacing = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, pasp ); +} + +static int isom_read_colr( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( colr, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + colr->color_parameter_type = lsmash_bs_get_be32( bs ); + if( colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_NCLC + || colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) + { + colr->primaries_index = lsmash_bs_get_be16( bs ); + colr->transfer_function_index = lsmash_bs_get_be16( bs ); + colr->matrix_index = lsmash_bs_get_be16( bs ); + if( colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) + { + if( lsmash_bs_count( bs ) < box->size ) + { + uint8_t temp8 = lsmash_bs_get_byte( bs ); + colr->full_range_flag = (temp8 >> 7) & 0x01; + colr->reserved = temp8 & 0x7f; + } + else + { + /* It seems this box is broken or incomplete. */ + box->manager |= LSMASH_INCOMPLETE_BOX; + colr->full_range_flag = 0; + colr->reserved = 0; + } + } + else + box->manager |= LSMASH_QTFF_BASE; + } + box->type = (box->manager & LSMASH_QTFF_BASE) ? QT_BOX_TYPE_COLR : ISOM_BOX_TYPE_COLR; + return isom_read_leaf_box_common_last_process( file, box, level, colr ); +} + +static int isom_read_gama( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( gama, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + gama->level = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, gama ); +} + +static int isom_read_fiel( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( fiel, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + fiel->fields = lsmash_bs_get_byte( bs ); + fiel->detail = lsmash_bs_get_byte( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, fiel ); +} + +static int isom_read_cspc( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( cspc, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + cspc->pixel_format = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, cspc ); +} + +static int isom_read_sgbt( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( sgbt, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + sgbt->significantBits = lsmash_bs_get_byte( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, sgbt ); +} + +static int isom_read_stsl( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( stsl, isom_visual_entry_t ); + lsmash_bs_t *bs = file->bs; + stsl->constraint_flag = lsmash_bs_get_byte( bs ); + stsl->scale_method = lsmash_bs_get_byte( bs ); + stsl->display_center_x = lsmash_bs_get_be16( bs ); + stsl->display_center_y = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, stsl ); +} + +static int isom_read_audio_description( 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_STSD ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_audio_entry_t *audio = (isom_audio_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); + if( !audio ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = file->bs; + for( int i = 0; i < 6; i++ ) + audio->reserved[i] = lsmash_bs_get_byte( bs ); + audio->data_reference_index = lsmash_bs_get_be16( bs ); + audio->version = lsmash_bs_get_be16( bs ); + audio->revision_level = lsmash_bs_get_be16( bs ); + audio->vendor = lsmash_bs_get_be32( bs ); + audio->channelcount = lsmash_bs_get_be16( bs ); + audio->samplesize = lsmash_bs_get_be16( bs ); + audio->compression_ID = lsmash_bs_get_be16( bs ); + audio->packet_size = lsmash_bs_get_be16( bs ); + audio->samplerate = lsmash_bs_get_be32( bs ); + if( audio->version == 1 ) + { + if( ((isom_stsd_t *)parent)->version == 0 ) + { + audio->samplesPerPacket = lsmash_bs_get_be32( bs ); + audio->bytesPerPacket = lsmash_bs_get_be32( bs ); + audio->bytesPerFrame = lsmash_bs_get_be32( bs ); + audio->bytesPerSample = lsmash_bs_get_be32( bs ); + box->manager |= LSMASH_QTFF_BASE; + } + else + /* AudioSampleEntryV1 has no additional fields. */ + box->manager &= ~LSMASH_QTFF_BASE; + } + else if( audio->version == 2 ) + { + audio->sizeOfStructOnly = lsmash_bs_get_be32( bs ); + audio->audioSampleRate = lsmash_bs_get_be64( bs ); + audio->numAudioChannels = lsmash_bs_get_be32( bs ); + audio->always7F000000 = lsmash_bs_get_be32( bs ); + audio->constBitsPerChannel = lsmash_bs_get_be32( bs ); + audio->formatSpecificFlags = lsmash_bs_get_be32( bs ); + audio->constBytesPerAudioPacket = lsmash_bs_get_be32( bs ); + audio->constLPCMFramesPerAudioPacket = lsmash_bs_get_be32( bs ); + box->manager |= LSMASH_QTFF_BASE; + } + box->parent = parent; + box->manager |= LSMASH_AUDIO_DESCRIPTION; + isom_box_common_copy( audio, box ); + int ret = isom_add_print_func( file, audio, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, audio, level ); +} + +static int isom_read_wave( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( wave, isom_audio_entry_t ); + isom_box_common_copy( wave, box ); + int ret = isom_add_print_func( file, wave, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, wave, level ); +} + +static int isom_read_frma( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) || ((isom_wave_t *)parent)->frma ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( frma, isom_wave_t ); + lsmash_bs_t *bs = file->bs; + frma->data_format = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, frma ); +} + +static int isom_read_enda( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) || ((isom_wave_t *)parent)->enda ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( enda, isom_wave_t ); + lsmash_bs_t *bs = file->bs; + enda->littleEndian = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, enda ); +} + +static int isom_read_terminator( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) || ((isom_wave_t *)parent)->terminator ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( terminator, isom_wave_t ); + return isom_read_leaf_box_common_last_process( file, box, level, terminator ); +} + +static int isom_read_chan( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( chan, isom_audio_entry_t ); + lsmash_bs_t *bs = file->bs; + chan->channelLayoutTag = lsmash_bs_get_be32( bs ); + chan->channelBitmap = lsmash_bs_get_be32( bs ); + chan->numberChannelDescriptions = lsmash_bs_get_be32( bs ); + if( chan->numberChannelDescriptions ) + { + isom_channel_description_t *desc = lsmash_malloc( chan->numberChannelDescriptions * sizeof(isom_channel_description_t) ); + if( !desc ) + return LSMASH_ERR_MEMORY_ALLOC; + chan->channelDescriptions = desc; + for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) + { + desc->channelLabel = lsmash_bs_get_be32( bs ); + desc->channelFlags = lsmash_bs_get_be32( bs ); + for( int j = 0; j < 3; j++ ) + desc->coordinates[j] = lsmash_bs_get_be32( bs ); + } + } + return isom_read_leaf_box_common_last_process( file, box, level, chan ); +} + +static int isom_read_srat( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + ADD_BOX( srat, isom_audio_entry_t ); + lsmash_bs_t *bs = file->bs; + srat->sampling_rate = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, srat ); +} + +static int isom_read_qt_text_description( 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_STSD ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); + if( !text ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = file->bs; + for( int i = 0; i < 6; i++ ) + text->reserved[i] = lsmash_bs_get_byte( bs ); + text->data_reference_index = lsmash_bs_get_be16( bs ); + text->displayFlags = lsmash_bs_get_be32( bs ); + text->textJustification = lsmash_bs_get_be32( bs ); + for( int i = 0; i < 3; i++ ) + text->bgColor[i] = lsmash_bs_get_be16( bs ); + text->top = lsmash_bs_get_be16( bs ); + text->left = lsmash_bs_get_be16( bs ); + text->bottom = lsmash_bs_get_be16( bs ); + text->right = lsmash_bs_get_be16( bs ); + text->scrpStartChar = lsmash_bs_get_be32( bs ); + text->scrpHeight = lsmash_bs_get_be16( bs ); + text->scrpAscent = lsmash_bs_get_be16( bs ); + text->scrpFont = lsmash_bs_get_be16( bs ); + text->scrpFace = lsmash_bs_get_be16( bs ); + text->scrpSize = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 3; i++ ) + text->scrpColor[i] = lsmash_bs_get_be16( bs ); + text->font_name_length = lsmash_bs_get_byte( bs ); + if( text->font_name_length ) + { + text->font_name = lsmash_malloc( text->font_name_length + 1 ); + if( !text->font_name ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint8_t i = 0; i < text->font_name_length; i++ ) + text->font_name[i] = lsmash_bs_get_byte( bs ); + text->font_name[text->font_name_length] = '\0'; + } + box->parent = parent; + isom_box_common_copy( text, box ); + int ret = isom_add_print_func( file, text, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, text, level ); +} + +static int isom_read_tx3g_description( 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_STSD ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); + if( !tx3g ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = file->bs; + for( int i = 0; i < 6; i++ ) + tx3g->reserved[i] = lsmash_bs_get_byte( bs ); + tx3g->data_reference_index = lsmash_bs_get_be16( bs ); + tx3g->displayFlags = lsmash_bs_get_be32( bs ); + tx3g->horizontal_justification = lsmash_bs_get_byte( bs ); + tx3g->vertical_justification = lsmash_bs_get_byte( bs ); + for( int i = 0; i < 4; i++ ) + tx3g->background_color_rgba[i] = lsmash_bs_get_byte( bs ); + tx3g->top = lsmash_bs_get_be16( bs ); + tx3g->left = lsmash_bs_get_be16( bs ); + tx3g->bottom = lsmash_bs_get_be16( bs ); + tx3g->right = lsmash_bs_get_be16( bs ); + tx3g->startChar = lsmash_bs_get_be16( bs ); + tx3g->endChar = lsmash_bs_get_be16( bs ); + tx3g->font_ID = lsmash_bs_get_be16( bs ); + tx3g->face_style_flags = lsmash_bs_get_byte( bs ); + tx3g->font_size = lsmash_bs_get_byte( bs ); + for( int i = 0; i < 4; i++ ) + tx3g->text_color_rgba[i] = lsmash_bs_get_byte( bs ); + box->parent = parent; + isom_box_common_copy( tx3g, box ); + int ret = isom_add_print_func( file, tx3g, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, tx3g, level ); +} + +static int isom_read_ftab( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, ISOM_CODEC_TYPE_TX3G_TEXT ) + || ((isom_tx3g_entry_t *)parent)->ftab ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( ftab, isom_tx3g_entry_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be16( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && ftab->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_font_record_t *data = lsmash_malloc_zero( sizeof(isom_font_record_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( ftab->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->font_ID = lsmash_bs_get_be16( bs ); + data->font_name_length = lsmash_bs_get_byte( bs ); + if( data->font_name_length ) + { + data->font_name = lsmash_malloc( data->font_name_length + 1 ); + if( !data->font_name ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint8_t i = 0; i < data->font_name_length; i++ ) + data->font_name[i] = lsmash_bs_get_byte( bs ); + data->font_name[data->font_name_length] = '\0'; + } + } + return isom_read_leaf_box_common_last_process( file, box, level, ftab ); +} + +static int isom_read_mp4s_description( 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_STSD ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); + if( !mp4s ) + return LSMASH_ERR_MEMORY_ALLOC; + lsmash_bs_t *bs = file->bs; + for( int i = 0; i < 6; i++ ) + mp4s->reserved[i] = lsmash_bs_get_byte( bs ); + mp4s->data_reference_index = lsmash_bs_get_be16( bs ); + box->parent = parent; + isom_box_common_copy( mp4s, box ); + int ret = isom_add_print_func( file, mp4s, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, mp4s, level ); +} + +static int isom_read_stts( 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)->stts ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stts, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && stts->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stts->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->sample_count = lsmash_bs_get_be32( bs ); + data->sample_delta = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, stts ); +} + +static int isom_read_ctts( 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)->ctts ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( ctts, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && ctts->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( ctts->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->sample_count = lsmash_bs_get_be32( bs ); + data->sample_offset = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, ctts ); +} + +static int isom_read_cslg( 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)->cslg ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( cslg, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + cslg->compositionToDTSShift = lsmash_bs_get_be32( bs ); + cslg->leastDecodeToDisplayDelta = lsmash_bs_get_be32( bs ); + cslg->greatestDecodeToDisplayDelta = lsmash_bs_get_be32( bs ); + cslg->compositionStartTime = lsmash_bs_get_be32( bs ); + cslg->compositionEndTime = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, cslg ); +} + +static int isom_read_stss( 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)->stss ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stss, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && stss->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stss->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->sample_number = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, stss ); +} + +static int isom_read_stps( 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)->stps ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stps, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && stps->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stps->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->sample_number = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, stps ); +} + +static int isom_read_sdtp( 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 ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF )) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) && ((isom_stbl_t *)parent)->sdtp) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) && ((isom_traf_t *)parent)->sdtp)) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( sdtp, isom_box_t ); + lsmash_bs_t *bs = file->bs; + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size; pos = lsmash_bs_count( bs ) ) + { + isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( sdtp->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + uint8_t temp = lsmash_bs_get_byte( bs ); + data->is_leading = (temp >> 6) & 0x3; + data->sample_depends_on = (temp >> 4) & 0x3; + data->sample_is_depended_on = (temp >> 2) & 0x3; + data->sample_has_redundancy = temp & 0x3; + } + return isom_read_leaf_box_common_last_process( file, box, level, sdtp ); +} + +static int isom_read_stsc( 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)->stsc ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stsc, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && stsc->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stsc->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->first_chunk = lsmash_bs_get_be32( bs ); + data->samples_per_chunk = lsmash_bs_get_be32( bs ); + data->sample_description_index = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level, stsc ); +} + +static int isom_read_stsz( 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)->stsz ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stsz, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + stsz->sample_size = lsmash_bs_get_be32( bs ); + stsz->sample_count = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_count( bs ); + if( pos < box->size ) + { + stsz->list = lsmash_create_entry_list(); + if( !stsz->list ) + return LSMASH_ERR_MEMORY_ALLOC; + for( ; pos < box->size && stsz->list->entry_count < stsz->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( stsz->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->entry_size = lsmash_bs_get_be32( bs ); + } + } + return isom_read_leaf_box_common_last_process( file, box, level, stsz ); +} + +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 ) + return isom_read_unknown_box( file, box, parent, level ); + box->type = lsmash_form_iso_box_type( box->type.fourcc ); + int is_stco = lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_STCO ); + isom_stco_t *stco = is_stco + ? isom_add_stco( (isom_stbl_t *)parent ) + : isom_add_co64( (isom_stbl_t *)parent ); + if( !stco ) + return LSMASH_ERR_NAMELESS; + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + if( is_stco ) + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && stco->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stco->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->chunk_offset = lsmash_bs_get_be32( bs ); + } + else + { + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && stco->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stco->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->chunk_offset = lsmash_bs_get_be64( bs ); + } + } + return isom_read_leaf_box_common_last_process( file, box, level, stco ); +} + +static int isom_read_sgpd( 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 ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( sgpd, void ); + lsmash_bs_t *bs = file->bs; + sgpd->grouping_type = lsmash_bs_get_be32( bs ); + if( box->version == 1 ) + sgpd->default_length = lsmash_bs_get_be32( bs ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + switch( sgpd->grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + { + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && sgpd->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( sgpd->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + memset( data, 0, sizeof(isom_rap_entry_t) ); + /* We don't know groups decided by variable description length. If encountering, skip getting of bytes of it. */ + if( box->version == 1 && !sgpd->default_length ) + data->description_length = lsmash_bs_get_be32( bs ); + else + { + uint8_t temp = lsmash_bs_get_byte( bs ); + data->num_leading_samples_known = (temp >> 7) & 0x01; + data->num_leading_samples = temp & 0x7f; + } + } + break; + } + case ISOM_GROUP_TYPE_ROLL : + case ISOM_GROUP_TYPE_PROL : + { + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && sgpd->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_roll_entry_t *data = lsmash_malloc( sizeof(isom_roll_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( sgpd->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + memset( data, 0, sizeof(isom_roll_entry_t) ); + /* We don't know groups decided by variable description length. If encountering, skip getting of bytes of it. */ + if( box->version == 1 && !sgpd->default_length ) + data->description_length = lsmash_bs_get_be32( bs ); + else + data->roll_distance = lsmash_bs_get_be16( bs ); + } + break; + } + default : + break; + } + return isom_read_leaf_box_common_last_process( file, box, level, sgpd ); +} + +static int isom_read_sbgp( 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 ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( sbgp, void ); + lsmash_bs_t *bs = file->bs; + sbgp->grouping_type = lsmash_bs_get_be32( bs ); + if( box->version == 1 ) + sbgp->grouping_type_parameter = lsmash_bs_get_be32( bs ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && sbgp->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_group_assignment_entry_t *data = lsmash_malloc( sizeof(isom_group_assignment_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( sbgp->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->sample_count = lsmash_bs_get_be32( bs ); + data->group_description_index = lsmash_bs_get_be32( bs ); + } + return isom_read_leaf_box_common_last_process( file, box, level,sbgp ); +} + +static int isom_read_udta( 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_MOOV ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK )) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) && ((isom_moov_t *)parent)->udta) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) && ((isom_trak_t *)parent)->udta) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( udta, void ); + isom_box_common_copy( udta, box ); + int ret = isom_add_print_func( file, udta, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, udta, level ); +} + +static int isom_read_chpl( 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_UDTA ) || ((isom_udta_t *)parent)->chpl ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( chpl, isom_udta_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count; + if( box->version == 1 ) + { + chpl->unknown = lsmash_bs_get_byte( bs ); + entry_count = lsmash_bs_get_be32( bs ); + } + else + entry_count = lsmash_bs_get_byte( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && chpl->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_chpl_entry_t *data = lsmash_malloc( sizeof(isom_chpl_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( chpl->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->start_time = lsmash_bs_get_be64( bs ); + data->chapter_name_length = lsmash_bs_get_byte( bs ); + data->chapter_name = lsmash_malloc( data->chapter_name_length + 1 ); + if( !data->chapter_name ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + for( uint8_t i = 0; i < data->chapter_name_length; i++ ) + data->chapter_name[i] = lsmash_bs_get_byte( bs ); + data->chapter_name[data->chapter_name_length] = '\0'; + } + return isom_read_leaf_box_common_last_process( file, box, level, chpl ); +} + +static int isom_read_mvex( 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_MOOV ) || ((isom_moov_t *)parent)->mvex ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mvex, isom_moov_t ); + file->flags |= LSMASH_FILE_MODE_FRAGMENTED; + isom_box_common_copy( mvex, box ); + int ret = isom_add_print_func( file, mvex, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, mvex, level ); +} + +static int isom_read_mehd( 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_MVEX ) || ((isom_mvex_t *)parent)->mehd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mehd, isom_mvex_t ); + lsmash_bs_t *bs = file->bs; + if( box->version == 1 ) + mehd->fragment_duration = lsmash_bs_get_be64( bs ); + else + mehd->fragment_duration = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, mehd ); +} + +static isom_sample_flags_t isom_bs_get_sample_flags( lsmash_bs_t *bs ) +{ + uint32_t temp = lsmash_bs_get_be32( bs ); + isom_sample_flags_t flags; + flags.reserved = (temp >> 28) & 0xf; + flags.is_leading = (temp >> 26) & 0x3; + flags.sample_depends_on = (temp >> 24) & 0x3; + flags.sample_is_depended_on = (temp >> 22) & 0x3; + flags.sample_has_redundancy = (temp >> 20) & 0x3; + flags.sample_padding_value = (temp >> 17) & 0x7; + flags.sample_is_non_sync_sample = (temp >> 16) & 0x1; + flags.sample_degradation_priority = temp & 0xffff; + return flags; +} + +static int isom_read_trex( 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_MVEX ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( trex, isom_mvex_t ); + box->parent = parent; + lsmash_bs_t *bs = file->bs; + trex->track_ID = lsmash_bs_get_be32( bs ); + trex->default_sample_description_index = lsmash_bs_get_be32( bs ); + trex->default_sample_duration = lsmash_bs_get_be32( bs ); + trex->default_sample_size = lsmash_bs_get_be32( bs ); + trex->default_sample_flags = isom_bs_get_sample_flags( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, trex ); +} + +static int isom_read_moof( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( moof, lsmash_file_t ); + box->parent = parent; + isom_box_common_copy( moof, box ); + int ret = isom_add_print_func( file, moof, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, moof, level ); +} + +static int isom_read_mfhd( 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_MOOF ) || ((isom_moof_t *)parent)->mfhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mfhd, isom_moof_t ); + lsmash_bs_t *bs = file->bs; + mfhd->sequence_number = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, mfhd ); +} + +static int isom_read_traf( 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_MOOF ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( traf, isom_moof_t ); + box->parent = parent; + isom_box_common_copy( traf, box ); + int ret = isom_add_print_func( file, traf, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, traf, level ); +} + +static int isom_read_tfhd( 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_TRAF ) || ((isom_traf_t *)parent)->tfhd ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( tfhd, isom_traf_t ); + lsmash_bs_t *bs = file->bs; + tfhd->track_ID = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) tfhd->base_data_offset = lsmash_bs_get_be64( bs ); + if( box->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) tfhd->sample_description_index = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) tfhd->default_sample_duration = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) tfhd->default_sample_size = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) tfhd->default_sample_flags = isom_bs_get_sample_flags( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, tfhd ); +} + +static int isom_read_tfdt( 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_TRAF ) || ((isom_traf_t *)parent)->tfdt ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( tfdt, isom_traf_t ); + lsmash_bs_t *bs = file->bs; + if( box->version == 1 ) + tfdt->baseMediaDecodeTime = lsmash_bs_get_be64( bs ); + else + tfdt->baseMediaDecodeTime = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, tfdt ); +} + +static int isom_read_trun( 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_TRAF ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( trun, isom_traf_t ); + box->parent = parent; + lsmash_bs_t *bs = file->bs; + int has_optional_rows = ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT + | ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT + | ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT + | ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT; + has_optional_rows &= box->flags; + trun->sample_count = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) trun->data_offset = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) trun->first_sample_flags = isom_bs_get_sample_flags( bs ); + if( trun->sample_count && has_optional_rows ) + { + trun->optional = lsmash_create_entry_list(); + if( !trun->optional ) + return LSMASH_ERR_MEMORY_ALLOC; + for( uint32_t i = 0; i < trun->sample_count; i++ ) + { + isom_trun_optional_row_t *data = lsmash_malloc( sizeof(isom_trun_optional_row_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( trun->optional, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + if( box->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) data->sample_duration = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) data->sample_size = lsmash_bs_get_be32( bs ); + if( box->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) data->sample_flags = isom_bs_get_sample_flags( bs ); + if( box->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) data->sample_composition_time_offset = lsmash_bs_get_be32( bs ); + } + } + return isom_read_leaf_box_common_last_process( file, box, level, trun ); +} + +static int isom_read_free( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( file->fake_file_mode ) + return isom_read_unknown_box( file, box, parent, level ); + isom_box_t *skip = lsmash_malloc_zero( sizeof(isom_box_t) ); + if( !skip ) + return LSMASH_ERR_MEMORY_ALLOC; + isom_skip_box_rest( file->bs, box ); + box->manager |= LSMASH_ABSENT_IN_FILE; + isom_box_common_copy( skip, box ); + int ret = isom_add_print_func( file, skip, level ); + if( ret < 0 ) + { + lsmash_free( skip ); + return ret; + } + return 0; +} + +static int isom_read_mdat( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( file->fake_file_mode || !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_box_t *mdat = lsmash_malloc_zero( sizeof(isom_box_t) ); + if( !mdat ) + return LSMASH_ERR_MEMORY_ALLOC; + isom_skip_box_rest( file->bs, box ); + box->manager |= LSMASH_ABSENT_IN_FILE; + file->flags |= LSMASH_FILE_MODE_MEDIA; + isom_box_common_copy( mdat, box ); + int ret = isom_add_print_func( file, mdat, level ); + if( ret < 0 ) + { + lsmash_free( mdat ); + return ret; + } + return 0; +} + +static int isom_read_meta( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( (!lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) + && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_UDTA )) + || (lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) && ((lsmash_file_t *)parent)->meta) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) && ((isom_moov_t *)parent)->meta) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) && ((isom_trak_t *)parent)->meta) + || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_UDTA ) && ((isom_udta_t *)parent)->meta) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( meta, void ); + isom_box_common_copy( meta, box ); + int is_qtff = lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_META ); + if( is_qtff ) + { + box->manager |= LSMASH_QTFF_BASE; + meta->manager |= LSMASH_QTFF_BASE; + } + int ret = isom_add_print_func( file, meta, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, meta, level ); +} + +static int isom_read_keys( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( (!lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) && !(parent->manager & LSMASH_QTFF_BASE)) + || ((isom_meta_t *)parent)->keys ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( keys, isom_meta_t ); + lsmash_bs_t *bs = file->bs; + uint32_t entry_count = lsmash_bs_get_be32( bs ); + for( uint64_t pos = lsmash_bs_count( bs ); pos < box->size && keys->list->entry_count < entry_count; pos = lsmash_bs_count( bs ) ) + { + isom_keys_entry_t *data = lsmash_malloc( sizeof(isom_keys_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( keys->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->key_size = lsmash_bs_get_be32( bs ); + data->key_namespace = lsmash_bs_get_be32( bs ); + if( data->key_size > 8 ) + { + data->key_value = lsmash_bs_get_bytes( bs, data->key_size - 8 ); + if( !data->key_value ) + return LSMASH_ERR_NAMELESS; + } + else + data->key_value = NULL; + } + return isom_read_leaf_box_common_last_process( file, box, level, keys ); +} + +static int isom_read_ilst( 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_META ) + && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META )) + || ((isom_meta_t *)parent)->ilst ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( ilst, isom_meta_t ); + isom_box_common_copy( ilst, box ); + int ret = isom_add_print_func( file, ilst, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, ilst, level ); +} + +static int isom_read_metaitem( 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_ILST ) + && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) + return isom_read_unknown_box( file, box, parent, level ); + isom_metaitem_t *metaitem = isom_add_metaitem( (isom_ilst_t *)parent, box->type.fourcc ); + if( !metaitem ) + return -1; + box->parent = parent; + isom_box_common_copy( metaitem, box ); + int ret = isom_add_print_func( file, metaitem, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, metaitem, level ); +} + +static int isom_read_mean( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type.fourcc != ITUNES_METADATA_ITEM_CUSTOM || ((isom_metaitem_t *)parent)->mean ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mean, isom_metaitem_t ); + lsmash_bs_t *bs = file->bs; + mean->meaning_string_length = box->size - lsmash_bs_count( bs ); + mean->meaning_string = lsmash_bs_get_bytes( bs, mean->meaning_string_length ); + if( !mean->meaning_string ) + return LSMASH_ERR_NAMELESS; + return isom_read_leaf_box_common_last_process( file, box, level, mean ); +} + +static int isom_read_name( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type.fourcc != ITUNES_METADATA_ITEM_CUSTOM || ((isom_metaitem_t *)parent)->name ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( name, isom_metaitem_t ); + lsmash_bs_t *bs = file->bs; + name->name_length = box->size - lsmash_bs_count( bs ); + name->name = lsmash_bs_get_bytes( bs, name->name_length ); + if( !name->name ) + return LSMASH_ERR_NAMELESS; + return isom_read_leaf_box_common_last_process( file, box, level, name ); +} + +static int isom_read_data( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( ((isom_metaitem_t *)parent)->data ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( data, isom_metaitem_t ); + lsmash_bs_t *bs = file->bs; + data->value_length = box->size - lsmash_bs_count( bs ) - 8; + data->reserved = lsmash_bs_get_be16( bs ); + data->type_set_identifier = lsmash_bs_get_byte( bs ); + data->type_code = lsmash_bs_get_byte( bs ); + data->the_locale = lsmash_bs_get_be32( bs ); + if( data->value_length ) + { + data->value = lsmash_bs_get_bytes( bs, data->value_length ); + if( !data->value ) + return LSMASH_ERR_NAMELESS; + } + return isom_read_leaf_box_common_last_process( file, box, level, data ); +} + +static int isom_read_WLOC( 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_UDTA ) || ((isom_udta_t *)parent)->WLOC ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( WLOC, isom_udta_t ); + lsmash_bs_t *bs = file->bs; + WLOC->x = lsmash_bs_get_be16( bs ); + WLOC->y = lsmash_bs_get_be16( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, WLOC ); +} + +static int isom_read_LOOP( 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_UDTA ) || ((isom_udta_t *)parent)->LOOP ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( LOOP, isom_udta_t ); + lsmash_bs_t *bs = file->bs; + LOOP->looping_mode = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, LOOP ); +} + +static int isom_read_SelO( 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_UDTA ) || ((isom_udta_t *)parent)->SelO ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( SelO, isom_udta_t ); + lsmash_bs_t *bs = file->bs; + SelO->selection_only = lsmash_bs_get_byte( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, SelO ); +} + +static int isom_read_AllF( 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_UDTA ) || ((isom_udta_t *)parent)->AllF ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( AllF, isom_udta_t ); + lsmash_bs_t *bs = file->bs; + AllF->play_all_frames = lsmash_bs_get_byte( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, AllF ); +} + +static int isom_read_cprt( 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_UDTA ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( cprt, isom_udta_t ); + box->parent = parent; + lsmash_bs_t *bs = file->bs; + cprt->language = lsmash_bs_get_be16( bs ); + cprt->notice_length = box->size - (ISOM_FULLBOX_COMMON_SIZE + 2); + if( cprt->notice_length ) + { + cprt->notice = lsmash_bs_get_bytes( bs, cprt->notice_length ); + if( !cprt->notice ) + { + cprt->notice_length = 0; + return LSMASH_ERR_NAMELESS; + } + } + return isom_read_leaf_box_common_last_process( file, box, level, cprt ); +} + +static int isom_read_mfra( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) || ((lsmash_file_t *)parent)->mfra ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mfra, lsmash_file_t ); + isom_box_common_copy( mfra, box ); + int ret = isom_add_print_func( file, mfra, level ); + if( ret < 0 ) + return ret; + return isom_read_children( file, box, mfra, level ); +} + +static int isom_read_tfra( 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_MFRA ) ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( tfra, isom_mfra_t ); + box->parent = parent; + lsmash_bs_t *bs = file->bs; + tfra->track_ID = lsmash_bs_get_be32( bs ); + uint32_t temp = lsmash_bs_get_be32( bs ); + tfra->number_of_entry = lsmash_bs_get_be32( bs ); + tfra->reserved = (temp >> 6) & 0x3ffffff; + tfra->length_size_of_traf_num = (temp >> 4) & 0x3; + tfra->length_size_of_trun_num = (temp >> 2) & 0x3; + tfra->length_size_of_sample_num = temp & 0x3; + if( tfra->number_of_entry ) + { + tfra->list = lsmash_create_entry_list(); + if( !tfra->list ) + return LSMASH_ERR_MEMORY_ALLOC; + uint64_t (*bs_get_funcs[5])( lsmash_bs_t * ) = + { + lsmash_bs_get_byte_to_64, + lsmash_bs_get_be16_to_64, + lsmash_bs_get_be24_to_64, + lsmash_bs_get_be32_to_64, + lsmash_bs_get_be64 + }; + uint64_t (*bs_put_time) ( lsmash_bs_t * ) = bs_get_funcs[ 3 + (box->version == 1) ]; + uint64_t (*bs_put_moof_offset) ( lsmash_bs_t * ) = bs_get_funcs[ 3 + (box->version == 1) ]; + uint64_t (*bs_put_traf_number) ( lsmash_bs_t * ) = bs_get_funcs[ tfra->length_size_of_traf_num ]; + uint64_t (*bs_put_trun_number) ( lsmash_bs_t * ) = bs_get_funcs[ tfra->length_size_of_trun_num ]; + uint64_t (*bs_put_sample_number)( lsmash_bs_t * ) = bs_get_funcs[ tfra->length_size_of_sample_num ]; + for( uint32_t i = 0; i < tfra->number_of_entry; i++ ) + { + isom_tfra_location_time_entry_t *data = lsmash_malloc( sizeof(isom_tfra_location_time_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( tfra->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->time = bs_put_time ( bs ); + data->moof_offset = bs_put_moof_offset ( bs ); + data->traf_number = bs_put_traf_number ( bs ); + data->trun_number = bs_put_trun_number ( bs ); + data->sample_number = bs_put_sample_number( bs ); + } + } + return isom_read_leaf_box_common_last_process( file, box, level, tfra ); +} + +static int isom_read_mfro( 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_MFRA ) || ((isom_mfra_t *)parent)->mfro ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( mfro, isom_mfra_t ); + lsmash_bs_t *bs = file->bs; + mfro->length = lsmash_bs_get_be32( bs ); + return isom_read_leaf_box_common_last_process( file, box, level, mfro ); +} + +static inline void isom_read_skip_extra_bytes( lsmash_bs_t *bs, uint64_t size ) +{ + if( !bs->unseekable ) + lsmash_bs_read_seek( bs, size, SEEK_CUR ); + else + lsmash_bs_skip_bytes_64( bs, size ); +} + +static int isom_read_skip_box_extra_bytes( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos ) +{ + lsmash_bs_t *bs = file->bs; + /* Skip extra bytes of the parent box if any. */ + if( parent->size < parent_pos + ISOM_BASEBOX_COMMON_SIZE ) + { + uint64_t extra_bytes = parent->size - parent_pos; + isom_read_skip_extra_bytes( bs, extra_bytes ); + /* This is not the size of a box but makes sense in isom_read_children(). */ + box->size = extra_bytes; + return 1; + } + /* Check if the size is valid or not. */ + if( lsmash_bs_is_end( bs, 3 ) == 0 ) + { + uint64_t size = (uint64_t)lsmash_bs_show_be32( bs, 0 ); + if( size > 1 + && size < ISOM_BASEBOX_COMMON_SIZE ) + { + /* It seems we are still within the box considered as previous. + * Skip bytes up to the next box. */ + isom_read_skip_extra_bytes( bs, parent->size - parent_pos ); + box->size = 0; + return 1; + } + if( size == 1 && lsmash_bs_is_end( bs, 15 ) == 0 ) + size = lsmash_bs_show_be64( bs, 8 ); + if( size == 0 && parent != (isom_box_t *)file ) + { + /* Check if this box is actually the last box or not. */ + size = parent->size - parent_pos; + uint64_t extra_bytes; + if( !bs->unseekable ) + extra_bytes = bs->written - lsmash_bs_get_stream_pos( bs ); + else + { + extra_bytes = lsmash_bs_get_remaining_buffer_size( bs ); + while( size < extra_bytes ) + { + int ret = lsmash_bs_read( bs, 1 ); + if( bs->eof || ret < 0 ) + break; + extra_bytes = lsmash_bs_get_remaining_buffer_size( bs ); + } + } + if( size != extra_bytes ) + { + /* This is not the size of the last box. + * It seems we are still within the box considered as previous. + * Skip bytes up to the next box. */ + isom_read_skip_extra_bytes( bs, box->size - lsmash_bs_count( bs ) ); + box->size = 0; + return 1; + } + } + } + return 0; +} + +int isom_read_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos, int level ) +{ + assert( parent && parent->root && parent->file ); + if( isom_read_skip_box_extra_bytes( file, box, parent, parent_pos ) != 0 ) + return 0; + memset( box, 0, sizeof(isom_box_t) ); + box->root = parent->root; + box->file = parent->file; + box->parent = parent; + lsmash_bs_t *bs = file->bs; + int ret = isom_bs_read_box_common( bs, box ); + if( !!ret ) + return ret; /* return if reached EOF */ + ++level; + lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ) = NULL; + int (*reader_func)( lsmash_file_t *, isom_box_t *, isom_box_t *, int ) = NULL; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) + { + /* Check whether CODEC is RAW Video/Audio encapsulated in QTFF. */ + if( box->type.fourcc == LSMASH_CODEC_TYPE_RAW.fourcc ) + { + if( ((isom_minf_t *)parent->parent->parent)->vmhd ) + { + form_box_type_func = lsmash_form_qtff_box_type; + reader_func = isom_read_visual_description; + } + else if( ((isom_minf_t *)parent->parent->parent)->smhd ) + { + form_box_type_func = lsmash_form_qtff_box_type; + reader_func = isom_read_audio_description; + } + goto read_box; + } + static struct description_reader_table_tag + { + lsmash_compact_box_type_t fourcc; + lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ); + int (*reader_func)( lsmash_file_t *, isom_box_t *, isom_box_t *, int ); + } description_reader_table[160] = { { 0, NULL, NULL } }; + if( !description_reader_table[0].reader_func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_DESCRIPTION_READER_TABLE_ELEMENT( type, form_box_type_func, reader_func ) \ + description_reader_table[i++] = (struct description_reader_table_tag){ type.fourcc, form_box_type_func, reader_func } + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + //ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + //ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_QDM2_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_QDMC_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CDX2_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CDX4_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCA_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVI_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_IMA4_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_VDVA_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MP3_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, lsmash_form_qtff_box_type, isom_read_audio_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, lsmash_form_qtff_box_type, isom_read_qt_text_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, lsmash_form_iso_box_type, isom_read_tx3g_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, lsmash_form_iso_box_type, isom_read_mp4s_description ); + ADD_DESCRIPTION_READER_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL, NULL ); +#undef ADD_DESCRIPTION_READER_TABLE_ELEMENT + } + for( int i = 0; description_reader_table[i].reader_func; i++ ) + if( box->type.fourcc == description_reader_table[i].fourcc ) + { + form_box_type_func = description_reader_table[i].form_box_type_func; + reader_func = description_reader_table[i].reader_func; + goto read_box; + } + goto read_box; + } + if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) + { + form_box_type_func = lsmash_form_qtff_box_type; + if( box->type.fourcc == QT_BOX_TYPE_FRMA.fourcc ) reader_func = isom_read_frma; + else if( box->type.fourcc == QT_BOX_TYPE_ENDA.fourcc ) reader_func = isom_read_enda; + else if( box->type.fourcc == QT_BOX_TYPE_ESDS.fourcc ) reader_func = isom_read_esds; + else if( box->type.fourcc == QT_BOX_TYPE_CHAN.fourcc ) reader_func = isom_read_chan; + else if( box->type.fourcc == QT_BOX_TYPE_TERMINATOR.fourcc ) reader_func = isom_read_terminator; + else reader_func = isom_read_codec_specific; + goto read_box; + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TREF ) ) + { + form_box_type_func = lsmash_form_iso_box_type; + reader_func = isom_read_track_reference_type; + goto read_box; + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_DREF ) ) + { + if( box->type.fourcc == ISOM_BOX_TYPE_URL.fourcc + || box->type.fourcc == ISOM_BOX_TYPE_URN.fourcc ) + form_box_type_func = lsmash_form_iso_box_type; + else if( box->type.fourcc == QT_BOX_TYPE_ALIS.fourcc + || box->type.fourcc == QT_BOX_TYPE_RSRC.fourcc ) + form_box_type_func = lsmash_form_qtff_box_type; + reader_func = isom_read_dref_entry; + goto read_box; + } + static struct box_reader_table_tag + { + lsmash_compact_box_type_t fourcc; + lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ); + int (*reader_func)( lsmash_file_t *, isom_box_t *, isom_box_t *, int ); + } box_reader_table[128] = { { 0, NULL, NULL } }; + if( !box_reader_table[0].reader_func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_BOX_READER_TABLE_ELEMENT( type, form_box_type_func, reader_func ) \ + box_reader_table[i++] = (struct box_reader_table_tag){ type.fourcc, form_box_type_func, reader_func } + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTYP, lsmash_form_iso_box_type, isom_read_ftyp ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STYP, lsmash_form_iso_box_type, isom_read_styp ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SIDX, lsmash_form_iso_box_type, isom_read_sidx ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOV, lsmash_form_iso_box_type, isom_read_moov ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVHD, lsmash_form_iso_box_type, isom_read_mvhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_IODS, lsmash_form_iso_box_type, isom_read_iods ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, lsmash_form_qtff_box_type, isom_read_ctab ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, lsmash_form_iso_box_type, isom_read_esds ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAK, lsmash_form_iso_box_type, isom_read_trak ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TKHD, lsmash_form_iso_box_type, isom_read_tkhd ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_TAPT, lsmash_form_qtff_box_type, isom_read_tapt ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CLEF, lsmash_form_qtff_box_type, isom_read_clef ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_PROF, lsmash_form_qtff_box_type, isom_read_prof ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_ENOF, lsmash_form_qtff_box_type, isom_read_enof ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_EDTS, lsmash_form_iso_box_type, isom_read_edts ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_ELST, lsmash_form_iso_box_type, isom_read_elst ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREF, lsmash_form_iso_box_type, isom_read_tref ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDIA, lsmash_form_iso_box_type, isom_read_mdia ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDHD, lsmash_form_iso_box_type, isom_read_mdhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_HDLR, lsmash_form_iso_box_type, isom_read_hdlr ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MINF, lsmash_form_iso_box_type, isom_read_minf ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_VMHD, lsmash_form_iso_box_type, isom_read_vmhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SMHD, lsmash_form_iso_box_type, isom_read_smhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_HMHD, lsmash_form_iso_box_type, isom_read_hmhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_NMHD, lsmash_form_iso_box_type, isom_read_nmhd ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GMHD, lsmash_form_qtff_box_type, isom_read_gmhd ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GMIN, lsmash_form_qtff_box_type, isom_read_gmin ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_TEXT, lsmash_form_qtff_box_type, isom_read_text ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_DINF, lsmash_form_iso_box_type, isom_read_dinf ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_DREF, lsmash_form_iso_box_type, isom_read_dref ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STBL, lsmash_form_iso_box_type, isom_read_stbl ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSD, lsmash_form_iso_box_type, isom_read_stsd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, lsmash_form_iso_box_type, isom_read_btrt ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, lsmash_form_iso_box_type, isom_read_colr ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, lsmash_form_iso_box_type, isom_read_clap ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, lsmash_form_iso_box_type, isom_read_pasp ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, lsmash_form_qtff_box_type, isom_read_glbl ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, lsmash_form_qtff_box_type, isom_read_gama ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, lsmash_form_qtff_box_type, isom_read_fiel ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, lsmash_form_qtff_box_type, isom_read_cspc ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, lsmash_form_qtff_box_type, isom_read_sgbt ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, lsmash_form_iso_box_type, isom_read_stsl ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, lsmash_form_qtff_box_type, isom_read_wave ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, lsmash_form_qtff_box_type, isom_read_chan ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SRAT, lsmash_form_iso_box_type, isom_read_srat ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, lsmash_form_iso_box_type, isom_read_ftab ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STTS, lsmash_form_iso_box_type, isom_read_stts ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CTTS, lsmash_form_iso_box_type, isom_read_ctts ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CSLG, lsmash_form_iso_box_type, isom_read_cslg ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSS, lsmash_form_iso_box_type, isom_read_stss ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_STPS, lsmash_form_qtff_box_type, isom_read_stps ); + 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_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 ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SBGP, lsmash_form_iso_box_type, isom_read_sbgp ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_UDTA, lsmash_form_iso_box_type, isom_read_udta ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CHPL, lsmash_form_iso_box_type, isom_read_chpl ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_WLOC, lsmash_form_qtff_box_type, isom_read_WLOC ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_LOOP, lsmash_form_qtff_box_type, isom_read_LOOP ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_SELO, lsmash_form_qtff_box_type, isom_read_SelO ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_ALLF, lsmash_form_qtff_box_type, isom_read_AllF ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVEX, lsmash_form_iso_box_type, isom_read_mvex ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MEHD, lsmash_form_iso_box_type, isom_read_mehd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREX, lsmash_form_iso_box_type, isom_read_trex ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOF, lsmash_form_iso_box_type, isom_read_moof ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFHD, lsmash_form_iso_box_type, isom_read_mfhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAF, lsmash_form_iso_box_type, isom_read_traf ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFHD, lsmash_form_iso_box_type, isom_read_tfhd ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFDT, lsmash_form_iso_box_type, isom_read_tfdt ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRUN, lsmash_form_iso_box_type, isom_read_trun ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_FREE, lsmash_form_iso_box_type, isom_read_free ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SKIP, lsmash_form_iso_box_type, isom_read_free ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDAT, lsmash_form_iso_box_type, isom_read_mdat ); + ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_KEYS, lsmash_form_qtff_box_type, isom_read_keys ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRA, lsmash_form_iso_box_type, isom_read_mfra ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFRA, lsmash_form_iso_box_type, isom_read_tfra ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRO, lsmash_form_iso_box_type, isom_read_mfro ); + ADD_BOX_READER_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL, NULL ); +#undef ADD_BOX_READER_TABLE_ELEMENT + } + for( int i = 0; box_reader_table[i].reader_func; i++ ) + if( box->type.fourcc == box_reader_table[i].fourcc ) + { + form_box_type_func = box_reader_table[i].form_box_type_func; + reader_func = box_reader_table[i].reader_func; + goto read_box; + } + if( box->type.fourcc == ISOM_BOX_TYPE_META.fourcc ) + { + if( lsmash_bs_is_end ( bs, 3 ) == 0 + && lsmash_bs_show_be32( bs, 0 ) == 0 ) + form_box_type_func = lsmash_form_iso_box_type; + else + form_box_type_func = lsmash_form_qtff_box_type; + reader_func = isom_read_meta; + goto read_box; + } + if( box->type.fourcc == ISOM_BOX_TYPE_ILST.fourcc ) + { + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ) + form_box_type_func = lsmash_form_iso_box_type; + else if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ) + form_box_type_func = lsmash_form_qtff_box_type; + if( form_box_type_func ) + { + reader_func = isom_read_ilst; + goto read_box; + } + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_ILST ) ) + form_box_type_func = lsmash_form_iso_box_type; + else if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) + form_box_type_func = lsmash_form_qtff_box_type; + if( form_box_type_func ) + { + reader_func = isom_read_metaitem; + goto read_box; + } + if( parent->parent && parent->parent->type.fourcc == ISOM_BOX_TYPE_ILST.fourcc ) + { + if( box->type.fourcc == ISOM_BOX_TYPE_MEAN.fourcc ) + reader_func = isom_read_mean; + else if( box->type.fourcc == ISOM_BOX_TYPE_NAME.fourcc ) + reader_func = isom_read_name; + else if( box->type.fourcc == ISOM_BOX_TYPE_DATA.fourcc ) + reader_func = isom_read_data; + if( reader_func ) + { + form_box_type_func = lsmash_form_iso_box_type; + goto read_box; + } + } + else if( box->type.fourcc == ISOM_BOX_TYPE_CPRT.fourcc ) + { + /* Avoid confusing udta.cprt with ilst.cprt. */ + form_box_type_func = lsmash_form_iso_box_type; + reader_func = isom_read_cprt; + goto read_box; + } + if( parent->parent && lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_STSD ) ) + { + static struct codec_specific_marker_table_tag + { + lsmash_compact_box_type_t fourcc; + lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ); + } codec_specific_marker_table[16] = { { 0, NULL } }; + if( !codec_specific_marker_table[0].form_box_type_func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( type, form_box_type_func ) \ + codec_specific_marker_table[i++] = (struct codec_specific_marker_table_tag){ type.fourcc, form_box_type_func } + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_ALAC, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_AVCC, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DAC3, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DAMR, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DDTS, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DEC3, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DVC1, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_HVCC, lsmash_form_iso_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, lsmash_form_qtff_box_type ); + ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); +#undef ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT + } + for( int i = 0; codec_specific_marker_table[i].form_box_type_func; i++ ) + if( box->type.fourcc == codec_specific_marker_table[i].fourcc ) + { + form_box_type_func = codec_specific_marker_table[i].form_box_type_func; + break; + } + reader_func = isom_read_codec_specific; + } +read_box: + if( form_box_type_func ) + box->type = form_box_type_func( box->type.fourcc ); + if( (ret = isom_read_fullbox_common_extension( bs, box )) < 0 ) + return ret; + return reader_func + ? reader_func( file, box, parent, level ) + : isom_read_unknown_box( file, box, parent, level ); +} + +int isom_read_file( lsmash_file_t *file ) +{ + lsmash_bs_t *bs = file->bs; + if( !bs ) + return LSMASH_ERR_NAMELESS; + /* Reset the counter so that we can use it to get position within the box. */ + lsmash_bs_reset_counter( bs ); + if( file->flags & LSMASH_FILE_MODE_DUMP ) + { + file->print = lsmash_create_entry_list(); + if( !file->print ) + return LSMASH_ERR_MEMORY_ALLOC; + } + file->size = UINT64_MAX; + isom_box_t box; + int ret = isom_read_children( file, &box, file, 0 ); + file->size = box.size; + lsmash_bs_empty( bs ); + bs->error = 0; /* Clear error flag. */ + if( ret < 0 ) + return ret; + return isom_check_compatibility( file ); +} diff -Nru l-smash-1.9.1/core/read.h l-smash-2.3.0/core/read.h --- l-smash-1.9.1/core/read.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/read.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,29 @@ +/***************************************************************************** + * read.h: + ***************************************************************************** + * Copyright (C) 2010-2014 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_READ_H +#define LSMASH_READ_H + +int isom_read_file( lsmash_file_t *file ); +int isom_read_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos, int level ); + +#endif /* LSMASH_READ_H */ diff -Nru l-smash-1.9.1/core/summary.c l-smash-2.3.0/core/summary.c --- l-smash-1.9.1/core/summary.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/summary.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,346 @@ +/***************************************************************************** + * summary.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include + +#include "box.h" + +#include "codecs/mp4a.h" +#include "codecs/mp4sys.h" +#include "codecs/description.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 LSMASH_ERR_FUNCTION_PARAM; + lsmash_bs_t* bs = lsmash_bs_create(); + if( !bs ) + return LSMASH_ERR_MEMORY_ALLOC; + mp4a_AudioSpecificConfig_t *asc = + mp4a_create_AudioSpecificConfig( summary->aot, + summary->frequency, + summary->channels, + summary->sbr_mode, + NULL,/*FIXME*/ + 0 /*FIXME*/); + if( !asc ) + { + lsmash_bs_cleanup( bs ); + return LSMASH_ERR_NAMELESS; + } + 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 LSMASH_ERR_NAMELESS; + lsmash_codec_specific_t *specific = lsmash_malloc_zero( sizeof(lsmash_codec_specific_t) ); + if( !specific ) + { + lsmash_free( new_asc ); + return LSMASH_ERR_MEMORY_ALLOC; + } + specific->type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; + specific->format = LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED; + specific->destruct = (lsmash_codec_specific_destructor_t)lsmash_free; + specific->size = new_length; + specific->data.unstructured = lsmash_memdup( new_asc, new_length ); + if( !specific->data.unstructured + || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) + { + lsmash_free( new_asc ); + lsmash_destroy_codec_specific_data( specific ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +lsmash_summary_t *lsmash_create_summary( lsmash_summary_type summary_type ) +{ + size_t summary_size; + switch( summary_type ) + { + case LSMASH_SUMMARY_TYPE_VIDEO : + summary_size = sizeof(lsmash_video_summary_t); + break; + case LSMASH_SUMMARY_TYPE_AUDIO : + summary_size = sizeof(lsmash_audio_summary_t); + break; + default : + summary_size = sizeof(lsmash_summary_t); + return NULL; + } + lsmash_summary_t *summary = (lsmash_summary_t *)lsmash_malloc_zero( summary_size ); + if( !summary ) + return NULL; + summary->opaque = (lsmash_codec_specific_list_t *)lsmash_malloc_zero( sizeof(lsmash_codec_specific_list_t) ); + if( !summary->opaque ) + { + lsmash_free( summary ); + return NULL; + } + summary->summary_type = summary_type; + summary->data_ref_index = 1; + return summary; +} + +void lsmash_cleanup_summary( lsmash_summary_t *summary ) +{ + if( !summary ) + return; + if( summary->opaque ) + { + for( lsmash_entry_t *entry = summary->opaque->list.head; entry; ) + { + lsmash_entry_t *next = entry->next; + lsmash_destroy_codec_specific_data( (lsmash_codec_specific_t *)entry->data ); + lsmash_free( entry ); + entry = next; + } + lsmash_free( summary->opaque ); + } + lsmash_free( summary ); +} + +int lsmash_add_codec_specific_data( lsmash_summary_t *summary, lsmash_codec_specific_t *specific ) +{ + if( !summary || !summary->opaque || !specific ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_codec_specific_t *dup = isom_duplicate_codec_specific_data( specific ); + if( !dup ) + return LSMASH_ERR_NAMELESS; + if( lsmash_add_entry( &summary->opaque->list, dup ) < 0 ) + { + lsmash_destroy_codec_specific_data( dup ); + return LSMASH_ERR_MEMORY_ALLOC; + } + return 0; +} + +uint32_t lsmash_count_summary( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 || track_ID == 0 ) + return 0; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->hdlr + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd ) + return 0; + return trak->mdia->minf->stbl->stsd->list.entry_count; +} + +lsmash_summary_t *lsmash_get_summary( lsmash_root_t *root, uint32_t track_ID, uint32_t description_number ) +{ + if( isom_check_initializer_present( root ) < 0 || track_ID == 0 || description_number == 0 ) + return NULL; + isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID ); + if( !trak + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->hdlr + || !trak->mdia->minf + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd ) + return NULL; + isom_minf_t *minf = trak->mdia->minf; + isom_stsd_t *stsd = minf->stbl->stsd; + uint32_t i = 1; + for( lsmash_entry_t *entry = stsd->list.head; entry; entry = entry->next ) + { + if( i != description_number ) + { + ++i; + continue; + } + isom_sample_entry_t *sample_entry = entry->data; + if( !sample_entry ) + return NULL; + if( minf->vmhd ) + return isom_create_video_summary_from_description( sample_entry ); + else if( minf->smhd ) + return isom_create_audio_summary_from_description( sample_entry ); + else + return NULL; + } + return NULL; +} + +int lsmash_compare_summary( lsmash_summary_t *a, lsmash_summary_t *b ) +{ + if( !a || !b ) + return LSMASH_ERR_FUNCTION_PARAM; + if( a->summary_type != b->summary_type + || !lsmash_check_box_type_identical( a->sample_type, b->sample_type ) ) + return 1; + if( a->summary_type == LSMASH_SUMMARY_TYPE_VIDEO ) + { + lsmash_video_summary_t *in_video = (lsmash_video_summary_t *)a; + lsmash_video_summary_t *out_video = (lsmash_video_summary_t *)b; + if( in_video->width != out_video->width + || in_video->height != out_video->height + || in_video->depth != out_video->depth + || in_video->par_h != out_video->par_h + || in_video->par_v != out_video->par_v + || memcmp( in_video->compressorname, out_video->compressorname, strlen( in_video->compressorname ) ) + || in_video->clap.width.n != out_video->clap.width.n + || in_video->clap.width.d != out_video->clap.width.d + || in_video->clap.height.n != out_video->clap.height.n + || in_video->clap.height.d != out_video->clap.height.d + || in_video->clap.horizontal_offset.n != out_video->clap.horizontal_offset.n + || in_video->clap.horizontal_offset.d != out_video->clap.horizontal_offset.d + || in_video->clap.vertical_offset.n != out_video->clap.vertical_offset.n + || in_video->clap.vertical_offset.d != out_video->clap.vertical_offset.d + || in_video->color.primaries_index != out_video->color.primaries_index + || in_video->color.transfer_index != out_video->color.transfer_index + || in_video->color.matrix_index != out_video->color.matrix_index + || in_video->color.full_range != out_video->color.full_range ) + return 1; + } + else if( a->summary_type == LSMASH_SUMMARY_TYPE_AUDIO ) + { + lsmash_audio_summary_t *in_audio = (lsmash_audio_summary_t *)a; + lsmash_audio_summary_t *out_audio = (lsmash_audio_summary_t *)b; + if( in_audio->frequency != out_audio->frequency + || in_audio->channels != out_audio->channels + || in_audio->sample_size != out_audio->sample_size + || in_audio->samples_in_frame != out_audio->samples_in_frame ) + return 1; + } + return isom_compare_opaque_extensions( a, b ); +} + +lsmash_codec_support_flag lsmash_check_codec_support( lsmash_codec_type_t sample_type ) +{ + static struct codec_support_table_tag + { + lsmash_codec_type_t type; + lsmash_codec_support_flag flags; + } codec_support_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, LSMASH_CODEC_SUPPORT_FLAG_NONE } }; + if( lsmash_check_codec_type_identical( codec_support_table[0].type, LSMASH_CODEC_TYPE_UNSPECIFIED ) ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_CODEC_SUPPORT_TABLE_ELEMENT( type, flags ) \ + codec_support_table[i++] = (struct codec_support_table_tag){ type, flags } + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_DEMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ALAC_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RAW_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_MUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RAW_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); + ADD_CODEC_SUPPORT_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, LSMASH_CODEC_SUPPORT_FLAG_NONE ); + } + for( int i = 0; !lsmash_check_codec_type_identical( codec_support_table[i].type, LSMASH_CODEC_TYPE_UNSPECIFIED ); i++ ) + if( lsmash_check_codec_type_identical( sample_type, codec_support_table[i].type ) ) + return codec_support_table[i].flags; + return LSMASH_CODEC_SUPPORT_FLAG_NONE; +} diff -Nru l-smash-1.9.1/core/timeline.c l-smash-2.3.0/core/timeline.c --- l-smash-1.9.1/core/timeline.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/timeline.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1950 @@ +/***************************************************************************** + * timeline.c: + ***************************************************************************** + * Copyright (C) 2011-2014 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. */ + +#ifdef LSMASH_DEMUXER_ENABLED + +#include "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "box.h" + +#include "codecs/mp4a.h" +#include "codecs/mp4sys.h" +#include "codecs/description.h" + +#define NO_RANDOM_ACCESS_POINT 0xffffffff + +typedef struct +{ + uint64_t data_offset; + uint64_t length; + uint32_t number; /* useless at present */ + lsmash_file_t *file; +} isom_portable_chunk_t; + +typedef struct +{ + uint64_t pos; + uint32_t duration; + uint32_t offset; + uint32_t length; + uint32_t index; + isom_portable_chunk_t *chunk; + lsmash_sample_property_t prop; +} isom_sample_info_t; + +typedef struct +{ + uint64_t pos; /* position of the first sample in this bunch */ + uint32_t duration; /* duration in media timescale each sample has */ + uint32_t offset; /* offset between composition time and decoding time each sample has */ + uint32_t length; /* data size each sample has */ + uint32_t index; /* sample_description_index applied to each sample */ + isom_portable_chunk_t *chunk; /* chunk samples belong to */ + lsmash_sample_property_t prop; /* property applied to each sample */ + uint32_t sample_count; /* number of samples in this bunch */ +} isom_lpcm_bunch_t; + +static const lsmash_class_t lsmash_timeline_class = +{ + "timeline" +}; + +typedef struct isom_timeline_tag isom_timeline_t; + +struct isom_timeline_tag +{ + const lsmash_class_t *class; + uint32_t track_ID; + uint32_t movie_timescale; + uint32_t media_timescale; + uint32_t sample_count; + uint32_t max_sample_size; + uint32_t ctd_shift; /* shift from composition to decode timeline */ + uint64_t media_duration; + uint64_t track_duration; + uint32_t last_accessed_sample_number; + uint64_t last_accessed_sample_dts; + uint32_t last_accessed_lpcm_bunch_number; + uint32_t last_accessed_lpcm_bunch_duration; + uint32_t last_accessed_lpcm_bunch_sample_count; + uint32_t last_accessed_lpcm_bunch_first_sample_number; + uint64_t last_accessed_lpcm_bunch_dts; + lsmash_entry_list_t edit_list [1]; /* list of edits */ + lsmash_entry_list_t chunk_list[1]; /* list of chunks */ + lsmash_entry_list_t info_list [1]; /* list of sample info */ + lsmash_entry_list_t bunch_list[1]; /* list of LPCM bunch */ + int (*get_dts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts ); + int (*get_cts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts ); + int (*get_sample_duration)( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration ); + lsmash_sample_t *(*get_sample)( isom_timeline_t *timeline, uint32_t sample_number ); + int (*get_sample_info)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample ); + int (*get_sample_property)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop ); + int (*check_sample_existence)( isom_timeline_t *timeline, uint32_t sample_number ); +}; + +static isom_timeline_t *isom_get_timeline( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 + || track_ID == 0 + || !root->file->timeline ) + return NULL; + for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next ) + { + isom_timeline_t *timeline = (isom_timeline_t *)entry->data; + if( !timeline ) + return NULL; + if( timeline->track_ID == track_ID ) + return timeline; + } + return NULL; +} + +static isom_timeline_t *isom_create_timeline( void ) +{ + isom_timeline_t *timeline = lsmash_malloc_zero( sizeof(isom_timeline_t) ); + if( !timeline ) + return NULL; + timeline->class = &lsmash_timeline_class; + lsmash_init_entry_list( timeline->edit_list ); + lsmash_init_entry_list( timeline->chunk_list ); + lsmash_init_entry_list( timeline->info_list ); + lsmash_init_entry_list( timeline->bunch_list ); + return timeline; +} + +static void isom_destruct_timeline_direct( isom_timeline_t *timeline ) +{ + if( !timeline ) + return; + lsmash_remove_entries( timeline->edit_list, NULL ); + lsmash_remove_entries( timeline->chunk_list, NULL ); /* chunk data must be already freed. */ + lsmash_remove_entries( timeline->info_list, NULL ); + lsmash_remove_entries( timeline->bunch_list, NULL ); + lsmash_free( timeline ); +} + +void isom_remove_timelines( lsmash_file_t *file ) +{ + if( !file + || !file->timeline ) + return; + lsmash_remove_list( file->timeline, isom_destruct_timeline_direct ); +} + +void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID ) +{ + if( track_ID == 0 + || !root + || !root->file + || !root->file->timeline ) + return; + for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next ) + { + isom_timeline_t *timeline = (isom_timeline_t *)entry->data; + if( !timeline ) + continue; + if( timeline->track_ID == track_ID ) + { + lsmash_remove_entry_direct( root->file->timeline, entry, isom_destruct_timeline_direct ); + break; + } + } +} + +static void isom_get_qt_fixed_comp_audio_sample_quants +( + isom_timeline_t *timeline, + isom_sample_entry_t *description, + uint32_t *samples_per_packet, + uint32_t *constant_sample_size +) +{ + isom_audio_entry_t *audio = (isom_audio_entry_t *)description; + if( audio->version == 0 ) + { + uint32_t dummy; + if( !isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, samples_per_packet, constant_sample_size, &dummy ) ) + { + /* LPCM */ + if( !isom_is_lpcm_audio( audio ) ) + lsmash_log( timeline, LSMASH_LOG_WARNING, "unsupported implicit sample table!\n" ); + *samples_per_packet = 1; + *constant_sample_size = (audio->samplesize * audio->channelcount) / 8; + } + } + else if( audio->version == 1 ) + { + *samples_per_packet = audio->samplesPerPacket; + *constant_sample_size = audio->bytesPerFrame; + } + else /* if( audio->version == 2 ) */ + { + *samples_per_packet = audio->constLPCMFramesPerAudioPacket; + *constant_sample_size = audio->constBytesPerAudioPacket; + } +} + +static int isom_is_qt_fixed_compressed_audio +( + isom_sample_entry_t *description +) +{ + if( (description->manager & LSMASH_VIDEO_DESCRIPTION) || !isom_is_qt_audio( description->type ) ) + return 0; + /* LPCM is a special case of fixed compression. */ + return (((isom_audio_entry_t *)description)->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION); +} + +static int isom_add_sample_info_entry( isom_timeline_t *timeline, isom_sample_info_t *src_info ) +{ + isom_sample_info_t *dst_info = lsmash_malloc( sizeof(isom_sample_info_t) ); + if( !dst_info ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( timeline->info_list, dst_info ) < 0 ) + { + lsmash_free( dst_info ); + return LSMASH_ERR_MEMORY_ALLOC; + } + *dst_info = *src_info; + return 0; +} + +static int isom_add_lpcm_bunch_entry( isom_timeline_t *timeline, isom_lpcm_bunch_t *src_bunch ) +{ + isom_lpcm_bunch_t *dst_bunch = lsmash_malloc( sizeof(isom_lpcm_bunch_t) ); + if( !dst_bunch ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( timeline->bunch_list, dst_bunch ) < 0 ) + { + lsmash_free( dst_bunch ); + return LSMASH_ERR_MEMORY_ALLOC; + } + *dst_bunch = *src_bunch; + return 0; +} + +static int isom_add_portable_chunk_entry( isom_timeline_t *timeline, isom_portable_chunk_t *src_chunk ) +{ + isom_portable_chunk_t *dst_chunk = lsmash_malloc( sizeof(isom_portable_chunk_t) ); + if( !dst_chunk ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( timeline->chunk_list, dst_chunk ) < 0 ) + { + lsmash_free( dst_chunk ); + return LSMASH_ERR_MEMORY_ALLOC; + } + *dst_chunk = *src_chunk; + return 0; +} + +static int isom_compare_lpcm_sample_info( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info ) +{ + return info->duration != bunch->duration + || info->offset != bunch->offset + || info->length != bunch->length + || info->index != bunch->index + || info->chunk != bunch->chunk; +} + +static void isom_update_bunch( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info ) +{ + bunch->pos = info->pos; + bunch->duration = info->duration; + bunch->offset = info->offset; + bunch->length = info->length; + bunch->index = info->index; + bunch->chunk = info->chunk; + bunch->prop = info->prop; + bunch->sample_count = 1; +} + +static isom_lpcm_bunch_t *isom_get_bunch( isom_timeline_t *timeline, uint32_t sample_number ) +{ + if( sample_number >= timeline->last_accessed_lpcm_bunch_first_sample_number + && sample_number < timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count ) + /* Get from the last accessed LPCM bunch. */ + return (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, timeline->last_accessed_lpcm_bunch_number ); + uint32_t first_sample_number_in_next_bunch; + uint32_t bunch_number = 1; + uint64_t bunch_dts; + if( timeline->last_accessed_lpcm_bunch_first_sample_number + && timeline->last_accessed_lpcm_bunch_first_sample_number <= sample_number ) + { + first_sample_number_in_next_bunch = timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count; + bunch_number += timeline->last_accessed_lpcm_bunch_number; + bunch_dts = timeline->last_accessed_lpcm_bunch_dts + + timeline->last_accessed_lpcm_bunch_duration * timeline->last_accessed_lpcm_bunch_sample_count; + } + else + { + /* Seek from the first LPCM bunch. */ + first_sample_number_in_next_bunch = 1; + bunch_dts = 0; + } + isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ ); + if( !bunch ) + return NULL; + first_sample_number_in_next_bunch += bunch->sample_count; + while( sample_number >= first_sample_number_in_next_bunch ) + { + bunch_dts += bunch->duration * bunch->sample_count; + bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ ); + if( !bunch ) + return NULL; + first_sample_number_in_next_bunch += bunch->sample_count; + } + timeline->last_accessed_lpcm_bunch_dts = bunch_dts; + timeline->last_accessed_lpcm_bunch_number = bunch_number - 1; + timeline->last_accessed_lpcm_bunch_duration = bunch->duration; + timeline->last_accessed_lpcm_bunch_sample_count = bunch->sample_count; + timeline->last_accessed_lpcm_bunch_first_sample_number = first_sample_number_in_next_bunch - bunch->sample_count; + return bunch; +} + +static int isom_get_dts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts ) +{ + if( sample_number == timeline->last_accessed_sample_number ) + *dts = timeline->last_accessed_sample_dts; + else if( sample_number == 1 ) + *dts = 0; + else if( sample_number == timeline->last_accessed_sample_number + 1 ) + { + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + *dts = timeline->last_accessed_sample_dts + info->duration; + } + else if( sample_number == timeline->last_accessed_sample_number - 1 ) + { + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 ); + if( !info ) + return LSMASH_ERR_NAMELESS; + *dts = timeline->last_accessed_sample_dts - info->duration; + } + else + { + *dts = 0; + uint32_t distance = sample_number - 1; + lsmash_entry_t *entry; + for( entry = timeline->info_list->head; entry; entry = entry->next ) + { + isom_sample_info_t *info = (isom_sample_info_t *)entry->data; + if( !info ) + return LSMASH_ERR_NAMELESS; + if( distance-- == 0 ) + break; + *dts += info->duration; + } + if( !entry ) + return LSMASH_ERR_NAMELESS; + } + /* Note: last_accessed_sample_number is always updated together with last_accessed_sample_dts, and vice versa. */ + timeline->last_accessed_sample_dts = *dts; + timeline->last_accessed_sample_number = sample_number; + return 0; +} + +static int isom_get_cts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts ) +{ + int ret = isom_get_dts_from_info_list( timeline, sample_number, cts ); + if( ret < 0 ) + return ret; + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + *cts = timeline->ctd_shift ? (*cts + (int32_t)info->offset) : (*cts + info->offset); + return 0; +} + +static int isom_get_dts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts ) +{ + isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); + if( !bunch ) + return LSMASH_ERR_NAMELESS; + *dts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration; + return 0; +} + +static int isom_get_cts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts ) +{ + isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); + if( !bunch ) + return LSMASH_ERR_NAMELESS; + *cts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration + bunch->offset; + return 0; +} + +static int isom_get_sample_duration_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration ) +{ + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + *sample_duration = info->duration; + return 0; +} + +static int isom_get_sample_duration_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration ) +{ + isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); + if( !bunch ) + return LSMASH_ERR_NAMELESS; + *sample_duration = bunch->duration; + return 0; +} + +static int isom_check_sample_existence_in_info_list( isom_timeline_t *timeline, uint32_t sample_number ) +{ + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); + if( !info || !info->chunk ) + return 0; + return !!info->chunk->file; +} + +static int isom_check_sample_existence_in_bunch_list( isom_timeline_t *timeline, uint32_t sample_number ) +{ + isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); + if( !bunch || !bunch->chunk ) + return 0; + return !!bunch->chunk->file; +} + +static lsmash_sample_t *isom_read_sample_data_from_stream +( + lsmash_file_t *file, + isom_timeline_t *timeline, + uint32_t sample_length, + uint64_t sample_pos +) +{ + lsmash_sample_t *sample = lsmash_create_sample( 0 ); + if( !sample ) + return NULL; + lsmash_bs_t *bs = file->bs; + lsmash_bs_read_seek( bs, sample_pos, SEEK_SET ); + sample->data = lsmash_bs_get_bytes( bs, sample_length ); + if( !sample->data ) + { + lsmash_delete_sample( sample ); + return NULL; + } + return sample; +} + +static lsmash_sample_t *isom_get_lpcm_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number ) +{ + isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); + if( !bunch + || !bunch->chunk ) + return NULL; + /* Get data of a sample from the stream. */ + uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number; + uint64_t sample_pos = bunch->pos + sample_number_offset * bunch->length; + lsmash_sample_t *sample = isom_read_sample_data_from_stream( bunch->chunk->file, timeline, bunch->length, sample_pos ); + if( !sample ) + return NULL; + /* Get sample info. */ + sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration; + sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset); + sample->pos = sample_pos; + sample->length = bunch->length; + sample->index = bunch->index; + sample->prop = bunch->prop; + return sample; +} + +static lsmash_sample_t *isom_get_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number ) +{ + uint64_t dts; + if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) < 0 ) + return NULL; + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); + if( !info + || !info->chunk ) + return NULL; + /* Get data of a sample from the stream. */ + lsmash_sample_t *sample = isom_read_sample_data_from_stream( info->chunk->file, timeline, info->length, info->pos ); + if( !sample ) + return NULL; + /* Get sample info. */ + sample->dts = dts; + sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset); + sample->pos = info->pos; + sample->length = info->length; + sample->index = info->index; + sample->prop = info->prop; + return sample; +} + +static int isom_get_lpcm_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample ) +{ + isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); + if( !bunch ) + return LSMASH_ERR_NAMELESS; + uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number; + sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration; + sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset); + sample->pos = bunch->pos + sample_number_offset * bunch->length; + sample->length = bunch->length; + sample->index = bunch->index; + sample->prop = bunch->prop; + return 0; +} + +static int isom_get_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample ) +{ + uint64_t dts; + int ret = isom_get_dts_from_info_list( timeline, sample_number, &dts ); + if( ret < 0 ) + return ret; + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + sample->dts = dts; + sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset); + sample->pos = info->pos; + sample->length = info->length; + sample->index = info->index; + sample->prop = info->prop; + return 0; +} + +static int isom_get_lpcm_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop ) +{ + memset( prop, 0, sizeof(lsmash_sample_property_t) ); + prop->ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + return 0; +} + +static int isom_get_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop ) +{ + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + *prop = info->prop; + return 0; +} + +static inline void isom_increment_sample_number_in_entry +( + uint32_t *sample_number_in_entry, + lsmash_entry_t **entry, + uint32_t sample_count +) +{ + if( *sample_number_in_entry == sample_count ) + { + *sample_number_in_entry = 1; + *entry = (*entry)->next; + } + else + *sample_number_in_entry += 1; +} + +static inline isom_sgpd_t *isom_select_appropriate_sgpd +( + isom_sgpd_t *sgpd, + isom_sgpd_t *sgpd_frag, + uint32_t *group_description_index +) +{ + if( sgpd_frag && *group_description_index >= 0x10000 ) + { + /* The specification doesn't define 0x10000 explicitly, however says that there must be fewer than + * 65536 group definitions for this track and grouping type in the sample table in the Movie Box. + * So, we assume 0x10000 is equivalent to 0. */ + *group_description_index -= 0x10000; + return sgpd_frag; + } + else + return sgpd; +} + +static int isom_get_roll_recovery_grouping_info +( + isom_timeline_t *timeline, + lsmash_entry_t **sbgp_roll_entry, + isom_sgpd_t *sgpd_roll, + isom_sgpd_t *sgpd_frag_roll, + uint32_t *sample_number_in_sbgp_roll_entry, + isom_sample_info_t *info, + uint32_t sample_number +) +{ + isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_roll_entry)->data; + if( !assignment ) + return LSMASH_ERR_NAMELESS; + if( assignment->group_description_index ) + { + uint32_t group_description_index = assignment->group_description_index; + isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_roll, sgpd_frag_roll, &group_description_index ); + isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index ); + if( roll_data ) + { + if( roll_data->roll_distance > 0 ) + { + /* post-roll */ + info->prop.post_roll.complete = sample_number + roll_data->roll_distance; + if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START; + } + else if( roll_data->roll_distance < 0 ) + { + /* pre-roll */ + info->prop.pre_roll.distance = -roll_data->roll_distance; + if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_PRE_ROLL_END; + } + } + else if( *sample_number_in_sbgp_roll_entry == 1 && group_description_index ) + lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of roll recoveries is not found in the Sample Group Description Box.\n" ); + } + isom_increment_sample_number_in_entry( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment->sample_count ); + return 0; +} + +static int isom_get_random_access_point_grouping_info +( + isom_timeline_t *timeline, + lsmash_entry_t **sbgp_rap_entry, + isom_sgpd_t *sgpd_rap, + isom_sgpd_t *sgpd_frag_rap, + uint32_t *sample_number_in_sbgp_rap_entry, + isom_sample_info_t *info, + uint32_t *distance +) +{ + isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_rap_entry)->data; + if( !assignment ) + return LSMASH_ERR_NAMELESS; + if( assignment->group_description_index && (info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) ) + { + uint32_t group_description_index = assignment->group_description_index; + isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_rap, sgpd_frag_rap, &group_description_index ); + isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index ); + if( rap_data ) + { + /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */ + info->prop.ra_flags |= (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples) + ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_OPEN_RAP + : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; + *distance = 0; + } + else if( *sample_number_in_sbgp_rap_entry == 1 && group_description_index ) + lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of random access points is not found in the Sample Group Description Box.\n" ); + } + isom_increment_sample_number_in_entry( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment->sample_count ); + return 0; +} + +int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID ) +{ + if( isom_check_initializer_present( root ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *file = root->file; + if( !file->moov + || !file->moov->mvhd + || file->moov->mvhd->timescale == 0 ) + return LSMASH_ERR_INVALID_DATA; + /* Get track by track_ID. */ + isom_trak_t *trak = isom_get_trak( file, track_ID ); + if( !trak + || !trak->tkhd + || !trak->mdia + || !trak->mdia->mdhd + || trak->mdia->mdhd->timescale == 0 + || !trak->mdia->minf + || !trak->mdia->minf->stbl ) + return LSMASH_ERR_INVALID_DATA; + /* Create a timeline list if it doesn't exist. */ + if( !file->timeline ) + { + file->timeline = lsmash_create_entry_list(); + if( !file->timeline ) + return LSMASH_ERR_MEMORY_ALLOC; + } + /* Create a timeline. */ + isom_timeline_t *timeline = isom_create_timeline(); + if( !timeline ) + return LSMASH_ERR_MEMORY_ALLOC; + timeline->track_ID = track_ID; + timeline->movie_timescale = file->moov->mvhd->timescale; + timeline->media_timescale = trak->mdia->mdhd->timescale; + timeline->track_duration = trak->tkhd->duration; + /* Preparation for construction. */ + isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL; + isom_minf_t *minf = trak->mdia->minf; + isom_dref_t *dref = minf->dinf->dref; + isom_stbl_t *stbl = minf->stbl; + isom_stsd_t *stsd = stbl->stsd; + isom_stts_t *stts = stbl->stts; + isom_ctts_t *ctts = stbl->ctts; + isom_stss_t *stss = stbl->stss; + isom_stps_t *stps = stbl->stps; + isom_sdtp_t *sdtp = stbl->sdtp; + isom_stsc_t *stsc = stbl->stsc; + isom_stsz_t *stsz = stbl->stsz; + 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 ); + isom_sgpd_t *sgpd_roll = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list ); + isom_sbgp_t *sbgp_roll = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list ); + lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL; + lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL; + lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL; + 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 && stsz->list ? stsz->list->head : NULL; + lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL; + lsmash_entry_t *stco_entry = stco && stco->list ? stco->list->head : NULL; + lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL; + lsmash_entry_t *sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL; + lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL; + isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL; + int err = LSMASH_ERR_INVALID_DATA; + int movie_fragments_present = (file->moov->mvex && file->moof_list.head); + if( !movie_fragments_present && (!stts_entry || !stsc_entry || !stco_entry || !stco_entry->data || (next_stsc_entry && !next_stsc_entry->data)) ) + goto fail; + isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data ? stsc_data->sample_description_index : 1 ); + if( !description ) + goto fail; + isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index ); + int all_sync = !stss; + int large_presentation = stco->large_presentation || lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_CO64 ); + int is_lpcm_audio = isom_is_lpcm_audio( description ); + int is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description ); + int iso_sdtp = file->max_isom_version >= 2 || file->avc_extensions; + int allow_negative_sample_offset = ctts && ((file->max_isom_version >= 4 && ctts->version == 1) || file->qt_compatible); + uint32_t sample_number_in_stts_entry = 1; + uint32_t sample_number_in_ctts_entry = 1; + uint32_t sample_number_in_sbgp_roll_entry = 1; + uint32_t sample_number_in_sbgp_rap_entry = 1; + uint64_t dts = 0; + uint32_t chunk_number = 1; + uint64_t offset_from_chunk = 0; + uint64_t data_offset = stco_entry && stco_entry->data + ? large_presentation + ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset + : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset + : 0; + uint32_t samples_per_packet; + uint32_t constant_sample_size; + if( is_qt_fixed_comp_audio ) + isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size ); + else + { + samples_per_packet = 1; + constant_sample_size = stsz->sample_size; + } + uint32_t sample_number = samples_per_packet; + uint32_t sample_number_in_chunk = samples_per_packet; + /* Copy edits. */ + while( elst_entry ) + { + isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) ); + if( !edit + || lsmash_add_entry( timeline->edit_list, edit ) < 0 ) + { + err = LSMASH_ERR_MEMORY_ALLOC; + goto fail; + } + elst_entry = elst_entry->next; + } + /* Check what the first 2-bits of sample dependency means. + * This check is for chimera of ISO Base Media and QTFF. */ + if( iso_sdtp && sdtp_entry ) + { + while( sdtp_entry ) + { + isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data; + if( !sdtp_data ) + goto fail; + if( sdtp_data->is_leading > 1 ) + break; /* Apparently, it's defined under ISO Base Media. */ + if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) ) + { + /* Obviously, it's not defined under ISO Base Media. */ + iso_sdtp = 0; + break; + } + sdtp_entry = sdtp_entry->next; + } + sdtp_entry = sdtp->list->head; + } + /**--- Construct media timeline. ---**/ + isom_portable_chunk_t chunk; + chunk.data_offset = data_offset; + chunk.length = 0; + chunk.number = chunk_number; + chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file; + if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 ) + goto fail; + uint32_t distance = NO_RANDOM_ACCESS_POINT; + uint32_t last_duration = UINT32_MAX; + uint32_t packet_number = 1; + isom_lpcm_bunch_t bunch = { 0 }; + while( sample_number <= stsz->sample_count ) + { + isom_sample_info_t info = { 0 }; + /* Get sample duration and sample offset. */ + for( uint32_t i = 0; i < samples_per_packet; i++ ) + { + /* sample duration */ + if( stts_entry ) + { + isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data; + if( !stts_data ) + goto fail; + isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count ); + last_duration = stts_data->sample_delta; + } + info.duration += last_duration; + dts += last_duration; + /* sample offset */ + uint32_t sample_offset; + if( ctts_entry ) + { + isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data; + if( !ctts_data ) + goto fail; + isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count ); + sample_offset = ctts_data->sample_offset; + if( allow_negative_sample_offset ) + { + uint64_t cts = dts + (int32_t)sample_offset; + if( (cts + timeline->ctd_shift) < dts ) + timeline->ctd_shift = dts - cts; + } + } + else + sample_offset = 0; + if( i == 0 ) + info.offset = sample_offset; + } + timeline->media_duration += info.duration; + if( !is_qt_fixed_comp_audio ) + { + /* Check whether sync sample or not. */ + if( stss_entry ) + { + isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data; + if( !stss_data ) + goto fail; + if( sample_number == stss_data->sample_number ) + { + info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + stss_entry = stss_entry->next; + distance = 0; + } + } + else if( all_sync ) + /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation + * though all of them could be marked as a sync sample. */ + info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + /* Check whether partial sync sample or not. */ + if( stps_entry ) + { + isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data; + if( !stps_data ) + goto fail; + if( sample_number == stps_data->sample_number ) + { + info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP; + stps_entry = stps_entry->next; + distance = 0; + } + } + /* Get sample dependency info. */ + if( sdtp_entry ) + { + isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data; + if( !sdtp_data ) + goto fail; + if( iso_sdtp ) + info.prop.leading = sdtp_data->is_leading; + else + info.prop.allow_earlier = sdtp_data->is_leading; + info.prop.independent = sdtp_data->sample_depends_on; + info.prop.disposable = sdtp_data->sample_is_depended_on; + info.prop.redundant = sdtp_data->sample_has_redundancy; + sdtp_entry = sdtp_entry->next; + } + /* Get roll recovery grouping info. */ + if( sbgp_roll_entry + && isom_get_roll_recovery_grouping_info( timeline, + &sbgp_roll_entry, sgpd_roll, NULL, + &sample_number_in_sbgp_roll_entry, + &info, sample_number ) < 0 ) + goto fail; + info.prop.post_roll.identifier = sample_number; + /* Get random access point grouping info. */ + if( sbgp_rap_entry + && isom_get_random_access_point_grouping_info( timeline, + &sbgp_rap_entry, sgpd_rap, NULL, + &sample_number_in_sbgp_rap_entry, + &info, &distance ) < 0 ) + goto fail; + /* Set up distance from the previous random access point. */ + if( distance != NO_RANDOM_ACCESS_POINT ) + { + if( info.prop.pre_roll.distance == 0 ) + info.prop.pre_roll.distance = distance; + ++distance; + } + } + else + /* All uncompressed and non-variable compressed audio frame is a sync sample. */ + info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + /* Get size of sample in the stream. */ + if( is_qt_fixed_comp_audio || !stsz_entry ) + info.length = constant_sample_size; + else + { + if( !stsz_entry->data ) + goto fail; + info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size; + stsz_entry = stsz_entry->next; + } + timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length ); + /* Get chunk info. */ + info.pos = data_offset; + info.index = stsc_data->sample_description_index; + info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data; + offset_from_chunk += info.length; + if( sample_number_in_chunk == stsc_data->samples_per_chunk ) + { + /* Set the length of the last chunk. */ + if( info.chunk ) + info.chunk->length = offset_from_chunk; + /* Move the next chunk. */ + if( stco_entry ) + stco_entry = stco_entry->next; + if( stco_entry + && stco_entry->data ) + data_offset = large_presentation + ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset + : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset; + chunk.data_offset = data_offset; + chunk.length = 0; + chunk.number = ++chunk_number; + offset_from_chunk = 0; + /* Check if the next entry is broken. */ + while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk ) + { + /* Just skip broken next entry. */ + lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" ); + lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" ); + next_stsc_entry = next_stsc_entry->next; + if( next_stsc_entry + && !next_stsc_entry->data ) + goto fail; + } + /* Check if the next chunk belongs to the next sequence of chunks. */ + if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk ) + { + stsc_entry = next_stsc_entry; + next_stsc_entry = next_stsc_entry->next; + if( next_stsc_entry + && !next_stsc_entry->data ) + goto fail; + stsc_data = (isom_stsc_entry_t *)stsc_entry->data; + /* Update sample description. */ + description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data->sample_description_index ); + is_lpcm_audio = isom_is_lpcm_audio( description ); + is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description ); + if( is_qt_fixed_comp_audio ) + isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size ); + else + { + samples_per_packet = 1; + constant_sample_size = stsz->sample_size; + } + /* Reference media data. */ + dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index ); + chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file; + } + sample_number_in_chunk = samples_per_packet; + if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 ) + goto fail; + } + else + { + data_offset += info.length; + sample_number_in_chunk += samples_per_packet; + } + /* OK. Let's add its info. */ + if( is_lpcm_audio ) + { + if( sample_number == samples_per_packet ) + isom_update_bunch( &bunch, &info ); + else if( isom_compare_lpcm_sample_info( &bunch, &info ) ) + { + if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 ) + goto fail; + isom_update_bunch( &bunch, &info ); + } + else + ++ bunch.sample_count; + } + else if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 ) + goto fail; + if( timeline->info_list->entry_count && timeline->bunch_list->entry_count ) + { + lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" ); + err = LSMASH_ERR_PATCH_WELCOME; + goto fail; + } + sample_number += samples_per_packet; + packet_number += 1; + } + isom_portable_chunk_t *last_chunk = lsmash_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count ); + if( last_chunk ) + { + if( offset_from_chunk ) + last_chunk->length = offset_from_chunk; + else + { + /* Remove the last invalid chunk. */ + lsmash_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count, NULL ); + --chunk_number; + } + } + uint32_t sample_count = packet_number - 1; + if( movie_fragments_present ) + { + isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID ); + lsmash_entry_t *tfra_entry = tfra && tfra->list ? tfra->list->head : NULL; + isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL; + chunk.data_offset = 0; + chunk.length = 0; + /* Movie fragments */ + for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next ) + { + isom_moof_t *moof = (isom_moof_t *)moof_entry->data; + if( !moof ) + goto fail; + uint64_t last_sample_end_pos = 0; + /* Track fragments */ + uint32_t traf_number = 1; + for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next ) + { + isom_traf_t *traf = (isom_traf_t *)traf_entry->data; + if( !traf ) + goto fail; + isom_tfhd_t *tfhd = traf->tfhd; + if( !tfhd ) + goto fail; + isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID ); + if( !trex ) + goto fail; + /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */ + if( !traf->trun_list.head ) + { + ++traf_number; + continue; + } + /* Get base_data_offset. */ + uint64_t base_data_offset; + if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) + base_data_offset = tfhd->base_data_offset; + else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head ) + base_data_offset = moof->pos; + else + base_data_offset = last_sample_end_pos; + /* sample grouping */ + isom_sgpd_t *sgpd_frag_rap; + isom_sgpd_t *sgpd_frag_roll; + sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP ); + sbgp_rap = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP ); + sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL; + sgpd_frag_roll = isom_get_roll_recovery_sample_group_description( &traf->sgpd_list ); + sbgp_roll = isom_get_roll_recovery_sample_to_group ( &traf->sbgp_list ); + sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL; + int need_data_offset_only = (tfhd->track_ID != track_ID); + /* Track runs */ + uint32_t trun_number = 1; + for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next ) + { + isom_trun_t *trun = (isom_trun_t *)trun_entry->data; + if( !trun ) + goto fail; + if( trun->sample_count == 0 ) + { + ++trun_number; + continue; + } + /* Get data_offset. */ + if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) + data_offset = trun->data_offset + base_data_offset; + else if( trun_entry == traf->trun_list.head ) + data_offset = base_data_offset; + else + data_offset = last_sample_end_pos; + /* */ + uint32_t sample_description_index = 0; + isom_sdtp_entry_t *sdtp_data = NULL; + if( !need_data_offset_only ) + { + /* Get sample_description_index of this track fragment. */ + if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) + sample_description_index = tfhd->sample_description_index; + else + sample_description_index = trex->default_sample_description_index; + description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, sample_description_index ); + is_lpcm_audio = isom_is_lpcm_audio( description ); + /* Reference media data. */ + dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index ); + lsmash_file_t *ref_file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file; + /* Each track run can be considered as a chunk. + * Here, we consider physically consecutive track runs as one chunk. */ + if( chunk.data_offset + chunk.length != data_offset || chunk.file != ref_file ) + { + chunk.data_offset = data_offset; + chunk.length = 0; + chunk.number = ++chunk_number; + chunk.file = ref_file; + if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 ) + goto fail; + } + /* Get dependency info for this track fragment. */ + sdtp_entry = traf->sdtp && traf->sdtp->list ? traf->sdtp->list->head : NULL; + sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL; + } + /* Get info of each sample. */ + lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL; + sample_number = 1; + while( sample_number <= trun->sample_count ) + { + isom_sample_info_t info = { 0 }; + isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL; + /* Get sample_size */ + if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) ) + info.length = row->sample_size; + else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) + info.length = tfhd->default_sample_size; + else + info.length = trex->default_sample_size; + if( !need_data_offset_only ) + { + info.pos = data_offset; + info.index = sample_description_index; + info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data; + info.chunk->length += info.length; + /* Get sample_duration. */ + if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) ) + info.duration = row->sample_duration; + else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) + info.duration = tfhd->default_sample_duration; + else + info.duration = trex->default_sample_duration; + /* Get composition time offset. */ + if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) ) + { + info.offset = row->sample_composition_time_offset; + /* Check composition to decode timeline shift. */ + if( file->max_isom_version >= 6 && trun->version != 0 ) + { + uint64_t cts = dts + (int32_t)info.offset; + if( (cts + timeline->ctd_shift) < dts ) + timeline->ctd_shift = dts - cts; + } + } + else + info.offset = 0; + dts += info.duration; + /* Update media duration and maximun sample size. */ + timeline->media_duration += info.duration; + timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length ); + if( !is_lpcm_audio ) + { + /* Get sample_flags. */ + isom_sample_flags_t sample_flags; + if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) ) + sample_flags = trun->first_sample_flags; + else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) ) + sample_flags = row->sample_flags; + else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) + sample_flags = tfhd->default_sample_flags; + else + sample_flags = trex->default_sample_flags; + if( sdtp_data ) + { + /* Independent and Disposable Samples Box overrides the information from sample_flags. + * There is no description in the specification about this, but the intention should be such a thing. + * The ground is that sample_flags is placed in media layer + * while Independent and Disposable Samples Box is placed in track or presentation layer. */ + info.prop.leading = sdtp_data->is_leading; + info.prop.independent = sdtp_data->sample_depends_on; + info.prop.disposable = sdtp_data->sample_is_depended_on; + info.prop.redundant = sdtp_data->sample_has_redundancy; + if( sdtp_entry ) + sdtp_entry = sdtp_entry->next; + sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL; + } + else + { + info.prop.leading = sample_flags.is_leading; + info.prop.independent = sample_flags.sample_depends_on; + info.prop.disposable = sample_flags.sample_is_depended_on; + info.prop.redundant = sample_flags.sample_has_redundancy; + } + /* Check this sample is a sync sample or not. + * Note: all sync sample shall be independent. */ + if( !sample_flags.sample_is_non_sync_sample + && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT ) + { + info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + distance = 0; + } + /* Get roll recovery grouping info. */ + uint32_t roll_id = sample_count + sample_number; + if( sbgp_roll_entry + && isom_get_roll_recovery_grouping_info( timeline, + &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll, + &sample_number_in_sbgp_roll_entry, + &info, roll_id ) < 0 ) + goto fail; + info.prop.post_roll.identifier = roll_id; + /* Get random access point grouping info. */ + if( sbgp_rap_entry + && isom_get_random_access_point_grouping_info( timeline, + &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap, + &sample_number_in_sbgp_rap_entry, + &info, &distance ) < 0 ) + goto fail; + /* Get the location of the sync sample from 'tfra' if it is not set up yet. + * Note: there is no guarantee that its entries are placed in a specific order. */ + if( tfra ) + { + if( tfra->number_of_entry == 0 + && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + if( rap + && rap->moof_offset == moof->pos + && rap->traf_number == traf_number + && rap->trun_number == trun_number + && rap->sample_number == sample_number ) + { + if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + if( tfra_entry ) + tfra_entry = tfra_entry->next; + rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL; + } + } + /* Set up distance from the previous random access point. */ + if( distance != NO_RANDOM_ACCESS_POINT ) + { + if( info.prop.pre_roll.distance == 0 ) + info.prop.pre_roll.distance = distance; + ++distance; + } + /* OK. Let's add its info. */ + if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 ) + goto fail; + } + else + { + /* All LPCMFrame is a sync sample. */ + info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + /* OK. Let's add its info. */ + if( sample_count == 0 && sample_number == 1 ) + isom_update_bunch( &bunch, &info ); + else if( isom_compare_lpcm_sample_info( &bunch, &info ) ) + { + if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 ) + goto fail; + isom_update_bunch( &bunch, &info ); + } + else + ++ bunch.sample_count; + } + if( timeline-> info_list->entry_count + && timeline->bunch_list->entry_count ) + { + lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" ); + err = LSMASH_ERR_PATCH_WELCOME; + goto fail; + } + } + data_offset += info.length; + last_sample_end_pos = data_offset; + if( row_entry ) + row_entry = row_entry->next; + ++sample_number; + } + if( !need_data_offset_only ) + sample_count += sample_number - 1; + ++trun_number; + } /* Track runs */ + ++traf_number; + } /* Track fragments */ + } /* Movie fragments */ + } + else if( timeline->chunk_list->entry_count == 0 ) + goto fail; /* No samples in this track. */ + if( bunch.sample_count && (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 ) + goto fail; + if( (err = lsmash_add_entry( file->timeline, timeline )) < 0 ) + goto fail; + /* Finish timeline construction. */ + timeline->sample_count = sample_count; + if( timeline->info_list->entry_count ) + { + timeline->get_dts = isom_get_dts_from_info_list; + timeline->get_cts = isom_get_cts_from_info_list; + timeline->get_sample_duration = isom_get_sample_duration_from_info_list; + timeline->check_sample_existence = isom_check_sample_existence_in_info_list; + timeline->get_sample = isom_get_sample_from_media_timeline; + timeline->get_sample_info = isom_get_sample_info_from_media_timeline; + timeline->get_sample_property = isom_get_sample_property_from_media_timeline; + } + else + { + timeline->get_dts = isom_get_dts_from_bunch_list; + timeline->get_cts = isom_get_cts_from_bunch_list; + timeline->get_sample_duration = isom_get_sample_duration_from_bunch_list; + timeline->check_sample_existence = isom_check_sample_existence_in_bunch_list; + timeline->get_sample = isom_get_lpcm_sample_from_media_timeline; + timeline->get_sample_info = isom_get_lpcm_sample_info_from_media_timeline; + timeline->get_sample_property = isom_get_lpcm_sample_property_from_media_timeline; + } + return 0; +fail: + isom_destruct_timeline_direct( timeline ); + return err; +} + +int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts ) +{ + if( !sample_number || !dts ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline || sample_number > timeline->sample_count ) + return LSMASH_ERR_NAMELESS; + return timeline->get_dts( timeline, sample_number, dts ); +} + +int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts ) +{ + if( !sample_number || !cts ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline || sample_number > timeline->sample_count ) + return LSMASH_ERR_NAMELESS; + return timeline->get_cts( timeline, sample_number, cts ); +} + +lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number ) +{ + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + return timeline ? timeline->get_sample( timeline, sample_number ) : NULL; +} + +int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample ) +{ + if( !sample ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1; +} + +int lsmash_get_sample_property_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_property_t *prop ) +{ + if( !prop ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1; +} + +int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift ) +{ + if( !ctd_shift ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline ) + return LSMASH_ERR_NAMELESS; + *ctd_shift = timeline->ctd_shift; + return 0; +} + +static int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number ) +{ + lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number-- ); + if( !entry + || !entry->data ) + return LSMASH_ERR_NAMELESS; + isom_sample_info_t *info = (isom_sample_info_t *)entry->data; + while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + { + entry = entry->prev; + if( !entry + || !entry->data ) + return LSMASH_ERR_NAMELESS; + info = (isom_sample_info_t *)entry->data; + --sample_number; + } + *rap_number = sample_number + 1; + return 0; +} + +static inline int isom_get_closest_future_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number ) +{ + lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number++ ); + if( !entry + || !entry->data ) + return LSMASH_ERR_NAMELESS; + isom_sample_info_t *info = (isom_sample_info_t *)entry->data; + while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) + { + entry = entry->next; + if( !entry + || !entry->data ) + return LSMASH_ERR_NAMELESS; + info = (isom_sample_info_t *)entry->data; + ++sample_number; + } + *rap_number = sample_number - 1; + return 0; +} + +static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number ) +{ + if( !timeline ) + return LSMASH_ERR_NAMELESS; + int ret; + if( (ret = isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number )) < 0 + && (ret = isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number )) < 0 ) + return ret; + return 0; +} + +int lsmash_get_closest_random_accessible_point_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *rap_number ) +{ + if( sample_number == 0 || !rap_number ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( timeline->info_list->entry_count == 0 ) + { + *rap_number = sample_number; /* All LPCM is sync sample. */ + return 0; + } + return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number ); +} + +int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, + uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance ) +{ + if( sample_number == 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( timeline->info_list->entry_count == 0 ) + { + /* All LPCM is sync sample. */ + *rap_number = sample_number; + if( ra_flags ) + *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; + if( leading ) + *leading = 0; + if( distance ) + *distance = 0; + return 0; + } + int ret = isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number ); + if( ret < 0 ) + return ret; + isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, *rap_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + if( ra_flags ) + *ra_flags = info->prop.ra_flags; + if( leading ) + *leading = 0; + if( distance ) + *distance = 0; + if( sample_number < *rap_number ) + /* Impossible to desire to decode the sample of given number correctly. */ + return 0; + else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) ) + { + if( leading ) + { + /* Count leading samples. */ + uint32_t current_sample_number = *rap_number + 1; + uint64_t dts; + if( (ret = isom_get_dts_from_info_list( timeline, *rap_number, &dts )) < 0 ) + return ret; + uint64_t rap_cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset); + do + { + dts += info->duration; + if( rap_cts <= dts ) + break; /* leading samples of this random accessible point must not be present more. */ + info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, current_sample_number++ ); + if( !info ) + break; + uint64_t cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset); + if( rap_cts > cts ) + ++ *leading; + } while( 1 ); + } + if( !distance || sample_number == *rap_number ) + return 0; + /* Measure distance from the first closest non-recovery random accessible point to the second. */ + uint32_t prev_rap_number = *rap_number; + do + { + if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 ) + /* The previous random accessible point is not present. */ + return 0; + info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) ) + { + /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */ + *distance = *rap_number - prev_rap_number; + return 0; + } + } while( 1 ); + } + if( !distance ) + return 0; + /* Calculate roll-distance. */ + if( info->prop.pre_roll.distance ) + { + /* Pre-roll recovery */ + uint32_t prev_rap_number = *rap_number; + do + { + if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 + && *rap_number < info->prop.pre_roll.distance ) + { + /* The previous random accessible point is not present. + * And sample of given number might be not able to decoded correctly. */ + *distance = 0; + return 0; + } + if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number ) + { + /* + * |<---- pre-roll distance ---->| + * |<--------- distance -------->| + * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * ^ ^ ^ ^ + * random accessible point starting point random accessible point given sample + * (complete) + */ + *distance = info->prop.pre_roll.distance; + return 0; + } + else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) ) + { + /* + * |<------------ pre-roll distance ------------------>| + * |<------ distance ------->| + * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * ^ ^ ^ ^ + * random accessible point random accessible point given sample + * (starting point) (complete) + */ + *distance = *rap_number - prev_rap_number; + return 0; + } + } while( 1 ); + } + /* Post-roll recovery */ + if( sample_number >= info->prop.post_roll.complete ) + /* + * |<----- post-roll distance ----->| + * (distance = 0) + * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * ^ ^ ^ + * random accessible point complete given sample + * (starting point) + */ + return 0; + uint32_t prev_rap_number = *rap_number; + do + { + if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 ) + /* The previous random accessible point is not present. */ + return 0; + info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number ); + if( !info ) + return LSMASH_ERR_NAMELESS; + if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete ) + { + *distance = *rap_number - prev_rap_number; + return 0; + } + } while( 1 ); +} + +int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number ) +{ + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0; +} + +int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta ) +{ + if( !last_sample_delta ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1; +} + +int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta ) +{ + if( !sample_delta ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1; +} + +uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline ) + return 0; + return timeline->sample_count; +} + +uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline ) + return 0; + return timeline->max_sample_size; +} + +uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline ) + return 0; + return timeline->media_duration; +} + +int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID ) +{ + if( isom_check_initializer_present( dst ) < 0 + || isom_check_initializer_present( src ) < 0 ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_file_t *dst_file = dst->file->initializer; + isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID ); + if( !dst_file->moov + || !dst_file->moov->mvhd + || dst_file->moov->mvhd->timescale == 0 + || !dst_trak + || !dst_trak->mdia + || !dst_trak->mdia->mdhd + || dst_trak->mdia->mdhd->timescale == 0 + || !dst_trak->mdia->minf + || !dst_trak->mdia->minf->stbl ) + return LSMASH_ERR_NAMELESS; + if( dst_trak->edts + && dst_trak->edts->elst ) + lsmash_remove_entries( dst_trak->edts->elst->list, NULL ); + uint32_t src_movie_timescale; + uint32_t src_media_timescale; + uint64_t src_track_duration; + uint64_t src_media_duration; + int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time. + * Therefore, call this function as later as possible. */ + lsmash_entry_t *src_entry = NULL; + lsmash_file_t *src_file = src->file->initializer; + isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID ); + int src_fragmented = !!(src_file->flags & LSMASH_FILE_MODE_FRAGMENTED); + if( !src_trak + || !src_trak->edts + || !src_trak->edts->elst + || !src_trak->edts->elst->list + || src_fragmented ) + { + /* Get from constructed timeline instead of boxes. */ + isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID ); + if( src_timeline + && src_timeline->movie_timescale + && src_timeline->media_timescale ) + { + src_entry = src_timeline->edit_list->head; + if( !src_entry ) + return 0; + src_movie_timescale = src_timeline->movie_timescale; + src_media_timescale = src_timeline->media_timescale; + src_track_duration = src_timeline->track_duration; + src_media_duration = src_timeline->media_duration; + src_ctd_shift = src_timeline->ctd_shift; + } + else if( !src_fragmented ) + return LSMASH_ERR_NAMELESS; + } + if( !src_entry ) + { + if( !src_file->moov + || !src_file->moov->mvhd + || src_file->moov->mvhd->timescale == 0 + || !src_trak->tkhd + || !src_trak->mdia + || !src_trak->mdia->mdhd + || src_trak->mdia->mdhd->timescale == 0 + || !src_trak->mdia->minf + || !src_trak->mdia->minf->stbl ) + return LSMASH_ERR_NAMELESS; + src_entry = src_trak->edts->elst->list->head; + if( !src_entry ) + return 0; + src_movie_timescale = src_file->moov->mvhd->timescale; + src_media_timescale = src_trak->mdia->mdhd->timescale; + src_track_duration = src_trak->tkhd->duration; + src_media_duration = src_trak->mdia->mdhd->duration; + src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0; + } + /* Generate the edit list if absent in the destination. */ + if( (!dst_trak->edts && !isom_add_edts( dst_trak )) + || (!dst_trak->edts->elst && !isom_add_elst( dst_trak->edts )) ) + return LSMASH_ERR_NAMELESS; + uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale; + uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale; + int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0; + int32_t media_time_shift = src_ctd_shift - dst_ctd_shift; + lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list; + while( src_entry ) + { + isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data; + if( !src_data ) + return LSMASH_ERR_NAMELESS; + isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) ); + if( !dst_data ) + return LSMASH_ERR_MEMORY_ALLOC; + uint64_t segment_duration; + if( src_data->segment_duration == 0 && !dst_file->fragment ) + /* The implicit duration edit is not suitable for non-fragmented movie file. + * Set an appropriate duration from the source track. */ + segment_duration = src_fragmented + ? (uint64_t)(src_media_duration * ((double)src_movie_timescale / src_media_timescale)) + : src_track_duration; + else + segment_duration = src_data->segment_duration; + dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5; + dst_data->media_rate = src_data->media_rate; + if( src_data->media_time != ISOM_EDIT_MODE_EMPTY ) + dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5; + else + dst_data->media_time = ISOM_EDIT_MODE_EMPTY; + if( lsmash_add_entry( dst_list, dst_data ) < 0 ) + { + lsmash_free( dst_data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + src_entry = src_entry->next; + } + return 0; +} + +int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list ) +{ + if( !root || !root->file || !ts_list ) + return -1; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline ) + return LSMASH_ERR_NAMELESS; + if( timeline->info_list->entry_count == 0 ) + { + lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" ); + return LSMASH_ERR_PATCH_WELCOME; + } + if( ts_list->sample_count != timeline->info_list->entry_count ) + return LSMASH_ERR_INVALID_DATA; /* Number of samples must be same. */ + lsmash_media_ts_t *ts = ts_list->timestamp; + if( ts[0].dts ) + return LSMASH_ERR_INVALID_DATA; /* DTS must start from value zero. */ + /* Update DTSs. */ + uint32_t sample_count = ts_list->sample_count; + uint32_t i; + if( timeline->info_list->entry_count > 1 ) + { + i = 1; + lsmash_entry_t *entry = timeline->info_list->head; + isom_sample_info_t *info; + while( i < sample_count ) + { + info = (isom_sample_info_t *)entry->data; + if( !info || (ts[i].dts < ts[i - 1].dts) ) + return LSMASH_ERR_INVALID_DATA; + info->duration = ts[i].dts - ts[i - 1].dts; + entry = entry->next; + ++i; + } + if( i > 1 ) + { + if( !entry + || !entry->data ) + return LSMASH_ERR_INVALID_DATA; + /* Copy the previous duration. */ + ((isom_sample_info_t *)entry->data)->duration = info->duration; + } + else + return LSMASH_ERR_INVALID_DATA; /* Irregular case: sample_count this timeline has is incorrect. */ + } + else /* still image */ + ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX; + /* Update CTSs. + * ToDo: hint track must not have any sample_offset. */ + i = 0; + timeline->ctd_shift = 0; + for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next ) + { + isom_sample_info_t *info = (isom_sample_info_t *)entry->data; + if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts ) + timeline->ctd_shift = ts[i].dts - ts[i].cts; + info->offset = ts[i].cts - ts[i].dts; + ++i; + } + if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) ) + return LSMASH_ERR_INVALID_DATA; /* Don't allow composition to decode timeline shift. */ + return 0; +} + +int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list ) +{ + if( !ts_list ) + return LSMASH_ERR_FUNCTION_PARAM; + isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); + if( !timeline ) + return LSMASH_ERR_NAMELESS; + uint32_t sample_count = timeline->info_list->entry_count; + if( !sample_count ) + { + ts_list->sample_count = 0; + ts_list->timestamp = NULL; + return 0; + } + lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) ); + if( !ts ) + return LSMASH_ERR_MEMORY_ALLOC; + uint64_t dts = 0; + uint32_t i = 0; + if( timeline->info_list->entry_count ) + for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next ) + { + isom_sample_info_t *info = (isom_sample_info_t *)entry->data; + if( !info ) + { + lsmash_free( ts ); + return LSMASH_ERR_NAMELESS; + } + ts[i].dts = dts; + ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset); + dts += info->duration; + ++i; + } + else + for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next ) + { + isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data; + if( !bunch ) + { + lsmash_free( ts ); + return LSMASH_ERR_NAMELESS; + } + for( uint32_t j = 0; j < bunch->sample_count; j++ ) + { + ts[i].dts = dts; + ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)bunch->offset) : (dts + bunch->offset); + dts += bunch->duration; + ++i; + } + } + ts_list->sample_count = sample_count; + ts_list->timestamp = ts; + return 0; +} + +void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list ) +{ + if( !ts_list ) + return; + lsmash_freep( &ts_list->timestamp ); + ts_list->sample_count = 0; +} + +static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b ) +{ + int64_t diff = (int64_t)(a->dts - b->dts); + return diff > 0 ? 1 : (diff == 0 ? 0 : -1); +} + +void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list ) +{ + if( !ts_list ) + return; + qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts ); +} + +static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b ) +{ + int64_t diff = (int64_t)(a->cts - b->cts); + return diff > 0 ? 1 : (diff == 0 ? 0 : -1); +} + +void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list ) +{ + if( !ts_list ) + return; + qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts ); +} + +int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay ) +{ + if( !ts_list || !max_sample_delay ) + return LSMASH_ERR_FUNCTION_PARAM; + lsmash_media_ts_t *orig_ts = ts_list->timestamp; + lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) ); + if( !ts ) + return LSMASH_ERR_MEMORY_ALLOC; + ts_list->timestamp = ts; + *max_sample_delay = 0; + for( uint32_t i = 0; i < ts_list->sample_count; i++ ) + { + ts[i].cts = orig_ts[i].cts; /* for sorting */ + ts[i].dts = i; + } + lsmash_sort_timestamps_composition_order( ts_list ); + for( uint32_t i = 0; i < ts_list->sample_count; i++ ) + if( i < ts[i].dts ) + { + uint32_t sample_delay = ts[i].dts - i; + *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay ); + } + lsmash_free( ts ); + ts_list->timestamp = orig_ts; + return 0; +} + +#endif /* LSMASH_DEMUXER_ENABLED */ diff -Nru l-smash-1.9.1/core/timeline.h l-smash-2.3.0/core/timeline.h --- l-smash-1.9.1/core/timeline.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/timeline.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,28 @@ +/***************************************************************************** + * timeline.h: + ***************************************************************************** + * Copyright (C) 2011-2014 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_TIMELINE_H +#define LSMASH_TIMELINE_H + +void isom_remove_timelines( lsmash_file_t *file ); + +#endif /* LSMASH_TIMELINE_H */ diff -Nru l-smash-1.9.1/core/write.c l-smash-2.3.0/core/write.c --- l-smash-1.9.1/core/write.c 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/write.c 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,1617 @@ +/***************************************************************************** + * write.c: + ***************************************************************************** + * Copyright (C) 2010-2014 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 "common/internal.h" /* must be placed first */ + +#include +#include +#include + +#include "box.h" +#include "write.h" + +#include "codecs/mp4a.h" +#include "codecs/mp4sys.h" +#include "codecs/description.h" + +static int isom_write_children( lsmash_bs_t *bs, isom_box_t *box ) +{ + for( lsmash_entry_t *entry = box->extensions.head; entry; entry = entry->next ) + { + isom_box_t *child = (isom_box_t *)entry->data; + if( !child ) + continue; + int ret = isom_write_box( bs, child ); + if( ret < 0 ) + return ret; + } + return 0; +} + +static int isom_write_binary_coded_box( lsmash_bs_t *bs, isom_box_t *box ) +{ + lsmash_bs_put_bytes( bs, box->size, box->binary ); + return 0; +} + +static int isom_write_unknown_box( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_unknown_box_t *unknown_box = (isom_unknown_box_t *)box; + isom_bs_put_box_common( bs, unknown_box ); + if( unknown_box->unknown_field + && unknown_box->unknown_size ) + lsmash_bs_put_bytes( bs, unknown_box->unknown_size, unknown_box->unknown_field ); + return 0; +} + +static void isom_bs_put_qt_color_table( lsmash_bs_t *bs, isom_qt_color_table_t *color_table ) +{ + lsmash_bs_put_be32( bs, color_table->seed ); + lsmash_bs_put_be16( bs, color_table->flags ); + lsmash_bs_put_be16( bs, color_table->size ); + isom_qt_color_array_t *array = color_table->array; + if( array ) + for( uint16_t i = 0; i <= color_table->size; i++ ) + { + lsmash_bs_put_be16( bs, array[i].value ); + lsmash_bs_put_be16( bs, array[i].r ); + lsmash_bs_put_be16( bs, array[i].g ); + lsmash_bs_put_be16( bs, array[i].b ); + } +} + +static int isom_write_ctab( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_ctab_t *ctab = (isom_ctab_t *)box; + isom_bs_put_box_common( bs, ctab ); + isom_bs_put_qt_color_table( bs, &ctab->color_table ); + return 0; +} + +static int isom_write_tkhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_tkhd_t *tkhd = (isom_tkhd_t *)box; + /* Check the version. */ + if( (tkhd->file && !tkhd->file->undefined_64_ver) + && (tkhd->creation_time > UINT32_MAX + || tkhd->modification_time > UINT32_MAX + || tkhd->duration > UINT32_MAX) ) + tkhd->version = 1; + else + tkhd->version = 0; + /* Write. */ + 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, LSMASH_MIN( tkhd->creation_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, LSMASH_MIN( tkhd->modification_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, tkhd->track_ID ); + lsmash_bs_put_be32( bs, tkhd->reserved1 ); + lsmash_bs_put_be32( bs, LSMASH_MIN( tkhd->duration, UINT32_MAX ) ); + } + 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 0; +} + +static int isom_write_clef( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_clef_t *clef = (isom_clef_t *)box; + isom_bs_put_box_common( bs, clef ); + lsmash_bs_put_be32( bs, clef->width ); + lsmash_bs_put_be32( bs, clef->height ); + return 0; +} + +static int isom_write_prof( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_prof_t *prof = (isom_prof_t *)box; + isom_bs_put_box_common( bs, prof ); + lsmash_bs_put_be32( bs, prof->width ); + lsmash_bs_put_be32( bs, prof->height ); + return 0; +} + +static int isom_write_enof( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_enof_t *enof = (isom_enof_t *)box; + isom_bs_put_box_common( bs, enof ); + lsmash_bs_put_be32( bs, enof->width ); + lsmash_bs_put_be32( bs, enof->height ); + return 0; +} + +static int isom_write_tapt( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_elst( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_elst_t *elst = (isom_elst_t *)box; + assert( elst->list ); + if( elst->list->entry_count == 0 ) + return 0; + /* Check the version. */ + elst->version = 0; + if( elst->file && !elst->file->undefined_64_ver ) + 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 LSMASH_ERR_NAMELESS; + if( data->segment_duration > UINT32_MAX + || data->media_time > INT32_MAX + || data->media_time < INT32_MIN ) + elst->version = 1; + } + /* Remember to rewrite entries. */ + if( elst->file->fragment && !elst->file->bs->unseekable ) + elst->pos = elst->file->bs->written; + /* Write. */ + 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( elst->version ) + { + lsmash_bs_put_be64( bs, data->segment_duration ); + lsmash_bs_put_be64( bs, data->media_time ); + } + else + { + lsmash_bs_put_be32( bs, LSMASH_MIN( data->segment_duration, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, data->media_time < 0 ? (uint32_t)data->media_time : LSMASH_MIN( data->media_time, INT32_MAX ) ); + } + lsmash_bs_put_be32( bs, data->media_rate ); + } + return 0; +} + +static int isom_write_edts( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_tref( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_track_reference_type( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_tref_type_t *ref = (isom_tref_type_t *)box; + 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 0; +} + +static int isom_write_mdhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mdhd_t *mdhd = (isom_mdhd_t *)box; + /* Check the version. */ + if( (mdhd->file && !mdhd->file->undefined_64_ver) + && (mdhd->creation_time > UINT32_MAX + || mdhd->modification_time > UINT32_MAX + || mdhd->duration > UINT32_MAX) ) + mdhd->version = 1; + else + mdhd->version = 0; + /* Write. */ + 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, LSMASH_MIN( mdhd->creation_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, LSMASH_MIN( mdhd->modification_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, mdhd->timescale ); + lsmash_bs_put_be32( bs, LSMASH_MIN( mdhd->duration, UINT32_MAX ) ); + } + lsmash_bs_put_be16( bs, mdhd->language ); + lsmash_bs_put_be16( bs, mdhd->quality ); + return 0; +} + +static int isom_write_hdlr( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_hdlr_t *hdlr = (isom_hdlr_t *)box; + 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_length, hdlr->componentName ); + return 0; +} + +static int isom_write_vmhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_vmhd_t *vmhd = (isom_vmhd_t *)box; + 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 0; +} + +static int isom_write_smhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_smhd_t *smhd = (isom_smhd_t *)box; + isom_bs_put_box_common( bs, smhd ); + lsmash_bs_put_be16( bs, smhd->balance ); + lsmash_bs_put_be16( bs, smhd->reserved ); + return 0; +} + +static int isom_write_hmhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_hmhd_t *hmhd = (isom_hmhd_t *)box; + 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 0; +} + +static int isom_write_nmhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_nmhd_t *nmhd = (isom_nmhd_t *)box; + isom_bs_put_box_common( bs, nmhd ); + return 0; +} + +static int isom_write_gmin( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_gmin_t *gmin = (isom_gmin_t *)box; + 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 0; +} + +static int isom_write_text( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_text_t *text = (isom_text_t *)box; + isom_bs_put_box_common( bs, text ); + for( uint32_t i = 0; i < 9; i++ ) + lsmash_bs_put_be32( bs, text->matrix[i] ); + return 0; +} + +static int isom_write_gmhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_dref( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_dref_t *dref = (isom_dref_t *)box; + isom_bs_put_box_common( bs, dref ); + lsmash_bs_put_be32( bs, dref->list.entry_count ); + return 0; +} + +static int isom_write_url( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_dref_entry_t *url = (isom_dref_entry_t *)box; + isom_bs_put_box_common( bs, url ); + lsmash_bs_put_bytes( bs, url->location_length, url->location ); + return 0; +} + +static int isom_write_dinf( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_pasp( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_pasp_t *pasp = (isom_pasp_t *)box; + isom_bs_put_box_common( bs, pasp ); + lsmash_bs_put_be32( bs, pasp->hSpacing ); + lsmash_bs_put_be32( bs, pasp->vSpacing ); + return 0; +} + +static int isom_write_clap( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_clap_t *clap = (isom_clap_t *)box; + 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 0; +} + +static int isom_write_colr( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_colr_t *colr = (isom_colr_t *)box; + if( colr->color_parameter_type != ISOM_COLOR_PARAMETER_TYPE_NCLX + && colr->color_parameter_type != QT_COLOR_PARAMETER_TYPE_NCLC ) + 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 ); + if( colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) + lsmash_bs_put_byte( bs, (colr->full_range_flag << 7) | colr->reserved ); + return 0; +} + +static int isom_write_gama( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_gama_t *gama = (isom_gama_t *)box; + if( !gama->parent ) + return 0; + /* Note: 'gama' box is superseded by 'colr' box. + * Therefore, writers of QTFF should never write both 'colr' and 'gama' box into an Image Description. */ + if( isom_get_extension_box_format( &((isom_visual_entry_t *)gama->parent)->extensions, QT_BOX_TYPE_COLR ) ) + return 0; + isom_bs_put_box_common( bs, gama ); + lsmash_bs_put_be32( bs, gama->level ); + return 0; +} + +static int isom_write_fiel( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_fiel_t *fiel = (isom_fiel_t *)box; + isom_bs_put_box_common( bs, fiel ); + lsmash_bs_put_byte( bs, fiel->fields ); + lsmash_bs_put_byte( bs, fiel->detail ); + return 0; +} + +static int isom_write_cspc( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_cspc_t *cspc = (isom_cspc_t *)box; + isom_bs_put_box_common( bs, cspc ); + lsmash_bs_put_be32( bs, cspc->pixel_format ); + return 0; +} + +static int isom_write_sgbt( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_sgbt_t *sgbt = (isom_sgbt_t *)box; + isom_bs_put_box_common( bs, sgbt ); + lsmash_bs_put_byte( bs, sgbt->significantBits ); + return 0; +} + +static int isom_write_stsl( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stsl_t *stsl = (isom_stsl_t *)box; + 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 0; +} + +static int isom_write_esds( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_esds_t *esds = (isom_esds_t *)box; + isom_bs_put_box_common( bs, esds ); + mp4sys_update_descriptor_size( esds->ES ); + return mp4sys_write_descriptor( bs, esds->ES ); +} + +static int isom_write_btrt( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_btrt_t *btrt = (isom_btrt_t *)box; + 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 0; +} + +static int isom_write_glbl( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_glbl_t *glbl = (isom_glbl_t *)box; + isom_bs_put_box_common( bs, glbl ); + if( glbl->header_data && glbl->header_size ) + lsmash_bs_put_bytes( bs, glbl->header_size, glbl->header_data ); + return 0; +} + +static int isom_write_frma( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_frma_t *frma = (isom_frma_t *)box; + isom_bs_put_box_common( bs, frma ); + lsmash_bs_put_be32( bs, frma->data_format ); + return 0; +} + +static int isom_write_enda( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_enda_t *enda = (isom_enda_t *)box; + isom_bs_put_box_common( bs, enda ); + lsmash_bs_put_be16( bs, enda->littleEndian ); + return 0; +} + +static int isom_write_mp4a( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mp4a_t *mp4a = (isom_mp4a_t *)box; + isom_bs_put_box_common( bs, mp4a ); + lsmash_bs_put_be32( bs, mp4a->unknown ); + return 0; +} + +static int isom_write_chan( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_chan_t *chan = (isom_chan_t *)box; + 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 LSMASH_ERR_NAMELESS; + 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 0; +} + +static int isom_write_terminator( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_wave( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_visual_description( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_visual_entry_t *data = (isom_visual_entry_t *)box; + if( !data ) + return LSMASH_ERR_NAMELESS; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, 6, data->reserved ); + 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, 32, data->compressorname ); + lsmash_bs_put_be16( bs, data->depth ); + lsmash_bs_put_be16( bs, data->color_table_ID ); + if( data->color_table_ID == 0 ) + isom_bs_put_qt_color_table( bs, &data->color_table ); + return 0; +} + +static int isom_write_audio_description( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_audio_entry_t *data = (isom_audio_entry_t *)box; + if( !data ) + return LSMASH_ERR_NAMELESS; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, 6, data->reserved ); + 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 ); + } + return 0; +} + +#if 0 +static int isom_write_hint_description( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_hint_entry_t *data = (isom_hint_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_NAMELESS; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, 6, data->reserved ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + if( data->data && data->data_length ) + lsmash_bs_put_bytes( bs, data->data_length, data->data ); + return 0; +} + +static int isom_write_metadata_description( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_metadata_entry_t *data = (isom_metadata_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_NAMELESS; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, 6, data->reserved ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + return 0; +} +#endif + +static int isom_write_qt_text_description( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_qt_text_entry_t *data = (isom_qt_text_entry_t *)box; + if( !data ) + return LSMASH_ERR_NAMELESS; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, 6, data->reserved ); + 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_length, data->font_name ); + return 0; +} + +static int isom_write_ftab( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_ftab_t *ftab = (isom_ftab_t *)box; + assert( ftab->list ); + 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 LSMASH_ERR_NAMELESS; + 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_length, data->font_name ); + } + return 0; +} + +static int isom_write_tx3g_description( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_tx3g_entry_t *data = (isom_tx3g_entry_t *)box; + if( !data ) + return LSMASH_ERR_NAMELESS; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, 6, data->reserved ); + 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] ); + return 0; +} + +static int isom_write_stsd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stsd_t *stsd = (isom_stsd_t *)box; + isom_bs_put_box_common( bs, stsd ); + lsmash_bs_put_be32( bs, stsd->list.entry_count ); + return 0; +} + +static int isom_write_stts( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stts_t *stts = (isom_stts_t *)box; + assert( stts->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->sample_count ); + lsmash_bs_put_be32( bs, data->sample_delta ); + } + return 0; +} + +static int isom_write_ctts( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_ctts_t *ctts = (isom_ctts_t *)box; + assert( ctts->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->sample_count ); + lsmash_bs_put_be32( bs, data->sample_offset ); + } + return 0; +} + +static int isom_write_cslg( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_cslg_t *cslg = (isom_cslg_t *)box; + 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 0; +} + +static int isom_write_stsz( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stsz_t *stsz = (isom_stsz_t *)box; + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->entry_size ); + } + return 0; +} + +static int isom_write_stss( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stss_t *stss = (isom_stss_t *)box; + assert( stss->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->sample_number ); + } + return 0; +} + +static int isom_write_stps( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stps_t *stps = (isom_stps_t *)box; + assert( stps->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->sample_number ); + } + return 0; +} + +static int isom_write_sdtp( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_sdtp_t *sdtp = (isom_sdtp_t *)box; + assert( sdtp->list ); + 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 LSMASH_ERR_NAMELESS; + 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 0; +} + +static int isom_write_stsc( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stsc_t *stsc = (isom_stsc_t *)box; + assert( stsc->list ); + 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 LSMASH_ERR_NAMELESS; + 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 0; +} + +static int isom_write_co64( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stco_t *co64 = (isom_stco_t *)box; + assert( co64->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be64( bs, data->chunk_offset ); + } + return 0; +} + +static int isom_write_stco( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stco_t *stco = (isom_stco_t *)box; + if( stco->large_presentation ) + return isom_write_co64( bs, box ); + assert( stco->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->chunk_offset ); + } + return 0; +} + +static int isom_write_sgpd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_sgpd_t *sgpd = (isom_sgpd_t *)box; + assert( sgpd->list ); + 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 LSMASH_ERR_NAMELESS; + 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 : + case ISOM_GROUP_TYPE_PROL : + 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_t *)entry->data)->description_length ); + break; + } + } + return 0; +} + +static int isom_write_sbgp( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_sbgp_t *sbgp = (isom_sbgp_t *)box; + assert( sbgp->list ); + 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 LSMASH_ERR_NAMELESS; + lsmash_bs_put_be32( bs, data->sample_count ); + lsmash_bs_put_be32( bs, data->group_description_index ); + } + return 0; +} + +static int isom_write_stbl( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_minf( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_mdia( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_chpl( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_chpl_t *chpl = (isom_chpl_t *)box; + assert( chpl->list ); + 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 LSMASH_ERR_NAMELESS; + 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_length, data->chapter_name ); + } + return 0; +} + +static int isom_write_mean( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mean_t *mean = (isom_mean_t *)box; + isom_bs_put_box_common( bs, mean ); + if( mean->meaning_string && mean->meaning_string_length ) + lsmash_bs_put_bytes( bs, mean->meaning_string_length, mean->meaning_string ); + return 0; +} + +static int isom_write_name( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_name_t *name = (isom_name_t *)box; + isom_bs_put_box_common( bs, name ); + if( name->name && name->name_length ) + lsmash_bs_put_bytes( bs, name->name_length, name->name ); + return 0; +} + +static int isom_write_data( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_data_t *data = (isom_data_t *)box; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_be16( bs, data->reserved ); + lsmash_bs_put_byte( bs, data->type_set_identifier ); + lsmash_bs_put_byte( bs, data->type_code ); + lsmash_bs_put_be32( bs, data->the_locale ); + if( data->value && data->value_length ) + lsmash_bs_put_bytes( bs, data->value_length, data->value ); + return 0; +} + +static int isom_write_metaitem( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_ilst( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_meta( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_cprt( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_cprt_t *cprt = (isom_cprt_t *)box; + isom_bs_put_box_common( bs, cprt ); + lsmash_bs_put_be16( bs, cprt->language ); + lsmash_bs_put_bytes( bs, cprt->notice_length, cprt->notice ); + return 0; +} + +static int isom_write_udta( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_trak( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_iods( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_iods_t *iods = (isom_iods_t *)box; + isom_bs_put_box_common( bs, iods ); + mp4sys_update_descriptor_size( iods->OD ); + return mp4sys_write_descriptor( bs, iods->OD ); +} + +static int isom_write_mvhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mvhd_t *mvhd = (isom_mvhd_t *)box; + /* Check the version. */ + if( (mvhd->file && !mvhd->file->undefined_64_ver) + && (mvhd->creation_time > UINT32_MAX + || mvhd->modification_time > UINT32_MAX + || mvhd->duration > UINT32_MAX) ) + mvhd->version = 1; + else + mvhd->version = 0; + /* Write. */ + 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, LSMASH_MIN( mvhd->creation_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, LSMASH_MIN( mvhd->modification_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, mvhd->timescale ); + lsmash_bs_put_be32( bs, LSMASH_MIN( mvhd->duration, UINT32_MAX ) ); + } + 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 0; +} + +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_box_t *box ) +{ + if( box->manager & LSMASH_PLACEHOLDER ) + { + /* Movie Extends Header Box is not written immediately. + * It's done after finishing all movie fragments. + * 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. */ + box->pos = box->file->bs->written; + lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE + 12 ); + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE.fourcc ); + lsmash_bs_put_be32( bs, 0 ); + lsmash_bs_put_be64( bs, 0 ); + } + else + { + isom_mehd_t *mehd = (isom_mehd_t *)box; + //mehd->version = mehd->fragment_duration > UINT32_MAX ? 1 : 0; + 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, LSMASH_MIN( mehd->fragment_duration, UINT32_MAX ) ); + } + return 0; +} + +static int isom_write_trex( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_trex_t *trex = (isom_trex_t *)box; + 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 0; +} + +static int isom_write_mvex( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_mfhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mfhd_t *mfhd = (isom_mfhd_t *)box; + isom_bs_put_box_common( bs, mfhd ); + lsmash_bs_put_be32( bs, mfhd->sequence_number ); + return 0; +} + +static int isom_write_tfhd( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_tfhd_t *tfhd = (isom_tfhd_t *)box; + 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 0; +} + +static int isom_write_tfdt( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_tfdt_t *tfdt = (isom_tfdt_t *)box; + /* Check the version. */ + tfdt->version = tfdt->baseMediaDecodeTime > UINT32_MAX ? 1 : 0; + /* Write. */ + isom_bs_put_box_common( bs, tfdt ); + if( tfdt->version == 1 ) + lsmash_bs_put_be64( bs, tfdt->baseMediaDecodeTime ); + else + lsmash_bs_put_be32( bs, tfdt->baseMediaDecodeTime ); + return 0; +} + +static int isom_write_trun( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_trun_t *trun = (isom_trun_t *)box; + 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 LSMASH_ERR_NAMELESS; + 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 0; +} + +static int isom_write_traf( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_moof( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_tfra( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_tfra_t *tfra = (isom_tfra_t *)box; + 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[ tfra->version == 1 ? 4 : 3 ]; + void (*bs_put_moof_offset) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->version == 1 ? 4 : 3 ]; + 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 LSMASH_ERR_NAMELESS; + 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 0; +} + +static int isom_write_mfro( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mfro_t *mfro = (isom_mfro_t *)box; + isom_bs_put_box_common( bs, mfro ); + lsmash_bs_put_be32( bs, mfro->length ); /* determined at isom_write_mfra(). */ + return 0; +} + +static int isom_write_mfra( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mfra_t *mfra = (isom_mfra_t *)box; + if( mfra->mfro ) + mfra->mfro->length = mfra->size; + isom_bs_put_box_common( bs, mfra ); + return 0; +} + +static int isom_write_mdat( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_mdat_t *mdat = (isom_mdat_t *)box; + lsmash_file_t *file = mdat->file; + /* If any fragment, write the Media Data Box all at once. */ + if( file->fragment ) + { + /* Write the size and type fields of the Media Data Box. */ + mdat->size = ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size; + if( mdat->size > UINT32_MAX ) + mdat->size += 8; /* large_size */ + isom_bs_put_box_common( bs, mdat ); + /* Write the samples in the current movie fragment. */ + for( lsmash_entry_t *entry = file->fragment->pool->head; entry; entry = entry->next ) + { + isom_sample_pool_t *pool = (isom_sample_pool_t *)entry->data; + if( !pool ) + return LSMASH_ERR_NAMELESS; + lsmash_bs_put_bytes( bs, pool->size, pool->data ); + } + mdat->media_size = file->fragment->pool_size; + return 0; + } + if( mdat->manager & LSMASH_PLACEHOLDER ) + { + /* Write the placeholder for large size. */ + if( !file->free && !isom_add_free( file ) ) + return LSMASH_ERR_NAMELESS; + isom_free_t *skip = file->free; + skip->pos = bs->offset; + skip->size = ISOM_BASEBOX_COMMON_SIZE; + skip->manager |= LSMASH_PLACEHOLDER; + int ret = isom_write_box( bs, (isom_box_t *)skip ); + if( ret < 0 ) + return ret; + /* Write an incomplete Media Data Box. */ + mdat->pos = bs->offset; + mdat->size = ISOM_BASEBOX_COMMON_SIZE; + mdat->manager |= LSMASH_INCOMPLETE_BOX; + mdat->manager &= ~LSMASH_PLACEHOLDER; + isom_bs_put_box_common( bs, mdat ); + return 0; + } + if( !bs->unseekable ) + { + /* Write the actual size. */ + uint64_t current_pos = bs->offset; + mdat->size = ISOM_BASEBOX_COMMON_SIZE + mdat->media_size; + if( mdat->size > UINT32_MAX ) + { + /* The placeholder is overwritten by the Media Data Box. */ + assert( file->free ); + mdat->pos = file->free->pos; + mdat->size += file->free->size; + isom_remove_box_by_itself( file->free ); + } + lsmash_bs_write_seek( bs, mdat->pos, SEEK_SET ); + isom_bs_put_box_common( bs, mdat ); + /* isom_write_box() also calls lsmash_bs_flush_buffer() but it must do nothing. */ + int ret = lsmash_bs_flush_buffer( bs ); + lsmash_bs_write_seek( bs, current_pos, SEEK_SET ); + return ret; + } + return LSMASH_ERR_NAMELESS; +} + +static int isom_write_ftyp( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_ftyp_t *ftyp = (isom_ftyp_t *)box; + if( ftyp->brand_count == 0 ) + return 0; + isom_bs_put_box_common( bs, ftyp ); + lsmash_bs_put_be32( bs, ftyp->major_brand ); + lsmash_bs_put_be32( bs, ftyp->minor_version ); + for( uint32_t i = 0; i < ftyp->brand_count; i++ ) + lsmash_bs_put_be32( bs, ftyp->compatible_brands[i] ); + return 0; +} + +static int isom_write_moov( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_box_common( bs, box ); + return 0; +} + +static int isom_write_free( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_free_t *skip = (isom_free_t *)box; + isom_bs_put_box_common( bs, skip ); + if( skip->data && skip->length ) + lsmash_bs_put_bytes( bs, skip->length, skip->data ); + return 0; +} + +static int isom_write_sidx( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_sidx_t *sidx = (isom_sidx_t *)box; + /* Check the version. */ + if( sidx->earliest_presentation_time > UINT32_MAX + || sidx->first_offset > UINT32_MAX ) + sidx->version = 1; + else + sidx->version = 0; + /* Write. */ + isom_bs_put_box_common( bs, sidx ); + lsmash_bs_put_be32( bs, sidx->reference_ID ); + lsmash_bs_put_be32( bs, sidx->timescale ); + if( sidx->version == 0 ) + { + lsmash_bs_put_be32( bs, LSMASH_MIN( sidx->earliest_presentation_time, UINT32_MAX ) ); + lsmash_bs_put_be32( bs, LSMASH_MIN( sidx->first_offset, UINT32_MAX ) ); + } + else + { + lsmash_bs_put_be64( bs, sidx->earliest_presentation_time ); + lsmash_bs_put_be64( bs, sidx->first_offset ); + } + lsmash_bs_put_be16( bs, sidx->reserved ); + lsmash_bs_put_be16( bs, sidx->reference_count ); + for( lsmash_entry_t *entry = sidx->list->head; entry; entry = entry->next ) + { + isom_sidx_referenced_item_t *data = (isom_sidx_referenced_item_t *)entry->data; + if( !data ) + return LSMASH_ERR_NAMELESS; + uint32_t temp32; + temp32 = (data->reference_type << 31) + | data->reference_size; + lsmash_bs_put_be32( bs, temp32 ); + lsmash_bs_put_be32( bs, data->subsegment_duration ); + temp32 = (data->starts_with_SAP << 31) + | (data->SAP_type << 28) + | data->SAP_delta_time; + lsmash_bs_put_be32( bs, temp32 ); + } + return 0; +} + +int isom_write_box( lsmash_bs_t *bs, isom_box_t *box ) +{ + assert( bs ); + /* Don't write any incomplete or already written box to a file. */ + if( !box || !box->write + || (bs->stream && (box->manager & (LSMASH_INCOMPLETE_BOX | LSMASH_WRITTEN_BOX))) ) + return 0; + int ret = box->write( bs, box ); + if( ret < 0 ) + return ret; + if( bs->stream ) + { + if( (ret = lsmash_bs_flush_buffer( bs )) < 0 ) + return ret; + /* Don't write any child box if this box is a placeholder or an incomplete box. */ + if( box->manager & (LSMASH_PLACEHOLDER | LSMASH_INCOMPLETE_BOX) ) + return 0; + else + box->manager |= LSMASH_WRITTEN_BOX; + } + return isom_write_children( bs, box ); +} + +void isom_set_box_writer( isom_box_t *box ) +{ + if( box->manager & LSMASH_BINARY_CODED_BOX ) + { + box->write = isom_write_binary_coded_box; + return; + } + else if( box->manager & LSMASH_UNKNOWN_BOX ) + { + box->write = isom_write_unknown_box; + return; + } + assert( box->parent ); + isom_box_t *parent = box->parent; + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) + { + /* Check whether CODEC is RAW Video/Audio encapsulated in QTFF. */ + if( parent->parent && parent->parent->parent ) + { + isom_minf_t *minf = (isom_minf_t *)parent->parent->parent; + if( minf->vmhd ) + box->write = isom_write_visual_description; + else if( minf->smhd ) + box->write = isom_write_audio_description; + if( box->write ) + return; + } + if( lsmash_check_box_type_identical( box->type, QT_CODEC_TYPE_TEXT_TEXT ) ) + box->write = isom_write_qt_text_description; + else if( lsmash_check_box_type_identical( box->type, ISOM_CODEC_TYPE_TX3G_TEXT ) ) + box->write = isom_write_tx3g_description; + if( box->write ) + return; + } + if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) + { + if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_FRMA ) ) box->write = isom_write_frma; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ENDA ) ) box->write = isom_write_enda; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_MP4A ) ) box->write = isom_write_mp4a; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ESDS ) ) box->write = isom_write_esds; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_CHAN ) ) box->write = isom_write_chan; + else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_TERMINATOR ) ) box->write = isom_write_terminator; + else box->write = NULL; + return; + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TREF ) ) + { + box->write = isom_write_track_reference_type; + return; + } + static struct box_writer_table_tag + { + lsmash_box_type_t type; + isom_extension_writer_t writer_func; + } box_writer_table[128] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } }; + if( !box_writer_table[0].writer_func ) + { + /* Initialize the table. */ + int i = 0; +#define ADD_BOX_WRITER_TABLE_ELEMENT( type, reader_func ) \ + box_writer_table[i++] = (struct box_writer_table_tag){ type, reader_func } + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTYP, isom_write_ftyp ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STYP, isom_write_ftyp ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SIDX, isom_write_sidx ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOV, isom_write_moov ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVHD, isom_write_mvhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_IODS, isom_write_iods ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, isom_write_ctab ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, isom_write_esds ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAK, isom_write_trak ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TKHD, isom_write_tkhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_TAPT, isom_write_tapt ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CLEF, isom_write_clef ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_PROF, isom_write_prof ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_ENOF, isom_write_enof ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_EDTS, isom_write_edts ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_ELST, isom_write_elst ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREF, isom_write_tref ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDIA, isom_write_mdia ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDHD, isom_write_mdhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_HDLR, isom_write_hdlr ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MINF, isom_write_minf ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_VMHD, isom_write_vmhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SMHD, isom_write_smhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_HMHD, isom_write_hmhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_NMHD, isom_write_nmhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GMHD, isom_write_gmhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GMIN, isom_write_gmin ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_TEXT, isom_write_text ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_DINF, isom_write_dinf ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_DREF, isom_write_dref ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_URL, isom_write_url ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STBL, isom_write_stbl ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSD, isom_write_stsd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, isom_write_btrt ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_write_colr ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_write_colr ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_write_clap ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_write_pasp ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_write_glbl ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_write_gama ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_write_fiel ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_write_cspc ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_write_sgbt ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_write_stsl ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_write_wave ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_MP4A, isom_write_mp4a ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_write_chan ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, isom_write_ftab ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STTS, isom_write_stts ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CTTS, isom_write_ctts ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CSLG, isom_write_cslg ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSS, isom_write_stss ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_STPS, isom_write_stps ); + 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_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 ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SBGP, isom_write_sbgp ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_UDTA, isom_write_udta ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CHPL, isom_write_chpl ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVEX, isom_write_mvex ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MEHD, isom_write_mehd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREX, isom_write_trex ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOF, isom_write_moof ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFHD, isom_write_mfhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAF, isom_write_traf ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFHD, isom_write_tfhd ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFDT, isom_write_tfdt ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRUN, isom_write_trun ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDAT, isom_write_mdat ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_FREE, isom_write_free ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SKIP, isom_write_free ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_META, isom_write_meta ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_META, isom_write_meta ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_ILST, isom_write_ilst ); + ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_ILST, isom_write_ilst ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRA, isom_write_mfra ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFRA, isom_write_tfra ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRO, isom_write_mfro ); + ADD_BOX_WRITER_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); +#undef ADD_BOX_WRITER_TABLE_ELEMENT + } + for( int i = 0; box_writer_table[i].writer_func; i++ ) + if( lsmash_check_box_type_identical( box->type, box_writer_table[i].type ) ) + { + box->write = box_writer_table[i].writer_func; + return; + } + if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_ILST ) + || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) + { + box->write = isom_write_metaitem; + return; + } + if( parent->parent && lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_ILST ) ) + { + if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_MEAN ) ) + box->write = isom_write_mean; + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_NAME ) ) + box->write = isom_write_name; + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_DATA ) ) + box->write = isom_write_data; + if( box->write ) + return; + } + else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_CPRT ) ) + { + /* Avoid confusing udta.cprt with ilst.cprt. */ + box->write = isom_write_cprt; + return; + } + box->write = isom_write_unknown_box; +} diff -Nru l-smash-1.9.1/core/write.h l-smash-2.3.0/core/write.h --- l-smash-1.9.1/core/write.h 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/core/write.h 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,29 @@ +/***************************************************************************** + * write.h: + ***************************************************************************** + * Copyright (C) 2011-2014 L-SMASH project + * + * Authors: Hiroki Taniura + * + * 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_WRITE_H +#define LSMASH_WRITE_H + +int isom_write_box( lsmash_bs_t *bs, isom_box_t *box ); +void isom_set_box_writer( isom_box_t *box ); + +#endif diff -Nru l-smash-1.9.1/debian/changelog l-smash-2.3.0/debian/changelog --- l-smash-1.9.1/debian/changelog 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/changelog 2014-11-27 20:34:51.000000000 +0000 @@ -1,3 +1,9 @@ +l-smash (2.3.0-1~trusty) trusty; urgency=medium + + * New upstream release + + -- Doug McMahon Thu, 27 Nov 2014 10:30:33 -0500 + l-smash (1.9.1-1~trusty) trusty; urgency=medium * For media ppa diff -Nru l-smash-1.9.1/debian/control l-smash-2.3.0/debian/control --- l-smash-1.9.1/debian/control 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/control 2014-11-27 15:29:09.000000000 +0000 @@ -2,15 +2,15 @@ Section: libs Priority: extra Maintainer: Doug McMahon -Bugs: +Bugs: Homepage: https://code.google.com/p/l-smash/ Standards-Version: 3.9.5 -Build-Depends: debhelper (>= 9), quilt +Build-Depends: debhelper (>= 9) -Package: liblsmash1 +Package: liblsmash2 Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Loyal to Spec of Mpeg4 and Ad-hoc Simple Hackwork. +Description: Loyal to Spec of Mpeg4 and Ad-hoc Simple Hackwork Yet another opensource mp4 handler. . This package contains runtime files. @@ -18,8 +18,8 @@ Package: liblsmash-dev Architecture: any Section: libdevel -Depends: liblsmash1 (= ${binary:Version}), ${misc:Depends} -Description: Loyal to Spec of Mpeg4 and Ad-hoc Simple Hackwork. +Depends: liblsmash2 (= ${binary:Version}), ${misc:Depends} +Description: Loyal to Spec of Mpeg4 and Ad-hoc Simple Hackwork Yet another opensource mp4 handler . This package contains development files. @@ -28,7 +28,7 @@ Architecture: any Section: utils Depends: ${shlibs:Depends}, ${misc:Depends} -Description: MP4 muxer and other tools. +Description: MP4 muxer and other tools This package contains various utilities : . o boxdumper - structual analyzer. diff -Nru l-smash-1.9.1/debian/copyright l-smash-2.3.0/debian/copyright --- l-smash-1.9.1/debian/copyright 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/copyright 2014-11-27 15:15:44.000000000 +0000 @@ -3,8 +3,8 @@ Source: https://code.google.com/p/l-smash/source/checkout Files: * -Copyright: 2010-2013 L-SMASH project -License: +Copyright: 2010-2014 L-SMASH project +License: permissive 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. diff -Nru l-smash-1.9.1/debian/liblsmash1.install l-smash-2.3.0/debian/liblsmash1.install --- l-smash-1.9.1/debian/liblsmash1.install 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/liblsmash1.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/liblsmash.so.* diff -Nru l-smash-1.9.1/debian/liblsmash2.install l-smash-2.3.0/debian/liblsmash2.install --- l-smash-1.9.1/debian/liblsmash2.install 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/debian/liblsmash2.install 2014-11-27 15:15:44.000000000 +0000 @@ -0,0 +1 @@ +usr/lib/liblsmash.so.* diff -Nru l-smash-1.9.1/debian/patches/01_lib-name.diff l-smash-2.3.0/debian/patches/01_lib-name.diff --- l-smash-1.9.1/debian/patches/01_lib-name.diff 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/patches/01_lib-name.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ ---- a/configure -+++ b/configure -@@ -105,7 +105,7 @@ TOOLS="" - - CFLAGS="-Wshadow -Wall -std=gnu99 -I. -I$SRCDIR" - LDFLAGS="-L." --SO_LDFLAGS='-shared -Wl,-soname,$@' -+SO_LDFLAGS='-shared -Wl,-soname,liblsmash.so.1' - LIBS="-lm" - - DEMUXER="enabled" diff -Nru l-smash-1.9.1/debian/patches/series l-smash-2.3.0/debian/patches/series --- l-smash-1.9.1/debian/patches/series 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01_lib-name.diff diff -Nru l-smash-1.9.1/debian/rules l-smash-2.3.0/debian/rules --- l-smash-1.9.1/debian/rules 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/rules 2014-11-27 15:30:16.000000000 +0000 @@ -1,7 +1,17 @@ #!/usr/bin/make -f +# +# See " grep LSMASH_VERSION_ lsmash.h " for soname number +# + +LSMASH_VERSION_MAJOR=$(shell grep -m1 LSMASH_VERSION_MAJOR lsmash.h | awk '{printf "%1d", $$3}') +LSMASH_VERSION_MINOR=$(shell grep -m1 LSMASH_VERSION_MINOR lsmash.h | awk '{printf "%1d", $$3}') +LSMASH_VERSION_MICRO=$(shell grep -m1 LSMASH_VERSION_MICRO lsmash.h | awk '{printf "%1d", $$3}') + +LSMASH_SONAME=$(LSMASH_VERSION_MAJOR).$(LSMASH_VERSION_MINOR).$(LSMASH_VERSION_MICRO) + %: - dh $@ --parallel --with quilt + dh $@ --parallel override_dh_auto_configure: ./configure --prefix=/usr --enable-shared \ @@ -10,13 +20,10 @@ override_dh_auto_install: dh_auto_install -# -# See " grep LSMASH_VERSION_ lsmash.h " for soname number -# - - mv debian/tmp/usr/lib/liblsmash.so debian/tmp/usr/lib/liblsmash.so.1.0.0 - cd debian/tmp/usr/lib && ln -s liblsmash.so.1.0.0 liblsmash.so.1 - cd debian/tmp/usr/lib && ln -s liblsmash.so.1.0.0 liblsmash.so + rm debian/tmp/usr/lib/liblsmash.so + mv debian/tmp/usr/lib/liblsmash.so.$(LSMASH_VERSION_MAJOR) debian/tmp/usr/lib/liblsmash.so.$(LSMASH_SONAME) + cd debian/tmp/usr/lib && ln -s liblsmash.so.$(LSMASH_SONAME) liblsmash.so.$(LSMASH_VERSION_MAJOR) + cd debian/tmp/usr/lib && ln -s liblsmash.so.$(LSMASH_SONAME) liblsmash.so override_dh_builddeb: dh_builddeb -- -Zxz diff -Nru l-smash-1.9.1/debian/source/format l-smash-2.3.0/debian/source/format --- l-smash-1.9.1/debian/source/format 2014-11-27 21:23:13.000000000 +0000 +++ l-smash-2.3.0/debian/source/format 2014-02-26 15:25:29.000000000 +0000 @@ -1 +1 @@ -1.0 +3.0 (quilt) diff -Nru l-smash-1.9.1/debian/watch l-smash-2.3.0/debian/watch --- l-smash-1.9.1/debian/watch 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/debian/watch 2014-11-27 15:15:44.000000000 +0000 @@ -0,0 +1,4 @@ +version=3 + +https://github.com/l-smash/l-smash/releases \ +/l-smash/l-smash/releases/tag/v(.*) diff -Nru l-smash-1.9.1/description.c l-smash-2.3.0/description.c --- l-smash-1.9.1/description.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/description.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2842 +0,0 @@ -/***************************************************************************** - * description.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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 "box.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "description.h" - -typedef isom_wave_t lsmash_qt_decoder_parameters_t; - -static void global_destruct_specific_data( void *data ) -{ - if( !data ) - return; - lsmash_codec_global_header_t *global = (lsmash_codec_global_header_t *)data; - if( global->header_data ) - lsmash_free( global->header_data ); - lsmash_free( global ); -} - -static int isom_is_qt_video( lsmash_codec_type_t type ) -{ - return lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_2VUY_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCH_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCN_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCS_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_APCO_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_AP4H_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CFHD_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_CIVD_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVC_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVCP_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVPP_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DV5N_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DV5P_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH2_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH3_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH5_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVH6_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVHP_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVHQ_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DV10_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVOO_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVOR_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVTV_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_DVVT_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_FLIC_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_GIF_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_H261_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_H263_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_HD10_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_JPEG_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_M105_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MJPA_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_MJPB_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_PNG_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_PNTG_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RAW_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RLE_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_RPZA_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR0_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR1_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR2_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR3_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SHR4_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SVQ1_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_SVQ3_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TGA_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_TIFF_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULRA_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULRG_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULY2_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULY0_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULH2_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_ULH0_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V210_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V216_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V308_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V408_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_V410_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_YUV2_VIDEO ) - || lsmash_check_codec_type_identical( type, QT_CODEC_TYPE_WRLE_VIDEO ); -} - -static int isom_is_nalff( lsmash_codec_type_t type ) -{ - return lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC1_VIDEO ) - || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC2_VIDEO ) - || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC3_VIDEO ) - || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVC4_VIDEO ) - || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_AVCP_VIDEO ) - || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_HVC1_VIDEO ) - || lsmash_check_codec_type_identical( type, ISOM_CODEC_TYPE_HEV1_VIDEO ); -} - -int lsmash_convert_crop_into_clap( lsmash_crop_t crop, uint32_t width, uint32_t height, lsmash_clap_t *clap ) -{ - if( !clap || crop.top.d == 0 || crop.bottom.d == 0 || crop.left.d == 0 || crop.right.d == 0 ) - return -1; - uint64_t vertical_crop_lcm = lsmash_get_lcm( crop.top.d, crop.bottom.d ); - uint64_t horizontal_crop_lcm = lsmash_get_lcm( crop.left.d, crop.right.d ); - lsmash_rational_u64_t clap_height; - lsmash_rational_u64_t clap_width; - lsmash_rational_s64_t clap_horizontal_offset; - lsmash_rational_s64_t clap_vertical_offset; - clap_height.d = vertical_crop_lcm; - clap_width.d = horizontal_crop_lcm; - clap_horizontal_offset.d = 2 * vertical_crop_lcm; - clap_vertical_offset.d = 2 * horizontal_crop_lcm; - clap_height.n = height * vertical_crop_lcm - - (crop.top.n * (vertical_crop_lcm / crop.top.d) + crop.bottom.n * (vertical_crop_lcm / crop.bottom.d)); - clap_width.n = width * horizontal_crop_lcm - - (crop.left.n * (horizontal_crop_lcm / crop.left.d) + crop.right.n * (horizontal_crop_lcm / crop.right.d)); - clap_horizontal_offset.n = (int64_t)(crop.left.n * (horizontal_crop_lcm / crop.left.d)) - - crop.right.n * (horizontal_crop_lcm / crop.right.d); - clap_vertical_offset.n = (int64_t)(crop.top.n * (vertical_crop_lcm / crop.top.d)) - - crop.bottom.n * (vertical_crop_lcm / crop.bottom.d); - lsmash_reduce_fraction( &clap_height.n, &clap_height.d ); - lsmash_reduce_fraction( &clap_width.n, &clap_width.d ); - lsmash_reduce_fraction_su( &clap_vertical_offset.n, &clap_vertical_offset.d ); - lsmash_reduce_fraction_su( &clap_horizontal_offset.n, &clap_horizontal_offset.d ); - clap->height = (lsmash_rational_u32_t){ clap_height.n, clap_height.d }; - clap->width = (lsmash_rational_u32_t){ clap_width.n, clap_width.d }; - clap->vertical_offset = (lsmash_rational_s32_t){ clap_vertical_offset.n, clap_vertical_offset.d }; - clap->horizontal_offset = (lsmash_rational_s32_t){ clap_horizontal_offset.n, clap_horizontal_offset.d }; - return 0; -} - -int lsmash_convert_clap_into_crop( lsmash_clap_t clap, uint32_t width, uint32_t height, lsmash_crop_t *crop ) -{ - if( !crop || clap.height.d == 0 || clap.vertical_offset.d == 0 || clap.width.d == 0 || clap.horizontal_offset.d == 0 ) - return -1; - uint64_t clap_vertical_lcm = lsmash_get_lcm( clap.height.d, clap.vertical_offset.d ); - uint64_t clap_horizontal_lcm = lsmash_get_lcm( clap.width.d, clap.horizontal_offset.d ); - lsmash_rational_u64_t crop_top; - lsmash_rational_u64_t crop_bottom; - lsmash_rational_u64_t crop_left; - lsmash_rational_u64_t crop_right; - crop_top.d = 2 * clap_vertical_lcm; - crop_bottom.d = 2 * clap_vertical_lcm; - crop_left.d = 2 * clap_horizontal_lcm; - crop_right.d = 2 * clap_horizontal_lcm; - crop_top.n = (height * crop_top.d - clap.height.n * (crop_top.d / clap.height.d)) / 2 - + clap.vertical_offset.n * (crop_top.d / clap.vertical_offset.d); - crop_bottom.n = (height * crop_bottom.d - clap.height.n * (crop_bottom.d / clap.height.d)) / 2 - - clap.vertical_offset.n * (crop_bottom.d / clap.vertical_offset.d); - crop_left.n = (width * crop_left.d - clap.width.n * (crop_left.d / clap.width.d)) / 2 - + clap.horizontal_offset.n * (crop_left.d / clap.horizontal_offset.d); - crop_right.n = (width * crop_right.d - clap.width.n * (crop_right.d / clap.width.d)) / 2 - - clap.horizontal_offset.n * (crop_right.d / clap.horizontal_offset.d); - lsmash_reduce_fraction( &crop_top.n, &crop_top.d ); - lsmash_reduce_fraction( &crop_bottom.n, &crop_bottom.d ); - lsmash_reduce_fraction( &crop_left.n, &crop_left.d ); - lsmash_reduce_fraction( &crop_right.n, &crop_right.d ); - crop->top = (lsmash_rational_u32_t){ crop_top.n, crop_top.d }; - crop->bottom = (lsmash_rational_u32_t){ crop_bottom.n, crop_bottom.d }; - crop->left = (lsmash_rational_u32_t){ crop_left.n, crop_left.d }; - crop->right = (lsmash_rational_u32_t){ crop_right.n, crop_right.d }; - return 0; -} - -static void isom_destruct_nothing( void *data ) -{ - /* Do nothing. */; -} - -static int isom_initialize_structured_codec_specific_data( lsmash_codec_specific_t *specific ) -{ - extern void mp4sys_destruct_decoder_config( void * ); - extern void h264_destruct_specific_data( void * ); - extern void hevc_destruct_specific_data( void * ); - extern void vc1_destruct_specific_data( void * ); - extern void dts_destruct_specific_data( void * ); - switch( specific->type ) - { - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : - specific->size = sizeof(lsmash_mp4sys_decoder_parameters_t); - specific->destruct = mp4sys_destruct_decoder_config; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 : - specific->size = sizeof(lsmash_h264_specific_parameters_t); - specific->destruct = h264_destruct_specific_data; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC : - specific->size = sizeof(lsmash_hevc_specific_parameters_t); - specific->destruct = hevc_destruct_specific_data; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 : - specific->size = sizeof(lsmash_vc1_specific_parameters_t); - specific->destruct = vc1_destruct_specific_data; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : - specific->size = sizeof(lsmash_ac3_specific_parameters_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : - specific->size = sizeof(lsmash_eac3_specific_parameters_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : - specific->size = sizeof(lsmash_dts_specific_parameters_t); - specific->destruct = dts_destruct_specific_data; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : - specific->size = sizeof(lsmash_alac_specific_parameters_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE : - specific->size = sizeof(lsmash_isom_sample_scale_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE : - specific->size = sizeof(lsmash_h264_bitrate_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON : - specific->size = sizeof(lsmash_qt_video_common_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : - specific->size = sizeof(lsmash_qt_audio_common_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : - specific->size = sizeof(lsmash_qt_audio_format_specific_flags_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : - specific->size = sizeof(lsmash_codec_global_header_t); - specific->destruct = global_destruct_specific_data; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO : - specific->size = sizeof(lsmash_qt_field_info_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT : - specific->size = sizeof(lsmash_qt_pixel_format_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS : - specific->size = sizeof(lsmash_qt_significant_bits_t); - specific->destruct = lsmash_free; - break; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : - specific->size = sizeof(lsmash_qt_audio_channel_layout_t); - specific->destruct = lsmash_free; - break; - default : - specific->size = 0; - specific->destruct = isom_destruct_nothing; - return 0; - } - specific->data.structured = lsmash_malloc_zero( specific->size ); - if( !specific->data.structured ) - { - specific->size = 0; - specific->destruct = NULL; - return -1; - } - return 0; -} - -static inline int isom_initialize_codec_specific_data( lsmash_codec_specific_t *specific, - lsmash_codec_specific_data_type type, - lsmash_codec_specific_format format ) -{ - specific->type = type; - specific->format = format; - if( format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - if( isom_initialize_structured_codec_specific_data( specific ) ) - return -1; - } - else - { - specific->data.unstructured = NULL; - specific->size = 0; - specific->destruct = (lsmash_codec_specific_destructor_t)lsmash_free; - } - return 0; -} - -void lsmash_destroy_codec_specific_data( lsmash_codec_specific_t *specific ) -{ - if( !specific ) - return; - if( specific->destruct ) - { - if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - if( specific->data.structured ) - specific->destruct( specific->data.structured ); - } - else - { - if( specific->data.unstructured ) - specific->destruct( specific->data.unstructured ); - } - } - lsmash_free( specific ); -} - -lsmash_codec_specific_t *lsmash_create_codec_specific_data( lsmash_codec_specific_data_type type, lsmash_codec_specific_format format ) -{ - lsmash_codec_specific_t *specific = lsmash_malloc( sizeof(lsmash_codec_specific_t) ); - if( !specific ) - return NULL; - if( isom_initialize_codec_specific_data( specific, type, format ) ) - { - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - return specific; -} - -static int isom_duplicate_structured_specific_data( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - extern int mp4sys_copy_decoder_config( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int h264_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int hevc_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int vc1_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int dts_copy_codec_specific( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - void *src_data = src->data.structured; - void *dst_data = dst->data.structured; - switch( src->type ) - { - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : - return mp4sys_copy_decoder_config( dst, src ); - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 : - return h264_copy_codec_specific( dst, src ); - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC : - return hevc_copy_codec_specific( dst, src ); - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 : - return vc1_copy_codec_specific( dst, src ); - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : - *(lsmash_ac3_specific_parameters_t *)dst_data = *(lsmash_ac3_specific_parameters_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : - *(lsmash_eac3_specific_parameters_t *)dst_data = *(lsmash_eac3_specific_parameters_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : - return dts_copy_codec_specific( dst, src ); - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : - *(lsmash_alac_specific_parameters_t *)dst_data = *(lsmash_alac_specific_parameters_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE : - *(lsmash_isom_sample_scale_t *)dst_data = *(lsmash_isom_sample_scale_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE : - *(lsmash_h264_bitrate_t *)dst_data = *(lsmash_h264_bitrate_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON : - *(lsmash_qt_video_common_t *)dst_data = *(lsmash_qt_video_common_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : - *(lsmash_qt_audio_common_t *)dst_data = *(lsmash_qt_audio_common_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : - *(lsmash_qt_audio_format_specific_flags_t *)dst_data = *(lsmash_qt_audio_format_specific_flags_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : - { - lsmash_codec_global_header_t *src_global = (lsmash_codec_global_header_t *)src_data; - if( src_global->header_data && src_global->header_size ) - { - lsmash_codec_global_header_t *dst_global = (lsmash_codec_global_header_t *)dst_data; - dst_global->header_data = lsmash_memdup( src_global->header_data, src_global->header_size ); - if( !dst_global->header_data ) - return -1; - dst_global->header_size = src_global->header_size; - } - return 0; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO : - *(lsmash_qt_field_info_t *)dst_data = *(lsmash_qt_field_info_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT : - *(lsmash_qt_pixel_format_t *)dst_data = *(lsmash_qt_pixel_format_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS : - *(lsmash_qt_significant_bits_t *)dst_data = *(lsmash_qt_significant_bits_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_GAMMA_LEVEL : - *(lsmash_qt_gamma_t *)dst_data = *(lsmash_qt_gamma_t *)src_data; - return 0; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : - *(lsmash_qt_audio_channel_layout_t *)dst_data = *(lsmash_qt_audio_channel_layout_t *)src_data; - return 0; - default : - return -1; - } -} - -lsmash_codec_specific_t *isom_duplicate_codec_specific_data( lsmash_codec_specific_t *specific ) -{ - if( !specific ) - return NULL; - lsmash_codec_specific_t *dup = lsmash_create_codec_specific_data( specific->type, specific->format ); - if( !dup ) - return NULL; - if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - if( isom_duplicate_structured_specific_data( dup, specific ) ) - { - lsmash_destroy_codec_specific_data( dup ); - return NULL; - } - } - else - { - dup->data.unstructured = lsmash_memdup( specific->data.unstructured, specific->size ); - if( !dup->data.unstructured ) - { - lsmash_destroy_codec_specific_data( dup ); - return NULL; - } - } - dup->size = specific->size; - return dup; -} - -static size_t isom_description_read_box_common( uint8_t **p_data, uint64_t *size, lsmash_box_type_t *type ) -{ - uint8_t *orig = *p_data; - uint8_t *data = *p_data; - *size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - type->fourcc = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( *size == 1 ) - { - *size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - *p_data = data; - if( type->fourcc == ISOM_BOX_TYPE_UUID.fourcc ) - { - type->user.fourcc = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - memcpy( type->user.id, &data[4], 12 ); - } - return data - orig; -} - -uint8_t *isom_get_child_box_position( uint8_t *parent_data, uint32_t parent_size, lsmash_box_type_t child_type, uint32_t *child_size ) -{ - if( !parent_data || !child_size || parent_size < ISOM_BASEBOX_COMMON_SIZE ) - return NULL; - uint8_t *data = parent_data; - uint64_t size; - lsmash_box_type_t type; - uint32_t offset = isom_description_read_box_common( &data, &size, &type ); - if( size != parent_size ) - return NULL; - uint8_t *end = parent_data + parent_size; - for( uint8_t *pos = data; pos + ISOM_BASEBOX_COMMON_SIZE <= end; ) - { - offset = isom_description_read_box_common( &pos, &size, &type ); - if( lsmash_check_box_type_identical( type, child_type ) ) - { - *child_size = size; - return pos - offset; - } - pos += size - offset; /* Move to the next box. */ - } - return NULL; -} - -static int isom_construct_global_specific_header( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - if( src->size < ISOM_BASEBOX_COMMON_SIZE ) - return -1; - lsmash_codec_global_header_t *global = (lsmash_codec_global_header_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - global->header_size = size - ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - global->header_size -= 8; - } - if( size != src->size ) - return -1; - if( global->header_size ) - { - global->header_data = lsmash_memdup( data, global->header_size ); - if( !global->header_data ) - return -1; - } - return 0; -} - -static int isom_construct_audio_channel_layout( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - if( src->size < ISOM_FULLBOX_COMMON_SIZE + 12 ) - return -1; - lsmash_qt_audio_channel_layout_t *layout = (lsmash_qt_audio_channel_layout_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_FULLBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - layout->channelLayoutTag = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - layout->channelBitmap = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; - return 0; -} - -#if 0 -static int codec_construct_qt_audio_decompression_info( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - if( src->size < ISOM_BASEBOX_COMMON_SIZE ) - return -1; - uint8_t *data = src->data.unstructured; - uint64_t size; - uint32_t type; - uint32_t offset = isom_description_read_box_common( &data, &size, &type ); - if( size != src->size ) - return -1; - uint8_t *end = src->data.unstructured + src->size; - isom_wave_t *wave = lsmash_malloc_zero( sizeof(isom_wave_t) ); - if( !wave ) - return -1; - wave->type = QT_BOX_TYPE_WAVE; - for( uint8_t *pos = data; pos + ISOM_BASEBOX_COMMON_SIZE <= end; ) - { - offset = isom_description_read_box_common( &pos, &size, &type ); - switch( type ) - { - case QT_BOX_TYPE_FRMA : - { - if( pos + 4 > end ) - return -1; - if( isom_add_frma( wave ) ) - return -1; - isom_frma_t *frma = (isom_frma_t *)wave->extensions.tail->data;; - frma->data_format = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3]; - pos += 4; - break; - } - case QT_BOX_TYPE_ENDA : - { - if( pos + 2 > end ) - return -1; - if( isom_add_enda( wave ) ) - return -1; - isom_enda_t *enda = (isom_enda_t *)wave->extensions.tail->data;; - enda->littleEndian = (pos[0] << 8) | pos[1]; - break; - } - case QT_BOX_TYPE_MP4A : - { - if( pos + 4 > end ) - return -1; - if( isom_add_mp4a( wave ) ) - return -1; - isom_mp4a_t *mp4a = (isom_mp4a_t *)wave->extensions.tail->data;; - mp4a->unknown = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3]; - pos += 4; - break; - } - case QT_BOX_TYPE_TERMINATOR : - { - if( isom_add_terminator( wave ) ) - return -1; - break; - } - default : - { - isom_unknown_box_t *box = lsmash_malloc_zero( sizeof(isom_unknown_box_t) ); - if( !box ) - return -1; - isom_init_box_common( box, wave, type, isom_remove_unknown_box, isom_update_unknown_box_size ); - box->unknown_size = size - offset; - box->unknown_field = lsmash_memdup( pos, box->unknown_size ); - if( !box->unknown_field ) - { - lsmash_free( box ); - return -1; - } - if( lsmash_add_entry( &wave->extensions, box ) ) - { - isom_remove_unknown_box( box ); - return -1; - } - pos += box->unknown_size; - break; - } - } - } - return 0; -} -#endif - -/* structured <-> unstructured conversion might be irreversible by CODEC - * since structured formats we defined don't always have all contents included in unstructured data. */ -lsmash_codec_specific_t *lsmash_convert_codec_specific_format( lsmash_codec_specific_t *specific, lsmash_codec_specific_format format ) -{ - if( !specific || format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSPECIFIED ) - return NULL; - if( format == specific->format ) - return isom_duplicate_codec_specific_data( specific ); - lsmash_codec_specific_t *dst = lsmash_create_codec_specific_data( specific->type, format ); - if( format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) - /* structured -> unstructured */ - switch( specific->type ) - { - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : - dst->data.unstructured = lsmash_create_mp4sys_decoder_config( (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 : - dst->data.unstructured = lsmash_create_h264_specific_info( (lsmash_h264_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC : - dst->data.unstructured = lsmash_create_hevc_specific_info( (lsmash_hevc_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 : - dst->data.unstructured = lsmash_create_vc1_specific_info( (lsmash_vc1_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 : - dst->data.unstructured = lsmash_create_ac3_specific_info( (lsmash_ac3_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 : - dst->data.unstructured = lsmash_create_eac3_specific_info( (lsmash_eac3_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS : - dst->data.unstructured = lsmash_create_dts_specific_info( (lsmash_dts_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : - dst->data.unstructured = lsmash_create_alac_specific_info( (lsmash_alac_specific_parameters_t *)specific->data.structured, &dst->size ); - if( !dst->data.unstructured ) - goto fail; - return dst; - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : - { - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - goto fail; - lsmash_codec_global_header_t *global = specific->data.structured; - lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE + global->header_size ); - lsmash_bs_put_be32( bs, QT_BOX_TYPE_GLBL.fourcc ); - lsmash_bs_put_bytes( bs, global->header_size, global->header_data ); - dst->data.unstructured = lsmash_bs_export_data( bs, &dst->size ); - lsmash_bs_cleanup( bs ); - if( !dst->data.unstructured || dst->size != (ISOM_BASEBOX_COMMON_SIZE + global->header_size) ) - goto fail; - return dst; - } - default : - break; - } - else if( format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - /* unstructured -> structured */ - extern int mp4sys_construct_decoder_config( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int h264_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int hevc_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int vc1_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int ac3_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int eac3_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int dts_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - extern int alac_construct_specific_parameters( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - static const struct - { - lsmash_codec_specific_data_type data_type; - int (*constructor)( lsmash_codec_specific_t *, lsmash_codec_specific_t * ); - } codec_specific_format_constructor_table[] = - { - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, mp4sys_construct_decoder_config }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264, h264_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC, hevc_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1, vc1_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3, ac3_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3, eac3_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS, dts_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC, alac_construct_specific_parameters }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER, isom_construct_global_specific_header }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT, isom_construct_audio_channel_layout }, - { LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN, NULL } - }; - int (*constructor)( lsmash_codec_specific_t *, lsmash_codec_specific_t * ) = NULL; - for( int i = 0; codec_specific_format_constructor_table[i].constructor; i++ ) - if( specific->type == codec_specific_format_constructor_table[i].data_type ) - { - constructor = codec_specific_format_constructor_table[i].constructor; - break; - } - if( constructor && !constructor( dst, specific ) ) - return dst; - } -fail: - lsmash_destroy_codec_specific_data( dst ); - return NULL; -} - -static inline void isom_set_default_compressorname( char *compressorname, lsmash_codec_type_t sample_type ) -{ - static struct compressorname_table_tag - { - lsmash_codec_type_t type; - char name[33]; - } compressorname_table[32] = { { LSMASH_CODEC_TYPE_INITIALIZER, { '\0' } } }; - if( compressorname_table[0].name[0] == '\0' ) - { - int i = 0; -#define ADD_COMPRESSORNAME_TABLE( type, name ) compressorname_table[i++] = (struct compressorname_table_tag){ type, name } - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC1_VIDEO, "\012AVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC2_VIDEO, "\012AVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC3_VIDEO, "\012AVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVC4_VIDEO, "\012AVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_AVCP_VIDEO, "\016AVC Parameters" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_HVC1_VIDEO, "\013HEVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_HEV1_VIDEO, "\013HEVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_SVC1_VIDEO, "\012SVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_MVC1_VIDEO, "\012MVC Coding" ); - ADD_COMPRESSORNAME_TABLE( ISOM_CODEC_TYPE_MVC2_VIDEO, "\012MVC Coding" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCH_VIDEO, "\023Apple ProRes 422 (HQ)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCN_VIDEO, "\023Apple ProRes 422 (SD)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCS_VIDEO, "\023Apple ProRes 422 (LT)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_APCO_VIDEO, "\026Apple ProRes 422 (Proxy)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_AP4H_VIDEO, "\019Apple ProRes 4444" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVPP_VIDEO, "\014DVCPRO - PAL" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DV5N_VIDEO, "\017DVCPRO50 - NTSC" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DV5P_VIDEO, "\016DVCPRO50 - PAL" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH2_VIDEO, "\019DVCPRO HD 1080p25" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH3_VIDEO, "\019DVCPRO HD 1080p30" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH5_VIDEO, "\019DVCPRO HD 1080i50" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVH6_VIDEO, "\019DVCPRO HD 1080i60" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVHP_VIDEO, "\018DVCPRO HD 720p60" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_DVHQ_VIDEO, "\018DVCPRO HD 720p50" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULRA_VIDEO, "\017Ut Video (ULRA)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULRG_VIDEO, "\017Ut Video (ULRG)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULY0_VIDEO, "\017Ut Video (ULY0)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULY2_VIDEO, "\017Ut Video (ULY2)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULH0_VIDEO, "\017Ut Video (ULH0)" ); - ADD_COMPRESSORNAME_TABLE( QT_CODEC_TYPE_ULH2_VIDEO, "\017Ut Video (ULH2)" ); - ADD_COMPRESSORNAME_TABLE( LSMASH_CODEC_TYPE_UNSPECIFIED, { '\0' } ); -#undef ADD_COMPRESSORNAME_TABLE - } - for( int i = 0; compressorname_table[i].name[0] != '\0'; i++ ) - if( lsmash_check_codec_type_identical( sample_type, compressorname_table[i].type ) ) - { - strcpy( compressorname, compressorname_table[i].name ); - return; - } -} - -lsmash_codec_specific_t *isom_get_codec_specific( lsmash_codec_specific_list_t *opaque, lsmash_codec_specific_data_type type ) -{ - for( lsmash_entry_t *entry = opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; - if( !specific || specific->type != type ) - continue; - return specific; - } - return NULL; -} - -static int isom_check_valid_summary( lsmash_summary_t *summary ) -{ - if( !summary ) - return -1; - isom_box_t temp_box; - temp_box.type = summary->sample_type; - temp_box.manager = summary->summary_type == LSMASH_SUMMARY_TYPE_AUDIO ? LSMASH_AUDIO_DESCRIPTION: 0; - if( isom_is_lpcm_audio( &temp_box ) ) - { - if( isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS ) ) - return 0; - return -1; - } - if( isom_is_uncompressed_ycbcr( summary->sample_type ) ) - { - if( isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO ) ) - { - if( !lsmash_check_codec_type_identical( summary->sample_type, QT_CODEC_TYPE_V216_VIDEO ) ) - return 0; - } - else - return -1; - } - lsmash_codec_type_t sample_type = summary->sample_type; - lsmash_codec_specific_data_type required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNSPECIFIED; - if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HEV1_VIDEO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_VC_1_VIDEO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 ; - else if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULRA_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULRG_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULY0_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULY2_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULH0_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ULH2_VIDEO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER; - else if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_V216_VIDEO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_MP4A_AUDIO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AC_3_AUDIO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS; - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) ) - required_data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC; - if( required_data_type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNSPECIFIED ) - return 0; - return isom_get_codec_specific( summary->opaque, required_data_type ) ? 0 : -1; -} - -static lsmash_box_type_t isom_guess_video_codec_specific_box_type( lsmash_codec_type_t active_codec_type, lsmash_compact_box_type_t fourcc ) -{ - lsmash_box_type_t box_type = LSMASH_BOX_TYPE_INITIALIZER; - box_type.fourcc = fourcc; -#define GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( codec_type, predefined_box_type ) \ - else if( (codec_type.user.fourcc == 0 \ - || lsmash_check_codec_type_identical( active_codec_type, codec_type )) \ - && box_type.fourcc == predefined_box_type.fourcc ) \ - box_type = predefined_box_type - if( 0 ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC1_VIDEO, ISOM_BOX_TYPE_AVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC2_VIDEO, ISOM_BOX_TYPE_AVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC3_VIDEO, ISOM_BOX_TYPE_AVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVC4_VIDEO, ISOM_BOX_TYPE_AVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AVCP_VIDEO, ISOM_BOX_TYPE_AVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_HVC1_VIDEO, ISOM_BOX_TYPE_HVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_HEV1_VIDEO, ISOM_BOX_TYPE_HVCC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_VC_1_VIDEO, ISOM_BOX_TYPE_DVC1 ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_MP4V_VIDEO, ISOM_BOX_TYPE_ESDS ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, ISOM_BOX_TYPE_BTRT ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_FIEL ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_CSPC ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_SGBT ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_GAMA ); - GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_GLBL ); -#undef GUESS_VIDEO_CODEC_SPECIFIC_BOX_TYPE - return box_type; -} - -int isom_setup_visual_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type, lsmash_video_summary_t *summary ) -{ - if( !summary || !stsd || !stsd->parent || !stsd->parent->parent - || !stsd->parent->parent->parent || !stsd->parent->parent->parent->parent ) - return -1; - if( isom_check_valid_summary( (lsmash_summary_t *)summary ) ) - return -1; - isom_visual_entry_t *visual = isom_add_visual_description( stsd, sample_type ); - if( !visual ) - return -1; - visual->data_reference_index = 1; - visual->version = 0; - visual->revision_level = 0; - visual->vendor = 0; - visual->temporalQuality = 0; - visual->spatialQuality = 0; - visual->width = (uint16_t)summary->width; - visual->height = (uint16_t)summary->height; - visual->horizresolution = 0x00480000; - visual->vertresolution = 0x00480000; - visual->dataSize = 0; - visual->frame_count = 1; - visual->depth = isom_is_qt_video( summary->sample_type ) || isom_is_nalff( summary->sample_type ) - ? summary->depth : 0x0018; - visual->color_table_ID = -1; - if( summary->compressorname[0] == '\0' ) - isom_set_default_compressorname( visual->compressorname, sample_type ); - else - { - memcpy( visual->compressorname, summary->compressorname, 32 ); - visual->compressorname[32] = '\0'; - } - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; - if( !specific ) - goto fail; - if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN - && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - continue; /* LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED is not supported. */ - switch( specific->type ) - { - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON : - { - if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) - continue; - lsmash_qt_video_common_t *data = (lsmash_qt_video_common_t *)specific->data.structured; - visual->revision_level = data->revision_level; - visual->vendor = data->vendor; - visual->temporalQuality = data->temporalQuality; - visual->spatialQuality = data->spatialQuality; - visual->horizresolution = data->horizontal_resolution; - visual->vertresolution = data->vertical_resolution; - visual->dataSize = data->dataSize; - visual->frame_count = data->frame_count; - visual->color_table_ID = data->color_table_ID; - if( data->color_table_ID == 0 ) - { - lsmash_qt_color_table_t *src_ct = &data->color_table; - uint16_t element_count = LSMASH_MIN( src_ct->size + 1, 256 ); - isom_qt_color_array_t *dst_array = lsmash_malloc_zero( element_count * sizeof(isom_qt_color_array_t) ); - if( !dst_array ) - goto fail; - isom_qt_color_table_t *dst_ct = &visual->color_table; - dst_ct->array = dst_array; - dst_ct->seed = src_ct->seed; - dst_ct->flags = src_ct->flags; - dst_ct->size = src_ct->size; - for( uint16_t i = 0; i < element_count; i++ ) - { - dst_array[i].value = src_ct->array[i].unused; - dst_array[i].r = src_ct->array[i].r; - dst_array[i].g = src_ct->array[i].g; - dst_array[i].b = src_ct->array[i].b; - } - } - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_isom_sample_scale_t *data = (lsmash_isom_sample_scale_t *)cs->data.structured; - isom_stsl_t *stsl = isom_add_stsl( visual ); - if( !stsl ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - stsl->constraint_flag = data->constraint_flag; - stsl->scale_method = data->scale_method; - stsl->display_center_x = data->display_center_x; - stsl->display_center_y = data->display_center_y; - lsmash_destroy_codec_specific_data( cs ); - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_h264_bitrate_t *data = (lsmash_h264_bitrate_t *)cs->data.structured; - isom_btrt_t *btrt = isom_add_btrt( visual ); - if( !btrt ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - btrt->bufferSizeDB = data->bufferSizeDB; - btrt->maxBitrate = data->maxBitrate; - btrt->avgBitrate = data->avgBitrate; - lsmash_destroy_codec_specific_data( cs ); - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_qt_field_info_t *data = (lsmash_qt_field_info_t *)cs->data.structured; - isom_fiel_t *fiel = isom_add_fiel( visual ); - if( !fiel ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - fiel->fields = data->fields; - fiel->detail = data->detail; - lsmash_destroy_codec_specific_data( cs ); - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_qt_pixel_format_t *data = (lsmash_qt_pixel_format_t *)cs->data.structured; - isom_cspc_t *cspc = isom_add_cspc( visual ); - if( !cspc ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - cspc->pixel_format = data->pixel_format; - lsmash_destroy_codec_specific_data( cs ); - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_qt_significant_bits_t *data = (lsmash_qt_significant_bits_t *)cs->data.structured; - isom_sgbt_t *sgbt = isom_add_sgbt( visual ); - if( !sgbt ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - sgbt->significantBits = data->significantBits; - lsmash_destroy_codec_specific_data( cs ); - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_GAMMA_LEVEL : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_qt_gamma_t *data = (lsmash_qt_gamma_t *)cs->data.structured; - isom_gama_t *gama = isom_add_gama( visual ); - if( !gama ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - gama->level = data->level; - lsmash_destroy_codec_specific_data( cs ); - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_codec_global_header_t *data = (lsmash_codec_global_header_t *)cs->data.structured; - isom_glbl_t *glbl = isom_add_glbl( visual ); - if( !glbl ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - glbl->header_size = data->header_size; - glbl->header_data = lsmash_memdup( data->header_data, data->header_size ); - lsmash_destroy_codec_specific_data( cs ); - if( !glbl->header_data ) - { - isom_remove_box_by_itself( glbl ); - goto fail; - } - break; - } - default : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !cs ) - goto fail; - if( cs->size < ISOM_BASEBOX_COMMON_SIZE ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - uint8_t *data = cs->data.unstructured; - lsmash_compact_box_type_t fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); - lsmash_box_type_t box_type = isom_guess_video_codec_specific_box_type( (lsmash_codec_type_t)visual->type, fourcc ); - /* Append the extension. */ - int ret = isom_add_extension_binary( visual, box_type, LSMASH_BOX_PRECEDENCE_HM, cs->data.unstructured, cs->size ); - cs->data.unstructured = NULL; /* Avoid freeing the binary data of the extension. */ - lsmash_destroy_codec_specific_data( cs ); - if( ret < 0 ) - goto fail; - break; - } - } - } - isom_trak_t *trak = (isom_trak_t *)visual->parent->parent->parent->parent->parent; - int qt_compatible = trak->file->qt_compatible; - isom_tapt_t *tapt = trak->tapt; - isom_stsl_t *stsl = (isom_stsl_t *)isom_get_extension_box_format( &visual->extensions, ISOM_BOX_TYPE_STSL ); - int set_aperture_modes = qt_compatible /* Track Aperture Modes is only available under QuickTime file format. */ - && (!stsl || stsl->scale_method == 0) /* 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 == 1; /* Multiple sample description might conflict with this, so in that case, disable this feature. */ - if( !set_aperture_modes ) - isom_remove_box_by_itself( trak->tapt ); - int uncompressed_ycbcr = qt_compatible && isom_is_uncompressed_ycbcr( visual->type ); - /* Set up Clean Aperture. */ - if( set_aperture_modes || uncompressed_ycbcr - || (summary->clap.width.d && summary->clap.height.d && summary->clap.horizontal_offset.d && summary->clap.vertical_offset.d) ) - { - isom_clap_t *clap = isom_add_clap( visual ); - if( !clap ) - goto fail; - if( summary->clap.width.d && summary->clap.height.d && summary->clap.horizontal_offset.d && summary->clap.vertical_offset.d ) - { - clap->cleanApertureWidthN = summary->clap.width.n; - clap->cleanApertureWidthD = summary->clap.width.d; - clap->cleanApertureHeightN = summary->clap.height.n; - clap->cleanApertureHeightD = summary->clap.height.d; - clap->horizOffN = summary->clap.horizontal_offset.n; - clap->horizOffD = summary->clap.horizontal_offset.d; - clap->vertOffN = summary->clap.vertical_offset.n; - clap->vertOffD = summary->clap.vertical_offset.d; - } - else - { - clap->cleanApertureWidthN = summary->width; - clap->cleanApertureWidthD = 1; - clap->cleanApertureHeightN = summary->height; - clap->cleanApertureHeightD = 1; - clap->horizOffN = 0; - clap->horizOffD = 1; - clap->vertOffN = 0; - clap->vertOffD = 1; - } - } - /* Set up Pixel Aspect Ratio. */ - if( set_aperture_modes || (summary->par_h && summary->par_v) ) - { - isom_pasp_t *pasp = isom_add_pasp( visual ); - if( !pasp ) - goto fail; - pasp->hSpacing = LSMASH_MAX( summary->par_h, 1 ); - pasp->vSpacing = LSMASH_MAX( summary->par_v, 1 ); - } - /* Set up Color Parameter. */ - if( uncompressed_ycbcr - || summary->color.primaries_index - || summary->color.transfer_index - || summary->color.matrix_index - || (trak->file->isom_compatible && summary->color.full_range) ) - { - isom_colr_t *colr = isom_add_colr( visual ); - if( !colr ) - goto fail; - /* Set 'nclc' to parameter type, we don't support 'prof'. */ - uint16_t primaries = summary->color.primaries_index; - uint16_t transfer = summary->color.transfer_index; - uint16_t matrix = summary->color.matrix_index; - if( qt_compatible && !trak->file->isom_compatible ) - { - colr->manager |= LSMASH_QTFF_BASE; - colr->type = QT_BOX_TYPE_COLR; - colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC; - colr->primaries_index = (primaries == 1 || primaries == 5 || primaries == 6) - ? primaries : QT_PRIMARIES_INDEX_UNSPECIFIED; - colr->transfer_function_index = (transfer == 1 || transfer == 7) - ? transfer : QT_TRANSFER_INDEX_UNSPECIFIED; - colr->matrix_index = (matrix == 1 || matrix == 6 || matrix == 7) - ? matrix : QT_MATRIX_INDEX_UNSPECIFIED; - } - else - { - colr->type = ISOM_BOX_TYPE_COLR; - colr->color_parameter_type = ISOM_COLOR_PARAMETER_TYPE_NCLX; - colr->primaries_index = (primaries == 1 || (primaries >= 4 && primaries <= 7)) - ? primaries : ISOM_PRIMARIES_INDEX_UNSPECIFIED; - colr->transfer_function_index = (transfer == 1 || (transfer >= 4 && transfer <= 8) || (transfer >= 11 && transfer <= 13)) - ? transfer : ISOM_TRANSFER_INDEX_UNSPECIFIED; - colr->matrix_index = (matrix == 1 || (matrix >= 4 && matrix <= 8)) - ? matrix : ISOM_MATRIX_INDEX_UNSPECIFIED; - colr->full_range_flag = summary->color.full_range; - } - } - /* Set up Track Apeture Modes. */ - if( set_aperture_modes ) - { - uint32_t width = visual->width << 16; - uint32_t height = visual->height << 16; - isom_clap_t *clap = (isom_clap_t *)isom_get_extension_box_format( &visual->extensions, ISOM_BOX_TYPE_CLAP ); - isom_pasp_t *pasp = (isom_pasp_t *)isom_get_extension_box_format( &visual->extensions, ISOM_BOX_TYPE_PASP ); - double clap_width = ((double)clap->cleanApertureWidthN / clap->cleanApertureWidthD) * (1<<16); - double clap_height = ((double)clap->cleanApertureHeightN / clap->cleanApertureHeightD) * (1<<16); - double par = (double)pasp->hSpacing / 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; -fail: - isom_remove_box_by_itself( visual ); - return -1; -} - -static int isom_append_audio_es_descriptor_extension( isom_box_t *box, lsmash_audio_summary_t *summary ) -{ - uint32_t esds_size = 0; - uint8_t *esds_data = NULL; - lsmash_codec_specific_t *specific = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); - if( !specific ) - return -1; - if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) - { - esds_size = specific->size; - esds_data = lsmash_memdup( specific->data.unstructured, specific->size ); - if( !esds_data ) - return -1; - } - else - { - esds_data = lsmash_create_mp4sys_decoder_config( (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured, &esds_size ); - if( !esds_data ) - return -1; - } - isom_esds_t *esds = isom_add_esds( box ); - if( !esds ) - { - lsmash_free( esds_data ); - return -1; - } - lsmash_bs_t bs = { 0 }; - bs.buffer.data = esds_data + ISOM_FULLBOX_COMMON_SIZE; - bs.buffer.alloc = esds_size - ISOM_FULLBOX_COMMON_SIZE; - bs.buffer.store = bs.buffer.alloc; - esds->ES = mp4sys_get_ES_Descriptor( &bs ); - lsmash_free( esds_data ); - if( !esds->ES ) - { - isom_remove_box_by_itself( esds ); - return -1; - } - return 0; -} - -static int isom_append_channel_layout_extension( lsmash_codec_specific_t *specific, void *parent, uint32_t channels ) -{ - assert( parent ); - if( isom_get_extension_box( &((isom_box_t *)parent)->extensions, QT_BOX_TYPE_CHAN ) ) - return 0; /* Audio Channel Layout Box is already present. */ - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - return -1; - lsmash_qt_audio_channel_layout_t *data = (lsmash_qt_audio_channel_layout_t *)cs->data.structured; - lsmash_channel_layout_tag channelLayoutTag = data->channelLayoutTag; - lsmash_channel_bitmap channelBitmap = data->channelBitmap; - if( channelLayoutTag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS /* We don't support the feature of Channel Descriptions. */ - || (channelLayoutTag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!channelBitmap || channelBitmap > QT_CHANNEL_BIT_FULL)) ) - { - channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN | channels; - channelBitmap = 0; - } - lsmash_destroy_codec_specific_data( cs ); - /* Don't create Audio Channel Layout Box if the channel layout is unknown. */ - if( (channelLayoutTag ^ QT_CHANNEL_LAYOUT_UNKNOWN) >> 16 ) - { - isom_chan_t *chan = isom_add_chan( parent ); - if( !chan ) - return -1; - chan->channelLayoutTag = channelLayoutTag; - chan->channelBitmap = channelBitmap; - chan->numberChannelDescriptions = 0; - chan->channelDescriptions = NULL; - } - return 0; -} - -static int isom_set_qtff_mp4a_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) -{ - isom_wave_t *wave = isom_add_wave( audio ); - if( isom_add_frma( wave ) - || isom_add_mp4a( wave ) - || isom_add_terminator( wave ) ) - { - lsmash_remove_entry_tail( &audio->extensions, wave->destruct ); - return -1; - } - wave->frma->data_format = audio->type.fourcc; - /* Add ES Descriptor Box. */ - if( isom_append_audio_es_descriptor_extension( (isom_box_t *)wave, summary ) ) - return -1; - /* */ - audio->type = QT_CODEC_TYPE_MP4A_AUDIO; - 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_AUDIO_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 = 2; - } - 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; - } - return 0; -} - -static int isom_set_isom_mp4a_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) -{ - if( summary->summary_type != LSMASH_SUMMARY_TYPE_AUDIO ) - return -1; - /* Check objectTypeIndication. */ - lsmash_mp4sys_object_type_indication objectTypeIndication = lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ); - switch( objectTypeIndication ) - { - 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; - } - /* Add ES Descriptor Box. */ - if( isom_append_audio_es_descriptor_extension( (isom_box_t *)audio, summary ) ) - return -1; - /* In pure mp4 file, these "template" fields shall be default values according to the spec. - But not pure - hybrid with other spec - mp4 file can take other values. - Which is to say, these template values shall be ignored in terms of mp4, except some object_type_indications. - see 14496-14, "Template fields used". */ - audio->type = ISOM_CODEC_TYPE_MP4A_AUDIO; - audio->version = 0; - audio->revision_level = 0; - audio->vendor = 0; - audio->channelcount = 2; - audio->samplesize = 16; - audio->compression_ID = 0; - audio->packet_size = 0; - /* 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; - return 0; -} - -static int isom_set_qtff_lpcm_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) -{ - lsmash_qt_audio_format_specific_flags_t *lpcm = NULL; - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; - if( !specific ) - continue; - if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS - && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - lpcm = (lsmash_qt_audio_format_specific_flags_t *)specific->data.structured; - break; - } - } - if( !lpcm ) - return -1; - audio->manager |= LSMASH_QTFF_BASE; - lsmash_codec_type_t sample_type = audio->type; - /* Convert the sample type into 'lpcm' if the description doesn't match the format or version = 2 fields are needed. */ - if( (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_RAW_AUDIO ) - && (summary->sample_size != 8 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL32_AUDIO ) - && (summary->sample_size != 32 || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL64_AUDIO ) - && (summary->sample_size != 64 || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN24_AUDIO ) - && (summary->sample_size != 24 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN32_AUDIO ) - && (summary->sample_size != 32 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_23NI_AUDIO ) - && (summary->sample_size != 32 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_SOWT_AUDIO ) - && (summary->sample_size != 16 || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) - && ((summary->sample_size != 16 && summary->sample_size != 8) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NONE_AUDIO ) - && ((summary->sample_size != 16 && summary->sample_size != 8) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) - || (lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NOT_SPECIFIED ) - && ((summary->sample_size != 16 && summary->sample_size != 8) || (lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT) || !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN))) - || (summary->channels > 2 || summary->frequency > UINT16_MAX || summary->sample_size % 8) ) - { - audio->type = QT_CODEC_TYPE_LPCM_AUDIO; - audio->version = 2; - } - else if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_LPCM_AUDIO ) ) - audio->version = 2; - else if( summary->sample_size > 16 - || (!lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_RAW_AUDIO ) - && !lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) - && !lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NONE_AUDIO ) - && !lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_NOT_SPECIFIED )) ) - audio->version = 1; - /* Set up constBytesPerAudioPacket field. - * We use constBytesPerAudioPacket as the actual size of LPCM audio frame even when version is not 2. */ - audio->constBytesPerAudioPacket = (summary->sample_size * 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->sample_size; - audio->constLPCMFramesPerAudioPacket = 1; - audio->formatSpecificFlags = lpcm->format_flags; - if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) && summary->sample_size != 8 ) - audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; - if( lpcm->format_flags & QT_LPCM_FORMAT_FLAG_FLOAT ) - audio->formatSpecificFlags &= ~QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER; - if( lpcm->format_flags & QT_LPCM_FORMAT_FLAG_PACKED ) - audio->formatSpecificFlags &= ~QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH; - } - 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( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_RAW_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TWOS_AUDIO ) ) - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED; - else - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_FIXED_COMPRESSION; - audio->samplerate = summary->frequency << 16; - audio->samplesPerPacket = 1; - audio->bytesPerPacket = summary->sample_size / 8; - audio->bytesPerFrame = audio->bytesPerPacket * summary->channels; /* sample_size field in stsz box is NOT used. */ - audio->bytesPerSample = 1 + (summary->sample_size != 8); - if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL32_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FL64_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN24_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_IN32_AUDIO ) ) - { - isom_wave_t *wave = isom_add_wave( audio ); - if( isom_add_frma( wave ) - || isom_add_enda( wave ) - || isom_add_terminator( wave ) ) - { - lsmash_remove_entry_tail( &audio->extensions, wave->destruct ); - return -1; - } - wave->frma->data_format = sample_type.fourcc; - wave->enda->littleEndian = !(lpcm->format_flags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN); - } - } - else /* audio->version == 0 */ - { - audio->channelcount = summary->channels; - audio->samplesize = summary->sample_size; - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED; - audio->samplerate = summary->frequency << 16; - } - return 0; -} - -static int isom_set_isom_dts_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) -{ - audio->version = 0; - audio->revision_level = 0; - audio->vendor = 0; - audio->channelcount = summary->channels; - audio->samplesize = 16; - audio->compression_ID = 0; - audio->packet_size = 0; - switch( summary->frequency ) - { - case 12000 : /* Invalid? (No reference in the spec) */ - case 24000 : - case 48000 : - case 96000 : - case 192000 : - case 384000 : /* Invalid? (No reference in the spec) */ - audio->samplerate = 48000 << 16; - break; - case 22050 : - case 44100 : - case 88200 : - case 176400 : - case 352800 : /* Invalid? (No reference in the spec) */ - audio->samplerate = 44100 << 16; - break; - case 8000 : /* Invalid? (No reference in the spec) */ - case 16000 : - case 32000 : - case 64000 : - case 128000 : - audio->samplerate = 32000 << 16; - break; - default : - audio->samplerate = 0; - break; - } - return 0; -} - -static lsmash_box_type_t isom_guess_audio_codec_specific_box_type( lsmash_codec_type_t active_codec_type, lsmash_compact_box_type_t fourcc ) -{ - lsmash_box_type_t box_type = LSMASH_BOX_TYPE_INITIALIZER; - box_type.fourcc = fourcc; -#define GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( codec_type, predefined_box_type ) \ - else if( (codec_type.user.fourcc == 0 \ - || lsmash_check_codec_type_identical( active_codec_type, codec_type )) \ - && box_type.fourcc == predefined_box_type.fourcc ) \ - box_type = predefined_box_type - if( 0 ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_AC_3_AUDIO, ISOM_BOX_TYPE_DAC3 ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_EC_3_AUDIO, ISOM_BOX_TYPE_DEC3 ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSC_AUDIO, ISOM_BOX_TYPE_DDTS ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSE_AUDIO, ISOM_BOX_TYPE_DDTS ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSH_AUDIO, ISOM_BOX_TYPE_DDTS ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_DTSL_AUDIO, ISOM_BOX_TYPE_DDTS ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_ALAC_AUDIO, ISOM_BOX_TYPE_ALAC ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( ISOM_CODEC_TYPE_MP4A_AUDIO, ISOM_BOX_TYPE_ESDS ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_ALAC_AUDIO, QT_BOX_TYPE_ALAC ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_MP4A_AUDIO, QT_BOX_TYPE_ESDS ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_FULLMP3_AUDIO, QT_CODEC_TYPE_MP3_AUDIO ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_ADPCM2_AUDIO, QT_CODEC_TYPE_ADPCM2_AUDIO ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_ADPCM17_AUDIO, QT_CODEC_TYPE_ADPCM17_AUDIO ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( QT_CODEC_TYPE_GSM49_AUDIO, QT_CODEC_TYPE_GSM49_AUDIO ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_CHAN ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_GLBL ); - GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE( LSMASH_CODEC_TYPE_UNSPECIFIED, QT_BOX_TYPE_WAVE ); -#undef GUESS_AUDIO_CODEC_SPECIFIC_BOX_TYPE - return box_type; -} - -typedef struct -{ - uint16_t wFormatTag; - uint16_t nChannels; - uint32_t nSamplesPerSec; - uint32_t nAvgBytesPerSec; - uint16_t nBlockAlign; - uint16_t wBitsPerSample; - uint16_t cbSize; -} wave_format_ex_t; - -static lsmash_bs_t *isom_create_waveform_audio_info -( - wave_format_ex_t *wfx, - lsmash_box_type_t type -) -{ - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return NULL; - lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE + 18 + wfx->cbSize ); - lsmash_bs_put_be32( bs, type.fourcc ); - lsmash_bs_put_le16( bs, wfx->wFormatTag ); - lsmash_bs_put_le16( bs, wfx->nChannels ); - lsmash_bs_put_le32( bs, wfx->nSamplesPerSec ); - lsmash_bs_put_le32( bs, wfx->nAvgBytesPerSec ); - lsmash_bs_put_le16( bs, wfx->nBlockAlign ); - lsmash_bs_put_le16( bs, wfx->wBitsPerSample ); - lsmash_bs_put_le16( bs, wfx->cbSize ); - return bs; -} - -static int isom_setup_waveform_audio_info -( - isom_wave_t *wave, - isom_audio_entry_t *audio, - lsmash_audio_summary_t *summary, - uint32_t samples_per_packet, - uint32_t bytes_per_frame, - uint32_t sample_size -) -{ - wave_format_ex_t wfx; - wfx.wFormatTag = 0x0000; /* WAVE_FORMAT_UNKNOWN */ - wfx.nChannels = summary->channels; - wfx.nSamplesPerSec = summary->frequency; - wfx.nAvgBytesPerSec = 0; - wfx.nBlockAlign = bytes_per_frame; - wfx.wBitsPerSample = sample_size; - wfx.cbSize = 0; - lsmash_bs_t *bs = NULL; - if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ADPCM2_AUDIO ) ) - { - /* ADPCMWAVEFORMAT */ - wfx.wFormatTag = 0x0002; /* WAVE_FORMAT_ADPCM */ - wfx.cbSize = 32; - bs = isom_create_waveform_audio_info( &wfx, audio->type ); - if( !bs ) - return -1; - uint16_t wSamplesPerBlock = samples_per_packet; /* nBlockAlign * 2 / nChannels - 12 */ - uint16_t wNumCoef = 7; /* Microsoft ADPCM uses just 7 coefficients. */ - static const struct - { - int16_t iCoef1; - int16_t iCoef2; - } aCoef[7] = { { 256, 0 }, { 512, -256 }, { 0,0 }, { 192,64 }, { 240,0 }, { 460, -208 }, { 392,-232 } }; - lsmash_bs_put_le16( bs, wSamplesPerBlock ); - lsmash_bs_put_le16( bs, wNumCoef ); - for( int i = 0; i < 7; i++ ) - { - lsmash_bs_put_le16( bs, aCoef[i].iCoef1 ); - lsmash_bs_put_le16( bs, aCoef[i].iCoef2 ); - } - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ADPCM17_AUDIO ) ) - { - /* IMAADPCMWAVEFORMAT */ - wfx.wFormatTag = 0x0011; /* WAVE_FORMAT_DVI_ADPCM / WAVE_FORMAT_IMA_ADPCM */ - wfx.cbSize = 2; - bs = isom_create_waveform_audio_info( &wfx, audio->type ); - if( !bs ) - return -1; - uint16_t wSamplesPerBlock = samples_per_packet; - lsmash_bs_put_le16( bs, wSamplesPerBlock ); - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_GSM49_AUDIO ) ) - { - /* GSM610WAVEFORMAT */ - wfx.wFormatTag = 0x0031; /* WAVE_FORMAT_GSM610 */ - wfx.cbSize = 2; - bs = isom_create_waveform_audio_info( &wfx, audio->type ); - if( !bs ) - return -1; - uint16_t wSamplesPerBlock = samples_per_packet; - lsmash_bs_put_le16( bs, wSamplesPerBlock ); - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_FULLMP3_AUDIO ) - || lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MP3_AUDIO ) ) - { - /* MPEGLAYER3WAVEFORMAT */ - wfx.wFormatTag = 0x0055; /* WAVE_FORMAT_MPEGLAYER3 */ - wfx.nBlockAlign = 1; /* ? */ - wfx.wBitsPerSample = 0; /* undefined */ - wfx.cbSize = 12; - bs = isom_create_waveform_audio_info( &wfx, audio->type ); - if( !bs ) - return -1; - uint16_t wID = 1; /* MPEGLAYER3_ID_MPEG */ - uint32_t fdwFlags = 0; /* We don't know whether the stream is padded or not here. */ - uint16_t nBlockSize = 0; /* (144 * (bitrate / nSamplesPerSec) + padding) * nFramesPerBlock */ - uint16_t nFramesPerBlock = 1; /* the number of audio frames per block */ - uint16_t nCodecDelay = 0; /* Encoder delay in samples is unknown. */ - lsmash_bs_put_le16( bs, wID ); - lsmash_bs_put_le32( bs, fdwFlags ); - lsmash_bs_put_le16( bs, nBlockSize ); - lsmash_bs_put_le16( bs, nFramesPerBlock ); - lsmash_bs_put_le16( bs, nCodecDelay ); - } - if( !bs ) - { - assert( 0 ); - return -1; - } - uint32_t wfx_size; - uint8_t *wfx_data = lsmash_bs_export_data( bs, &wfx_size ); - lsmash_bs_cleanup( bs ); - if( !wfx_data ) - return -1; - if( wfx_size != ISOM_BASEBOX_COMMON_SIZE + 18 + wfx.cbSize - || isom_add_extension_binary( wave, audio->type, LSMASH_BOX_PRECEDENCE_HM, wfx_data, wfx_size ) < 0 ) - { - lsmash_free( wfx_data ); - return -1; - } - return 0; -} - -static int isom_set_qtff_sound_decompression_parameters -( - isom_audio_entry_t *audio, - lsmash_audio_summary_t *summary, - lsmash_qt_audio_format_specific_flag *format_flags, - uint32_t samples_per_packet, - uint32_t bytes_per_frame, - uint32_t sample_size -) -{ - /* A 'wave' extension itself shall be absent in the opaque CODEC specific info list. - * So, create a 'wave' extension here and append it as an extension to the audio sample description. */ - isom_wave_t *wave = isom_add_wave( audio ); - if( isom_add_frma ( wave ) < 0 - || isom_add_terminator( wave ) < 0 ) - { - lsmash_remove_entry_tail( &audio->extensions, wave->destruct ); - return -1; - } - wave->frma->data_format = audio->type.fourcc; - /* Append extensions from the opaque CODEC specific info list to 'wave' extension. */ - int waveform_audio_info_present = 0; - int requires_waveform_audio_info = isom_is_waveform_audio( audio->type ); - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; - if( !specific ) - return -1; - if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN - && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - continue; /* LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED is not supported. */ - switch( specific->type ) - { - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : - continue; /* These cannot be an extension for 'wave' extension. */ - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : - /* (Legacy?) ALAC might have an Audio Channel Layout Box inside 'wave' extension. */ -#if 1 - continue; -#else - if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAC_AUDIO ) ) - continue; - if( isom_append_channel_layout_extension( specific, wave, summary->channels ) ) - return -1; - break; -#endif - default : - { - assert( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED - || specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS ); - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !cs ) - return -1; - if( cs->size < ISOM_BASEBOX_COMMON_SIZE ) - { - lsmash_destroy_codec_specific_data( cs ); - continue; - } - uint8_t *box_data = cs->data.unstructured; - uint64_t box_size = cs->size; - lsmash_compact_box_type_t fourcc = LSMASH_4CC( box_data[4], box_data[5], box_data[6], box_data[7] ); - if( audio->version == 2 && fourcc == QT_BOX_TYPE_ENDA.fourcc ) - { - /* Don't append a 'enda' extension if version == 2. - * Endianness is indicated in QuickTime audio format specific flags. */ - if( box_size >= ISOM_BASEBOX_COMMON_SIZE + 2 ) - { - /* Override endianness indicated in format specific flags. */ - if( box_data[9] == 1 ) - *format_flags &= ~QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN; - else - *format_flags |= QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN; - } - lsmash_destroy_codec_specific_data( cs ); - continue; - } - lsmash_box_type_t box_type = isom_guess_audio_codec_specific_box_type( (lsmash_codec_type_t)audio->type, fourcc ); - if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_WAVE ) ) - { - /* It is insane to appened a 'wave' extension to a 'wave' extension. */ - lsmash_destroy_codec_specific_data( cs ); - continue; - } - box_type = lsmash_form_qtff_box_type( box_type.fourcc ); - /* Determine 'precedence'. */ - uint64_t precedence; - if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_FRMA ) ) - precedence = LSMASH_BOX_PRECEDENCE_QTFF_FRMA; - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ESDS ) ) - precedence = LSMASH_BOX_PRECEDENCE_QTFF_ESDS; - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ENDA ) ) - precedence = LSMASH_BOX_PRECEDENCE_QTFF_ENDA; - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_MP4A ) ) - precedence = LSMASH_BOX_PRECEDENCE_QTFF_MP4A; - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_TERMINATOR ) ) - precedence = LSMASH_BOX_PRECEDENCE_QTFF_TERMINATOR; - else - precedence = LSMASH_BOX_PRECEDENCE_HM; - /* Append the extension. */ - int ret = isom_add_extension_binary( wave, box_type, precedence, cs->data.unstructured, cs->size ); - cs->data.unstructured = NULL; /* Avoid freeing the binary data of the extension. */ - lsmash_destroy_codec_specific_data( cs ); - if( ret < 0 ) - return ret; - if( isom_is_waveform_audio( box_type ) ) - waveform_audio_info_present = 1; - break; - } - } - } - if( requires_waveform_audio_info && !waveform_audio_info_present - && isom_setup_waveform_audio_info( wave, audio, summary, samples_per_packet, bytes_per_frame, sample_size ) < 0 ) - return -1; - return 0; -} - -static int isom_set_qtff_template_audio_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) -{ - audio->manager |= LSMASH_QTFF_BASE; - audio->type = lsmash_form_qtff_box_type( audio->type.fourcc ); - audio->version = (summary->channels > 2 || summary->frequency > UINT16_MAX) ? 2 : 1; - /* Try to get QuickTime audio format specific flags. */ - lsmash_qt_audio_format_specific_flag format_flags = QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN; - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; - if( !specific - || !specific->data.structured ) - continue; - if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS - && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - /* A format specific flags is found. - * Force audio sample description version == 2. */ - format_flags = ((lsmash_qt_audio_format_specific_flags_t *)specific->data.structured)->format_flags; - audio->version = 2; - break; - } - } - uint32_t samples_per_packet; - uint32_t bytes_per_frame; - uint32_t sample_size; - if( !((summary->samples_in_frame == 0 || summary->bytes_per_frame == 0 || summary->sample_size == 0) - && isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, &samples_per_packet, &bytes_per_frame, &sample_size )) ) - { - samples_per_packet = summary->samples_in_frame; - bytes_per_frame = summary->bytes_per_frame; - sample_size = summary->sample_size; - } - if( (!lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC3_AUDIO ) - && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC6_AUDIO ) - && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_AGSM_AUDIO ) - && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAW_AUDIO ) - && !lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ULAW_AUDIO )) - && isom_set_qtff_sound_decompression_parameters( audio, summary, &format_flags, samples_per_packet, bytes_per_frame, sample_size ) < 0 ) - return -1; - /* Set up common audio description fields. */ - audio->samplesize = 16; - audio->packet_size = 0; - if( audio->version == 2 ) - { - audio->channelcount = 3; - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION; - 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; - audio->constBytesPerAudioPacket = bytes_per_frame; - audio->constLPCMFramesPerAudioPacket = samples_per_packet; - if( lsmash_check_codec_type_identical( (lsmash_codec_type_t)audio->type, QT_CODEC_TYPE_ALAC_AUDIO ) ) - { - switch( sample_size ) - { - case 16 : - audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_16BIT_SOURCE_DATA; - break; - case 20 : - audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_20BIT_SOURCE_DATA; - break; - case 24 : - audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_24BIT_SOURCE_DATA; - break; - case 32 : - audio->formatSpecificFlags = QT_ALAC_FORMAT_FLAG_32BIT_SOURCE_DATA; - break; - default : - break; - } - } - else - { - if( format_flags & QT_AUDIO_FORMAT_FLAG_FLOAT ) - format_flags &= ~QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER; - if( format_flags & QT_AUDIO_FORMAT_FLAG_PACKED ) - format_flags &= ~QT_AUDIO_FORMAT_FLAG_ALIGNED_HIGH; - audio->formatSpecificFlags = format_flags; - } - } - else /* if( audio->version == 1 ) */ - { - audio->channelcount = LSMASH_MIN( summary->channels, 2 ); - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_FIXED_COMPRESSION; - audio->samplerate = summary->frequency << 16; - audio->samplesPerPacket = samples_per_packet; - audio->bytesPerPacket = bytes_per_frame / summary->channels; - audio->bytesPerFrame = bytes_per_frame; /* sample_size field in stsz box is NOT used. */ - audio->bytesPerSample = 1 + (sample_size != 8); - } - return 0; -} - -static int isom_set_isom_template_audio_description( isom_audio_entry_t *audio, lsmash_audio_summary_t *summary ) -{ - audio->version = 0; - audio->revision_level = 0; - audio->vendor = 0; - audio->channelcount = summary->channels; - audio->samplesize = 16; - audio->compression_ID = 0; - audio->packet_size = 0; - audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0; - return 0; -} - -int isom_setup_audio_description( isom_stsd_t *stsd, lsmash_codec_type_t sample_type, lsmash_audio_summary_t *summary ) -{ - if( !stsd || !stsd->file || !summary ) - return -1; - if( isom_check_valid_summary( (lsmash_summary_t *)summary ) ) - return -1; - isom_audio_entry_t *audio = isom_add_audio_description( stsd, sample_type ); - if( !audio ) - return -1; - audio->data_reference_index = 1; - lsmash_file_t *file = stsd->file; - lsmash_codec_type_t audio_type = (lsmash_codec_type_t)audio->type; - int ret; - if( lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_MP4A_AUDIO ) ) - { - if( (file->ftyp && file->ftyp->major_brand == ISOM_BRAND_TYPE_QT) - || (!file->ftyp && (file->qt_compatible || (file->moov && !file->moov->iods))) ) - ret = isom_set_qtff_mp4a_description( audio, summary ); - else - ret = isom_set_isom_mp4a_description( audio, summary ); - } - else if( isom_is_lpcm_audio( audio ) ) - ret = isom_set_qtff_lpcm_description( audio, summary ); - else if( lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) - ret = isom_set_isom_dts_description( audio, summary ); - else if( file->qt_compatible ) - ret = isom_set_qtff_template_audio_description( audio, summary ); - else - ret = isom_set_isom_template_audio_description( audio, summary ); - if( ret ) - goto fail; - /* Don't use audio_type since audio->type might have changed. */ - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *specific = (lsmash_codec_specific_t *)entry->data; - if( !specific ) - goto fail; - if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN - && specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - continue; /* LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN + LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED is not supported. */ - switch( specific->type ) - { - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON : - { - if( specific->format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) - continue; /* Ignore since not fatal. */ - lsmash_qt_audio_common_t *data = (lsmash_qt_audio_common_t *)specific->data.structured; - audio->revision_level = data->revision_level; - audio->vendor = data->vendor; - if( audio->version == 1 - && !isom_is_lpcm_audio( audio ) - && data->compression_ID != QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED ) - { - /* Compressed audio must not be set to QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED. */ - audio->compression_ID = data->compression_ID; - if( audio->compression_ID == QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION ) - { - /* For variable compression, bytesPerPacket and bytesPerFrame are reserved and should be set to 0. */ - audio->bytesPerPacket = 0; - audio->bytesPerFrame = 0; - } - } - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT : - { - if( !file->qt_compatible - && !lsmash_check_codec_type_identical( (lsmash_codec_type_t)audio->type, ISOM_CODEC_TYPE_ALAC_AUDIO ) - && !lsmash_check_codec_type_identical( (lsmash_codec_type_t)audio->type, QT_CODEC_TYPE_ALAC_AUDIO ) ) - continue; - if( isom_append_channel_layout_extension( specific, audio, summary->channels ) ) - goto fail; - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !cs ) - goto fail; - lsmash_codec_global_header_t *data = (lsmash_codec_global_header_t *)cs->data.structured; - isom_glbl_t *glbl = isom_add_glbl( audio ); - if( !glbl ) - { - lsmash_destroy_codec_specific_data( cs ); - goto fail; - } - glbl->header_size = data->header_size; - glbl->header_data = lsmash_memdup( data->header_data, data->header_size ); - lsmash_destroy_codec_specific_data( cs ); - if( !glbl->header_data ) - { - isom_remove_box_by_itself( glbl ); - goto fail; - } - break; - } - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS : - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS : - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG : - break; /* shall be set up already */ - case LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC : - if( file->qt_compatible ) - continue; /* shall be set up already */ - default : - { - lsmash_codec_specific_t *cs = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !cs ) - goto fail; - if( cs->size < ISOM_BASEBOX_COMMON_SIZE ) - { - lsmash_destroy_codec_specific_data( cs ); - continue; - } - uint8_t *box_data = cs->data.unstructured; - lsmash_compact_box_type_t fourcc = LSMASH_4CC( box_data[4], box_data[5], box_data[6], box_data[7] ); - lsmash_box_type_t box_type = isom_guess_audio_codec_specific_box_type( (lsmash_codec_type_t)audio->type, fourcc ); - if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_WAVE ) ) - { - /* CODEC specific info shall be already inside 'wave' extension. */ - lsmash_destroy_codec_specific_data( cs ); - continue; - } - /* Append the extension. */ - ret = isom_add_extension_binary( audio, box_type, LSMASH_BOX_PRECEDENCE_HM, cs->data.unstructured, cs->size ); - cs->data.unstructured = NULL; /* Avoid freeing the binary data of the extension. */ - lsmash_destroy_codec_specific_data( cs ); - if( ret < 0 ) - goto fail; - break; - } - } - } - if( audio->version == 0 ) - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_NOT_COMPRESSED; - else if( audio->version == 2 ) - audio->compression_ID = QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION; - return 0; -fail: - isom_remove_box_by_itself( audio ); - return -1; -} - -static lsmash_codec_specific_data_type isom_get_codec_specific_data_type( lsmash_compact_box_type_t extension_fourcc ) -{ - static struct codec_specific_data_type_table_tag - { - lsmash_compact_box_type_t extension_fourcc; - lsmash_codec_specific_data_type data_type; - } codec_specific_data_type_table[32] = { { 0, LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN } }; - if( codec_specific_data_type_table[0].data_type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN ) - { - int i = 0; -#define ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( extension_type, data_type ) \ - codec_specific_data_type_table[i++] = (struct codec_specific_data_type_table_tag){ extension_type.fourcc, data_type } - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_AVCC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264 ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_HVCC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DVC1, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1 ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DAC3, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DEC3, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_DDTS, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_ALAC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE ); - //ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_ALAC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_ALAC ); - //ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_ESDS, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_GAMMA_LEVEL ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER ); - ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN ); -#undef ADD_CODEC_SPECIFIC_DATA_TYPE_TABLE_ELEMENT - } - lsmash_codec_specific_data_type data_type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; - for( int i = 0; codec_specific_data_type_table[i].data_type != LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; i++ ) - if( extension_fourcc == codec_specific_data_type_table[i].extension_fourcc ) - { - data_type = codec_specific_data_type_table[i].data_type; - break; - } - return data_type; -} - -lsmash_summary_t *isom_create_video_summary_from_description( isom_sample_entry_t *sample_entry ) -{ - if( !sample_entry ) - return NULL; - isom_visual_entry_t *visual = (isom_visual_entry_t *)sample_entry; - lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); - if( !summary ) - return NULL; - summary->sample_type = visual->type; - summary->width = visual->width; - summary->height = visual->height; - summary->depth = visual->depth; - memcpy( summary->compressorname, visual->compressorname, 32 ); - summary->compressorname[32] = '\0'; - if( isom_is_qt_video( summary->sample_type ) ) - { - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - lsmash_qt_video_common_t *data = (lsmash_qt_video_common_t *)specific->data.structured; - data->revision_level = visual->revision_level; - data->vendor = visual->vendor; - data->temporalQuality = visual->temporalQuality; - data->spatialQuality = visual->spatialQuality; - data->horizontal_resolution = visual->horizresolution; - data->vertical_resolution = visual->vertresolution; - data->dataSize = visual->dataSize; - data->frame_count = visual->frame_count; - data->color_table_ID = visual->color_table_ID; - if( visual->color_table_ID == 0 ) - { - isom_qt_color_table_t *src_ct = &visual->color_table; - if( !src_ct->array ) - goto fail; - uint16_t element_count = LSMASH_MIN( src_ct->size + 1, 256 ); - lsmash_qt_color_table_t *dst_ct = &data->color_table; - dst_ct->seed = src_ct->seed; - dst_ct->flags = src_ct->flags; - dst_ct->size = src_ct->size; - for( uint16_t i = 0; i < element_count; i++ ) - { - dst_ct->array[i].unused = src_ct->array[i].value; - dst_ct->array[i].r = src_ct->array[i].r; - dst_ct->array[i].g = src_ct->array[i].g; - dst_ct->array[i].b = src_ct->array[i].b; - } - } - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - } - for( lsmash_entry_t *entry = visual->extensions.head; entry; entry = entry->next ) - { - isom_box_t *box = (isom_box_t *)entry->data; - if( !box ) - continue; - if( !(box->manager & LSMASH_BINARY_CODED_BOX) ) - { - lsmash_codec_specific_t *specific = NULL; - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_CLAP ) ) - { - isom_clap_t *clap = (isom_clap_t *)box; - summary->clap.width.n = clap->cleanApertureWidthN; - summary->clap.width.d = clap->cleanApertureWidthD; - summary->clap.height.n = clap->cleanApertureHeightN; - summary->clap.height.d = clap->cleanApertureHeightD; - summary->clap.horizontal_offset.n = clap->horizOffN; - summary->clap.horizontal_offset.d = clap->horizOffD; - summary->clap.vertical_offset.n = clap->vertOffN; - summary->clap.vertical_offset.d = clap->vertOffD; - continue; - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_PASP ) ) - { - isom_pasp_t *pasp = (isom_pasp_t *)box; - summary->par_h = pasp->hSpacing; - summary->par_v = pasp->vSpacing; - continue; - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_COLR ) - || lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_COLR ) ) - { - isom_colr_t *colr = (isom_colr_t *)box; - summary->color.primaries_index = colr->primaries_index; - summary->color.transfer_index = colr->transfer_function_index; - summary->color.matrix_index = colr->matrix_index; - summary->color.full_range = colr->full_range_flag; - continue; - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_STSL ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_SAMPLE_SCALE, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_stsl_t *stsl = (isom_stsl_t *)box; - lsmash_isom_sample_scale_t *data = (lsmash_isom_sample_scale_t *)specific->data.structured; - data->constraint_flag = stsl->constraint_flag; - data->scale_method = stsl->scale_method; - data->display_center_x = stsl->display_center_x; - data->display_center_y = stsl->display_center_y; - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_BTRT ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_btrt_t *btrt = (isom_btrt_t *)box; - lsmash_h264_bitrate_t *data = (lsmash_h264_bitrate_t *)specific->data.structured; - data->bufferSizeDB = btrt->bufferSizeDB; - data->maxBitrate = btrt->maxBitrate; - data->avgBitrate = btrt->avgBitrate; - } - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_FIEL ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_FIELD_INFO, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_fiel_t *fiel = (isom_fiel_t *)box; - lsmash_qt_field_info_t *data = (lsmash_qt_field_info_t *)specific->data.structured; - data->fields = fiel->fields; - data->detail = fiel->detail; - } - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_CSPC ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_PIXEL_FORMAT, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_cspc_t *cspc = (isom_cspc_t *)box; - lsmash_qt_pixel_format_t *data = (lsmash_qt_pixel_format_t *)specific->data.structured; - data->pixel_format = cspc->pixel_format; - } - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_SGBT ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_SIGNIFICANT_BITS, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_sgbt_t *sgbt = (isom_sgbt_t *)box; - lsmash_qt_significant_bits_t *data = (lsmash_qt_significant_bits_t *)specific->data.structured; - data->significantBits = sgbt->significantBits; - } - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_GLBL ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_CODEC_GLOBAL_HEADER, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_glbl_t *glbl = (isom_glbl_t *)box; - lsmash_codec_global_header_t *data = (lsmash_codec_global_header_t *)specific->data.structured; - data->header_size = glbl->header_size; - data->header_data = lsmash_memdup( glbl->header_data, glbl->header_size ); - if( !data->header_data ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - } - else - continue; - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - } - else - { - if( box->size < ISOM_BASEBOX_COMMON_SIZE ) - continue; - uint8_t *data = box->binary; - lsmash_compact_box_type_t fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); - lsmash_codec_specific_data_type type = isom_get_codec_specific_data_type( fourcc ); - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !specific ) - goto fail; - specific->size = box->size; - specific->data.unstructured = lsmash_memdup( box->binary, box->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - } - } - return (lsmash_summary_t *)summary; -fail: - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return NULL; -} - -static int isom_append_structured_mp4sys_decoder_config( lsmash_codec_specific_list_t *opaque, isom_esds_t *esds ) -{ - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return -1; - /* Put box size, type, version and flags fields. */ - lsmash_bs_put_be32( bs, 0 ); - lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_ESDS.fourcc ); - lsmash_bs_put_be32( bs, 0 ); - /* Put ES Descriptor. */ - mp4sys_put_ES_Descriptor( bs, esds->ES ); - /* Export ES Descriptor Box as binary string. */ - uint32_t esds_size; - uint8_t *esds_data = lsmash_bs_export_data( bs, &esds_size ); - lsmash_bs_cleanup( bs ); - if( !esds_data ) - return -1; - /* Update box size. */ - esds_data[0] = ((esds_size) >> 24) & 0xff; - esds_data[1] = ((esds_size) >> 16) & 0xff; - esds_data[2] = ((esds_size) >> 8) & 0xff; - esds_data[3] = (esds_size) & 0xff; - lsmash_codec_specific_data_type type = isom_get_codec_specific_data_type( ISOM_BOX_TYPE_ESDS.fourcc ); - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !specific ) - { - lsmash_free( esds_data ); - return -1; - } - specific->data.unstructured = esds_data; - specific->size = esds_size; - /* Convert unstructured CODEC specific data format into structured, and append it to the opaque list. */ - lsmash_codec_specific_t *conv = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - lsmash_destroy_codec_specific_data( specific ); - if( !conv ) - return -1; - if( lsmash_add_entry( &opaque->list, conv ) ) - { - lsmash_destroy_codec_specific_data( conv ); - return -1; - } - return 0; -} - -lsmash_summary_t *isom_create_audio_summary_from_description( isom_sample_entry_t *sample_entry ) -{ - if( !sample_entry || !sample_entry->file || !sample_entry->parent ) - return NULL; - isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry; - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - summary->sample_type = audio->type; - summary->sample_size = audio->samplesize; - summary->channels = audio->channelcount; - summary->frequency = audio->samplerate >> 16; - if( ((isom_stsd_t *)audio->parent)->version == 0 - && audio->file->qt_compatible - && isom_is_qt_audio( (lsmash_codec_type_t)audio->type ) ) - { - if( audio->version == 0 ) - isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, &summary->samples_in_frame, &summary->bytes_per_frame, &summary->sample_size ); - else if( audio->version == 1 ) - { - summary->channels = audio->bytesPerPacket ? audio->bytesPerFrame / audio->bytesPerPacket : audio->channelcount; - summary->sample_size = audio->bytesPerPacket * 8; - summary->samples_in_frame = audio->samplesPerPacket; - summary->bytes_per_frame = audio->bytesPerFrame; - } - else if( audio->version == 2 ) - { - summary->frequency = (union {uint64_t i; double d;}){audio->audioSampleRate}.d; - summary->channels = audio->numAudioChannels; - summary->sample_size = audio->constBitsPerChannel; - summary->samples_in_frame = audio->constLPCMFramesPerAudioPacket; - summary->bytes_per_frame = audio->constBytesPerAudioPacket; - } - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - lsmash_qt_audio_common_t *common = (lsmash_qt_audio_common_t *)specific->data.structured; - common->revision_level = audio->revision_level; - common->vendor = audio->vendor; - common->compression_ID = audio->compression_ID; - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - if( isom_is_lpcm_audio( audio ) ) - { - specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - lsmash_qt_audio_format_specific_flags_t *data = (lsmash_qt_audio_format_specific_flags_t *)specific->data.structured; - if( audio->version == 2 ) - data->format_flags = audio->formatSpecificFlags; - else - { - data->format_flags = 0; - /* Here, don't override samplesize. - * We should trust samplesize field in the description for misused CODEC indentifier. */ - lsmash_codec_type_t audio_type = (lsmash_codec_type_t)audio->type; - if( lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_FL32_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_FL64_AUDIO ) ) - data->format_flags = QT_LPCM_FORMAT_FLAG_FLOAT; - else if( lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_TWOS_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_NONE_AUDIO ) - || lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_NOT_SPECIFIED ) ) - { - if( lsmash_check_codec_type_identical( audio_type, QT_CODEC_TYPE_TWOS_AUDIO ) ) - data->format_flags = QT_LPCM_FORMAT_FLAG_BIG_ENDIAN | QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER; - if( summary->sample_size > 8 ) - data->format_flags = QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; - } - } - isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box_format( &audio->extensions, QT_BOX_TYPE_WAVE ); - if( wave && wave->enda && !wave->enda->littleEndian ) - data->format_flags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - } - else if( audio->version == 2 - && (lsmash_check_codec_type_identical( (lsmash_codec_type_t)audio->type, ISOM_CODEC_TYPE_ALAC_AUDIO ) - || lsmash_check_codec_type_identical( (lsmash_codec_type_t)audio->type, QT_CODEC_TYPE_ALAC_AUDIO )) ) - switch( audio->formatSpecificFlags ) - { - case QT_ALAC_FORMAT_FLAG_16BIT_SOURCE_DATA : - summary->sample_size = 16; - break; - case QT_ALAC_FORMAT_FLAG_20BIT_SOURCE_DATA : - summary->sample_size = 20; - break; - case QT_ALAC_FORMAT_FLAG_24BIT_SOURCE_DATA : - summary->sample_size = 24; - break; - case QT_ALAC_FORMAT_FLAG_32BIT_SOURCE_DATA : - summary->sample_size = 32; - break; - default : - break; - } - } - for( lsmash_entry_t *entry = audio->extensions.head; entry; entry = entry->next ) - { - isom_box_t *box = (isom_box_t *)entry->data; - if( !box ) - continue; - if( !(box->manager & LSMASH_BINARY_CODED_BOX) ) - { - if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_CHAN ) ) - { - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - isom_chan_t *chan = (isom_chan_t *)box; - lsmash_qt_audio_channel_layout_t *data = (lsmash_qt_audio_channel_layout_t *)specific->data.structured; - data->channelLayoutTag = chan->channelLayoutTag; - data->channelBitmap = chan->channelBitmap; - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_ESDS ) - || lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ESDS ) ) - { - isom_esds_t *esds = (isom_esds_t *)box; - if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) - || isom_append_structured_mp4sys_decoder_config( summary->opaque, esds ) ) - goto fail; - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_SRAT ) ) - { - /* Set the actual sampling rate. */ - isom_srat_t *srat = (isom_srat_t *)box; - summary->frequency = srat->sampling_rate; - } - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_WAVE ) ) - { - /* Don't append 'wave' extension itself to the opaque CODEC specific info list. */ - isom_wave_t *wave = (isom_wave_t *)box; - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - goto fail; - for( lsmash_entry_t *wave_entry = wave->extensions.head; wave_entry; wave_entry = wave_entry->next ) - { - isom_box_t *wave_ext = (isom_box_t *)wave_entry->data; - if( !wave_ext ) - continue; - lsmash_box_type_t box_type = LSMASH_BOX_TYPE_INITIALIZER; - if( !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) ) - { - box_type = wave_ext->type; - if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ENDA ) ) - { - isom_enda_t *enda = (isom_enda_t *)wave_ext; - isom_bs_put_box_common( bs, enda ); - lsmash_bs_put_be16( bs, enda->littleEndian ); - } - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_MP4A ) ) - { - isom_mp4a_t *mp4a = (isom_mp4a_t *)wave_ext; - isom_bs_put_box_common( bs, mp4a ); - lsmash_bs_put_be32( bs, mp4a->unknown ); - } - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_CHAN ) ) - { - isom_chan_t *chan = (isom_chan_t *)wave_ext; - 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 ) - { - lsmash_bs_cleanup( bs ); - goto fail; - } - 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] ); - } - } - else if( lsmash_check_box_type_identical( box_type, QT_BOX_TYPE_ESDS ) ) - { - isom_esds_t *esds = (isom_esds_t *)wave_ext; - if( !esds - || mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) - || isom_append_structured_mp4sys_decoder_config( summary->opaque, esds ) ) - { - lsmash_bs_cleanup( bs ); - goto fail; - } - continue; - } - else - /* Skip Format Box and Terminator Box since they are mandatory and fixed structure. */ - continue; - } - else - { - if( wave_ext->size < ISOM_BASEBOX_COMMON_SIZE ) - continue; - uint8_t *data = wave_ext->binary; - box_type.fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); - lsmash_bs_put_bytes( bs, wave_ext->size, wave_ext->binary ); - } - /* Export as binary string. */ - uint32_t box_size; - uint8_t *box_data = lsmash_bs_export_data( bs, &box_size ); - lsmash_bs_empty( bs ); - if( !box_data ) - { - lsmash_bs_cleanup( bs ); - goto fail; - } - /* Append as an unstructured CODEC specific info. */ - lsmash_codec_specific_data_type type; - if( box_type.fourcc == QT_BOX_TYPE_CHAN.fourcc ) - /* Complete audio channel layout is stored as binary string. - * We distinguish it from one of the outside of 'wave' extension here. */ - type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS; - else - { - type = isom_get_codec_specific_data_type( box_type.fourcc ); - if( type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN ) - type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS; - } - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !specific ) - { - lsmash_bs_cleanup( bs ); - goto fail; - } - specific->data.unstructured = box_data; - specific->size = box_size; - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - lsmash_bs_cleanup( bs ); - goto fail; - } - } - lsmash_bs_cleanup( bs ); - } - } - else - { - if( box->size < ISOM_BASEBOX_COMMON_SIZE ) - continue; - uint8_t *data = box->binary; - lsmash_compact_box_type_t fourcc = LSMASH_4CC( data[4], data[5], data[6], data[7] ); - lsmash_codec_specific_data_type type = isom_get_codec_specific_data_type( fourcc ); - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( type, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !specific ) - goto fail; - specific->size = box->size; - specific->data.unstructured = lsmash_memdup( box->binary, box->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_destroy_codec_specific_data( specific ); - goto fail; - } - if( specific->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS ) - { - specific = lsmash_convert_codec_specific_format( specific, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - goto fail; - lsmash_dts_specific_parameters_t *param = (lsmash_dts_specific_parameters_t *)specific->data.structured; - summary->sample_size = param->pcmSampleDepth; - summary->samples_in_frame = (summary->frequency * (512 << param->FrameDuration)) / param->DTSSamplingFrequency; - lsmash_destroy_codec_specific_data( specific ); - } - } - } - return (lsmash_summary_t *)summary; -fail: - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return NULL; -} - -lsmash_codec_specific_t *lsmash_get_codec_specific_data( lsmash_summary_t *summary, uint32_t extension_number ) -{ - if( !summary || !summary->opaque ) - return NULL; - uint32_t i = 0; - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; entry = entry->next ) - if( ++i == extension_number ) - return (lsmash_codec_specific_t *)entry->data; - return NULL; -} - -uint32_t lsmash_count_codec_specific_data( lsmash_summary_t *summary ) -{ - if( !summary || !summary->opaque ) - return 0; - return summary->opaque->list.entry_count; -} - -int isom_compare_opaque_extensions( lsmash_summary_t *a, lsmash_summary_t *b ) -{ - assert( a && b ); - uint32_t in_number_of_extensions = lsmash_count_codec_specific_data( a ); - uint32_t out_number_of_extensions = lsmash_count_codec_specific_data( b ); - if( out_number_of_extensions != in_number_of_extensions ) - return 1; - uint32_t active_number_of_extensions = in_number_of_extensions; - uint32_t identical_count = 0; - for( uint32_t j = 1; j <= in_number_of_extensions; j++ ) - { - lsmash_codec_specific_t *in_cs_orig = lsmash_get_codec_specific_data( a, j ); - lsmash_codec_specific_t *in_cs; - lsmash_codec_specific_format compare_format = LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED; - if( in_cs_orig->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - if( in_cs_orig->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON - || in_cs_orig->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON - || in_cs_orig->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS ) - { - compare_format = LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED; - in_cs = in_cs_orig; - } - else - { - in_cs = lsmash_convert_codec_specific_format( in_cs_orig, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !in_cs ) - { - /* We don't support the format converter of this data type. */ - --active_number_of_extensions; - continue; - } - } - } - else - in_cs = in_cs_orig; - for( uint32_t k = 1; k <= out_number_of_extensions; k++ ) - { - lsmash_codec_specific_t *out_cs_orig = lsmash_get_codec_specific_data( b, k ); - if( out_cs_orig->type != in_cs_orig->type ) - continue; - lsmash_codec_specific_t *out_cs; - if( out_cs_orig->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - { - if( compare_format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - out_cs = out_cs_orig; - else - { - out_cs = lsmash_convert_codec_specific_format( out_cs_orig, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - if( !out_cs ) - continue; - } - } - else - out_cs = out_cs_orig; - int identical; - if( compare_format == LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ) - identical = out_cs->size == in_cs->size && !memcmp( out_cs->data.unstructured, in_cs->data.unstructured, in_cs->size ); - else - { - if( in_cs->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_VIDEO_COMMON ) - { - lsmash_qt_video_common_t *in_data = (lsmash_qt_video_common_t *)in_cs->data.structured; - lsmash_qt_video_common_t *out_data = (lsmash_qt_video_common_t *)out_cs->data.structured; - identical = in_data->revision_level == out_data->revision_level - && in_data->vendor == out_data->vendor - && in_data->temporalQuality == out_data->temporalQuality - && in_data->spatialQuality == out_data->spatialQuality - && in_data->horizontal_resolution == out_data->horizontal_resolution - && in_data->vertical_resolution == out_data->vertical_resolution - && in_data->dataSize == out_data->dataSize - && in_data->frame_count == out_data->frame_count - && in_data->color_table_ID == out_data->color_table_ID; - } - else if( in_cs->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON ) - { - lsmash_qt_audio_common_t *in_data = (lsmash_qt_audio_common_t *)in_cs->data.structured; - lsmash_qt_audio_common_t *out_data = (lsmash_qt_audio_common_t *)out_cs->data.structured; - identical = in_data->revision_level == out_data->revision_level - && in_data->vendor == out_data->vendor - && in_data->compression_ID == out_data->compression_ID; - } - else - { - lsmash_qt_audio_format_specific_flags_t *in_data = (lsmash_qt_audio_format_specific_flags_t *)in_cs->data.structured; - lsmash_qt_audio_format_specific_flags_t *out_data = (lsmash_qt_audio_format_specific_flags_t *)out_cs->data.structured; - identical = (in_data->format_flags == out_data->format_flags); - } - } - if( out_cs != out_cs_orig ) - lsmash_destroy_codec_specific_data( out_cs ); - if( identical ) - { - ++identical_count; - break; - } - } - if( in_cs != in_cs_orig ) - lsmash_destroy_codec_specific_data( in_cs ); - } - return (identical_count != active_number_of_extensions); -} - -int isom_get_implicit_qt_fixed_comp_audio_sample_quants -( - isom_audio_entry_t *audio, - uint32_t *samples_per_packet, - uint32_t *constant_bytes_per_frame, - uint32_t *sample_size -) -{ - if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC3_AUDIO ) ) - { - *samples_per_packet = 6; - *constant_bytes_per_frame = 2 * audio->channelcount; - *sample_size = 8; - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_MAC6_AUDIO ) ) - { - *samples_per_packet = 6; - *constant_bytes_per_frame = audio->channelcount; - *sample_size = 8; - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ADPCM17_AUDIO ) ) - { - *samples_per_packet = 64; - *constant_bytes_per_frame = 34 * audio->channelcount; - *sample_size = 16; - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_AGSM_AUDIO ) ) - { - *samples_per_packet = 160; - *constant_bytes_per_frame = 33; - *sample_size = 16; - } - else if( lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ALAW_AUDIO ) - || lsmash_check_codec_type_identical( audio->type, QT_CODEC_TYPE_ULAW_AUDIO ) ) - { - *samples_per_packet = 1; - *constant_bytes_per_frame = audio->channelcount; - *sample_size = 16; - } - else - return 0; - return 1; -} diff -Nru l-smash-1.9.1/description.h l-smash-2.3.0/description.h --- l-smash-1.9.1/description.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/description.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -/***************************************************************************** - * description.h: - ***************************************************************************** - * Copyright (C) 2012-2014 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. */ - -struct lsmash_codec_specific_list_tag -{ - lsmash_entry_list_t list; -}; - -lsmash_codec_specific_t *isom_duplicate_codec_specific_data -( - lsmash_codec_specific_t *specific -); - -lsmash_codec_specific_t *isom_get_codec_specific -( - lsmash_codec_specific_list_t *opaque, - lsmash_codec_specific_data_type type -); - -uint8_t *isom_get_child_box_position -( - uint8_t *parent_data, - uint32_t parent_size, - lsmash_box_type_t child_type, - uint32_t *child_size -); - -int isom_setup_visual_description -( - isom_stsd_t *stsd, - lsmash_codec_type_t sample_type, - lsmash_video_summary_t *summary -); - -int isom_setup_audio_description -( - isom_stsd_t *stsd, - lsmash_codec_type_t sample_type, - lsmash_audio_summary_t *summary -); - -lsmash_summary_t *isom_create_video_summary_from_description -( - isom_sample_entry_t *sample_entry -); - -lsmash_summary_t *isom_create_audio_summary_from_description -( - isom_sample_entry_t *sample_entry -); - -int isom_compare_opaque_extensions -( - lsmash_summary_t *a, - lsmash_summary_t *b -); - -int isom_get_implicit_qt_fixed_comp_audio_sample_quants -( - isom_audio_entry_t *audio, - uint32_t *samples_per_packet, - uint32_t *constant_bytes_per_frame, - uint32_t *sample_size -); diff -Nru l-smash-1.9.1/dts.c l-smash-2.3.0/dts.c --- l-smash-1.9.1/dts.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/dts.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1689 +0,0 @@ -/***************************************************************************** - * dts.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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 - -#include "box.h" - -/*************************************************************************** - ETSI TS 102 114 V1.2.1 (2002-12) - ETSI TS 102 114 V1.3.1 (2011-08) - ETSI TS 102 114 V1.4.1 (2012-09) - - IMPLEMENTATION OF DTS AUDIO IN MEDIA FILES BASED ON ISO/IEC 14496 - Document No.: 9302J81100 - Revision: F - Version: 1.3 -***************************************************************************/ -#include "dts.h" - -#define DTS_MIN_CORE_SIZE 96 -#define DTS_MAX_STREAM_CONSTRUCTION 21 -#define DTS_SPECIFIC_BOX_MIN_LENGTH 28 - -typedef enum -{ - DTS_SYNCWORD_CORE = 0x7FFE8001, - DTS_SYNCWORD_XCH = 0x5A5A5A5A, - DTS_SYNCWORD_XXCH = 0x47004A03, - DTS_SYNCWORD_X96K = 0x1D95F262, - DTS_SYNCWORD_XBR = 0x655E315E, - DTS_SYNCWORD_LBR = 0x0A801921, - DTS_SYNCWORD_XLL = 0x41A29547, - DTS_SYNCWORD_SUBSTREAM = 0x64582025, - DTS_SYNCWORD_SUBSTREAM_CORE = 0x02b09261, -} dts_syncword; - -typedef enum -{ - DTS_XXCH_LOUDSPEAKER_MASK_C = 0x00000001, /* Centre in front of listener */ - DTS_XXCH_LOUDSPEAKER_MASK_L = 0x00000002, /* Left in front */ - DTS_XXCH_LOUDSPEAKER_MASK_R = 0x00000004, /* Right in front */ - DTS_XXCH_LOUDSPEAKER_MASK_LS = 0x00000008, /* Left surround on side in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_RS = 0x00000010, /* Right surround on side in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_LFE1 = 0x00000020, /* Low frequency effects subwoofer */ - DTS_XXCH_LOUDSPEAKER_MASK_CS = 0x00000040, /* Centre surround in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_LSR = 0x00000080, /* Left surround in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_RSR = 0x00000100, /* Right surround in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_LSS = 0x00000200, /* Left surround on side */ - DTS_XXCH_LOUDSPEAKER_MASK_RSS = 0x00000400, /* Right surround on side */ - DTS_XXCH_LOUDSPEAKER_MASK_LC = 0x00000800, /* Between left and centre in front */ - DTS_XXCH_LOUDSPEAKER_MASK_RC = 0x00001000, /* Between right and centre in front */ - DTS_XXCH_LOUDSPEAKER_MASK_LH = 0x00002000, /* Left height in front */ - DTS_XXCH_LOUDSPEAKER_MASK_CH = 0x00004000, /* Centre Height in front */ - DTS_XXCH_LOUDSPEAKER_MASK_RH = 0x00008000, /* Right Height in front */ - DTS_XXCH_LOUDSPEAKER_MASK_LFE2 = 0x00010000, /* Second low frequency effects subwoofer */ - DTS_XXCH_LOUDSPEAKER_MASK_LW = 0x00020000, /* Left on side in front */ - DTS_XXCH_LOUDSPEAKER_MASK_RW = 0x00040000, /* Right on side in front */ - DTS_XXCH_LOUDSPEAKER_MASK_OH = 0x00080000, /* Over the listener's head */ - DTS_XXCH_LOUDSPEAKER_MASK_LHS = 0x00100000, /* Left height on side */ - DTS_XXCH_LOUDSPEAKER_MASK_RHS = 0x00200000, /* Right height on side */ - DTS_XXCH_LOUDSPEAKER_MASK_CHR = 0x00400000, /* Centre height in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_LHR = 0x00800000, /* Left height in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_RHR = 0x01000000, /* Right height in rear */ - DTS_XXCH_LOUDSPEAKER_MASK_CL = 0x02000000, /* Centre in the plane lower than listener's ears */ - DTS_XXCH_LOUDSPEAKER_MASK_LL = 0x04000000, /* Left in the plane lower than listener's ears */ - DTS_XXCH_LOUDSPEAKER_MASK_RL = 0x08000000, /* Right in the plane lower than listener's ears */ -} dts_loudspeaker_mask; - -typedef enum -{ - DTS_CHANNEL_LAYOUT_C = 0x0001, /* Centre in front of listener */ - DTS_CHANNEL_LAYOUT_L_R = 0x0002, /* Left/Right in front */ - DTS_CHANNEL_LAYOUT_LS_RS = 0x0004, /* Left/Right surround on side in rear */ - DTS_CHANNEL_LAYOUT_LFE1 = 0x0008, /* Low frequency effects subwoofer */ - DTS_CHANNEL_LAYOUT_CS = 0x0010, /* Centre surround in rear */ - DTS_CHANNEL_LAYOUT_LH_RH = 0x0020, /* Left/Right height in front */ - DTS_CHANNEL_LAYOUT_LSR_RSR = 0x0040, /* Left/Right surround in rear */ - DTS_CHANNEL_LAYOUT_CH = 0x0080, /* Centre height in front */ - DTS_CHANNEL_LAYOUT_OH = 0x0100, /* Over the listener's head */ - DTS_CHANNEL_LAYOUT_LC_RC = 0x0200, /* Between left/right and centre in front */ - DTS_CHANNEL_LAYOUT_LW_RW = 0x0400, /* Left/Right on side in front */ - DTS_CHANNEL_LAYOUT_LSS_RSS = 0x0800, /* Left/Right surround on side */ - DTS_CHANNEL_LAYOUT_LFE2 = 0x1000, /* Second low frequency effects subwoofer */ - DTS_CHANNEL_LAYOUT_LHS_RHS = 0x2000, /* Left/Right height on side */ - DTS_CHANNEL_LAYOUT_CHR = 0x4000, /* Centre height in rear */ - DTS_CHANNEL_LAYOUT_LHR_RHR = 0x8000, /* Left/Right height in rear */ -} dts_channel_layout; - -static const lsmash_dts_construction_flag construction_info[DTS_MAX_STREAM_CONSTRUCTION + 1] = - { - 0, - DTS_CORE_SUBSTREAM_CORE_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_X96_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_XBR_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG, - DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_X96_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG, - DTS_EXT_SUBSTREAM_XLL_FLAG, - DTS_EXT_SUBSTREAM_LBR_FLAG, - DTS_EXT_SUBSTREAM_CORE_FLAG, - DTS_EXT_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG, - DTS_EXT_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG , - }; - -void dts_setup_parser( dts_info_t *info ) -{ - dts_extension_info_t *exss = &info->exss[0]; - /* By default the core substream data, if present, has the nuBcCoreExtSSIndex = 0 and the nuBcCoreAssetIndex = 0. - * Therefore, we can treat as if one extension substream is there even if no extension substreams. */ - exss->nuNumAudioPresnt = 1; - exss->nuNumAssets = 1; - exss->bBcCorePresent [0] = 0; - exss->nuBcCoreExtSSIndex[0] = 0; - exss->nuBcCoreAssetIndex[0] = 0; -} - -struct lsmash_dts_reserved_box_tag -{ - uint32_t size; - uint8_t *data; -}; - -int lsmash_append_dts_reserved_box( lsmash_dts_specific_parameters_t *param, uint8_t *box_data, uint32_t box_size ) -{ - if( !param || !box_data || box_size == 0 ) - return -1; - param->box = lsmash_malloc( sizeof(lsmash_dts_reserved_box_t) ); - if( !param->box ) - return -1; - param->box->data = lsmash_memdup( box_data, box_size ); - if( !param->box->data ) - { - lsmash_freep( ¶m->box ); - return -1; - } - param->box->size = box_size; - return 0; -} - -void lsmash_remove_dts_reserved_box( lsmash_dts_specific_parameters_t *param ) -{ - if( !param->box ) - return; - if( param->box->data ) - lsmash_free( param->box->data ); - lsmash_freep( ¶m->box ); -} - -void dts_destruct_specific_data( void *data ) -{ - if( !data ) - return; - lsmash_remove_dts_reserved_box( data ); - lsmash_free( data ); -} - -uint8_t lsmash_dts_get_stream_construction( lsmash_dts_construction_flag flags ) -{ - uint8_t StreamConstruction; - for( StreamConstruction = 1; StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION; StreamConstruction++ ) - if( flags == construction_info[StreamConstruction] ) - break; - /* For any stream type not listed in the above table, - * StreamConstruction shall be set to 0 and the codingname shall default to 'dtsh'. */ - return StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION ? StreamConstruction : 0; -} - -lsmash_dts_construction_flag lsmash_dts_get_construction_flags( uint8_t stream_construction ) -{ - if( stream_construction <= DTS_MAX_STREAM_CONSTRUCTION ) - return construction_info[stream_construction]; - return 0; -} - -lsmash_codec_type_t lsmash_dts_get_codingname( lsmash_dts_specific_parameters_t *param ) -{ - assert( param->StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION ); - if( param->MultiAssetFlag ) - return ISOM_CODEC_TYPE_DTSH_AUDIO; /* Multiple asset streams shall use the 'dtsh' coding_name. */ - static lsmash_codec_type_t codingname_table[DTS_MAX_STREAM_CONSTRUCTION + 1] = { LSMASH_CODEC_TYPE_INITIALIZER }; - if( lsmash_check_codec_type_identical( codingname_table[0], LSMASH_CODEC_TYPE_UNSPECIFIED ) ) - { - int i = 0; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; /* Undefined stream types shall be set to 0 and the codingname shall default to 'dtsh'. */ - codingname_table[i++] = ISOM_CODEC_TYPE_DTSC_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSC_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSC_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSE_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSH_AUDIO; - codingname_table[i++] = ISOM_CODEC_TYPE_DTSL_AUDIO; - } - return codingname_table[ param->StreamConstruction ]; -} - -uint8_t *lsmash_create_dts_specific_info( lsmash_dts_specific_parameters_t *param, uint32_t *data_length ) -{ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - lsmash_bits_init( &bits, &bs ); - int reserved_box_present = (param->box && param->box->data && param->box->size); - uint32_t buffer_length = DTS_SPECIFIC_BOX_MIN_LENGTH + (reserved_box_present ? param->box->size : 0); - uint8_t buffer[buffer_length]; - memset( buffer, 0, buffer_length ); - bs.buffer.data = buffer; - bs.buffer.alloc = buffer_length; - /* Create a DTSSpecificBox. */ - lsmash_bits_put( &bits, 32, 0 ); /* box size */ - lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DDTS.fourcc ); /* box type: 'ddts' */ - lsmash_bits_put( &bits, 32, param->DTSSamplingFrequency ); - lsmash_bits_put( &bits, 32, param->maxBitrate ); /* maxBitrate; setup by isom_update_bitrate_description */ - lsmash_bits_put( &bits, 32, param->avgBitrate ); /* avgBitrate; setup by isom_update_bitrate_description */ - lsmash_bits_put( &bits, 8, param->pcmSampleDepth ); - lsmash_bits_put( &bits, 2, param->FrameDuration ); - lsmash_bits_put( &bits, 5, param->StreamConstruction ); - lsmash_bits_put( &bits, 1, param->CoreLFEPresent ); - lsmash_bits_put( &bits, 6, param->CoreLayout ); - lsmash_bits_put( &bits, 14, param->CoreSize ); - lsmash_bits_put( &bits, 1, param->StereoDownmix ); - lsmash_bits_put( &bits, 3, param->RepresentationType ); - lsmash_bits_put( &bits, 16, param->ChannelLayout ); - lsmash_bits_put( &bits, 1, param->MultiAssetFlag ); - lsmash_bits_put( &bits, 1, param->LBRDurationMod ); - lsmash_bits_put( &bits, 1, reserved_box_present ); - lsmash_bits_put( &bits, 5, 0 ); /* Reserved */ - /* ReservedBox */ - if( reserved_box_present ) - for( uint32_t i = 0; i < param->box->size; i++ ) - lsmash_bits_put( &bits, 8, param->box->data[i] ); - /* */ - uint8_t *data = lsmash_bits_export_data( &bits, data_length ); - /* Update box size. */ - data[0] = ((*data_length) >> 24) & 0xff; - data[1] = ((*data_length) >> 16) & 0xff; - data[2] = ((*data_length) >> 8) & 0xff; - data[3] = (*data_length) & 0xff; - return data; -} - -int lsmash_setup_dts_specific_parameters_from_frame( lsmash_dts_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) -{ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t buffer[DTS_MAX_EXSS_SIZE] = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = DTS_MAX_EXSS_SIZE; - dts_info_t handler = { 0 }; - dts_info_t *info = &handler; - uint32_t overall_wasted_data_length = 0; - info->buffer_pos = info->buffer; - info->buffer_end = info->buffer; - info->bits = &bits; - lsmash_bits_init( &bits, &bs ); - dts_setup_parser( info ); - while( 1 ) - { - /* Check the remainder length of the buffer. - * If there is enough length, then continue to parse the frame in it. - * The length 10 is the required byte length to get frame size. */ - uint32_t remainder_length = info->buffer_end - info->buffer_pos; - if( !info->no_more_read && remainder_length < DTS_MAX_EXSS_SIZE ) - { - if( remainder_length ) - memmove( info->buffer, info->buffer_pos, remainder_length ); - uint32_t wasted_data_length = LSMASH_MIN( data_length, DTS_MAX_EXSS_SIZE ); - memcpy( info->buffer + remainder_length, data + overall_wasted_data_length, wasted_data_length ); - data_length -= wasted_data_length; - overall_wasted_data_length += wasted_data_length; - remainder_length += wasted_data_length; - info->buffer_pos = info->buffer; - info->buffer_end = info->buffer + remainder_length; - info->no_more_read = (data_length < 10); - } - if( remainder_length < 10 && info->no_more_read ) - goto setup_param; /* No more valid data. */ - /* Parse substream frame. */ - dts_substream_type prev_substream_type = info->substream_type; - info->substream_type = dts_get_substream_type( info ); - int (*dts_parse_frame)( dts_info_t *, uint8_t *, uint32_t ) = NULL; - switch( info->substream_type ) - { - /* Decide substream frame parser and check if this frame and the previous frame belong to the same AU. */ - case DTS_SUBSTREAM_TYPE_CORE : - if( prev_substream_type != DTS_SUBSTREAM_TYPE_NONE ) - goto setup_param; - dts_parse_frame = dts_parse_core_substream; - break; - case DTS_SUBSTREAM_TYPE_EXTENSION : - { - uint8_t prev_exss_index = info->exss_index; - if( dts_get_exss_index( info, &info->exss_index ) ) - return -1; - if( prev_substream_type == DTS_SUBSTREAM_TYPE_EXTENSION && info->exss_index <= prev_exss_index ) - goto setup_param; - dts_parse_frame = dts_parse_extension_substream; - break; - } - default : - return -1; - } - info->frame_size = 0; - if( dts_parse_frame( info, info->buffer_pos, LSMASH_MIN( remainder_length, DTS_MAX_EXSS_SIZE ) ) ) - return -1; /* Failed to parse. */ - info->buffer_pos += info->frame_size; - } -setup_param: - dts_update_specific_param( info ); - *param = info->ddts_param; - return 0; -} - -static uint64_t dts_bits_get( lsmash_bits_t *bits, uint32_t width, uint64_t *bits_pos ) -{ - *bits_pos += width; - return lsmash_bits_get( bits, width ); -} - -static int dts_get_channel_count_from_channel_layout( uint16_t channel_layout ) -{ -#define DTS_CHANNEL_PAIR_MASK \ - (DTS_CHANNEL_LAYOUT_L_R \ - | DTS_CHANNEL_LAYOUT_LS_RS \ - | DTS_CHANNEL_LAYOUT_LH_RH \ - | DTS_CHANNEL_LAYOUT_LSR_RSR \ - | DTS_CHANNEL_LAYOUT_LC_RC \ - | DTS_CHANNEL_LAYOUT_LW_RW \ - | DTS_CHANNEL_LAYOUT_LSS_RSS \ - | DTS_CHANNEL_LAYOUT_LHS_RHS \ - | DTS_CHANNEL_LAYOUT_LHR_RHR) - return lsmash_count_bits( channel_layout ) - + lsmash_count_bits( channel_layout & DTS_CHANNEL_PAIR_MASK ); -#undef DTS_CHANNEL_PAIR_MASK -} - -static uint32_t dts_get_channel_layout_from_xxch_mask( uint32_t mask ) -{ - uint32_t layout = 0; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_C ) - layout |= DTS_CHANNEL_LAYOUT_C; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_L | DTS_XXCH_LOUDSPEAKER_MASK_R) ) - layout |= DTS_CHANNEL_LAYOUT_L_R; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LS | DTS_XXCH_LOUDSPEAKER_MASK_RS) ) - layout |= DTS_CHANNEL_LAYOUT_LS_RS; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_LFE1 ) - layout |= DTS_CHANNEL_LAYOUT_LFE1; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_CS ) - layout |= DTS_CHANNEL_LAYOUT_CS; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LH | DTS_XXCH_LOUDSPEAKER_MASK_RH) ) - layout |= DTS_CHANNEL_LAYOUT_LH_RH; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LSR | DTS_XXCH_LOUDSPEAKER_MASK_RSR) ) - layout |= DTS_CHANNEL_LAYOUT_LSR_RSR; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_CH ) - layout |= DTS_CHANNEL_LAYOUT_CH; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_OH ) - layout |= DTS_CHANNEL_LAYOUT_OH; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LC | DTS_XXCH_LOUDSPEAKER_MASK_RC) ) - layout |= DTS_CHANNEL_LAYOUT_LC_RC; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LW | DTS_XXCH_LOUDSPEAKER_MASK_RW) ) - layout |= DTS_CHANNEL_LAYOUT_LW_RW; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LSS | DTS_XXCH_LOUDSPEAKER_MASK_RSS) ) - layout |= DTS_CHANNEL_LAYOUT_LSS_RSS; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_LFE2 ) - layout |= DTS_CHANNEL_LAYOUT_LFE2; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LHS | DTS_XXCH_LOUDSPEAKER_MASK_RHS) ) - layout |= DTS_CHANNEL_LAYOUT_LHS_RHS; - if( mask & DTS_XXCH_LOUDSPEAKER_MASK_CHR ) - layout |= DTS_CHANNEL_LAYOUT_CHR; - if( mask & (DTS_XXCH_LOUDSPEAKER_MASK_LHR | DTS_XXCH_LOUDSPEAKER_MASK_RHR) ) - layout |= DTS_CHANNEL_LAYOUT_LHR_RHR; - return layout; -} - -static void dts_parse_xll_navigation( lsmash_bits_t *bits, dts_xll_info_t *xll, int nuBits4ExSSFsize, uint64_t *bits_pos ) -{ - xll->size = dts_bits_get( bits, nuBits4ExSSFsize, bits_pos ) + 1; /* nuExSSXLLFsize (nuBits4ExSSFsize) */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bExSSXLLSyncPresent (1) */ - { - dts_bits_get( bits, 4, bits_pos ); /* nuPeakBRCntrlBuffSzkB (4) */ - int nuBitsInitDecDly = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuBitsInitDecDly (5) */ - dts_bits_get( bits, nuBitsInitDecDly, bits_pos ); /* nuInitLLDecDlyFrames (nuBitsInitDecDly) */ - dts_bits_get( bits, nuBits4ExSSFsize, bits_pos ); /* nuExSSXLLSyncOffset (nuBits4ExSSFsize) */ - } -} - -static void dts_parse_lbr_navigation( lsmash_bits_t *bits, dts_lbr_info_t *lbr, uint64_t *bits_pos ) -{ - lbr->size = dts_bits_get( bits, 14, bits_pos ); /* nuExSSLBRFsize (14) */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bExSSLBRSyncPresent (1) */ - dts_bits_get( bits, 2, bits_pos ); /* nuExSSLBRSyncDistInFrames (2) */ -} - -static int dts_parse_asset_descriptor( dts_info_t *info, uint64_t *bits_pos ) -{ - lsmash_bits_t *bits = info->bits; - dts_extension_info_t *exss = &info->exss[ info->exss_index ]; - /* Audio asset descriptor */ - uint64_t asset_descriptor_pos = *bits_pos; - int nuAssetDescriptFsize = dts_bits_get( bits, 9, bits_pos ) + 1; /* nuAssetDescriptFsize (9) */ - dts_audio_asset_t *asset = &exss->asset[ dts_bits_get( bits, 3, bits_pos ) ]; /* nuAssetIndex (3) */ - /* Static metadata */ - int bEmbeddedStereoFlag = 0; - int bEmbeddedSixChFlag = 0; - int nuTotalNumChs = 0; - if( exss->bStaticFieldsPresent ) - { - if( dts_bits_get( bits, 1, bits_pos ) ) /* bAssetTypeDescrPresent (1)*/ - dts_bits_get( bits, 4, bits_pos ); /* nuAssetTypeDescriptor (4) */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bLanguageDescrPresent (1) */ - dts_bits_get( bits, 24, bits_pos ); /* LanguageDescriptor (24) */ - if( dts_bits_get( bits, 1, bits_pos ) ) - { - int nuInfoTextByteSize = dts_bits_get( bits, 10, bits_pos ) + 1; /* nuInfoTextByteSize (10) */ - dts_bits_get( bits, nuInfoTextByteSize * 8, bits_pos ); /* InfoTextString (nuInfoTextByteSize) */ - } - int nuBitResolution = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuBitResolution (5) */ - exss->bit_resolution = LSMASH_MAX( exss->bit_resolution, nuBitResolution ); - int nuMaxSampleRate = dts_bits_get( bits, 4, bits_pos ); /* nuMaxSampleRate (4) */ - static const uint32_t source_sample_rate_table[16] = - { - 8000, 16000, 32000, 64000, 128000, - 22050, 44100, 88200, 176400, 352800, - 12000, 24000, 48000, 96000, 192000, 384000 - }; - exss->sampling_frequency = LSMASH_MAX( exss->sampling_frequency, source_sample_rate_table[nuMaxSampleRate] ); - nuTotalNumChs = dts_bits_get( bits, 8, bits_pos ) + 1; /* nuTotalNumChs (8) */ - asset->bOne2OneMapChannels2Speakers = dts_bits_get( bits, 1, bits_pos ); /* bOne2OneMapChannels2Speakers (1) */ - if( asset->bOne2OneMapChannels2Speakers ) - { - if( nuTotalNumChs > 2 ) - { - bEmbeddedStereoFlag = dts_bits_get( bits, 1, bits_pos ); /* bEmbeddedStereoFlag (1) */ - exss->stereo_downmix |= bEmbeddedStereoFlag; - } - if( nuTotalNumChs > 6 ) - bEmbeddedSixChFlag = dts_bits_get( bits, 1, bits_pos ); /* bEmbeddedSixChFlag (1) */ - int nuNumBits4SAMask; - if( dts_bits_get( bits, 1, bits_pos ) ) /* bSpkrMaskEnabled (1) */ - { - nuNumBits4SAMask = (dts_bits_get( bits, 2, bits_pos ) + 1) << 2; /* nuNumBits4SAMask (2) */ - asset->channel_layout |= dts_bits_get( bits, nuNumBits4SAMask, bits_pos ); /* nuSpkrActivityMask (nuNumBits4SAMask) */ - } - else - /* The specification doesn't mention the value of nuNumBits4SAMask if bSpkrMaskEnabled is set to 0. */ - nuNumBits4SAMask = 16; - int nuNumSpkrRemapSets = dts_bits_get( bits, 3, bits_pos ); - int nuStndrSpkrLayoutMask[8] = { 0 }; - for( int ns = 0; ns < nuNumSpkrRemapSets; ns++ ) - nuStndrSpkrLayoutMask[ns] = dts_bits_get( bits, nuNumBits4SAMask, bits_pos ); - for( int ns = 0; ns < nuNumSpkrRemapSets; ns++ ) - { - int nuNumSpeakers = dts_get_channel_count_from_channel_layout( nuStndrSpkrLayoutMask[ns] ); - int nuNumDecCh4Remap = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuNumDecCh4Remap[ns] (5) */ - for( int nCh = 0; nCh < nuNumSpeakers; nCh++ ) - { - uint32_t nuRemapDecChMask = dts_bits_get( bits, nuNumDecCh4Remap, bits_pos ); - int nCoef = lsmash_count_bits( nuRemapDecChMask ); - for( int nc = 0; nc < nCoef; nc++ ) - dts_bits_get( bits, 5, bits_pos ); /* nuSpkrRemapCodes[ns][nCh][nc] (5) */ - } - } - } - else - { - asset->nuRepresentationType = dts_bits_get( bits, 3, bits_pos ); /* nuRepresentationType (3) */ - if( asset->nuRepresentationType == 2 - || asset->nuRepresentationType == 3 ) - nuTotalNumChs = 2; - } - } - /* Dynamic metadata */ - int bDRCCoefPresent = dts_bits_get( bits, 1, bits_pos ); /* bDRCCoefPresent (1) */ - if( bDRCCoefPresent ) - dts_bits_get( bits, 8, bits_pos ); /* nuDRCCode (8) */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bDialNormPresent (1) */ - dts_bits_get( bits, 5, bits_pos ); /* nuDialNormCode (5) */ - if( bDRCCoefPresent && bEmbeddedStereoFlag ) - dts_bits_get( bits, 8, bits_pos ); /* nuDRC2ChDmixCode (8) */ - int bMixMetadataPresent; - if( exss->bMixMetadataEnbl ) - bMixMetadataPresent = dts_bits_get( bits, 1, bits_pos ); /* bMixMetadataPresent (1) */ - else - bMixMetadataPresent = 0; - if( bMixMetadataPresent ) - { - dts_bits_get( bits, 7, bits_pos ); /* bExternalMixFlag (1) - * nuPostMixGainAdjCode (7) */ - if( dts_bits_get( bits, 2, bits_pos ) < 3 ) /* nuControlMixerDRC (2) */ - dts_bits_get( bits, 3, bits_pos ); /* nuLimit4EmbeddedDRC (3) */ - else - dts_bits_get( bits, 8, bits_pos ); /* nuCustomDRCCode (8) */ - int bEnblPerChMainAudioScale = dts_bits_get( bits, 1, bits_pos ); /* bEnblPerChMainAudioScale (1) */ - for( uint8_t ns = 0; ns < exss->nuNumMixOutConfigs; ns++ ) - if( bEnblPerChMainAudioScale ) - for( uint8_t nCh = 0; nCh < exss->nNumMixOutCh[ns]; nCh++ ) - dts_bits_get( bits, 6, bits_pos ); /* nuMainAudioScaleCode[ns][nCh] (6) */ - else - dts_bits_get( bits, 6, bits_pos ); /* nuMainAudioScaleCode[ns][0] (6) */ - int nEmDM = 1; - int nDecCh[3] = { nuTotalNumChs, 0, 0 }; - if( bEmbeddedSixChFlag ) - { - nDecCh[nEmDM] = 6; - ++nEmDM; - } - if( bEmbeddedStereoFlag ) - { - nDecCh[nEmDM] = 2; - ++nEmDM; - } - for( uint8_t ns = 0; ns < exss->nuNumMixOutConfigs; ns++ ) - for( int nE = 0; nE < nEmDM; nE++ ) - for( int nCh = 0; nCh < nDecCh[nE]; nCh++ ) - { - int nuMixMapMask = dts_bits_get( bits, exss->nNumMixOutCh[ns], bits_pos ); /* nuMixMapMask (nNumMixOutCh[ns]) */ - int nuNumMixCoefs = lsmash_count_bits( nuMixMapMask ); - for( int nC = 0; nC < nuNumMixCoefs; nC++ ) - dts_bits_get( bits, 6, bits_pos ); /* nuMixCoeffs[ns][nE][nCh][nC] (6) */ - } - } - /* Decoder navigation data */ - asset->nuCodingMode = dts_bits_get( bits, 2, bits_pos ); /* nuCodingMode (2) */ - switch( asset->nuCodingMode ) - { - case 0 : /* DTS-HD Coding Mode that may contain multiple coding components */ - { - int nuCoreExtensionMask = dts_bits_get( bits, 12, bits_pos ); /* nuCoreExtensionMask (12) */ - asset->nuCoreExtensionMask = nuCoreExtensionMask; - if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_CORE_FLAG ) - { - asset->core.frame_size = dts_bits_get( bits, 14, bits_pos ) + 1; /* nuExSSCoreFsize (14) */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bExSSCoreSyncPresent (1) */ - dts_bits_get( bits, 2, bits_pos ); /* nuExSSCoreSyncDistInFrames (2) */ - } - if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XBR_FLAG ) - asset->xbr_size = dts_bits_get( bits, 14, bits_pos ) + 1; - if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XXCH_FLAG ) - asset->core.xxch.size = dts_bits_get( bits, 14, bits_pos ) + 1; - if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_X96_FLAG ) - asset->x96_size = dts_bits_get( bits, 12, bits_pos ) + 1; - if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_LBR_FLAG ) - dts_parse_lbr_navigation( bits, &asset->lbr, bits_pos ); - if( nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XLL_FLAG ) - dts_parse_xll_navigation( bits, &asset->xll, exss->nuBits4ExSSFsize, bits_pos ); - break; - } - case 1 : /* DTS-HD Loss-less coding mode without CBR component */ - dts_parse_xll_navigation( bits, &asset->xll, exss->nuBits4ExSSFsize, bits_pos ); - break; - case 2 : /* DTS-HD Low bit-rate mode */ - dts_parse_lbr_navigation( bits, &asset->lbr, bits_pos ); - break; - case 3 : /* Auxiliary coding mode */ - asset->aux_size = dts_bits_get( bits, 14, bits_pos ) + 1; /* nuExSSAuxFsize (14) */ - break; - default : - assert( 0 ); - break; - } - dts_bits_get( bits, nuAssetDescriptFsize * 8 - (*bits_pos - asset_descriptor_pos), bits_pos ); /* Skip remaining part of Audio asset descriptor. */ - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_xxch( dts_info_t *info, uint64_t *bits_pos, dts_xxch_info_t *xxch ) -{ - lsmash_bits_t *bits = info->bits; - /* XXCH Frame Header */ - uint64_t xxch_pos = *bits_pos - 32; /* SYNCXXCh (32) */ - uint64_t nuHeaderSizeXXCh = dts_bits_get( bits, 6, bits_pos ) + 1; /* nuHeaderSizeXXCh (6) */ - dts_bits_get( bits, 1, bits_pos ); /* bCRCPresent4ChSetHeaderXXCh (1) */ - int nuBits4SpkrMaskXXCh = dts_bits_get( bits, 5, bits_pos ) + 1; /* nuBits4SpkrMaskXXCh (5) */ - int nuNumChSetsInXXCh = dts_bits_get( bits, 2, bits_pos ) + 1; /* nuNumChSetsInXXCh (2) */ - for( int nChSet = 0; nChSet < nuNumChSetsInXXCh; nChSet++ ) - dts_bits_get( bits, 14, bits_pos ); /* pnuChSetFsizeXXCh[nChSet] - 1 (14) */ - /* A 5.1 decoder uses this AMODE to configure its decoded outputs to C, L, R, Ls and Rs layout. - * On the other hand a 7.1 decoder ignores the AMODE information from the core stream and uses - * instead the nuCoreSpkrActivityMask (C, L, R, LFE1, Lss and Rss) and the nuXXChSpkrLayoutMask - * (Lsr and Rsr) from the XXCh stream to get the original 7.1 speaker layout (C, L, R, LFE1, Lss, - * Rsr, Lsr and Rsr) and configures its outputs accordingly. */ - uint32_t xxch_mask = dts_bits_get( bits, nuBits4SpkrMaskXXCh, bits_pos ); /* nuCoreSpkrActivityMask (nuBits4SpkrMaskXXCh) */ - xxch->channel_layout |= dts_get_channel_layout_from_xxch_mask( xxch_mask ); - /* Store channels which cannot be expressed by ChannelLayout; CL, LL and RL. */ - xxch->lower_planes = (xxch_mask >> 25) & 0x7; - dts_bits_get( bits, nuHeaderSizeXXCh * 8 - (*bits_pos - xxch_pos), bits_pos ); /* Skip remaining part of XXCH Frame Header. */ - for( int nChSet = 0; nChSet < nuNumChSetsInXXCh; nChSet++ ) - { - /* XXCH Channel Set Header */ - xxch_pos = *bits_pos; - uint64_t nuXXChChSetHeaderSize = dts_bits_get( bits, 7, bits_pos ) + 1; /* nuXXChChSetHeaderSize (7)*/ - dts_bits_get( bits, 3, bits_pos ); /* nuChInChSetXXCh (3) */ - if( nuBits4SpkrMaskXXCh > 6 ) - { - xxch_mask = dts_bits_get( bits, nuBits4SpkrMaskXXCh - 6, bits_pos ) << 6; /* nuXXChSpkrLayoutMask (nuBits4SpkrMaskXXCh - 6) */ - xxch->channel_layout |= dts_get_channel_layout_from_xxch_mask( xxch_mask ); - xxch->lower_planes |= (xxch_mask >> 25) & 0x7; - } -#if 0 /* FIXME: Can we detect stereo downmixing from only XXCH data within the core substream? */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bDownMixCoeffCodeEmbedded (1) */ - { - int bDownMixEmbedded = dts_bits_get( bits, 1, bits_pos ); /* bDownMixEmbedded (1) */ - dts_bits_get( bits, 6, bits_pos ); /* nDmixScaleFactor (6) */ - uint32_t DownMixChMapMask[8]; - for( int nCh = 0; nCh < nuChInChSetXXCh; nCh++ ) - DownMixChMapMask[nCh] = dts_bits_get( bits, nuBits4SpkrMaskXXCh, bits_pos ); - } -#endif - dts_bits_get( bits, nuXXChChSetHeaderSize * 8 - (*bits_pos - xxch_pos), bits_pos ); /* Skip remaining part of XXCH Channel Set Header. */ - } - return 0; -} - -static int dts_parse_core_xxch( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) -{ - if( core->extension_audio_descriptor == 0 - || core->extension_audio_descriptor == 3 ) - return -1; - if( dts_parse_xxch( info, bits_pos, &core->xxch ) < 0 ) - return -1; - info->flags |= DTS_CORE_SUBSTREAM_XXCH_FLAG; - return info->bits->bs->error ? -1 : 0; -} - -static int dts_parse_exss_xxch( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) -{ - lsmash_bits_t *bits = info->bits; - if( DTS_SYNCWORD_XXCH != dts_bits_get( bits, 32, bits_pos ) ) - return -1; - if( dts_parse_xxch( info, bits_pos, &core->xxch ) < 0 ) - return -1; - info->flags |= DTS_EXT_SUBSTREAM_XXCH_FLAG; - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_core_x96( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) -{ - if( core->extension_audio_descriptor != 2 - && core->extension_audio_descriptor != 3 ) - return 0; /* Probably this is not an X96 extension. We skip this anyway. */ - lsmash_bits_t *bits = info->bits; - /* DTS_BCCORE_X96 Frame Header */ - /* SYNCX96 (32) */ - /* To reduce the probability of false synchronization caused by the presence of pseudo sync words, it is - * imperative to check the distance between the detected sync word and the end of current frame. This - * distance in bytes shall match the value of FSIZE96. */ - uint64_t FSIZE96 = ((lsmash_bs_show_byte( bits->bs, 0 ) << 4) - | ((lsmash_bs_show_byte( bits->bs, 1 ) >> 4) & 0x0F)) + 1; - if( core->frame_size * 8 != (*bits_pos - 32 + FSIZE96 * 8) ) - return 0; /* Encountered four emulation bytes (pseudo sync word). */ - dts_bits_get( bits, 16, bits_pos ); /* FSIZE96 (12) - * REVNO (4) */ - core->sampling_frequency *= 2; - core->frame_duration *= 2; - info->flags |= DTS_CORE_SUBSTREAM_X96_FLAG; - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_core_xch( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) -{ - if( core->extension_audio_descriptor != 0 - && core->extension_audio_descriptor != 3 ) - return 0; /* Probably this is not an XCh extension. We skip this anyway. */ - lsmash_bits_t *bits = info->bits; - /* XCH Frame Header */ - /* XChSYNC (32) */ - /* For compatibility reasons with legacy bitstreams the estimated distance in bytes is checked against - * the XChFSIZE+1 as well as the XChFSIZE. The XCh synchronization is pronounced if the distance matches - * either of these two values. */ - uint64_t XChFSIZE = (lsmash_bs_show_byte( bits->bs, 0 ) << 2) - | ((lsmash_bs_show_byte( bits->bs, 1 ) >> 6) & 0x03); - if( core->frame_size * 8 != (*bits_pos - 32 + (XChFSIZE + 1) * 8) - && core->frame_size * 8 != (*bits_pos - 32 + XChFSIZE * 8) ) - return 0; /* Encountered four emulation bytes (pseudo sync word). */ - if( ((lsmash_bs_show_byte( bits->bs, 1 ) >> 2) & 0xF) != 1 ) - return 0; /* A known value of AMODE is only 1. Otherwise just skip. */ - dts_bits_get( bits, 16, bits_pos ); /* XChFSIZE (10) - * AMODE (4) - * byte align (2) */ - core->channel_layout |= DTS_CHANNEL_LAYOUT_CS; - info->flags |= DTS_CORE_SUBSTREAM_XCH_FLAG; - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_exss_xbr( dts_info_t *info, uint64_t *bits_pos ) -{ - lsmash_bits_t *bits = info->bits; - /* XBR Frame Header */ - uint64_t xbr_pos = *bits_pos; - if( DTS_SYNCWORD_XBR != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCXBR (32) */ - return -1; - uint64_t nHeaderSizeXBR = dts_bits_get( bits, 6, bits_pos ) + 1; /* nHeaderSizeXBR (6) */ - dts_bits_get( bits, nHeaderSizeXBR * 8 - (*bits_pos - xbr_pos), bits_pos ); /* Skip the remaining bits in XBR Frame Header. */ - info->flags |= DTS_EXT_SUBSTREAM_XBR_FLAG; - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_exss_x96( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) -{ - lsmash_bits_t *bits = info->bits; - /* DTS_EXSUB_STREAM_X96 Frame Header */ - uint64_t x96_pos = *bits_pos; - if( DTS_SYNCWORD_X96K != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCX96 (32) */ - return -1; - uint64_t nHeaderSizeX96 = dts_bits_get( bits, 6, bits_pos ) + 1; /* nHeaderSizeXBR (6) */ - dts_bits_get( bits, nHeaderSizeX96 * 8 - (*bits_pos - x96_pos), bits_pos ); /* Skip the remaining bits in DTS_EXSUB_STREAM_X96 Frame Header. */ - /* What the fuck! The specification drops 'if' sentence. - * We assume the same behaviour for core substream. */ - core->sampling_frequency *= 2; - core->frame_duration *= 2; - info->flags |= DTS_EXT_SUBSTREAM_X96_FLAG; - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_exss_lbr( dts_info_t *info, uint64_t *bits_pos, dts_audio_asset_t *asset ) -{ - lsmash_bits_t *bits = info->bits; - dts_lbr_info_t *lbr = &asset->lbr; - if( DTS_SYNCWORD_LBR != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCEXTLBR (32) */ - return -1; - int ucFmtInfoCode = dts_bits_get( bits, 8, bits_pos ); - if( ucFmtInfoCode == 2 ) - { - /* LBR decoder initialization data */ - int nLBRSampleRateCode = dts_bits_get( bits, 8, bits_pos ); /* nLBRSampleRateCode (8) */ - int usLBRSpkrMask = dts_bits_get( bits, 16, bits_pos ); /* usLBRSpkrMask (16) */ - dts_bits_get( bits, 16, bits_pos ); /* nLBRversion (16) */ - int nLBRCompressedFlags = dts_bits_get( bits, 8, bits_pos ); /* nLBRCompressedFlags (8) */ - dts_bits_get( bits, 40, bits_pos ); /* nLBRBitRateMSnybbles (8) - * nLBROriginalBitRate_LSW (16) - * nLBRScaledBitRate_LSW (16) */ - static const uint32_t source_sample_rate_table[16] = - { - 8000, 16000, 32000, 0, 0, - 11025, 22050, 44100, 0, 0, - 12000, 24000, 48000, 0, 0, 0 - }; - enum LBRFlags - { - LBR_FLAG_24_BIT_SAMPLES = 0x01, /* 0b00000001 */ - LBR_FLAG_USE_LFE = 0x02, /* 0b00000010 */ - LBR_FLAG_BANDLMT_MASK = 0x1C, /* 0b00011100 */ - LBR_FLAG_STEREO_DOWNMIX = 0x20, /* 0b00100000 */ - LBR_FLAG_MULTICHANNEL_DOWNMIX = 0x40, /* 0b01000000 */ - }; - lbr->sampling_frequency = source_sample_rate_table[nLBRSampleRateCode]; - lbr->frame_duration = lbr->sampling_frequency < 16000 ? 1024 - : lbr->sampling_frequency < 32000 ? 2048 - : 4096; - lbr->channel_layout = ((usLBRSpkrMask >> 8) & 0xff) | ((usLBRSpkrMask << 8) & 0xff00); /* usLBRSpkrMask is little-endian. */ - lbr->stereo_downmix |= !!(nLBRCompressedFlags & LBR_FLAG_STEREO_DOWNMIX); - lbr->lfe_present |= !!(nLBRCompressedFlags & LBR_FLAG_USE_LFE); - lbr->duration_modifier |= ((nLBRCompressedFlags & LBR_FLAG_BANDLMT_MASK) == 0x04) - || ((nLBRCompressedFlags & LBR_FLAG_BANDLMT_MASK) == 0x0C); - lbr->sample_size = (nLBRCompressedFlags & LBR_FLAG_24_BIT_SAMPLES) ? 24 : 16; - } - else if( ucFmtInfoCode != 1 ) - return -1; /* unknown */ - info->flags |= DTS_EXT_SUBSTREAM_LBR_FLAG; - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_exss_xll( dts_info_t *info, uint64_t *bits_pos, dts_audio_asset_t *asset ) -{ - lsmash_bits_t *bits = info->bits; - dts_xll_info_t *xll = &asset->xll; - /* Common Header */ - uint64_t xll_pos = *bits_pos; - if( DTS_SYNCWORD_XLL != dts_bits_get( bits, 32, bits_pos ) ) /* SYNCXLL (32) */ - return -1; - dts_bits_get( bits, 4, bits_pos ); /* nVersion (4) */ - uint64_t nHeaderSize = dts_bits_get( bits, 8, bits_pos ) + 1; /* nHeaderSize (8) */ - int nBits4FrameFsize = dts_bits_get( bits, 5, bits_pos ) + 1; /* nBits4FrameFsize (5) */ - dts_bits_get( bits, nBits4FrameFsize, bits_pos ); /* nLLFrameSize (nBits4FrameFsize) */ - int nNumChSetsInFrame = dts_bits_get( bits, 4, bits_pos ) + 1; /* nNumChSetsInFrame (4) */ - uint16_t nSegmentsInFrame = 1 << dts_bits_get( bits, 4, bits_pos ); /* nSegmentsInFrame (4) */ - uint16_t nSmplInSeg = 1 << dts_bits_get( bits, 4, bits_pos ); /* nSmplInSeg (4) */ - dts_bits_get( bits, 5, bits_pos ); /* nBits4SSize (5) */ - dts_bits_get( bits, 3, bits_pos ); /* nBandDataCRCEn (2) - * bScalableLSBs (1) */ - int nBits4ChMask = dts_bits_get( bits, 5, bits_pos ) + 1; /* nBits4ChMask (5) */ - dts_bits_get( bits, nHeaderSize * 8 - (*bits_pos - xll_pos), bits_pos ); /* Skip the remaining bits in Common Header. */ - int sum_nChSetLLChannel = 0; - uint32_t nFs1 = 0; - int nNumFreqBands1 = 0; - xll->channel_layout = 0; - for( int nChSet = 0; nChSet < nNumChSetsInFrame; nChSet++ ) - { - /* Channel Set Sub-Header */ - xll_pos = *bits_pos; - uint64_t nChSetHeaderSize = dts_bits_get( bits, 10, bits_pos ) + 1; /* nChSetHeaderSize (10) */ - int nChSetLLChannel = dts_bits_get( bits, 4, bits_pos ) + 1; /* nChSetLLChannel (4) */ - dts_bits_get( bits, nChSetLLChannel, bits_pos ); /* nResidualChEncode (nChSetLLChannel) */ - uint8_t nBitResolution = dts_bits_get( bits, 5, bits_pos ) + 1; /* nBitResolution (5) */ - dts_bits_get( bits, 5, bits_pos ); /* nBitWidth (5) */ - xll->pcm_resolution = LSMASH_MAX( xll->pcm_resolution, nBitResolution ); - static const uint32_t source_sample_rate_table[16] = - { - 8000, 16000, 32000, 64000, 128000, - 22050, 44100, 88200, 176400, 352800, - 12000, 24000, 48000, 96000, 192000, 384000 - }; - int sFreqIndex = dts_bits_get( bits, 4, bits_pos ); /* sFreqIndex (4) */ - uint32_t nFs = source_sample_rate_table[sFreqIndex]; - dts_bits_get( bits, 2, bits_pos ); /* nFsInterpolate (2) */ - int nReplacementSet = dts_bits_get( bits, 2, bits_pos ); /* nReplacementSet (2) */ - if( nReplacementSet > 0 ) - dts_bits_get( bits, 1, bits_pos ); /* bActiveReplaceSet (1) */ - if( asset->bOne2OneMapChannels2Speakers ) - { - /* Downmix is allowed only when the encoded channel represents a signal feed to a corresponding loudspeaker. */ - int bPrimaryChSet = dts_bits_get( bits, 1, bits_pos ); /* bPrimaryChSet (1) */ - int bDownmixCoeffCodeEmbedded = dts_bits_get( bits, 1, bits_pos ); /* bDownmixCoeffCodeEmbedded (1) */ - int nLLDownmixType = 0x7; /* 0b111: Unused */ - if( bDownmixCoeffCodeEmbedded ) - { - dts_bits_get( bits, 1, bits_pos ); /* bDownmixEmbedded (1) */ - if( bPrimaryChSet ) - nLLDownmixType = dts_bits_get( bits, 3, bits_pos ); /* nLLDownmixType (3) */ - } - int bHierChSet = dts_bits_get( bits, 1, bits_pos ); /* bHierChSet (1) */ - if( bDownmixCoeffCodeEmbedded ) - { - /* N: the number of channels in the current channel set + 1 (+1 for the down scaling coefficients that prevent overflow) - * M: the number of channels that the current channel set is mixed into - * Downmix coefficients are transmitted using 9-bit codes. */ - static const int downmix_channel_count_table[8] = { 1, 2, 2, 3, 3, 4, 4, 0 }; - int N = nChSetLLChannel + 1; - int M = bPrimaryChSet ? downmix_channel_count_table[nLLDownmixType] : sum_nChSetLLChannel; - int nDownmixCoeffs = N * M; - dts_bits_get( bits, nDownmixCoeffs * 9, bits_pos ); /* DownmixCoeffs (nDownmixCoeffs * 9) */ - if( bPrimaryChSet && downmix_channel_count_table[nLLDownmixType] == 2 ) - xll->stereo_downmix |= 1; - } - if( bHierChSet ) - sum_nChSetLLChannel += nChSetLLChannel; - if( dts_bits_get( bits, 1, bits_pos ) ) /* bChMaskEnabled (1) */ - xll->channel_layout |= dts_bits_get( bits, nBits4ChMask, bits_pos ); /* nChMask (nBits4ChMask) */ - else - dts_bits_get( bits, 25 * nChSetLLChannel, bits_pos ); /* RadiusDelta[ch] (9) - * Theta[ch] (9) - * Phi[ch] (7) - * per channel */ - } - else - { - /* No downmixing is allowed and each channel set is the primary channel set. */ - if( dts_bits_get( bits, 1, bits_pos ) ) /* bMappingCoeffsPresent (1) */ - { - int nBitsCh2SpkrCoef = 6 + 2 * dts_bits_get( bits, 3, bits_pos ); /* nBitsCh2SpkrCoef (3) */ - int nNumSpeakerConfigs = dts_bits_get( bits, 2, bits_pos ) + 1; /* nNumSpeakerConfigs (2) */ - for( int nSpkrConf = 0; nSpkrConf < nNumSpeakerConfigs; nSpkrConf++ ) - { - int pnActiveChannelMask = dts_bits_get( bits, nChSetLLChannel, bits_pos ); /* pnActiveChannelMask[nSpkrConf] (nChSetLLChannel) */ - int pnNumSpeakers = dts_bits_get( bits, 6, bits_pos ) + 1; /* pnNumSpeakers[nSpkrConf] (6) */ - int bSpkrMaskEnabled = dts_bits_get( bits, 1, bits_pos ); /* bSpkrMaskEnabled (1) */ - if( bSpkrMaskEnabled ) - xll->channel_layout |= dts_bits_get( bits, nBits4ChMask, bits_pos ); /* nSpkrMask[nSpkrConf] (nBits4ChMask) */ - for( int nSpkr = 0; nSpkr < pnNumSpeakers; nSpkr++ ) - { - if( !bSpkrMaskEnabled ) - dts_bits_get( bits, 25, bits_pos ); /* ChSetSpeakerConfiguration (25) */ - for( int nCh = 0; nCh < nChSetLLChannel; nCh++ ) - if( pnActiveChannelMask & (1 << nCh) ) - dts_bits_get( bits, nBitsCh2SpkrCoef, bits_pos ); /* pnCh2SpkrMapCoeff (nBitsCh2SpkrCoef) */ - } - } - } - } - int nNumFreqBands; - if( nFs > 96000 ) - { - if( dts_bits_get( bits, 1, bits_pos ) ) /* bXtraFreqBands (1) */ - nNumFreqBands = nFs > 192000 ? 4 : 2; - else - nNumFreqBands = nFs > 192000 ? 2 : 1; - } - else - nNumFreqBands = 1; - uint32_t nSmplInSeg_nChSet; - if( nChSet == 0 ) - { - nFs1 = nFs; - nNumFreqBands1 = nNumFreqBands; - nSmplInSeg_nChSet = nSmplInSeg; - } - else - nSmplInSeg_nChSet = (nSmplInSeg * (nFs * nNumFreqBands1)) / (nFs1 * nNumFreqBands); - if( xll->sampling_frequency < nFs ) - { - xll->sampling_frequency = nFs; - uint32_t samples_per_band_in_frame = nSegmentsInFrame * nSmplInSeg_nChSet; - xll->frame_duration = samples_per_band_in_frame * nNumFreqBands; - } - dts_bits_get( bits, nChSetHeaderSize * 8 - (*bits_pos - xll_pos), bits_pos ); /* Skip the remaining bits in Channel Set Sub-Header. */ - } - info->flags |= DTS_EXT_SUBSTREAM_XLL_FLAG; - return bits->bs->error ? -1 : 0; -} - -static uint16_t dts_generate_channel_layout_from_core( int channel_arrangement ) -{ - static const uint16_t channel_layout_map_table[] = - { - DTS_CHANNEL_LAYOUT_C, - DTS_CHANNEL_LAYOUT_L_R, /* dual mono */ - DTS_CHANNEL_LAYOUT_L_R, /* stereo */ - DTS_CHANNEL_LAYOUT_L_R, /* sum-difference */ - DTS_CHANNEL_LAYOUT_L_R, /* Lt/Rt */ - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R, - DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_CS, - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_CS, - DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LS_RS, - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LS_RS, - DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LS_RS, - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LSR_RSR | DTS_CHANNEL_LAYOUT_OH, - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_CS | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LSR_RSR, - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_LS_RS, - DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_LS_RS | DTS_CHANNEL_LAYOUT_LSR_RSR, - DTS_CHANNEL_LAYOUT_C | DTS_CHANNEL_LAYOUT_CS | DTS_CHANNEL_LAYOUT_L_R | DTS_CHANNEL_LAYOUT_LC_RC | DTS_CHANNEL_LAYOUT_LS_RS - }; - return channel_arrangement < 16 ? channel_layout_map_table[channel_arrangement] : 0; -} - -static int dts_parse_core( dts_info_t *info, uint64_t *bits_pos, dts_core_info_t *core ) -{ - lsmash_bits_t *bits = info->bits; - memset( core, 0, sizeof(dts_core_info_t) ); - /* SYNC (32) */ - int frame_type = dts_bits_get( bits, 1, bits_pos ); /* FTYPE (1) */ - int deficit_sample_count = dts_bits_get( bits, 5, bits_pos ); /* SHORT (5) */ - if( frame_type == 1 && deficit_sample_count != 31 ) - return -1; /* Any normal frame (FTYPE == 1) must have SHORT == 31. */ - int crc_present_flag = dts_bits_get( bits, 1, bits_pos ); /* CPF (1) */ - int num_of_pcm_sample_blocks = dts_bits_get( bits, 7, bits_pos ) + 1; /* NBLKS (7) */ - if( num_of_pcm_sample_blocks <= 5 ) - return -1; - core->frame_duration = 32 * num_of_pcm_sample_blocks; - if( frame_type == 1 - && core->frame_duration != 256 - && core->frame_duration != 512 && core->frame_duration != 1024 - && core->frame_duration != 2048 && core->frame_duration != 4096 ) - return -1; /* For any normal frame, the actual number of PCM core samples per channel must be - * either 4096, 2048, 1024, 512, or 256 samples per channel. */ - core->frame_size = dts_bits_get( bits, 14, bits_pos ) + 1; /* FSIZE (14) */ - if( core->frame_size < DTS_MIN_CORE_SIZE ) - return -1; - core->channel_arrangement = dts_bits_get( bits, 6, bits_pos ); /* AMODE (6) */ - core->channel_layout = dts_generate_channel_layout_from_core( core->channel_arrangement ); - int core_audio_sampling_frequency = dts_bits_get( bits, 4, bits_pos ); /* SFREQ (4) */ - static const uint32_t sampling_frequency_table[16] = - { - 0, - 8000, 16000, 32000, 0, 0, - 11025, 22050, 44100, 0, 0, - 12000, 24000, 48000, 0, 0 - }; - core->sampling_frequency = sampling_frequency_table[core_audio_sampling_frequency]; - if( core->sampling_frequency == 0 ) - return -1; /* invalid */ - dts_bits_get( bits, 10, bits_pos ); /* Skip remainder 10 bits. - * RATE (5) - * MIX (1) - * DYNF (1) - * TIMEF (1) - * AUXF (1) - * HDCD (1) */ - core->extension_audio_descriptor = dts_bits_get( bits, 3, bits_pos ); /* EXT_AUDIO_ID (3) - * Note: EXT_AUDIO_ID == 3 is defined in V1.2.1. - * However, its definition disappears and is reserved in V1.3.1. */ - int extended_coding_flag = dts_bits_get( bits, 1, bits_pos ); /* EXT_AUDIO (1) */ - dts_bits_get( bits, 1, bits_pos ); /* ASPF (1) */ - int low_frequency_effects_flag = dts_bits_get( bits, 2, bits_pos ); /* LFF (2) */ - if( low_frequency_effects_flag == 0x3 ) - return -1; /* invalid */ - if( low_frequency_effects_flag ) - core->channel_layout |= DTS_CHANNEL_LAYOUT_LFE1; - dts_bits_get( bits, 8 + crc_present_flag * 16, bits_pos ); /* HFLAG (1) - * HCRC (16) - * FILTS (1) - * VERNUM (4) - * CHIST (2) */ - int PCMR = dts_bits_get( bits, 3, bits_pos ); /* PCMR (3) */ - static const uint8_t source_resolution_table[8] = { 16, 16, 20, 20, 0, 24, 24, 0 }; - core->pcm_resolution = source_resolution_table[PCMR]; - if( core->pcm_resolution == 0 ) - return -1; /* invalid */ - dts_bits_get( bits, 6, bits_pos ); /* SUMF (1) - * SUMS (1) - * DIALNORM/UNSPEC (4) */ - if( extended_coding_flag ) - { - uint32_t syncword = dts_bits_get( bits, 24, bits_pos ); - uint64_t frame_size_bits = core->frame_size * 8; - while( (*bits_pos + 24) < frame_size_bits ) - { - syncword = ((syncword << 8) & 0xffffff00) | dts_bits_get( bits, 8, bits_pos ); - switch( syncword ) - { - case DTS_SYNCWORD_XXCH : - if( dts_parse_core_xxch( info, bits_pos, core ) ) - return -1; - syncword = dts_bits_get( bits, 24, bits_pos ); - break; - case DTS_SYNCWORD_X96K : - if( dts_parse_core_x96( info, bits_pos, core ) ) - return -1; - syncword = dts_bits_get( bits, 24, bits_pos ); - break; - case DTS_SYNCWORD_XCH : - if( dts_parse_core_xch( info, bits_pos, core ) ) - return -1; - break; - default : - continue; - } - } - } - return bits->bs->error ? -1 : 0; -} - -static int dts_parse_exss_core( dts_info_t *info, uint64_t *bits_pos, dts_audio_asset_t *asset ) -{ - lsmash_bits_t *bits = info->bits; - if( DTS_SYNCWORD_SUBSTREAM_CORE != dts_bits_get( bits, 32, bits_pos ) ) - return -1; - if( dts_parse_core( info, bits_pos, &asset->core ) < 0 ) - return -1; - info->flags |= DTS_EXT_SUBSTREAM_CORE_FLAG; - return bits->bs->error ? -1 : 0; -} - -int dts_parse_core_substream( dts_info_t *info, uint8_t *data, uint32_t data_length ) -{ - lsmash_bits_t *bits = info->bits; - if( lsmash_bits_import_data( info->bits, data, data_length ) ) - return -1; - uint64_t bits_pos = 0; - if( DTS_SYNCWORD_CORE != dts_bits_get( bits, 32, &bits_pos ) ) - goto parse_fail; - /* By default the core substream data, if present, has the nuBcCoreExtSSIndex = 0 and the nuBcCoreAssetIndex = 0. */ - dts_extension_info_t *exss = &info->exss[0]; - if( dts_parse_core( info, &bits_pos, &exss->asset[0].core ) < 0 ) - goto parse_fail; - exss->bBcCorePresent [0] = 1; - exss->nuBcCoreExtSSIndex[0] = 0; - exss->nuBcCoreAssetIndex[0] = 0; - info->flags |= DTS_CORE_SUBSTREAM_CORE_FLAG; - info->exss_count = 0; - info->core = exss->asset[0].core; - info->frame_size = exss->asset[0].core.frame_size; - lsmash_bits_empty( bits ); - return 0; -parse_fail: - lsmash_bits_empty( bits ); - return -1; -} - -int dts_parse_extension_substream( dts_info_t *info, uint8_t *data, uint32_t data_length ) -{ - lsmash_bits_t *bits = info->bits; - if( lsmash_bits_import_data( info->bits, data, data_length ) ) - return -1; - uint64_t bits_pos = 0; - dts_bits_get( bits, 40, &bits_pos ); /* SYNCEXTSSH (32) - * UserDefinedBits (8) */ - int nExtSSIndex = dts_bits_get( bits, 2, &bits_pos ); /* nExtSSIndex (2) */ - info->exss_index = nExtSSIndex; - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - memset( exss, 0, sizeof(dts_extension_info_t) ); - int bHeaderSizeType = dts_bits_get( bits, 1, &bits_pos ); /* bHeaderSizeType (1) */ - int nuBits4Header = 8 + bHeaderSizeType * 4; - int nuBits4ExSSFsize = 16 + bHeaderSizeType * 4; - exss->nuBits4ExSSFsize = nuBits4ExSSFsize; - uint32_t nuExtSSHeaderSize = dts_bits_get( bits, nuBits4Header, &bits_pos ) + 1; /* nuExtSSHeaderSize (8 or 12) */ - info->frame_size = dts_bits_get( bits, nuBits4ExSSFsize, &bits_pos ) + 1; /* nuExtSSFsize (16 or 20) */ - if( info->frame_size < 10 ) - return -1; - exss->bStaticFieldsPresent = dts_bits_get( bits, 1, &bits_pos ); /* bStaticFieldsPresent (1) */ - if( exss->bStaticFieldsPresent ) - { - dts_bits_get( bits, 2, &bits_pos ); /* nuRefClockCode (2) */ - exss->frame_duration = 512 * (dts_bits_get( bits, 3, &bits_pos ) + 1); /* nuExSSFrameDurationCode (3) */ - if( dts_bits_get( bits, 1, &bits_pos ) ) /* bTimeStampFlag (1) */ - dts_bits_get( bits, 36, &bits_pos ); /* nuTimeStamp (32) - * nLSB (4) */ - exss->nuNumAudioPresnt = dts_bits_get( bits, 3, &bits_pos ) + 1; /* nuNumAudioPresnt (3) */ - exss->nuNumAssets = dts_bits_get( bits, 3, &bits_pos ) + 1; /* nuNumAssets (3) */ - /* The extension substreams with indexes lower than or equal to the index of the current extension substream can - * be activated in the audio presentations indicated within the current extension substream. */ - for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) - exss->nuActiveExSSMask[nAuPr] - = dts_bits_get( bits, nExtSSIndex + 1, &bits_pos ); /* nuActiveExSSMask[nAuPr] (nExtSSIndex + 1) */ - for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) - for( uint8_t nSS = 0; nSS <= nExtSSIndex; nSS++ ) - exss->nuActiveAssetMask[nAuPr][nSS] - = ((exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1) - ? dts_bits_get( bits, 8, &bits_pos ) /* nuActiveAssetMask[nAuPr][nSS] (8) */ - : 0; - exss->bMixMetadataEnbl = dts_bits_get( bits, 1, &bits_pos ); /* bMixMetadataEnbl (1) */ - if( exss->bMixMetadataEnbl ) - { - dts_bits_get( bits, 2, &bits_pos ); /* nuMixMetadataAdjLevel (2) */ - int nuBits4MixOutMask = (dts_bits_get( bits, 2, &bits_pos ) + 1) << 2; /* nuBits4MixOutMask (2) */ - exss->nuNumMixOutConfigs = dts_bits_get( bits, 2, &bits_pos ) + 1; /* nuNumMixOutConfigs (2) */ - for( int ns = 0; ns < exss->nuNumMixOutConfigs; ns++ ) - { - int nuMixOutChMask = dts_bits_get( bits, nuBits4MixOutMask, &bits_pos ); /* nuMixOutChMask[ns] (nuBits4MixOutMask) */ - exss->nNumMixOutCh[ns] = dts_get_channel_count_from_channel_layout( nuMixOutChMask ); - } - } - } - else - { - exss->nuNumAudioPresnt = 1; - exss->nuNumAssets = 1; - exss->bMixMetadataEnbl = 0; - exss->nuNumMixOutConfigs = 0; - } - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - exss->asset[nAst].size = dts_bits_get( bits, nuBits4ExSSFsize, &bits_pos ) + 1; /* nuAssetFsize[nAst] - 1 (nuBits4ExSSFsize) */ - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - if( dts_parse_asset_descriptor( info, &bits_pos ) ) - goto parse_fail; - for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) - exss->bBcCorePresent[nAuPr] = dts_bits_get( bits, 1, &bits_pos ); - for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) - if( exss->bBcCorePresent[nAuPr] ) - { - exss->nuBcCoreExtSSIndex[nAuPr] = dts_bits_get( bits, 2, &bits_pos ); - exss->nuBcCoreAssetIndex[nAuPr] = dts_bits_get( bits, 3, &bits_pos ); - } - dts_bits_get( bits, nuExtSSHeaderSize * 8 - bits_pos, &bits_pos ); - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - { - /* Asset Data */ - dts_audio_asset_t *asset = &exss->asset[nAst]; - uint32_t asset_pos = bits_pos; - switch( asset->nuCodingMode ) - { - case 0 : /* DTS-HD Coding Mode that may contain multiple coding components */ - { - if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_CORE_FLAG ) - { - /* Core component */ - uint64_t core_pos = bits_pos; - if( dts_parse_exss_core( info, &bits_pos, asset ) < 0 ) - goto parse_fail; - dts_bits_get( bits, asset->core.frame_size * 8 - (bits_pos - core_pos), &bits_pos ); - } - if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XBR_FLAG ) - { - /* XBR extension */ - uint64_t xbr_pos = bits_pos; - if( dts_parse_exss_xbr( info, &bits_pos ) < 0 ) - goto parse_fail; - dts_bits_get( bits, asset->xbr_size * 8 - (bits_pos - xbr_pos), &bits_pos ); - } - if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XXCH_FLAG ) - { - /* XXCH extension */ - uint64_t xxch_pos = bits_pos; - if( dts_parse_exss_xxch( info, &bits_pos, &asset->core ) < 0 ) - goto parse_fail; - dts_bits_get( bits, asset->core.xxch.size * 8 - (bits_pos - xxch_pos), &bits_pos ); - } - if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_X96_FLAG ) - { - /* X96 extension */ - uint64_t x96_pos = bits_pos; - if( dts_parse_exss_x96( info, &bits_pos, &asset->core ) < 0 ) - goto parse_fail; - dts_bits_get( bits, asset->x96_size * 8 - (bits_pos - x96_pos), &bits_pos ); - } - if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_LBR_FLAG ) - { - /* LBR component */ - uint64_t lbr_pos = bits_pos; - if( dts_parse_exss_lbr( info, &bits_pos, asset ) < 0 ) - goto parse_fail; - dts_bits_get( bits, asset->lbr.size * 8 - (bits_pos - lbr_pos), &bits_pos ); - } - if( asset->nuCoreExtensionMask & DTS_EXT_SUBSTREAM_XLL_FLAG ) - { - /* Lossless extension */ - uint64_t xll_pos = bits_pos; - if( dts_parse_exss_xll( info, &bits_pos, asset ) < 0 ) - goto parse_fail; - dts_bits_get( bits, asset->xll.size * 8 - (bits_pos - xll_pos), &bits_pos ); - } - break; - } - case 1 : /* DTS-HD Loss-less coding mode without CBR component */ - if( dts_parse_exss_xll( info, &bits_pos, asset ) < 0 ) - goto parse_fail; - break; - case 2 : /* DTS-HD Low bit-rate mode */ - if( dts_parse_exss_lbr( info, &bits_pos, asset ) < 0 ) - goto parse_fail; - break; - case 3 : /* Auxiliary coding mode */ - dts_bits_get( bits, asset->aux_size * 8, &bits_pos ); - break; - } - dts_bits_get( bits, asset->size * 8 - (bits_pos - asset_pos), &bits_pos ); - } - dts_bits_get( bits, info->frame_size * 8 - bits_pos, &bits_pos ); - lsmash_bits_empty( bits ); - if( info->exss_count < DTS_MAX_NUM_EXSS ) - info->exss_count += 1; - return 0; -parse_fail: - lsmash_bits_empty( bits ); - return -1; -} - -dts_substream_type dts_get_substream_type( dts_info_t *info ) -{ - if( info->buffer_end - info->buffer_pos < 4 ) - return DTS_SUBSTREAM_TYPE_NONE; - uint8_t *buffer = info->buffer_pos; - uint32_t syncword = LSMASH_4CC( buffer[0], buffer[1], buffer[2], buffer[3] ); - switch( syncword ) - { - case DTS_SYNCWORD_CORE : - return DTS_SUBSTREAM_TYPE_CORE; - case DTS_SYNCWORD_SUBSTREAM : - return DTS_SUBSTREAM_TYPE_EXTENSION; - default : - return DTS_SUBSTREAM_TYPE_NONE; - } -} - -int dts_get_exss_index( dts_info_t *info, uint8_t *exss_index ) -{ - if( info->buffer_end - info->buffer_pos < 6 ) - return -1; - *exss_index = info->buffer_pos[5] >> 6; - return 0; -} - -int dts_get_max_channel_count( dts_info_t *info ) -{ - int max_channel_count = 0; - for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) - { - uint16_t channel_layout = 0; - int channel_count = 0; - if( exss->bBcCorePresent [nAuPr] - && exss->nuBcCoreAssetIndex[nAuPr] < exss->nuNumAssets ) - { - dts_core_info_t *core = &info->exss[ exss->nuBcCoreExtSSIndex[nAuPr] ].asset[ exss->nuBcCoreAssetIndex[nAuPr] ].core; - if( core->xxch.channel_layout | core->xxch.lower_planes ) - { - channel_layout = core->xxch.channel_layout; - channel_count = lsmash_count_bits( core->xxch.lower_planes ); /* FIXME: Should we count these channels? */ - } - else - channel_layout = core->channel_layout; - } - uint16_t ext_channel_layout = 0; - uint16_t lbr_channel_layout = 0; - uint16_t xll_channel_layout = 0; - for( int nSS = 0; nSS <= nExtSSIndex; nSS++ ) - if( (exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1 ) - { - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - if( (exss->nuActiveAssetMask[nAuPr][nSS] >> nAst) & 0x1 ) - { - dts_audio_asset_t *asset = &exss->asset[nAst]; - ext_channel_layout |= asset->channel_layout; - lbr_channel_layout |= asset->lbr.channel_layout; - xll_channel_layout |= asset->xll.channel_layout; - } - } - channel_count += dts_get_channel_count_from_channel_layout( channel_layout ); - max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); - channel_count = dts_get_channel_count_from_channel_layout( ext_channel_layout ); - max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); - channel_count = dts_get_channel_count_from_channel_layout( lbr_channel_layout ); - max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); - channel_count = dts_get_channel_count_from_channel_layout( xll_channel_layout ); - max_channel_count = LSMASH_MAX( max_channel_count, channel_count ); - } - } - return max_channel_count; -} - -void dts_update_specific_param( dts_info_t *info ) -{ - lsmash_dts_specific_parameters_t *param = &info->ddts_param; - int exss_index_start = 0; - for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - if( exss->nuNumAudioPresnt && exss->nuNumAssets ) - { - exss_index_start = nExtSSIndex; - break; - } - } - /* DTSSamplingFrequency and FrameDuration */ - for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) - continue; - if( param->DTSSamplingFrequency <= exss->sampling_frequency ) - { - param->DTSSamplingFrequency = exss->sampling_frequency; - info->frame_duration = exss->frame_duration; - } - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - { - dts_audio_asset_t *asset = &exss->asset[nAst]; - if( param->DTSSamplingFrequency <= asset->core.sampling_frequency ) - { - param->DTSSamplingFrequency = asset->core.sampling_frequency; - info->frame_duration = asset->core.frame_duration; - } - if( param->DTSSamplingFrequency <= asset->lbr.sampling_frequency ) - { - param->DTSSamplingFrequency = asset->lbr.sampling_frequency; - info->frame_duration = asset->lbr.frame_duration; - } - if( param->DTSSamplingFrequency <= asset->xll.sampling_frequency ) - { - param->DTSSamplingFrequency = asset->xll.sampling_frequency; - info->frame_duration = asset->xll.frame_duration; - } - } - } - param->FrameDuration = 0; - for( uint32_t frame_duration = info->frame_duration >> 10; frame_duration; frame_duration >>= 1 ) - ++ param->FrameDuration; - /* pcmSampleDepth */ - param->pcmSampleDepth = 0; - for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) - continue; - param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, exss->bit_resolution ); - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - { - dts_audio_asset_t *asset = &exss->asset[nAst]; - param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, asset->core.pcm_resolution ); - param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, asset->lbr.sample_size ); - param->pcmSampleDepth = LSMASH_MAX( param->pcmSampleDepth, asset->xll.pcm_resolution ); - } - } - param->pcmSampleDepth = param->pcmSampleDepth > 16 ? 24 : 16; - /* StreamConstruction */ - param->StreamConstruction = lsmash_dts_get_stream_construction( info->flags ); - /* CoreLFEPresent */ - param->CoreLFEPresent = !!(info->core.channel_layout & DTS_CHANNEL_LAYOUT_LFE1); - /* CoreLayout */ - if( param->StreamConstruction == 0 /* Unknown */ - || param->StreamConstruction >= 17 /* No core substream */ ) - /* Use ChannelLayout. */ - param->CoreLayout = 31; - else - { - if( info->core.channel_arrangement != 1 - && info->core.channel_arrangement != 3 - && info->core.channel_arrangement <= 9 ) - param->CoreLayout = info->core.channel_arrangement; - else - /* Use ChannelLayout. */ - param->CoreLayout = 31; - } - /* CoreSize - * The specification says this field is the size of a core substream AU in bytes. - * If we don't assume CoreSize is the copy of FSIZE, when FSIZE equals 0x3FFF, this field overflows and becomes 0. */ - param->CoreSize = info->core.frame_size ? LSMASH_MIN( info->core.frame_size - 1, 0x3FFF ) : 0; - /* StereoDownmix */ - param->StereoDownmix = 0; - for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - param->StereoDownmix |= exss->stereo_downmix; - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - { - param->StereoDownmix |= exss->asset[nAst].lbr.stereo_downmix; - param->StereoDownmix |= exss->asset[nAst].xll.stereo_downmix; - } - } - /* RepresentationType - * Available only when core substream is absent and ChannelLayout is set to 0. */ - for( int nExtSSIndex = exss_index_start; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) - continue; - for( uint8_t nAuPr = 0; nAuPr < exss->nuNumAudioPresnt; nAuPr++ ) - { - int asset_count = 0; - for( int nSS = 0; nSS <= nExtSSIndex; nSS++ ) - if( (exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1 ) - asset_count += lsmash_count_bits( exss->nuActiveAssetMask[nAuPr][nSS] ); - if( asset_count > 1 ) - { - /* An audio presentation has mulple audio assets. - * Audio asset designated for mixing with another audio asset. */ - param->RepresentationType = 0; - nExtSSIndex = DTS_MAX_NUM_EXSS; - break; - } - for( int nSS = 0; nSS <= nExtSSIndex; nSS++ ) - if( (exss->nuActiveExSSMask[nAuPr] >> nSS) & 0x1 ) - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - if( (exss->nuActiveAssetMask[nAuPr][nSS] >> nAst) & 0x1 ) - { - dts_audio_asset_t *asset = &exss->asset[nAst]; - if( asset->nuRepresentationType == info->exss[exss_index_start].asset[0].nuRepresentationType ) - param->RepresentationType = asset->nuRepresentationType; - else - { - /* Detected different representation types. Use ChannelLayout. */ - param->RepresentationType = 0; - nAuPr = exss->nuNumAudioPresnt; - nExtSSIndex = DTS_MAX_NUM_EXSS; - break; - } - } - } - } - /* ChannelLayout - * complete information on channels coded in the audio stream including core and extensions */ - param->ChannelLayout = 0; - if( param->RepresentationType == 0 ) - for( int nExtSSIndex = 0; nExtSSIndex < DTS_MAX_NUM_EXSS; nExtSSIndex++ ) - { - dts_extension_info_t *exss = &info->exss[nExtSSIndex]; - if( exss->nuNumAudioPresnt == 0 || exss->nuNumAssets == 0 ) - continue; - for( uint8_t nAst = 0; nAst < exss->nuNumAssets; nAst++ ) - { - dts_audio_asset_t *asset = &exss->asset[nAst]; - param->ChannelLayout |= asset->channel_layout; - param->ChannelLayout |= asset->core.channel_layout; - param->ChannelLayout |= asset->core.xxch.channel_layout; - param->ChannelLayout |= asset->lbr.channel_layout; - param->ChannelLayout |= asset->xll.channel_layout; - } - } - /* MultiAssetFlag - * When multiple assets exist, the remaining parameters in the DTSSpecificBox only reflect the coding parameters of the first asset. */ - param->MultiAssetFlag = ((info->exss[0].nuNumAssets - + info->exss[1].nuNumAssets - + info->exss[2].nuNumAssets - + info->exss[3].nuNumAssets) > 1); - /* LBRDurationMod */ - param->LBRDurationMod = info->exss[exss_index_start].asset[0].lbr.duration_modifier; - info->ddts_param_initialized = 1; -} - -int dts_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < DTS_SPECIFIC_BOX_MIN_LENGTH ) - return -1; - lsmash_dts_specific_parameters_t *param = (lsmash_dts_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - int dts_specific_box_min_length = DTS_SPECIFIC_BOX_MIN_LENGTH; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - dts_specific_box_min_length += 8; - data += 8; - } - if( size != src->size ) - return -1; - param->DTSSamplingFrequency = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - param->maxBitrate = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; - param->avgBitrate = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11]; - param->pcmSampleDepth = data[12]; - param->FrameDuration = (data[13] >> 6) & 0x03; - param->StreamConstruction = (data[13] >> 1) & 0x1F; - param->CoreLFEPresent = data[13] & 0x01; - param->CoreLayout = (data[14] >> 2) & 0x3F; - param->CoreSize = ((data[14] & 0x03) << 12) | (data[15] << 4) | ((data[16] >> 4) & 0x0F); - param->StereoDownmix = (data[16] >> 3) & 0x01; - param->RepresentationType = data[16] & 0x07; - param->ChannelLayout = (data[17] << 8) | data[18]; - param->MultiAssetFlag = (data[19] >> 7) & 0x01; - param->LBRDurationMod = (data[19] >> 6) & 0x01; - int reserved_box_present = ((data[19] >> 5) & 0x01) && (size > DTS_SPECIFIC_BOX_MIN_LENGTH); - if( reserved_box_present ) - lsmash_append_dts_reserved_box( param, data + 20, size - DTS_SPECIFIC_BOX_MIN_LENGTH ); - return 0; -} - -int dts_copy_codec_specific( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); - assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); - lsmash_dts_specific_parameters_t *src_data = (lsmash_dts_specific_parameters_t *)src->data.structured; - lsmash_dts_specific_parameters_t *dst_data = (lsmash_dts_specific_parameters_t *)dst->data.structured; - lsmash_remove_dts_reserved_box( dst_data ); - *dst_data = *src_data; - if( !src_data->box && src_data->box->data && src_data->box->size ) - return 0; - return lsmash_append_dts_reserved_box( dst_data, src_data->box->data, src_data->box->size ); -} - -int dts_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: DTS Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - if( box->size < DTS_SPECIFIC_BOX_MIN_LENGTH ) - return -1; - uint8_t *data = box->binary; - isom_skip_box_common( &data ); - uint32_t DTSSamplingFrequency = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - uint32_t maxBitrate = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; - uint32_t avgBitrate = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11]; - uint8_t pcmSampleDepth = data[12]; - uint8_t FrameDuration = (data[13] >> 6) & 0x03; - uint8_t StreamConstruction = (data[13] >> 1) & 0x1F; - uint8_t CoreLFEPresent = data[13] & 0x01; - uint8_t CoreLayout = (data[14] >> 2) & 0x3F; - uint16_t CoreSize = ((data[14] & 0x03) << 12) | (data[15] << 4) | ((data[16] >> 4) & 0x0F); - uint8_t StereoDownmix = (data[16] >> 3) & 0x01; - uint8_t RepresentationType = data[16] & 0x07; - uint16_t ChannelLayout = (data[17] << 8) | data[18]; - uint8_t MultiAssetFlag = (data[19] >> 7) & 0x01; - uint8_t LBRDurationMod = (data[19] >> 6) & 0x01; - uint8_t ReservedBoxPresent = (data[19] >> 5) & 0x01; - uint8_t Reserved = data[19] & 0x1F; - uint32_t frame_duration = 512 << FrameDuration; - int construction_flags = StreamConstruction <= DTS_MAX_STREAM_CONSTRUCTION ? construction_info[StreamConstruction] : 0; - static const char *core_layout_description[64] = - { - "Mono (1/0)", - "Undefined", - "Stereo (2/0)", - "Undefined", - "LT,RT (2/0)", - "L, C, R (3/0)", - "L, R, S (2/1)", - "L, C, R, S (3/1)", - "L, R, LS, RS (2/2)", - "L, C, R, LS, RS (3/2)", - [31] = "use ChannelLayout" - }; - static const char *representation_type_description[8] = - { - "Audio asset designated for mixing with another audio asset", - "Reserved", - "Lt/Rt Encoded for matrix surround decoding", - "Audio processed for headphone playback", - "Reserved", - "Reserved", - "Reserved", - "Reserved" - }; - static const char *channel_layout_description[16] = - { - "Center in front of listener", - "Left/Right in front", - "Left/Right surround on side in rear", - "Low frequency effects subwoofer", - "Center surround in rear", - "Left/Right height in front", - "Left/Right surround in rear", - "Center Height in front", - "Over the listener's head", - "Between left/right and center in front", - "Left/Right on side in front", - "Left/Right surround on side", - "Second low frequency effects subwoofer", - "Left/Right height on side", - "Center height in rear", - "Left/Right height in rear" - }; - lsmash_ifprintf( fp, indent, "DTSSamplingFrequency = %"PRIu32" Hz\n", DTSSamplingFrequency ); - lsmash_ifprintf( fp, indent, "maxBitrate = %"PRIu32" bit/s\n", maxBitrate ); - lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32" bit/s\n", avgBitrate ); - lsmash_ifprintf( fp, indent, "pcmSampleDepth = %"PRIu8" bits\n", pcmSampleDepth ); - lsmash_ifprintf( fp, indent, "FrameDuration = %"PRIu8" (%"PRIu32" samples)\n", FrameDuration, frame_duration ); - lsmash_ifprintf( fp, indent, "StreamConstruction = 0x%02"PRIx8"\n", StreamConstruction ); - if( construction_flags & (DTS_CORE_SUBSTREAM_CORE_FLAG | DTS_CORE_SUBSTREAM_XCH_FLAG | DTS_CORE_SUBSTREAM_X96_FLAG | DTS_CORE_SUBSTREAM_XXCH_FLAG) ) - { - lsmash_ifprintf( fp, indent + 1, "Core substream\n" ); - if( construction_flags & DTS_CORE_SUBSTREAM_CORE_FLAG ) - lsmash_ifprintf( fp, indent + 2, "Core\n" ); - if( construction_flags & DTS_CORE_SUBSTREAM_XCH_FLAG ) - lsmash_ifprintf( fp, indent + 2, "XCH\n" ); - if( construction_flags & DTS_CORE_SUBSTREAM_X96_FLAG ) - lsmash_ifprintf( fp, indent + 2, "X96\n" ); - if( construction_flags & DTS_CORE_SUBSTREAM_XXCH_FLAG ) - lsmash_ifprintf( fp, indent + 2, "XXCH\n" ); - } - if( construction_flags & (DTS_EXT_SUBSTREAM_CORE_FLAG | DTS_EXT_SUBSTREAM_XXCH_FLAG | DTS_EXT_SUBSTREAM_X96_FLAG - | DTS_EXT_SUBSTREAM_XBR_FLAG | DTS_EXT_SUBSTREAM_XLL_FLAG | DTS_EXT_SUBSTREAM_LBR_FLAG) ) - { - lsmash_ifprintf( fp, indent + 1, "Extension substream\n" ); - if( construction_flags & DTS_EXT_SUBSTREAM_CORE_FLAG ) - lsmash_ifprintf( fp, indent + 2, "Core\n" ); - if( construction_flags & DTS_EXT_SUBSTREAM_XXCH_FLAG ) - lsmash_ifprintf( fp, indent + 2, "XXCH\n" ); - if( construction_flags & DTS_EXT_SUBSTREAM_X96_FLAG ) - lsmash_ifprintf( fp, indent + 2, "X96\n" ); - if( construction_flags & DTS_EXT_SUBSTREAM_XBR_FLAG ) - lsmash_ifprintf( fp, indent + 2, "XBR\n" ); - if( construction_flags & DTS_EXT_SUBSTREAM_XLL_FLAG ) - lsmash_ifprintf( fp, indent + 2, "XLL\n" ); - if( construction_flags & DTS_EXT_SUBSTREAM_LBR_FLAG ) - lsmash_ifprintf( fp, indent + 2, "LBR\n" ); - } - lsmash_ifprintf( fp, indent, "CoreLFEPresent = %s\n", CoreLFEPresent ? "1 (LFE exists)" : "0 (no LFE)" ); - if( core_layout_description[CoreLayout] ) - lsmash_ifprintf( fp, indent, "CoreLayout = %"PRIu8" (%s)\n", CoreLayout, core_layout_description[CoreLayout] ); - else - lsmash_ifprintf( fp, indent, "CoreLayout = %"PRIu8" (Undefined)\n", CoreLayout ); - if( CoreSize ) - lsmash_ifprintf( fp, indent, "CoreSize = %"PRIu16"\n", CoreSize ); - else - lsmash_ifprintf( fp, indent, "CoreSize = 0 (no core substream exists)\n" ); - lsmash_ifprintf( fp, indent, "StereoDownmix = %s\n", StereoDownmix ? "1 (embedded downmix present)" : "0 (no embedded downmix)" ); - lsmash_ifprintf( fp, indent, "RepresentationType = %"PRIu8" (%s)\n", RepresentationType, representation_type_description[RepresentationType] ); - lsmash_ifprintf( fp, indent, "ChannelLayout = 0x%04"PRIx16"\n", ChannelLayout ); - if( ChannelLayout ) - for( int i = 0; i < 16; i++ ) - if( (ChannelLayout >> i) & 0x01 ) - lsmash_ifprintf( fp, indent + 1, "%s\n", channel_layout_description[i] ); - lsmash_ifprintf( fp, indent, "MultiAssetFlag = %s\n", MultiAssetFlag ? "1 (multiple asset)" : "0 (single asset)" ); - if( LBRDurationMod ) - lsmash_ifprintf( fp, indent, "LBRDurationMod = 1 (%"PRIu32" -> %"PRIu32" samples)\n", frame_duration, (frame_duration * 3) / 2 ); - else - lsmash_ifprintf( fp, indent, "LBRDurationMod = 0 (no LBR duration modifier)\n" ); - lsmash_ifprintf( fp, indent, "ReservedBoxPresent = %s\n", ReservedBoxPresent ? "1 (ReservedBox present)" : "0 (no ReservedBox)" ); - lsmash_ifprintf( fp, indent, "Reserved = 0x%02"PRIx8"\n", Reserved ); - return 0; -} diff -Nru l-smash-1.9.1/dts.h l-smash-2.3.0/dts.h --- l-smash-1.9.1/dts.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/dts.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -/***************************************************************************** - * dts.h: - ***************************************************************************** - * Copyright (C) 2012-2014 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. */ - -#define DTS_MAX_CORE_SIZE 16384 -#define DTS_MAX_EXSS_SIZE 32768 -#define DTS_MAX_NUM_EXSS 4 /* the maximum number of extension substreams */ - -typedef enum -{ - DTS_SUBSTREAM_TYPE_NONE = 0, - DTS_SUBSTREAM_TYPE_CORE = 1, - DTS_SUBSTREAM_TYPE_EXTENSION = 2, -} dts_substream_type; - -typedef struct -{ - uint16_t size; - uint16_t channel_layout; - uint8_t lower_planes; /* CL, LL and RL */ -} dts_xxch_info_t; - -typedef struct -{ - uint32_t sampling_frequency; - uint32_t frame_duration; - uint16_t frame_size; - uint16_t channel_layout; - uint8_t channel_arrangement; - uint8_t extension_audio_descriptor; - uint8_t pcm_resolution; - dts_xxch_info_t xxch; -} dts_core_info_t; - -typedef struct -{ - uint16_t size; - uint16_t channel_layout; - uint32_t sampling_frequency; - uint32_t frame_duration; - uint8_t pcm_resolution; - uint8_t stereo_downmix; -} dts_xll_info_t; - -typedef struct -{ - uint16_t size; - uint16_t channel_layout; - uint32_t sampling_frequency; - uint32_t frame_duration; - uint8_t stereo_downmix; - uint8_t lfe_present; - uint8_t duration_modifier; - uint8_t sample_size; -} dts_lbr_info_t; - -typedef struct -{ - uint32_t size; - uint16_t channel_layout; - uint8_t bOne2OneMapChannels2Speakers; - uint8_t nuRepresentationType; - uint8_t nuCodingMode; - lsmash_dts_construction_flag nuCoreExtensionMask; - dts_core_info_t core; - dts_xll_info_t xll; - dts_lbr_info_t lbr; - uint16_t xbr_size; - uint16_t x96_size; - uint16_t aux_size; -} dts_audio_asset_t; - -typedef struct -{ - uint32_t sampling_frequency; - uint32_t frame_duration; - uint8_t nuBits4ExSSFsize; - uint8_t bStaticFieldsPresent; - uint8_t bMixMetadataEnbl; - uint8_t nuNumMixOutConfigs; - uint8_t nNumMixOutCh[4]; - uint8_t nuNumAudioPresnt; - uint8_t nuNumAssets; - uint8_t nuActiveExSSMask[8]; - uint8_t nuActiveAssetMask[8][4]; - uint8_t bBcCorePresent[8]; - uint8_t nuBcCoreExtSSIndex[8]; - uint8_t nuBcCoreAssetIndex[8]; - uint8_t stereo_downmix; - uint8_t bit_resolution; - dts_audio_asset_t asset[8]; -} dts_extension_info_t; - -typedef struct -{ - dts_substream_type substream_type; - lsmash_dts_construction_flag flags; - lsmash_dts_specific_parameters_t ddts_param; - dts_core_info_t core; /* core component and its extensions in core substream */ - dts_extension_info_t exss[4]; /* extension substreams */ - uint8_t ddts_param_initialized; - uint8_t no_more_read; - uint8_t exss_index; - uint8_t exss_count; - uint32_t frame_duration; - uint32_t frame_size; /* size of substream */ - uint8_t buffer[2 * DTS_MAX_EXSS_SIZE]; - uint8_t *buffer_pos; - uint8_t *buffer_end; - lsmash_bits_t *bits; - lsmash_multiple_buffers_t *au_buffers; - uint8_t *au; - uint32_t au_length; - uint8_t *incomplete_au; - uint32_t incomplete_au_length; - uint32_t au_number; -} dts_info_t; - -void dts_setup_parser( dts_info_t *info ); -int dts_parse_core_substream( dts_info_t *info, uint8_t *data, uint32_t data_length ); -int dts_parse_extension_substream( dts_info_t *info, uint8_t *data, uint32_t data_length ); -int dts_get_max_channel_count( dts_info_t *info ); -dts_substream_type dts_get_substream_type( dts_info_t *info ); -int dts_get_exss_index( dts_info_t *info, uint8_t *exss_index ); -void dts_update_specific_param( dts_info_t *info ); diff -Nru l-smash-1.9.1/fragment.c l-smash-2.3.0/fragment.c --- l-smash-1.9.1/fragment.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/fragment.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1667 +0,0 @@ -/***************************************************************************** - * fragment.c - ***************************************************************************** - * Copyright (C) 2011-2014 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 "box.h" -#include "write.h" - -static isom_sidx_t *isom_get_sidx( lsmash_file_t *file, uint32_t reference_ID ) -{ - if( reference_ID == 0 || !file ) - return NULL; - for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) - { - isom_sidx_t *sidx = (isom_sidx_t *)entry->data; - if( !sidx ) - return NULL; - if( sidx->reference_ID == reference_ID ) - return sidx; - } - return NULL; -} - -static int isom_finish_fragment_movie( lsmash_file_t *file ); - -/* 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 ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->bs - || !file->fragment - || !file->moov ) - return -1; - /* Finish and write the current movie fragment before starting a new one. */ - if( isom_finish_fragment_movie( file ) < 0 ) - return -1; - /* Add a new movie fragment if the current one is not present or not written. */ - if( !file->fragment->movie || (file->fragment->movie->manager & LSMASH_WRITTEN_BOX) ) - { - /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */ - if( file->fragment->movie && file->moof_list.entry_count != 1 ) - return -1; - isom_moof_t *moof = isom_add_moof( file ); - if( isom_add_mfhd( moof ) < 0 ) - return -1; - file->fragment->movie = moof; - moof->mfhd->sequence_number = ++ file->fragment->fragment_count; - if( file->moof_list.entry_count == 1 ) - return 0; - /* Remove the previous movie fragment. */ - if( file->moof_list.head ) - isom_remove_box_by_itself( file->moof_list.head->data ); - } - return 0; -} - -static int isom_set_fragment_overall_duration( lsmash_file_t *file ) -{ - /* Get the longest duration of the tracks. */ - uint64_t longest_duration = 0; - for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_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) * file->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; - if( data->segment_duration == ISOM_EDIT_DURATION_IMPLICIT ) - { - uint64_t segment_duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration; - duration += (uint64_t)(((double)segment_duration / trak->mdia->mdhd->timescale) * file->moov->mvhd->timescale); - } - else - duration += data->segment_duration; - } - } - longest_duration = LSMASH_MAX( duration, longest_duration ); - } - isom_mehd_t *mehd = file->moov->mvex->mehd; - mehd->fragment_duration = longest_duration; - mehd->version = 1; - mehd->manager &= ~LSMASH_PLACEHOLDER; - isom_update_box_size( mehd ); - /* Write Movie Extends Header Box here. */ - lsmash_bs_t *bs = file->bs; - uint64_t current_pos = bs->offset; - lsmash_bs_seek( bs, mehd->pos, SEEK_SET ); - int ret = isom_write_box( bs, (isom_box_t *)mehd ); - lsmash_bs_seek( bs, current_pos, SEEK_SET ); - return ret; -} - -static int isom_write_fragment_random_access_info( lsmash_file_t *file ) -{ - if( !file->moov->mvex ) - return 0; - /* Reconstruct the Movie Fragment Random Access Box. - * All 'time' field in the Track Fragment Random Access Boxes shall reflect edit list. */ - uint32_t movie_timescale = lsmash_get_movie_timescale( file->root ); - if( movie_timescale == 0 ) - return -1; /* Division by zero will occur. */ - for( lsmash_entry_t *trex_entry = file->moov->mvex->trex_list.head; trex_entry; trex_entry = trex_entry->next ) - { - isom_trex_t *trex = (isom_trex_t *)trex_entry->data; - if( !trex ) - return -1; - /* Get the edit list of the track associated with the trex->track_ID. - * If failed or absent, implicit timeline mapping edit is used, and skip this operation for the track. */ - isom_trak_t *trak = isom_get_trak( file, trex->track_ID ); - if( !trak ) - return -1; - if( !trak->edts - || !trak->edts->elst - || !trak->edts->elst->list - || !trak->edts->elst->list->head - || !trak->edts->elst->list->head->data ) - continue; - isom_elst_t *elst = trak->edts->elst; - /* Get the Track Fragment Random Access Boxes of the track associated with the trex->track_ID. - * If failed or absent, skip reconstructing the Track Fragment Random Access Box of the track. */ - isom_tfra_t *tfra = isom_get_tfra( file->mfra, trex->track_ID ); - if( !tfra ) - continue; - /* Reconstruct the Track Fragment Random Access Box. */ - lsmash_entry_t *edit_entry = elst->list->head; - isom_elst_entry_t *edit = edit_entry->data; - uint64_t edit_offset = 0; /* units in media timescale */ - uint32_t media_timescale = lsmash_get_media_timescale( file->root, trex->track_ID ); - for( lsmash_entry_t *rap_entry = tfra->list->head; rap_entry; ) - { - isom_tfra_location_time_entry_t *rap = (isom_tfra_location_time_entry_t *)rap_entry->data; - if( !rap ) - { - /* Irregular case. Drop this entry. */ - lsmash_entry_t *next = rap_entry->next; - lsmash_remove_entry_direct( tfra->list, rap_entry, NULL ); - rap_entry = next; - continue; - } - uint64_t composition_time = rap->time; - /* Skip edits that doesn't need the current sync sample indicated in the Track Fragment Random Access Box. */ - while( edit ) - { - uint64_t segment_duration = ((edit->segment_duration - 1) / movie_timescale + 1) * media_timescale; - if( edit->media_time != ISOM_EDIT_MODE_EMPTY - && composition_time < edit->media_time + segment_duration ) - break; /* This Timeline Mapping Edit might require the current sync sample. - * Note: this condition doesn't cover all cases. - * For instance, matching the both following conditions - * 1. A sync sample isn't in the presentation. - * 2. The other samples, which precede it in the composition timeline, is in the presentation. */ - edit_offset += segment_duration; - edit_entry = edit_entry->next; - if( !edit_entry ) - { - /* No more presentation. */ - edit = NULL; - break; - } - edit = edit_entry->data; - } - if( !edit ) - { - /* No more presentation. - * Drop the rest of sync samples since they are generally absent in the whole presentation. - * Though the exceptions are sync samples with earlier composition time, we ignore them. (SAP type 2: TEPT = TDEC = TSAP < TPTF) - * To support this exception, we need sorting entries of the list by composition times. */ - while( rap_entry ) - { - lsmash_entry_t *next = rap_entry->next; - lsmash_remove_entry_direct( tfra->list, rap_entry, NULL ); - rap_entry = next; - } - break; - } - /* If the sync sample isn't in the presentation, - * we pick the earliest presentation time of the current edit as its presentation time. */ - rap->time = edit_offset; - if( composition_time >= edit->media_time ) - rap->time += composition_time - edit->media_time; - rap_entry = rap_entry->next; - } - } - /* Decide the size of the Movie Fragment Random Access Box. */ - if( isom_update_box_size( file->mfra ) == 0 ) - return -1; - /* Write the Movie Fragment Random Access Box. */ - return isom_write_box( file->bs, (isom_box_t *)file->mfra ); -} - -static int isom_update_indexed_material_offset -( - lsmash_file_t *file, - isom_sidx_t *last_sidx -) -{ - /* Update the size of each Segment Index Box. */ - for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) - { - isom_sidx_t *sidx = (isom_sidx_t *)entry->data; - if( !sidx ) - continue; - if( isom_update_box_size( sidx ) == 0 ) - return -1; - } - /* first_offset: the sum of the size of subsequent Segment Index Boxes - * Be careful about changing the size of them. */ - last_sidx->first_offset = 0; - for( lsmash_entry_t *a_entry = file->sidx_list.head; a_entry && a_entry->data != last_sidx; a_entry = a_entry->next ) - { - isom_sidx_t *a = (isom_sidx_t *)a_entry->data; - a->first_offset = 0; - for( lsmash_entry_t *b_entry = a_entry->next; b_entry; b_entry = b_entry->next ) - { - isom_sidx_t *b = (isom_sidx_t *)b_entry->data; - a->first_offset += b->size; - } - } - return 0; -} - -int isom_finish_final_fragment_movie -( - lsmash_file_t *file, - lsmash_adhoc_remux_t *remux -) -{ - /* Output the final movie fragment. */ - if( isom_finish_fragment_movie( file ) < 0 ) - return -1; - if( file->bs->unseekable ) - return 0; - /* Write Segment Index Boxes. - * This occurs only when the initial movie has no samples. - * We don't consider updating of chunk offsets within initial movie sample table here. - * This is reasonable since DASH requires no samples in the initial movie. - * This implementation is not suitable for live-streaming. - + To support live-streaming, it is good to use daisy-chained index. */ - uint8_t *buf[2] = { NULL, NULL }; - if( file->flags & LSMASH_FILE_MODE_INDEX ) - { - /* Update the size of each Segment Index Box and establish the offset from the anchor point to the indexed material. */ - if( isom_update_indexed_material_offset( file, (isom_sidx_t *)file->sidx_list.tail->data ) < 0 ) - return -1; - /* Get the total size of all Segment Index Boxes. */ - uint64_t total_sidx_size = 0; - for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) - { - isom_sidx_t *sidx = (isom_sidx_t *)entry->data; - if( !sidx ) - continue; - total_sidx_size += sidx->size; - } - /* buffer size must be at least total_sidx_size * 2 */ - size_t buffer_size = total_sidx_size * 2; - if( remux && remux->buffer_size > buffer_size ) - buffer_size = remux->buffer_size; - /* Split to 2 buffers. */ - if( (buf[0] = (uint8_t *)lsmash_malloc( buffer_size )) == NULL ) - return -1; - size_t size = buffer_size / 2; - buf[1] = buf[0] + size; - /* Seek to the beginning of the first Movie Fragment Box. */ - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_seek( bs, file->fragment->first_moof_pos, SEEK_SET ) < 0 ) - goto fail; - size_t read_num = size; - lsmash_bs_read_data( bs, buf[0], &read_num ); - uint64_t read_pos = bs->offset; - /* */ - if( lsmash_bs_seek( bs, file->fragment->first_moof_pos, SEEK_SET ) < 0 ) - goto fail; - for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next ) - { - isom_sidx_t *sidx = (isom_sidx_t *)entry->data; - if( !sidx ) - continue; - if( isom_write_box( file->bs, (isom_box_t *)sidx ) < 0 ) - return -1; - } - uint64_t write_pos = bs->offset; - uint64_t total = file->size + total_sidx_size; - if( isom_rearrange_boxes( file, remux, buf, read_num, size, read_pos, write_pos, total ) < 0 ) - goto fail; - file->size += total_sidx_size; - lsmash_freep( &buf[0] ); - /* Update moof_offset. */ - if( file->mfra ) - for( lsmash_entry_t *entry = file->mfra->tfra_list.head; entry; entry = entry->next ) - { - isom_tfra_t *tfra = (isom_tfra_t *)entry->data; - if( !tfra ) - continue; - for( lsmash_entry_t *rap_entry = tfra->list->head; rap_entry; rap_entry = rap_entry->next ) - { - isom_tfra_location_time_entry_t *rap = (isom_tfra_location_time_entry_t *)rap_entry->data; - if( !rap ) - continue; - rap->moof_offset += total_sidx_size; - } - } - } - /* Write the overall random access information at the tail of the movie. */ - if( isom_write_fragment_random_access_info( file ) ) - return -1; - /* Set overall duration of the movie. */ - return isom_set_fragment_overall_duration( file ); -fail: - lsmash_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_file_t *file ) -{ - if( isom_add_mvex( file->moov ) ) - return -1; - if( !file->bs->unseekable ) - { - if( isom_add_mehd( file->moov->mvex ) ) - return -1; - file->moov->mvex->mehd->manager |= LSMASH_PLACEHOLDER; - } - for( lsmash_entry_t *trak_entry = file->moov->trak_list.head; trak_entry; trak_entry = trak_entry->next ) - { - isom_trak_t *trak = (isom_trak_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_t *trex = isom_add_trex( file->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_file_t *file ) -{ - if( file->bs->unseekable ) - return 0; - if( isom_add_mfra( file ) - || isom_add_mfro( file->mfra ) ) - return -1; - return 0; -} - -static int isom_output_fragment_media_data( lsmash_file_t *file ) -{ - isom_fragment_manager_t *fragment = file->fragment; - /* If there is no available Media Data Box to write samples, add and write a new one. */ - if( fragment->sample_count ) - { - if( !file->mdat && isom_add_mdat( file ) < 0 ) - return -1; - file->mdat->manager &= ~(LSMASH_INCOMPLETE_BOX | LSMASH_WRITTEN_BOX); - if( isom_write_box( file->bs, (isom_box_t *)file->mdat ) < 0 ) - return -1; - file->size += file->mdat->size; - file->mdat->size = 0; - file->mdat->media_size = 0; - } - lsmash_remove_entries( fragment->pool, isom_remove_sample_pool ); - fragment->pool_size = 0; - fragment->sample_count = 0; - return 0; -} - -static int isom_finish_fragment_initial_movie( lsmash_file_t *file ) -{ - if( !file->moov ) - return -1; - isom_moov_t *moov = file->moov; - for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - if( !trak - || !trak->cache - || !trak->tkhd - || !trak->mdia - || !trak->mdia->mdhd - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || isom_complement_data_reference( trak->mdia->minf ) < 0 ) - return -1; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - if( isom_get_sample_count( trak ) ) - { - /* Add stss box if any samples aren't sync sample. */ - if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) ) - return -1; - if( isom_update_tkhd_duration( trak ) < 0 ) - return -1; - } - else - trak->tkhd->duration = 0; - if( isom_update_bitrate_description( trak->mdia ) < 0 ) - return -1; - /* Complete the last sample groups within tracks in the initial movie. */ - if( trak->cache->rap ) - { - isom_sgpd_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); - if( !sgpd || isom_rap_grouping_established( trak->cache->rap, 1, sgpd, 0 ) < 0 ) - return -1; - lsmash_freep( &trak->cache->rap ); - } - if( trak->cache->roll.pool ) - { - isom_sbgp_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL ); - if( !sbgp || isom_all_recovery_completed( sbgp, trak->cache->roll.pool ) < 0 ) - return -1; - } - } - if( file->mp4_version1 == 1 && isom_setup_iods( moov ) < 0 ) - return -1; - if( isom_create_fragment_overall_default_settings( file ) < 0 - || isom_prepare_random_access_info ( file ) < 0 - || isom_establish_movie ( file ) < 0 ) - return -1; - /* stco->co64 conversion, depending on last chunk's offset */ - uint64_t meta_size = file->meta ? file->meta->size : 0; - for( lsmash_entry_t *entry = moov->trak_list.head; entry; ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - isom_stco_t *stco = trak->mdia->minf->stbl->stco; - if( !stco->list->tail /* no samples */ - || stco->large_presentation - || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX ) - { - entry = entry->next; - continue; /* no need to convert stco into co64 */ - } - /* stco->co64 conversion */ - if( isom_convert_stco_to_co64( trak->mdia->minf->stbl ) < 0 - || isom_update_box_size( moov ) == 0 ) - return -1; - entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */ - } - /* Now, the amount of offset is fixed. Apply that to stco/co64. */ - uint64_t preceding_size = moov->size + meta_size; - for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) - { - isom_stco_t *stco = ((isom_trak_t *)entry->data)->mdia->minf->stbl->stco; - if( stco->large_presentation ) - for( lsmash_entry_t *co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next ) - ((isom_co64_entry_t *)co64_entry->data)->chunk_offset += preceding_size; - else - for( lsmash_entry_t *stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next ) - ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += preceding_size; - } - /* Write File Type Box here if it was not written yet. */ - if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) ) - { - if( isom_write_box( file->bs, (isom_box_t *)file->ftyp ) ) - return -1; - file->size += file->ftyp->size; - } - /* Write Movie Box. */ - if( isom_write_box( file->bs, (isom_box_t *)file->moov ) < 0 - || isom_write_box( file->bs, (isom_box_t *)file->meta ) < 0 ) - return -1; - file->size += preceding_size; - /* Output samples. */ - if( isom_output_fragment_media_data( file ) < 0 ) - return -1; - /* Revert the number of samples in tracks to 0. */ - for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - if( trak->cache->fragment ) - trak->cache->fragment->sample_count = 0; - } - return 0; -} - -/* Return 1 if there is diffrence, otherwise return 0. */ -static int isom_compare_sample_flags( isom_sample_flags_t *a, isom_sample_flags_t *b ) -{ - return (a->reserved != b->reserved) - || (a->is_leading != b->is_leading) - || (a->sample_depends_on != b->sample_depends_on) - || (a->sample_is_depended_on != b->sample_is_depended_on) - || (a->sample_has_redundancy != b->sample_has_redundancy) - || (a->sample_padding_value != b->sample_padding_value) - || (a->sample_is_non_sync_sample != b->sample_is_non_sync_sample) - || (a->sample_degradation_priority != b->sample_degradation_priority); -} - -static int isom_finish_fragment_movie( lsmash_file_t *file ) -{ - if( !file->moov - || !file->fragment - || !file->fragment->pool ) - return -1; - isom_moof_t *moof = file->fragment->movie; - if( !moof ) - return isom_finish_fragment_initial_movie( file ); - /* Don't write the current movie fragment if containing no track fragments. - * This is a requirement of DASH Media Segment. */ - if( !moof->traf_list.head - || !moof->traf_list.head->data ) - return 0; - /* 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_t *traf = (isom_traf_t *)entry->data; - if( !traf - || !traf->tfhd - || !traf->file - || !traf->file->moov - || !traf->file->moov->mvex ) - return -1; - isom_tfhd_t *tfhd = traf->tfhd; - isom_trex_t *trex = isom_get_trex( file->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_t *trun = (isom_trun_t *)trun_entry->data; - if( !trun || trun->sample_count == 0 ) - 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_t *trun = (isom_trun_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( !isom_compare_sample_flags( &trun->first_sample_flags, &tfhd->default_sample_flags ) ) - 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( isom_compare_sample_flags( &tfhd->default_sample_flags, &representative_sample_flags ) ) - useful_default_sample_flags = 0; - if( !isom_compare_sample_flags( &trun->first_sample_flags, &representative_sample_flags ) ) - 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( isom_compare_sample_flags( &representative_sample_flags, &row->sample_flags ) ) - { - 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( !isom_compare_sample_flags( &tfhd->default_sample_flags, &trex->default_sample_flags ) ) - tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT; - } - /* Complete the last sample groups in the previous track fragments. */ - for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)entry->data; - if( traf->cache->rap ) - { - isom_sgpd_t *sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP ); - if( !sgpd || isom_rap_grouping_established( traf->cache->rap, 1, sgpd, 1 ) < 0 ) - return -1; - lsmash_freep( &traf->cache->rap ); - } - if( traf->cache->roll.pool ) - { - isom_sbgp_t *sbgp = isom_get_fragment_sample_to_group( traf, ISOM_GROUP_TYPE_ROLL ); - if( !sbgp || isom_all_recovery_completed( sbgp, traf->cache->roll.pool ) < 0 ) - return -1; - } - } - /* Establish Movie Fragment Box. - * We write exactly one Media Data Box starting immediately after the corresponding Movie Fragment Box. */ - if( file->allow_moof_base ) - { - /* In this branch, we use default-base-is-moof flag, which indicates implicit base_data_offsets originate in the - * first byte of each enclosing Movie Fragment Box. - * We use the sum of the size of the Movie Fragment Box and the offset from the size field of the Media Data Box to - * the type field of it as the data_offset of the first track run like the following. - * - * _____________ _ offset := 0 - * | | | - * | m | s i z e | - * | |_________| - * | o | | - * | | t y p e | - * | o |_________| - * | | | - * | f | d a t a | - * |___|_________|_ offset := the size of the Movie Fragment Box - * | | | - * | m | s i z e | - * | |_________| - * | d | | - * | | t y p e | - * | a |_________|_ offset := the data_offset of the first track run - * | | | - * | t | d a t a | - * |___|_________|_ offset := the size of a subsegment containing exactly one movie fragment - * - * For a pair of one Movie Fragment Box and one Media Data Box, placed in this order, implicit base_data_offsets - * indicated by the absence of both base-data-offset-present and default-base-is-moof are somewhat complicated - * since the implicit base_data_offset of the current track fragment is defined by the end of the data of the - * previous track fragment and the data_offset of the track runs could be negative value because of interleaving - * track runs or something other reasons. - * In contrast, implicit base_data_offsets indicated by default-base-is-moof are simple since the base_data_offset - * of each track fragment is always constant for that pair and has no dependency on other track fragments. - */ - for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)entry->data; - traf->tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF; - traf->tfhd->base_data_offset = file->size; /* not written actually though */ - for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next ) - { - /* Here, data_offset is always greater than zero. */ - isom_trun_t *trun = trun_entry->data; - trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; - } - } - /* Consider the update of tr_flags here. */ - if( isom_update_box_size( moof ) == 0 ) - 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_t *traf = (isom_traf_t *)entry->data; - for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next ) - { - isom_trun_t *trun = trun_entry->data; - trun->data_offset += moof->size + ISOM_BASEBOX_COMMON_SIZE; - } - } - } - else - { - /* In this branch, we use explicit base_data_offset. */ - for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)entry->data; - traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT; - } - /* Consider the update of tf_flags here. */ - if( isom_update_box_size( moof ) == 0 ) - 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_t *traf = (isom_traf_t *)entry->data; - traf->tfhd->base_data_offset = file->size + moof->size + ISOM_BASEBOX_COMMON_SIZE; - } - } - /* Write Movie Fragment Box and its children. */ - moof->pos = file->size; - if( isom_write_box( file->bs, (isom_box_t *)moof ) ) - return -1; - if( file->fragment->fragment_count == 1 ) - file->fragment->first_moof_pos = moof->pos; - file->size += moof->size; - /* Output samples. */ - if( isom_output_fragment_media_data( file ) < 0 ) - return -1; - /* Revert the number of samples in track fragments to 0. */ - for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)entry->data; - if( traf->cache->fragment ) - traf->cache->fragment->sample_count = 0; - } - if( !(file->flags & LSMASH_FILE_MODE_INDEX) || file->max_isom_version < 6 ) - return 0; - /* Make the index of this subsegment. */ - for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)entry->data; - isom_tfhd_t *tfhd = traf->tfhd; - isom_fragment_t *track_fragment = traf->cache->fragment; - isom_subsegment_t *subsegment = &track_fragment->subsegment; - isom_sidx_t *sidx = isom_get_sidx( file, tfhd->track_ID ); - isom_trak_t *trak = isom_get_trak( file, tfhd->track_ID ); - if( !trak - || !trak->mdia - || !trak->mdia->mdhd ) - return -1; - assert( traf->tfdt ); - if( !sidx ) - { - sidx = isom_add_sidx( file ); - if( !sidx ) - return -1; - sidx->reference_ID = tfhd->track_ID; - sidx->timescale = trak->mdia->mdhd->timescale; - sidx->reserved = 0; - sidx->reference_count = 0; - if( isom_update_indexed_material_offset( file, sidx ) < 0 ) - return -1; - } - /* One pair of a Movie Fragment Box with an associated Media Box per subsegment. */ - isom_sidx_referenced_item_t *data = lsmash_malloc( sizeof(isom_sidx_referenced_item_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( sidx->list, data ) < 0 ) - { - lsmash_free( data ); - return -1; - } - sidx->reference_count = sidx->list->entry_count; - data->reference_type = 0; /* media */ - data->reference_size = file->size - moof->pos; - /* presentation */ - uint64_t TSAP; - uint64_t TDEC; - uint64_t TEPT; - uint64_t TPTF; - uint64_t composition_duration = subsegment->largest_cts - subsegment->smallest_cts + track_fragment->last_duration; - int subsegment_in_presentation; - int first_rp_in_presentation; - int first_sample_in_presentation; - isom_elst_entry_t *edit = NULL; - if( trak->edts && trak->edts->elst && trak->edts->elst->list ) - { - /* Explicit edits */ - isom_elst_t *elst = trak->edts->elst; - uint32_t movie_timescale = file->moov->mvhd->timescale; - uint64_t pts = subsegment->segment_duration; - subsegment_in_presentation = 0; - first_rp_in_presentation = 0; - first_sample_in_presentation = 0; - for( lsmash_entry_t *elst_entry = elst->list->head; elst_entry; elst_entry = elst_entry->next ) - { - edit = (isom_elst_entry_t *)elst_entry->data; - if( !edit ) - continue; - uint64_t edit_end_pts; - uint64_t edit_end_cts; - if( edit->segment_duration == ISOM_EDIT_DURATION_IMPLICIT - || (elst->version == 0 && edit->segment_duration == ISOM_EDIT_DURATION_UNKNOWN32) - || (elst->version == 1 && edit->segment_duration == ISOM_EDIT_DURATION_UNKNOWN64) ) - { - edit_end_cts = UINT64_MAX; - edit_end_pts = UINT64_MAX; - } - else - { - if( edit->segment_duration ) - { - double segment_duration = edit->segment_duration * ((double)sidx->timescale / movie_timescale); - edit_end_cts = edit->media_time + (uint64_t)(segment_duration * ((double)edit->media_rate / (1 << 16))); - edit_end_pts = pts + (uint64_t)segment_duration; - } - else - { - uint64_t segment_duration = composition_duration; - if( edit->media_time > subsegment->smallest_cts ) - { - if( subsegment->largest_cts + track_fragment->last_duration > edit->media_time ) - segment_duration -= edit->media_time - subsegment->smallest_cts; - else - segment_duration = 0; - } - edit_end_cts = edit->media_time + (uint64_t)(segment_duration * ((double)edit->media_rate / (1 << 16))); - edit_end_pts = pts + (uint64_t)segment_duration; - } - } - if( edit->media_time == ISOM_EDIT_MODE_EMPTY ) - { - pts = edit_end_pts; - continue; - } - if( (subsegment->smallest_cts >= edit->media_time && subsegment->smallest_cts < edit_end_cts) - || (subsegment->largest_cts >= edit->media_time && subsegment->largest_cts < edit_end_cts) ) - { - /* This subsegment is present in this edit. */ - double rate = (double)edit->media_rate / (1 << 16); - uint64_t start_time = LSMASH_MAX( subsegment->smallest_cts, edit->media_time ); - if( sidx->reference_count == 1 ) - sidx->earliest_presentation_time = pts; - if( subsegment_in_presentation == 0 ) - { - subsegment_in_presentation = 1; - if( subsegment->smallest_cts >= edit->media_time ) - TEPT = pts + (uint64_t)((subsegment->smallest_cts - start_time) / rate); - else - TEPT = pts; - } - if( first_rp_in_presentation == 0 - && ((subsegment->first_ed_cts >= edit->media_time && subsegment->first_ed_cts < edit_end_cts) - || (subsegment->first_rp_cts >= edit->media_time && subsegment->first_rp_cts < edit_end_cts)) ) - { - /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */ - first_rp_in_presentation = 1; - if( subsegment->first_ed_cts >= edit->media_time && subsegment->first_ed_cts < edit_end_cts ) - TSAP = pts + (uint64_t)((subsegment->first_ed_cts - start_time) / rate); - else - TSAP = pts; - TDEC = TSAP; - } - if( first_sample_in_presentation == 0 - && subsegment->first_cts >= edit->media_time && subsegment->first_cts < edit_end_cts ) - { - first_sample_in_presentation = 1; - TPTF = pts + (uint64_t)((subsegment->first_cts - start_time) / rate); - } - uint64_t subsegment_end_pts = pts + (uint64_t)(composition_duration / rate); - pts = LSMASH_MIN( edit_end_pts, subsegment_end_pts ); - /* Update subsegment_duration. */ - data->subsegment_duration = pts - subsegment->segment_duration; - } - else - /* This subsegment is not present in this edit. */ - pts = edit_end_pts; - } - } - else - { - /* Implicit edit */ - if( sidx->reference_count == 1 ) - sidx->earliest_presentation_time = subsegment->smallest_cts; - data->subsegment_duration = composition_duration; - /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */ - TSAP = subsegment->first_rp_cts; - TDEC = subsegment->first_rp_cts; - TEPT = subsegment->smallest_cts; - TPTF = subsegment->first_cts; - subsegment_in_presentation = 1; - first_rp_in_presentation = 1; - first_sample_in_presentation = 1; - } - if( subsegment->first_ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE - || subsegment->first_ra_number == 0 - || subsegment->first_rp_number == 0 - || subsegment_in_presentation == 0 - || first_rp_in_presentation == 0 ) - { - /* No SAP in this subsegment. */ - data->starts_with_SAP = 0; - data->SAP_type = 0; - data->SAP_delta_time = 0; - } - else - { - data->starts_with_SAP = (subsegment->first_ra_number == 1); - data->SAP_type = 0; - data->SAP_delta_time = TSAP - TEPT; - /* Decide SAP_type. */ - if( first_sample_in_presentation ) - { - if( TEPT == TDEC && TDEC == TSAP && TSAP == TPTF ) - data->SAP_type = 1; - else if( TEPT == TDEC && TDEC == TSAP && TSAP < TPTF ) - data->SAP_type = 2; - else if( TEPT < TDEC && TDEC == TSAP && TSAP <= TPTF ) - data->SAP_type = 3; - else if( TEPT <= TPTF && TPTF < TDEC && TDEC == TSAP ) - data->SAP_type = 4; - } - if( data->SAP_type == 0 ) - { - if( TEPT == TDEC && TDEC < TSAP ) - data->SAP_type = 5; - else if( TEPT < TDEC && TDEC < TSAP ) - data->SAP_type = 6; - } - } - subsegment->segment_duration += data->subsegment_duration; - subsegment->first_ed_cts = UINT64_MAX; - subsegment->first_rp_cts = UINT64_MAX; - subsegment->first_rp_number = 0; - subsegment->first_ra_number = 0; - subsegment->first_ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE; - subsegment->decodable = 0; - } - return 0; -} - -#undef GET_MOST_USED - -static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_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 = lsmash_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 ) ) - { - lsmash_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 ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->fragment - || !file->fragment->movie - || !file->moov ) - return -1; - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->tkhd ) - return -1; - isom_trex_t *trex = isom_get_trex( file->moov->mvex, track_ID ); - if( !trex ) - return -1; - isom_moof_t *moof = file->fragment->movie; - isom_traf_t *traf = isom_get_traf( moof, track_ID ); - if( traf ) - return -1; - traf = isom_add_traf( 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; -} - -int isom_set_fragment_last_duration -( - isom_traf_t *traf, - uint32_t last_duration -) -{ - isom_tfhd_t *tfhd = traf->tfhd; - if( !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_t *trex = isom_get_trex( traf->file->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_t *trun = (isom_trun_t *)traf->trun_list.tail->data; - if( trun->sample_count == 1 - && traf->trun_list.entry_count == 1 ) - { - isom_trex_t *trex = isom_get_trex( traf->file->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 isom_append_fragment_track_run -( - lsmash_file_t *file, - isom_chunk_t *chunk -) -{ - if( !chunk->pool || chunk->pool->size == 0 ) - return 0; - isom_fragment_manager_t *fragment = file->fragment; - /* Move data in the pool of the current track fragment to the pool of the current movie fragment. - * Empty the pool of current track. We don't delete data of samples here. */ - if( lsmash_add_entry( fragment->pool, chunk->pool ) < 0 ) - return -1; - fragment->sample_count += chunk->pool->sample_count; - fragment->pool_size += chunk->pool->size; - chunk->pool = isom_create_sample_pool( chunk->pool->size ); - return chunk->pool ? 0 : -1; -} - -static int isom_output_fragment_cache( isom_traf_t *traf ) -{ - isom_cache_t *cache = traf->cache; - if( isom_append_fragment_track_run( traf->file, &cache->chunk ) < 0 ) - return -1; - for( lsmash_entry_t *entry = traf->sgpd_list.head; entry; entry = entry->next ) - { - isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data; - if( !sgpd ) - return -1; - switch( sgpd->grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - { - isom_rap_group_t *group = cache->rap; - if( !group ) - { - if( traf->file->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( !cache->roll.pool ) - { - if( traf->file->fragment ) - continue; - else - return -1; - } - isom_sbgp_t *sbgp = isom_get_fragment_sample_to_group( traf, ISOM_GROUP_TYPE_ROLL ); - if( !sbgp || isom_all_recovery_completed( sbgp, cache->roll.pool ) < 0 ) - return -1; - break; - default : - break; - } - } - return 0; -} - -int isom_flush_fragment_pooled_samples -( - lsmash_file_t *file, - uint32_t track_ID, - uint32_t last_sample_duration -) -{ - isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID ); - if( !traf ) - /* No samples. We don't return as an error here since user might call the flushing function even if the - * current movie fragment has no track fragment with this track_ID. */ - return 0; - if( !traf->cache - || !traf->cache->fragment ) - return -1; - if( 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_t *trun = (isom_trun_t *)traf->trun_list.tail->data; - if( file->fragment->pool_size ) - trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; - trun->data_offset = file->fragment->pool_size; - } - if( isom_output_fragment_cache( traf ) < 0 ) - return -1; - return isom_set_fragment_last_duration( traf, last_sample_duration ); -} - -/* 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_t *traf, isom_trex_t *trex, uint32_t duration ) -{ - isom_tfhd_t *tfhd = traf->tfhd; - isom_trun_t *trun = (isom_trun_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_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.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC); - flags.sample_degradation_priority = 0; - return flags; -} - -static int isom_update_fragment_sample_tables( isom_traf_t *traf, lsmash_sample_t *sample ) -{ - isom_tfhd_t *tfhd = traf->tfhd; - isom_trex_t *trex = isom_get_trex( traf->file->moov->mvex, tfhd->track_ID ); - if( !trex ) - return -1; - lsmash_file_t *file = traf->file; - isom_cache_t *cache = traf->cache; - isom_chunk_t *current = &cache->chunk; - if( !current->pool ) - { - /* Very initial settings, just once per track */ - current->pool = isom_create_sample_pool( 0 ); - if( !current->pool ) - return -1; - } - /* Create a new track run if the duration exceeds max_chunk_duration. - * Old one will be appended to the pool of this movie fragment. */ - uint32_t media_timescale = lsmash_get_media_timescale( file->root, tfhd->track_ID ); - if( !media_timescale ) - return -1; - int delimit = (file->max_chunk_duration < ((double)(sample->dts - current->first_dts) / media_timescale)) - || (file->max_chunk_size < (current->pool->size + sample->length)); - isom_trun_t *trun = NULL; - if( !traf->trun_list.entry_count || delimit ) - { - if( delimit - && 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_t *)traf->trun_list.tail->data; - if( file->fragment->pool_size ) - trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; - trun->data_offset = file->fragment->pool_size; - } - trun = isom_add_trun( traf ); - if( !trun ) - return -1; - } - else - { - if( !traf->trun_list.tail - || !traf->trun_list.tail->data ) - return -1; - trun = (isom_trun_t *)traf->trun_list.tail->data; - } - isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample ); - if( ++trun->sample_count == 1 ) - { - if( traf->trun_list.entry_count == 1 ) - { - /* This track fragment isn't empty-duration-fragment any more. */ - tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY; - /* Set up sample_description_index in this track fragment. */ - if( sample->index != trex->default_sample_description_index ) - tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT; - tfhd->sample_description_index = current->sample_description_index = sample->index; - /* Set up default_sample_size used in this track fragment. */ - tfhd->default_sample_size = sample->length; - /* Set up default_sample_flags used in this track fragment. - * Note: we decide an appropriate default value at the end of this movie fragment. */ - tfhd->default_sample_flags = sample_flags; - /* Set up random access information if this sample is a sync sample. - * We inform only the first sample in each movie fragment. */ - if( !file->bs->unseekable && (sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) - { - isom_tfra_t *tfra = isom_get_tfra( file->mfra, tfhd->track_ID ); - if( !tfra ) - { - tfra = isom_add_tfra( file->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 = lsmash_malloc( sizeof(isom_tfra_location_time_entry_t) ); - if( !rap ) - return -1; - rap->time = sample->cts; /* Set composition timestamp temporally. - * At the end of the whole movie, this will be reset as presentation time. */ - rap->moof_offset = file->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 ) ) - { - lsmash_free( 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 ); - } - /* Set up the base media decode time of this track fragment. - * This feature is available under ISO Base Media version 6 or later. - * For DASH Media Segment, each Track Fragment Box shall contain a Track Fragment Base Media Decode Time Box. */ - if( file->max_isom_version >= 6 || file->media_segment ) - { - assert( !traf->tfdt ); - if( isom_add_tfdt( traf ) ) - return -1; - if( sample->dts > UINT32_MAX ) - traf->tfdt->version = 1; - traf->tfdt->baseMediaDecodeTime = sample->dts; - } - } - trun->first_sample_flags = sample_flags; - current->first_dts = sample->dts; - } - /* Update the optional rows in the current track run except for sample_duration if needed. */ - if( sample->length != tfhd->default_sample_size ) - trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT; - if( isom_compare_sample_flags( &sample_flags, &tfhd->default_sample_flags ) ) - trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT; - uint32_t sample_composition_time_offset = sample->cts - sample->dts; - if( sample_composition_time_offset ) - { - trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT; - /* Check if negative composition time offset is present. */ - isom_timestamp_t *ts_cache = &cache->timestamp; - if( (sample->cts + ts_cache->ctd_shift) < sample->dts ) - { - if( file->max_isom_version < 6 ) - return -1; /* Negative composition time offset is not supported. */ - if( (sample->dts - sample->cts) > INT32_MAX ) - return -1; /* Overflow */ - ts_cache->ctd_shift = sample->dts - sample->cts; - if( trun->version == 0 && file->max_isom_version >= 6 ) - trun->version = 1; - } - } - 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; - } - if( isom_group_random_access( (isom_box_t *)traf, sample ) < 0 - || isom_group_roll_recovery( (isom_box_t *)traf, sample ) < 0 ) - return -1; - /* 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 ) < 0 ) - return -1; - } - /* Cache */ - cache->timestamp.dts = sample->dts; - cache->timestamp.cts = sample->cts; - cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts ); - isom_subsegment_t *subsegment = &cache->fragment->subsegment; - if( trun->sample_count == 1 && traf->trun_list.entry_count == 1 ) - { - subsegment->first_cts = sample->cts; - subsegment->largest_cts = sample->cts; - subsegment->smallest_cts = sample->cts; - } - else - { - subsegment->largest_cts = LSMASH_MAX( sample->cts, subsegment->largest_cts ); - subsegment->smallest_cts = LSMASH_MIN( sample->cts, subsegment->smallest_cts ); - } - if( subsegment->first_ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - { - subsegment->first_ra_flags = sample->prop.ra_flags; - subsegment->first_ra_number = cache->fragment->sample_count + 1; - if( sample->prop.ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP) ) - { - subsegment->first_rp_number = subsegment->first_ra_number; - subsegment->first_rp_cts = sample->cts; - subsegment->first_ed_cts = sample->cts; - subsegment->decodable = 1; - } - } - else if( subsegment->decodable ) - { - if( (subsegment->first_ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)) - ? (sample->prop.leading == ISOM_SAMPLE_IS_DECODABLE_LEADING) - : (subsegment->first_ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START) ) - subsegment->first_ed_cts = LSMASH_MIN( sample->cts, subsegment->first_ed_cts ); - else - subsegment->decodable = 0; - } - return delimit; -} - -static int isom_append_fragment_sample_internal_initial( isom_trak_t *trak, lsmash_sample_t *sample ) -{ - /* 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. */ - uint32_t samples_per_packet; - int delimit = isom_update_sample_tables( trak, sample, &samples_per_packet ); - if( delimit < 0 ) - return -1; - else if( delimit == 1 ) - isom_append_fragment_track_run( trak->file, &trak->cache->chunk ); - /* Add a new sample into the pool of this track fragment. */ - if( isom_pool_sample( trak->cache->chunk.pool, sample, samples_per_packet ) ) - return -1; - trak->cache->fragment->has_samples = 1; - trak->cache->fragment->sample_count += 1; - return 0; -} - -static int isom_append_fragment_sample_internal( isom_traf_t *traf, lsmash_sample_t *sample ) -{ - /* 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. */ - int delimit = isom_update_fragment_sample_tables( traf, sample ); - if( delimit < 0 ) - return -1; - else if( delimit == 1 ) - isom_append_fragment_track_run( traf->file, &traf->cache->chunk ); - /* Add a new sample into the pool of this track fragment. */ - if( isom_pool_sample( traf->cache->chunk.pool, sample, 1 ) ) - return -1; - traf->cache->fragment->has_samples = 1; - traf->cache->fragment->sample_count += 1; - return 0; -} - -int isom_append_fragment_sample -( - lsmash_file_t *file, - uint32_t track_ID, - lsmash_sample_t *sample -) -{ - isom_fragment_manager_t *fragment = file->fragment; - assert( fragment && fragment->pool ); - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->file - || !trak->cache - || !trak->cache->fragment - || !trak->tkhd - || !trak->mdia - || !trak->mdia->mdhd - || trak->mdia->mdhd->timescale == 0 - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stsd - || !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 ) - { - /* Forbid adding a sample into the initial movie if requiring compatibility with Media Segment. */ - if( file->media_segment ) - return -1; - append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial; - track_fragment = trak; - } - else - { - isom_traf_t *traf = isom_get_traf( fragment->movie, track_ID ); - if( !traf ) - { - traf = isom_add_traf( 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; - if( (traf->cache->fragment->rap_grouping && isom_add_sample_grouping( (isom_box_t *)traf, ISOM_GROUP_TYPE_RAP ) < 0) - || (traf->cache->fragment->roll_grouping && isom_add_sample_grouping( (isom_box_t *)traf, ISOM_GROUP_TYPE_ROLL ) < 0) ) - return -1; - } - else if( !traf->file - || !traf->file->moov - || !traf->file->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 ) ) - { - uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket; - if( sample->length == frame_size ) - return append_sample_func( track_fragment, sample ); - else if( sample->length < frame_size ) - return -1; - /* Append samples splitted into each LPCMFrame. */ - uint64_t dts = sample->dts; - uint64_t cts = sample->cts; - for( uint32_t offset = 0; offset < sample->length; offset += frame_size ) - { - lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size ); - if( !lpcm_sample ) - return -1; - memcpy( lpcm_sample->data, sample->data + offset, frame_size ); - lpcm_sample->dts = dts++; - lpcm_sample->cts = cts++; - lpcm_sample->prop = sample->prop; - lpcm_sample->index = sample->index; - if( append_sample_func( track_fragment, lpcm_sample ) ) - { - lsmash_delete_sample( lpcm_sample ); - return -1; - } - } - lsmash_delete_sample( sample ); - return 0; - } - return append_sample_func( track_fragment, sample ); -} diff -Nru l-smash-1.9.1/fragment.h l-smash-2.3.0/fragment.h --- l-smash-1.9.1/fragment.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/fragment.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/***************************************************************************** - * fragment.h - ***************************************************************************** - * Copyright (C) 2011-2014 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. */ - -int isom_finish_final_fragment_movie -( - lsmash_file_t *file, - lsmash_adhoc_remux_t *remux -); - -int isom_set_fragment_last_duration -( - isom_traf_t *traf, - uint32_t last_duration -); - -int isom_append_fragment_track_run -( - lsmash_file_t *file, - isom_chunk_t *chunk -); - -int isom_flush_fragment_pooled_samples -( - lsmash_file_t *file, - uint32_t track_ID, - uint32_t last_sample_duration -); - -int isom_append_fragment_sample -( - lsmash_file_t *file, - uint32_t track_ID, - lsmash_sample_t *sample -); diff -Nru l-smash-1.9.1/h264.c l-smash-2.3.0/h264.c --- l-smash-1.9.1/h264.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/h264.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2495 +0,0 @@ -/***************************************************************************** - * h264.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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" - -#include -#include -#include - -#include "box.h" - -/*************************************************************************** - ITU-T Recommendation H.264 (04/13) - ISO/IEC 14496-15:2010 -***************************************************************************/ -#include "h264.h" -#include "nalu.h" - -#define IF_INVALID_VALUE( x ) if( x ) -#define IF_EXCEED_INT32( x ) if( (x) < INT32_MIN || (x) > INT32_MAX ) -#define H264_REQUIRES_AVCC_EXTENSION( x ) ((x) == 100 || (x) == 110 || (x) == 122 || (x) == 144) -#define H264_POC_DEBUG_PRINT 0 - -typedef enum -{ - H264_SLICE_TYPE_P = 0, - H264_SLICE_TYPE_B = 1, - H264_SLICE_TYPE_I = 2, - H264_SLICE_TYPE_SP = 3, - H264_SLICE_TYPE_SI = 4 -} h264_slice_type; - -void lsmash_destroy_h264_parameter_sets -( - lsmash_h264_specific_parameters_t *param -) -{ - if( !param || !param->parameter_sets ) - return; - lsmash_remove_entries( param->parameter_sets->sps_list, isom_remove_dcr_ps ); - lsmash_remove_entries( param->parameter_sets->pps_list, isom_remove_dcr_ps ); - lsmash_remove_entries( param->parameter_sets->spsext_list, isom_remove_dcr_ps ); - lsmash_free( param->parameter_sets ); - param->parameter_sets = NULL; -} - -void h264_destruct_specific_data -( - void *data -) -{ - if( !data ) - return; - lsmash_destroy_h264_parameter_sets( data ); - lsmash_free( data ); -} - -void h264_cleanup_parser -( - h264_info_t *info -) -{ - if( !info ) - return; - lsmash_remove_entries( info->sps_list, NULL ); - lsmash_remove_entries( info->pps_list, NULL ); - lsmash_remove_entries( info->slice_list, NULL ); - lsmash_destroy_h264_parameter_sets( &info->avcC_param ); - lsmash_destroy_h264_parameter_sets( &info->avcC_param_next ); - lsmash_stream_buffers_cleanup( info->buffer.sb ); - lsmash_bits_adhoc_cleanup( info->bits ); - info->bits = NULL; -} - -int h264_setup_parser -( - h264_info_t *info, - lsmash_stream_buffers_t *sb, - int parse_only, - lsmash_stream_buffers_type type, - void *stream -) -{ - assert( sb ); - if( !info ) - return -1; - memset( info, 0, sizeof(h264_info_t) ); - info->avcC_param .lengthSizeMinusOne = H264_DEFAULT_NALU_LENGTH_SIZE - 1; - info->avcC_param_next.lengthSizeMinusOne = H264_DEFAULT_NALU_LENGTH_SIZE - 1; - h264_stream_buffer_t *hb = &info->buffer; - hb->sb = sb; - lsmash_stream_buffers_setup( sb, type, stream ); - sb->bank = lsmash_create_multiple_buffers( parse_only ? 2 : 4, H264_DEFAULT_BUFFER_SIZE ); - if( !sb->bank ) - return -1; - sb->start = lsmash_withdraw_buffer( sb->bank, 1 ); - hb->rbsp = lsmash_withdraw_buffer( sb->bank, 2 ); - sb->pos = sb->start; - sb->end = sb->start; - if( !parse_only ) - { - info->picture.au = lsmash_withdraw_buffer( sb->bank, 3 ); - info->picture.incomplete_au = lsmash_withdraw_buffer( sb->bank, 4 ); - } - info->bits = lsmash_bits_adhoc_create(); - if( !info->bits ) - { - lsmash_stream_buffers_cleanup( sb ); - return -1; - } - lsmash_init_entry_list( info->sps_list ); - lsmash_init_entry_list( info->pps_list ); - lsmash_init_entry_list( info->slice_list ); - return 0; -} - -static h264_sps_t *h264_get_sps -( - lsmash_entry_list_t *sps_list, - uint8_t sps_id -) -{ - if( !sps_list || sps_id > 31 ) - return NULL; - for( lsmash_entry_t *entry = sps_list->head; entry; entry = entry->next ) - { - h264_sps_t *sps = (h264_sps_t *)entry->data; - if( !sps ) - return NULL; - if( sps->seq_parameter_set_id == sps_id ) - return sps; - } - h264_sps_t *sps = lsmash_malloc_zero( sizeof(h264_sps_t) ); - if( !sps ) - return NULL; - sps->seq_parameter_set_id = sps_id; - if( lsmash_add_entry( sps_list, sps ) ) - { - lsmash_free( sps ); - return NULL; - } - return sps; -} - -static h264_pps_t *h264_get_pps -( - lsmash_entry_list_t *pps_list, - uint8_t pps_id -) -{ - if( !pps_list ) - return NULL; - for( lsmash_entry_t *entry = pps_list->head; entry; entry = entry->next ) - { - h264_pps_t *pps = (h264_pps_t *)entry->data; - if( !pps ) - return NULL; - if( pps->pic_parameter_set_id == pps_id ) - return pps; - } - h264_pps_t *pps = lsmash_malloc_zero( sizeof(h264_pps_t) ); - if( !pps ) - return NULL; - pps->pic_parameter_set_id = pps_id; - if( lsmash_add_entry( pps_list, pps ) ) - { - lsmash_free( pps ); - return NULL; - } - return pps; -} - -static h264_slice_info_t *h264_get_slice_info -( - lsmash_entry_list_t *slice_list, - uint8_t slice_id -) -{ - if( !slice_list ) - return NULL; - for( lsmash_entry_t *entry = slice_list->head; entry; entry = entry->next ) - { - h264_slice_info_t *slice = (h264_slice_info_t *)entry->data; - if( !slice ) - return NULL; - if( slice->slice_id == slice_id ) - return slice; - } - h264_slice_info_t *slice = lsmash_malloc_zero( sizeof(h264_slice_info_t) ); - if( !slice ) - return NULL; - slice->slice_id = slice_id; - if( lsmash_add_entry( slice_list, slice ) ) - { - lsmash_free( slice ); - return NULL; - } - return slice; -} - -int h264_calculate_poc -( - h264_info_t *info, - h264_picture_info_t *picture, - h264_picture_info_t *prev_picture -) -{ -#if H264_POC_DEBUG_PRINT - fprintf( stderr, "PictureOrderCount\n" ); -#endif - h264_pps_t *pps = h264_get_pps( info->pps_list, picture->pic_parameter_set_id ); - if( !pps ) - return -1; - h264_sps_t *sps = h264_get_sps( info->sps_list, pps->seq_parameter_set_id ); - if( !sps ) - return -1; - int64_t TopFieldOrderCnt = 0; - int64_t BottomFieldOrderCnt = 0; - if( sps->pic_order_cnt_type == 0 ) - { - int32_t prevPicOrderCntMsb; - int32_t prevPicOrderCntLsb; - if( picture->idr ) - { - prevPicOrderCntMsb = 0; - prevPicOrderCntLsb = 0; - } - else if( prev_picture->ref_pic_has_mmco5 ) - { - prevPicOrderCntMsb = 0; - prevPicOrderCntLsb = prev_picture->ref_pic_bottom_field_flag ? 0 : prev_picture->ref_pic_TopFieldOrderCnt; - } - else - { - prevPicOrderCntMsb = prev_picture->ref_pic_PicOrderCntMsb; - prevPicOrderCntLsb = prev_picture->ref_pic_PicOrderCntLsb; - } - int64_t PicOrderCntMsb; - int32_t pic_order_cnt_lsb = picture->pic_order_cnt_lsb; - uint64_t MaxPicOrderCntLsb = sps->MaxPicOrderCntLsb; - if( (pic_order_cnt_lsb < prevPicOrderCntLsb) - && ((prevPicOrderCntLsb - pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2)) ) - PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; - else if( (pic_order_cnt_lsb > prevPicOrderCntLsb) - && ((pic_order_cnt_lsb - prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2)) ) - PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; - else - PicOrderCntMsb = prevPicOrderCntMsb; - IF_EXCEED_INT32( PicOrderCntMsb ) - return -1; - BottomFieldOrderCnt = TopFieldOrderCnt = PicOrderCntMsb + pic_order_cnt_lsb; - if( !picture->field_pic_flag ) - BottomFieldOrderCnt += picture->delta_pic_order_cnt_bottom; - IF_EXCEED_INT32( TopFieldOrderCnt ) - return -1; - IF_EXCEED_INT32( BottomFieldOrderCnt ) - return -1; - if( !picture->disposable ) - { - picture->ref_pic_has_mmco5 = picture->has_mmco5; - picture->ref_pic_bottom_field_flag = picture->bottom_field_flag; - picture->ref_pic_TopFieldOrderCnt = TopFieldOrderCnt; - picture->ref_pic_PicOrderCntMsb = PicOrderCntMsb; - picture->ref_pic_PicOrderCntLsb = pic_order_cnt_lsb; - } -#if H264_POC_DEBUG_PRINT - fprintf( stderr, " prevPicOrderCntMsb: %"PRId32"\n", prevPicOrderCntMsb ); - fprintf( stderr, " prevPicOrderCntLsb: %"PRId32"\n", prevPicOrderCntLsb ); - fprintf( stderr, " PicOrderCntMsb: %"PRId64"\n", PicOrderCntMsb ); - fprintf( stderr, " pic_order_cnt_lsb: %"PRId32"\n", pic_order_cnt_lsb ); - fprintf( stderr, " MaxPicOrderCntLsb: %"PRIu64"\n", MaxPicOrderCntLsb ); -#endif - } - else if( sps->pic_order_cnt_type == 1 ) - { - uint32_t frame_num = picture->frame_num; - uint32_t prevFrameNum = prev_picture->has_mmco5 ? 0 : prev_picture->frame_num; - uint32_t prevFrameNumOffset = prev_picture->has_mmco5 ? 0 : prev_picture->FrameNumOffset; - uint64_t FrameNumOffset = picture->idr ? 0 : prevFrameNumOffset + (prevFrameNum > frame_num ? sps->MaxFrameNum : 0); - IF_INVALID_VALUE( FrameNumOffset > INT32_MAX ) - return -1; - int64_t expectedPicOrderCnt; - if( sps->num_ref_frames_in_pic_order_cnt_cycle ) - { - uint64_t absFrameNum = FrameNumOffset + frame_num; - absFrameNum -= picture->disposable && absFrameNum > 0; - if( absFrameNum ) - { - uint64_t picOrderCntCycleCnt = (absFrameNum - 1) / sps->num_ref_frames_in_pic_order_cnt_cycle; - uint8_t frameNumInPicOrderCntCycle = (absFrameNum - 1) % sps->num_ref_frames_in_pic_order_cnt_cycle; - expectedPicOrderCnt = picOrderCntCycleCnt * sps->ExpectedDeltaPerPicOrderCntCycle; - for( uint8_t i = 0; i <= frameNumInPicOrderCntCycle; i++ ) - expectedPicOrderCnt += sps->offset_for_ref_frame[i]; - } - else - expectedPicOrderCnt = 0; - } - else - expectedPicOrderCnt = 0; - if( picture->disposable ) - expectedPicOrderCnt += sps->offset_for_non_ref_pic; - TopFieldOrderCnt = expectedPicOrderCnt + picture->delta_pic_order_cnt[0]; - BottomFieldOrderCnt = TopFieldOrderCnt + sps->offset_for_top_to_bottom_field; - if( !picture->field_pic_flag ) - BottomFieldOrderCnt += picture->delta_pic_order_cnt[1]; - IF_EXCEED_INT32( TopFieldOrderCnt ) - return -1; - IF_EXCEED_INT32( BottomFieldOrderCnt ) - return -1; - picture->FrameNumOffset = FrameNumOffset; - } - else if( sps->pic_order_cnt_type == 2 ) - { - uint32_t frame_num = picture->frame_num; - uint32_t prevFrameNum = prev_picture->has_mmco5 ? 0 : prev_picture->frame_num; - int32_t prevFrameNumOffset = prev_picture->has_mmco5 ? 0 : prev_picture->FrameNumOffset; - int64_t FrameNumOffset; - int64_t tempPicOrderCnt; - if( picture->idr ) - { - FrameNumOffset = 0; - tempPicOrderCnt = 0; - } - else - { - FrameNumOffset = prevFrameNumOffset + (prevFrameNum > frame_num ? sps->MaxFrameNum : 0); - tempPicOrderCnt = 2 * (FrameNumOffset + frame_num) - picture->disposable; - IF_EXCEED_INT32( FrameNumOffset ) - return -1; - IF_EXCEED_INT32( tempPicOrderCnt ) - return -1; - } - TopFieldOrderCnt = tempPicOrderCnt; - BottomFieldOrderCnt = tempPicOrderCnt; - picture->FrameNumOffset = FrameNumOffset; - } - if( !picture->field_pic_flag ) - picture->PicOrderCnt = LSMASH_MIN( TopFieldOrderCnt, BottomFieldOrderCnt ); - else - picture->PicOrderCnt = picture->bottom_field_flag ? BottomFieldOrderCnt : TopFieldOrderCnt; -#if H264_POC_DEBUG_PRINT - if( picture->field_pic_flag ) - { - if( !picture->bottom_field_flag ) - fprintf( stderr, " TopFieldOrderCnt: %"PRId64"\n", TopFieldOrderCnt ); - else - fprintf( stderr, " BottomFieldOrderCnt: %"PRId64"\n", BottomFieldOrderCnt ); - } - fprintf( stderr, " POC: %"PRId32"\n", picture->PicOrderCnt ); -#endif - return 0; -} - -int h264_check_nalu_header -( - h264_nalu_header_t *nalu_header, - lsmash_stream_buffers_t *sb, - int use_long_start_code -) -{ - uint8_t forbidden_zero_bit = (*sb->pos >> 7) & 0x01; - uint8_t nal_ref_idc = nalu_header->nal_ref_idc = (*sb->pos >> 5) & 0x03; - uint8_t nal_unit_type = nalu_header->nal_unit_type = *sb->pos & 0x1f; - nalu_header->length = 1; - sb->pos += nalu_header->length; - if( nal_unit_type == H264_NALU_TYPE_PREFIX - || nal_unit_type >= H264_NALU_TYPE_SLICE_EXT ) - return -1; /* We don't support yet. */ - IF_INVALID_VALUE( forbidden_zero_bit ) - return -1; - /* SPS and PPS require long start code (0x00000001). - * Also AU delimiter requires it too because this type of NALU shall be the first NALU of any AU if present. */ - IF_INVALID_VALUE( !use_long_start_code - && (nal_unit_type == H264_NALU_TYPE_SPS - || nal_unit_type == H264_NALU_TYPE_PPS - || nal_unit_type == H264_NALU_TYPE_AUD) ) - return -1; - if( nal_ref_idc ) - { - /* nal_ref_idc shall be equal to 0 for all NALUs having nal_unit_type equal to 6, 9, 10, 11, or 12. */ - IF_INVALID_VALUE( nal_unit_type == H264_NALU_TYPE_SEI - || nal_unit_type == H264_NALU_TYPE_AUD - || nal_unit_type == H264_NALU_TYPE_EOS - || nal_unit_type == H264_NALU_TYPE_EOB - || nal_unit_type == H264_NALU_TYPE_FD ) - return -1; - } - else - /* nal_ref_idc shall not be equal to 0 for NALUs with nal_unit_type equal to 5. */ - IF_INVALID_VALUE( nal_unit_type == H264_NALU_TYPE_SLICE_IDR ) - return -1; - return 0; -} - -static int h264_parse_scaling_list -( - lsmash_bits_t *bits, - int sizeOfScalingList -) -{ - /* scaling_list( scalingList, sizeOfScalingList, useDefaultScalingMatrixFlag ) */ - int nextScale = 8; - for( int i = 0; i < sizeOfScalingList; i++ ) - { - int64_t delta_scale = nalu_get_exp_golomb_se( bits ); - IF_INVALID_VALUE( delta_scale < -128 || delta_scale > 127 ) - return -1; - nextScale = (nextScale + delta_scale + 256) % 256; - if( nextScale == 0 ) - break; - } - return 0; -} - -static int h264_parse_hrd_parameters -( - lsmash_bits_t *bits, - h264_hrd_t *hrd -) -{ - /* hrd_parameters() */ - uint64_t cpb_cnt_minus1 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( cpb_cnt_minus1 > 31 ) - return -1; - lsmash_bits_get( bits, 4 ); /* bit_rate_scale */ - lsmash_bits_get( bits, 4 ); /* cpb_size_scale */ - for( uint64_t SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++ ) - { - nalu_get_exp_golomb_ue( bits ); /* bit_rate_value_minus1[ SchedSelIdx ] */ - nalu_get_exp_golomb_ue( bits ); /* cpb_size_value_minus1[ SchedSelIdx ] */ - lsmash_bits_get( bits, 1 ); /* cbr_flag [ SchedSelIdx ] */ - } - lsmash_bits_get( bits, 5 ); /* initial_cpb_removal_delay_length_minus1 */ - hrd->cpb_removal_delay_length = lsmash_bits_get( bits, 5 ) + 1; - hrd->dpb_output_delay_length = lsmash_bits_get( bits, 5 ) + 1; - lsmash_bits_get( bits, 5 ); /* time_offset_length */ - return 0; -} - -static int h264_parse_sps_minimally -( - lsmash_bits_t *bits, - h264_sps_t *sps, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - memset( sps, 0, sizeof(h264_sps_t) ); - sps->profile_idc = lsmash_bits_get( bits, 8 ); - sps->constraint_set_flags = lsmash_bits_get( bits, 8 ); - sps->level_idc = lsmash_bits_get( bits, 8 ); - uint64_t seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( seq_parameter_set_id > 31 ) - return -1; - sps->seq_parameter_set_id = seq_parameter_set_id; - if( sps->profile_idc == 100 || sps->profile_idc == 110 || sps->profile_idc == 122 - || sps->profile_idc == 244 || sps->profile_idc == 44 || sps->profile_idc == 83 - || sps->profile_idc == 86 || sps->profile_idc == 118 || sps->profile_idc == 128 - || sps->profile_idc == 138 ) - { - sps->chroma_format_idc = nalu_get_exp_golomb_ue( bits ); - if( sps->chroma_format_idc == 3 ) - sps->separate_colour_plane_flag = lsmash_bits_get( bits, 1 ); - uint64_t bit_depth_luma_minus8 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( bit_depth_luma_minus8 > 6 ) - return -1; - uint64_t bit_depth_chroma_minus8 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( bit_depth_chroma_minus8 > 6 ) - return -1; - sps->bit_depth_luma_minus8 = bit_depth_luma_minus8; - sps->bit_depth_chroma_minus8 = bit_depth_chroma_minus8; - lsmash_bits_get( bits, 1 ); /* qpprime_y_zero_transform_bypass_flag */ - if( lsmash_bits_get( bits, 1 ) ) /* seq_scaling_matrix_present_flag */ - { - int num_loops = sps->chroma_format_idc != 3 ? 8 : 12; - for( int i = 0; i < num_loops; i++ ) - if( lsmash_bits_get( bits, 1 ) /* seq_scaling_list_present_flag[i] */ - && h264_parse_scaling_list( bits, i < 6 ? 16 : 64 ) ) - return -1; - } - } - else - { - sps->chroma_format_idc = 1; - sps->separate_colour_plane_flag = 0; - sps->bit_depth_luma_minus8 = 0; - sps->bit_depth_chroma_minus8 = 0; - } - return bits->bs->error ? -1 : 0; -} - -int h264_parse_sps -( - h264_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - /* seq_parameter_set_data() */ - h264_sps_t temp_sps; - if( h264_parse_sps_minimally( bits, &temp_sps, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - h264_sps_t *sps = h264_get_sps( info->sps_list, temp_sps.seq_parameter_set_id ); - if( !sps ) - return -1; - memset( sps, 0, sizeof(h264_sps_t) ); - sps->profile_idc = temp_sps.profile_idc; - sps->constraint_set_flags = temp_sps.constraint_set_flags; - sps->level_idc = temp_sps.level_idc; - sps->seq_parameter_set_id = temp_sps.seq_parameter_set_id; - sps->chroma_format_idc = temp_sps.chroma_format_idc; - sps->separate_colour_plane_flag = temp_sps.separate_colour_plane_flag; - sps->bit_depth_luma_minus8 = temp_sps.bit_depth_luma_minus8; - sps->bit_depth_chroma_minus8 = temp_sps.bit_depth_chroma_minus8; - sps->ChromaArrayType = sps->separate_colour_plane_flag ? 0 : sps->chroma_format_idc; - uint64_t log2_max_frame_num_minus4 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( log2_max_frame_num_minus4 > 12 ) - return -1; - sps->log2_max_frame_num = log2_max_frame_num_minus4 + 4; - sps->MaxFrameNum = 1 << sps->log2_max_frame_num; - uint64_t pic_order_cnt_type = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( pic_order_cnt_type > 2 ) - return -1; - sps->pic_order_cnt_type = pic_order_cnt_type; - if( sps->pic_order_cnt_type == 0 ) - { - uint64_t log2_max_pic_order_cnt_lsb_minus4 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( log2_max_pic_order_cnt_lsb_minus4 > 12 ) - return -1; - sps->log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4; - sps->MaxPicOrderCntLsb = 1 << sps->log2_max_pic_order_cnt_lsb; - } - else if( sps->pic_order_cnt_type == 1 ) - { - sps->delta_pic_order_always_zero_flag = lsmash_bits_get( bits, 1 ); - int64_t max_value = ((uint64_t)1 << 31) - 1; - int64_t min_value = -((uint64_t)1 << 31) + 1; - int64_t offset_for_non_ref_pic = nalu_get_exp_golomb_se( bits ); - if( offset_for_non_ref_pic < min_value || offset_for_non_ref_pic > max_value ) - return -1; - sps->offset_for_non_ref_pic = offset_for_non_ref_pic; - int64_t offset_for_top_to_bottom_field = nalu_get_exp_golomb_se( bits ); - if( offset_for_top_to_bottom_field < min_value || offset_for_top_to_bottom_field > max_value ) - return -1; - sps->offset_for_top_to_bottom_field = offset_for_top_to_bottom_field; - uint64_t num_ref_frames_in_pic_order_cnt_cycle = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( num_ref_frames_in_pic_order_cnt_cycle > 255 ) - return -1; - sps->num_ref_frames_in_pic_order_cnt_cycle = num_ref_frames_in_pic_order_cnt_cycle; - sps->ExpectedDeltaPerPicOrderCntCycle = 0; - for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) - { - int64_t offset_for_ref_frame = nalu_get_exp_golomb_se( bits ); - if( offset_for_ref_frame < min_value || offset_for_ref_frame > max_value ) - return -1; - sps->offset_for_ref_frame[i] = offset_for_ref_frame; - sps->ExpectedDeltaPerPicOrderCntCycle += offset_for_ref_frame; - } - } - sps->max_num_ref_frames = nalu_get_exp_golomb_ue( bits ); - lsmash_bits_get( bits, 1 ); /* gaps_in_frame_num_value_allowed_flag */ - uint64_t pic_width_in_mbs_minus1 = nalu_get_exp_golomb_ue( bits ); - uint64_t pic_height_in_map_units_minus1 = nalu_get_exp_golomb_ue( bits ); - sps->frame_mbs_only_flag = lsmash_bits_get( bits, 1 ); - if( !sps->frame_mbs_only_flag ) - lsmash_bits_get( bits, 1 ); /* mb_adaptive_frame_field_flag */ - lsmash_bits_get( bits, 1 ); /* direct_8x8_inference_flag */ - uint64_t PicWidthInMbs = pic_width_in_mbs_minus1 + 1; - uint64_t PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1; - sps->PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits; - sps->cropped_width = PicWidthInMbs * 16; - sps->cropped_height = (2 - sps->frame_mbs_only_flag) * PicHeightInMapUnits * 16; - if( lsmash_bits_get( bits, 1 ) ) /* frame_cropping_flag */ - { - uint8_t CropUnitX; - uint8_t CropUnitY; - if( sps->ChromaArrayType == 0 ) - { - CropUnitX = 1; - CropUnitY = 2 - sps->frame_mbs_only_flag; - } - else - { - static const int SubWidthC [] = { 0, 2, 2, 1 }; - static const int SubHeightC[] = { 0, 2, 1, 1 }; - CropUnitX = SubWidthC [ sps->chroma_format_idc ]; - CropUnitY = SubHeightC[ sps->chroma_format_idc ] * (2 - sps->frame_mbs_only_flag); - } - uint64_t frame_crop_left_offset = nalu_get_exp_golomb_ue( bits ); - uint64_t frame_crop_right_offset = nalu_get_exp_golomb_ue( bits ); - uint64_t frame_crop_top_offset = nalu_get_exp_golomb_ue( bits ); - uint64_t frame_crop_bottom_offset = nalu_get_exp_golomb_ue( bits ); - sps->cropped_width -= (frame_crop_left_offset + frame_crop_right_offset) * CropUnitX; - sps->cropped_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * CropUnitY; - } - if( lsmash_bits_get( bits, 1 ) ) /* vui_parameters_present_flag */ - { - /* vui_parameters() */ - if( lsmash_bits_get( bits, 1 ) ) /* aspect_ratio_info_present_flag */ - { - uint8_t aspect_ratio_idc = lsmash_bits_get( bits, 8 ); - if( aspect_ratio_idc == 255 ) - { - /* Extended_SAR */ - sps->vui.sar_width = lsmash_bits_get( bits, 16 ); - sps->vui.sar_height = lsmash_bits_get( bits, 16 ); - } - else - { - static const struct - { - uint16_t sar_width; - uint16_t sar_height; - } pre_defined_sar[] - = { - { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, - { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, { 80, 33 }, - { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, { 4, 3 }, - { 3, 2 }, { 2, 1 } - }; - if( aspect_ratio_idc < (sizeof(pre_defined_sar) / sizeof(pre_defined_sar[0])) ) - { - sps->vui.sar_width = pre_defined_sar[ aspect_ratio_idc ].sar_width; - sps->vui.sar_height = pre_defined_sar[ aspect_ratio_idc ].sar_height; - } - else - { - /* Behavior when unknown aspect_ratio_idc is detected is not specified in the specification. */ - sps->vui.sar_width = 0; - sps->vui.sar_height = 0; - } - } - } - if( lsmash_bits_get( bits, 1 ) ) /* overscan_info_present_flag */ - lsmash_bits_get( bits, 1 ); /* overscan_appropriate_flag */ - if( lsmash_bits_get( bits, 1 ) ) /* video_signal_type_present_flag */ - { - lsmash_bits_get( bits, 3 ); /* video_format */ - sps->vui.video_full_range_flag = lsmash_bits_get( bits, 1 ); - if( lsmash_bits_get( bits, 1 ) ) /* colour_description_present_flag */ - { - sps->vui.colour_primaries = lsmash_bits_get( bits, 8 ); - sps->vui.transfer_characteristics = lsmash_bits_get( bits, 8 ); - sps->vui.matrix_coefficients = lsmash_bits_get( bits, 8 ); - } - } - if( lsmash_bits_get( bits, 1 ) ) /* chroma_loc_info_present_flag */ - { - nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_top_field */ - nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_bottom_field */ - } - if( lsmash_bits_get( bits, 1 ) ) /* timing_info_present_flag */ - { - sps->vui.num_units_in_tick = lsmash_bits_get( bits, 32 ); - sps->vui.time_scale = lsmash_bits_get( bits, 32 ); - sps->vui.fixed_frame_rate_flag = lsmash_bits_get( bits, 1 ); - } - else - { - sps->vui.num_units_in_tick = 1; /* arbitrary */ - sps->vui.time_scale = 50; /* arbitrary */ - sps->vui.fixed_frame_rate_flag = 0; - } - int nal_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); - if( nal_hrd_parameters_present_flag - && h264_parse_hrd_parameters( bits, &sps->vui.hrd ) ) - return -1; - int vcl_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); - if( vcl_hrd_parameters_present_flag - && h264_parse_hrd_parameters( bits, &sps->vui.hrd ) ) - return -1; - if( nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag ) - { - sps->vui.hrd.present = 1; - sps->vui.hrd.CpbDpbDelaysPresentFlag = 1; - lsmash_bits_get( bits, 1 ); /* low_delay_hrd_flag */ - } - sps->vui.pic_struct_present_flag = lsmash_bits_get( bits, 1 ); - if( lsmash_bits_get( bits, 1 ) ) /* bitstream_restriction_flag */ - { - lsmash_bits_get( bits, 1 ); /* motion_vectors_over_pic_boundaries_flag */ - nalu_get_exp_golomb_ue( bits ); /* max_bytes_per_pic_denom */ - nalu_get_exp_golomb_ue( bits ); /* max_bits_per_mb_denom */ - nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_horizontal */ - nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_vertical */ - nalu_get_exp_golomb_ue( bits ); /* max_num_reorder_frames */ - nalu_get_exp_golomb_ue( bits ); /* max_dec_frame_buffering */ - } - } - else - { - sps->vui.video_full_range_flag = 0; - sps->vui.num_units_in_tick = 1; /* arbitrary */ - sps->vui.time_scale = 50; /* arbitrary */ - sps->vui.fixed_frame_rate_flag = 0; - } - /* rbsp_trailing_bits() */ - IF_INVALID_VALUE( !lsmash_bits_get( bits, 1 ) ) /* rbsp_stop_one_bit */ - return -1; - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - sps->present = 1; - info->sps = *sps; - return 0; -} - -static int h264_parse_pps_minimally -( - lsmash_bits_t *bits, - h264_pps_t *pps, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - memset( pps, 0, sizeof(h264_pps_t) ); - uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( pic_parameter_set_id > 255 ) - return -1; - pps->pic_parameter_set_id = pic_parameter_set_id; - return bits->bs->error ? -1 : 0; -} - -int h264_parse_pps -( - h264_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - /* pic_parameter_set_rbsp */ - h264_pps_t temp_pps; - if( h264_parse_pps_minimally( bits, &temp_pps, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - h264_pps_t *pps = h264_get_pps( info->pps_list, temp_pps.pic_parameter_set_id ); - if( !pps ) - return -1; - memset( pps, 0, sizeof(h264_pps_t) ); - pps->pic_parameter_set_id = temp_pps.pic_parameter_set_id; - uint64_t seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( seq_parameter_set_id > 31 ) - return -1; - h264_sps_t *sps = h264_get_sps( info->sps_list, seq_parameter_set_id ); - if( !sps ) - return -1; - pps->seq_parameter_set_id = seq_parameter_set_id; - pps->entropy_coding_mode_flag = lsmash_bits_get( bits, 1 ); - pps->bottom_field_pic_order_in_frame_present_flag = lsmash_bits_get( bits, 1 ); - uint64_t num_slice_groups_minus1 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( num_slice_groups_minus1 > 7 ) - return -1; - pps->num_slice_groups_minus1 = num_slice_groups_minus1; - if( num_slice_groups_minus1 ) /* num_slice_groups_minus1 */ - { - uint64_t slice_group_map_type = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( slice_group_map_type > 6 ) - return -1; - pps->slice_group_map_type = slice_group_map_type; - if( slice_group_map_type == 0 ) - for( uint64_t iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ ) - nalu_get_exp_golomb_ue( bits ); /* run_length_minus1[ iGroup ] */ - else if( slice_group_map_type == 2 ) - for( uint64_t iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) - { - nalu_get_exp_golomb_ue( bits ); /* top_left [ iGroup ] */ - nalu_get_exp_golomb_ue( bits ); /* bottom_right[ iGroup ] */ - } - else if( slice_group_map_type == 3 - || slice_group_map_type == 4 - || slice_group_map_type == 5 ) - { - lsmash_bits_get( bits, 1 ); /* slice_group_change_direction_flag */ - uint64_t slice_group_change_rate_minus1 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( slice_group_change_rate_minus1 > (sps->PicSizeInMapUnits - 1) ) - return -1; - pps->SliceGroupChangeRate = slice_group_change_rate_minus1 + 1; - } - else if( slice_group_map_type == 6 ) - { - uint64_t pic_size_in_map_units_minus1 = nalu_get_exp_golomb_ue( bits ); - int length = lsmash_ceil_log2( num_slice_groups_minus1 + 1 ); - for( uint64_t i = 0; i <= pic_size_in_map_units_minus1; i++ ) - /* slice_group_id */ - IF_INVALID_VALUE( lsmash_bits_get( bits, length ) > num_slice_groups_minus1 ) - return -1; - } - } - pps->num_ref_idx_l0_default_active_minus1 = nalu_get_exp_golomb_ue( bits ); - pps->num_ref_idx_l1_default_active_minus1 = nalu_get_exp_golomb_ue( bits ); - pps->weighted_pred_flag = lsmash_bits_get( bits, 1 ); - pps->weighted_bipred_idc = lsmash_bits_get( bits, 2 ); - nalu_get_exp_golomb_se( bits ); /* pic_init_qp_minus26 */ - nalu_get_exp_golomb_se( bits ); /* pic_init_qs_minus26 */ - nalu_get_exp_golomb_se( bits ); /* chroma_qp_index_offset */ - pps->deblocking_filter_control_present_flag = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 1 ); /* constrained_intra_pred_flag */ - pps->redundant_pic_cnt_present_flag = lsmash_bits_get( bits, 1 ); - if( nalu_check_more_rbsp_data( bits ) ) - { - int transform_8x8_mode_flag = lsmash_bits_get( bits, 1 ); - if( lsmash_bits_get( bits, 1 ) ) /* pic_scaling_matrix_present_flag */ - { - int num_loops = 6 + (sps->chroma_format_idc != 3 ? 2 : 6) * transform_8x8_mode_flag; - for( int i = 0; i < num_loops; i++ ) - if( lsmash_bits_get( bits, 1 ) /* pic_scaling_list_present_flag[i] */ - && h264_parse_scaling_list( bits, i < 6 ? 16 : 64 ) ) - return -1; - } - nalu_get_exp_golomb_se( bits ); /* second_chroma_qp_index_offset */ - } - /* rbsp_trailing_bits() */ - IF_INVALID_VALUE( !lsmash_bits_get( bits, 1 ) ) /* rbsp_stop_one_bit */ - return -1; - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - pps->present = 1; - info->sps = *sps; - info->pps = *pps; - return 0; -} - -int h264_parse_sei -( - lsmash_bits_t *bits, - h264_sps_t *sps, - h264_sei_t *sei, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - uint8_t *rbsp_start = rbsp_buffer; - uint64_t rbsp_pos = 0; - do - { - /* sei_message() */ - uint32_t payloadType = 0; - for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) - { - /* 0xff : ff_byte - * otherwise: last_payload_type_byte */ - payloadType += temp; - ++rbsp_pos; - if( temp != 0xff ) - break; - } - uint32_t payloadSize = 0; - for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) - { - /* 0xff : ff_byte - * otherwise: last_payload_size_byte */ - payloadSize += temp; - ++rbsp_pos; - if( temp != 0xff ) - break; - } - if( payloadType == 1 ) - { - /* pic_timing */ - h264_hrd_t *hrd = sps ? &sps->vui.hrd : NULL; - if( !hrd ) - goto skip_sei_message; /* Any active SPS is not found. */ - sei->pic_timing.present = 1; - if( hrd->CpbDpbDelaysPresentFlag ) - { - lsmash_bits_get( bits, hrd->cpb_removal_delay_length ); /* cpb_removal_delay */ - lsmash_bits_get( bits, hrd->dpb_output_delay_length ); /* dpb_output_delay */ - } - if( sps->vui.pic_struct_present_flag ) - { - sei->pic_timing.pic_struct = lsmash_bits_get( bits, 4 ); - /* Skip the remaining bits. */ - uint32_t remaining_bits = payloadSize * 8 - 4; - if( hrd->CpbDpbDelaysPresentFlag ) - remaining_bits -= hrd->cpb_removal_delay_length - + hrd->dpb_output_delay_length; - lsmash_bits_get( bits, remaining_bits ); - } - } - else if( payloadType == 3 ) - { - /* filler_payload - * AVC file format is forbidden to contain this. */ - return -1; - } - else if( payloadType == 6 ) - { - /* recovery_point */ - sei->recovery_point.present = 1; - sei->recovery_point.random_accessible = 1; - sei->recovery_point.recovery_frame_cnt = nalu_get_exp_golomb_ue( bits ); - lsmash_bits_get( bits, 1 ); /* exact_match_flag */ - sei->recovery_point.broken_link_flag = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 2 ); /* changing_slice_group_idc */ - } - else - { -skip_sei_message: - lsmash_bits_get( bits, payloadSize * 8 ); - } - lsmash_bits_get_align( bits ); - rbsp_pos += payloadSize; - } while( *(rbsp_start + rbsp_pos) != 0x80 ); /* All SEI messages are byte aligned at their end. - * Therefore, 0x80 shall be rbsp_trailing_bits(). */ - lsmash_bits_empty( bits ); - return bits->bs->error ? -1 : 0; -} - -static int h264_parse_slice_header -( - h264_info_t *info, - h264_nalu_header_t *nalu_header -) -{ - h264_slice_info_t *slice = &info->slice; - memset( slice, 0, sizeof(h264_slice_info_t) ); - /* slice_header() */ - lsmash_bits_t *bits = info->bits; - nalu_get_exp_golomb_ue( bits ); /* first_mb_in_slice */ - uint8_t slice_type = slice->type = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( (uint64_t)slice->type > 9 ) - return -1; - if( slice_type > 4 ) - slice_type = slice->type -= 5; - uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( pic_parameter_set_id > 255 ) - return -1; - slice->pic_parameter_set_id = pic_parameter_set_id; - h264_pps_t *pps = h264_get_pps( info->pps_list, pic_parameter_set_id ); - if( !pps ) - return -1; - h264_sps_t *sps = h264_get_sps( info->sps_list, pps->seq_parameter_set_id ); - if( !sps ) - return -1; - slice->seq_parameter_set_id = pps->seq_parameter_set_id; - slice->nal_ref_idc = nalu_header->nal_ref_idc; - slice->IdrPicFlag = (nalu_header->nal_unit_type == H264_NALU_TYPE_SLICE_IDR); - slice->pic_order_cnt_type = sps->pic_order_cnt_type; - IF_INVALID_VALUE( (slice->IdrPicFlag || sps->max_num_ref_frames == 0) && slice_type != 2 && slice_type != 4 ) - return -1; - if( sps->separate_colour_plane_flag ) - lsmash_bits_get( bits, 2 ); /* colour_plane_id */ - uint64_t frame_num = lsmash_bits_get( bits, sps->log2_max_frame_num ); - IF_INVALID_VALUE( frame_num >= (1 << sps->log2_max_frame_num) || (slice->IdrPicFlag && frame_num) ) - return -1; - slice->frame_num = frame_num; - if( !sps->frame_mbs_only_flag ) - { - slice->field_pic_flag = lsmash_bits_get( bits, 1 ); - if( slice->field_pic_flag ) - slice->bottom_field_flag = lsmash_bits_get( bits, 1 ); - } - if( slice->IdrPicFlag ) - { - uint64_t idr_pic_id = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( idr_pic_id > 65535 ) - return -1; - slice->idr_pic_id = idr_pic_id; - } - if( sps->pic_order_cnt_type == 0 ) - { - uint64_t pic_order_cnt_lsb = lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); - IF_INVALID_VALUE( pic_order_cnt_lsb >= sps->MaxPicOrderCntLsb ) - return -1; - slice->pic_order_cnt_lsb = pic_order_cnt_lsb; - if( pps->bottom_field_pic_order_in_frame_present_flag && !slice->field_pic_flag ) - slice->delta_pic_order_cnt_bottom = nalu_get_exp_golomb_se( bits ); - } - else if( sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag ) - { - slice->delta_pic_order_cnt[0] = nalu_get_exp_golomb_se( bits ); - if( pps->bottom_field_pic_order_in_frame_present_flag && !slice->field_pic_flag ) - slice->delta_pic_order_cnt[1] = nalu_get_exp_golomb_se( bits ); - } - if( pps->redundant_pic_cnt_present_flag ) - { - uint64_t redundant_pic_cnt = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( redundant_pic_cnt > 127 ) - return -1; - slice->has_redundancy = !!redundant_pic_cnt; - } - if( slice_type == H264_SLICE_TYPE_B ) - lsmash_bits_get( bits, 1 ); - uint64_t num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_default_active_minus1; - uint64_t num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_default_active_minus1; - if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_B ) - { - if( lsmash_bits_get( bits, 1 ) ) /* num_ref_idx_active_override_flag */ - { - num_ref_idx_l0_active_minus1 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( num_ref_idx_l0_active_minus1 > 31 ) - return -1; - if( slice_type == H264_SLICE_TYPE_B ) - { - num_ref_idx_l1_active_minus1 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( num_ref_idx_l1_active_minus1 > 31 ) - return -1; - } - } - } - if( nalu_header->nal_unit_type == H264_NALU_TYPE_SLICE_EXT - || nalu_header->nal_unit_type == H264_NALU_TYPE_SLICE_EXT_DVC ) - { - return -1; /* No support of MVC yet */ -#if 0 - /* ref_pic_list_mvc_modification() */ - if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_B || slice_type == H264_SLICE_TYPE_SP ) - { - for( int i = 0; i < 1 + (slice_type == H264_SLICE_TYPE_B); i++ ) - { - if( lsmash_bits_get( bits, 1 ) ) /* (S)P and B: ref_pic_list_modification_flag_l0 - * B: ref_pic_list_modification_flag_l1 */ - { - uint64_t modification_of_pic_nums_idc; - do - { - modification_of_pic_nums_idc = nalu_get_exp_golomb_ue( bits ); -#if 0 - if( modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1 ) - nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1 */ - else if( modification_of_pic_nums_idc == 2 ) - nalu_get_exp_golomb_ue( bits ); /* long_term_pic_num */ - else if( modification_of_pic_nums_idc == 4 || modification_of_pic_nums_idc == 5 ) - nalu_get_exp_golomb_ue( bits ); /* abs_diff_view_idx_minus1 */ -#else - if( modification_of_pic_nums_idc != 3 ) - nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1, long_term_pic_num or abs_diff_view_idx_minus1 */ -#endif - } while( modification_of_pic_nums_idc != 3 ); - } - } - } -#endif - } - else - { - /* ref_pic_list_modification() */ - if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_B || slice_type == H264_SLICE_TYPE_SP ) - { - for( int i = 0; i < 1 + (slice_type == H264_SLICE_TYPE_B); i++ ) - { - if( lsmash_bits_get( bits, 1 ) ) /* (S)P and B: ref_pic_list_modification_flag_l0 - * B: ref_pic_list_modification_flag_l1 */ - { - uint64_t modification_of_pic_nums_idc; - do - { - modification_of_pic_nums_idc = nalu_get_exp_golomb_ue( bits ); -#if 0 - if( modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1 ) - nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1 */ - else if( modification_of_pic_nums_idc == 2 ) - nalu_get_exp_golomb_ue( bits ); /* long_term_pic_num */ -#else - if( modification_of_pic_nums_idc != 3 ) - nalu_get_exp_golomb_ue( bits ); /* abs_diff_pic_num_minus1 or long_term_pic_num */ -#endif - } while( modification_of_pic_nums_idc != 3 ); - } - } - } - } - if( (pps->weighted_pred_flag && (slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_SP)) - || (pps->weighted_bipred_idc == 1 && slice_type == H264_SLICE_TYPE_B) ) - { - /* pred_weight_table() */ - nalu_get_exp_golomb_ue( bits ); /* luma_log2_weight_denom */ - if( sps->ChromaArrayType ) - nalu_get_exp_golomb_ue( bits ); /* chroma_log2_weight_denom */ - for( uint8_t i = 0; i <= num_ref_idx_l0_active_minus1; i++ ) - { - if( lsmash_bits_get( bits, 1 ) ) /* luma_weight_l0_flag */ - { - nalu_get_exp_golomb_se( bits ); /* luma_weight_l0[i] */ - nalu_get_exp_golomb_se( bits ); /* luma_offset_l0[i] */ - } - if( sps->ChromaArrayType - && lsmash_bits_get( bits, 1 ) /* chroma_weight_l0_flag */ ) - for( int j = 0; j < 2; j++ ) - { - nalu_get_exp_golomb_se( bits ); /* chroma_weight_l0[i][j]*/ - nalu_get_exp_golomb_se( bits ); /* chroma_offset_l0[i][j] */ - } - } - if( slice_type == H264_SLICE_TYPE_B ) - for( uint8_t i = 0; i <= num_ref_idx_l1_active_minus1; i++ ) - { - if( lsmash_bits_get( bits, 1 ) ) /* luma_weight_l1_flag */ - { - nalu_get_exp_golomb_se( bits ); /* luma_weight_l1[i] */ - nalu_get_exp_golomb_se( bits ); /* luma_offset_l1[i] */ - } - if( sps->ChromaArrayType - && lsmash_bits_get( bits, 1 ) /* chroma_weight_l1_flag */ ) - for( int j = 0; j < 2; j++ ) - { - nalu_get_exp_golomb_se( bits ); /* chroma_weight_l1[i][j]*/ - nalu_get_exp_golomb_se( bits ); /* chroma_offset_l1[i][j] */ - } - } - } - if( nalu_header->nal_ref_idc ) - { - /* dec_ref_pic_marking() */ - if( slice->IdrPicFlag ) - { - lsmash_bits_get( bits, 1 ); /* no_output_of_prior_pics_flag */ - lsmash_bits_get( bits, 1 ); /* long_term_reference_flag */ - } - else if( lsmash_bits_get( bits, 1 ) ) /* adaptive_ref_pic_marking_mode_flag */ - { - uint64_t memory_management_control_operation; - do - { - memory_management_control_operation = nalu_get_exp_golomb_ue( bits ); - if( memory_management_control_operation ) - { - if( memory_management_control_operation == 5 ) - slice->has_mmco5 = 1; - else - { - nalu_get_exp_golomb_ue( bits ); - if( memory_management_control_operation == 3 ) - nalu_get_exp_golomb_ue( bits ); - } - } - } while( memory_management_control_operation ); - } - } - /* We needn't read more if not slice data partition A. - * Skip slice_data() and rbsp_slice_trailing_bits(). */ - if( nalu_header->nal_unit_type == H264_NALU_TYPE_SLICE_DP_A ) - { - if( pps->entropy_coding_mode_flag && slice_type != H264_SLICE_TYPE_I && slice_type != H264_SLICE_TYPE_SI ) - nalu_get_exp_golomb_ue( bits ); /* cabac_init_idc */ - nalu_get_exp_golomb_se( bits ); /* slice_qp_delta */ - if( slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_SI ) - { - if( slice_type == H264_SLICE_TYPE_SP ) - lsmash_bits_get( bits, 1 ); /* sp_for_switch_flag */ - nalu_get_exp_golomb_se( bits ); /* slice_qs_delta */ - } - if( pps->deblocking_filter_control_present_flag - && nalu_get_exp_golomb_ue( bits ) != 1 /* disable_deblocking_filter_idc */ ) - { - int64_t slice_alpha_c0_offset_div2 = nalu_get_exp_golomb_se( bits ); - IF_INVALID_VALUE( slice_alpha_c0_offset_div2 < -6 || slice_alpha_c0_offset_div2 > 6 ) - return -1; - int64_t slice_beta_offset_div2 = nalu_get_exp_golomb_se( bits ); - IF_INVALID_VALUE( slice_beta_offset_div2 < -6 || slice_beta_offset_div2 > 6 ) - return -1; - } - if( pps->num_slice_groups_minus1 - && (pps->slice_group_map_type == 3 || pps->slice_group_map_type == 4 || pps->slice_group_map_type == 5) ) - { - uint64_t temp = ((uint64_t)sps->PicSizeInMapUnits - 1) / pps->SliceGroupChangeRate + 1; - uint64_t slice_group_change_cycle = lsmash_bits_get( bits, lsmash_ceil_log2( temp + 1 ) ); - IF_INVALID_VALUE( slice_group_change_cycle > temp ) - return -1; - } - /* end of slice_header() */ - slice->slice_id = nalu_get_exp_golomb_ue( bits ); - h264_slice_info_t *slice_part = h264_get_slice_info( info->slice_list, slice->slice_id ); - if( !slice_part ) - return -1; - *slice_part = *slice; - } - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - info->sps = *sps; - info->pps = *pps; - return 0; -} - -int h264_parse_slice -( - h264_info_t *info, - h264_nalu_header_t *nalu_header, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - uint64_t size = nalu_header->nal_unit_type == H264_NALU_TYPE_SLICE_IDR || nalu_header->nal_ref_idc == 0 - ? LSMASH_MIN( ebsp_size, 100 ) - : LSMASH_MIN( ebsp_size, 1000 ); - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, size ) ) - return -1; - if( nalu_header->nal_unit_type != H264_NALU_TYPE_SLICE_DP_B - && nalu_header->nal_unit_type != H264_NALU_TYPE_SLICE_DP_C ) - return h264_parse_slice_header( info, nalu_header ); - /* slice_data_partition_b_layer_rbsp() or slice_data_partition_c_layer_rbsp() */ - uint64_t slice_id = nalu_get_exp_golomb_ue( bits ); - h264_slice_info_t *slice = h264_get_slice_info( info->slice_list, slice_id ); - if( !slice ) - return -1; - h264_pps_t *pps = h264_get_pps( info->pps_list, slice->pic_parameter_set_id ); - if( !pps ) - return -1; - h264_sps_t *sps = h264_get_sps( info->sps_list, pps->seq_parameter_set_id ); - if( !sps ) - return -1; - slice->seq_parameter_set_id = pps->seq_parameter_set_id; - if( sps->separate_colour_plane_flag ) - lsmash_bits_get( bits, 2 ); /* colour_plane_id */ - if( pps->redundant_pic_cnt_present_flag ) - { - uint64_t redundant_pic_cnt = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( redundant_pic_cnt > 127 ) - return -1; - slice->has_redundancy = !!redundant_pic_cnt; - } - /* Skip slice_data() and rbsp_slice_trailing_bits(). */ - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - info->sps = *sps; - info->pps = *pps; - return 0; -} - -static int h264_get_sps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id -) -{ - /* max number of bits of sps_id = 11: 0b000001XXXXX - * (24 + 11 - 1) / 8 + 1 = 5 bytes - * Why +1? Because there might be an emulation_prevention_three_byte. */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[6]; - uint8_t buffer [6]; - bs.buffer.data = buffer; - bs.buffer.alloc = 6; - lsmash_bits_init( &bits, &bs ); - if( nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 6 ) ) ) - return -1; - lsmash_bits_get( &bits, 24 ); /* profile_idc, constraint_set_flags and level_idc */ - uint64_t sec_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); - IF_INVALID_VALUE( sec_parameter_set_id > 31 ) - return -1; - *ps_id = sec_parameter_set_id; - return bs.error ? -1 : 0; -} - -static int h264_get_pps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id -) -{ - /* max number of bits of pps_id = 17: 0b000000001XXXXXXXX - * (17 - 1) / 8 + 1 = 3 bytes - * Why +1? Because there might be an emulation_prevention_three_byte. */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[4]; - uint8_t buffer [4]; - bs.buffer.data = buffer; - bs.buffer.alloc = 4; - lsmash_bits_init( &bits, &bs ); - if( nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 4 ) ) ) - return -1; - uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); - IF_INVALID_VALUE( pic_parameter_set_id > 255 ) - return -1; - *ps_id = pic_parameter_set_id; - return bs.error ? -1 : 0; -} - -static inline int h264_get_ps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id, - lsmash_h264_parameter_set_type ps_type -) -{ - int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) - = ps_type == H264_PARAMETER_SET_TYPE_SPS ? h264_get_sps_id - : ps_type == H264_PARAMETER_SET_TYPE_PPS ? h264_get_pps_id - : NULL; - return get_ps_id ? get_ps_id( ps_ebsp, ps_ebsp_length, ps_id ) : -1; -} - -static inline lsmash_entry_list_t *h264_get_parameter_set_list -( - lsmash_h264_specific_parameters_t *param, - lsmash_h264_parameter_set_type ps_type -) -{ - if( !param->parameter_sets ) - return NULL; - return ps_type == H264_PARAMETER_SET_TYPE_SPS ? param->parameter_sets->sps_list - : ps_type == H264_PARAMETER_SET_TYPE_PPS ? param->parameter_sets->pps_list - : ps_type == H264_PARAMETER_SET_TYPE_SPSEXT ? param->parameter_sets->spsext_list - : NULL; -} - -static lsmash_entry_t *h264_get_ps_entry_from_param -( - lsmash_h264_specific_parameters_t *param, - lsmash_h264_parameter_set_type ps_type, - uint8_t ps_id -) -{ - int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) - = ps_type == H264_PARAMETER_SET_TYPE_SPS ? h264_get_sps_id - : ps_type == H264_PARAMETER_SET_TYPE_PPS ? h264_get_pps_id - : NULL; - if( !get_ps_id ) - return NULL; - lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( param, ps_type ); - if( !ps_list ) - return NULL; - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return NULL; - uint8_t param_ps_id; - if( get_ps_id( ps->nalUnit + 1, ps->nalUnitLength - 1, ¶m_ps_id ) ) - return NULL; - if( ps_id == param_ps_id ) - return entry; - } - return NULL; -} - -static inline void h264_update_picture_type -( - h264_picture_info_t *picture, - h264_slice_info_t *slice -) -{ - if( picture->type == H264_PICTURE_TYPE_I_P ) - { - if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_P_B; - else if( slice->type == H264_SLICE_TYPE_SI || slice->type == H264_SLICE_TYPE_SP ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP; - } - else if( picture->type == H264_PICTURE_TYPE_I_P_B ) - { - if( slice->type != H264_SLICE_TYPE_P && slice->type != H264_SLICE_TYPE_B && slice->type != H264_SLICE_TYPE_I ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; - } - else if( picture->type == H264_PICTURE_TYPE_I ) - { - if( slice->type == H264_SLICE_TYPE_P ) - picture->type = H264_PICTURE_TYPE_I_P; - else if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_P_B; - else if( slice->type == H264_SLICE_TYPE_SI ) - picture->type = H264_PICTURE_TYPE_I_SI; - else if( slice->type == H264_SLICE_TYPE_SP ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP; - } - else if( picture->type == H264_PICTURE_TYPE_SI_SP ) - { - if( slice->type == H264_SLICE_TYPE_P || slice->type == H264_SLICE_TYPE_I ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP; - else if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; - } - else if( picture->type == H264_PICTURE_TYPE_SI ) - { - if( slice->type == H264_SLICE_TYPE_P ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP; - else if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; - else if( slice->type != H264_SLICE_TYPE_I ) - picture->type = H264_PICTURE_TYPE_I_SI; - else if( slice->type == H264_SLICE_TYPE_SP ) - picture->type = H264_PICTURE_TYPE_SI_SP; - } - else if( picture->type == H264_PICTURE_TYPE_I_SI ) - { - if( slice->type == H264_SLICE_TYPE_P || slice->type == H264_SLICE_TYPE_SP ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP; - else if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; - } - else if( picture->type == H264_PICTURE_TYPE_I_SI_P_SP ) - { - if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B; - } - else if( picture->type == H264_PICTURE_TYPE_NONE ) - { - if( slice->type == H264_SLICE_TYPE_P ) - picture->type = H264_PICTURE_TYPE_I_P; - else if( slice->type == H264_SLICE_TYPE_B ) - picture->type = H264_PICTURE_TYPE_I_P_B; - else if( slice->type == H264_SLICE_TYPE_I ) - picture->type = H264_PICTURE_TYPE_I; - else if( slice->type == H264_SLICE_TYPE_SI ) - picture->type = H264_PICTURE_TYPE_SI; - else if( slice->type == H264_SLICE_TYPE_SP ) - picture->type = H264_PICTURE_TYPE_SI_SP; - } -#if 0 - fprintf( stderr, "Picture type = %s\n", picture->type == H264_PICTURE_TYPE_I_P ? "P" - : picture->type == H264_PICTURE_TYPE_I_P_B ? "B" - : picture->type == H264_PICTURE_TYPE_I ? "I" - : picture->type == H264_PICTURE_TYPE_SI ? "SI" - : picture->type == H264_PICTURE_TYPE_I_SI ? "SI" - : "SP" ); -#endif -} - -/* Shall be called at least once per picture. */ -void h264_update_picture_info_for_slice -( - h264_info_t *info, - h264_picture_info_t *picture, - h264_slice_info_t *slice -) -{ - assert( info ); - picture->has_mmco5 |= slice->has_mmco5; - picture->has_redundancy |= slice->has_redundancy; - picture->incomplete_au_has_primary |= !slice->has_redundancy; - h264_update_picture_type( picture, slice ); - /* Mark 'used' on active parameter sets. */ - uint8_t ps_id[2] = { slice->seq_parameter_set_id, slice->pic_parameter_set_id }; - for( int i = 0; i < 2; i++ ) - { - lsmash_h264_parameter_set_type ps_type = (lsmash_h264_parameter_set_type)i; - lsmash_entry_t *entry = h264_get_ps_entry_from_param( &info->avcC_param, ps_type, ps_id[i] ); - if( entry && entry->data ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( ps->unused ) - lsmash_append_h264_parameter_set( &info->avcC_param, ps_type, ps->nalUnit, ps->nalUnitLength ); - } - } - /* Discard this slice info. */ - slice->present = 0; -} - -/* Shall be called exactly once per picture. */ -void h264_update_picture_info -( - h264_info_t *info, - h264_picture_info_t *picture, - h264_slice_info_t *slice, - h264_sei_t *sei -) -{ - picture->frame_num = slice->frame_num; - picture->pic_order_cnt_lsb = slice->pic_order_cnt_lsb; - picture->delta_pic_order_cnt_bottom = slice->delta_pic_order_cnt_bottom; - picture->delta_pic_order_cnt[0] = slice->delta_pic_order_cnt[0]; - picture->delta_pic_order_cnt[1] = slice->delta_pic_order_cnt[1]; - picture->field_pic_flag = slice->field_pic_flag; - picture->bottom_field_flag = slice->bottom_field_flag; - picture->idr = slice->IdrPicFlag; - picture->pic_parameter_set_id = slice->pic_parameter_set_id; - picture->disposable = (slice->nal_ref_idc == 0); - picture->random_accessible = slice->IdrPicFlag; - h264_update_picture_info_for_slice( info, picture, slice ); - picture->independent = picture->type == H264_PICTURE_TYPE_I || picture->type == H264_PICTURE_TYPE_I_SI; - if( sei->pic_timing.present ) - { - if( sei->pic_timing.pic_struct < 9 ) - { - static const uint8_t DeltaTfiDivisor[9] = { 2, 1, 1, 2, 2, 3, 3, 4, 6 }; - picture->delta = DeltaTfiDivisor[ sei->pic_timing.pic_struct ]; - } - else - /* Reserved values in the spec we refer to. */ - picture->delta = picture->field_pic_flag ? 1 : 2; - sei->pic_timing.present = 0; - } - else - picture->delta = picture->field_pic_flag ? 1 : 2; - if( sei->recovery_point.present ) - { - picture->random_accessible |= sei->recovery_point.random_accessible; - picture->broken_link_flag |= sei->recovery_point.broken_link_flag; - picture->recovery_frame_cnt = sei->recovery_point.recovery_frame_cnt; - sei->recovery_point.present = 0; - } -} - -int h264_find_au_delimit_by_slice_info -( - h264_slice_info_t *slice, - h264_slice_info_t *prev_slice -) -{ - if( slice->frame_num != prev_slice->frame_num - || ((slice->pic_order_cnt_type == 0 && prev_slice->pic_order_cnt_type == 0) - && (slice->pic_order_cnt_lsb != prev_slice->pic_order_cnt_lsb - || slice->delta_pic_order_cnt_bottom != prev_slice->delta_pic_order_cnt_bottom)) - || ((slice->pic_order_cnt_type == 1 && prev_slice->pic_order_cnt_type == 1) - && (slice->delta_pic_order_cnt[0] != prev_slice->delta_pic_order_cnt[0] - || slice->delta_pic_order_cnt[1] != prev_slice->delta_pic_order_cnt[1])) - || slice->field_pic_flag != prev_slice->field_pic_flag - || slice->bottom_field_flag != prev_slice->bottom_field_flag - || slice->IdrPicFlag != prev_slice->IdrPicFlag - || slice->pic_parameter_set_id != prev_slice->pic_parameter_set_id - || ((slice->nal_ref_idc == 0 || prev_slice->nal_ref_idc == 0) - && (slice->nal_ref_idc != prev_slice->nal_ref_idc)) - || (slice->IdrPicFlag == 1 && prev_slice->IdrPicFlag == 1 - && slice->idr_pic_id != prev_slice->idr_pic_id) ) - return 1; - return 0; -} - -int h264_find_au_delimit_by_nalu_type -( - uint8_t nalu_type, - uint8_t prev_nalu_type -) -{ - return ((nalu_type >= H264_NALU_TYPE_SEI && nalu_type <= H264_NALU_TYPE_AUD) - || (nalu_type >= H264_NALU_TYPE_PREFIX && nalu_type <= H264_NALU_TYPE_RSV_NVCL18)) - && ((prev_nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && prev_nalu_type <= H264_NALU_TYPE_SLICE_IDR) - || prev_nalu_type == H264_NALU_TYPE_FD || prev_nalu_type == H264_NALU_TYPE_SLICE_AUX); -} - -int h264_supplement_buffer -( - h264_stream_buffer_t *hb, - h264_picture_info_t *picture, - uint32_t size -) -{ - lsmash_stream_buffers_t *sb = hb->sb; - uint32_t buffer_pos_offset = sb->pos - sb->start; - uint32_t buffer_valid_length = sb->end - sb->start; - lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, size ); - if( !bank ) - return -1; - sb->bank = bank; - sb->start = lsmash_withdraw_buffer( bank, 1 ); - hb->rbsp = lsmash_withdraw_buffer( bank, 2 ); - sb->pos = sb->start + buffer_pos_offset; - sb->end = sb->start + buffer_valid_length; - if( picture && bank->number_of_buffers == 4 ) - { - picture->au = lsmash_withdraw_buffer( bank, 3 ); - picture->incomplete_au = lsmash_withdraw_buffer( bank, 4 ); - } - return 0; -} - -static void h264_bs_put_parameter_sets -( - lsmash_bs_t *bs, - lsmash_entry_list_t *ps_list, - uint32_t max_ps_count -) -{ - uint32_t ps_count = 0; - for( lsmash_entry_t *entry = ps_list->head; entry && ps_count < max_ps_count; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( ps && !ps->unused ) - { - lsmash_bs_put_be16( bs, ps->nalUnitLength ); - lsmash_bs_put_bytes( bs, ps->nalUnitLength, ps->nalUnit ); - } - else - continue; - ++ps_count; - } -} - -uint8_t *lsmash_create_h264_specific_info -( - lsmash_h264_specific_parameters_t *param, - uint32_t *data_length -) -{ - if( !param || !param->parameter_sets || !data_length ) - return NULL; - if( param->lengthSizeMinusOne != 0 && param->lengthSizeMinusOne != 1 && param->lengthSizeMinusOne != 3 ) - return NULL; - static const uint32_t max_ps_count[3] = { 31, 255, 255 }; - lsmash_entry_list_t *ps_list[3] = - { - param->parameter_sets->sps_list, /* SPS */ - param->parameter_sets->pps_list, /* PPS */ - param->parameter_sets->spsext_list /* SPSExt */ - }; - uint32_t ps_count[3] = { 0, 0, 0 }; - /* SPS and PPS are mandatory. */ - if( !ps_list[0] || !ps_list[0]->head || ps_list[0]->entry_count == 0 - || !ps_list[1] || !ps_list[1]->head || ps_list[1]->entry_count == 0 ) - return NULL; - /* Calculate enough buffer size. */ - uint32_t buffer_size = ISOM_BASEBOX_COMMON_SIZE + 11; - for( int i = 0; i < 3; i++ ) - if( ps_list[i] ) - for( lsmash_entry_t *entry = ps_list[i]->head; entry && ps_count[i] < max_ps_count[i]; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return NULL; - if( ps->unused ) - continue; - buffer_size += 2 + ps->nalUnitLength; - ++ps_count[i]; - } - /* Set up bytestream writer. */ - uint8_t buffer[buffer_size]; - lsmash_bs_t bs = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = buffer_size; - /* Create an AVCConfigurationBox */ - lsmash_bs_put_be32( &bs, 0 ); /* box size */ - lsmash_bs_put_be32( &bs, ISOM_BOX_TYPE_AVCC.fourcc ); /* box type: 'avcC' */ - lsmash_bs_put_byte( &bs, 1 ); /* configurationVersion */ - lsmash_bs_put_byte( &bs, param->AVCProfileIndication ); /* AVCProfileIndication */ - lsmash_bs_put_byte( &bs, param->profile_compatibility ); /* profile_compatibility */ - lsmash_bs_put_byte( &bs, param->AVCLevelIndication ); /* AVCLevelIndication */ - lsmash_bs_put_byte( &bs, param->lengthSizeMinusOne | 0xfc ); /* lengthSizeMinusOne */ - lsmash_bs_put_byte( &bs, ps_count[0] | 0xe0 ); /* numOfSequenceParameterSets */ - h264_bs_put_parameter_sets( &bs, ps_list[0], ps_count[0] ); /* sequenceParameterSetLength - * sequenceParameterSetNALUnit */ - lsmash_bs_put_byte( &bs, ps_count[1] ); /* numOfPictureParameterSets */ - h264_bs_put_parameter_sets( &bs, ps_list[1], ps_count[1] ); /* pictureParameterSetLength - * pictureParameterSetNALUnit */ - if( H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) - { - lsmash_bs_put_byte( &bs, param->chroma_format | 0xfc ); /* chroma_format */ - lsmash_bs_put_byte( &bs, param->bit_depth_luma_minus8 | 0xf8 ); /* bit_depth_luma_minus8 */ - lsmash_bs_put_byte( &bs, param->bit_depth_chroma_minus8 | 0xf8 ); /* bit_depth_chroma_minus8 */ - if( ps_list[2] ) - { - lsmash_bs_put_byte( &bs, ps_count[2] ); /* numOfSequenceParameterSetExt */ - h264_bs_put_parameter_sets( &bs, ps_list[2], ps_count[2] ); /* sequenceParameterSetExtLength - * sequenceParameterSetExtNALUnit */ - } - else /* no sequence parameter set extensions */ - lsmash_bs_put_byte( &bs, 0 ); /* numOfSequenceParameterSetExt */ - } - uint8_t *data = lsmash_bs_export_data( &bs, data_length ); - /* Update box size. */ - data[0] = ((*data_length) >> 24) & 0xff; - data[1] = ((*data_length) >> 16) & 0xff; - data[2] = ((*data_length) >> 8) & 0xff; - data[3] = (*data_length) & 0xff; - return data; -} - -static inline int h264_validate_ps_type -( - lsmash_h264_parameter_set_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - if( !ps_data || ps_length < 2 ) - return -1; - if( ps_type != H264_PARAMETER_SET_TYPE_SPS - && ps_type != H264_PARAMETER_SET_TYPE_PPS - && ps_type != H264_PARAMETER_SET_TYPE_SPSEXT ) - return -1; - uint8_t nalu_type = *((uint8_t *)ps_data) & 0x1f; - if( nalu_type != H264_NALU_TYPE_SPS - && nalu_type != H264_NALU_TYPE_PPS - && nalu_type != H264_NALU_TYPE_SPS_EXT ) - return -1; - if( (ps_type == H264_PARAMETER_SET_TYPE_SPS && nalu_type != H264_NALU_TYPE_SPS) - || (ps_type == H264_PARAMETER_SET_TYPE_PPS && nalu_type != H264_NALU_TYPE_PPS) - || (ps_type == H264_PARAMETER_SET_TYPE_SPSEXT && nalu_type != H264_NALU_TYPE_SPS_EXT) ) - return -1; - return 0; -} - -lsmash_dcr_nalu_appendable lsmash_check_h264_parameter_set_appendable -( - lsmash_h264_specific_parameters_t *param, - lsmash_h264_parameter_set_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - if( !param ) - return DCR_NALU_APPEND_ERROR; - if( h264_validate_ps_type( ps_type, ps_data, ps_length ) ) - return DCR_NALU_APPEND_ERROR; - if( ps_type == H264_PARAMETER_SET_TYPE_SPSEXT - && !H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) - return DCR_NALU_APPEND_ERROR; - /* Check whether the same parameter set already exsits or not. */ - lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( param, ps_type ); - if( !ps_list || !ps_list->head ) - return DCR_NALU_APPEND_POSSIBLE; /* No parameter set */ - switch( nalu_check_same_ps_existence( ps_list, ps_data, ps_length ) ) - { - case 0 : break; - case 1 : return DCR_NALU_APPEND_DUPLICATED; /* The same parameter set already exists. */ - default : return DCR_NALU_APPEND_ERROR; /* An error occured. */ - } - uint32_t max_ps_length; - if( nalu_get_max_ps_length( ps_list, &max_ps_length ) ) - return DCR_NALU_APPEND_ERROR; - max_ps_length = LSMASH_MAX( max_ps_length, ps_length ); - uint32_t ps_count; - if( nalu_get_ps_count( ps_list, &ps_count ) ) - return DCR_NALU_APPEND_ERROR; - if( (ps_type == H264_PARAMETER_SET_TYPE_SPS && ps_count >= 31) - || (ps_type == H264_PARAMETER_SET_TYPE_PPS && ps_count >= 255) - || (ps_type == H264_PARAMETER_SET_TYPE_SPSEXT && ps_count >= 255) ) - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; /* No more appendable parameter sets. */ - if( ps_type == H264_PARAMETER_SET_TYPE_SPSEXT ) - return DCR_NALU_APPEND_POSSIBLE; - /* Check whether a new specific info is needed or not. */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[max_ps_length]; - uint8_t buffer [max_ps_length]; - bs.buffer.data = buffer; - bs.buffer.alloc = max_ps_length; - lsmash_bits_init( &bits, &bs ); - if( ps_type == H264_PARAMETER_SET_TYPE_PPS ) - { - /* PPS */ - uint8_t pps_id; - if( h264_get_pps_id( ps_data + 1, ps_length - 1, &pps_id ) ) - return DCR_NALU_APPEND_ERROR; - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return DCR_NALU_APPEND_ERROR; - if( ps->unused ) - continue; - uint8_t param_pps_id; - if( h264_get_pps_id( ps->nalUnit + 1, ps->nalUnitLength - 1, ¶m_pps_id ) ) - return DCR_NALU_APPEND_ERROR; - if( pps_id == param_pps_id ) - /* PPS that has the same pic_parameter_set_id already exists with different form. */ - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - } - return DCR_NALU_APPEND_POSSIBLE; - } - /* SPS */ - h264_sps_t sps; - if( h264_parse_sps_minimally( &bits, &sps, rbsp_buffer, ps_data + 1, ps_length - 1 ) ) - return DCR_NALU_APPEND_ERROR; - lsmash_bits_empty( &bits ); - /* FIXME; If the sequence parameter sets are marked with different profiles, - * and the relevant profile compatibility flags are all zero, - * then the stream may need examination to determine which profile, if any, the stream conforms to. - * If the stream is not examined, or the examination reveals that there is no profile to which the stream conforms, - * then the stream must be split into two or more sub-streams with separate configuration records in which these rules can be met. */ -#if 0 - if( sps.profile_idc != param->AVCProfileIndication && (sps->constraint_set_flags & param->profile_compatibility) ) -#else - if( sps.profile_idc != param->AVCProfileIndication ) -#endif - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - /* The values of chroma_format_idc, bit_depth_luma_minus8 and bit_depth_chroma_minus8 - * must be identical in all SPSs in a single AVC configuration record. */ - if( H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) - && (sps.chroma_format_idc != param->chroma_format - || sps.bit_depth_luma_minus8 != param->bit_depth_luma_minus8 - || sps.bit_depth_chroma_minus8 != param->bit_depth_chroma_minus8) ) - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - /* Forbidden to duplicate SPS that has the same seq_parameter_set_id with different form within the same configuration record. */ - uint8_t sps_id = sps.seq_parameter_set_id; - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return DCR_NALU_APPEND_ERROR; - if( ps->unused ) - continue; - uint8_t param_sps_id; - if( h264_get_sps_id( ps->nalUnit + 1, ps->nalUnitLength - 1, ¶m_sps_id ) ) - return DCR_NALU_APPEND_ERROR; - if( sps_id == param_sps_id ) - /* SPS that has the same seq_parameter_set_id already exists with different form. */ - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - if( entry == ps_list->head ) - { - /* Check if the visual presentation sizes are different. */ - h264_sps_t first_sps; - if( h264_parse_sps_minimally( &bits, &first_sps, rbsp_buffer, - ps->nalUnit + 1, - ps->nalUnitLength - 1 ) ) - return DCR_NALU_APPEND_ERROR; - if( sps.cropped_width != first_sps.cropped_width - || sps.cropped_height != first_sps.cropped_height ) - return DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED; - } - } - return DCR_NALU_APPEND_POSSIBLE; -} - -static inline void h264_reorder_parameter_set_ascending_id -( - lsmash_h264_specific_parameters_t *param, - lsmash_h264_parameter_set_type ps_type, - lsmash_entry_list_t *ps_list, - uint8_t ps_id -) -{ - lsmash_entry_t *entry = NULL; - if( ps_id ) - for( int i = ps_id - 1; i; i-- ) - { - entry = h264_get_ps_entry_from_param( param, ps_type, i ); - if( entry ) - break; - } - int append_head = 0; - if( !entry ) - { - /* Couldn't find any parameter set with lower identifier. - * Next, find parameter set with upper identifier. */ - int max_ps_id = ps_type == H264_PARAMETER_SET_TYPE_SPS ? 31 : 255; - for( int i = ps_id + 1; i <= max_ps_id; i++ ) - { - entry = h264_get_ps_entry_from_param( param, ps_type, i ); - if( entry ) - break; - } - if( entry ) - append_head = 1; - } - if( !entry ) - return; /* The new entry was appended to the tail. */ - lsmash_entry_t *new_entry = ps_list->tail; - if( append_head ) - { - /* before: entry[i > ps_id] ... -> prev_entry -> new_entry[ps_id] - * after: new_entry[ps_id] -> entry[i > ps_id] -> ... -> prev_entry */ - if( new_entry->prev ) - new_entry->prev->next = NULL; - new_entry->prev = NULL; - entry->prev = new_entry; - new_entry->next = entry; - return; - } - /* before: entry[i < ps_id] -> next_entry -> ... -> prev_entry -> new_entry[ps_id] - * after: entry[i < ps_id] -> new_entry[ps_id] -> next_entry -> ... -> prev_entry */ - if( new_entry->prev ) - new_entry->prev->next = NULL; - new_entry->prev = entry; - new_entry->next = entry->next; - if( entry->next ) - entry->next->prev = new_entry; - entry->next = new_entry; -} - -int lsmash_append_h264_parameter_set -( - lsmash_h264_specific_parameters_t *param, - lsmash_h264_parameter_set_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - if( !param || !ps_data || ps_length < 2 ) - return -1; - if( !param->parameter_sets ) - { - param->parameter_sets = lsmash_malloc_zero( sizeof(lsmash_h264_parameter_sets_t) ); - if( !param->parameter_sets ) - return -1; - } - lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( param, ps_type ); - if( !ps_list ) - return -1; - if( ps_type != H264_PARAMETER_SET_TYPE_SPS - && ps_type != H264_PARAMETER_SET_TYPE_PPS - && ps_type != H264_PARAMETER_SET_TYPE_SPSEXT ) - return -1; - if( ps_type == H264_PARAMETER_SET_TYPE_SPSEXT ) - { - if( !H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) - return 0; - isom_dcr_ps_entry_t *ps = isom_create_ps_entry( ps_data, ps_length ); - if( !ps ) - return -1; - if( lsmash_add_entry( ps_list, ps ) ) - { - isom_remove_dcr_ps( ps ); - return -1; - } - return 0; - } - /* Check if the same parameter set identifier already exists. */ - uint8_t ps_id; - if( h264_get_ps_id( ps_data + 1, ps_length - 1, &ps_id, ps_type ) ) - return -1; - lsmash_entry_t *entry = h264_get_ps_entry_from_param( param, ps_type, ps_id ); - isom_dcr_ps_entry_t *ps = entry ? (isom_dcr_ps_entry_t *)entry->data : NULL; - if( ps && !ps->unused ) - /* The same parameter set identifier already exists. */ - return -1; - int invoke_reorder; - if( ps ) - { - /* Reuse an already existed parameter set in the list. */ - ps->unused = 0; - if( ps->nalUnit != ps_data ) - { - /* The same address could be given when called by h264_update_picture_info_for_slice(). */ - lsmash_free( ps->nalUnit ); - ps->nalUnit = ps_data; - } - ps->nalUnitLength = ps_length; - invoke_reorder = 0; - } - else - { - /* Create a new parameter set and append it into the list. */ - ps = isom_create_ps_entry( ps_data, ps_length ); - if( !ps ) - return -1; - if( lsmash_add_entry( ps_list, ps ) ) - { - isom_remove_dcr_ps( ps ); - return -1; - } - invoke_reorder = 1; - } - if( ps_type == H264_PARAMETER_SET_TYPE_SPS ) - { - /* Update specific info with SPS. */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[ps_length]; - uint8_t buffer [ps_length]; - bs.buffer.data = buffer; - bs.buffer.alloc = ps_length; - lsmash_bits_init( &bits, &bs ); - h264_sps_t sps; - if( h264_parse_sps_minimally( &bits, &sps, rbsp_buffer, ps_data + 1, ps_length - 1 ) ) - { - lsmash_remove_entry_tail( ps_list, isom_remove_dcr_ps ); - return -1; - } - if( ps_list->entry_count == 1 ) - param->profile_compatibility = 0xff; - param->AVCProfileIndication = sps.profile_idc; - param->profile_compatibility &= sps.constraint_set_flags; - param->AVCLevelIndication = LSMASH_MAX( param->AVCLevelIndication, sps.level_idc ); - param->chroma_format = sps.chroma_format_idc; - param->bit_depth_luma_minus8 = sps.bit_depth_luma_minus8; - param->bit_depth_chroma_minus8 = sps.bit_depth_chroma_minus8; - } - if( invoke_reorder ) - /* Add a new parameter set in order of ascending parameter set identifier. */ - h264_reorder_parameter_set_ascending_id( param, ps_type, ps_list, ps_id ); - return 0; -} - -int h264_try_to_append_parameter_set -( - h264_info_t *info, - lsmash_h264_parameter_set_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - lsmash_dcr_nalu_appendable ret = lsmash_check_h264_parameter_set_appendable( &info->avcC_param, ps_type, ps_data, ps_length ); - lsmash_h264_specific_parameters_t *param; - switch( ret ) - { - case DCR_NALU_APPEND_ERROR : /* Error */ - return -1; - case DCR_NALU_APPEND_NEW_DCR_REQUIRED : /* Mulitiple sample description is needed. */ - case DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED : /* Mulitiple sample description is needed. */ - param = &info->avcC_param_next; - info->avcC_pending = 1; - break; - case DCR_NALU_APPEND_POSSIBLE : /* Appendable */ - param = info->avcC_pending ? &info->avcC_param_next : &info->avcC_param; - break; - default : /* No need to append */ - return 0; - } - switch( ps_type ) - { - case H264_PARAMETER_SET_TYPE_SPS : - if( h264_parse_sps( info, info->buffer.rbsp, ps_data + 1, ps_length - 1 ) < 0 ) - return -1; - break; - case H264_PARAMETER_SET_TYPE_PPS : - if( h264_parse_pps( info, info->buffer.rbsp, ps_data + 1, ps_length - 1 ) < 0 ) - return -1; - break; - default : - break; - } - return lsmash_append_h264_parameter_set( param, ps_type, ps_data, ps_length ); -} - -static inline int h264_move_dcr_nalu_entry -( - lsmash_h264_specific_parameters_t *dst_data, - lsmash_h264_specific_parameters_t *src_data, - lsmash_h264_parameter_set_type ps_type -) -{ - lsmash_entry_list_t *src_ps_list = h264_get_parameter_set_list( src_data, ps_type ); - lsmash_entry_list_t *dst_ps_list = h264_get_parameter_set_list( dst_data, ps_type ); - assert( src_ps_list && dst_ps_list ); - for( lsmash_entry_t *src_entry = src_ps_list->head; src_entry; src_entry = src_entry->next ) - { - isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)src_entry->data; - if( !src_ps ) - continue; - uint8_t src_ps_id; - if( h264_get_ps_id( src_ps->nalUnit + 1, src_ps->nalUnitLength - 1, &src_ps_id, ps_type ) < 0 ) - return -1; - lsmash_entry_t *dst_entry; - for( dst_entry = dst_ps_list->head; dst_entry; dst_entry = dst_entry->next ) - { - isom_dcr_ps_entry_t *dst_ps = (isom_dcr_ps_entry_t *)dst_entry->data; - if( !dst_ps ) - continue; - uint8_t dst_ps_id; - if( h264_get_ps_id( dst_ps->nalUnit + 1, dst_ps->nalUnitLength - 1, &dst_ps_id, ps_type ) < 0 ) - return -1; - if( dst_ps_id == src_ps_id ) - { - /* Replace the old parameter set with the new one. */ - assert( dst_entry->data != src_entry->data ); - isom_remove_dcr_ps( dst_ps ); - dst_entry->data = src_entry->data; - src_entry->data = NULL; - break; - } - } - if( !dst_entry ) - { - /* Move the parameter set. */ - if( lsmash_add_entry( dst_ps_list, src_ps ) ) - return -1; - src_entry->data = NULL; - } - } - return 0; -} - -int h264_move_pending_avcC_param -( - h264_info_t *info -) -{ - assert( info ); - if( !info->avcC_pending ) - return 0; - /* Mark 'unused' on parameter sets within the decoder configuration record. */ - for( int i = 0; i < H264_PARAMETER_SET_TYPE_NUM; i++ ) - { - lsmash_entry_list_t *ps_list = h264_get_parameter_set_list( &info->avcC_param, i ); - assert( ps_list ); - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - continue; - ps->unused = 1; - } - } - /* Move the new parameter sets. */ - if( h264_move_dcr_nalu_entry( &info->avcC_param, &info->avcC_param_next, H264_PARAMETER_SET_TYPE_SPS ) < 0 - || h264_move_dcr_nalu_entry( &info->avcC_param, &info->avcC_param_next, H264_PARAMETER_SET_TYPE_PPS ) < 0 ) - return -1; - /* Move to the pending. */ - lsmash_h264_parameter_sets_t *parameter_sets = info->avcC_param.parameter_sets; /* Back up parameter sets. */ - info->avcC_param = info->avcC_param_next; - info->avcC_param.parameter_sets = parameter_sets; - /* No pending avcC. */ - lsmash_destroy_h264_parameter_sets( &info->avcC_param_next ); - memset( &info->avcC_param_next, 0, sizeof(lsmash_h264_specific_parameters_t) ); - info->avcC_pending = 0; - return 0; -} - -static int h264_parse_succeeded -( - h264_info_t *info, - lsmash_h264_specific_parameters_t *param -) -{ - int ret; - if( info->sps.present && info->pps.present ) - { - *param = info->avcC_param; - /* Avoid freeing parameter sets. */ - info->avcC_param.parameter_sets = NULL; - ret = 0; - } - else - ret = -1; - h264_cleanup_parser( info ); - return ret; -} - -static inline int h264_parse_failed -( - h264_info_t *info -) -{ - h264_cleanup_parser( info ); - return -1; -} - -int lsmash_setup_h264_specific_parameters_from_access_unit -( - lsmash_h264_specific_parameters_t *param, - uint8_t *data, - uint32_t data_length -) -{ - if( !param || !data || data_length == 0 ) - return -1; - h264_info_t handler = { { 0 } }; - h264_info_t *info = &handler; - lsmash_stream_buffers_t _sb = { LSMASH_STREAM_BUFFERS_TYPE_NONE }; - lsmash_stream_buffers_t *sb = &_sb; - lsmash_data_string_handler_t stream = { 0 }; - stream.data = data; - stream.data_length = data_length; - stream.remainder_length = data_length; - if( h264_setup_parser( info, sb, 1, LSMASH_STREAM_BUFFERS_TYPE_DATA_STRING, &stream ) ) - return h264_parse_failed( info ); - h264_stream_buffer_t *hb = &info->buffer; - h264_slice_info_t *slice = &info->slice; - h264_nalu_header_t nalu_header = { 0 }; - uint64_t consecutive_zero_byte_count = 0; - uint64_t ebsp_length = 0; - int no_more_buf = 0; - int complete_au = 0; - while( 1 ) - { - lsmash_stream_buffers_update( sb, 2 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - int no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !nalu_check_next_short_start_code( sb->pos, sb->end ) && !no_more ) - { - if( lsmash_stream_buffers_get_byte( sb ) ) - consecutive_zero_byte_count = 0; - else - ++consecutive_zero_byte_count; - ++ebsp_length; - continue; - } - if( no_more && ebsp_length == 0 ) - /* For the last NALU. This NALU already has been parsed. */ - return h264_parse_succeeded( info, param ); - uint64_t next_nalu_head_pos = info->ebsp_head_pos + ebsp_length + !no_more * H264_SHORT_START_CODE_LENGTH; - /* Memorize position of short start code of the next NALU in buffer. - * This is used when backward reading of stream doesn't occur. */ - uint8_t *next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - uint8_t nalu_type = nalu_header.nal_unit_type; - int read_back = 0; - if( nalu_type == H264_NALU_TYPE_FD ) - { - /* We don't support streams with both filler and HRD yet. - * Otherwise, just skip filler because elemental streams defined in 14496-15 are forbidden to use filler. */ - if( info->sps.vui.hrd.present ) - return h264_parse_failed( info ); - } - else if( (nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SPS_EXT) - || nalu_type == H264_NALU_TYPE_SLICE_AUX ) - { - /* Get the EBSP of the current NALU here. - * AVC elemental stream defined in 14496-15 can recognize from 0 to 13, and 19 of nal_unit_type. - * We don't support SVC and MVC elemental stream defined in 14496-15 yet. */ - ebsp_length -= consecutive_zero_byte_count; /* Any EBSP doesn't have zero bytes at the end. */ - uint64_t nalu_length = nalu_header.length + ebsp_length; - if( lsmash_stream_buffers_get_buffer_size( sb ) < (H264_DEFAULT_NALU_LENGTH_SIZE + nalu_length) ) - { - if( h264_supplement_buffer( hb, NULL, 2 * (H264_DEFAULT_NALU_LENGTH_SIZE + nalu_length) ) ) - return h264_parse_failed( info ); - next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - } - /* Move to the first byte of the current NALU. */ - read_back = (lsmash_stream_buffers_get_offset( sb ) < (nalu_length + consecutive_zero_byte_count)); - if( read_back ) - { - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_data_string_copy( sb, &stream, nalu_length, info->ebsp_head_pos - nalu_header.length ); - } - else - lsmash_stream_buffers_seek( sb, -(nalu_length + consecutive_zero_byte_count), SEEK_CUR ); - if( nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SLICE_IDR ) - { - /* VCL NALU (slice) */ - h264_slice_info_t prev_slice = *slice; - if( h264_parse_slice( info, &nalu_header, hb->rbsp, - lsmash_stream_buffers_get_pos( sb ) + nalu_header.length, ebsp_length ) ) - return h264_parse_failed( info ); - if( prev_slice.present ) - { - /* Check whether the AU that contains the previous VCL NALU completed or not. */ - if( h264_find_au_delimit_by_slice_info( slice, &prev_slice ) ) - /* The current NALU is the first VCL NALU of the primary coded picture of an new AU. - * Therefore, the previous slice belongs to that new AU. */ - complete_au = 1; - } - slice->present = 1; - } - else - { - if( h264_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) - { - /* The last slice belongs to the AU you want at this time. */ - slice->present = 0; - complete_au = 1; - } - else if( no_more ) - complete_au = 1; - switch( nalu_type ) - { - case H264_NALU_TYPE_SPS : - if( h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPS, - lsmash_stream_buffers_get_pos( sb ), nalu_length ) ) - return h264_parse_failed( info ); - break; - case H264_NALU_TYPE_PPS : - if( h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_PPS, - lsmash_stream_buffers_get_pos( sb ), nalu_length ) ) - return h264_parse_failed( info ); - break; - case H264_NALU_TYPE_SPS_EXT : - if( h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPSEXT, - lsmash_stream_buffers_get_pos( sb ), nalu_length ) ) - return h264_parse_failed( info ); - break; - default : - break; - } - } - } - /* Move to the first byte of the next NALU. */ - if( read_back ) - { - uint64_t consumed_data_length = LSMASH_MIN( stream.remainder_length, lsmash_stream_buffers_get_buffer_size( sb ) ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_data_string_copy( sb, &stream, consumed_data_length, next_nalu_head_pos ); - } - else - lsmash_stream_buffers_set_pos( sb, next_short_start_code_pos + H264_SHORT_START_CODE_LENGTH ); - info->prev_nalu_type = nalu_type; - lsmash_stream_buffers_update( sb, 0 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - ebsp_length = 0; - no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !no_more && !complete_au ) - { - /* Check the next NALU header. */ - if( h264_check_nalu_header( &nalu_header, sb, !!consecutive_zero_byte_count ) ) - return h264_parse_failed( info ); - info->ebsp_head_pos = next_nalu_head_pos + nalu_header.length; - } - else - return h264_parse_succeeded( info, param ); - consecutive_zero_byte_count = 0; - } -} - -int h264_construct_specific_parameters -( - lsmash_codec_specific_t *dst, - lsmash_codec_specific_t *src -) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) - return -1; - lsmash_h264_specific_parameters_t *param = (lsmash_h264_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - if( !param->parameter_sets ) - { - param->parameter_sets = lsmash_malloc_zero( sizeof(lsmash_h264_parameter_sets_t) ); - if( !param->parameter_sets ) - return -1; - } - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return -1; - if( lsmash_bs_import_data( bs, data, src->size - (data - src->data.unstructured) ) ) - goto fail; - if( lsmash_bs_get_byte( bs ) != 1 ) - goto fail; /* We don't support configurationVersion other than 1. */ - param->AVCProfileIndication = lsmash_bs_get_byte( bs ); - param->profile_compatibility = lsmash_bs_get_byte( bs ); - param->AVCLevelIndication = lsmash_bs_get_byte( bs ); - param->lengthSizeMinusOne = lsmash_bs_get_byte( bs ) & 0x03; - uint8_t numOfSequenceParameterSets = lsmash_bs_get_byte( bs ) & 0x1F; - if( numOfSequenceParameterSets - && nalu_get_dcr_ps( bs, param->parameter_sets->sps_list, numOfSequenceParameterSets ) ) - goto fail; - uint8_t numOfPictureParameterSets = lsmash_bs_get_byte( bs ); - if( numOfPictureParameterSets - && nalu_get_dcr_ps( bs, param->parameter_sets->pps_list, numOfPictureParameterSets ) ) - goto fail; - if( H264_REQUIRES_AVCC_EXTENSION( param->AVCProfileIndication ) ) - { - param->chroma_format = lsmash_bs_get_byte( bs ) & 0x03; - param->bit_depth_luma_minus8 = lsmash_bs_get_byte( bs ) & 0x07; - param->bit_depth_chroma_minus8 = lsmash_bs_get_byte( bs ) & 0x07; - uint8_t numOfSequenceParameterSetExt = lsmash_bs_get_byte( bs ); - if( numOfSequenceParameterSetExt - && nalu_get_dcr_ps( bs, param->parameter_sets->spsext_list, numOfSequenceParameterSetExt ) ) - goto fail; - } - lsmash_bs_cleanup( bs ); - return 0; -fail: - lsmash_bs_cleanup( bs ); - return -1; -} - -int h264_print_codec_specific -( - FILE *fp, - lsmash_file_t *file, - isom_box_t *box, - int level -) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: AVC Configuration Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - uint8_t *data = box->binary; - uint32_t offset = isom_skip_box_common( &data ); - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return -1; - if( lsmash_bs_import_data( bs, data, box->size - offset ) ) - { - lsmash_bs_cleanup( bs ); - return -1; - } - lsmash_ifprintf( fp, indent, "configurationVersion = %"PRIu8"\n", lsmash_bs_get_byte( bs ) ); - uint8_t AVCProfileIndication = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "AVCProfileIndication = %"PRIu8"\n", AVCProfileIndication ); - lsmash_ifprintf( fp, indent, "profile_compatibility = 0x%02"PRIx8"\n", lsmash_bs_get_byte( bs ) ); - lsmash_ifprintf( fp, indent, "AVCLevelIndication = %"PRIu8"\n", lsmash_bs_get_byte( bs ) ); - uint8_t temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); - lsmash_ifprintf( fp, indent, "lengthSizeMinusOne = %"PRIu8"\n", temp8 & 0x03 ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 5) & 0x07 ); - uint8_t numOfSequenceParameterSets = temp8 & 0x1f; - lsmash_ifprintf( fp, indent, "numOfSequenceParameterSets = %"PRIu8"\n", numOfSequenceParameterSets ); - for( uint8_t i = 0; i < numOfSequenceParameterSets; i++ ) - { - uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); - lsmash_bs_skip_bytes( bs, nalUnitLength ); - } - uint8_t numOfPictureParameterSets = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "numOfPictureParameterSets = %"PRIu8"\n", numOfPictureParameterSets ); - for( uint8_t i = 0; i < numOfPictureParameterSets; i++ ) - { - uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); - lsmash_bs_skip_bytes( bs, nalUnitLength ); - } - /* Note: there are too many files, in the world, that don't contain the following fields. */ - if( H264_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) - && (lsmash_bs_get_pos( bs ) < (box->size - offset)) ) - { - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); - lsmash_ifprintf( fp, indent, "chroma_format = %"PRIu8"\n", temp8 & 0x03 ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); - lsmash_ifprintf( fp, indent, "bit_depth_luma_minus8 = %"PRIu8"\n", temp8 & 0x7 ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); - lsmash_ifprintf( fp, indent, "bit_depth_chroma_minus8 = %"PRIu8"\n", temp8 & 0x7 ); - lsmash_ifprintf( fp, indent, "numOfSequenceParameterSetExt = %"PRIu8"\n", lsmash_bs_get_byte( bs ) ); - } - lsmash_bs_cleanup( bs ); - return 0; -} - -int h264_copy_codec_specific -( - lsmash_codec_specific_t *dst, - lsmash_codec_specific_t *src -) -{ - assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); - assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); - lsmash_h264_specific_parameters_t *src_data = (lsmash_h264_specific_parameters_t *)src->data.structured; - lsmash_h264_specific_parameters_t *dst_data = (lsmash_h264_specific_parameters_t *)dst->data.structured; - lsmash_destroy_h264_parameter_sets( dst_data ); - *dst_data = *src_data; - if( !src_data->parameter_sets ) - return 0; - dst_data->parameter_sets = lsmash_malloc_zero( sizeof(lsmash_h264_parameter_sets_t) ); - if( !dst_data->parameter_sets ) - return -1; - for( int i = 0; i < 3; i++ ) - { - lsmash_entry_list_t *src_ps_list = h264_get_parameter_set_list( src_data, i ); - lsmash_entry_list_t *dst_ps_list = h264_get_parameter_set_list( dst_data, i ); - assert( src_ps_list && dst_ps_list ); - for( lsmash_entry_t *entry = src_ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)entry->data; - if( !src_ps || src_ps->unused ) - continue; - isom_dcr_ps_entry_t *dst_ps = isom_create_ps_entry( src_ps->nalUnit, src_ps->nalUnitLength ); - if( !dst_ps ) - { - lsmash_destroy_h264_parameter_sets( dst_data ); - return -1; - } - if( lsmash_add_entry( dst_ps_list, dst_ps ) ) - { - lsmash_destroy_h264_parameter_sets( dst_data ); - isom_remove_dcr_ps( dst_ps ); - return -1; - } - } - } - return 0; -} - -int h264_print_bitrate -( - FILE *fp, - lsmash_file_t *file, - isom_box_t *box, - int level -) -{ - assert( fp && file && box ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: MPEG-4 Bit Rate Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - isom_btrt_t *btrt = (isom_btrt_t *)box; - lsmash_ifprintf( fp, indent, "bufferSizeDB = %"PRIu32"\n", btrt->bufferSizeDB ); - lsmash_ifprintf( fp, indent, "maxBitrate = %"PRIu32"\n", btrt->maxBitrate ); - lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32"\n", btrt->avgBitrate ); - return 0; -} diff -Nru l-smash-1.9.1/h264.h l-smash-2.3.0/h264.h --- l-smash-1.9.1/h264.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/h264.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,372 +0,0 @@ -/***************************************************************************** - * h264.h: - ***************************************************************************** - * Copyright (C) 2012-2014 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. */ - -#define H264_DEFAULT_BUFFER_SIZE (1<<16) -#define H264_DEFAULT_NALU_LENGTH_SIZE 4 /* We always use 4 bytes length. */ -#define H264_SHORT_START_CODE_LENGTH 3 - - -enum -{ - H264_NALU_TYPE_UNSPECIFIED0 = 0, /* Unspecified */ - H264_NALU_TYPE_SLICE_N_IDR = 1, /* Coded slice of a non-IDR picture */ - H264_NALU_TYPE_SLICE_DP_A = 2, /* Coded slice data partition A */ - H264_NALU_TYPE_SLICE_DP_B = 3, /* Coded slice data partition B */ - H264_NALU_TYPE_SLICE_DP_C = 4, /* Coded slice data partition C */ - H264_NALU_TYPE_SLICE_IDR = 5, /* Coded slice of an IDR picture */ - H264_NALU_TYPE_SEI = 6, /* Supplemental Enhancement Information */ - H264_NALU_TYPE_SPS = 7, /* Sequence Parameter Set */ - H264_NALU_TYPE_PPS = 8, /* Picture Parameter Set */ - H264_NALU_TYPE_AUD = 9, /* Access Unit Delimiter */ - H264_NALU_TYPE_EOS = 10, /* End of Sequence */ - H264_NALU_TYPE_EOB = 11, /* End of Bitstream */ - H264_NALU_TYPE_FD = 12, /* Filler Data */ - H264_NALU_TYPE_SPS_EXT = 13, /* Sequence Parameter Set Extension */ - H264_NALU_TYPE_PREFIX = 14, /* Prefix NAL unit */ - H264_NALU_TYPE_SUBSET_SPS = 15, /* Subset Sequence Parameter Set */ - H264_NALU_TYPE_RSV_NVCL16 = 16, /* Reserved */ - H264_NALU_TYPE_RSV_NVCL17 = 17, /* Reserved */ - H264_NALU_TYPE_RSV_NVCL18 = 18, /* Reserved */ - H264_NALU_TYPE_SLICE_AUX = 19, /* Coded slice of an auxiliary coded picture without partitioning */ - H264_NALU_TYPE_SLICE_EXT = 20, /* Coded slice extension */ - H264_NALU_TYPE_SLICE_EXT_DVC = 21, /* Coded slice extension for depth view components */ - H264_NALU_TYPE_RSV22 = 22, /* Reserved */ - H264_NALU_TYPE_RSV23 = 23, /* Reserved */ - H264_NALU_TYPE_UNSPECIFIED24 = 24, /* Unspecified */ - H264_NALU_TYPE_UNSPECIFIED31 = 31, /* Unspecified */ -}; - -struct lsmash_h264_parameter_sets_tag -{ - /* Each list contains entries as isom_dcr_ps_entry_t. */ - lsmash_entry_list_t sps_list [1]; - lsmash_entry_list_t pps_list [1]; - lsmash_entry_list_t spsext_list[1]; -}; - -typedef struct -{ - uint8_t nal_ref_idc; - uint8_t nal_unit_type; - uint16_t length; -} h264_nalu_header_t; - -typedef struct -{ - uint8_t present; - uint8_t CpbDpbDelaysPresentFlag; - uint8_t cpb_removal_delay_length; - uint8_t dpb_output_delay_length; -} h264_hrd_t; - -typedef struct -{ - uint16_t sar_width; - uint16_t sar_height; - uint8_t video_full_range_flag; - uint8_t colour_primaries; - uint8_t transfer_characteristics; - uint8_t matrix_coefficients; - uint32_t num_units_in_tick; - uint32_t time_scale; - uint8_t fixed_frame_rate_flag; - uint8_t pic_struct_present_flag; - h264_hrd_t hrd; -} h264_vui_t; - -typedef struct -{ - uint8_t present; - uint8_t profile_idc; - uint8_t constraint_set_flags; - uint8_t level_idc; - uint8_t seq_parameter_set_id; - uint8_t chroma_format_idc; - uint8_t separate_colour_plane_flag; - uint8_t ChromaArrayType; - uint8_t bit_depth_luma_minus8; - uint8_t bit_depth_chroma_minus8; - uint8_t pic_order_cnt_type; - uint8_t delta_pic_order_always_zero_flag; - uint8_t num_ref_frames_in_pic_order_cnt_cycle; - uint8_t frame_mbs_only_flag; - int32_t offset_for_non_ref_pic; - int32_t offset_for_top_to_bottom_field; - int32_t offset_for_ref_frame[255]; - int64_t ExpectedDeltaPerPicOrderCntCycle; - uint32_t max_num_ref_frames; - uint32_t log2_max_frame_num; - uint32_t MaxFrameNum; - uint32_t log2_max_pic_order_cnt_lsb; - uint32_t MaxPicOrderCntLsb; - uint32_t PicSizeInMapUnits; - uint32_t cropped_width; - uint32_t cropped_height; - h264_vui_t vui; -} h264_sps_t; - -typedef struct -{ - uint8_t present; - uint8_t pic_parameter_set_id; - uint8_t seq_parameter_set_id; - uint8_t entropy_coding_mode_flag; - uint8_t bottom_field_pic_order_in_frame_present_flag; - uint8_t num_slice_groups_minus1; - uint8_t slice_group_map_type; - uint8_t num_ref_idx_l0_default_active_minus1; - uint8_t num_ref_idx_l1_default_active_minus1; - uint8_t weighted_pred_flag; - uint8_t weighted_bipred_idc; - uint8_t deblocking_filter_control_present_flag; - uint8_t redundant_pic_cnt_present_flag; - uint32_t SliceGroupChangeRate; -} h264_pps_t; - -typedef struct -{ - uint8_t present; - uint8_t pic_struct; -} h264_pic_timing_t; - -typedef struct -{ - uint8_t present; - uint8_t random_accessible; - uint8_t broken_link_flag; - uint32_t recovery_frame_cnt; -} h264_recovery_point_t; - -typedef struct -{ - h264_pic_timing_t pic_timing; - h264_recovery_point_t recovery_point; -} h264_sei_t; - -typedef struct -{ - uint8_t present; - uint8_t slice_id; /* only for slice data partition */ - uint8_t type; - uint8_t pic_order_cnt_type; - uint8_t nal_ref_idc; - uint8_t IdrPicFlag; - uint8_t seq_parameter_set_id; - uint8_t pic_parameter_set_id; - uint8_t field_pic_flag; - uint8_t bottom_field_flag; - uint8_t has_mmco5; - uint8_t has_redundancy; - uint16_t idr_pic_id; - uint32_t frame_num; - int32_t pic_order_cnt_lsb; - int32_t delta_pic_order_cnt_bottom; - int32_t delta_pic_order_cnt[2]; -} h264_slice_info_t; - -typedef enum -{ - H264_PICTURE_TYPE_IDR = 0, - H264_PICTURE_TYPE_I = 1, - H264_PICTURE_TYPE_I_P = 2, - H264_PICTURE_TYPE_I_P_B = 3, - H264_PICTURE_TYPE_SI = 4, - H264_PICTURE_TYPE_SI_SP = 5, - H264_PICTURE_TYPE_I_SI = 6, - H264_PICTURE_TYPE_I_SI_P_SP = 7, - H264_PICTURE_TYPE_I_SI_P_SP_B = 8, - H264_PICTURE_TYPE_NONE = 9, -} h264_picture_type; - -typedef struct -{ - h264_picture_type type; - uint8_t idr; - uint8_t random_accessible; - uint8_t independent; - uint8_t disposable; /* 1: nal_ref_idc == 0, 0: otherwise */ - uint8_t has_redundancy; - uint8_t incomplete_au_has_primary; - uint8_t pic_parameter_set_id; - uint8_t field_pic_flag; - uint8_t bottom_field_flag; - uint8_t delta; - uint8_t broken_link_flag; - /* POC */ - uint8_t has_mmco5; - uint8_t ref_pic_has_mmco5; - uint8_t ref_pic_bottom_field_flag; - int32_t ref_pic_TopFieldOrderCnt; - int32_t ref_pic_PicOrderCntMsb; - int32_t ref_pic_PicOrderCntLsb; - int32_t pic_order_cnt_lsb; - int32_t delta_pic_order_cnt_bottom; - int32_t delta_pic_order_cnt[2]; - int32_t PicOrderCnt; - uint32_t FrameNumOffset; - /* */ - uint32_t recovery_frame_cnt; - uint32_t frame_num; - uint8_t *au; - uint32_t au_length; - uint8_t *incomplete_au; - uint32_t incomplete_au_length; - uint32_t au_number; -} h264_picture_info_t; - -typedef struct h264_info_tag h264_info_t; - -typedef struct -{ - lsmash_stream_buffers_t *sb; - uint8_t *rbsp; -} h264_stream_buffer_t; - -struct h264_info_tag -{ - lsmash_h264_specific_parameters_t avcC_param; - lsmash_h264_specific_parameters_t avcC_param_next; - h264_nalu_header_t nalu_header; - lsmash_entry_list_t sps_list [1]; /* contains entries as h264_sps_t */ - lsmash_entry_list_t pps_list [1]; /* contains entries as h264_pps_t */ - lsmash_entry_list_t slice_list[1]; /* for slice data partition */ - h264_sps_t sps; /* active SPS */ - h264_pps_t pps; /* active PPS */ - h264_sei_t sei; /* active SEI */ - h264_slice_info_t slice; /* active slice */ - h264_picture_info_t picture; - uint8_t prev_nalu_type; - uint8_t avcC_pending; - uint64_t ebsp_head_pos; - lsmash_bits_t *bits; - h264_stream_buffer_t buffer; -}; - -int h264_setup_parser -( - h264_info_t *info, - lsmash_stream_buffers_t *sb, - int parse_only, - lsmash_stream_buffers_type type, - void *stream -); - -void h264_cleanup_parser -( - h264_info_t *info -); - -int h264_calculate_poc -( - h264_info_t *info, - h264_picture_info_t *picture, - h264_picture_info_t *prev_picture -); - -void h264_update_picture_info_for_slice -( - h264_info_t *info, - h264_picture_info_t *picture, - h264_slice_info_t *slice -); - -void h264_update_picture_info -( - h264_info_t *info, - h264_picture_info_t *picture, - h264_slice_info_t *slice, - h264_sei_t *sei -); - -int h264_find_au_delimit_by_slice_info -( - h264_slice_info_t *slice, - h264_slice_info_t *prev_slice -); - -int h264_find_au_delimit_by_nalu_type -( - uint8_t nalu_type, - uint8_t prev_nalu_type -); - -int h264_supplement_buffer -( - h264_stream_buffer_t *buffer, - h264_picture_info_t *picture, - uint32_t size -); - -int h264_check_nalu_header -( - h264_nalu_header_t *nalu_header, - lsmash_stream_buffers_t *sb, - int use_long_start_code -); - -int h264_parse_sps -( - h264_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int h264_parse_pps -( - h264_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int h264_parse_sei -( - lsmash_bits_t *bits, - h264_sps_t *sps, - h264_sei_t *sei, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int h264_parse_slice -( - h264_info_t *info, - h264_nalu_header_t *nalu_header, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int h264_try_to_append_parameter_set -( - h264_info_t *info, - lsmash_h264_parameter_set_type ps_type, - void *ps_data, - uint32_t ps_length -); - -int h264_move_pending_avcC_param -( - h264_info_t *info -); diff -Nru l-smash-1.9.1/hevc.c l-smash-2.3.0/hevc.c --- l-smash-1.9.1/hevc.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/hevc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2979 +0,0 @@ -/***************************************************************************** - * hevc.c: - ***************************************************************************** - * Copyright (C) 2013-2014 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" - -#include -#include -#include - -#include "box.h" - -/*************************************************************************** - ITU-T Recommendation H.265 (04/13) - ISO/IEC 14496-15:2013 FDIS -***************************************************************************/ -#include "hevc.h" -#include "nalu.h" - -#define IF_INVALID_VALUE( x ) if( x ) -#define IF_EXCEED_INT32( x ) if( (x) < INT32_MIN || (x) > INT32_MAX ) -#define HEVC_POC_DEBUG_PRINT 0 - -#define HEVC_MIN_NALU_HEADER_LENGTH 2 -#define HEVC_MAX_VPS_ID 15 -#define HEVC_MAX_SPS_ID 15 -#define HEVC_MAX_PPS_ID 63 -#define HEVC_MAX_DPB_SIZE 16 -#define HVCC_CONFIGURATION_VERSION 0 /* Fix this value when the NAL file format is finalized. */ - -typedef enum -{ - HEVC_SLICE_TYPE_B = 0, - HEVC_SLICE_TYPE_P = 1, - HEVC_SLICE_TYPE_I = 2, -} hevc_slice_type; - -void lsmash_destroy_hevc_parameter_arrays -( - lsmash_hevc_specific_parameters_t *param -) -{ - if( !param || !param->parameter_arrays ) - return; - for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) - lsmash_remove_entries( param->parameter_arrays->ps_array[i].list, isom_remove_dcr_ps ); - lsmash_free( param->parameter_arrays ); - param->parameter_arrays = NULL; -} - -void hevc_destruct_specific_data -( - void *data -) -{ - if( !data ) - return; - lsmash_destroy_hevc_parameter_arrays( data ); - lsmash_free( data ); -} - -static void hevc_remove_pps -( - hevc_pps_t *pps -) -{ - if( !pps ) - return; - if( pps->colWidth ) - lsmash_free( pps->colWidth ); - if( pps->rowHeight ) - lsmash_free( pps->rowHeight ); - lsmash_free( pps ); -} - -void hevc_cleanup_parser -( - hevc_info_t *info -) -{ - if( !info ) - return; - lsmash_remove_entries( info->vps_list, NULL ); - lsmash_remove_entries( info->sps_list, NULL ); - lsmash_remove_entries( info->pps_list, hevc_remove_pps ); - lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param ); - lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param_next ); - lsmash_stream_buffers_cleanup( info->buffer.sb ); - lsmash_bits_adhoc_cleanup( info->bits ); - info->bits = NULL; -} - -int hevc_setup_parser -( - hevc_info_t *info, - lsmash_stream_buffers_t *sb, - int parse_only, - lsmash_stream_buffers_type type, - void *stream -) -{ - assert( sb ); - if( !info ) - return -1; - memset( info, 0, sizeof(hevc_info_t) ); - info->hvcC_param .lengthSizeMinusOne = HEVC_DEFAULT_NALU_LENGTH_SIZE - 1; - info->hvcC_param_next.lengthSizeMinusOne = HEVC_DEFAULT_NALU_LENGTH_SIZE - 1; - hevc_stream_buffer_t *hb = &info->buffer; - hb->sb = sb; - lsmash_stream_buffers_setup( sb, type, stream ); - sb->bank = lsmash_create_multiple_buffers( parse_only ? 2 : 4, HEVC_DEFAULT_BUFFER_SIZE ); - if( !sb->bank ) - return -1; - sb->start = lsmash_withdraw_buffer( sb->bank, 1 ); - hb->rbsp = lsmash_withdraw_buffer( sb->bank, 2 ); - sb->pos = sb->start; - sb->end = sb->start; - if( !parse_only ) - { - info->au.data = lsmash_withdraw_buffer( sb->bank, 3 ); - info->au.incomplete_data = lsmash_withdraw_buffer( sb->bank, 4 ); - } - info->bits = lsmash_bits_adhoc_create(); - if( !info->bits ) - { - lsmash_stream_buffers_cleanup( sb ); - return -1; - } - lsmash_init_entry_list( info->vps_list ); - lsmash_init_entry_list( info->sps_list ); - lsmash_init_entry_list( info->pps_list ); - return 0; -} - -static hevc_vps_t *hevc_get_vps -( - lsmash_entry_list_t *vps_list, - uint8_t vps_id -) -{ - if( !vps_list || vps_id > HEVC_MAX_VPS_ID ) - return NULL; - for( lsmash_entry_t *entry = vps_list->head; entry; entry = entry->next ) - { - hevc_vps_t *vps = (hevc_vps_t *)entry->data; - if( !vps ) - return NULL; - if( vps->video_parameter_set_id == vps_id ) - return vps; - } - hevc_vps_t *vps = lsmash_malloc_zero( sizeof(hevc_vps_t) ); - if( !vps ) - return NULL; - vps->video_parameter_set_id = vps_id; - if( lsmash_add_entry( vps_list, vps ) ) - { - lsmash_free( vps ); - return NULL; - } - return vps; -} - -static hevc_sps_t *hevc_get_sps -( - lsmash_entry_list_t *sps_list, - uint8_t sps_id -) -{ - if( !sps_list || sps_id > HEVC_MAX_SPS_ID ) - return NULL; - for( lsmash_entry_t *entry = sps_list->head; entry; entry = entry->next ) - { - hevc_sps_t *sps = (hevc_sps_t *)entry->data; - if( !sps ) - return NULL; - if( sps->seq_parameter_set_id == sps_id ) - return sps; - } - hevc_sps_t *sps = lsmash_malloc_zero( sizeof(hevc_sps_t) ); - if( !sps ) - return NULL; - sps->seq_parameter_set_id = sps_id; - if( lsmash_add_entry( sps_list, sps ) ) - { - lsmash_free( sps ); - return NULL; - } - return sps; -} - -static hevc_pps_t *hevc_get_pps -( - lsmash_entry_list_t *pps_list, - uint8_t pps_id -) -{ - if( !pps_list || pps_id > HEVC_MAX_PPS_ID ) - return NULL; - for( lsmash_entry_t *entry = pps_list->head; entry; entry = entry->next ) - { - hevc_pps_t *pps = (hevc_pps_t *)entry->data; - if( !pps ) - return NULL; - if( pps->pic_parameter_set_id == pps_id ) - return pps; - } - hevc_pps_t *pps = lsmash_malloc_zero( sizeof(hevc_pps_t) ); - if( !pps ) - return NULL; - pps->pic_parameter_set_id = pps_id; - if( lsmash_add_entry( pps_list, pps ) ) - { - lsmash_free( pps ); - return NULL; - } - return pps; -} - -int hevc_calculate_poc -( - hevc_info_t *info, - hevc_picture_info_t *picture, - hevc_picture_info_t *prev_picture -) -{ -#if HEVC_POC_DEBUG_PRINT - fprintf( stderr, "PictureOrderCount\n" ); -#endif - hevc_pps_t *pps = hevc_get_pps( info->pps_list, picture->pic_parameter_set_id ); - if( !pps ) - return -1; - hevc_sps_t *sps = hevc_get_sps( info->sps_list, pps->seq_parameter_set_id ); - if( !sps ) - return -1; - /* 8.3.1 Decoding process for picture order count - * This process needs to be invoked only for the first slice segment of a picture. */ - int NoRaslOutputFlag = picture->irap && (picture->idr || picture->broken_link || picture->first); - int64_t poc_msb; - int32_t poc_lsb = picture->poc_lsb; - if( picture->irap && NoRaslOutputFlag ) - poc_msb = 0; - else - { - int32_t prev_poc_msb = picture->idr ? 0 : prev_picture->tid0_poc_msb; - int32_t prev_poc_lsb = picture->idr ? 0 : prev_picture->tid0_poc_lsb; - int32_t max_poc_lsb = 1 << sps->log2_max_pic_order_cnt_lsb; - if( (poc_lsb < prev_poc_lsb) - && ((prev_poc_lsb - poc_lsb) >= (max_poc_lsb / 2)) ) - poc_msb = prev_poc_msb + max_poc_lsb; - else if( (poc_lsb > prev_poc_lsb) - && ((poc_lsb - prev_poc_lsb) > (max_poc_lsb / 2)) ) - poc_msb = prev_poc_msb - max_poc_lsb; - else - poc_msb = prev_poc_msb; - } - picture->poc = poc_msb + poc_lsb; - if( picture->TemporalId == 0 && (!picture->radl || !picture->rasl || !picture->sublayer_nonref) ) - { - picture->tid0_poc_msb = poc_msb; - picture->tid0_poc_lsb = poc_lsb; - } -#if HEVC_POC_DEBUG_PRINT - fprintf( stderr, " prevPicOrderCntMsb: %"PRId32"\n", prev_poc_msb ); - fprintf( stderr, " prevPicOrderCntLsb: %"PRId32"\n", prev_poc_lsb ); - fprintf( stderr, " PicOrderCntMsb: %"PRId64"\n", poc_msb ); - fprintf( stderr, " pic_order_cnt_lsb: %"PRId32"\n", poc_lsb ); - fprintf( stderr, " MaxPicOrderCntLsb: %"PRIu64"\n", max_poc_lsb ); - fprintf( stderr, " POC: %"PRId32"\n", picture->poc ); -#endif - return 0; -} - -int hevc_check_nalu_header -( - hevc_nalu_header_t *nalu_header, - lsmash_stream_buffers_t *sb, - int use_long_start_code -) -{ - uint8_t forbidden_zero_bit = (*sb->pos >> 7) & 0x01; - uint8_t nal_unit_type = (*sb->pos >> 1) & 0x3f; - uint8_t nuh_layer_id = ((*sb->pos & 0x01) << 5) | ((*(sb->pos + 1) >> 3) & 0x1f); - uint8_t nuh_temporal_id_plus1 = *(sb->pos + 1) & 0x07; - nalu_header->nal_unit_type = nal_unit_type; - nalu_header->TemporalId = nuh_temporal_id_plus1 - 1; - nalu_header->length = HEVC_MIN_NALU_HEADER_LENGTH; - sb->pos += nalu_header->length; - IF_INVALID_VALUE( forbidden_zero_bit || nuh_temporal_id_plus1 == 0 ) - return -1; - /* nuh_layer_id shall be 0 in the specification we refer to. */ - if( nuh_layer_id ) - return -1; - if( nalu_header->TemporalId == 0 ) - { - /* For TSA_N, TSA_R, STSA_N and STSA_R, TemporalId shall not be equal to 0. */ - IF_INVALID_VALUE( nal_unit_type >= HEVC_NALU_TYPE_TSA_N && nal_unit_type <= HEVC_NALU_TYPE_STSA_R ) - return -1; - } - else - { - /* For BLA_W_LP to RSV_IRAP_VCL23, TemporalId shall be equal to 0. */ - IF_INVALID_VALUE( nal_unit_type >= HEVC_NALU_TYPE_BLA_W_LP && nal_unit_type <= HEVC_NALU_TYPE_RSV_IRAP_VCL23 ) - return -1; - /* For VPS, SPS, EOS and EOB, TemporalId shall be equal to 0. */ - IF_INVALID_VALUE( nal_unit_type >= HEVC_NALU_TYPE_VPS && nal_unit_type <= HEVC_NALU_TYPE_EOB - && nal_unit_type != HEVC_NALU_TYPE_PPS && nal_unit_type != HEVC_NALU_TYPE_AUD ) - return -1; - } - /* VPS, SPS and PPS require long start code (0x00000001). - * Also AU delimiter requires it too because this type of NALU shall be the first NALU of any AU if present. */ - IF_INVALID_VALUE( !use_long_start_code && (nal_unit_type >= HEVC_NALU_TYPE_VPS && nal_unit_type <= HEVC_NALU_TYPE_AUD) ) - return -1; - return 0; -} - -static inline int hevc_activate_vps -( - hevc_info_t *info, - uint8_t video_parameter_set_id -) -{ - hevc_vps_t *vps = hevc_get_vps( info->vps_list, video_parameter_set_id ); - if( !vps ) - return -1; - info->vps = *vps; - return 0; -} - -static inline int hevc_activate_sps -( - hevc_info_t *info, - uint8_t seq_parameter_set_id -) -{ - hevc_sps_t *sps = hevc_get_sps( info->sps_list, seq_parameter_set_id ); - if( !sps ) - return -1; - info->sps = *sps; - return 0; -} - -static void hevc_parse_scaling_list_data -( - lsmash_bits_t *bits -) -{ - for( int sizeId = 0; sizeId < 4; sizeId++ ) - for( int matrixId = 0; matrixId < (sizeId == 3 ? 2 : 6); matrixId++ ) - { - if( !lsmash_bits_get( bits, 1 ) ) /* scaling_list_pred_mode_flag[sizeId][matrixId] */ - nalu_get_exp_golomb_ue( bits ); /* scaling_list_pred_matrix_id_delta[sizeId][matrixId] */ - else - { - int coefNum = LSMASH_MIN( 64, 1 << (4 + (sizeId << 1)) ); - if( sizeId > 1 ) - nalu_get_exp_golomb_se( bits ); /* scaling_list_dc_coef_minus8[sizeId - 2][matrixId] */ - for( int i = 0; i < coefNum; i++ ) - nalu_get_exp_golomb_se( bits ); /* scaling_list_delta_coef */ - } - } -} - -static int hevc_short_term_ref_pic_set -( - lsmash_bits_t *bits, - hevc_sps_t *sps, - int stRpsIdx -) -{ - int inter_ref_pic_set_prediction_flag = stRpsIdx != 0 ? lsmash_bits_get( bits, 1 ) : 0; - if( inter_ref_pic_set_prediction_flag ) - { - /* delta_idx_minus1 is always 0 in SPS since stRpsIdx must not be equal to num_short_term_ref_pic_sets. */ - uint64_t delta_idx_minus1 = stRpsIdx == sps->num_short_term_ref_pic_sets ? nalu_get_exp_golomb_ue( bits ) : 0; - int delta_rps_sign = lsmash_bits_get( bits, 1 ); - uint64_t abs_delta_rps_minus1 = nalu_get_exp_golomb_ue( bits ); - int RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1); - int deltaRps = (delta_rps_sign ? -1 : 1) * (abs_delta_rps_minus1 + 1); - hevc_st_rps_t *st_rps = &sps->st_rps[stRpsIdx]; - hevc_st_rps_t *ref_rps = &sps->st_rps[RefRpsIdx]; - uint8_t used_by_curr_pic_flag[32]; - uint8_t use_delta_flag [32]; - for( int j = 0; j <= ref_rps->NumDeltaPocs; j++ ) - { - used_by_curr_pic_flag[j] = lsmash_bits_get( bits, 1 ); - use_delta_flag [j] = !used_by_curr_pic_flag[j] ? lsmash_bits_get( bits, 1 ) : 1; - } - /* NumNegativePics */ - int i = 0; - for( int j = ref_rps->NumPositivePics - 1; j >= 0; j-- ) - { - int dPoc = ref_rps->DeltaPocS1[j] + deltaRps; - if( dPoc < 0 && use_delta_flag[ ref_rps->NumNegativePics + j ] ) - { - st_rps->DeltaPocS0 [i ] = dPoc; - st_rps->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[ ref_rps->NumNegativePics + j ]; - } - } - if( deltaRps < 0 && use_delta_flag[ ref_rps->NumDeltaPocs ] ) - { - st_rps->DeltaPocS0 [i ] = deltaRps; - st_rps->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[ ref_rps->NumDeltaPocs ]; - } - for( int j = 0; j < ref_rps->NumNegativePics; j++ ) - { - int dPoc = ref_rps->DeltaPocS0[j] + deltaRps; - if( dPoc < 0 && use_delta_flag[j] ) - { - st_rps->DeltaPocS0 [i ] = dPoc; - st_rps->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[j]; - } - } - st_rps->NumNegativePics = i; - /* NumPositivePics */ - i = 0; - for( int j = ref_rps->NumNegativePics - 1; j >= 0; j-- ) - { - int dPoc = ref_rps->DeltaPocS0[j] + deltaRps; - if( dPoc > 0 && use_delta_flag[j] ) - { - st_rps->DeltaPocS1 [i ] = dPoc; - st_rps->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[j]; - } - } - if( deltaRps > 0 && use_delta_flag[ ref_rps->NumDeltaPocs ] ) - { - st_rps->DeltaPocS1 [i ] = deltaRps; - st_rps->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[ ref_rps->NumDeltaPocs ]; - } - for( int j = 0; j < ref_rps->NumPositivePics; j++ ) - { - int dPoc = ref_rps->DeltaPocS1[j] + deltaRps; - if( dPoc > 0 && use_delta_flag[ ref_rps->NumNegativePics + j ] ) - { - st_rps->DeltaPocS1 [i ] = dPoc; - st_rps->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[ ref_rps->NumNegativePics + j ]; - } - } - st_rps->NumPositivePics = i; - /* NumDeltaPocs */ - st_rps->NumDeltaPocs = st_rps->NumNegativePics + st_rps->NumPositivePics; - } - else - { - uint64_t num_negative_pics = nalu_get_exp_golomb_ue( bits ); - uint64_t num_positive_pics = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( num_negative_pics >= HEVC_MAX_DPB_SIZE || num_positive_pics >= HEVC_MAX_DPB_SIZE ) - return -1; - hevc_st_rps_t *st_rps = &sps->st_rps[stRpsIdx]; - st_rps->NumNegativePics = num_negative_pics; - st_rps->NumPositivePics = num_positive_pics; - st_rps->NumDeltaPocs = st_rps->NumNegativePics + st_rps->NumPositivePics; - for( int i = 0; i < num_negative_pics; i++ ) - { - uint64_t delta_poc_s0_minus1 = nalu_get_exp_golomb_ue( bits ); - if( i == 0 ) - st_rps->DeltaPocS0[i] = -(delta_poc_s0_minus1 + 1); - else - st_rps->DeltaPocS0[i] = st_rps->DeltaPocS0[i - 1] - (delta_poc_s0_minus1 + 1); - st_rps->UsedByCurrPicS0[i] = lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_s0_flag */ - } - for( int i = 0; i < num_positive_pics; i++ ) - { - uint64_t delta_poc_s1_minus1 = nalu_get_exp_golomb_ue( bits ); - if( i == 0 ) - st_rps->DeltaPocS1[i] = +(delta_poc_s1_minus1 + 1); - else - st_rps->DeltaPocS1[i] = st_rps->DeltaPocS1[i - 1] + (delta_poc_s1_minus1 + 1); - st_rps->UsedByCurrPicS0[i] = lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_s1_flag */ - } - } - return 0; -} - -static inline void hevc_parse_sub_layer_hrd_parameters -( - lsmash_bits_t *bits, - int CpbCnt, - int sub_pic_hrd_params_present_flag -) -{ - for( int i = 0; i <= CpbCnt; i++ ) - { - nalu_get_exp_golomb_ue( bits ); /* bit_rate_value_minus1[i] */ - nalu_get_exp_golomb_ue( bits ); /* cpb_size_value_minus1[i] */ - if( sub_pic_hrd_params_present_flag ) - { - nalu_get_exp_golomb_ue( bits ); /* cpb_size_du_value_minus1[i] */ - nalu_get_exp_golomb_ue( bits ); /* bit_rate_du_value_minus1[i] */ - } - lsmash_bits_get( bits, 1 ); /* cbr_flag[i] */ - } -} - -static void hevc_parse_hrd_parameters -( - lsmash_bits_t *bits, - hevc_hrd_t *hrd, - int commonInfPresentFlag, - int maxNumSubLayersMinus1 -) -{ - /* The specification we refer to doesn't define the implicit value of some fields. - * According to JCTVC-HM reference software, - * the implicit value of nal_hrd_parameters_present_flag is to be equal to 0, - * the implicit value of vcl_hrd_parameters_present_flag is to be equal to 0. */ - int nal_hrd_parameters_present_flag = 0; - int vcl_hrd_parameters_present_flag = 0; - memset( hrd, 0, sizeof(hevc_hrd_t) ); - if( commonInfPresentFlag ) - { - nal_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); - vcl_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 ); - if( nal_hrd_parameters_present_flag - || vcl_hrd_parameters_present_flag ) - { - hrd->CpbDpbDelaysPresentFlag = 1; - hrd->sub_pic_hrd_params_present_flag = lsmash_bits_get( bits, 1 ); - if( hrd->sub_pic_hrd_params_present_flag ) - { - lsmash_bits_get( bits, 8 ); /* tick_divisor_minus2 */ - hrd->du_cpb_removal_delay_increment_length = lsmash_bits_get( bits, 5 ) + 1; - hrd->sub_pic_cpb_params_in_pic_timing_sei_flag = lsmash_bits_get( bits, 1 ); - hrd->dpb_output_delay_du_length = lsmash_bits_get( bits, 5 ) + 1; - } - lsmash_bits_get( bits, 4 ); /* bit_rate_scale */ - lsmash_bits_get( bits, 4 ); /* cpb_size_scale */ - if( hrd->sub_pic_hrd_params_present_flag ) - lsmash_bits_get( bits, 4 ); /* cpb_size_du_scale */ - lsmash_bits_get( bits, 5 ); /* initial_cpb_removal_delay_length_minus1 */ - hrd->au_cpb_removal_delay_length = lsmash_bits_get( bits, 5 ) + 1; - hrd->dpb_output_delay_length = lsmash_bits_get( bits, 5 ) + 1; - } - } - for( int i = 0; i <= maxNumSubLayersMinus1; i++ ) - { - hrd->fixed_pic_rate_general_flag[i] = lsmash_bits_get( bits, 1 ); - uint8_t fixed_pic_rate_within_cvs_flag = !hrd->fixed_pic_rate_general_flag[i] ? lsmash_bits_get( bits, 1 ) : 1; - uint8_t low_delay_hrd_flag = !fixed_pic_rate_within_cvs_flag ? lsmash_bits_get( bits, 1 ) : 0; - hrd->elemental_duration_in_tc[i] = fixed_pic_rate_within_cvs_flag ? nalu_get_exp_golomb_ue( bits ) + 1 : 0; - uint8_t cpb_cnt_minus1 = !low_delay_hrd_flag ? nalu_get_exp_golomb_ue( bits ) : 0; - if( nal_hrd_parameters_present_flag ) - hevc_parse_sub_layer_hrd_parameters( bits, cpb_cnt_minus1, hrd->sub_pic_hrd_params_present_flag ); - if( vcl_hrd_parameters_present_flag ) - hevc_parse_sub_layer_hrd_parameters( bits, cpb_cnt_minus1, hrd->sub_pic_hrd_params_present_flag ); - } -} - -static inline void hevc_parse_profile_tier_level_common -( - lsmash_bits_t *bits, - hevc_ptl_common_t *ptlc, - int profile_present, - int level_present -) -{ - if( profile_present ) - { - ptlc->profile_space = lsmash_bits_get( bits, 2 ); - ptlc->tier_flag = lsmash_bits_get( bits, 1 ); - ptlc->profile_idc = lsmash_bits_get( bits, 5 ); - ptlc->profile_compatibility_flags = lsmash_bits_get( bits, 32 ); - ptlc->progressive_source_flag = lsmash_bits_get( bits, 1 ); - ptlc->interlaced_source_flag = lsmash_bits_get( bits, 1 ); - ptlc->non_packed_constraint_flag = lsmash_bits_get( bits, 1 ); - ptlc->frame_only_constraint_flag = lsmash_bits_get( bits, 1 ); - ptlc->reserved_zero_44bits = lsmash_bits_get( bits, 44 ); - } - if( level_present ) - ptlc->level_idc = lsmash_bits_get( bits, 8 ); -} - -static void hevc_parse_profile_tier_level -( - lsmash_bits_t *bits, - hevc_ptl_t *ptl, - int maxNumSubLayersMinus1 -) -{ - hevc_parse_profile_tier_level_common( bits, &ptl->general, 1, 1 ); - if( maxNumSubLayersMinus1 == 0 ) - return; - assert( maxNumSubLayersMinus1 <= 6 ); - int sub_layer_profile_present_flag[6] = { 0 }; - int sub_layer_level_present_flag [6] = { 0 }; - for( int i = 0; i < maxNumSubLayersMinus1; i++ ) - { - sub_layer_profile_present_flag[i] = lsmash_bits_get( bits, 1 ); - sub_layer_level_present_flag [i] = lsmash_bits_get( bits, 1 ); - } - for( int i = maxNumSubLayersMinus1; i < 8; i++ ) - lsmash_bits_get( bits, 2 ); /* reserved_zero_2bits[i] */ - for( int i = 0; i < maxNumSubLayersMinus1; i++ ) - hevc_parse_profile_tier_level_common( bits, &ptl->sub_layer[i], sub_layer_profile_present_flag[i], sub_layer_level_present_flag[i] ); -} - -static int hevc_parse_vps_minimally -( - lsmash_bits_t *bits, - hevc_vps_t *vps, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - memset( vps, 0, sizeof(hevc_vps_t) ); - vps->video_parameter_set_id = lsmash_bits_get( bits, 4 ); - /* vps_reserved_three_2bits shall be 3 in the specification we refer to. */ - if( lsmash_bits_get( bits, 2 ) != 3 ) - return -1; - /* vps_max_layers_minus1 shall be 0 in the specification we refer to. */ - if( lsmash_bits_get( bits, 6 ) != 0 ) - return -1; - vps->max_sub_layers_minus1 = lsmash_bits_get( bits, 3 ); - vps->temporal_id_nesting_flag = lsmash_bits_get( bits, 1 ); - /* When vps_max_sub_layers_minus1 is equal to 0, vps_temporal_id_nesting_flag shall be equal to 1. */ - if( (vps->max_sub_layers_minus1 | vps->temporal_id_nesting_flag) == 0 ) - return -1; - /* vps_reserved_0xffff_16bits shall be 0xFFFF in the specification we refer to. */ - if( lsmash_bits_get( bits, 16 ) != 0xFFFF ) - return -1; - hevc_parse_profile_tier_level( bits, &vps->ptl, vps->max_sub_layers_minus1 ); - vps->frame_field_info_present_flag = vps->ptl.general.progressive_source_flag - && vps->ptl.general.interlaced_source_flag; - int sub_layer_ordering_info_present_flag = lsmash_bits_get( bits, 1 ); - for( int i = sub_layer_ordering_info_present_flag ? 0 : vps->max_sub_layers_minus1; i <= vps->max_sub_layers_minus1; i++ ) - { - nalu_get_exp_golomb_ue( bits ); /* max_dec_pic_buffering_minus1[i] */ - nalu_get_exp_golomb_ue( bits ); /* max_num_reorder_pics [i] */ - nalu_get_exp_golomb_ue( bits ); /* max_latency_increase_plus1 [i] */ - } - uint8_t max_layer_id = lsmash_bits_get( bits, 6 ); - uint16_t num_layer_sets_minus1 = nalu_get_exp_golomb_ue( bits ); - for( int i = 1; i <= num_layer_sets_minus1; i++ ) - for( int j = 0; j <= max_layer_id; j++ ) - lsmash_bits_get( bits, 1 ); /* layer_id_included_flag[i][j] */ - return bits->bs->error ? -1 : 0; -} - -int hevc_parse_vps -( - hevc_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - hevc_vps_t *vps; - { - /* Parse VPS minimally for configuration records. */ - hevc_vps_t min_vps; - if( hevc_parse_vps_minimally( bits, &min_vps, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - vps = hevc_get_vps( info->vps_list, min_vps.video_parameter_set_id ); - if( !vps ) - return -1; - *vps = min_vps; - } - vps->timing_info_present_flag = lsmash_bits_get( bits, 1 ); - if( vps->timing_info_present_flag ) - { - lsmash_bits_get( bits, 32 ); /* num_units_in_tick */ - lsmash_bits_get( bits, 32 ); /* time_scale */ - if( lsmash_bits_get( bits, 1 ) ) /* poc_proportional_to_timing_flag */ - nalu_get_exp_golomb_ue( bits ); /* num_ticks_poc_diff_one_minus1 */ - vps->num_hrd_parameters = nalu_get_exp_golomb_ue( bits ); - for( int i = 0; i < vps->num_hrd_parameters; i++ ) - { - nalu_get_exp_golomb_ue( bits ); /* hrd_layer_set_idx[i] */ - int cprms_present_flag = i > 0 ? lsmash_bits_get( bits, 1 ) : 1; - /* Although the value of vps_num_hrd_parameters is required to be less than or equal to 1 in the spec - * we refer to, decoders shall allow other values of vps_num_hrd_parameters in the range of 0 to 1024, - * inclusive, to appear in the syntax. */ - if( i <= 1 ) - hevc_parse_hrd_parameters( bits, &vps->hrd[i], cprms_present_flag, vps->max_sub_layers_minus1 ); - else - { - hevc_hrd_t dummy_hrd; - hevc_parse_hrd_parameters( bits, &dummy_hrd, cprms_present_flag, vps->max_sub_layers_minus1 ); - } - } - } - /* Skip VPS extension. */ - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - vps->present = 1; - info->vps = *vps; - return 0; -} - -static int hevc_parse_sps_minimally -( - lsmash_bits_t *bits, - hevc_sps_t *sps, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - memset( sps, 0, sizeof(hevc_sps_t) ); - sps->video_parameter_set_id = lsmash_bits_get( bits, 4 ); - sps->max_sub_layers_minus1 = lsmash_bits_get( bits, 3 ); - sps->temporal_id_nesting_flag = lsmash_bits_get( bits, 1 ); - hevc_parse_profile_tier_level( bits, &sps->ptl, sps->max_sub_layers_minus1 ); - sps->seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - sps->chroma_format_idc = nalu_get_exp_golomb_ue( bits ); - if( sps->chroma_format_idc == 3 ) - sps->separate_colour_plane_flag = lsmash_bits_get( bits, 1 ); - static const int SubWidthC [] = { 1, 2, 2, 1 }; - static const int SubHeightC[] = { 1, 2, 1, 1 }; - uint64_t pic_width_in_luma_samples = nalu_get_exp_golomb_ue( bits ); - uint64_t pic_height_in_luma_samples = nalu_get_exp_golomb_ue( bits ); - sps->cropped_width = pic_width_in_luma_samples; - sps->cropped_height = pic_height_in_luma_samples; - if( lsmash_bits_get( bits, 1 ) ) /* conformance_window_flag */ - { - uint64_t conf_win_left_offset = nalu_get_exp_golomb_ue( bits ); - uint64_t conf_win_right_offset = nalu_get_exp_golomb_ue( bits ); - uint64_t conf_win_top_offset = nalu_get_exp_golomb_ue( bits ); - uint64_t conf_win_bottom_offset = nalu_get_exp_golomb_ue( bits ); - sps->cropped_width -= (conf_win_left_offset + conf_win_right_offset) * SubWidthC [ sps->chroma_format_idc ]; - sps->cropped_height -= (conf_win_top_offset + conf_win_bottom_offset) * SubHeightC[ sps->chroma_format_idc ]; - } - sps->bit_depth_luma_minus8 = nalu_get_exp_golomb_ue( bits ); - sps->bit_depth_chroma_minus8 = nalu_get_exp_golomb_ue( bits ); - sps->log2_max_pic_order_cnt_lsb = nalu_get_exp_golomb_ue( bits ) + 4; - int sub_layer_ordering_info_present_flag = lsmash_bits_get( bits, 1 ); - for( int i = sub_layer_ordering_info_present_flag ? 0 : sps->max_sub_layers_minus1; i <= sps->max_sub_layers_minus1; i++ ) - { - nalu_get_exp_golomb_ue( bits ); /* max_dec_pic_buffering_minus1[i] */ - nalu_get_exp_golomb_ue( bits ); /* max_num_reorder_pics [i] */ - nalu_get_exp_golomb_ue( bits ); /* max_latency_increase_plus1 [i] */ - } - uint64_t log2_min_luma_coding_block_size_minus3 = nalu_get_exp_golomb_ue( bits ); - uint64_t log2_diff_max_min_luma_coding_block_size = nalu_get_exp_golomb_ue( bits ); - nalu_get_exp_golomb_ue( bits ); /* log2_min_transform_block_size_minus2 */ - nalu_get_exp_golomb_ue( bits ); /* log2_diff_max_min_transform_block_size */ - nalu_get_exp_golomb_ue( bits ); /* max_transform_hierarchy_depth_inter */ - nalu_get_exp_golomb_ue( bits ); /* max_transform_hierarchy_depth_intra */ - { - int MinCbLog2SizeY = log2_min_luma_coding_block_size_minus3 + 3; - int MinCbSizeY = 1 << MinCbLog2SizeY; - if( pic_width_in_luma_samples == 0 || pic_width_in_luma_samples % MinCbSizeY - || pic_height_in_luma_samples == 0 || pic_height_in_luma_samples % MinCbSizeY ) - return -1; /* Both shall be an integer multiple of MinCbSizeY. */ - int CtbLog2SizeY = MinCbLog2SizeY + log2_diff_max_min_luma_coding_block_size; - int CtbSizeY = 1 << CtbLog2SizeY; - sps->PicWidthInCtbsY = (pic_width_in_luma_samples - 1) / CtbSizeY + 1; - sps->PicHeightInCtbsY = (pic_height_in_luma_samples - 1) / CtbSizeY + 1; - sps->PicSizeInCtbsY = sps->PicWidthInCtbsY * sps->PicHeightInCtbsY; - } - if( lsmash_bits_get( bits, 1 ) /* scaling_list_enabled_flag */ - && lsmash_bits_get( bits, 1 ) ) /* sps_scaling_list_data_present_flag */ - hevc_parse_scaling_list_data( bits ); - lsmash_bits_get( bits, 1 ); /* amp_enabled_flag */ - lsmash_bits_get( bits, 1 ); /* sample_adaptive_offset_enabled_flag */ - if( lsmash_bits_get( bits, 1 ) ) /* pcm_enabled_flag */ - { - lsmash_bits_get( bits, 4 ); /* pcm_sample_bit_depth_luma_minus1 */ - lsmash_bits_get( bits, 4 ); /* pcm_sample_bit_depth_chroma_minus1 */ - nalu_get_exp_golomb_ue( bits ); /* log2_min_pcm_luma_coding_block_size_minus3 */ - nalu_get_exp_golomb_ue( bits ); /* log2_diff_max_min_pcm_luma_coding_block_size */ - lsmash_bits_get( bits, 1 ); /* pcm_loop_filter_disabled_flag */ - } - sps->num_short_term_ref_pic_sets = nalu_get_exp_golomb_ue( bits ); - for( int i = 0; i < sps->num_short_term_ref_pic_sets; i++ ) - if( hevc_short_term_ref_pic_set( bits, sps, i ) < 0 ) - return -1; - sps->long_term_ref_pics_present_flag = lsmash_bits_get( bits, 1 ); - if( sps->long_term_ref_pics_present_flag ) - { - sps->num_long_term_ref_pics_sps = nalu_get_exp_golomb_ue( bits ); - for( int i = 0; i < sps->num_long_term_ref_pics_sps; i++ ) - { - lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); /* lt_ref_pic_poc_lsb_sps [i] */ - lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_lt_sps_flag[i] */ - } - } - sps->temporal_mvp_enabled_flag = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 1 ); /* strong_intra_smoothing_enabled_flag */ - sps->vui.present = lsmash_bits_get( bits, 1 ); /* vui_parameters_present_flag */ - if( sps->vui.present ) - { - /* vui_parameters() */ - if( lsmash_bits_get( bits, 1 ) ) /* aspect_ratio_info_present_flag */ - { - uint8_t aspect_ratio_idc = lsmash_bits_get( bits, 8 ); - if( aspect_ratio_idc == 255 ) - { - /* EXTENDED_SAR */ - sps->vui.sar_width = lsmash_bits_get( bits, 16 ); - sps->vui.sar_height = lsmash_bits_get( bits, 16 ); - } - else - { - static const struct - { - uint16_t sar_width; - uint16_t sar_height; - } pre_defined_sar[] = - { - { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, - { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, { 80, 33 }, - { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, { 4, 3 }, - { 3, 2 }, { 2, 1 } - }; - if( aspect_ratio_idc < (sizeof(pre_defined_sar) / sizeof(pre_defined_sar[0])) ) - { - sps->vui.sar_width = pre_defined_sar[ aspect_ratio_idc ].sar_width; - sps->vui.sar_height = pre_defined_sar[ aspect_ratio_idc ].sar_height; - } - else - { - /* Behavior when unknown aspect_ratio_idc is detected is not specified in the specification. */ - sps->vui.sar_width = 0; - sps->vui.sar_height = 0; - } - } - } - else - { - sps->vui.sar_width = 0; - sps->vui.sar_height = 0; - } - if( lsmash_bits_get( bits, 1 ) ) /* overscan_info_present_flag */ - lsmash_bits_get( bits, 1 ); /* overscan_appropriate_flag */ - if( lsmash_bits_get( bits, 1 ) ) /* video_signal_type_present_flag */ - { - lsmash_bits_get( bits, 3 ); /* video_format */ - sps->vui.video_full_range_flag = lsmash_bits_get( bits, 1 ); - sps->vui.colour_description_present_flag = lsmash_bits_get( bits, 1 ); - if( sps->vui.colour_description_present_flag ) - { - sps->vui.colour_primaries = lsmash_bits_get( bits, 8 ); - sps->vui.transfer_characteristics = lsmash_bits_get( bits, 8 ); - sps->vui.matrix_coeffs = lsmash_bits_get( bits, 8 ); - } - else - { - sps->vui.colour_primaries = 2; - sps->vui.transfer_characteristics = 2; - sps->vui.matrix_coeffs = 2; - } - } - if( lsmash_bits_get( bits, 1 ) ) /* chroma_loc_info_present_flag */ - { - nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_top_field */ - nalu_get_exp_golomb_ue( bits ); /* chroma_sample_loc_type_bottom_field */ - } - lsmash_bits_get( bits, 1 ); /* neutral_chroma_indication_flag */ - sps->vui.field_seq_flag = lsmash_bits_get( bits, 1 ); - sps->vui.frame_field_info_present_flag = lsmash_bits_get( bits, 1 ); - if( sps->vui.field_seq_flag ) - /* cropped_height indicates in a frame. */ - sps->cropped_height *= 2; - if( lsmash_bits_get( bits, 1 ) ) /* default_display_window_flag */ - { - /* default display window - * A rectangular region for display specified by these values is not considered - * as cropped visual presentation size which decoder delivers. - * Maybe, these values shall be indicated by the clean aperture on container level. */ - sps->vui.def_disp_win_offset.left = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubWidthC [ sps->chroma_format_idc ], 1 }; - sps->vui.def_disp_win_offset.right = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubWidthC [ sps->chroma_format_idc ], 1 }; - sps->vui.def_disp_win_offset.top = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubHeightC[ sps->chroma_format_idc ], 1 }; - sps->vui.def_disp_win_offset.bottom = (lsmash_rational_u32_t){ nalu_get_exp_golomb_ue( bits ) * SubHeightC[ sps->chroma_format_idc ], 1 }; - } - if( lsmash_bits_get( bits, 1 ) ) /* vui_timing_info_present_flag */ - { - sps->vui.num_units_in_tick = lsmash_bits_get( bits, 32 ); - sps->vui.time_scale = lsmash_bits_get( bits, 32 ); - if( lsmash_bits_get( bits, 1 ) ) /* vui_poc_proportional_to_timing_flag */ - nalu_get_exp_golomb_ue( bits ); /* vui_num_ticks_poc_diff_one_minus1 */ - if( lsmash_bits_get( bits, 1 ) ) /* vui_hrd_parameters_present_flag */ - hevc_parse_hrd_parameters( bits, &sps->vui.hrd, 1, sps->max_sub_layers_minus1 ); - } - else - { - sps->vui.num_units_in_tick = 1; /* arbitrary */ - sps->vui.time_scale = 25; /* arbitrary */ - } - if( lsmash_bits_get( bits, 1 ) ) /* bitstream_restriction_flag */ - { - lsmash_bits_get( bits, 1 ); /* tiles_fixed_structure_flag */ - lsmash_bits_get( bits, 1 ); /* motion_vectors_over_pic_boundaries_flag */ - lsmash_bits_get( bits, 1 ); /* restricted_ref_pic_lists_flag */ - sps->vui.min_spatial_segmentation_idc = nalu_get_exp_golomb_ue( bits ); - nalu_get_exp_golomb_ue( bits ); /* max_bytes_per_pic_denom */ - nalu_get_exp_golomb_ue( bits ); /* max_bits_per_min_cu_denom */ - nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_horizontal */ - nalu_get_exp_golomb_ue( bits ); /* log2_max_mv_length_vertical */ - } - else - sps->vui.min_spatial_segmentation_idc = 0; - } - else - { - sps->vui.sar_width = 0; - sps->vui.sar_height = 0; - sps->vui.colour_primaries = 2; - sps->vui.transfer_characteristics = 2; - sps->vui.matrix_coeffs = 2; - sps->vui.field_seq_flag = 0; - sps->vui.frame_field_info_present_flag = sps->ptl.general.progressive_source_flag - && sps->ptl.general.interlaced_source_flag; - sps->vui.num_units_in_tick = 1; /* arbitrary */ - sps->vui.time_scale = 25; /* arbitrary */ - sps->vui.min_spatial_segmentation_idc = 0; - } - return bits->bs->error ? -1 : 0; -} - -int hevc_parse_sps -( - hevc_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - hevc_sps_t *sps; - { - /* Parse SPS minimally for configuration records. */ - hevc_sps_t min_sps; - if( hevc_parse_sps_minimally( bits, &min_sps, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - sps = hevc_get_sps( info->sps_list, min_sps.seq_parameter_set_id ); - if( !sps ) - return -1; - *sps = min_sps; - } - /* Skip SPS extension. */ - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - sps->present = 1; - info->sps = *sps; - hevc_activate_vps( info, info->sps.video_parameter_set_id ); - return 0; -} - -static int hevc_allocate_tile_sizes -( - hevc_pps_t *pps, - uint32_t num_tile_columns, - uint32_t num_tile_rows -) -{ - /* Allocate columns and rows of tiles. */ - void *temp = lsmash_realloc( pps->colWidth, 2 * num_tile_columns * sizeof(uint32_t) ); - if( !temp ) - return -1; - pps->colWidth = temp; - temp = lsmash_realloc( pps->rowHeight, 2 * num_tile_rows * sizeof(uint32_t) ); - if( !temp ) - return -1; - pps->rowHeight = temp; - pps->colBd = pps->colWidth + num_tile_columns; - pps->rowBd = pps->rowHeight + num_tile_rows; - return 0; -} - -static int hevc_parse_pps_minimally -( - lsmash_bits_t *bits, - hevc_pps_t *pps, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - memset( pps, 0, sizeof(hevc_pps_t) ); - pps->pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - pps->seq_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - pps->dependent_slice_segments_enabled_flag = lsmash_bits_get( bits, 1 ); - pps->output_flag_present_flag = lsmash_bits_get( bits, 1 ); - pps->num_extra_slice_header_bits = lsmash_bits_get( bits, 3 ); - lsmash_bits_get( bits, 1 ); /* sign_data_hiding_enabled_flag */ - lsmash_bits_get( bits, 1 ); /* cabac_init_present_flag */ - nalu_get_exp_golomb_ue( bits ); /* num_ref_idx_l0_default_active_minus1 */ - nalu_get_exp_golomb_ue( bits ); /* num_ref_idx_l1_default_active_minus1 */ - nalu_get_exp_golomb_se( bits ); /* init_qp_minus26 */ - lsmash_bits_get( bits, 1 ); /* constrained_intra_pred_flag */ - lsmash_bits_get( bits, 1 ); /* transform_skip_enabled_flag */ - if( lsmash_bits_get( bits, 1 ) ) /* cu_qp_delta_enabled_flag */ - nalu_get_exp_golomb_ue( bits ); /* diff_cu_qp_delta_depth */ - nalu_get_exp_golomb_se( bits ); /* cb_qp_offset */ - nalu_get_exp_golomb_se( bits ); /* cr_qp_offset */ - lsmash_bits_get( bits, 1 ); /* slice_chroma_qp_offsets_present_flag */ - lsmash_bits_get( bits, 1 ); /* weighted_pred_flag */ - lsmash_bits_get( bits, 1 ); /* weighted_bipred_flag */ - lsmash_bits_get( bits, 1 ) /* transquant_bypass_enabled_flag */; - pps->tiles_enabled_flag = lsmash_bits_get( bits, 1 ); - pps->entropy_coding_sync_enabled_flag = lsmash_bits_get( bits, 1 ); - return bits->bs->error ? -1 : 0; -} - -int hevc_parse_pps -( - hevc_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - hevc_pps_t *pps; - { - /* Parse PPS minimally for configuration records. */ - hevc_pps_t min_pps; - if( hevc_parse_pps_minimally( bits, &min_pps, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - pps = hevc_get_pps( info->pps_list, min_pps.pic_parameter_set_id ); - if( !pps ) - return -1; - *pps = min_pps; - } - hevc_sps_t temp_sps = info->sps; - if( hevc_activate_sps( info, pps->seq_parameter_set_id ) < 0 ) - return -1; - hevc_sps_t *sps = &info->sps; - if( pps->tiles_enabled_flag ) - { - pps->num_tile_columns_minus1 = nalu_get_exp_golomb_ue( bits ); - pps->num_tile_rows_minus1 = nalu_get_exp_golomb_ue( bits ); - IF_INVALID_VALUE( pps->num_tile_columns_minus1 >= sps->PicWidthInCtbsY - || pps->num_tile_rows_minus1 >= sps->PicHeightInCtbsY ) - goto fail; - if( hevc_allocate_tile_sizes( pps, pps->num_tile_columns_minus1 + 1, pps->num_tile_rows_minus1 + 1 ) < 0 ) - goto fail; - if( lsmash_bits_get( bits, 1 ) ) /* uniform_spacing_flag */ - { - for( int i = 0; i <= pps->num_tile_columns_minus1; i++ ) - pps->colWidth[i] = ((i + 1) * sps->PicWidthInCtbsY) / (pps->num_tile_columns_minus1 + 1) - - ( i * sps->PicWidthInCtbsY) / (pps->num_tile_columns_minus1 + 1); - for( int j = 0; j <= pps->num_tile_rows_minus1; j++ ) - pps->rowHeight[j] = ((j + 1) * sps->PicHeightInCtbsY) / (pps->num_tile_rows_minus1 + 1) - - ( j * sps->PicHeightInCtbsY) / (pps->num_tile_rows_minus1 + 1); - } - else - { - pps->colWidth[ pps->num_tile_columns_minus1 ] = sps->PicWidthInCtbsY; - for( uint64_t i = 0; i < pps->num_tile_columns_minus1; i++ ) - { - pps->colWidth[i] = nalu_get_exp_golomb_ue( bits ) + 1; /* column_width_minus1[i] */ - pps->colWidth[ pps->num_tile_columns_minus1 ] -= pps->colWidth[i]; - } - pps->rowHeight[ pps->num_tile_rows_minus1 ] = sps->PicHeightInCtbsY; - for( uint64_t j = 0; j < pps->num_tile_rows_minus1; j++ ) - { - pps->rowHeight[j] = nalu_get_exp_golomb_ue( bits ) + 1; /* row_height_minus1 [j] */ - pps->rowHeight[ pps->num_tile_rows_minus1 ] -= pps->rowHeight[j]; - } - } - pps->colBd[0] = 0; - for( uint64_t i = 0; i < pps->num_tile_columns_minus1; i++ ) - pps->colBd[i + 1] = pps->colBd[i] + pps->colWidth[i]; - pps->rowBd[0] = 0; - for( uint64_t j = 0; j < pps->num_tile_rows_minus1; j++ ) - pps->rowBd[j + 1] = pps->rowBd[j] + pps->rowHeight[j]; - lsmash_bits_get( bits, 1 ); /* loop_filter_across_tiles_enabled_flag */ - } - else - { - pps->num_tile_columns_minus1 = 0; - pps->num_tile_rows_minus1 = 0; - if( hevc_allocate_tile_sizes( pps, 1, 1 ) < 0 ) - goto fail; - pps->colWidth [0] = sps->PicWidthInCtbsY; - pps->rowHeight[0] = sps->PicHeightInCtbsY; - pps->colBd [0] = 0; - pps->rowBd [0] = 0; - } - /* */ - /* Skip PPS extension. */ - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - pps->present = 1; - info->pps = *pps; - hevc_activate_vps( info, info->sps.video_parameter_set_id ); - return 0; -fail: - /* Revert SPS. */ - info->sps = temp_sps; - return 0; -} - -int hevc_parse_sei -( - lsmash_bits_t *bits, - hevc_vps_t *vps, - hevc_sps_t *sps, - hevc_sei_t *sei, - hevc_nalu_header_t *nalu_header, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, ebsp_size ) ) - return -1; - uint8_t *rbsp_start = rbsp_buffer; - uint64_t rbsp_pos = 0; - do - { - /* sei_message() */ - uint32_t payloadType = 0; - for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) - { - /* 0xff : ff_byte - * otherwise: last_payload_type_byte */ - payloadType += temp; - ++rbsp_pos; - if( temp != 0xff ) - break; - } - uint32_t payloadSize = 0; - for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) ) - { - /* 0xff : ff_byte - * otherwise: last_payload_size_byte */ - payloadSize += temp; - ++rbsp_pos; - if( temp != 0xff ) - break; - } - if( nalu_header->nal_unit_type == HEVC_NALU_TYPE_PREFIX_SEI ) - { - if( payloadType == 1 ) - { - /* pic_timing */ - hevc_hrd_t *hrd = sps ? &sps->vui.hrd : vps ? &vps->hrd[0] : NULL; - if( !hrd ) - goto skip_sei_message; /* Any active VPS or SPS is not found. */ - sei->pic_timing.present = 1; - if( (sps && sps->vui.frame_field_info_present_flag) || vps->frame_field_info_present_flag ) - { - sei->pic_timing.pic_struct = lsmash_bits_get( bits, 4 ); - lsmash_bits_get( bits, 2 ); /* source_scan_type */ - lsmash_bits_get( bits, 1 ); /* duplicate_flag */ - } - if( hrd->CpbDpbDelaysPresentFlag ) - { - lsmash_bits_get( bits, hrd->au_cpb_removal_delay_length ); /* au_cpb_removal_delay_minus1 */ - lsmash_bits_get( bits, hrd->dpb_output_delay_length ); /* pic_dpb_output_delay */ - if( hrd->sub_pic_hrd_params_present_flag ) - { - lsmash_bits_get( bits, hrd->dpb_output_delay_du_length ); /* pic_dpb_output_du_delay */ - if( hrd->sub_pic_cpb_params_in_pic_timing_sei_flag ) - { - uint64_t num_decoding_units_minus1 = nalu_get_exp_golomb_ue( bits ); - int du_common_cpb_removal_delay_flag = lsmash_bits_get( bits, 1 ); - if( du_common_cpb_removal_delay_flag ) - /* du_common_cpb_removal_delay_increment_minus1 */ - lsmash_bits_get( bits, hrd->du_cpb_removal_delay_increment_length ); - for( uint64_t i = 0; i <= num_decoding_units_minus1; i++ ) - { - nalu_get_exp_golomb_ue( bits ); /* num_nalus_in_du_minus1 */ - if( !du_common_cpb_removal_delay_flag && i < num_decoding_units_minus1 ) - nalu_get_exp_golomb_ue( bits ); /* du_cpb_removal_delay_increment_minus1 */ - } - } - } - } - } - else if( payloadType == 3 ) - { - /* filler_payload - * FIXME: remove if array_completeness equal to 1. */ - return -1; - } - else if( payloadType == 6 ) - { - /* recovery_point */ - sei->recovery_point.present = 1; - sei->recovery_point.recovery_poc_cnt = nalu_get_exp_golomb_ue( bits ); - lsmash_bits_get( bits, 1 ); /* exact_match_flag */ - sei->recovery_point.broken_link_flag = lsmash_bits_get( bits, 1 ); - } - else - goto skip_sei_message; - } - else if( nalu_header->nal_unit_type == HEVC_NALU_TYPE_SUFFIX_SEI ) - { - if( payloadType == 3 ) - { - /* filler_payload - * FIXME: remove if array_completeness equal to 1. */ - return -1; - } - else - goto skip_sei_message; - } - else - { -skip_sei_message: - lsmash_bits_get( bits, payloadSize * 8 ); - } - lsmash_bits_get_align( bits ); - rbsp_pos += payloadSize; - } while( *(rbsp_start + rbsp_pos) != 0x80 ); /* All SEI messages are byte aligned at their end. - * Therefore, 0x80 shall be rbsp_trailing_bits(). */ - lsmash_bits_empty( bits ); - return bits->bs->error ? -1 : 0; -} - -int hevc_parse_slice_segment_header -( - hevc_info_t *info, - hevc_nalu_header_t *nalu_header, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - lsmash_bits_t *bits = info->bits; - if( nalu_import_rbsp_from_ebsp( bits, rbsp_buffer, ebsp, LSMASH_MIN( ebsp_size, 50 ) ) ) - return -1; - hevc_slice_info_t *slice = &info->slice; - memset( slice, 0, sizeof(hevc_slice_info_t) ); - slice->nalu_type = nalu_header->nal_unit_type; - slice->TemporalId = nalu_header->TemporalId; - slice->first_slice_segment_in_pic_flag = lsmash_bits_get( bits, 1 ); - if( nalu_header->nal_unit_type >= HEVC_NALU_TYPE_BLA_W_LP - && nalu_header->nal_unit_type <= HEVC_NALU_TYPE_RSV_IRAP_VCL23 ) - lsmash_bits_get( bits, 1 ); /* no_output_of_prior_pics_flag */ - slice->pic_parameter_set_id = nalu_get_exp_golomb_ue( bits ); - /* Get PPS by slice_pic_parameter_set_id. */ - hevc_pps_t *pps = hevc_get_pps( info->pps_list, slice->pic_parameter_set_id ); - if( !pps ) - return -1; - /* Get SPS by pps_seq_parameter_set_id. */ - hevc_sps_t *sps = hevc_get_sps( info->sps_list, pps->seq_parameter_set_id ); - if( !sps ) - return -1; - slice->video_parameter_set_id = sps->video_parameter_set_id; - slice->seq_parameter_set_id = pps->seq_parameter_set_id; - if( !slice->first_slice_segment_in_pic_flag ) - { - slice->dependent_slice_segment_flag = pps->dependent_slice_segments_enabled_flag ? lsmash_bits_get( bits, 1 ) : 0; - slice->segment_address = lsmash_bits_get( bits, lsmash_ceil_log2( sps->PicSizeInCtbsY ) ); - } - else - { - slice->dependent_slice_segment_flag = 0; - slice->segment_address = 0; - } - if( !slice->dependent_slice_segment_flag ) - { - /* independent slice segment - * The values of the slice segment header of dependent slice segment are inferred from the values - * for the preceding independent slice segment in decoding order, if some of the values are not present. */ - for( int i = 0; i < pps->num_extra_slice_header_bits; i++ ) - lsmash_bits_get( bits, 1 ); /* slice_reserved_flag[i] */ - slice->type = nalu_get_exp_golomb_ue( bits ); - if( pps->output_flag_present_flag ) - lsmash_bits_get( bits, 1 ); /* pic_output_flag */ - if( sps->separate_colour_plane_flag ) - lsmash_bits_get( bits, 1 ); /* colour_plane_id */ - if( nalu_header->nal_unit_type != HEVC_NALU_TYPE_IDR_W_RADL - && nalu_header->nal_unit_type != HEVC_NALU_TYPE_IDR_N_LP ) - { - slice->pic_order_cnt_lsb = lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); - if( !lsmash_bits_get( bits, 1 ) ) /* short_term_ref_pic_set_sps_flag */ - { - if( hevc_short_term_ref_pic_set( bits, sps, sps->num_short_term_ref_pic_sets ) < 0 ) - return -1; - } - else - { - int length = lsmash_ceil_log2( sps->num_short_term_ref_pic_sets ); - if( length > 0 ) - lsmash_bits_get( bits, length ); /* short_term_ref_pic_set_idx */ - } - if( sps->long_term_ref_pics_present_flag ) - { - uint64_t num_long_term_sps = sps->num_long_term_ref_pics_sps > 0 ? nalu_get_exp_golomb_ue( bits ) : 0; - uint64_t num_long_term_pics = nalu_get_exp_golomb_ue( bits ); - for( uint64_t i = 0; i < num_long_term_sps + num_long_term_pics; i++ ) - { - if( i < num_long_term_sps ) - { - int length = lsmash_ceil_log2( sps->num_long_term_ref_pics_sps ); - if( length > 0 ) - lsmash_bits_get( bits, length ); /* lt_idx_sps[i] */ - } - else - { - lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb ); /* poc_lsb_lt [i] */ - lsmash_bits_get( bits, 1 ); /* used_by_curr_pic_lt_flag[i] */ - } - if( lsmash_bits_get( bits, 1 ) ) /* delta_poc_msb_present_flag[i] */ - nalu_get_exp_golomb_ue( bits ); /* delta_poc_msb_cycle_lt [i] */ - } - } - if( sps->temporal_mvp_enabled_flag ) - lsmash_bits_get( bits, 1 ); /* slice_temporal_mvp_enabled_flag */ - } - else - /* For IDR-pictures, slice_pic_order_cnt_lsb is inferred to be 0. */ - slice->pic_order_cnt_lsb = 0; - } - lsmash_bits_empty( bits ); - if( bits->bs->error ) - return -1; - info->sps = *sps; - info->pps = *pps; - return 0; -} - -static int hevc_get_vps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id -) -{ - /* the number of bits of vps_id = 4 - * (4 - 1) / 8 + 1 = 1 bytes */ - *ps_id = (*ps_ebsp >> 4) & 0x0F; /* vps_video_parameter_set_id */ - return 0; -} - -static int hevc_get_sps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id -) -{ - /* the maximum number of bits of sps_id = 9: 0b00001XXXX - * (8 + 688 + 9 - 1) / 8 + 1 = 89 bytes - * Here more additional bytes because there might be emulation_prevention_three_byte(s). */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[128]; - uint8_t buffer [128]; - bs.buffer.data = buffer; - bs.buffer.alloc = 128; - lsmash_bits_init( &bits, &bs ); - if( nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 128 ) ) ) - return -1; - /* Skip sps_video_parameter_set_id and sps_temporal_id_nesting_flag. */ - uint8_t sps_max_sub_layers_minus1 = (lsmash_bits_get( &bits, 8 ) >> 1) & 0x07; - /* profile_tier_level() costs at most 688 bits. */ - hevc_ptl_t sps_ptl; - hevc_parse_profile_tier_level( &bits, &sps_ptl, sps_max_sub_layers_minus1 ); - uint64_t sps_seq_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); - IF_INVALID_VALUE( sps_seq_parameter_set_id > HEVC_MAX_SPS_ID ) - return -1; - *ps_id = sps_seq_parameter_set_id; - return bs.error ? -1 : 0; -} - -static int hevc_get_pps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id -) -{ - /* the maximum number of bits of pps_id = 13: 0b0000001XXXXXX - * (13 - 1) / 8 + 1 = 2 bytes - * Why +1? Because there might be an emulation_prevention_three_byte. */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[3]; - uint8_t buffer [3]; - bs.buffer.data = buffer; - bs.buffer.alloc = 3; - lsmash_bits_init( &bits, &bs ); - if( nalu_import_rbsp_from_ebsp( &bits, rbsp_buffer, ps_ebsp, LSMASH_MIN( ps_ebsp_length, 3 ) ) ) - return -1; - uint64_t pic_parameter_set_id = nalu_get_exp_golomb_ue( &bits ); - IF_INVALID_VALUE( pic_parameter_set_id > HEVC_MAX_PPS_ID ) - return -1; - *ps_id = pic_parameter_set_id; - return bs.error ? -1 : 0; -} - -static inline int hevc_get_ps_id -( - uint8_t *ps_ebsp, - uint32_t ps_ebsp_length, - uint8_t *ps_id, - lsmash_hevc_dcr_nalu_type ps_type -) -{ - int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) - = ps_type == HEVC_DCR_NALU_TYPE_VPS ? hevc_get_vps_id - : ps_type == HEVC_DCR_NALU_TYPE_SPS ? hevc_get_sps_id - : ps_type == HEVC_DCR_NALU_TYPE_PPS ? hevc_get_pps_id - : NULL; - return get_ps_id ? get_ps_id( ps_ebsp, ps_ebsp_length, ps_id ) : -1; -} - -static inline hevc_parameter_array_t *hevc_get_parameter_set_array -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type -) -{ - if( !param->parameter_arrays ) - return NULL; - if( ps_type >= HEVC_DCR_NALU_TYPE_NUM ) - return NULL; - return ¶m->parameter_arrays->ps_array[ps_type]; -} - -static inline lsmash_entry_list_t *hevc_get_parameter_set_list -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type -) -{ - if( !param->parameter_arrays ) - return NULL; - if( ps_type >= HEVC_DCR_NALU_TYPE_NUM ) - return NULL; - return param->parameter_arrays->ps_array[ps_type].list; -} - -static lsmash_entry_t *hevc_get_ps_entry_from_param -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type, - uint8_t ps_id -) -{ - int (*get_ps_id)( uint8_t *ps_ebsp, uint32_t ps_ebsp_length, uint8_t *ps_id ) - = ps_type == HEVC_DCR_NALU_TYPE_VPS ? hevc_get_vps_id - : ps_type == HEVC_DCR_NALU_TYPE_SPS ? hevc_get_sps_id - : ps_type == HEVC_DCR_NALU_TYPE_PPS ? hevc_get_pps_id - : NULL; - if( !get_ps_id ) - return NULL; - lsmash_entry_list_t *list = hevc_get_parameter_set_list( param, ps_type ); - if( !list ) - return NULL; - for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return NULL; - uint8_t param_ps_id; - if( get_ps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_ps_id ) ) - return NULL; - if( ps_id == param_ps_id ) - return entry; - } - return NULL; -} - -static inline void hevc_update_picture_type -( - hevc_picture_info_t *picture, - hevc_slice_info_t *slice -) -{ - if( picture->type == HEVC_PICTURE_TYPE_I_P ) - { - if( slice->type == HEVC_SLICE_TYPE_B ) - picture->type = HEVC_PICTURE_TYPE_I_P_B; - } - else if( picture->type == HEVC_PICTURE_TYPE_I ) - { - if( slice->type == HEVC_SLICE_TYPE_P ) - picture->type = HEVC_PICTURE_TYPE_I_P; - else if( slice->type == HEVC_SLICE_TYPE_B ) - picture->type = HEVC_PICTURE_TYPE_I_P_B; - } - else if( picture->type == HEVC_PICTURE_TYPE_NONE ) - { - if( slice->type == HEVC_SLICE_TYPE_P ) - picture->type = HEVC_PICTURE_TYPE_I_P; - else if( slice->type == HEVC_SLICE_TYPE_B ) - picture->type = HEVC_PICTURE_TYPE_I_P_B; - else if( slice->type == HEVC_SLICE_TYPE_I ) - picture->type = HEVC_PICTURE_TYPE_I; - } -#if 0 - fprintf( stderr, "Picture type = %s\n", picture->type == HEVC_PICTURE_TYPE_I_P ? "P" - : picture->type == HEVC_PICTURE_TYPE_I_P_B ? "B" - : picture->type == HEVC_PICTURE_TYPE_I ? "I" ); -#endif -} - -/* Shall be called at least once per picture. */ -void hevc_update_picture_info_for_slice -( - hevc_info_t *info, - hevc_picture_info_t *picture, - hevc_slice_info_t *slice -) -{ - assert( info ); - picture->has_primary |= !slice->dependent_slice_segment_flag; - hevc_update_picture_type( picture, slice ); - /* Mark 'used' on active parameter sets. */ - uint8_t ps_id[3] = { slice->video_parameter_set_id, slice->seq_parameter_set_id, slice->pic_parameter_set_id }; - for( int i = 0; i < 3; i++ ) - { - lsmash_hevc_dcr_nalu_type ps_type = (lsmash_hevc_dcr_nalu_type)i; - lsmash_entry_t *entry = hevc_get_ps_entry_from_param( &info->hvcC_param, ps_type, ps_id[i] ); - if( entry && entry->data ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( ps->unused ) - lsmash_append_hevc_dcr_nalu( &info->hvcC_param, ps_type, ps->nalUnit, ps->nalUnitLength ); - } - } - /* Discard this slice info. */ - slice->present = 0; -} - -/* Shall be called exactly once per picture. */ -void hevc_update_picture_info -( - hevc_info_t *info, - hevc_picture_info_t *picture, - hevc_slice_info_t *slice, - hevc_sps_t *sps, - hevc_sei_t *sei -) -{ - picture->irap = slice->nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && slice->nalu_type <= HEVC_NALU_TYPE_CRA; - picture->idr = slice->nalu_type == HEVC_NALU_TYPE_IDR_W_RADL || slice->nalu_type == HEVC_NALU_TYPE_IDR_N_LP; - picture->broken_link = slice->nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && slice->nalu_type <= HEVC_NALU_TYPE_BLA_N_LP; - picture->radl = slice->nalu_type == HEVC_NALU_TYPE_RADL_N || slice->nalu_type == HEVC_NALU_TYPE_RADL_R; - picture->rasl = slice->nalu_type == HEVC_NALU_TYPE_RASL_N || slice->nalu_type == HEVC_NALU_TYPE_RASL_R; - picture->sublayer_nonref = slice->nalu_type <= HEVC_NALU_TYPE_RSV_VCL_R15 && ((slice->nalu_type & 0x01) == 0); - picture->closed_rap = slice->nalu_type >= HEVC_NALU_TYPE_BLA_W_RADL && slice->nalu_type <= HEVC_NALU_TYPE_IDR_N_LP; - picture->random_accessible = picture->irap; - picture->TemporalId = slice->TemporalId; - picture->pic_parameter_set_id = slice->pic_parameter_set_id; - picture->poc_lsb = slice->pic_order_cnt_lsb; - hevc_update_picture_info_for_slice( info, picture, slice ); - picture->independent = (picture->type == HEVC_PICTURE_TYPE_I); - picture->field_coded = sps->vui.field_seq_flag; - if( sei->pic_timing.present ) - { - if( sei->pic_timing.pic_struct < 13 ) - { - static const uint8_t delta[13] = { 2, 1, 1, 2, 2, 3, 3, 4, 6, 1, 1, 1, 1 }; - picture->delta = delta[ sei->pic_timing.pic_struct ]; - } - else - /* Reserved values in the spec we refer to. */ - picture->delta = picture->field_coded ? 1 : 2; - sei->pic_timing.present = 0; - } - else - picture->delta = picture->field_coded ? 1 : 2; - if( sei->recovery_point.present ) - { - picture->random_accessible |= sei->recovery_point.present; - picture->recovery_poc_cnt = sei->recovery_point.recovery_poc_cnt; - picture->broken_link |= sei->recovery_point.broken_link_flag; - sei->recovery_point.present = 0; - } - else - picture->recovery_poc_cnt = 0; -} - -static uint64_t hevc_get_ctb_address_in_tile_scan -( - hevc_sps_t *sps, - hevc_pps_t *pps, - uint64_t segment_address, - uint64_t *TileId -) -{ - uint64_t tbX = segment_address % sps->PicWidthInCtbsY; - uint64_t tbY = segment_address / sps->PicWidthInCtbsY; - uint32_t tileX = pps->num_tile_columns_minus1; - for( uint32_t i = 0; i <= pps->num_tile_columns_minus1; i++ ) - if( tbX >= pps->colBd[i] ) - tileX = i; - uint32_t tileY = pps->num_tile_rows_minus1; - for( uint32_t j = 0; j <= pps->num_tile_rows_minus1; j++ ) - if( tbY >= pps->rowBd[j] ) - tileY = j; - uint64_t CtbAddrInTs = 0; - for( uint32_t i = 0; i < tileX; i++ ) - CtbAddrInTs += pps->rowHeight[tileY] * pps->colWidth[i]; - for( uint32_t j = 0; j < tileY; j++ ) - CtbAddrInTs += sps->PicWidthInCtbsY * pps->rowHeight[j]; - CtbAddrInTs += (tbY - pps->rowBd[tileY]) * pps->colWidth[tileX] + tbX - pps->colBd[tileX]; - *TileId = (uint64_t)tileY * (pps->num_tile_columns_minus1 + 1) + tileX; - return CtbAddrInTs; -} - -int hevc_find_au_delimit_by_slice_info -( - hevc_info_t *info, - hevc_slice_info_t *slice, - hevc_slice_info_t *prev_slice -) -{ - /* 7.4.2.4.5 Order of VCL NAL units and association to coded pictures - * - The first VCL NAL unit of the coded picture shall have first_slice_segment_in_pic_flag equal to 1. */ - if( slice->first_slice_segment_in_pic_flag ) - return 1; - /* The value of TemporalId shall be the same for all VCL NAL units of an access unit. */ - if( slice->TemporalId != prev_slice->TemporalId ) - return 1; - /* 7.4.2.4.5 Order of VCL NAL units and association to coded pictures - * - if( TileId[ CtbAddrRsToTs[ slice->segment_address ] ] <= TileId[ CtbAddrRsToTs[ prev_slice->segment_address ] ] - * || CtbAddrRsToTs[ slice->segment_address ] <= CtbAddrRsToTs[ prev_slice->segment_address ] ) - * return 1; - */ - hevc_pps_t *prev_pps = hevc_get_pps( info->pps_list, prev_slice->pic_parameter_set_id ); - if( !prev_pps ) - return 0; - hevc_sps_t *prev_sps = hevc_get_sps( info->sps_list, prev_pps->seq_parameter_set_id ); - if( !prev_sps ) - return 0; - uint64_t currTileId; - uint64_t prevTileId; - uint64_t currCtbAddrInTs = hevc_get_ctb_address_in_tile_scan( &info->sps, &info->pps, slice->segment_address, &currTileId ); - uint64_t prevCtbAddrInTs = hevc_get_ctb_address_in_tile_scan( prev_sps, prev_pps, prev_slice->segment_address, &prevTileId ); - if( currTileId <= prevTileId - || currCtbAddrInTs <= prevCtbAddrInTs ) - return 1; - return 0; -} - -int hevc_find_au_delimit_by_nalu_type -( - uint8_t nalu_type, - uint8_t prev_nalu_type -) -{ - return (prev_nalu_type <= HEVC_NALU_TYPE_RSV_VCL31) - && ((nalu_type >= HEVC_NALU_TYPE_VPS && nalu_type <= HEVC_NALU_TYPE_AUD) - || nalu_type == HEVC_NALU_TYPE_PREFIX_SEI - || (nalu_type >= HEVC_NALU_TYPE_RSV_NVCL41 && nalu_type <= HEVC_NALU_TYPE_RSV_NVCL44) - || (nalu_type >= HEVC_NALU_TYPE_UNSPEC48 && nalu_type <= HEVC_NALU_TYPE_UNSPEC55)); -} - -int hevc_supplement_buffer -( - hevc_stream_buffer_t *hb, - hevc_access_unit_t *au, - uint32_t size -) -{ - lsmash_stream_buffers_t *sb = hb->sb; - uint32_t buffer_pos_offset = sb->pos - sb->start; - uint32_t buffer_valid_length = sb->end - sb->start; - lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, size ); - if( !bank ) - return -1; - sb->bank = bank; - sb->start = lsmash_withdraw_buffer( bank, 1 ); - hb->rbsp = lsmash_withdraw_buffer( bank, 2 ); - sb->pos = sb->start + buffer_pos_offset; - sb->end = sb->start + buffer_valid_length; - if( au && bank->number_of_buffers == 4 ) - { - au->data = lsmash_withdraw_buffer( bank, 3 ); - au->incomplete_data = lsmash_withdraw_buffer( bank, 4 ); - } - return 0; -} - -static void hevc_bs_put_parameter_sets -( - lsmash_bs_t *bs, - lsmash_entry_list_t *dcr_ps_list, - uint32_t max_dcr_ps_count -) -{ - uint32_t dcr_ps_count = 0; - for( lsmash_entry_t *entry = dcr_ps_list->head; entry && dcr_ps_count < max_dcr_ps_count; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( ps && !ps->unused ) - { - lsmash_bs_put_be16( bs, ps->nalUnitLength ); - lsmash_bs_put_bytes( bs, ps->nalUnitLength, ps->nalUnit ); - } - else - continue; - ++dcr_ps_count; - } -} - -uint8_t *lsmash_create_hevc_specific_info -( - lsmash_hevc_specific_parameters_t *param, - uint32_t *data_length -) -{ - if( !param || !param->parameter_arrays || !data_length ) - return NULL; - if( param->lengthSizeMinusOne != 0 - && param->lengthSizeMinusOne != 1 - && param->lengthSizeMinusOne != 3 ) - return NULL; - hevc_parameter_array_t *param_arrays[HEVC_DCR_NALU_TYPE_NUM]; - lsmash_entry_list_t *dcr_ps_list [HEVC_DCR_NALU_TYPE_NUM]; - for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) - { - param_arrays[i] = ¶m->parameter_arrays->ps_array[i]; - dcr_ps_list [i] = param_arrays[i]->list; - } - /* VPS, SPS and PPS are mandatory. */ - if( !dcr_ps_list[0] || !dcr_ps_list[0]->head || dcr_ps_list[0]->entry_count == 0 - || !dcr_ps_list[1] || !dcr_ps_list[1]->head || dcr_ps_list[1]->entry_count == 0 - || !dcr_ps_list[2] || !dcr_ps_list[2]->head || dcr_ps_list[2]->entry_count == 0 ) - return NULL; - /* Calculate enough buffer size. */ - static const uint32_t max_dcr_ps_count[HEVC_DCR_NALU_TYPE_NUM] = - { - HEVC_MAX_VPS_ID + 1, - HEVC_MAX_SPS_ID + 1, - HEVC_MAX_PPS_ID + 1, - UINT16_MAX, - UINT16_MAX - }; - uint32_t ps_count[HEVC_DCR_NALU_TYPE_NUM] = { 0 }; - uint32_t buffer_size = ISOM_BASEBOX_COMMON_SIZE + 23; - for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) - if( dcr_ps_list[i] ) - { - for( lsmash_entry_t *entry = dcr_ps_list[i]->head; entry && ps_count[i] < max_dcr_ps_count[i]; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return NULL; - if( ps->unused ) - continue; - buffer_size += 2 + ps->nalUnitLength; - ++ps_count[i]; - } - buffer_size += 3; - } - /* Set up bytestream writer. */ - uint8_t buffer[buffer_size]; - lsmash_bs_t bs = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = buffer_size; - /* Create an HEVCConfigurationBox */ - lsmash_bs_put_be32( &bs, 0 ); /* box size */ - lsmash_bs_put_be32( &bs, ISOM_BOX_TYPE_HVCC.fourcc ); /* box type: 'hvcC' */ - lsmash_bs_put_byte( &bs, HVCC_CONFIGURATION_VERSION ); /* configurationVersion */ - uint8_t temp8 = (param->general_profile_space << 6) - | (param->general_tier_flag << 5) - | param->general_profile_idc; - lsmash_bs_put_byte( &bs, temp8 ); - lsmash_bs_put_be32( &bs, param->general_profile_compatibility_flags ); - lsmash_bs_put_be32( &bs, param->general_constraint_indicator_flags >> 16 ); - lsmash_bs_put_be16( &bs, param->general_constraint_indicator_flags ); - lsmash_bs_put_byte( &bs, param->general_level_idc ); - lsmash_bs_put_be16( &bs, param->min_spatial_segmentation_idc | 0xF000 ); - lsmash_bs_put_byte( &bs, param->parallelismType | 0xFC ); - lsmash_bs_put_byte( &bs, param->chromaFormat | 0xFC ); - lsmash_bs_put_byte( &bs, param->bitDepthLumaMinus8 | 0xF8 ); - lsmash_bs_put_byte( &bs, param->bitDepthChromaMinus8 | 0xF8 ); - lsmash_bs_put_be16( &bs, param->avgFrameRate ); - temp8 = (param->constantFrameRate << 6) - | (param->numTemporalLayers << 3) - | (param->temporalIdNested << 2) - | param->lengthSizeMinusOne; - lsmash_bs_put_byte( &bs, temp8 ); - uint8_t numOfArrays = !!ps_count[0] - + !!ps_count[1] - + !!ps_count[2] - + !!ps_count[3] - + !!ps_count[4]; - lsmash_bs_put_byte( &bs, numOfArrays ); - for( uint8_t i = 0; i < numOfArrays; i++ ) - { - temp8 = (param_arrays[i]->array_completeness << 7) | param_arrays[i]->NAL_unit_type; - lsmash_bs_put_byte( &bs, temp8 ); - lsmash_bs_put_be16( &bs, ps_count[i] ); - hevc_bs_put_parameter_sets( &bs, dcr_ps_list[i], ps_count[i] ); - } - uint8_t *data = lsmash_bs_export_data( &bs, data_length ); - /* Update box size. */ - data[0] = ((*data_length) >> 24) & 0xff; - data[1] = ((*data_length) >> 16) & 0xff; - data[2] = ((*data_length) >> 8) & 0xff; - data[3] = (*data_length) & 0xff; - return data; -} - -static inline int hevc_validate_dcr_nalu_type -( - lsmash_hevc_dcr_nalu_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - if( !ps_data || ps_length < 3 ) - return -1; - if( ps_type != HEVC_DCR_NALU_TYPE_VPS - && ps_type != HEVC_DCR_NALU_TYPE_SPS - && ps_type != HEVC_DCR_NALU_TYPE_PPS - && ps_type != HEVC_DCR_NALU_TYPE_PREFIX_SEI - && ps_type != HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) - return -1; - uint8_t nalu_type = (*((uint8_t *)ps_data) >> 1) & 0x3f; - if( nalu_type != HEVC_NALU_TYPE_VPS - && nalu_type != HEVC_NALU_TYPE_SPS - && nalu_type != HEVC_NALU_TYPE_PPS - && nalu_type != HEVC_NALU_TYPE_PREFIX_SEI - && nalu_type != HEVC_NALU_TYPE_SUFFIX_SEI ) - return -1; - if( (ps_type == HEVC_DCR_NALU_TYPE_VPS && nalu_type != HEVC_NALU_TYPE_VPS) - || (ps_type == HEVC_DCR_NALU_TYPE_SPS && nalu_type != HEVC_NALU_TYPE_SPS) - || (ps_type == HEVC_DCR_NALU_TYPE_PPS && nalu_type != HEVC_NALU_TYPE_PPS) - || (ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI && nalu_type != HEVC_NALU_TYPE_PREFIX_SEI) - || (ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI && nalu_type != HEVC_NALU_TYPE_SUFFIX_SEI) ) - return -1; - return 0; -} - -lsmash_dcr_nalu_appendable lsmash_check_hevc_dcr_nalu_appendable -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - if( !param ) - return DCR_NALU_APPEND_ERROR; - if( hevc_validate_dcr_nalu_type( ps_type, ps_data, ps_length ) ) - return DCR_NALU_APPEND_ERROR; - /* Check whether the same parameter set already exsits or not. */ - lsmash_entry_list_t *ps_list = hevc_get_parameter_set_list( param, ps_type ); - if( !ps_list || !ps_list->head ) - return DCR_NALU_APPEND_POSSIBLE; /* No parameter set */ - switch( nalu_check_same_ps_existence( ps_list, ps_data, ps_length ) ) - { - case 0 : break; - case 1 : return DCR_NALU_APPEND_DUPLICATED; /* The same parameter set already exists. */ - default : return DCR_NALU_APPEND_ERROR; /* An error occured. */ - } - /* Check the number of parameter sets in HEVC Decoder Configuration Record. */ - uint32_t ps_count; - if( nalu_get_ps_count( ps_list, &ps_count ) ) - return DCR_NALU_APPEND_ERROR; - if( (ps_type == HEVC_DCR_NALU_TYPE_VPS && ps_count >= HEVC_MAX_VPS_ID) - || (ps_type == HEVC_DCR_NALU_TYPE_SPS && ps_count >= HEVC_MAX_SPS_ID) - || (ps_type == HEVC_DCR_NALU_TYPE_PPS && ps_count >= HEVC_MAX_PPS_ID) - || (ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI && ps_count >= UINT16_MAX) - || (ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI && ps_count >= UINT16_MAX) ) - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; /* No more appendable parameter sets. */ - if( ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI - || ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) - return DCR_NALU_APPEND_POSSIBLE; - /* Check the maximum length of parameter sets in HEVC Decoder Configuration Record. */ - uint32_t max_ps_length; - if( nalu_get_max_ps_length( ps_list, &max_ps_length ) ) - return DCR_NALU_APPEND_ERROR; - max_ps_length = LSMASH_MAX( max_ps_length, ps_length ); - /* Check whether a new specific info is needed or not. */ - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[max_ps_length]; - uint8_t bs_buffer [max_ps_length]; - bs.buffer.data = bs_buffer; - bs.buffer.alloc = max_ps_length; - lsmash_bits_init( &bits, &bs ); - if( ps_type == HEVC_DCR_NALU_TYPE_PPS ) - { - /* PPS */ - uint8_t pps_id; - if( hevc_get_pps_id( ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH, &pps_id ) ) - return DCR_NALU_APPEND_ERROR; - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return DCR_NALU_APPEND_ERROR; - if( ps->unused ) - continue; - uint8_t param_pps_id; - if( hevc_get_pps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_pps_id ) ) - return DCR_NALU_APPEND_ERROR; - if( pps_id == param_pps_id ) - /* PPS that has the same pic_parameter_set_id already exists with different form. */ - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - } - return DCR_NALU_APPEND_POSSIBLE; - } - else if( ps_type == HEVC_DCR_NALU_TYPE_VPS ) - { - /* VPS */ - hevc_vps_t vps; - if( hevc_parse_vps_minimally( &bits, &vps, rbsp_buffer, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) ) - return DCR_NALU_APPEND_ERROR; - /* The value of profile_space must be identical in all the parameter sets in a single HEVC Decoder Configuration Record. */ - if( vps.ptl.general.profile_space != param->general_profile_space ) - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - /* FIXME */ - if( vps.ptl.general.profile_idc != param->general_profile_idc ) - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return DCR_NALU_APPEND_ERROR; - if( ps->unused ) - continue; - uint8_t param_vps_id; - if( hevc_get_vps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_vps_id ) ) - return DCR_NALU_APPEND_ERROR; - if( param_vps_id == vps.video_parameter_set_id ) - /* VPS that has the same video_parameter_set_id already exists with different form. */ - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - } - return DCR_NALU_APPEND_POSSIBLE; - } - /* SPS */ - hevc_sps_t sps; - if( hevc_parse_sps_minimally( &bits, &sps, rbsp_buffer, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) ) - return DCR_NALU_APPEND_ERROR; - lsmash_bits_empty( &bits ); - /* The values of profile_space, chromaFormat, bitDepthLumaMinus8 and bitDepthChromaMinus8 - * must be identical in all the parameter sets in a single HEVC Decoder Configuration Record. */ - if( sps.ptl.general.profile_space != param->general_profile_space - || sps.chroma_format_idc != param->chromaFormat - || sps.bit_depth_luma_minus8 != param->bitDepthLumaMinus8 - || sps.bit_depth_chroma_minus8 != param->bitDepthChromaMinus8 ) - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - /* FIXME; If the sequence parameter sets are marked with different profiles, - * and the relevant profile compatibility flags are all zero, - * then the stream may need examination to determine which profile, if any, the stream conforms to. - * If the stream is not examined, or the examination reveals that there is no profile to which the stream conforms, - * then the stream must be split into two or more sub-streams with separate configuration records in which these rules can be met. */ -#if 0 - if( sps.ptl.general.profile_idc != param->general_profile_idc - && (sps.ptl.general.profile_compatibility_flags & param->general_profile_compatibility_flags) ) -#else - if( sps.ptl.general.profile_idc != param->general_profile_idc ) -#endif - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - /* Forbidden to duplicate SPS that has the same seq_parameter_set_id with different form within the same configuration record. */ - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return DCR_NALU_APPEND_ERROR; - if( ps->unused ) - continue; - uint8_t param_sps_id; - if( hevc_get_sps_id( ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, ¶m_sps_id ) ) - return DCR_NALU_APPEND_ERROR; - if( param_sps_id == sps.seq_parameter_set_id ) - /* SPS that has the same seq_parameter_set_id already exists with different form. */ - return DCR_NALU_APPEND_NEW_DCR_REQUIRED; - if( entry == ps_list->head ) - { - /* Check if the cropped visual presentation sizes, the sample aspect ratios, the colour descriptions and - * the default display windows are different. */ - hevc_sps_t first_sps; - if( hevc_parse_sps_minimally( &bits, &first_sps, rbsp_buffer, - ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH ) ) - return DCR_NALU_APPEND_ERROR; - if( sps.cropped_width != first_sps.cropped_width - || sps.cropped_height != first_sps.cropped_height - || sps.vui.sar_width != first_sps.vui.sar_width - || sps.vui.sar_height != first_sps.vui.sar_height - || sps.vui.colour_primaries != first_sps.vui.colour_primaries - || sps.vui.transfer_characteristics != first_sps.vui.transfer_characteristics - || sps.vui.matrix_coeffs != first_sps.vui.matrix_coeffs - || sps.vui.video_full_range_flag != first_sps.vui.video_full_range_flag - || sps.vui.def_disp_win_offset.left .n != first_sps.vui.def_disp_win_offset.left .n - || sps.vui.def_disp_win_offset.right .n != first_sps.vui.def_disp_win_offset.right .n - || sps.vui.def_disp_win_offset.top .n != first_sps.vui.def_disp_win_offset.top .n - || sps.vui.def_disp_win_offset.bottom.n != first_sps.vui.def_disp_win_offset.bottom.n ) - return DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED; - } - } - return DCR_NALU_APPEND_POSSIBLE; -} - -static inline void hevc_specific_parameters_ready -( - lsmash_hevc_specific_parameters_t *param -) -{ - param->general_profile_compatibility_flags = ~0UL; - param->general_constraint_indicator_flags = 0x0000FFFFFFFFFFFF; - param->min_spatial_segmentation_idc = 0x0FFF; - param->avgFrameRate = 0; /* unspecified average frame rate */ - param->constantFrameRate = 2; - param->numTemporalLayers = 0; - param->temporalIdNested = 1; -} - -static inline void hevc_specific_parameters_update_ptl -( - lsmash_hevc_specific_parameters_t *param, - hevc_ptl_t *ptl -) -{ - param->general_profile_space = ptl->general.profile_space; - param->general_tier_flag = LSMASH_MAX( param->general_tier_flag, ptl->general.tier_flag ); - param->general_profile_idc = ptl->general.profile_idc; - param->general_profile_compatibility_flags &= ptl->general.profile_compatibility_flags; - param->general_constraint_indicator_flags &= ((uint64_t)ptl->general.progressive_source_flag << 47) - | ((uint64_t)ptl->general.interlaced_source_flag << 46) - | ((uint64_t)ptl->general.non_packed_constraint_flag << 45) - | ((uint64_t)ptl->general.frame_only_constraint_flag << 44) - | ptl->general.reserved_zero_44bits; - param->general_level_idc = LSMASH_MAX( param->general_level_idc, ptl->general.level_idc ); -} - -static inline void hevc_reorder_parameter_set_ascending_id -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type, - lsmash_entry_list_t *ps_list, - uint8_t ps_id -) -{ - lsmash_entry_t *entry = NULL; - if( ps_id ) - for( int i = ps_id - 1; i; i-- ) - { - entry = hevc_get_ps_entry_from_param( param, ps_type, i ); - if( entry ) - break; - } - int append_head = 0; - if( !entry ) - { - /* Couldn't find any parameter set with lower identifier. - * Next, find parameter set with upper identifier. */ - int max_ps_id = ps_type == HEVC_DCR_NALU_TYPE_VPS ? HEVC_MAX_VPS_ID - : ps_type == HEVC_DCR_NALU_TYPE_SPS ? HEVC_MAX_SPS_ID - : HEVC_MAX_PPS_ID; - for( int i = ps_id + 1; i <= max_ps_id; i++ ) - { - entry = hevc_get_ps_entry_from_param( param, ps_type, i ); - if( entry ) - break; - } - if( entry ) - append_head = 1; - } - if( !entry ) - return; /* The new entry was appended to the tail. */ - lsmash_entry_t *new_entry = ps_list->tail; - if( append_head ) - { - /* before: entry[i > ps_id] ... -> prev_entry -> new_entry[ps_id] - * after: new_entry[ps_id] -> entry[i > ps_id] -> ... -> prev_entry */ - if( new_entry->prev ) - new_entry->prev->next = NULL; - new_entry->prev = NULL; - entry->prev = new_entry; - new_entry->next = entry; - return; - } - /* before: entry[i < ps_id] -> next_entry -> ... -> prev_entry -> new_entry[ps_id] - * after: entry[i < ps_id] -> new_entry[ps_id] -> next_entry -> ... -> prev_entry */ - if( new_entry->prev ) - new_entry->prev->next = NULL; - new_entry->prev = entry; - new_entry->next = entry->next; - if( entry->next ) - entry->next->prev = new_entry; - entry->next = new_entry; -} - -static inline int hevc_alloc_parameter_arrays -( - lsmash_hevc_specific_parameters_t *param -) -{ - assert( param ); - if( param->parameter_arrays ) - return 0; - lsmash_hevc_parameter_arrays_t *parameter_arrays = lsmash_malloc_zero( sizeof(lsmash_hevc_parameter_arrays_t) ); - if( !parameter_arrays ) - return -1; - param->parameter_arrays = parameter_arrays; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_VPS ].array_completeness = 1; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_VPS ].NAL_unit_type = HEVC_NALU_TYPE_VPS; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SPS ].array_completeness = 1; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SPS ].NAL_unit_type = HEVC_NALU_TYPE_SPS; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PPS ].array_completeness = 1; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PPS ].NAL_unit_type = HEVC_NALU_TYPE_PPS; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PREFIX_SEI].array_completeness = 0; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PREFIX_SEI].NAL_unit_type = HEVC_NALU_TYPE_PREFIX_SEI; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SUFFIX_SEI].array_completeness = 0; - parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SUFFIX_SEI].NAL_unit_type = HEVC_NALU_TYPE_SUFFIX_SEI; - return 0; -} - -int lsmash_append_hevc_dcr_nalu -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - if( !param || !ps_data || ps_length < 2 ) - return -1; - if( hevc_alloc_parameter_arrays( param ) < 0 ) - return -1; - hevc_parameter_array_t *ps_array = hevc_get_parameter_set_array( param, ps_type ); - if( !ps_array ) - return -1; - lsmash_entry_list_t *ps_list = ps_array->list; - if( ps_type == HEVC_DCR_NALU_TYPE_PREFIX_SEI - || ps_type == HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) - { - /* Append a SEI anyway. */ - isom_dcr_ps_entry_t *ps = isom_create_ps_entry( ps_data, ps_length ); - if( !ps ) - return -1; - if( lsmash_add_entry( ps_list, ps ) ) - { - isom_remove_dcr_ps( ps ); - return -1; - } - return 0; - } - if( ps_type != HEVC_DCR_NALU_TYPE_VPS - && ps_type != HEVC_DCR_NALU_TYPE_SPS - && ps_type != HEVC_DCR_NALU_TYPE_PPS ) - return -1; - /* Check if the same parameter set identifier already exists. */ - uint8_t ps_id; - if( hevc_get_ps_id( ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH, &ps_id, ps_type ) ) - return -1; - lsmash_entry_t *entry = hevc_get_ps_entry_from_param( param, ps_type, ps_id ); - isom_dcr_ps_entry_t *ps = entry ? (isom_dcr_ps_entry_t *)entry->data : NULL; - if( ps && !ps->unused ) - /* The same parameter set identifier already exists. */ - return -1; - int invoke_reorder; - if( ps ) - { - /* Reuse an already existed parameter set in the list. */ - ps->unused = 0; - if( ps->nalUnit != ps_data ) - { - /* The same address could be given when called by hevc_update_picture_info_for_slice(). */ - lsmash_free( ps->nalUnit ); - ps->nalUnit = ps_data; - } - ps->nalUnitLength = ps_length; - invoke_reorder = 0; - } - else - { - /* Create a new parameter set and append it into the list. */ - ps = isom_create_ps_entry( ps_data, ps_length ); - if( !ps ) - return -1; - if( lsmash_add_entry( ps_list, ps ) ) - { - isom_remove_dcr_ps( ps ); - return -1; - } - invoke_reorder = 1; - } - uint32_t ps_count; - if( nalu_get_ps_count( ps_list, &ps_count ) < 0 ) - goto fail; - /* Update specific info with VPS, SPS or PPS. */ - { - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - uint8_t rbsp_buffer[ps_length]; - uint8_t buffer [ps_length]; - bs.buffer.data = buffer; - bs.buffer.alloc = ps_length; - lsmash_bits_init( &bits, &bs ); - if( ps_type == HEVC_DCR_NALU_TYPE_VPS ) - { - hevc_vps_t vps; - if( hevc_parse_vps_minimally( &bits, &vps, rbsp_buffer, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) < 0 ) - goto fail; - if( ps_count == 1 ) - { - /* Initialize if not initialized yet. */ - lsmash_entry_list_t *sps_list = hevc_get_parameter_set_list( param, HEVC_DCR_NALU_TYPE_SPS ); - uint32_t sps_count; - if( nalu_get_ps_count( sps_list, &sps_count ) < 0 ) - goto fail; - if( sps_count == 0 ) - hevc_specific_parameters_ready( param ); - } - hevc_specific_parameters_update_ptl( param, &vps.ptl ); - param->numTemporalLayers = LSMASH_MAX( param->numTemporalLayers, vps.max_sub_layers_minus1 + 1 ); - //param->temporalIdNested &= vps.temporal_id_nesting_flag; - } - else if( ps_type == HEVC_DCR_NALU_TYPE_SPS ) - { - hevc_sps_t sps; - if( hevc_parse_sps_minimally( &bits, &sps, rbsp_buffer, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) < 0 ) - goto fail; - if( ps_count == 1 ) - { - /* Initialize if not initialized yet. */ - lsmash_entry_list_t *vps_list = hevc_get_parameter_set_list( param, HEVC_DCR_NALU_TYPE_VPS ); - uint32_t vps_count; - if( nalu_get_ps_count( vps_list, &vps_count ) < 0 ) - goto fail; - if( vps_count == 0 ) - hevc_specific_parameters_ready( param ); - } - hevc_specific_parameters_update_ptl( param, &sps.ptl ); - param->min_spatial_segmentation_idc = LSMASH_MIN( param->min_spatial_segmentation_idc, sps.vui.min_spatial_segmentation_idc ); - param->chromaFormat = sps.chroma_format_idc; - param->bitDepthLumaMinus8 = sps.bit_depth_luma_minus8; - param->bitDepthChromaMinus8 = sps.bit_depth_chroma_minus8; - param->numTemporalLayers = LSMASH_MAX( param->numTemporalLayers, sps.max_sub_layers_minus1 + 1 ); - param->temporalIdNested &= sps.temporal_id_nesting_flag; - /* Check type of constant frame rate. */ - if( param->constantFrameRate ) - { - int cfr; - if( param->constantFrameRate == 2 ) - { - cfr = 1; - for( uint8_t i = 0; i <= sps.max_sub_layers_minus1; i++ ) - cfr &= sps.vui.hrd.fixed_pic_rate_general_flag[i]; - } - else - cfr = 0; - if( cfr ) - param->constantFrameRate = 2; - else - { - for( uint8_t i = 0; i <= sps.max_sub_layers_minus1; i++ ) - cfr |= sps.vui.hrd.fixed_pic_rate_general_flag[i]; - param->constantFrameRate = cfr; - } - } -#if 0 - /* FIXME: probably, we can get average frame rate according to C.3.3 Picture output. */ - if( param->constantFrameRate ) - { - uint64_t interval = 0; - for( uint8_t i = 0; i <= sps.max_sub_layers_minus1; i++ ) - interval += sps.vui.num_units_in_tick * sps.vui.hrd.elemental_duration_in_tc_minus1[i]; - uint64_t frame_rate; - if( interval ) - frame_rate = ((256 * (2 - sps.vui.field_seq_flag) * (uint64_t)sps.vui.time_scale) - * (sps.max_sub_layers_minus1 + 1)) / interval; - else - frame_rate = 0; - if( frame_rate != param->avgFrameRate && param->avgFrameRate ) - param->constantFrameRate = 0; - param->avgFrameRate = frame_rate; - } -#endif - } - else - { - hevc_pps_t pps; - if( hevc_parse_pps_minimally( &bits, &pps, rbsp_buffer, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) < 0 ) - goto fail; - uint8_t parallelismType = 0; -#if 1 /* Replace 1 with 0 if parallelismType shall be set to 0 when min_spatial_segmentation_idc equal to 0. */ - - if( pps.entropy_coding_sync_enabled_flag ) - parallelismType = pps.tiles_enabled_flag ? 0 : 3; - else if( pps.tiles_enabled_flag ) - parallelismType = 2; - else - parallelismType = 1; -#else - /* Parse SPS and check the value of min_spatial_segmentation_idc is equal to zero or not. - * If the value is not equal to zero, update parallelismType appropriately. - * If corresponding SPS is not found, set 0 to parallelismType. */ - entry = hevc_get_ps_entry_from_param( param, HEVC_DCR_NALU_TYPE_SPS, pps.seq_parameter_set_id ); - if( entry && entry->data ) - { - ps = (isom_dcr_ps_entry_t *)entry->data; - lsmash_bits_t sps_bits = { 0 }; - lsmash_bs_t sps_bs = { 0 }; - uint8_t sps_rbsp_buffer[ ps->nalUnitLength ]; - uint8_t sps_buffer [ ps->nalUnitLength ]; - sps_bs.buffer.data = sps_buffer; - sps_bs.buffer.alloc = ps->nalUnitLength; - lsmash_bits_init( &sps_bits, &sps_bs ); - hevc_sps_t sps; - if( hevc_parse_sps_minimally( &sps_bits, &sps, sps_rbsp_buffer, - ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH ) == 0 ) - { - if( sps.vui.min_spatial_segmentation_idc ) - { - if( pps.entropy_coding_sync_enabled_flag ) - parallelismType = pps.tiles_enabled_flag ? 0 : 3; - else if( pps.tiles_enabled_flag ) - parallelismType = 2; - else - parallelismType = 1; - } - else - parallelismType = 0; - } - } -#endif - if( ps_count == 1 ) - param->parallelismType = parallelismType; - else if( param->parallelismType != parallelismType ) - param->parallelismType = 0; - } - } - if( invoke_reorder ) - /* Add a new parameter set in order of ascending parameter set identifier. */ - hevc_reorder_parameter_set_ascending_id( param, ps_type, ps_list, ps_id ); - return 0; -fail: - ps = (isom_dcr_ps_entry_t *)lsmash_get_entry_data( ps_list, ps_list->entry_count ); - if( ps ) - ps->unused = 1; - return -1; -} - -int hevc_try_to_append_dcr_nalu -( - hevc_info_t *info, - lsmash_hevc_dcr_nalu_type ps_type, - void *ps_data, - uint32_t ps_length -) -{ - lsmash_dcr_nalu_appendable ret = lsmash_check_hevc_dcr_nalu_appendable( &info->hvcC_param, ps_type, ps_data, ps_length ); - lsmash_hevc_specific_parameters_t *param; - switch( ret ) - { - case DCR_NALU_APPEND_ERROR : /* Error */ - return -1; - case DCR_NALU_APPEND_NEW_DCR_REQUIRED : /* Mulitiple sample description is needed. */ - case DCR_NALU_APPEND_NEW_SAMPLE_ENTRY_REQUIRED : /* Mulitiple sample description is needed. */ - param = &info->hvcC_param_next; - info->hvcC_pending = 1; - break; - case DCR_NALU_APPEND_POSSIBLE : /* Appendable */ - param = info->hvcC_pending ? &info->hvcC_param_next : &info->hvcC_param; - break; - default : /* No need to append */ - return DCR_NALU_APPEND_DUPLICATED; - } - switch( ps_type ) - { - case HEVC_DCR_NALU_TYPE_VPS : - if( hevc_parse_vps( info, info->buffer.rbsp, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) ) - return -1; - break; - case HEVC_DCR_NALU_TYPE_SPS : - if( hevc_parse_sps( info, info->buffer.rbsp, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) ) - return -1; - break; - case HEVC_DCR_NALU_TYPE_PPS : - if( hevc_parse_pps( info, info->buffer.rbsp, - ps_data + HEVC_MIN_NALU_HEADER_LENGTH, - ps_length - HEVC_MIN_NALU_HEADER_LENGTH ) ) - return -1; - break; - default : - break; - } - return lsmash_append_hevc_dcr_nalu( param, ps_type, ps_data, ps_length ); -} - -static int hevc_move_dcr_nalu_entry -( - lsmash_hevc_specific_parameters_t *dst_data, - lsmash_hevc_specific_parameters_t *src_data, - lsmash_hevc_dcr_nalu_type ps_type -) -{ - lsmash_entry_list_t *src_ps_list = hevc_get_parameter_set_list( src_data, ps_type ); - lsmash_entry_list_t *dst_ps_list = hevc_get_parameter_set_list( dst_data, ps_type ); - assert( src_ps_list && dst_ps_list ); - for( lsmash_entry_t *src_entry = src_ps_list->head; src_entry; src_entry = src_entry->next ) - { - isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)src_entry->data; - if( !src_ps ) - continue; - uint8_t src_ps_id; - if( hevc_get_ps_id( src_ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - src_ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, - &src_ps_id, ps_type ) < 0 ) - return -1; - lsmash_entry_t *dst_entry; - for( dst_entry = dst_ps_list->head; dst_entry; dst_entry = dst_entry->next ) - { - isom_dcr_ps_entry_t *dst_ps = (isom_dcr_ps_entry_t *)dst_entry->data; - if( !dst_ps ) - continue; - uint8_t dst_ps_id; - if( hevc_get_ps_id( dst_ps->nalUnit + HEVC_MIN_NALU_HEADER_LENGTH, - dst_ps->nalUnitLength - HEVC_MIN_NALU_HEADER_LENGTH, - &dst_ps_id, ps_type ) < 0 ) - return -1; - if( dst_ps_id == src_ps_id ) - { - /* Replace the old parameter set with the new one. */ - assert( dst_entry->data != src_entry->data ); - isom_remove_dcr_ps( dst_ps ); - dst_entry->data = src_entry->data; - src_entry->data = NULL; - break; - } - } - if( !dst_entry ) - { - /* Move the parameter set. */ - if( lsmash_add_entry( dst_ps_list, src_ps ) ) - return -1; - src_entry->data = NULL; - } - } - return 0; -} - -int hevc_move_pending_hvcC_param -( - hevc_info_t *info -) -{ - assert( info ); - if( !info->hvcC_pending ) - return 0; - /* Mark 'unused' on parameter sets within the decoder configuration record. */ - for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) - { - lsmash_entry_list_t *ps_list = hevc_get_parameter_set_list( &info->hvcC_param, i ); - assert( ps_list ); - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - continue; - ps->unused = 1; - } - } - /* Move the new parameter sets. */ - if( hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_VPS ) < 0 - || hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_SPS ) < 0 - || hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_PPS ) < 0 - || hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_PREFIX_SEI ) < 0 - || hevc_move_dcr_nalu_entry( &info->hvcC_param, &info->hvcC_param_next, HEVC_DCR_NALU_TYPE_SUFFIX_SEI ) < 0 ) - return -1; - /* Move to the pending. */ - lsmash_hevc_parameter_arrays_t *parameter_arrays = info->hvcC_param.parameter_arrays; /* Back up parameter arrays. */ - info->hvcC_param = info->hvcC_param_next; - info->hvcC_param.parameter_arrays = parameter_arrays; - /* No pending hvcC. */ - lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param_next ); - memset( &info->hvcC_param_next, 0, sizeof(lsmash_hevc_specific_parameters_t) ); - info->hvcC_pending = 0; - return 0; -} - -int lsmash_set_hevc_array_completeness -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type, - int array_completeness -) -{ - if( hevc_alloc_parameter_arrays( param ) < 0 ) - return -1; - hevc_parameter_array_t *ps_array = hevc_get_parameter_set_array( param, ps_type ); - if( !ps_array ) - return -1; - ps_array->array_completeness = array_completeness; - return 0; -} - -int lsmash_get_hevc_array_completeness -( - lsmash_hevc_specific_parameters_t *param, - lsmash_hevc_dcr_nalu_type ps_type, - int *array_completeness -) -{ - if( hevc_alloc_parameter_arrays( param ) < 0 ) - return -1; - hevc_parameter_array_t *ps_array = hevc_get_parameter_set_array( param, ps_type ); - if( !ps_array ) - return -1; - *array_completeness = ps_array->array_completeness; - return 0; -} - -static int hevc_parse_succeeded -( - hevc_info_t *info, - lsmash_hevc_specific_parameters_t *param -) -{ - int ret; - if( info->vps.present - && info->sps.present - && info->pps.present ) - { - *param = info->hvcC_param; - /* Avoid freeing parameter sets. */ - info->hvcC_param.parameter_arrays = NULL; - ret = 0; - } - else - ret = -1; - hevc_cleanup_parser( info ); - return ret; -} - -static inline int hevc_parse_failed -( - hevc_info_t *info -) -{ - hevc_cleanup_parser( info ); - return -1; -} - -int lsmash_setup_hevc_specific_parameters_from_access_unit -( - lsmash_hevc_specific_parameters_t *param, - uint8_t *data, - uint32_t data_length -) -{ - if( !param || !data || data_length == 0 ) - return -1; - hevc_info_t handler = { { 0 } }; - hevc_info_t *info = &handler; - lsmash_stream_buffers_t _sb = { LSMASH_STREAM_BUFFERS_TYPE_NONE }; - lsmash_stream_buffers_t *sb = &_sb; - lsmash_data_string_handler_t stream = { 0 }; - stream.data = data; - stream.data_length = data_length; - stream.remainder_length = data_length; - if( hevc_setup_parser( info, sb, 1, LSMASH_STREAM_BUFFERS_TYPE_DATA_STRING, &stream ) ) - return hevc_parse_failed( info ); - hevc_stream_buffer_t *hb = &info->buffer; - hevc_slice_info_t *slice = &info->slice; - hevc_nalu_header_t nalu_header = { 0 }; - uint64_t consecutive_zero_byte_count = 0; - uint64_t ebsp_length = 0; - int no_more_buf = 0; - int complete_au = 0; - while( 1 ) - { - lsmash_stream_buffers_update( sb, 2 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - int no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !nalu_check_next_short_start_code( sb->pos, sb->end ) && !no_more ) - { - if( lsmash_stream_buffers_get_byte( sb ) ) - consecutive_zero_byte_count = 0; - else - ++consecutive_zero_byte_count; - ++ebsp_length; - continue; - } - if( no_more && ebsp_length == 0 ) - /* For the last NALU. This NALU already has been parsed. */ - return hevc_parse_succeeded( info, param ); - uint64_t next_nalu_head_pos = info->ebsp_head_pos + ebsp_length + !no_more * HEVC_SHORT_START_CODE_LENGTH; - /* Memorize position of short start code of the next NALU in buffer. - * This is used when backward reading of stream doesn't occur. */ - uint8_t *next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - uint8_t nalu_type = nalu_header.nal_unit_type; - int read_back = 0; - if( nalu_type == HEVC_NALU_TYPE_FD ) - { - /* We don't support streams with both filler and HRD yet. - * Otherwise, just skip filler because elemental streams defined in 14496-15 are forbidden to use filler. */ - if( info->sps.vui.hrd.present ) - return hevc_parse_failed( info ); - } - else if( nalu_type <= HEVC_NALU_TYPE_RASL_R - || (nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && nalu_type <= HEVC_NALU_TYPE_CRA) - || (nalu_type >= HEVC_NALU_TYPE_VPS && nalu_type <= HEVC_NALU_TYPE_SUFFIX_SEI) ) - { - /* Get the EBSP of the current NALU here. */ - ebsp_length -= consecutive_zero_byte_count; /* Any EBSP doesn't have zero bytes at the end. */ - uint64_t nalu_length = nalu_header.length + ebsp_length; - if( lsmash_stream_buffers_get_buffer_size( sb ) < (HEVC_DEFAULT_NALU_LENGTH_SIZE + nalu_length) ) - { - if( hevc_supplement_buffer( hb, NULL, 2 * (HEVC_DEFAULT_NALU_LENGTH_SIZE + nalu_length) ) ) - return hevc_parse_failed( info ); - next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - } - /* Move to the first byte of the current NALU. */ - read_back = (lsmash_stream_buffers_get_offset( sb ) < (nalu_length + consecutive_zero_byte_count)); - if( read_back ) - { - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_data_string_copy( sb, &stream, nalu_length, info->ebsp_head_pos - nalu_header.length ); - } - else - lsmash_stream_buffers_seek( sb, -(nalu_length + consecutive_zero_byte_count), SEEK_CUR ); - if( nalu_type <= HEVC_NALU_TYPE_RSV_VCL31 ) - { - /* VCL NALU (slice) */ - hevc_slice_info_t prev_slice = *slice; - if( hevc_parse_slice_segment_header( info, &nalu_header, hb->rbsp, - lsmash_stream_buffers_get_pos( sb ) + nalu_header.length, ebsp_length ) ) - return hevc_parse_failed( info ); - if( prev_slice.present ) - { - /* Check whether the AU that contains the previous VCL NALU completed or not. */ - if( hevc_find_au_delimit_by_slice_info( info, slice, &prev_slice ) ) - /* The current NALU is the first VCL NALU of the primary coded picture of a new AU. - * Therefore, the previous slice belongs to that new AU. */ - complete_au = 1; - } - slice->present = 1; - } - else - { - if( hevc_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) - { - /* The last slice belongs to the AU you want at this time. */ - slice->present = 0; - complete_au = 1; - } - else if( no_more ) - complete_au = 1; - switch( nalu_type ) - { - case HEVC_NALU_TYPE_VPS : - if( hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_VPS, - lsmash_stream_buffers_get_pos( sb ), nalu_length ) ) - return hevc_parse_failed( info ); - break; - case HEVC_NALU_TYPE_SPS : - if( hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_SPS, - lsmash_stream_buffers_get_pos( sb ), nalu_length ) ) - return hevc_parse_failed( info ); - break; - case HEVC_NALU_TYPE_PPS : - if( hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_PPS, - lsmash_stream_buffers_get_pos( sb ), nalu_length ) ) - return hevc_parse_failed( info ); - break; - default : - break; - } - } - } - /* Move to the first byte of the next NALU. */ - if( read_back ) - { - uint64_t consumed_data_length = LSMASH_MIN( stream.remainder_length, lsmash_stream_buffers_get_buffer_size( sb ) ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_data_string_copy( sb, &stream, consumed_data_length, next_nalu_head_pos ); - } - else - lsmash_stream_buffers_set_pos( sb, next_short_start_code_pos + HEVC_SHORT_START_CODE_LENGTH ); - info->prev_nalu_type = nalu_type; - lsmash_stream_buffers_update( sb, 1 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - ebsp_length = 0; - no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !no_more && !complete_au ) - { - /* Check the next NALU header. */ - if( hevc_check_nalu_header( &nalu_header, sb, !!consecutive_zero_byte_count ) ) - return hevc_parse_failed( info ); - info->ebsp_head_pos = next_nalu_head_pos + nalu_header.length; - } - else - return hevc_parse_succeeded( info, param ); - consecutive_zero_byte_count = 0; - } -} - -int hevc_construct_specific_parameters -( - lsmash_codec_specific_t *dst, - lsmash_codec_specific_t *src -) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) - return -1; - lsmash_hevc_specific_parameters_t *param = (lsmash_hevc_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - if( hevc_alloc_parameter_arrays( param ) < 0 ) - return -1; - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return -1; - if( lsmash_bs_import_data( bs, data, src->size - (data - src->data.unstructured) ) ) - goto fail; - if( lsmash_bs_get_byte( bs ) != HVCC_CONFIGURATION_VERSION ) - goto fail; /* We don't support configurationVersion other than HVCC_CONFIGURATION_VERSION. */ - uint8_t temp8 = lsmash_bs_get_byte( bs ); - param->general_profile_space = (temp8 >> 6) & 0x03; - param->general_tier_flag = (temp8 >> 5) & 0x01; - param->general_profile_idc = temp8 & 0x1F; - param->general_profile_compatibility_flags = lsmash_bs_get_be32( bs ); - uint32_t temp32 = lsmash_bs_get_be32( bs ); - uint16_t temp16 = lsmash_bs_get_be16( bs ); - param->general_constraint_indicator_flags = ((uint64_t)temp32 << 16) | temp16; - param->general_level_idc = lsmash_bs_get_byte( bs ); - param->min_spatial_segmentation_idc = lsmash_bs_get_be16( bs ) & 0x0FFF; - param->parallelismType = lsmash_bs_get_byte( bs ) & 0x03; - param->chromaFormat = lsmash_bs_get_byte( bs ) & 0x03; - param->bitDepthLumaMinus8 = lsmash_bs_get_byte( bs ) & 0x07; - param->bitDepthChromaMinus8 = lsmash_bs_get_byte( bs ) & 0x07; - param->avgFrameRate = lsmash_bs_get_be16( bs ); - temp8 = lsmash_bs_get_byte( bs ); - param->constantFrameRate = (temp8 >> 6) & 0x03; - param->numTemporalLayers = (temp8 >> 3) & 0x07; - param->temporalIdNested = (temp8 >> 2) & 0x01; - param->lengthSizeMinusOne = temp8 & 0x03; - uint8_t numOfArrays = lsmash_bs_get_byte( bs ); - for( uint8_t i = 0; i < numOfArrays; i++ ) - { - hevc_parameter_array_t param_array; - memset( ¶m_array, 0, sizeof(hevc_parameter_array_t) ); - temp8 = lsmash_bs_get_byte( bs ); - param_array.array_completeness = (temp8 >> 7) & 0x01; - param_array.NAL_unit_type = temp8 & 0x3F; - param_array.list->entry_count = lsmash_bs_get_be16( bs ); - if( param_array.NAL_unit_type == HEVC_NALU_TYPE_VPS - || param_array.NAL_unit_type == HEVC_NALU_TYPE_SPS - || param_array.NAL_unit_type == HEVC_NALU_TYPE_PPS - || param_array.NAL_unit_type == HEVC_NALU_TYPE_PREFIX_SEI - || param_array.NAL_unit_type == HEVC_NALU_TYPE_SUFFIX_SEI ) - { - if( nalu_get_dcr_ps( bs, param_array.list, param_array.list->entry_count ) < 0 ) - goto fail; - } - else - for( uint16_t j = 0; j < param_array.list->entry_count; j++ ) - { - uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); - lsmash_bs_skip_bytes( bs, nalUnitLength ); /* nalUnit */ - } - switch( param_array.NAL_unit_type ) - { - case HEVC_NALU_TYPE_VPS : - param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_VPS] = param_array; - break; - case HEVC_NALU_TYPE_SPS : - param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SPS] = param_array; - break; - case HEVC_NALU_TYPE_PPS : - param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PPS] = param_array; - break; - case HEVC_NALU_TYPE_PREFIX_SEI : - param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_PREFIX_SEI] = param_array; - break; - case HEVC_NALU_TYPE_SUFFIX_SEI : - param->parameter_arrays->ps_array[HEVC_DCR_NALU_TYPE_SUFFIX_SEI] = param_array; - break; - default : - /* Discard unknown NALUs. */ - break; - } - } - lsmash_bs_cleanup( bs ); - return 0; -fail: - lsmash_bs_cleanup( bs ); - return -1; -} - -int hevc_print_codec_specific -( - FILE *fp, - lsmash_file_t *file, - isom_box_t *box, - int level -) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: HEVC Configuration Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - uint8_t *data = box->binary; - uint32_t offset = isom_skip_box_common( &data ); - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return -1; - if( lsmash_bs_import_data( bs, data, box->size - offset ) ) - { - lsmash_bs_cleanup( bs ); - return -1; - } - uint8_t configurationVersion = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "configurationVersion = %"PRIu8"\n", configurationVersion ); - if( configurationVersion != HVCC_CONFIGURATION_VERSION ) - { - lsmash_bs_cleanup( bs ); - return 0; - } - uint8_t temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "general_profile_space = %"PRIu8"\n", (temp8 >> 6) & 0x03 ); - lsmash_ifprintf( fp, indent, "general_tier_flag = %"PRIu8"\n", (temp8 >> 5) & 0x01 ); - lsmash_ifprintf( fp, indent, "general_profile_idc = %"PRIu8"\n", temp8 & 0x1F ); - lsmash_ifprintf( fp, indent, "general_profile_compatibility_flags = 0x%08"PRIx32"\n", lsmash_bs_get_be32( bs ) ); - uint32_t temp32 = lsmash_bs_get_be32( bs ); - uint16_t temp16 = lsmash_bs_get_be16( bs ); - lsmash_ifprintf( fp, indent, "general_constraint_indicator_flags = 0x%012"PRIx64"\n", ((uint64_t)temp32 << 16) | temp16 ); - uint8_t general_level_idc = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "general_level_idc = %"PRIu8" (Level %g)\n", general_level_idc, general_level_idc / 30.0 ); - temp16 = lsmash_bs_get_be16( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp16 >> 12) & 0x0F ); - lsmash_ifprintf( fp, indent, "min_spatial_segmentation_idc = %"PRIu16"\n", temp16 & 0x0FFF ); - temp8 = lsmash_bs_get_byte( bs ); - uint8_t parallelismType = temp8 & 0x03; - static const char *parallelism_table[4] = - { - "Mixed types or Unknown", - "Slice based", - "Tile based", - "Entropy coding synchronization based / WPP: Wavefront Parallel Processing" - }; - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); - lsmash_ifprintf( fp, indent, "parallelismType = %"PRIu8" (%s)\n", parallelismType, - parallelism_table[parallelismType] ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 2) & 0x3F ); - lsmash_ifprintf( fp, indent, "chromaFormat = %"PRIu8"\n", temp8 & 0x03 ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); - lsmash_ifprintf( fp, indent, "bitDepthLumaMinus8 = %"PRIu8"\n", temp8 & 0x07 ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "reserved = 0x%02"PRIx8"\n", (temp8 >> 3) & 0x1F ); - lsmash_ifprintf( fp, indent, "bitDepthChromaMinus8 = %"PRIu8"\n", temp8 & 0x07 ); - lsmash_ifprintf( fp, indent, "avgFrameRate = %"PRIu16"\n", lsmash_bs_get_be16( bs ) ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "constantFrameRate = %"PRIu8"\n", (temp8 >> 6) & 0x03 ); - lsmash_ifprintf( fp, indent, "numTemporalLayers = %"PRIu8"\n", (temp8 >> 3) & 0x07 ); - lsmash_ifprintf( fp, indent, "temporalIdNested = %"PRIu8"\n", (temp8 >> 2) & 0x01 ); - lsmash_ifprintf( fp, indent, "lengthSizeMinusOne = %"PRIu8"\n", temp8 & 0x03 ); - uint8_t numOfArrays = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, indent, "numOfArrays = %"PRIu8"\n", numOfArrays ); - for( uint8_t i = 0; i < numOfArrays; i++ ) - { - int array_indent = indent + 1; - lsmash_ifprintf( fp, array_indent++, "array[%"PRIu8"]\n", i ); - temp8 = lsmash_bs_get_byte( bs ); - lsmash_ifprintf( fp, array_indent, "array_completeness = %"PRIu8"\n", (temp8 >> 7) & 0x01 ); - lsmash_ifprintf( fp, array_indent, "reserved = %"PRIu8"\n", (temp8 >> 6) & 0x01 ); - lsmash_ifprintf( fp, array_indent, "NAL_unit_type = %"PRIu8"\n", temp8 & 0x3F ); - uint16_t numNalus = lsmash_bs_get_be16( bs ); - lsmash_ifprintf( fp, array_indent, "numNalus = %"PRIu16"\n", numNalus ); - for( uint16_t j = 0; j < numNalus; j++ ) - { - uint16_t nalUnitLength = lsmash_bs_get_be16( bs ); - lsmash_bs_skip_bytes( bs, nalUnitLength ); - } - } - lsmash_bs_cleanup( bs ); - return 0; -} - -static inline int hevc_copy_dcr_nalu_array -( - lsmash_hevc_specific_parameters_t *dst_data, - lsmash_hevc_specific_parameters_t *src_data, - lsmash_hevc_dcr_nalu_type ps_type -) -{ - hevc_parameter_array_t *src_ps_array = hevc_get_parameter_set_array( src_data, ps_type ); - hevc_parameter_array_t *dst_ps_array = hevc_get_parameter_set_array( dst_data, ps_type ); - assert( src_ps_array && dst_ps_array ); - dst_ps_array->array_completeness = src_ps_array->array_completeness; - dst_ps_array->NAL_unit_type = src_ps_array->NAL_unit_type; - lsmash_entry_list_t *src_ps_list = src_ps_array->list; - lsmash_entry_list_t *dst_ps_list = dst_ps_array->list; - for( lsmash_entry_t *entry = src_ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *src_ps = (isom_dcr_ps_entry_t *)entry->data; - if( !src_ps || src_ps->unused ) - continue; - isom_dcr_ps_entry_t *dst_ps = isom_create_ps_entry( src_ps->nalUnit, src_ps->nalUnitLength ); - if( !dst_ps ) - { - lsmash_destroy_hevc_parameter_arrays( dst_data ); - return -1; - } - if( lsmash_add_entry( dst_ps_list, dst_ps ) ) - { - lsmash_destroy_hevc_parameter_arrays( dst_data ); - isom_remove_dcr_ps( dst_ps ); - return -1; - } - } - return 0; -} - -int hevc_copy_codec_specific -( - lsmash_codec_specific_t *dst, - lsmash_codec_specific_t *src -) -{ - assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); - assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); - lsmash_hevc_specific_parameters_t *src_data = (lsmash_hevc_specific_parameters_t *)src->data.structured; - lsmash_hevc_specific_parameters_t *dst_data = (lsmash_hevc_specific_parameters_t *)dst->data.structured; - lsmash_destroy_hevc_parameter_arrays( dst_data ); - *dst_data = *src_data; - if( !src_data->parameter_arrays ) - return 0; - dst_data->parameter_arrays = lsmash_malloc_zero( sizeof(lsmash_hevc_parameter_arrays_t) ); - if( !dst_data->parameter_arrays ) - return -1; - for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) - if( hevc_copy_dcr_nalu_array( dst_data, src_data, (lsmash_hevc_dcr_nalu_type)i ) < 0 ) - return -1; - return 0; -} diff -Nru l-smash-1.9.1/hevc.h l-smash-2.3.0/hevc.h --- l-smash-1.9.1/hevc.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/hevc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,446 +0,0 @@ -/***************************************************************************** - * hevc.h: - ***************************************************************************** - * Copyright (C) 2013-2014 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. */ - -#define HEVC_DEFAULT_BUFFER_SIZE (1<<16) -#define HEVC_DEFAULT_NALU_LENGTH_SIZE 4 /* We always use 4 bytes length. */ -#define HEVC_SHORT_START_CODE_LENGTH 3 - -enum -{ - HEVC_NALU_TYPE_TRAIL_N = 0, - HEVC_NALU_TYPE_TRAIL_R = 1, - HEVC_NALU_TYPE_TSA_N = 2, - HEVC_NALU_TYPE_TSA_R = 3, - HEVC_NALU_TYPE_STSA_N = 4, - HEVC_NALU_TYPE_STSA_R = 5, - HEVC_NALU_TYPE_RADL_N = 6, - HEVC_NALU_TYPE_RADL_R = 7, - HEVC_NALU_TYPE_RASL_N = 8, - HEVC_NALU_TYPE_RASL_R = 9, - HEVC_NALU_TYPE_RSV_VCL_R15 = 15, - HEVC_NALU_TYPE_BLA_W_LP = 16, - HEVC_NALU_TYPE_BLA_W_RADL = 17, - HEVC_NALU_TYPE_BLA_N_LP = 18, - HEVC_NALU_TYPE_IDR_W_RADL = 19, - HEVC_NALU_TYPE_IDR_N_LP = 20, - HEVC_NALU_TYPE_CRA = 21, - HEVC_NALU_TYPE_RSV_IRAP_VCL22 = 22, - HEVC_NALU_TYPE_RSV_IRAP_VCL23 = 23, - HEVC_NALU_TYPE_RSV_VCL31 = 31, - HEVC_NALU_TYPE_VPS = 32, - HEVC_NALU_TYPE_SPS = 33, - HEVC_NALU_TYPE_PPS = 34, - HEVC_NALU_TYPE_AUD = 35, - HEVC_NALU_TYPE_EOS = 36, - HEVC_NALU_TYPE_EOB = 37, - HEVC_NALU_TYPE_FD = 38, - HEVC_NALU_TYPE_PREFIX_SEI = 39, - HEVC_NALU_TYPE_SUFFIX_SEI = 40, - HEVC_NALU_TYPE_RSV_NVCL41 = 41, - HEVC_NALU_TYPE_RSV_NVCL44 = 44, - HEVC_NALU_TYPE_RSV_NVCL47 = 47, - HEVC_NALU_TYPE_UNSPEC48 = 48, - HEVC_NALU_TYPE_UNSPEC55 = 55, - HEVC_NALU_TYPE_UNSPEC63 = 63, - HEVC_NALU_TYPE_UNKNOWN = 64 -}; - -typedef struct -{ - uint8_t array_completeness; - uint8_t NAL_unit_type; - lsmash_entry_list_t list[1]; -} hevc_parameter_array_t; - -struct lsmash_hevc_parameter_arrays_tag -{ - hevc_parameter_array_t ps_array[HEVC_DCR_NALU_TYPE_NUM]; -}; - -typedef struct -{ - uint8_t nal_unit_type; - uint8_t TemporalId; - uint16_t length; -} hevc_nalu_header_t; - -/* Profile, Tier and Level */ -typedef struct -{ - uint8_t profile_space; - uint8_t tier_flag; - uint8_t profile_idc; - uint32_t profile_compatibility_flags; - uint8_t progressive_source_flag; - uint8_t interlaced_source_flag; - uint8_t non_packed_constraint_flag; - uint8_t frame_only_constraint_flag; - uint64_t reserved_zero_44bits; - uint8_t level_idc; -} hevc_ptl_common_t; - -typedef struct -{ - hevc_ptl_common_t general; - hevc_ptl_common_t sub_layer[6]; -} hevc_ptl_t; - -/* HRD (Hypothetical Reference Decoder) */ -typedef struct -{ - uint8_t present; - uint8_t CpbDpbDelaysPresentFlag; - uint8_t sub_pic_hrd_params_present_flag; - uint8_t du_cpb_removal_delay_increment_length; - uint8_t sub_pic_cpb_params_in_pic_timing_sei_flag; - uint8_t dpb_output_delay_du_length; - uint8_t au_cpb_removal_delay_length; - uint8_t dpb_output_delay_length; - uint8_t fixed_pic_rate_general_flag[7]; - uint16_t elemental_duration_in_tc [7]; -} hevc_hrd_t; - -/* VPS (Video Parameter Set) */ -typedef struct -{ - uint8_t present; - uint8_t video_parameter_set_id; - uint8_t max_sub_layers_minus1; - uint8_t temporal_id_nesting_flag; - uint8_t timing_info_present_flag; - uint8_t frame_field_info_present_flag; - uint16_t num_hrd_parameters; - hevc_ptl_t ptl; - hevc_hrd_t hrd[2]; -} hevc_vps_t; - -typedef struct -{ - uint8_t present; - uint16_t sar_width; - uint16_t sar_height; - uint8_t video_full_range_flag; - uint8_t colour_description_present_flag; - uint8_t colour_primaries; - uint8_t transfer_characteristics; - uint8_t matrix_coeffs; - uint8_t field_seq_flag; - uint8_t frame_field_info_present_flag; - uint32_t num_units_in_tick; - uint32_t time_scale; - uint16_t min_spatial_segmentation_idc; - lsmash_crop_t def_disp_win_offset; - hevc_hrd_t hrd; -} hevc_vui_t; - -/* Short term reference picture sets */ -typedef struct -{ - uint8_t NumNegativePics; - uint8_t NumPositivePics; - uint8_t NumDeltaPocs; - uint8_t UsedByCurrPicS0[16]; - uint8_t UsedByCurrPicS1[16]; - int32_t DeltaPocS0[16]; - int32_t DeltaPocS1[16]; -} hevc_st_rps_t; - -/* SPS (Sequence Parameter Set) */ -typedef struct -{ - uint8_t present; - uint8_t video_parameter_set_id; - uint8_t max_sub_layers_minus1; - uint8_t temporal_id_nesting_flag; - hevc_ptl_t ptl; - uint8_t seq_parameter_set_id; - uint8_t chroma_format_idc; - uint8_t separate_colour_plane_flag; - uint8_t bit_depth_luma_minus8; - uint8_t bit_depth_chroma_minus8; - uint8_t log2_max_pic_order_cnt_lsb; - uint8_t num_short_term_ref_pic_sets; - uint8_t long_term_ref_pics_present_flag; - uint8_t num_long_term_ref_pics_sps; - uint8_t temporal_mvp_enabled_flag; - uint32_t cropped_width; - uint32_t cropped_height; - uint32_t PicWidthInCtbsY; - uint32_t PicHeightInCtbsY; - uint64_t PicSizeInCtbsY; - hevc_st_rps_t st_rps[65]; - hevc_vui_t vui; -} hevc_sps_t; - -/* PPS (Picture Parameter Set) */ -typedef struct -{ - uint8_t present; - uint8_t pic_parameter_set_id; - uint8_t seq_parameter_set_id; - uint8_t dependent_slice_segments_enabled_flag; - uint8_t output_flag_present_flag; - uint8_t num_extra_slice_header_bits; - uint8_t tiles_enabled_flag; - uint8_t entropy_coding_sync_enabled_flag; - uint32_t num_tile_columns_minus1; - uint32_t num_tile_rows_minus1; - uint32_t *colWidth; - uint32_t *colBd; - uint32_t *rowHeight; - uint32_t *rowBd; -} hevc_pps_t; - -/* SEI (Supplemental Enhancement Information) */ -typedef struct -{ - uint8_t present; - uint8_t pic_struct; -} hevc_pic_timing_t; - -typedef struct -{ - uint8_t present; - uint8_t broken_link_flag; - uint32_t recovery_poc_cnt; -} hevc_recovery_point_t; - -typedef struct -{ - hevc_pic_timing_t pic_timing; - hevc_recovery_point_t recovery_point; -} hevc_sei_t; - -/* Slice segment */ -typedef struct -{ - uint8_t present; - uint8_t nalu_type; - uint8_t TemporalId; - uint8_t type; - uint8_t video_parameter_set_id; - uint8_t seq_parameter_set_id; - uint8_t pic_parameter_set_id; - uint8_t first_slice_segment_in_pic_flag; - uint8_t dependent_slice_segment_flag; - uint64_t segment_address; - int32_t pic_order_cnt_lsb; -} hevc_slice_info_t; - -/* Picture */ -typedef enum -{ - HEVC_PICTURE_TYPE_IDR = 0, - HEVC_PICTURE_TYPE_I = 1, - HEVC_PICTURE_TYPE_I_P = 2, - HEVC_PICTURE_TYPE_I_P_B = 3, - HEVC_PICTURE_TYPE_NONE = 4, -} hevc_picture_type; - -typedef struct -{ - hevc_picture_type type; - uint8_t irap; - uint8_t idr; - uint8_t broken_link; - uint8_t radl; - uint8_t rasl; - uint8_t sublayer_nonref; - uint8_t closed_rap; - uint8_t first; - uint8_t random_accessible; - uint8_t TemporalId; - uint8_t independent; - uint8_t field_coded; - uint8_t pic_parameter_set_id; - uint8_t has_primary; - uint8_t delta; - /* POC */ - uint16_t poc_lsb; - int32_t poc; - int32_t tid0_poc_msb; - int32_t tid0_poc_lsb; - /* */ - uint32_t recovery_poc_cnt; -} hevc_picture_info_t; - -/* Access unit */ -typedef struct -{ - uint8_t *data; - uint8_t *incomplete_data; - uint32_t length; - uint32_t incomplete_length; - uint32_t number; - uint8_t TemporalId; - hevc_picture_info_t picture; -} hevc_access_unit_t; - -typedef struct hevc_info_tag hevc_info_t; - -typedef struct -{ - lsmash_stream_buffers_t *sb; - uint8_t *rbsp; -} hevc_stream_buffer_t; - -struct hevc_info_tag -{ - lsmash_hevc_specific_parameters_t hvcC_param; - lsmash_hevc_specific_parameters_t hvcC_param_next; - hevc_nalu_header_t nalu_header; - lsmash_entry_list_t vps_list[1]; - lsmash_entry_list_t sps_list[1]; - lsmash_entry_list_t pps_list[1]; - hevc_vps_t vps; /* active VPS */ - hevc_sps_t sps; /* active SPS */ - hevc_pps_t pps; /* active PPS */ - hevc_sei_t sei; /* active SEI */ - hevc_slice_info_t slice; /* active slice */ - hevc_access_unit_t au; - uint8_t prev_nalu_type; - uint8_t hvcC_pending; - uint64_t ebsp_head_pos; - lsmash_bits_t *bits; - hevc_stream_buffer_t buffer; -}; - -int hevc_setup_parser -( - hevc_info_t *info, - lsmash_stream_buffers_t *sb, - int parse_only, - lsmash_stream_buffers_type type, - void *stream -); - -void hevc_cleanup_parser -( - hevc_info_t *info -); - -int hevc_calculate_poc -( - hevc_info_t *info, - hevc_picture_info_t *picture, - hevc_picture_info_t *prev_picture -); - -void hevc_update_picture_info_for_slice -( - hevc_info_t *info, - hevc_picture_info_t *picture, - hevc_slice_info_t *slice -); - -void hevc_update_picture_info -( - hevc_info_t *info, - hevc_picture_info_t *picture, - hevc_slice_info_t *slice, - hevc_sps_t *sps, - hevc_sei_t *sei -); - -int hevc_find_au_delimit_by_slice_info -( - hevc_info_t *info, - hevc_slice_info_t *slice, - hevc_slice_info_t *prev_slice -); - -int hevc_find_au_delimit_by_nalu_type -( - uint8_t nalu_type, - uint8_t prev_nalu_type -); - -int hevc_supplement_buffer -( - hevc_stream_buffer_t *hb, - hevc_access_unit_t *au, - uint32_t size -); - -int hevc_check_nalu_header -( - hevc_nalu_header_t *nalu_header, - lsmash_stream_buffers_t *sb, - int use_long_start_code -); - -int hevc_parse_vps -( - hevc_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int hevc_parse_sps -( - hevc_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int hevc_parse_pps -( - hevc_info_t *info, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int hevc_parse_sei -( - lsmash_bits_t *bits, - hevc_vps_t *vps, - hevc_sps_t *sps, - hevc_sei_t *sei, - hevc_nalu_header_t *nalu_header, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); -int hevc_parse_slice_segment_header -( - hevc_info_t *info, - hevc_nalu_header_t *nalu_header, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -); - -int hevc_try_to_append_dcr_nalu -( - hevc_info_t *info, - lsmash_hevc_dcr_nalu_type ps_type, - void *ps_data, - uint32_t ps_length -); - -int hevc_move_pending_hvcC_param -( - hevc_info_t *info -); diff -Nru l-smash-1.9.1/importer.c l-smash-2.3.0/importer.c --- l-smash-1.9.1/importer.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/importer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,4643 +0,0 @@ -/***************************************************************************** - * importer.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 -#include -#include - -#define LSMASH_IMPORTER_INTERNAL -#include "importer.h" - -#include "mp4a.h" -#include "box.h" -#include "description.h" - -/*************************************************************************** - importer framework -***************************************************************************/ -struct importer_tag; - -static const lsmash_class_t lsmash_importer_class = -{ - "importer" -}; - -typedef void ( *importer_cleanup ) ( struct importer_tag * ); -typedef int ( *importer_get_accessunit ) ( struct importer_tag *, uint32_t, lsmash_sample_t * ); -typedef int ( *importer_probe ) ( struct importer_tag * ); -typedef uint32_t ( *importer_get_last_duration )( struct importer_tag *, uint32_t ); - -typedef struct -{ - lsmash_class_t class; - int detectable; - importer_probe probe; - importer_get_accessunit get_accessunit; - importer_get_last_duration get_last_delta; - importer_cleanup cleanup; -} importer_functions; - -typedef struct importer_tag -{ - const lsmash_class_t *class; - lsmash_stream_buffers_t sb; - FILE *stream; /* will be deprecated */ - int is_stdin; - void *info; /* importer internal status information. */ - importer_functions funcs; - lsmash_entry_list_t *summaries; -} importer_t; - -typedef enum -{ - IMPORTER_ERROR = -1, - IMPORTER_OK = 0, - IMPORTER_CHANGE = 1, - IMPORTER_EOF = 2, -} importer_status; - -/*************************************************************************** - ADTS importer -***************************************************************************/ -#define MP4SYS_ADTS_FIXED_HEADER_LENGTH 4 /* this is partly a lie. actually 28 bits. */ -#define MP4SYS_ADTS_BASIC_HEADER_LENGTH 7 -#define MP4SYS_ADTS_MAX_FRAME_LENGTH ( ( 1 << 13 ) - 1 ) -#define MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS 4 - -typedef struct -{ - uint16_t syncword; /* 12; */ - uint8_t ID; /* 1; */ - uint8_t layer; /* 2; */ - uint8_t protection_absent; /* 1; */ - uint8_t profile_ObjectType; /* 2; */ - uint8_t sampling_frequency_index; /* 4; */ -// uint8_t private_bit; /* 1; we don't care. */ - uint8_t channel_configuration; /* 3; */ -// uint8_t original_copy; /* 1; we don't care. */ -// uint8_t home; /* 1; we don't care. */ - -} mp4sys_adts_fixed_header_t; - -typedef struct -{ -// uint8_t copyright_identification_bit; /* 1; we don't care. */ -// uint8_t copyright_identification_start; /* 1; we don't care. */ - uint16_t frame_length; /* 13; */ -// uint16_t adts_buffer_fullness; /* 11; we don't care. */ - uint8_t number_of_raw_data_blocks_in_frame; /* 2; */ -// uint16_t adts_error_check; /* we don't support */ -// uint16_t raw_data_block_position[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS-1]; /* we don't use this directly, and... */ - uint16_t raw_data_block_size[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS]; /* use this instead of above. */ -// uint16_t adts_header_error_check; /* we don't support, actually crc_check within this */ -// uint16_t adts_raw_data_block_error_check[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS]; /* we don't support */ -} mp4sys_adts_variable_header_t; - -static void mp4sys_adts_parse_fixed_header( uint8_t* buf, mp4sys_adts_fixed_header_t* header ) -{ - /* FIXME: should we rewrite these code using bitstream reader? */ - header->syncword = (buf[0] << 4) | (buf[1] >> 4); - header->ID = (buf[1] >> 3) & 0x1; - header->layer = (buf[1] >> 1) & 0x3; - header->protection_absent = buf[1] & 0x1; - header->profile_ObjectType = buf[2] >> 6; - header->sampling_frequency_index = (buf[2] >> 2) & 0xF; -// header->private_bit = (buf[2] >> 1) & 0x1; /* we don't care currently. */ - header->channel_configuration = ((buf[2] << 2) | (buf[3] >> 6)) & 0x07; -// header->original_copy = (buf[3] >> 5) & 0x1; /* we don't care currently. */ -// header->home = (buf[3] >> 4) & 0x1; /* we don't care currently. */ -} - -static int mp4sys_adts_check_fixed_header( mp4sys_adts_fixed_header_t* header ) -{ - if( header->syncword != 0xFFF ) return -1; -// if( header->ID != 0x0 ) return -1; /* we don't care. */ - if( header->layer != 0x0 ) return -1; /* must be 0b00 for any type of AAC */ -// if( header->protection_absent != 0x1 ) return -1; /* we don't care. */ - if( header->profile_ObjectType != 0x1 ) return -1; /* FIXME: 0b00=Main, 0b01=LC, 0b10=SSR, 0b11=LTP. */ - if( header->sampling_frequency_index > 0xB ) return -1; /* must not be > 0xB. */ - if( header->channel_configuration == 0x0 ) return -1; /* FIXME: we do not support 0b000 currently. */ - if( header->profile_ObjectType == 0x3 && header->ID != 0x0 ) return -1; /* LTP is valid only if ID==0. */ - return 0; -} - -static int mp4sys_adts_parse_variable_header( FILE* stream, uint8_t* buf, unsigned int protection_absent, mp4sys_adts_variable_header_t* header ) -{ - /* FIXME: should we rewrite these code using bitstream reader? */ -// header->copyright_identification_bit = (buf[3] >> 3) & 0x1; /* we don't care. */ -// header->copyright_identification_start = (buf[3] >> 2) & 0x1; /* we don't care. */ - header->frame_length = ((buf[3] << 11) | (buf[4] << 3) | (buf[5] >> 5)) & 0x1FFF ; -// header->adts_buffer_fullness = ((buf[5] << 6) | (buf[6] >> 2)) 0x7FF ; /* we don't care. */ - header->number_of_raw_data_blocks_in_frame = buf[6] & 0x3; - - if( header->frame_length <= MP4SYS_ADTS_BASIC_HEADER_LENGTH + 2 * (protection_absent == 0) ) - return -1; /* easy error check */ - - /* protection_absent and number_of_raw_data_blocks_in_frame relatives */ - - uint8_t buf2[2]; - unsigned int number_of_blocks = header->number_of_raw_data_blocks_in_frame; - if( number_of_blocks == 0 ) - { - header->raw_data_block_size[0] = header->frame_length - MP4SYS_ADTS_BASIC_HEADER_LENGTH; - /* skip adts_error_check() and subtract that from block_size */ - if( protection_absent == 0 ) - { - header->raw_data_block_size[0] -= 2; - if( fread( buf2, 1, 2, stream ) != 2 ) - return -1; - } - return 0; - } - - /* now we have multiple raw_data_block()s, so evaluate adts_header_error_check() */ - - uint16_t raw_data_block_position[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS]; - uint16_t first_offset = MP4SYS_ADTS_BASIC_HEADER_LENGTH; - if( protection_absent == 0 ) - { - /* process adts_header_error_check() */ - for( int i = 0 ; i < number_of_blocks ; i++ ) /* 1-based in the spec, but we use 0-based */ - { - if( fread( buf2, 1, 2, stream ) != 2 ) - return -1; - raw_data_block_position[i] = (buf2[0] << 8) | buf2[1]; - } - /* skip crc_check in adts_header_error_check(). - Or might be sizeof( adts_error_check() ) if we share with the case number_of_raw_data_blocks_in_frame == 0 */ - if( fread( buf2, 1, 2, stream ) != 2 ) - return -1; - first_offset += ( 2 * number_of_blocks ) + 2; /* according to above */ - } - else - { - /* - * NOTE: We never support the case where number_of_raw_data_blocks_in_frame != 0 && protection_absent != 0, - * because we have to parse the raw AAC bitstream itself to find boundaries of raw_data_block()s in this case. - * Which is to say, that braindamaged spec requires us (mp4 muxer) to decode AAC once to split frames. - * L-SMASH is NOT AAC DECODER, so that we've just given up for this case. - * This is ISO/IEC 13818-7's sin which defines ADTS format originally. - */ - return -1; - } - - /* convert raw_data_block_position --> raw_data_block_size */ - - /* do conversion for first */ - header->raw_data_block_size[0] = raw_data_block_position[0] - first_offset; - /* set dummy offset to tail for loop, do coversion for rest. */ - raw_data_block_position[number_of_blocks] = header->frame_length; - for( int i = 1 ; i <= number_of_blocks ; i++ ) - header->raw_data_block_size[i] = raw_data_block_position[i] - raw_data_block_position[i-1]; - - /* adjustment for adts_raw_data_block_error_check() */ - if( protection_absent == 0 && number_of_blocks != 0 ) - for( int i = 0 ; i <= number_of_blocks ; i++ ) - header->raw_data_block_size[i] -= 2; - - return 0; -} - -static int mp4sys_adts_parse_headers( FILE* stream, uint8_t* buf, mp4sys_adts_fixed_header_t* header, mp4sys_adts_variable_header_t* variable_header ) -{ - mp4sys_adts_parse_fixed_header( buf, header ); - if( mp4sys_adts_check_fixed_header( header ) ) - return -1; - /* get payload length & skip extra(crc) header */ - return mp4sys_adts_parse_variable_header( stream, buf, header->protection_absent, variable_header ); -} - -static lsmash_audio_summary_t *mp4sys_adts_create_summary( mp4sys_adts_fixed_header_t *header ) -{ - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; - summary->max_au_length = MP4SYS_ADTS_MAX_FRAME_LENGTH; - summary->frequency = mp4a_sampling_frequency_table[header->sampling_frequency_index][1]; - summary->channels = header->channel_configuration + ( header->channel_configuration == 0x07 ); /* 0x07 means 7.1ch */ - summary->sample_size = 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 - uint32_t data_length; - uint8_t *data = mp4a_export_AudioSpecificConfig( header->profile_ObjectType + MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN, - summary->frequency, summary->channels, summary->sbr_mode, - NULL, 0, &data_length ); - if( !data ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return NULL; - } - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_free( data ); - return NULL; - } - lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured; - param->objectTypeIndication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3; - param->streamType = MP4SYS_STREAM_TYPE_AudioStream; - if( lsmash_set_mp4sys_decoder_specific_info( param, data, data_length ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - lsmash_free( data ); - return NULL; - } - lsmash_free( data ); - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - return summary; -} - -typedef struct -{ - importer_status status; - unsigned int raw_data_block_idx; - mp4sys_adts_fixed_header_t header; - mp4sys_adts_variable_header_t variable_header; - uint32_t samples_in_frame; - uint32_t au_number; -} mp4sys_adts_info_t; - -static int mp4sys_adts_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - mp4sys_adts_info_t* info = (mp4sys_adts_info_t*)importer->info; - 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 == IMPORTER_ERROR || buffered_sample->length < raw_data_block_size ) - return -1; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - if( current_status == IMPORTER_CHANGE ) - { - lsmash_audio_summary_t* summary = mp4sys_adts_create_summary( &info->header ); - if( !summary ) - return -1; - lsmash_entry_t* entry = lsmash_get_entry( importer->summaries, track_number ); - if( !entry || !entry->data ) - return -1; - lsmash_cleanup_summary( entry->data ); - entry->data = summary; - info->samples_in_frame = summary->samples_in_frame; - } - - /* read a raw_data_block(), typically == payload of a ADTS frame */ - if( fread( buffered_sample->data, 1, raw_data_block_size, importer->stream ) != raw_data_block_size ) - { - info->status = IMPORTER_ERROR; - return -1; - } - buffered_sample->length = raw_data_block_size; - buffered_sample->dts = info->au_number++ * info->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - buffered_sample->prop.pre_roll.distance = 1; /* MDCT */ - - /* now we succeeded to read current frame, so "return" takes 0 always below. */ - - /* skip adts_raw_data_block_error_check() */ - if( info->header.protection_absent == 0 - && info->variable_header.number_of_raw_data_blocks_in_frame != 0 - && fread( buffered_sample->data, 1, 2, importer->stream ) != 2 ) - { - info->status = 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 = 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 = IMPORTER_EOF; - return 0; - } - if( ret != MP4SYS_ADTS_BASIC_HEADER_LENGTH ) - { - info->status = 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 = 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 = 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 = IMPORTER_CHANGE; - return 0; - } - /* no change which matters to mp4 muxing was found */ - info->status = IMPORTER_OK; - return 0; -} - -static void mp4sys_adts_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - lsmash_free( importer->info ); -} - -/* returns 0 if it seems adts. */ -static int mp4sys_adts_probe( importer_t *importer ) -{ - uint8_t buf[MP4SYS_ADTS_MAX_FRAME_LENGTH]; - if( fread( buf, 1, MP4SYS_ADTS_BASIC_HEADER_LENGTH, importer->stream ) != MP4SYS_ADTS_BASIC_HEADER_LENGTH ) - return -1; - - mp4sys_adts_fixed_header_t header = {0}; - mp4sys_adts_variable_header_t variable_header = {0}; - if( mp4sys_adts_parse_headers( importer->stream, buf, &header, &variable_header ) ) - return -1; - - /* now the stream seems valid ADTS */ - - lsmash_audio_summary_t* summary = mp4sys_adts_create_summary( &header ); - if( !summary ) - return -1; - - /* importer status */ - mp4sys_adts_info_t* info = lsmash_malloc_zero( sizeof(mp4sys_adts_info_t) ); - if( !info ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - info->status = IMPORTER_OK; - info->raw_data_block_idx = 0; - info->header = header; - info->variable_header = variable_header; - info->samples_in_frame = summary->samples_in_frame; - - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_free( info ); - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - importer->info = info; - - return 0; -} - -static uint32_t mp4sys_adts_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - mp4sys_adts_info_t *info = (mp4sys_adts_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - return info->samples_in_frame; -} - -const static importer_functions mp4sys_adts_importer = -{ - { "adts" }, - 1, - mp4sys_adts_probe, - mp4sys_adts_get_accessunit, - mp4sys_adts_get_last_delta, - mp4sys_adts_cleanup -}; - -/*************************************************************************** - mp3 (Legacy Interface) importer -***************************************************************************/ - -static void mp4sys_mp3_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - lsmash_free( importer->info ); -} - -typedef struct -{ - uint16_t syncword; /* <12> */ - uint8_t ID; /* <1> */ - uint8_t layer; /* <2> */ - uint8_t protection_bit; /* <1> */ - 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; - 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)!=3) -#define MP4SYS_LAYER_III 0x1 -#define MP4SYS_LAYER_II 0x2 -#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 int mp4sys_mp3_samples_in_frame( mp4sys_mp3_header_t *header ) -{ - if( header->layer == MP4SYS_LAYER_I ) - return 384; - else if( header->ID == 1 || header->layer == MP4SYS_LAYER_II ) - return 1152; - else - return 576; -} - -static lsmash_audio_summary_t *mp4sys_mp3_create_summary( mp4sys_mp3_header_t *header, int legacy_mode ) -{ - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; - 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->sample_size = 16; - summary->samples_in_frame = mp4sys_mp3_samples_in_frame( header ); - 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 */ -#if 0 /* FIXME: This is very unstable. Many players crash with this. */ - if( !legacy_mode ) - { - summary->object_type_indication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3; - if( lsmash_setup_AudioSpecificConfig( summary ) ) - { - lsmash_cleanup_summary( summary ); - return NULL; - } - } -#endif - uint32_t data_length; - uint8_t *data = mp4a_export_AudioSpecificConfig( MP4A_AUDIO_OBJECT_TYPE_Layer_1 + (MP4SYS_LAYER_I - header->layer), - summary->frequency, summary->channels, summary->sbr_mode, - NULL, 0, &data_length ); - if( !data ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return NULL; - } - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_free( data ); - return NULL; - } - lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured; - param->objectTypeIndication = header->ID ? MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3 : MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3; - param->streamType = MP4SYS_STREAM_TYPE_AudioStream; - if( lsmash_set_mp4sys_decoder_specific_info( param, data, data_length ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - lsmash_free( data ); - return NULL; - } - lsmash_free( data ); - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - return summary; -} - -typedef struct -{ - importer_status status; - mp4sys_mp3_header_t header; - uint8_t raw_header[MP4SYS_MP3_HEADER_LENGTH]; - uint32_t samples_in_frame; - uint32_t au_number; - uint16_t main_data_size[32]; /* size of main_data of the last 32 frames, FIFO */ - uint16_t prev_preroll_count; /* number of dependent frames of *previous* frame */ -} mp4sys_mp3_info_t; - -static int mp4sys_mp3_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - mp4sys_mp3_info_t *info = (mp4sys_mp3_info_t *)importer->info; - mp4sys_mp3_header_t *header = (mp4sys_mp3_header_t *)&info->header; - 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. */ - uint32_t div = frequency; - if( header->layer == MP4SYS_LAYER_III && header->ID == 0 ) - div <<= 1; - frame_size = 144 * 1000 * bitrate / div + header->padding_bit; - } - - if( current_status == IMPORTER_ERROR || frame_size <= 4 || buffered_sample->length < frame_size ) - return -1; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - if( current_status == IMPORTER_CHANGE ) - { - lsmash_audio_summary_t* summary = mp4sys_mp3_create_summary( header, 1 ); /* FIXME: use legacy mode. */ - if( !summary ) - return -1; - lsmash_entry_t* entry = lsmash_get_entry( importer->summaries, track_number ); - if( !entry || !entry->data ) - return -1; - lsmash_cleanup_summary( entry->data ); - entry->data = summary; - info->samples_in_frame = summary->samples_in_frame; - } - /* read a frame's data. */ - memcpy( buffered_sample->data, info->raw_header, MP4SYS_MP3_HEADER_LENGTH ); - frame_size -= MP4SYS_MP3_HEADER_LENGTH; - if( fread( ((uint8_t*)buffered_sample->data)+MP4SYS_MP3_HEADER_LENGTH, 1, frame_size, importer->stream ) != frame_size ) - { - info->status = IMPORTER_ERROR; - return -1; - } - buffered_sample->length = MP4SYS_MP3_HEADER_LENGTH + frame_size; - buffered_sample->dts = info->au_number++ * info->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - buffered_sample->prop.pre_roll.distance = header->layer == MP4SYS_LAYER_III ? 1 : 0; /* Layer III uses MDCT */ - - /* handle additional inter-frame dependency due to bit reservoir */ - if( header->layer == MP4SYS_LAYER_III ) - { - /* position of side_info */ - unsigned int sip = header->protection_bit ? 4 : 6; - unsigned int main_data_begin = buffered_sample->data[sip]; - if( header->ID == 1 ) - { - main_data_begin <<= 1; - main_data_begin |= (buffered_sample->data[sip + 1] >> 7); - } - if( main_data_begin > 0 ) - { - /* main_data_begin is a backpointer to the start of - * bit reservoir data for this frame. - * it contains total amount of bytes required from - * preceding frames. - * we just add up main_data size from history until it reaches - * the required amount. - */ - unsigned int reservoir_data = 0; - unsigned int i; - for( i = 0; i < 32 && reservoir_data < main_data_begin; ++i ) - { - reservoir_data += info->main_data_size[i]; - if( info->main_data_size[i] == 0 ) - break; - } - buffered_sample->prop.pre_roll.distance += info->prev_preroll_count; - info->prev_preroll_count = i; - } - uint16_t side_info_size; - if( header->ID == 1 ) - side_info_size = MP4SYS_MODE_IS_2CH( header->mode ) ? 32 : 17; - else - side_info_size = MP4SYS_MODE_IS_2CH( header->mode ) ? 17 : 9; - - /* pop back main_data_size[] and push main_data size of this frame - * to the front */ - memmove( info->main_data_size + 1, info->main_data_size, sizeof(info->main_data_size) - sizeof( info->main_data_size[0] ) ); - info->main_data_size[0] = frame_size - sip - side_info_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 = IMPORTER_EOF; - return 0; - } - if( ret == 1 && *buf == 0x00 ) - { - /* NOTE: ugly hack for mp1 stream created with SCMPX. */ - info->status = IMPORTER_EOF; - return 0; - } - if( ret != MP4SYS_MP3_HEADER_LENGTH ) - { - info->status = IMPORTER_ERROR; - return 0; - } - - mp4sys_mp3_header_t new_header = {0}; - if( mp4sys_mp3_parse_header( buf, &new_header ) ) - { - info->status = 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 = IMPORTER_ERROR; - return 0; - } - - /* currently supported "change(s)". */ - if( MP4SYS_MODE_IS_2CH( header->mode ) != MP4SYS_MODE_IS_2CH( new_header.mode ) ) - info->status = IMPORTER_CHANGE; - else - info->status = IMPORTER_OK; /* no change which matters to mp4 muxing was found */ - info->header = new_header; - return 0; -} - -static int mp4sys_mp3_probe( importer_t *importer ) -{ - uint8_t buf[MP4SYS_MP3_HEADER_LENGTH]; - if( fread( buf, 1, MP4SYS_MP3_HEADER_LENGTH, importer->stream ) != MP4SYS_MP3_HEADER_LENGTH ) - return -1; - - mp4sys_mp3_header_t header = {0}; - if( mp4sys_mp3_parse_header( buf, &header ) ) - return -1; - - /* now the stream seems valid mp3 */ - - lsmash_audio_summary_t* summary = mp4sys_mp3_create_summary( &header, 1 ); /* FIXME: use legacy mode. */ - if( !summary ) - return -1; - - /* importer status */ - mp4sys_mp3_info_t* info = lsmash_malloc_zero( sizeof(mp4sys_mp3_info_t) ); - if( !info ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - info->status = IMPORTER_OK; - info->header = header; - info->samples_in_frame = summary->samples_in_frame; - memcpy( info->raw_header, buf, MP4SYS_MP3_HEADER_LENGTH ); - - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_free( info ); - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - importer->info = info; - - return 0; -} - -static uint32_t mp4sys_mp3_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - mp4sys_mp3_info_t *info = (mp4sys_mp3_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - return info->samples_in_frame; -} - -const static importer_functions mp4sys_mp3_importer = -{ - { "MPEG-1/2BC_Audio_Legacy" }, - 1, - mp4sys_mp3_probe, - mp4sys_mp3_get_accessunit, - mp4sys_mp3_get_last_delta, - mp4sys_mp3_cleanup -}; - -/*************************************************************************** - AMR-NB/WB storage format importer - http://www.ietf.org/rfc/rfc3267.txt (Obsoleted) - http://www.ietf.org/rfc/rfc4867.txt -***************************************************************************/ -static void amr_cleanup( importer_t* importer ) -{ - debug_if( importer && importer->info ) - lsmash_free( importer->info ); -} - -typedef struct -{ - uint8_t wb; - uint32_t samples_in_frame; - uint32_t au_number; -} amr_importer_info_t; - -static int amr_get_accessunit( importer_t* importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( track_number != 1 ) - return -1; - amr_importer_info_t *info = (amr_importer_info_t *)importer->info; - - uint8_t* buf = buffered_sample->data; - if( fread( buf, 1, 1, importer->stream ) == 0 ) - { - /* EOF */ - buffered_sample->length = 0; - return 0; - } - uint8_t FT = (*buf >> 3) & 0x0F; - - /* AMR-NB has varieties of frame-size table like this. so I'm not sure yet. */ - const int frame_size[2][16] = { - { 13, 14, 16, 18, 20, 21, 27, 32, 5, 5, 5, 5, 0, 0, 0, 1 }, - { 18, 24, 33, 37, 41, 47, 51, 59, 61, 6, 6, 0, 0, 0, 1, 1 } - }; - int read_size = frame_size[info->wb][FT]; - if( read_size == 0 || buffered_sample->length < read_size-- ) - return -1; - if( read_size == 0 ) - buffered_sample->length = 1; - else - { - if( fread( buf+1, 1, read_size, importer->stream ) != read_size ) - return -1; - buffered_sample->length = read_size + 1; - } - buffered_sample->dts = info->au_number++ * info->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - return 0; -} - -#define MP4SYS_DAMR_LENGTH 17 - -int mp4sys_amr_create_damr( lsmash_audio_summary_t *summary ) -{ - lsmash_bs_t* bs = lsmash_bs_create(); - if( !bs ) - return -1; - lsmash_bs_put_be32( bs, MP4SYS_DAMR_LENGTH ); - lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_DAMR.fourcc ); - /* 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 */ - lsmash_codec_specific_t *specific = lsmash_malloc_zero( sizeof(lsmash_codec_specific_t) ); - if( !specific ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_bs_cleanup( bs ); - return -1; - } - specific->type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; - specific->format = LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED; - specific->destruct = (lsmash_codec_specific_destructor_t)lsmash_free; - specific->data.unstructured = lsmash_bs_export_data( bs, &specific->size ); - specific->size = MP4SYS_DAMR_LENGTH; - lsmash_bs_cleanup( bs ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return -1; - } - return 0; -} - -#define MP4SYS_AMR_STORAGE_MAGIC_LENGTH 6 -#define MP4SYS_AMRWB_EX_MAGIC_LENGTH 3 - -static int amr_probe( importer_t* importer ) -{ - uint8_t buf[MP4SYS_AMR_STORAGE_MAGIC_LENGTH]; - uint8_t wb = 0; - if( fread( buf, 1, MP4SYS_AMR_STORAGE_MAGIC_LENGTH, importer->stream ) != MP4SYS_AMR_STORAGE_MAGIC_LENGTH ) - return -1; - if( memcmp( buf, "#!AMR", MP4SYS_AMR_STORAGE_MAGIC_LENGTH-1 ) ) - return -1; - if( buf[MP4SYS_AMR_STORAGE_MAGIC_LENGTH-1] != '\n' ) - { - if( buf[MP4SYS_AMR_STORAGE_MAGIC_LENGTH-1] != '-' ) - return -1; - if( fread( buf, 1, MP4SYS_AMRWB_EX_MAGIC_LENGTH, importer->stream ) != MP4SYS_AMRWB_EX_MAGIC_LENGTH ) - return -1; - if( memcmp( buf, "WB\n", MP4SYS_AMRWB_EX_MAGIC_LENGTH ) ) - return -1; - wb = 1; - } - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return -1; - summary->sample_type = wb ? ISOM_CODEC_TYPE_SAWB_AUDIO : ISOM_CODEC_TYPE_SAMR_AUDIO; - summary->max_au_length = wb ? 61 : 32; - summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ - summary->frequency = (8000 << wb); - summary->channels = 1; - summary->sample_size = 16; - summary->samples_in_frame = (160 << wb); - summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ - amr_importer_info_t *info = lsmash_malloc( sizeof(amr_importer_info_t) ); - if( !info ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - info->wb = wb; - info->samples_in_frame = summary->samples_in_frame; - info->au_number = 0; - importer->info = info; - if( mp4sys_amr_create_damr( summary ) || lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_free( importer->info ); - importer->info = NULL; - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - return 0; -} - -static uint32_t amr_get_last_delta( importer_t* importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - amr_importer_info_t *info = (amr_importer_info_t *)importer->info; - if( !info || track_number != 1 ) - return 0; - return info->samples_in_frame; -} - -const static importer_functions amr_importer = -{ - { "amr" }, - 1, - amr_probe, - amr_get_accessunit, - amr_get_last_delta, - amr_cleanup -}; - -/*************************************************************************** - AC-3 importer - ETSI TS 102 366 V1.2.1 (2008-08) -***************************************************************************/ -#include "a52.h" - -#define AC3_SAMPLE_DURATION 1536 /* 256 (samples per audio block) * 6 (audio blocks) */ - -typedef struct -{ - importer_status status; - ac3_info_t info; -} ac3_importer_info_t; - -static void remove_ac3_importer_info( ac3_importer_info_t *info ) -{ - if( !info ) - return; - lsmash_bits_adhoc_cleanup( info->info.bits ); - lsmash_free( info ); -} - -static ac3_importer_info_t *create_ac3_importer_info( void ) -{ - ac3_importer_info_t *info = (ac3_importer_info_t *)lsmash_malloc_zero( sizeof(ac3_importer_info_t) ); - if( !info ) - return NULL; - info->info.bits = lsmash_bits_adhoc_create(); - if( !info->info.bits ) - { - lsmash_free( info ); - return NULL; - } - return info; -} - -static void ac3_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - remove_ac3_importer_info( importer->info ); -} - -static const uint32_t ac3_frame_size_table[19][3] = -{ - /* 48, 44.1, 32 */ - { 128, 138, 192 }, - { 160, 174, 240 }, - { 192, 208, 288 }, - { 224, 242, 336 }, - { 256, 278, 384 }, - { 320, 348, 480 }, - { 384, 416, 576 }, - { 448, 486, 672 }, - { 512, 556, 768 }, - { 640, 696, 960 }, - { 768, 834, 1152 }, - { 896, 974, 1344 }, - { 1024, 1114, 1536 }, - { 1280, 1392, 1920 }, - { 1536, 1670, 2304 }, - { 1792, 1950, 2688 }, - { 2048, 2228, 3072 }, - { 2304, 2506, 3456 }, - { 2560, 2786, 3840 } -}; - -static const uint32_t ac3_channel_count_table[8] = { 2, 1, 2, 3, 3, 4, 4, 5 }; - -#if 0 -/* FIXME: though this table is for AC-3 in QTFF, we don't support it yet since the structure of CODEC specific info is unknown. - * ChannelLayout is given by ac3_channel_layout_table[ acmod ][ lfeon ]. */ -static const lsmash_channel_layout_tag ac3_channel_layout_table[8][2] = -{ - /* LFE: off LFE: on */ - { QT_CHANNEL_LAYOUT_UNKNOWN, QT_CHANNEL_LAYOUT_UNKNOWN }, /* FIXME: dual mono */ - { QT_CHANNEL_LAYOUT_MONO, QT_CHANNEL_LAYOUT_AC3_1_0_1 }, - { QT_CHANNEL_LAYOUT_STEREO, QT_CHANNEL_LAYOUT_DVD_4 }, - { QT_CHANNEL_LAYOUT_AC3_3_0, QT_CHANNEL_LAYOUT_AC3_3_0_1 }, - { QT_CHANNEL_LAYOUT_DVD_2, QT_CHANNEL_LAYOUT_AC3_2_1_1 }, - { QT_CHANNEL_LAYOUT_AC3_3_1, QT_CHANNEL_LAYOUT_AC3_3_1_1 }, - { QT_CHANNEL_LAYOUT_DVD_3, QT_CHANNEL_LAYOUT_DVD_18 }, - { QT_CHANNEL_LAYOUT_MPEG_5_0_C, QT_CHANNEL_LAYOUT_MPEG_5_1_C } -}; -#endif - -static lsmash_audio_summary_t *ac3_create_summary( ac3_info_t *info ) -{ - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - lsmash_ac3_specific_parameters_t *param = &info->dac3_param; - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3, - LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - specific->data.unstructured = lsmash_create_ac3_specific_info( &info->dac3_param, &specific->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - summary->sample_type = ISOM_CODEC_TYPE_AC_3_AUDIO; - summary->max_au_length = AC3_MAX_SYNCFRAME_LENGTH; - summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ - summary->frequency = ac3_sample_rate_table[ param->fscod ]; - summary->channels = ac3_channel_count_table[ param->acmod ] + param->lfeon; - summary->sample_size = 16; /* no effect */ - summary->samples_in_frame = AC3_SAMPLE_DURATION; - summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ - return summary; -} - -static int ac3_compare_specific_param( lsmash_ac3_specific_parameters_t *a, lsmash_ac3_specific_parameters_t *b ) -{ - return (a->fscod != b->fscod) - || (a->bsid != b->bsid) - || (a->bsmod != b->bsmod) - || (a->acmod != b->acmod) - || (a->lfeon != b->lfeon) - || ((a->frmsizecod >> 1) != (b->frmsizecod >> 1)); -} - -static int ac3_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); - if( !summary ) - return -1; - ac3_importer_info_t *importer_info = (ac3_importer_info_t *)importer->info; - ac3_info_t *info = &importer_info->info; - importer_status current_status = importer_info->status; - if( current_status == IMPORTER_ERROR ) - return -1; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - lsmash_ac3_specific_parameters_t *param = &info->dac3_param; - uint32_t frame_size = ac3_frame_size_table[ param->frmsizecod >> 1 ][ param->fscod ]; - if( param->fscod == 0x1 && param->frmsizecod & 0x1 ) - frame_size += 2; - if( buffered_sample->length < frame_size ) - return -1; - if( current_status == IMPORTER_CHANGE ) - { - lsmash_codec_specific_t *specific = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_AC_3 ); - if( specific ) - { - specific->destruct( specific->data.unstructured ); - specific->data.unstructured = info->next_dac3; - } - summary->frequency = ac3_sample_rate_table[ param->fscod ]; - summary->channels = ac3_channel_count_table[ param->acmod ] + param->lfeon; - //summary->layout_tag = ac3_channel_layout_table[ param->acmod ][ param->lfeon ]; - } - if( frame_size > AC3_MIN_SYNCFRAME_LENGTH ) - { - uint32_t read_size = frame_size - AC3_MIN_SYNCFRAME_LENGTH; - if( fread( info->buffer + AC3_MIN_SYNCFRAME_LENGTH, 1, read_size, importer->stream ) != read_size ) - return -1; - } - memcpy( buffered_sample->data, info->buffer, frame_size ); - buffered_sample->length = frame_size; - buffered_sample->dts = info->au_number++ * summary->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - buffered_sample->prop.pre_roll.distance = 1; /* MDCT */ - if( fread( info->buffer, 1, AC3_MIN_SYNCFRAME_LENGTH, importer->stream ) != AC3_MIN_SYNCFRAME_LENGTH ) - importer_info->status = IMPORTER_EOF; - else - { - /* Parse the next syncframe header. */ - IF_A52_SYNCWORD( info->buffer ) - { - importer_info->status = IMPORTER_ERROR; - return current_status; - } - lsmash_ac3_specific_parameters_t current_param = info->dac3_param; - ac3_parse_syncframe_header( info, info->buffer ); - if( ac3_compare_specific_param( ¤t_param, &info->dac3_param ) ) - { - uint32_t dummy; - uint8_t *dac3 = lsmash_create_ac3_specific_info( &info->dac3_param, &dummy ); - if( !dac3 ) - { - importer_info->status = IMPORTER_ERROR; - return current_status; - } - importer_info->status = IMPORTER_CHANGE; - info->next_dac3 = dac3; - } - else - importer_info->status = IMPORTER_OK; - } - return current_status; -} - -static int ac3_importer_probe( importer_t* importer ) -{ - uint8_t buf[AC3_MIN_SYNCFRAME_LENGTH]; - if( fread( buf, 1, AC3_MIN_SYNCFRAME_LENGTH, importer->stream ) != AC3_MIN_SYNCFRAME_LENGTH ) - return -1; - IF_A52_SYNCWORD( buf ) - return -1; - ac3_importer_info_t *info = create_ac3_importer_info(); - if( !info ) - return -1; - if( ac3_parse_syncframe_header( &info->info, buf ) ) - { - remove_ac3_importer_info( info ); - return -1; - } - lsmash_audio_summary_t *summary = ac3_create_summary( &info->info ); - if( !summary ) - { - remove_ac3_importer_info( info ); - return -1; - } - info->status = IMPORTER_OK; - info->info.au_number = 0; - memcpy( info->info.buffer, buf, AC3_MIN_SYNCFRAME_LENGTH ); - importer->info = info; - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - remove_ac3_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - return 0; -} - -static uint32_t ac3_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - ac3_importer_info_t *info = (ac3_importer_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - return AC3_SAMPLE_DURATION; -} - -const static importer_functions ac3_importer = -{ - { "AC-3" }, - 1, - ac3_importer_probe, - ac3_importer_get_accessunit, - ac3_importer_get_last_delta, - ac3_importer_cleanup -}; - -/*************************************************************************** - Enhanced AC-3 importer - ETSI TS 102 366 V1.2.1 (2008-08) -***************************************************************************/ -#define EAC3_MIN_SAMPLE_DURATION 256 - -typedef struct -{ - importer_status status; - eac3_info_t info; -} eac3_importer_info_t; - -static void remove_eac3_importer_info( eac3_importer_info_t *info ) -{ - if( !info ) - return; - lsmash_destroy_multiple_buffers( info->info.au_buffers ); - lsmash_bits_adhoc_cleanup( info->info.bits ); - lsmash_free( info ); -} - -static eac3_importer_info_t *create_eac3_importer_info( void ) -{ - eac3_importer_info_t *info = (eac3_importer_info_t *)lsmash_malloc_zero( sizeof(eac3_importer_info_t) ); - if( !info ) - return NULL; - eac3_info_t *eac3_info = &info->info; - eac3_info->buffer_pos = eac3_info->buffer; - eac3_info->buffer_end = eac3_info->buffer; - eac3_info->bits = lsmash_bits_adhoc_create(); - if( !eac3_info->bits ) - { - lsmash_free( info ); - return NULL; - } - eac3_info->au_buffers = lsmash_create_multiple_buffers( 2, EAC3_MAX_SYNCFRAME_LENGTH ); - if( !eac3_info->au_buffers ) - { - lsmash_bits_adhoc_cleanup( eac3_info->bits ); - lsmash_free( info ); - return NULL; - } - eac3_info->au = lsmash_withdraw_buffer( eac3_info->au_buffers, 1 ); - eac3_info->incomplete_au = lsmash_withdraw_buffer( eac3_info->au_buffers, 2 ); - return info; -} - -static void eac3_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - remove_eac3_importer_info( importer->info ); -} - -static void eac3_update_sample_rate( lsmash_audio_summary_t *summary, lsmash_eac3_specific_parameters_t *dec3_param ) -{ - /* Additional independent substreams 1 to 7 must be encoded at the same sample rate as independent substream 0. */ - summary->frequency = ac3_sample_rate_table[ dec3_param->independent_info[0].fscod ]; - if( summary->frequency == 0 ) - { - static const uint32_t eac3_reduced_sample_rate_table[4] = { 24000, 22050, 16000, 0 }; - summary->frequency = eac3_reduced_sample_rate_table[ dec3_param->independent_info[0].fscod2 ]; - } -} - -#if 0 -/* FIXME: though this table is for EAC-3 in QTFF, we don't support it yet since the structure of CODEC specific info is unknown. */ -static void eac3_update_channel_layout( lsmash_audio_summary_t *summary, lsmash_eac3_substream_info_t *independent_info ) -{ - if( independent_info->chan_loc == 0 ) - { - summary->layout_tag = ac3_channel_layout_table[ independent_info->acmod ][ independent_info->lfeon ]; - return; - } - else if( independent_info->acmod != 0x7 ) - { - summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN; - return; - } - /* OK. All L, C, R, Ls and Rs exsist. */ - if( !independent_info->lfeon ) - { - if( independent_info->chan_loc == 0x80 ) - summary->layout_tag = QT_CHANNEL_LAYOUT_EAC_7_0_A; - else if( independent_info->chan_loc == 0x40 ) - summary->layout_tag = QT_CHANNEL_LAYOUT_EAC_6_0_A; - else - summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN; - return; - } - /* Also LFE exsists. */ - static const struct - { - uint16_t chan_loc; - lsmash_channel_layout_tag tag; - } eac3_channel_layout_table[] - = { - { 0x100, QT_CHANNEL_LAYOUT_EAC3_7_1_B }, - { 0x80, QT_CHANNEL_LAYOUT_EAC3_7_1_A }, - { 0x40, QT_CHANNEL_LAYOUT_EAC3_6_1_A }, - { 0x20, QT_CHANNEL_LAYOUT_EAC3_6_1_B }, - { 0x10, QT_CHANNEL_LAYOUT_EAC3_7_1_C }, - { 0x10, QT_CHANNEL_LAYOUT_EAC3_7_1_D }, - { 0x4, QT_CHANNEL_LAYOUT_EAC3_7_1_E }, - { 0x2, QT_CHANNEL_LAYOUT_EAC3_6_1_C }, - { 0x60, QT_CHANNEL_LAYOUT_EAC3_7_1_F }, - { 0x42, QT_CHANNEL_LAYOUT_EAC3_7_1_G }, - { 0x22, QT_CHANNEL_LAYOUT_EAC3_7_1_H }, - { 0 } - }; - for( int i = 0; eac3_channel_layout_table[i].chan_loc; i++ ) - if( independent_info->chan_loc == eac3_channel_layout_table[i].chan_loc ) - { - summary->layout_tag = eac3_channel_layout_table[i].tag; - return; - } - summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN; -} -#endif - -static void eac3_update_channel_info( lsmash_audio_summary_t *summary, lsmash_eac3_specific_parameters_t *dec3_param ) -{ - summary->channels = 0; - for( int i = 0; i <= dec3_param->num_ind_sub; i++ ) - { - int channel_count = 0; - lsmash_eac3_substream_info_t *independent_info = &dec3_param->independent_info[i]; - channel_count = ac3_channel_count_table[ independent_info->acmod ] /* L/C/R/Ls/Rs combination */ - + 2 * !!(independent_info->chan_loc & 0x100) /* Lc/Rc pair */ - + 2 * !!(independent_info->chan_loc & 0x80) /* Lrs/Rrs pair */ - + !!(independent_info->chan_loc & 0x40) /* Cs */ - + !!(independent_info->chan_loc & 0x20) /* Ts */ - + 2 * !!(independent_info->chan_loc & 0x10) /* Lsd/Rsd pair */ - + 2 * !!(independent_info->chan_loc & 0x8) /* Lw/Rw pair */ - + 2 * !!(independent_info->chan_loc & 0x4) /* Lvh/Rvh pair */ - + !!(independent_info->chan_loc & 0x2) /* Cvh */ - + !!(independent_info->chan_loc & 0x1) /* LFE2 */ - + independent_info->lfeon; /* LFE */ - if( channel_count > summary->channels ) - { - /* Pick the maximum number of channels. */ - summary->channels = channel_count; - //eac3_update_channel_layout( summary, independent_info ); - } - } -} - -static int eac3_importer_get_next_accessunit_internal( importer_t *importer ) -{ - int complete_au = 0; - eac3_importer_info_t *importer_info = (eac3_importer_info_t *)importer->info; - eac3_info_t *info = &importer_info->info; - while( !complete_au ) - { - /* Read data from the stream if needed. */ - uint32_t remainder_length = info->buffer_end - info->buffer_pos; - if( !info->no_more_read && remainder_length < EAC3_MAX_SYNCFRAME_LENGTH ) - { - if( remainder_length ) - memmove( info->buffer, info->buffer_pos, remainder_length ); - uint32_t read_size = fread( info->buffer + remainder_length, 1, EAC3_MAX_SYNCFRAME_LENGTH, importer->stream ); - remainder_length += read_size; - info->buffer_pos = info->buffer; - info->buffer_end = info->buffer + remainder_length; - info->no_more_read = read_size == 0 ? feof( importer->stream ) : 0; - } - /* Check the remainder length of the buffer. - * If there is enough length, then parse the syncframe in it. - * The length 5 is the required byte length to get frame size. */ - if( remainder_length < 5 ) - { - /* Reached the end of stream. - * According to ETSI TS 102 366 V1.2.1 (2008-08), - * one access unit consists of 6 audio blocks and begins with independent substream 0. - * The specification doesn't mention the case where a enhanced AC-3 stream ends at non-mod6 audio blocks. - * At the end of the stream, therefore, we might make an access unit which has less than 6 audio blocks anyway. */ - importer_info->status = IMPORTER_EOF; - complete_au = !!info->incomplete_au_length; - if( !complete_au ) - return remainder_length ? -1 : 0; /* No more access units in the stream. */ - if( !info->dec3_param_initialized ) - eac3_update_specific_param( info ); - } - else - { - /* Parse syncframe. */ - IF_A52_SYNCWORD( info->buffer_pos ) - return -1; - info->frame_size = 0; - if( eac3_parse_syncframe( info, info->buffer_pos, LSMASH_MIN( remainder_length, EAC3_MAX_SYNCFRAME_LENGTH ) ) ) - return -1; - if( remainder_length < info->frame_size ) - return -1; - int independent = info->strmtyp != 0x1; - if( independent && info->substreamid == 0x0 ) - { - if( info->number_of_audio_blocks == 6 ) - { - /* Encountered the first syncframe of the next access unit. */ - info->number_of_audio_blocks = 0; - complete_au = 1; - } - else if( info->number_of_audio_blocks > 6 ) - return -1; - info->number_of_audio_blocks += eac3_audio_block_table[ info->numblkscod ]; - info->number_of_independent_substreams = 0; - } - else if( info->syncframe_count == 0 ) - /* The first syncframe in an AU must be independent and assigned substream ID 0. */ - return -1; - if( independent ) - info->independent_info[info->number_of_independent_substreams ++].num_dep_sub = 0; - else - ++ info->independent_info[info->number_of_independent_substreams - 1].num_dep_sub; - } - if( complete_au ) - { - memcpy( info->au, info->incomplete_au, info->incomplete_au_length ); - info->au_length = info->incomplete_au_length; - info->incomplete_au_length = 0; - info->syncframe_count_in_au = info->syncframe_count; - info->syncframe_count = 0; - if( importer_info->status == IMPORTER_EOF ) - break; - } - /* Increase buffer size to store AU if short. */ - if( info->incomplete_au_length + info->frame_size > info->au_buffers->buffer_size ) - { - lsmash_multiple_buffers_t *temp = lsmash_resize_multiple_buffers( info->au_buffers, info->au_buffers->buffer_size + EAC3_MAX_SYNCFRAME_LENGTH ); - if( !temp ) - return -1; - info->au_buffers = temp; - info->au = lsmash_withdraw_buffer( info->au_buffers, 1 ); - info->incomplete_au = lsmash_withdraw_buffer( info->au_buffers, 2 ); - } - /* Append syncframe data. */ - memcpy( info->incomplete_au + info->incomplete_au_length, info->buffer_pos, info->frame_size ); - info->incomplete_au_length += info->frame_size; - info->buffer_pos += info->frame_size; - ++ info->syncframe_count; - } - return info->bits->bs->error ? -1 : 0; -} - -static int eac3_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); - if( !summary ) - return -1; - eac3_importer_info_t *importer_info = (eac3_importer_info_t *)importer->info; - eac3_info_t *info = &importer_info->info; - importer_status current_status = importer_info->status; - if( current_status == IMPORTER_ERROR || buffered_sample->length < info->au_length ) - return -1; - if( current_status == IMPORTER_EOF && info->au_length == 0 ) - { - buffered_sample->length = 0; - return 0; - } - if( current_status == IMPORTER_CHANGE ) - { - lsmash_codec_specific_t *specific = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ); - if( specific ) - { - specific->destruct( specific->data.unstructured ); - specific->data.unstructured = info->next_dec3; - specific->size = info->next_dec3_length; - } - summary->max_au_length = info->syncframe_count_in_au * EAC3_MAX_SYNCFRAME_LENGTH; - eac3_update_sample_rate( summary, &info->dec3_param ); - eac3_update_channel_info( summary, &info->dec3_param ); - } - memcpy( buffered_sample->data, info->au, info->au_length ); - buffered_sample->length = info->au_length; - buffered_sample->dts = info->au_number++ * summary->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - buffered_sample->prop.pre_roll.distance = 1; /* MDCT */ - if( importer_info->status == IMPORTER_EOF ) - { - info->au_length = 0; - return 0; - } - uint32_t old_syncframe_count_in_au = info->syncframe_count_in_au; - if( eac3_importer_get_next_accessunit_internal( importer ) ) - { - importer_info->status = IMPORTER_ERROR; - return current_status; - } - if( info->syncframe_count_in_au ) - { - /* Check sample description change. */ - uint32_t new_length; - uint8_t *dec3 = lsmash_create_eac3_specific_info( &info->dec3_param, &new_length ); - if( !dec3 ) - { - importer_info->status = IMPORTER_ERROR; - return current_status; - } - lsmash_codec_specific_t *specific = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3 ); - if( (info->syncframe_count_in_au > old_syncframe_count_in_au) - || (specific && (new_length != specific->size || memcmp( dec3, specific->data.unstructured, specific->size ))) ) - { - importer_info->status = IMPORTER_CHANGE; - info->next_dec3 = dec3; - info->next_dec3_length = new_length; - } - else - { - if( importer_info->status != IMPORTER_EOF ) - importer_info->status = IMPORTER_OK; - lsmash_free( dec3 ); - } - } - return current_status; -} - -static lsmash_audio_summary_t *eac3_create_summary( eac3_info_t *info ) -{ - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_EC_3, - LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - specific->data.unstructured = lsmash_create_eac3_specific_info( &info->dec3_param, &specific->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - summary->sample_type = ISOM_CODEC_TYPE_EC_3_AUDIO; - summary->max_au_length = info->syncframe_count_in_au * EAC3_MAX_SYNCFRAME_LENGTH; - summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ - summary->sample_size = 16; /* no effect */ - summary->samples_in_frame = EAC3_MIN_SAMPLE_DURATION * 6; /* 256 (samples per audio block) * 6 (audio blocks) */ - summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ - eac3_update_sample_rate( summary, &info->dec3_param ); - eac3_update_channel_info( summary, &info->dec3_param ); - return summary; -} - -static int eac3_importer_probe( importer_t *importer ) -{ - eac3_importer_info_t *info = create_eac3_importer_info(); - if( !info ) - return -1; - importer->info = info; - if( eac3_importer_get_next_accessunit_internal( importer ) ) - { - remove_eac3_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - lsmash_audio_summary_t *summary = eac3_create_summary( &info->info ); - if( !summary ) - { - remove_eac3_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - if( info->status != IMPORTER_EOF ) - info->status = IMPORTER_OK; - info->info.au_number = 0; - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - remove_eac3_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - return 0; -} - -static uint32_t eac3_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - eac3_importer_info_t *info = (eac3_importer_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF || info->info.au_length ) - return 0; - return EAC3_MIN_SAMPLE_DURATION * info->info.number_of_audio_blocks; -} - -const static importer_functions eac3_importer = -{ - { "Enhanced AC-3" }, - 1, - eac3_importer_probe, - eac3_importer_get_accessunit, - eac3_importer_get_last_delta, - eac3_importer_cleanup -}; - -/*************************************************************************** - MPEG-4 ALS importer - ISO/IEC 14496-3 2009 Fourth edition -***************************************************************************/ -#define ALSSC_TWELVE_LENGTH 22 - -typedef struct -{ - uint32_t size; - uint32_t samp_freq; - uint32_t samples; - uint32_t channels; - uint16_t frame_length; - uint8_t resolution; - uint8_t random_access; - uint8_t ra_flag; - uint32_t access_unit_size; - uint32_t number_of_ra_units; - uint32_t *ra_unit_size; - uint8_t *sc_data; - size_t alloc; -} als_specific_config_t; - -typedef struct -{ - importer_status status; - lsmash_stream_buffers_t *sb; - als_specific_config_t alssc; - uint32_t samples_in_frame; - uint32_t au_number; -} mp4sys_als_info_t; - -static void mp4sys_remove_als_info( mp4sys_als_info_t *info ) -{ - if( info->alssc.ra_unit_size ) - lsmash_free( info->alssc.ra_unit_size ); - if( info->alssc.sc_data ) - lsmash_free( info->alssc.sc_data ); - lsmash_stream_buffers_cleanup( info->sb ); - lsmash_free( info ); -} - -static void mp4sys_als_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - mp4sys_remove_als_info( importer->info ); -} - -static mp4sys_als_info_t *create_mp4sys_als_importer_info( importer_t *importer ) -{ - mp4sys_als_info_t *info = lsmash_malloc_zero( sizeof(mp4sys_als_info_t) ); - if( !info ) - return NULL; - info->sb = &importer->sb; - lsmash_stream_buffers_t *sb = info->sb; - lsmash_stream_buffers_setup( sb, LSMASH_STREAM_BUFFERS_TYPE_FILE, importer->stream ); - sb->bank = lsmash_create_multiple_buffers( 1, 1 << 16 ); - if( !sb->bank ) - { - lsmash_free( info ); - return NULL; - } - sb->start = lsmash_withdraw_buffer( sb->bank, 1 ); - sb->pos = sb->start; - sb->end = sb->start; - return info; -} - -static int als_prepare_buffer_read( lsmash_stream_buffers_t *sb, size_t buffer_size ) -{ - if( buffer_size <= sb->bank->buffer_size ) - return 0; - uintptr_t buffer_pos_offset = sb->pos - sb->start; - uintptr_t buffer_valid_length = sb->end - sb->start; - lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, buffer_size ); - if( !bank ) - return -1; - sb->bank = bank; - sb->start = lsmash_withdraw_buffer( bank, 1 ); - sb->pos = sb->start + buffer_pos_offset; - sb->end = sb->start + buffer_valid_length; - return 0; -} - -static void als_copy_from_buffer( als_specific_config_t *alssc, lsmash_stream_buffers_t *sb, size_t size ) -{ - if( alssc->alloc < size ) - { - size_t alloc = alssc->alloc ? (alssc->alloc << 1) : (1 << 16); - uint8_t *temp = lsmash_realloc( alssc->sc_data, alloc ); - if( !temp ) - return; - alssc->sc_data = temp; - alssc->alloc = alloc; - } - lsmash_stream_buffers_memcpy( alssc->sc_data + alssc->size, sb, size ); - alssc->size += size; -} - -#define CHECK_UPDATE( SIZE ) \ - if( sb->update( sb, (SIZE) - 1 ) < (SIZE) ) return -1 - -static int als_parse_specific_config( lsmash_stream_buffers_t *sb, als_specific_config_t *alssc ) -{ - /* Check ALS identifier( = 0x414C5300). */ - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - uint8_t *buf = sb->pos; - if( sb->end < sb->start + ALSSC_TWELVE_LENGTH - || buf[0] != 0x41 || buf[1] != 0x4C || buf[2] != 0x53 || buf[3] != 0x00 ) - return -1; - alssc->samp_freq = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; - alssc->samples = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11]; - if( alssc->samples == 0xffffffff ) - return -1; /* We don't support this case. */ - alssc->channels = (buf[12] << 8) | buf[13]; - alssc->resolution = (buf[14] & 0x1c) >> 2; - if( alssc->resolution > 3 ) - return -1; /* reserved */ - alssc->frame_length = (buf[15] << 8) | buf[16]; - alssc->random_access = buf[17]; - alssc->ra_flag = (buf[18] & 0xc0) >> 6; - if( alssc->ra_flag == 0 ) - return -1; /* We don't support this case. */ - buf[18] &= 0x3f; /* Set 0 to ra_flag. We will remove ra_unit_size in each access unit. */ -#if 0 - if( alssc->samples == 0xffffffff && alssc->ra_flag == 2 ) - return -1; -#endif - int chan_sort = !!(buf[20] & 0x1); - if( alssc->channels == 0 ) - { - if( buf[20] & 0x8 ) - return -1; /* If channels = 0 (mono), joint_stereo = 0. */ - else if( buf[20] & 0x4 ) - return -1; /* If channels = 0 (mono), mc_coding = 0. */ - else if( chan_sort ) - return -1; /* If channels = 0 (mono), chan_sort = 0. */ - } - int chan_config = !!(buf[20] & 0x2); - int crc_enabled = !!(buf[21] & 0x80); - int aux_data_enabled = !!(buf[21] & 0x1); - als_copy_from_buffer( alssc, sb, ALSSC_TWELVE_LENGTH ); - if( chan_config ) - { - /* chan_config_info */ - CHECK_UPDATE( 2 ); - als_copy_from_buffer( alssc, sb, 2 ); - } - if( chan_sort ) - { - uint32_t ChBits; /* ceil[log2(channels+1)] = 1...16 */ - for( ChBits = 1; alssc->channels >> ChBits; ChBits++ ); - uint32_t chan_pos_length = (alssc->channels + 1) * ChBits; - chan_pos_length = chan_pos_length / 8 + !!(chan_pos_length % 8); - if( als_prepare_buffer_read( sb, chan_pos_length ) < 0 ) - return -1; - CHECK_UPDATE( chan_pos_length ); - als_copy_from_buffer( alssc, sb, chan_pos_length ); - } - /* header_size and trailer_size */ - CHECK_UPDATE( 8 ); - uint32_t header_size = (sb->pos[0] << 24) | (sb->pos[1] << 16) | (sb->pos[2] << 8) | sb->pos[3]; - uint32_t trailer_size = (sb->pos[4] << 24) | (sb->pos[5] << 16) | (sb->pos[6] << 8) | sb->pos[7]; - als_copy_from_buffer( alssc, sb, 8 ); - /* orig_header, orig_trailer and crc. */ - uint32_t read_size = header_size * (header_size != 0xffffffff) + trailer_size * (trailer_size != 0xffffffff) + 4 * crc_enabled; - if( als_prepare_buffer_read( sb, read_size ) < 0 ) - return -1; - CHECK_UPDATE( read_size ); - als_copy_from_buffer( alssc, sb, read_size ); - /* Random access unit */ - uint32_t number_of_frames = (alssc->samples / (alssc->frame_length + 1)) + !!(alssc->samples % (alssc->frame_length + 1)); - if( alssc->random_access != 0 ) - alssc->number_of_ra_units = number_of_frames / alssc->random_access + !!(number_of_frames % alssc->random_access); - else - alssc->number_of_ra_units = 0; - if( alssc->ra_flag == 2 && alssc->random_access != 0 ) - { - /* We don't copy all ra_unit_size. */ - read_size = alssc->number_of_ra_units * 4; - if( als_prepare_buffer_read( sb, read_size ) < 0 ) - return -1; - CHECK_UPDATE( read_size ); - alssc->ra_unit_size = lsmash_malloc( read_size ); - if( !alssc->ra_unit_size ) - return -1; - uint32_t max_ra_unit_size = 0; - for( uint32_t i = 0; i < alssc->number_of_ra_units; i++ ) - { - if( sb->pos + 4 > sb->end ) - return -1; - alssc->ra_unit_size[i] = (sb->pos[0] << 24) | (sb->pos[1] << 16) | (sb->pos[2] << 8) | sb->pos[3]; - lsmash_stream_buffers_seek( sb, 4, SEEK_CUR ); - max_ra_unit_size = LSMASH_MAX( max_ra_unit_size, alssc->ra_unit_size[i] ); - } - if( max_ra_unit_size > sb->bank->buffer_size - && als_prepare_buffer_read( sb, max_ra_unit_size ) < 0 ) - return -1; - } - else - alssc->ra_unit_size = NULL; - /* auxiliary data */ - if( aux_data_enabled ) - { - CHECK_UPDATE( 4 ); - uint32_t aux_size = (sb->pos[0] << 24) | (sb->pos[1] << 16) | (sb->pos[2] << 8) | sb->pos[3]; - als_copy_from_buffer( alssc, sb, 4 ); - if( aux_size && aux_size != 0xffffffff ) - { - if( als_prepare_buffer_read( sb, aux_size ) < 0 ) - return -1; - CHECK_UPDATE( aux_size ); - als_copy_from_buffer( alssc, sb, aux_size ); - } - } - return 0; -} - -static int mp4sys_als_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); - if( !summary ) - return -1; - mp4sys_als_info_t *info = (mp4sys_als_info_t *)importer->info; - importer_status current_status = info->status; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - lsmash_stream_buffers_t *sb = info->sb; - als_specific_config_t *alssc = &info->alssc; - if( alssc->number_of_ra_units == 0 ) - { - lsmash_stream_buffers_memcpy( buffered_sample->data, sb, alssc->access_unit_size ); - buffered_sample->length = alssc->access_unit_size; - buffered_sample->cts = 0; - buffered_sample->dts = 0; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - info->status = IMPORTER_EOF; - return 0; - } - uint32_t au_length; - if( alssc->ra_flag == 2 ) - au_length = alssc->ra_unit_size[info->au_number]; - else /* if( alssc->ra_flag == 1 ) */ - { - CHECK_UPDATE( 4 ); - /* We remove ra_unit_size. */ - au_length = (sb->pos[0] << 24) | (sb->pos[1] << 16) | (sb->pos[2] << 8) | sb->pos[3]; - lsmash_stream_buffers_seek( sb, 4, SEEK_CUR ); - } - if( buffered_sample->length < au_length - || als_prepare_buffer_read( sb, au_length ) < 0 ) - return -1; - CHECK_UPDATE( au_length ); - lsmash_stream_buffers_memcpy( buffered_sample->data, sb, au_length ); - buffered_sample->length = au_length; - buffered_sample->dts = info->au_number++ * info->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - if( info->au_number == alssc->number_of_ra_units ) - info->status = IMPORTER_EOF; - return 0; -} - -#undef CHECK_UPDATE - -static lsmash_audio_summary_t *als_create_summary( lsmash_stream_buffers_t *sb, als_specific_config_t *alssc ) -{ - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; - summary->aot = MP4A_AUDIO_OBJECT_TYPE_ALS; - summary->frequency = alssc->samp_freq; - summary->channels = alssc->channels + 1; - summary->sample_size = (alssc->resolution + 1) * 8; - summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ - if( alssc->random_access != 0 ) - { - summary->samples_in_frame = (alssc->frame_length + 1) * alssc->random_access; - summary->max_au_length = summary->channels * (summary->sample_size / 8) * summary->samples_in_frame; - } - else - { - /* Read the remainder of overall stream as an access unit. */ - alssc->access_unit_size = lsmash_stream_buffers_get_remainder( sb ); - for( uint32_t buffer_size = sb->bank->buffer_size; !sb->no_more_read; buffer_size <<= 1 ) - { - if( als_prepare_buffer_read( sb, buffer_size ) < 0 ) - return NULL; - alssc->access_unit_size = sb->update( sb, buffer_size - 1 ); - } - summary->max_au_length = alssc->access_unit_size; - summary->samples_in_frame = 0; /* hack for mp4sys_als_importer_get_last_delta() */ - } - uint32_t data_length; - uint8_t *data = mp4a_export_AudioSpecificConfig( MP4A_AUDIO_OBJECT_TYPE_ALS, - summary->frequency, summary->channels, summary->sbr_mode, - alssc->sc_data, alssc->size, &data_length ); - if( !data ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return NULL; - } - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !specific ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_free( data ); - return NULL; - } - lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)specific->data.structured; - param->objectTypeIndication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3; - param->streamType = MP4SYS_STREAM_TYPE_AudioStream; - if( lsmash_set_mp4sys_decoder_specific_info( param, data, data_length ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - lsmash_free( data ); - return NULL; - } - lsmash_free( data ); - if( lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - return summary; -} - -static int mp4sys_als_importer_probe( importer_t *importer ) -{ - mp4sys_als_info_t *info = create_mp4sys_als_importer_info( importer ); - if( !info ) - return -1; - /* Parse ALS specific configuration. */ - if( als_parse_specific_config( info->sb, &info->alssc ) ) - { - mp4sys_remove_als_info( info ); - return -1; - } - lsmash_audio_summary_t *summary = als_create_summary( info->sb, &info->alssc ); - if( !summary ) - { - mp4sys_remove_als_info( info ); - return -1; - } - /* importer status */ - info->status = IMPORTER_OK; - info->samples_in_frame = summary->samples_in_frame; - if( lsmash_add_entry( importer->summaries, summary ) ) - { - mp4sys_remove_als_info( info ); - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - importer->info = info; - return 0; -} - -static uint32_t mp4sys_als_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - mp4sys_als_info_t *info = (mp4sys_als_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - als_specific_config_t *alssc = &info->alssc; - /* If alssc->number_of_ra_units == 0, then the last sample duration is just alssc->samples - * since als_create_summary sets 0 to summary->samples_in_frame i.e. info->samples_in_frame. */ - return alssc->samples - (alssc->number_of_ra_units - 1) * info->samples_in_frame; -} - -const static importer_functions mp4sys_als_importer = -{ - { "MPEG-4 ALS" }, - 1, - mp4sys_als_importer_probe, - mp4sys_als_importer_get_accessunit, - mp4sys_als_importer_get_last_delta, - mp4sys_als_importer_cleanup -}; - -/*************************************************************************** - DTS importer - ETSI TS 102 114 V1.2.1 (2002-12) - ETSI TS 102 114 V1.3.1 (2011-08) - ETSI TS 102 114 V1.4.1 (2012-09) -***************************************************************************/ -#include "dts.h" - -typedef struct -{ - importer_status status; - dts_info_t info; -} dts_importer_info_t; - -static void remove_dts_importer_info( dts_importer_info_t *info ) -{ - if( !info ) - return; - lsmash_destroy_multiple_buffers( info->info.au_buffers ); - lsmash_bits_adhoc_cleanup( info->info.bits ); - lsmash_free( info ); -} - -static dts_importer_info_t *create_dts_importer_info( void ) -{ - dts_importer_info_t *info = (dts_importer_info_t *)lsmash_malloc_zero( sizeof(dts_importer_info_t) ); - if( !info ) - return NULL; - dts_info_t *dts_info = &info->info; - dts_info->buffer_pos = dts_info->buffer; - dts_info->buffer_end = dts_info->buffer; - dts_info->bits = lsmash_bits_adhoc_create(); - if( !dts_info->bits ) - { - lsmash_free( info ); - return NULL; - } - dts_info->au_buffers = lsmash_create_multiple_buffers( 2, DTS_MAX_EXSS_SIZE ); - if( !dts_info->au_buffers ) - { - lsmash_bits_adhoc_cleanup( dts_info->bits ); - lsmash_free( info ); - return NULL; - } - dts_info->au = lsmash_withdraw_buffer( dts_info->au_buffers, 1 ); - dts_info->incomplete_au = lsmash_withdraw_buffer( dts_info->au_buffers, 2 ); - dts_setup_parser( dts_info ); - return info; -} - -static void dts_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - remove_dts_importer_info( importer->info ); -} - -static int dts_importer_get_next_accessunit_internal( importer_t *importer ) -{ - int complete_au = 0; - dts_importer_info_t *importer_info = (dts_importer_info_t *)importer->info; - dts_info_t *info = &importer_info->info; - while( !complete_au ) - { - /* Read data from the stream if needed. */ - uint32_t remainder_length = info->buffer_end - info->buffer_pos; - if( !info->no_more_read && remainder_length < DTS_MAX_EXSS_SIZE ) - { - if( remainder_length ) - memmove( info->buffer, info->buffer_pos, remainder_length ); - uint32_t read_size = fread( info->buffer + remainder_length, 1, DTS_MAX_EXSS_SIZE, importer->stream ); - remainder_length += read_size; - info->buffer_pos = info->buffer; - info->buffer_end = info->buffer + remainder_length; - info->no_more_read = read_size == 0 ? feof( importer->stream ) : 0; - } - /* Check the remainder length of the buffer. - * If there is enough length, then parse the frame in it. - * The length 10 is the required byte length to get frame size. */ - if( remainder_length < 10 ) - { - /* Reached the end of stream. */ - importer_info->status = IMPORTER_EOF; - complete_au = !!info->incomplete_au_length; - if( !complete_au ) - return remainder_length ? -1 : 0; /* No more access units in the stream. */ - if( !info->ddts_param_initialized ) - dts_update_specific_param( info ); - } - else - { - /* Parse substream frame. */ - dts_substream_type prev_substream_type = info->substream_type; - info->substream_type = dts_get_substream_type( info ); - int (*dts_parse_frame)( dts_info_t *, uint8_t *, uint32_t ) = NULL; - switch( info->substream_type ) - { - /* Decide substream frame parser and check if this frame and the previous frame belong to the same AU. */ - case DTS_SUBSTREAM_TYPE_CORE : - if( prev_substream_type != DTS_SUBSTREAM_TYPE_NONE ) - complete_au = 1; - dts_parse_frame = dts_parse_core_substream; - break; - case DTS_SUBSTREAM_TYPE_EXTENSION : - { - uint8_t prev_exss_index = info->exss_index; - if( dts_get_exss_index( info, &info->exss_index ) ) - return -1; - if( prev_substream_type == DTS_SUBSTREAM_TYPE_EXTENSION - && info->exss_index <= prev_exss_index ) - complete_au = 1; - dts_parse_frame = dts_parse_extension_substream; - break; - } - default : - return -1; - } - if( !info->ddts_param_initialized && complete_au ) - dts_update_specific_param( info ); - info->frame_size = 0; - if( dts_parse_frame( info, info->buffer_pos, LSMASH_MIN( remainder_length, DTS_MAX_EXSS_SIZE ) ) ) - return -1; /* Failed to parse. */ - } - if( complete_au ) - { - memcpy( info->au, info->incomplete_au, info->incomplete_au_length ); - info->au_length = info->incomplete_au_length; - info->incomplete_au_length = 0; - info->exss_count = (info->substream_type == DTS_SUBSTREAM_TYPE_EXTENSION); - if( importer_info->status == IMPORTER_EOF ) - break; - } - /* Increase buffer size to store AU if short. */ - if( info->incomplete_au_length + info->frame_size > info->au_buffers->buffer_size ) - { - lsmash_multiple_buffers_t *temp = lsmash_resize_multiple_buffers( info->au_buffers, info->au_buffers->buffer_size + DTS_MAX_EXSS_SIZE ); - if( !temp ) - return -1; - info->au_buffers = temp; - info->au = lsmash_withdraw_buffer( info->au_buffers, 1 ); - info->incomplete_au = lsmash_withdraw_buffer( info->au_buffers, 2 ); - } - /* Append frame data. */ - memcpy( info->incomplete_au + info->incomplete_au_length, info->buffer_pos, info->frame_size ); - info->incomplete_au_length += info->frame_size; - info->buffer_pos += info->frame_size; - } - return info->bits->bs->error ? -1 : 0; -} - -static int dts_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); - if( !summary ) - return -1; - dts_importer_info_t *importer_info = (dts_importer_info_t *)importer->info; - dts_info_t *info = &importer_info->info; - importer_status current_status = importer_info->status; - if( current_status == IMPORTER_ERROR || buffered_sample->length < info->au_length ) - return -1; - if( current_status == IMPORTER_EOF && info->au_length == 0 ) - { - buffered_sample->length = 0; - return 0; - } - if( current_status == IMPORTER_CHANGE ) - summary->max_au_length = 0; - memcpy( buffered_sample->data, info->au, info->au_length ); - buffered_sample->length = info->au_length; - buffered_sample->dts = info->au_number++ * summary->samples_in_frame; - buffered_sample->cts = buffered_sample->dts; - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - buffered_sample->prop.pre_roll.distance = !!(info->flags & DTS_EXT_SUBSTREAM_LBR_FLAG); /* MDCT */ - if( importer_info->status == IMPORTER_EOF ) - { - info->au_length = 0; - return 0; - } - if( dts_importer_get_next_accessunit_internal( importer ) ) - importer_info->status = IMPORTER_ERROR; - return current_status; -} - -static lsmash_audio_summary_t *dts_create_summary( dts_info_t *info ) -{ - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); - if( !summary ) - return NULL; - lsmash_dts_specific_parameters_t *param = &info->ddts_param; - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS, - LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - specific->data.unstructured = lsmash_create_dts_specific_info( param, &specific->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */ - summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */ - summary->sample_type = lsmash_dts_get_codingname( param ); - switch( param->DTSSamplingFrequency ) - { - case 12000 : /* Invalid? (No reference in the spec) */ - case 24000 : - case 48000 : - case 96000 : - case 192000 : - case 384000 : /* Invalid? (No reference in the spec) */ - summary->frequency = 48000; - break; - case 22050 : - case 44100 : - case 88200 : - case 176400 : - case 352800 : /* Invalid? (No reference in the spec) */ - summary->frequency = 44100; - break; - case 8000 : /* Invalid? (No reference in the spec) */ - case 16000 : - case 32000 : - case 64000 : - case 128000 : - summary->frequency = 32000; - break; - default : - summary->frequency = 0; - break; - } - summary->samples_in_frame = (summary->frequency * info->frame_duration) / param->DTSSamplingFrequency; - summary->max_au_length = DTS_MAX_CORE_SIZE + DTS_MAX_NUM_EXSS * DTS_MAX_EXSS_SIZE; - summary->sample_size = param->pcmSampleDepth; - summary->channels = dts_get_max_channel_count( info ); - return summary; -} - -static int dts_importer_probe( importer_t *importer ) -{ - dts_importer_info_t *info = create_dts_importer_info(); - if( !info ) - return -1; - importer->info = info; - if( dts_importer_get_next_accessunit_internal( importer ) ) - { - remove_dts_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - lsmash_audio_summary_t *summary = dts_create_summary( &info->info ); - if( !summary ) - { - remove_dts_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - if( info->status != IMPORTER_EOF ) - info->status = IMPORTER_OK; - info->info.au_number = 0; - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - remove_dts_importer_info( importer->info ); - importer->info = NULL; - return -1; - } - return 0; -} - -static uint32_t dts_importer_get_last_delta( importer_t* importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - dts_importer_info_t *info = (dts_importer_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF || info->info.au_length ) - return 0; - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number ); - if( !summary ) - return 0; - return (summary->frequency * info->info.frame_duration) / info->info.ddts_param.DTSSamplingFrequency; -} - -const static importer_functions dts_importer = -{ - { "DTS Coherent Acoustics" }, - 1, - dts_importer_probe, - dts_importer_get_accessunit, - dts_importer_get_last_delta, - dts_importer_cleanup -}; - -/*************************************************************************** - H.264 importer - ITU-T Recommendation H.264 (04/13) - ISO/IEC 14496-15:2010 -***************************************************************************/ -#include "h264.h" -#include "nalu.h" - -typedef struct -{ - importer_status status; - h264_info_t info; - lsmash_entry_list_t avcC_list[1]; /* stored as lsmash_codec_specific_t */ - lsmash_media_ts_list_t ts_list; - uint32_t max_au_length; - uint32_t num_undecodable; - uint32_t avcC_number; - uint32_t last_delta; - uint64_t last_intra_cts; - uint8_t composition_reordering_present; - uint8_t field_pic_present; -} h264_importer_info_t; - -typedef struct -{ - int64_t poc; - uint32_t delta; - uint16_t poc_delta; - uint16_t reset; -} nal_pic_timing_t; - -static void remove_h264_importer_info( h264_importer_info_t *info ) -{ - if( !info ) - return; - lsmash_remove_entries( info->avcC_list, lsmash_destroy_codec_specific_data ); - h264_cleanup_parser( &info->info ); - if( info->ts_list.timestamp ) - lsmash_free( info->ts_list.timestamp ); - lsmash_free( info ); -} - -static void h264_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - remove_h264_importer_info( importer->info ); -} - -static h264_importer_info_t *create_h264_importer_info( importer_t *importer ) -{ - h264_importer_info_t *info = lsmash_malloc_zero( sizeof(h264_importer_info_t) ); - if( !info ) - return NULL; - if( h264_setup_parser( &info->info, &importer->sb, 0, LSMASH_STREAM_BUFFERS_TYPE_FILE, importer->stream ) ) - { - remove_h264_importer_info( info ); - return NULL; - } - lsmash_init_entry_list( info->avcC_list ); - return info; -} - -static inline int h264_complete_au( h264_picture_info_t *picture, int probe ) -{ - if( !picture->incomplete_au_has_primary || picture->incomplete_au_length == 0 ) - return 0; - if( !probe ) - memcpy( picture->au, picture->incomplete_au, picture->incomplete_au_length ); - picture->au_length = picture->incomplete_au_length; - picture->incomplete_au_length = 0; - picture->incomplete_au_has_primary = 0; - return 1; -} - -static void h264_append_nalu_to_au( h264_picture_info_t *picture, uint8_t *src_nalu, uint32_t nalu_length, int probe ) -{ - if( !probe ) - { - uint8_t *dst_nalu = picture->incomplete_au + picture->incomplete_au_length + H264_DEFAULT_NALU_LENGTH_SIZE; - for( int i = H264_DEFAULT_NALU_LENGTH_SIZE; i; i-- ) - *(dst_nalu - i) = (nalu_length >> ((i - 1) * 8)) & 0xff; - memcpy( dst_nalu, src_nalu, nalu_length ); - } - /* Note: picture->incomplete_au_length shall be 0 immediately after AU has completed. - * Therefore, possible_au_length in h264_get_access_unit_internal() can't be used here - * to avoid increasing AU length monotonously through the entire stream. */ - picture->incomplete_au_length += H264_DEFAULT_NALU_LENGTH_SIZE + nalu_length; -} - -static inline void h264_get_au_internal_end( h264_importer_info_t *info, h264_picture_info_t *picture, h264_nalu_header_t *nalu_header, int no_more_buf ) -{ - if( info->info.buffer.sb->no_more_read && no_more_buf && (picture->incomplete_au_length == 0) ) - info->status = IMPORTER_EOF; - else if( info->status != IMPORTER_CHANGE ) - info->status = IMPORTER_OK; - info->info.nalu_header = *nalu_header; -} - -static int h264_get_au_internal_succeeded( h264_importer_info_t *info, h264_picture_info_t *picture, h264_nalu_header_t *nalu_header, int no_more_buf ) -{ - h264_get_au_internal_end( info, picture, nalu_header, no_more_buf ); - picture->au_number += 1; - return 0; -} - -static int h264_get_au_internal_failed( h264_importer_info_t *info, h264_picture_info_t *picture, h264_nalu_header_t *nalu_header, int no_more_buf, int complete_au ) -{ - h264_get_au_internal_end( info, picture, nalu_header, no_more_buf ); - if( complete_au ) - picture->au_number += 1; - return -1; -} - -static lsmash_video_summary_t *h264_create_summary -( - lsmash_h264_specific_parameters_t *param, - h264_sps_t *sps, - uint32_t max_au_length -) -{ - lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); - if( !summary ) - return NULL; - /* Update summary here. - * max_au_length is set at the last of mp4sys_h264_probe function. */ - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264, - LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - specific->data.unstructured = lsmash_create_h264_specific_info( param, &specific->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - summary->sample_type = ISOM_CODEC_TYPE_AVC1_VIDEO; - summary->max_au_length = max_au_length; - summary->timescale = sps->vui.time_scale; - summary->timebase = sps->vui.num_units_in_tick; - summary->vfr = !sps->vui.fixed_frame_rate_flag; - summary->sample_per_field = 0; - summary->width = sps->cropped_width; - summary->height = sps->cropped_height; - summary->par_h = sps->vui.sar_width; - summary->par_v = sps->vui.sar_height; - summary->color.primaries_index = sps->vui.colour_primaries; - summary->color.transfer_index = sps->vui.transfer_characteristics; - summary->color.matrix_index = sps->vui.matrix_coefficients; - summary->color.full_range = sps->vui.video_full_range_flag; - return summary; -} - -static int h264_store_codec_specific -( - h264_importer_info_t *info, - lsmash_h264_specific_parameters_t *avcC_param -) -{ - lsmash_codec_specific_t *src_cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !src_cs ) - return -1; - lsmash_h264_specific_parameters_t *src_param = (lsmash_h264_specific_parameters_t *)src_cs->data.structured; - *src_param = *avcC_param; - lsmash_codec_specific_t *dst_cs = lsmash_convert_codec_specific_format( src_cs, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - src_param->parameter_sets = NULL; /* Avoid freeing parameter sets within avcC_param. */ - lsmash_destroy_codec_specific_data( src_cs ); - if( !dst_cs || lsmash_add_entry( info->avcC_list, dst_cs ) ) - { - lsmash_destroy_codec_specific_data( dst_cs ); - return -1; - } - return 0; -} - -/* If probe equals 0, don't get the actual data (EBPS) of an access unit and only parse NALU. - * Currently, you can get AU of AVC video elemental stream only, not AVC parameter set elemental stream defined in 14496-15. */ -static int h264_get_access_unit_internal -( - h264_importer_info_t *importer_info, - int probe -) -{ - h264_info_t *info = &importer_info->info; - h264_slice_info_t *slice = &info->slice; - h264_picture_info_t *picture = &info->picture; - h264_stream_buffer_t *hb = &info->buffer; - lsmash_stream_buffers_t *sb = hb->sb; - h264_nalu_header_t nalu_header = info->nalu_header; - uint64_t consecutive_zero_byte_count = 0; - uint64_t ebsp_length = 0; - int no_more_buf = 0; - int complete_au = 0; - picture->au_length = 0; - picture->type = H264_PICTURE_TYPE_NONE; - picture->random_accessible = 0; - picture->recovery_frame_cnt = 0; - picture->has_mmco5 = 0; - picture->has_redundancy = 0; - picture->broken_link_flag = 0; - while( 1 ) - { - lsmash_stream_buffers_update( sb, 2 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - int no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !nalu_check_next_short_start_code( sb->pos, sb->end ) && !no_more ) - { - if( lsmash_stream_buffers_get_byte( sb ) ) - consecutive_zero_byte_count = 0; - else - ++consecutive_zero_byte_count; - ++ebsp_length; - continue; - } - if( no_more && ebsp_length == 0 ) - { - /* For the last NALU. - * This NALU already has been appended into the latest access unit and parsed. */ - h264_update_picture_info( info, picture, slice, &info->sei ); - h264_complete_au( picture, probe ); - return h264_get_au_internal_succeeded( importer_info, picture, &nalu_header, no_more_buf ); - } - uint64_t next_nalu_head_pos = info->ebsp_head_pos + ebsp_length + !no_more * H264_SHORT_START_CODE_LENGTH; - /* Memorize position of short start code of the next NALU in buffer. - * This is used when backward reading of stream doesn't occur. */ - uint8_t *next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - uint8_t nalu_type = nalu_header.nal_unit_type; - int read_back = 0; -#if 0 - if( probe ) - { - fprintf( stderr, "NALU type: %"PRIu8"\n", nalu_type ); - fprintf( stderr, " NALU header position: %"PRIx64"\n", info->ebsp_head_pos - nalu_header.length ); - fprintf( stderr, " EBSP position: %"PRIx64"\n", info->ebsp_head_pos ); - fprintf( stderr, " EBSP length: %"PRIx64" (%"PRIu64")\n", ebsp_length - consecutive_zero_byte_count, - ebsp_length - consecutive_zero_byte_count ); - fprintf( stderr, " consecutive_zero_byte_count: %"PRIx64"\n", consecutive_zero_byte_count ); - fprintf( stderr, " Next NALU header position: %"PRIx64"\n", next_nalu_head_pos ); - } -#endif - if( nalu_type == H264_NALU_TYPE_FD ) - { - /* We don't support streams with both filler and HRD yet. - * Otherwise, just skip filler because elemental streams defined in 14496-15 are forbidden to use filler. */ - if( info->sps.vui.hrd.present ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - } - else if( (nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SPS_EXT) - || nalu_type == H264_NALU_TYPE_SLICE_AUX ) - { - /* Get the EBSP of the current NALU here. - * AVC elemental stream defined in 14496-15 can recognizes from 0 to 13, and 19 of nal_unit_type. - * We don't support SVC and MVC elemental stream defined in 14496-15 yet. */ - ebsp_length -= consecutive_zero_byte_count; /* Any EBSP doesn't have zero bytes at the end. */ - uint64_t nalu_length = nalu_header.length + ebsp_length; - uint64_t possible_au_length = picture->incomplete_au_length + H264_DEFAULT_NALU_LENGTH_SIZE + nalu_length; - if( lsmash_stream_buffers_get_buffer_size( sb ) < possible_au_length ) - { - if( h264_supplement_buffer( hb, picture, 2 * possible_au_length ) ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - } - /* Move to the first byte of the current NALU. */ - read_back = (lsmash_stream_buffers_get_offset( sb ) < (nalu_length + consecutive_zero_byte_count)); - if( read_back ) - { - lsmash_fseek( sb->stream, info->ebsp_head_pos - nalu_header.length, SEEK_SET ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, nalu_length ); - if( lsmash_stream_buffers_get_valid_size( sb ) != nalu_length ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); -#if 0 - if( probe ) - fprintf( stderr, " ----Read Back\n" ); -#endif - } - else - lsmash_stream_buffers_seek( sb, -(nalu_length + consecutive_zero_byte_count), SEEK_CUR ); - if( nalu_type >= H264_NALU_TYPE_SLICE_N_IDR && nalu_type <= H264_NALU_TYPE_SLICE_IDR ) - { - /* VCL NALU (slice) */ - h264_slice_info_t prev_slice = *slice; - if( h264_parse_slice( info, &nalu_header, hb->rbsp, - lsmash_stream_buffers_get_pos( sb ) + nalu_header.length, ebsp_length ) ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - if( probe && info->avcC_pending ) - { - /* Copy and append a Codec Specific info. */ - if( h264_store_codec_specific( importer_info, &info->avcC_param ) < 0 ) - return -1; - } - if( h264_move_pending_avcC_param( info ) < 0 ) - return -1; - if( prev_slice.present ) - { - /* Check whether the AU that contains the previous VCL NALU completed or not. */ - if( h264_find_au_delimit_by_slice_info( slice, &prev_slice ) ) - { - /* The current NALU is the first VCL NALU of the primary coded picture of an new AU. - * Therefore, the previous slice belongs to the AU you want at this time. */ - h264_update_picture_info( info, picture, &prev_slice, &info->sei ); - complete_au = h264_complete_au( picture, probe ); - } - else - h264_update_picture_info_for_slice( info, picture, &prev_slice ); - } - h264_append_nalu_to_au( picture, lsmash_stream_buffers_get_pos( sb ), nalu_length, probe ); - slice->present = 1; - } - else - { - if( h264_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) - { - /* The last slice belongs to the AU you want at this time. */ - h264_update_picture_info( info, picture, slice, &info->sei ); - complete_au = h264_complete_au( picture, probe ); - } - else if( no_more ) - complete_au = h264_complete_au( picture, probe ); - switch( nalu_type ) - { - case H264_NALU_TYPE_SEI : - { - uint8_t *sei_pos = lsmash_stream_buffers_get_pos( sb ); - if( h264_parse_sei( info->bits, &info->sps, &info->sei, hb->rbsp, sei_pos + nalu_header.length, ebsp_length ) < 0 ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - h264_append_nalu_to_au( picture, sei_pos, nalu_length, probe ); - break; - } - case H264_NALU_TYPE_SPS : - if( h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPS, sb->pos, nalu_header.length + ebsp_length ) < 0 ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - break; - case H264_NALU_TYPE_PPS : - if( h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_PPS, sb->pos, nalu_header.length + ebsp_length ) < 0 ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - break; - case H264_NALU_TYPE_AUD : /* We drop access unit delimiters. */ - break; - case H264_NALU_TYPE_SPS_EXT : - if( h264_try_to_append_parameter_set( info, H264_PARAMETER_SET_TYPE_SPSEXT, sb->pos, nalu_header.length + ebsp_length ) < 0 ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - break; - default : - h264_append_nalu_to_au( picture, lsmash_stream_buffers_get_pos( sb ), nalu_length, probe ); - break; - } - if( info->avcC_pending ) - importer_info->status = IMPORTER_CHANGE; - } - } - /* Move to the first byte of the next NALU. */ - if( read_back ) - { - lsmash_fseek( sb->stream, next_nalu_head_pos, SEEK_SET ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - } - else - lsmash_stream_buffers_set_pos( sb, next_short_start_code_pos + H264_SHORT_START_CODE_LENGTH ); - info->prev_nalu_type = nalu_type; - lsmash_stream_buffers_update( sb, 0 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - ebsp_length = 0; - no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !no_more ) - { - /* Check the next NALU header. */ - if( h264_check_nalu_header( &nalu_header, sb, !!consecutive_zero_byte_count ) ) - return h264_get_au_internal_failed( importer_info, picture, &nalu_header, no_more_buf, complete_au ); - info->ebsp_head_pos = next_nalu_head_pos + nalu_header.length; - } - /* If there is no more data in the stream, and flushed chunk of NALUs, flush it as complete AU here. */ - else if( picture->incomplete_au_length && picture->au_length == 0 ) - { - h264_update_picture_info( info, picture, slice, &info->sei ); - h264_complete_au( picture, probe ); - return h264_get_au_internal_succeeded( importer_info, picture, &nalu_header, no_more_buf ); - } - if( complete_au ) - return h264_get_au_internal_succeeded( importer_info, picture, &nalu_header, no_more_buf ); - consecutive_zero_byte_count = 0; - } -} - -static int h264_importer_get_accessunit -( - importer_t *importer, - uint32_t track_number, - lsmash_sample_t *buffered_sample -) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - h264_importer_info_t *importer_info = (h264_importer_info_t *)importer->info; - h264_info_t *info = &importer_info->info; - importer_status current_status = importer_info->status; - if( current_status == IMPORTER_ERROR || buffered_sample->length < importer_info->max_au_length ) - return -1; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - if( h264_get_access_unit_internal( importer_info, 0 ) ) - { - importer_info->status = IMPORTER_ERROR; - return -1; - } - if( importer_info->status == IMPORTER_CHANGE && !info->avcC_pending ) - current_status = IMPORTER_CHANGE; - if( current_status == IMPORTER_CHANGE ) - { - /* Update the active summary. */ - lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( importer_info->avcC_list, ++ importer_info->avcC_number ); - if( !cs ) - return -1; - lsmash_h264_specific_parameters_t *avcC_param = (lsmash_h264_specific_parameters_t *)cs->data.structured; - lsmash_video_summary_t *summary = h264_create_summary( avcC_param, &info->sps, importer_info->max_au_length ); - if( !summary ) - return -1; - lsmash_remove_entry( importer->summaries, track_number, lsmash_cleanup_summary ); - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - importer_info->status = IMPORTER_OK; - } - h264_sps_t *sps = &info->sps; - h264_picture_info_t *picture = &info->picture; - buffered_sample->dts = importer_info->ts_list.timestamp[picture->au_number - 1].dts; - buffered_sample->cts = importer_info->ts_list.timestamp[picture->au_number - 1].cts; - if( picture->au_number < importer_info->num_undecodable ) - buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING; - else - buffered_sample->prop.leading = picture->independent || buffered_sample->cts >= importer_info->last_intra_cts - ? ISOM_SAMPLE_IS_NOT_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING; - if( picture->independent ) - importer_info->last_intra_cts = buffered_sample->cts; - if( importer_info->composition_reordering_present && !picture->disposable && !picture->idr ) - buffered_sample->prop.allow_earlier = QT_SAMPLE_EARLIER_PTS_ALLOWED; - buffered_sample->prop.independent = picture->independent ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT; - buffered_sample->prop.disposable = picture->disposable ? ISOM_SAMPLE_IS_DISPOSABLE : ISOM_SAMPLE_IS_NOT_DISPOSABLE; - buffered_sample->prop.redundant = picture->has_redundancy ? ISOM_SAMPLE_HAS_REDUNDANCY : ISOM_SAMPLE_HAS_NO_REDUNDANCY; - buffered_sample->prop.post_roll.identifier = picture->frame_num; - if( picture->random_accessible ) - { - if( picture->idr ) - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - else if( picture->recovery_frame_cnt ) - { - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START; - buffered_sample->prop.post_roll.complete = (picture->frame_num + picture->recovery_frame_cnt) % sps->MaxFrameNum; - } - else - { - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; - if( !picture->broken_link_flag ) - buffered_sample->prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC; - } - } - buffered_sample->length = picture->au_length; - memcpy( buffered_sample->data, picture->au, picture->au_length ); - return current_status; -} - -static void nalu_deduplicate_poc -( - nal_pic_timing_t *npt, - uint32_t *max_composition_delay, - uint32_t num_access_units, - uint32_t max_num_reorder_pics -) -{ - /* Deduplicate POCs. */ - int64_t poc_offset = 0; - int64_t poc_min = 0; - int64_t invalid_poc_min = 0; - uint32_t last_poc_reset = UINT32_MAX; - uint32_t invalid_poc_start = 0; - int invalid_poc_present = 0; - for( uint32_t i = 0; ; i++ ) - { - if( i < num_access_units && npt[i].poc != 0 && !npt[i].reset ) - { - /* poc_offset is not added to each POC here. - * It is done when we encounter the next coded video sequence. */ - if( npt[i].poc < 0 ) - { - /* Pictures with negative POC shall precede IDR-picture in composition order. - * The minimum POC is added to poc_offset when we encounter the next coded video sequence. */ - if( last_poc_reset == UINT32_MAX || i > last_poc_reset + max_num_reorder_pics ) - { - if( !invalid_poc_present ) - { - invalid_poc_present = 1; - invalid_poc_start = i; - } - if( invalid_poc_min > npt[i].poc ) - invalid_poc_min = npt[i].poc; - } - else if( poc_min > npt[i].poc ) - { - poc_min = npt[i].poc; - *max_composition_delay = LSMASH_MAX( *max_composition_delay, i - last_poc_reset ); - } - } - continue; - } - /* Encountered a new coded video sequence or no more POCs. - * Add poc_offset to each POC of the previous coded video sequence. */ - poc_offset -= poc_min; - int64_t poc_max = 0; - for( uint32_t j = last_poc_reset; j < i + !!npt[i].reset; j++ ) - if( npt[j].poc >= 0 || (j <= last_poc_reset + max_num_reorder_pics) ) - { - npt[j].poc += poc_offset; - if( poc_max < npt[j].poc ) - poc_max = npt[j].poc; - } - poc_offset = poc_max + 1; - if( invalid_poc_present ) - { - /* Pictures with invalid negative POC is probably supposed to be composited - * both before the next coded video sequence and after the current one. */ - poc_offset -= invalid_poc_min; - for( uint32_t j = invalid_poc_start; j < i + !!npt[i].reset; j++ ) - if( npt[j].poc < 0 ) - { - npt[j].poc += poc_offset; - if( poc_max < npt[j].poc ) - poc_max = npt[j].poc; - } - invalid_poc_present = 0; - invalid_poc_start = 0; - invalid_poc_min = 0; - poc_offset = poc_max + 1; - } - if( i < num_access_units ) - { - if( npt[i].reset ) - npt[i].poc = 0; - poc_min = 0; - last_poc_reset = i; - } - else - break; /* no more POCs */ - } -} - -static void nalu_generate_timestamps_from_poc -( - importer_t *importer, - lsmash_media_ts_t *timestamp, - nal_pic_timing_t *npt, - uint8_t *composition_reordering_present, - uint32_t *last_delta, - uint32_t max_composition_delay, - uint32_t num_access_units -) -{ - /* Check if composition delay derived from reordering is present. */ - if( max_composition_delay == 0 ) - { - for( uint32_t i = 1; i < num_access_units; i++ ) - if( npt[i].poc < npt[i - 1].poc ) - { - *composition_reordering_present = 1; - break; - } - } - else - *composition_reordering_present = 1; - /* Generate timestamps. */ - if( *composition_reordering_present ) - { - /* Generate timestamps. - * Here, DTSs and CTSs are temporary values for sort. */ - for( uint32_t i = 0; i < num_access_units; i++ ) - { - timestamp[i].cts = (uint64_t)npt[i].poc; - timestamp[i].dts = (uint64_t)i; - } - qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_cts ); - /* Check POC gap in output order. */ - for( uint32_t i = 1; i < num_access_units; i++ ) - if( timestamp[i].cts > timestamp[i - 1].cts + npt[i - 1].poc_delta ) - lsmash_log( importer, LSMASH_LOG_WARNING, - "POC gap is detected at picture %"PRIu64". Maybe some pictures are lost.\n", timestamp[i].dts ); - /* Get the maximum composition delay derived from reordering. */ - for( uint32_t i = 0; i < num_access_units; i++ ) - if( i < timestamp[i].dts ) - { - uint32_t composition_delay = timestamp[i].dts - i; - max_composition_delay = LSMASH_MAX( max_composition_delay, composition_delay ); - } - uint64_t *ts_buffer = (uint64_t *)lsmash_malloc( (num_access_units + max_composition_delay) * sizeof(uint64_t) ); - if( !ts_buffer ) - { - /* It seems that there is no enough memory to generate more appropriate timestamps. - * Anyway, generate CTSs and DTSs. */ - for( uint32_t i = 0; i < num_access_units; i++ ) - timestamp[i].cts = i + max_composition_delay; - qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_dts ); - *last_delta = 1; - return; - } - uint64_t *reorder_cts = ts_buffer; - uint64_t *prev_reorder_cts = ts_buffer + num_access_units; - *last_delta = npt[num_access_units - 1].delta; - /* Generate CTSs. */ - timestamp[0].cts = 0; - for( uint32_t i = 1; i < num_access_units; i++ ) - timestamp[i].cts = timestamp[i - 1].cts + npt[i - 1].delta; - int64_t composition_delay_time = timestamp[max_composition_delay].cts; - for( uint32_t i = 0; i < num_access_units; i++ ) - { - timestamp[i].cts += composition_delay_time; - reorder_cts[i] = timestamp[i].cts; - } - /* Generate DTSs. */ - qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_dts ); - for( uint32_t i = 0; i < num_access_units; i++ ) - { - timestamp[i].dts = i <= max_composition_delay - ? reorder_cts[i] - composition_delay_time - : prev_reorder_cts[(i - max_composition_delay) % max_composition_delay]; - prev_reorder_cts[i % max_composition_delay] = reorder_cts[i]; - } - lsmash_free( ts_buffer ); -#if 0 - fprintf( stderr, "max_composition_delay=%"PRIu32", composition_delay_time=%"PRIu64"\n", - max_composition_delay, composition_delay_time ); -#endif - } - else - { - timestamp[0].dts = 0; - timestamp[0].cts = 0; - for( uint32_t i = 1; i < num_access_units; i++ ) - { - timestamp[i].dts = timestamp[i - 1].dts + npt[i - 1].delta; - timestamp[i].cts = timestamp[i - 1].cts + npt[i - 1].delta; - } - *last_delta = npt[num_access_units - 1].delta; - } -} - -static void nalu_reduce_timescale -( - lsmash_media_ts_t *timestamp, - nal_pic_timing_t *npt, - uint32_t *last_delta, - uint32_t *timescale, - uint32_t num_access_units -) -{ - uint64_t gcd_delta = *timescale; - for( uint32_t i = 0; i < num_access_units && gcd_delta > 1; i++ ) - gcd_delta = lsmash_get_gcd( gcd_delta, npt[i].delta ); - if( gcd_delta > 1 ) - { - for( uint32_t i = 0; i < num_access_units; i++ ) - { - timestamp[i].dts /= gcd_delta; - timestamp[i].cts /= gcd_delta; - } - *last_delta /= gcd_delta; - *timescale /= gcd_delta; - } -#if 0 - for( uint32_t i = 0; i < num_access_units; i++ ) - fprintf( stderr, "Timestamp[%"PRIu32"]: POC=%"PRId64", DTS=%"PRIu64", CTS=%"PRIu64"\n", - i, npt[i].poc, timestamp[i].dts, timestamp[i].cts ); -#endif -} - -static int h264_importer_probe( importer_t *importer ) -{ -#define H264_LONG_START_CODE_LENGTH 4 -#define H264_CHECK_NEXT_LONG_START_CODE( x ) (!(x)[0] && !(x)[1] && !(x)[2] && ((x)[3] == 0x01)) - h264_importer_info_t *importer_info = create_h264_importer_info( importer ); - if( !importer_info ) - return -1; - h264_info_t *info = &importer_info->info; - h264_stream_buffer_t *hb = &info->buffer; - lsmash_stream_buffers_t *sb = hb->sb; /* shall be equal to &importer->sb */ - /* Find the first start code. */ - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - while( 1 ) - { - /* The first NALU of an AU in decoding order shall have long start code (0x00000001). */ - if( H264_CHECK_NEXT_LONG_START_CODE( sb->pos ) ) - break; - /* If the first trial of finding long start code failed, we assume this stream is not byte stream format of H.264. */ - if( lsmash_stream_buffers_get_remainder( sb ) == H264_LONG_START_CODE_LENGTH ) - goto fail; - /* Invalid if encountered any value of non-zero before the first start code. */ - if( lsmash_stream_buffers_get_byte( sb ) ) - goto fail; - } - /* OK. It seems the stream has a long start code of H.264. */ - importer->info = importer_info; - lsmash_stream_buffers_seek( sb, H264_LONG_START_CODE_LENGTH, SEEK_CUR ); - uint64_t first_ebsp_head_pos = lsmash_stream_buffers_get_offset( sb ); - lsmash_stream_buffers_update( sb, 0 ); - h264_nalu_header_t first_nalu_header; - if( h264_check_nalu_header( &first_nalu_header, sb, 1 ) ) - goto fail; - if( lsmash_stream_buffers_get_remainder( sb ) == 0 ) - goto fail; /* It seems the stream ends at the first incomplete access unit. */ - first_ebsp_head_pos += first_nalu_header.length; /* EBSP doesn't include NALU header. */ - importer_info->status = IMPORTER_OK; - info->nalu_header = first_nalu_header; - info->ebsp_head_pos = first_ebsp_head_pos; - /* Parse all NALU in the stream for preparation of calculating timestamps. */ - uint32_t npt_alloc = (1 << 12) * sizeof(nal_pic_timing_t); - nal_pic_timing_t *npt = lsmash_malloc( npt_alloc ); - if( !npt ) - goto fail; - uint32_t picture_stats[H264_PICTURE_TYPE_NONE + 1] = { 0 }; - uint32_t num_access_units = 0; - lsmash_log( importer, LSMASH_LOG_INFO, "Analyzing stream as H.264\r" ); - while( importer_info->status != IMPORTER_EOF ) - { -#if 0 - lsmash_log( importer, LSMASH_LOG_INFO, "Analyzing stream as H.264: %"PRIu32"\n", num_access_units + 1 ); -#endif - h264_picture_info_t *picture = &info->picture; - h264_picture_info_t prev_picture = *picture; - if( h264_get_access_unit_internal( importer_info, 1 ) - || h264_calculate_poc( info, picture, &prev_picture ) ) - { - lsmash_free( npt ); - goto fail; - } - if( npt_alloc <= num_access_units * sizeof(nal_pic_timing_t) ) - { - uint32_t alloc = 2 * num_access_units * sizeof(nal_pic_timing_t); - nal_pic_timing_t *temp = (nal_pic_timing_t *)lsmash_realloc( npt, alloc ); - if( !temp ) - { - lsmash_free( npt ); - goto fail; - } - npt = temp; - npt_alloc = alloc; - } - importer_info->field_pic_present |= info->picture.field_pic_flag; - npt[num_access_units].poc = picture->PicOrderCnt; - npt[num_access_units].delta = picture->delta; - npt[num_access_units].poc_delta = picture->field_pic_flag ? 1 : 2; - npt[num_access_units].reset = picture->has_mmco5; - ++num_access_units; - importer_info->max_au_length = LSMASH_MAX( info->picture.au_length, importer_info->max_au_length ); - if( picture->idr ) - ++picture_stats[H264_PICTURE_TYPE_IDR]; - else if( picture->type >= H264_PICTURE_TYPE_NONE ) - ++picture_stats[H264_PICTURE_TYPE_NONE]; - else - ++picture_stats[ picture->type ]; - } - fprintf( stderr, " \r" ); - lsmash_log( importer, LSMASH_LOG_INFO, - "IDR: %"PRIu32", I: %"PRIu32", P: %"PRIu32", B: %"PRIu32", " - "SI: %"PRIu32", SP: %"PRIu32", Unknown: %"PRIu32"\n", - picture_stats[H264_PICTURE_TYPE_IDR ], - picture_stats[H264_PICTURE_TYPE_I ], - picture_stats[H264_PICTURE_TYPE_I_P ], - picture_stats[H264_PICTURE_TYPE_I_P_B ], - picture_stats[H264_PICTURE_TYPE_SI ] - + picture_stats[H264_PICTURE_TYPE_I_SI ], - picture_stats[H264_PICTURE_TYPE_SI_SP ] - + picture_stats[H264_PICTURE_TYPE_I_SI_P_SP ] - + picture_stats[H264_PICTURE_TYPE_I_SI_P_SP_B], - picture_stats[H264_PICTURE_TYPE_NONE ] ); - /* Copy and append the last Codec Specific info. */ - if( h264_store_codec_specific( importer_info, &info->avcC_param ) < 0 ) - return -1; - /* Set up the first summary. */ - lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( importer_info->avcC_list, ++ importer_info->avcC_number ); - if( !cs || !cs->data.structured ) - { - lsmash_free( npt ); - goto fail; - } - lsmash_h264_specific_parameters_t *avcC_param = (lsmash_h264_specific_parameters_t *)cs->data.structured; - lsmash_video_summary_t *summary = h264_create_summary( avcC_param, &info->sps, importer_info->max_au_length ); - if( !summary || lsmash_add_entry( importer->summaries, summary ) ) - { - if( summary ) - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_free( npt ); - goto fail; - } - summary->sample_per_field = importer_info->field_pic_present; - /* */ - lsmash_media_ts_t *timestamp = lsmash_malloc( num_access_units * sizeof(lsmash_media_ts_t) ); - if( !timestamp ) - { - lsmash_free( npt ); - goto fail; - } - /* Count leading samples that are undecodable. */ - for( uint32_t i = 0; i < num_access_units; i++ ) - { - if( npt[i].poc == 0 ) - break; - ++ importer_info->num_undecodable; - } - /* Deduplicate POCs. */ - uint32_t max_composition_delay = 0; - nalu_deduplicate_poc( npt, &max_composition_delay, num_access_units, 32 ); - /* Generate timestamps. */ - nalu_generate_timestamps_from_poc( importer, timestamp, npt, - &importer_info->composition_reordering_present, - &importer_info->last_delta, - max_composition_delay, num_access_units ); - nalu_reduce_timescale( timestamp, npt, &importer_info->last_delta, &summary->timescale, num_access_units ); - lsmash_free( npt ); - importer_info->ts_list.sample_count = num_access_units; - importer_info->ts_list.timestamp = timestamp; - /* Go back to EBSP of the first NALU. */ - lsmash_fseek( sb->stream, first_ebsp_head_pos, SEEK_SET ); - importer_info->status = IMPORTER_OK; - info->nalu_header = first_nalu_header; - info->prev_nalu_type = 0; - sb->no_more_read = 0; - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - info->ebsp_head_pos = first_ebsp_head_pos; - uint8_t *temp_au = info->picture.au; - uint8_t *temp_incomplete_au = info->picture.incomplete_au; - memset( &info->picture, 0, sizeof(h264_picture_info_t) ); - info->picture.au = temp_au; - info->picture.incomplete_au = temp_incomplete_au; - memset( &info->slice, 0, sizeof(h264_slice_info_t) ); - memset( &info->sps, 0, sizeof(h264_sps_t) ); - memset( &info->pps, 0, sizeof(h264_pps_t) ); - lsmash_remove_entries( info->avcC_param.parameter_sets->sps_list, isom_remove_dcr_ps ); - lsmash_remove_entries( info->avcC_param.parameter_sets->pps_list, isom_remove_dcr_ps ); - lsmash_remove_entries( info->avcC_param.parameter_sets->spsext_list, isom_remove_dcr_ps ); - lsmash_destroy_h264_parameter_sets( &info->avcC_param_next ); - return 0; -fail: - remove_h264_importer_info( importer_info ); - importer->info = NULL; - lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); - return -1; -#undef H264_LONG_START_CODE_LENGTH -#undef H264_CHECK_NEXT_LONG_START_CODE -} - -static uint32_t h264_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - h264_importer_info_t *info = (h264_importer_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - return info->ts_list.sample_count - ? info->last_delta - : UINT32_MAX; /* arbitrary */ -} - -const static importer_functions h264_importer = -{ - { "H.264" }, - 1, - h264_importer_probe, - h264_importer_get_accessunit, - h264_importer_get_last_delta, - h264_importer_cleanup -}; - -/*************************************************************************** - HEVC importer - ITU-T Recommendation H.265 (04/13) - ISO/IEC 14496-15:2013 FDIS -***************************************************************************/ -#include "hevc.h" - -typedef struct -{ - const lsmash_class_t *class; - importer_status status; - hevc_info_t info; - lsmash_entry_list_t hvcC_list[1]; /* stored as lsmash_codec_specific_t */ - lsmash_media_ts_list_t ts_list; - uint32_t max_au_length; - uint32_t num_undecodable; - uint32_t hvcC_number; - uint32_t last_delta; - uint64_t last_intra_cts; - uint8_t composition_reordering_present; - uint8_t field_pic_present; - uint8_t max_TemporalId; -} hevc_importer_info_t; - -static void remove_hevc_importer_info( hevc_importer_info_t *info ) -{ - if( !info ) - return; - lsmash_remove_entries( info->hvcC_list, lsmash_destroy_codec_specific_data ); - hevc_cleanup_parser( &info->info ); - if( info->ts_list.timestamp ) - lsmash_free( info->ts_list.timestamp ); - lsmash_free( info ); -} - -static void hevc_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - remove_hevc_importer_info( importer->info ); -} - -static hevc_importer_info_t *create_hevc_importer_info( importer_t *importer ) -{ - hevc_importer_info_t *info = lsmash_malloc_zero( sizeof(hevc_importer_info_t) ); - if( !info ) - return NULL; - if( hevc_setup_parser( &info->info, &importer->sb, 0, LSMASH_STREAM_BUFFERS_TYPE_FILE, importer->stream ) ) - { - remove_hevc_importer_info( info ); - return NULL; - } - lsmash_init_entry_list( info->hvcC_list ); - return info; -} - -static inline int hevc_complete_au( hevc_access_unit_t *au, int probe ) -{ - if( !au->picture.has_primary || au->incomplete_length == 0 ) - return 0; - if( !probe ) - memcpy( au->data, au->incomplete_data, au->incomplete_length ); - au->TemporalId = au->picture.TemporalId; - au->length = au->incomplete_length; - au->incomplete_length = 0; - au->picture.has_primary = 0; - return 1; -} - -static void hevc_append_nalu_to_au( hevc_access_unit_t *au, uint8_t *src_nalu, uint32_t nalu_length, int probe ) -{ - if( !probe ) - { - uint8_t *dst_nalu = au->incomplete_data + au->incomplete_length + HEVC_DEFAULT_NALU_LENGTH_SIZE; - for( int i = HEVC_DEFAULT_NALU_LENGTH_SIZE; i; i-- ) - *(dst_nalu - i) = (nalu_length >> ((i - 1) * 8)) & 0xff; - memcpy( dst_nalu, src_nalu, nalu_length ); - } - /* Note: picture->incomplete_au_length shall be 0 immediately after AU has completed. - * Therefore, possible_au_length in hevc_get_access_unit_internal() can't be used here - * to avoid increasing AU length monotonously through the entire stream. */ - au->incomplete_length += HEVC_DEFAULT_NALU_LENGTH_SIZE + nalu_length; -} - -static inline void hevc_get_au_internal_end( hevc_importer_info_t *info, hevc_access_unit_t *au, hevc_nalu_header_t *nalu_header, int no_more_buf ) -{ - if( info->info.buffer.sb->no_more_read && no_more_buf && (au->incomplete_length == 0) ) - info->status = IMPORTER_EOF; - else if( info->status != IMPORTER_CHANGE ) - info->status = IMPORTER_OK; - info->info.nalu_header = *nalu_header; -} - -static int hevc_get_au_internal_succeeded( hevc_importer_info_t *info, hevc_access_unit_t *au, hevc_nalu_header_t *nalu_header, int no_more_buf ) -{ - hevc_get_au_internal_end( info, au, nalu_header, no_more_buf ); - au->number += 1; - return 0; -} - -static int hevc_get_au_internal_failed( hevc_importer_info_t *info, hevc_access_unit_t *au, hevc_nalu_header_t *nalu_header, int no_more_buf, int complete_au ) -{ - hevc_get_au_internal_end( info, au, nalu_header, no_more_buf ); - if( complete_au ) - au->number += 1; - return -1; -} - -static lsmash_video_summary_t *hevc_create_summary -( - lsmash_hevc_specific_parameters_t *param, - hevc_sps_t *sps, - uint32_t max_au_length -) -{ - lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); - if( !summary ) - return NULL; - /* Update summary here. - * max_au_length is set at the last of hevc_importer_probe function. */ - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC, - LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - specific->data.unstructured = lsmash_create_hevc_specific_info( param, &specific->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - summary->sample_type = ISOM_CODEC_TYPE_HVC1_VIDEO; - summary->max_au_length = max_au_length; - summary->timescale = sps->vui.time_scale; - summary->timebase = sps->vui.num_units_in_tick; - summary->vfr = (param->constantFrameRate == 0); - summary->sample_per_field = 0; - summary->width = sps->cropped_width; - summary->height = sps->cropped_height; - summary->par_h = sps->vui.sar_width; - summary->par_v = sps->vui.sar_height; - summary->color.primaries_index = sps->vui.colour_primaries != 2 ? sps->vui.colour_primaries : 0; - summary->color.transfer_index = sps->vui.transfer_characteristics != 2 ? sps->vui.transfer_characteristics : 0; - summary->color.matrix_index = sps->vui.matrix_coeffs != 2 ? sps->vui.matrix_coeffs : 0; - summary->color.full_range = sps->vui.video_full_range_flag; - lsmash_convert_crop_into_clap( sps->vui.def_disp_win_offset, summary->width, summary->height, &summary->clap ); - return summary; -} - -static int hevc_store_codec_specific -( - hevc_importer_info_t *info, - lsmash_hevc_specific_parameters_t *hvcC_param -) -{ - lsmash_codec_specific_t *src_cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_HEVC, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !src_cs ) - return -1; - lsmash_hevc_specific_parameters_t *src_param = (lsmash_hevc_specific_parameters_t *)src_cs->data.structured; - *src_param = *hvcC_param; - lsmash_codec_specific_t *dst_cs = lsmash_convert_codec_specific_format( src_cs, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - src_param->parameter_arrays = NULL; /* Avoid freeing parameter arrays within hvcC_param. */ - lsmash_destroy_codec_specific_data( src_cs ); - if( !dst_cs || lsmash_add_entry( info->hvcC_list, dst_cs ) ) - { - lsmash_destroy_codec_specific_data( dst_cs ); - return -1; - } - return 0; -} - -/* If probe equals 0, don't get the actual data (EBPS) of an access unit and only parse NALU. */ -static int hevc_get_access_unit_internal -( - hevc_importer_info_t *importer_info, - int probe -) -{ - hevc_info_t *info = &importer_info->info; - hevc_slice_info_t *slice = &info->slice; - hevc_access_unit_t *au = &info->au; - hevc_picture_info_t *picture = &au->picture; - hevc_stream_buffer_t *hb = &info->buffer; - lsmash_stream_buffers_t *sb = hb->sb; - hevc_nalu_header_t nalu_header = info->nalu_header; - uint64_t consecutive_zero_byte_count = 0; - uint64_t ebsp_length = 0; - int no_more_buf = 0; - int complete_au = 0; - picture->type = HEVC_PICTURE_TYPE_NONE; - picture->random_accessible = 0; - picture->recovery_poc_cnt = 0; - au->length = 0; - while( 1 ) - { - lsmash_stream_buffers_update( sb, 2 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - int no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !nalu_check_next_short_start_code( sb->pos, sb->end ) && !no_more ) - { - if( lsmash_stream_buffers_get_byte( sb ) ) - consecutive_zero_byte_count = 0; - else - ++consecutive_zero_byte_count; - ++ebsp_length; - continue; - } - if( no_more && ebsp_length == 0 ) - { - /* For the last NALU. - * This NALU already has been appended into the latest access unit and parsed. */ - hevc_update_picture_info( info, picture, slice, &info->sps, &info->sei ); - hevc_complete_au( au, probe ); - return hevc_get_au_internal_succeeded( importer_info, au, &nalu_header, no_more_buf ); - } - uint64_t next_nalu_head_pos = info->ebsp_head_pos + ebsp_length + !no_more * HEVC_SHORT_START_CODE_LENGTH; - /* Memorize position of short start code of the next NALU in buffer. - * This is used when backward reading of stream doesn't occur. */ - uint8_t *next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - uint8_t nalu_type = nalu_header.nal_unit_type; - int read_back = 0; -#if 0 - if( probe ) - { - fprintf( stderr, "NALU type: %"PRIu8"\n", nalu_type ); - fprintf( stderr, " NALU header position: %"PRIx64"\n", info->ebsp_head_pos - nalu_header.length ); - fprintf( stderr, " EBSP position: %"PRIx64"\n", info->ebsp_head_pos ); - fprintf( stderr, " EBSP length: %"PRIx64" (%"PRIu64")\n", ebsp_length - consecutive_zero_byte_count, - ebsp_length - consecutive_zero_byte_count ); - fprintf( stderr, " consecutive_zero_byte_count: %"PRIx64"\n", consecutive_zero_byte_count ); - fprintf( stderr, " Next NALU header position: %"PRIx64"\n", next_nalu_head_pos ); - } -#endif - if( nalu_type == HEVC_NALU_TYPE_FD ) - { - /* We don't support streams with both filler and HRD yet. - * Otherwise, just skip filler because elemental streams defined in 14496-15 are forbidden to use filler. */ - if( info->sps.vui.hrd.present ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - } - else if( nalu_type <= HEVC_NALU_TYPE_RASL_R - || (nalu_type >= HEVC_NALU_TYPE_BLA_W_LP && nalu_type <= HEVC_NALU_TYPE_CRA) - || (nalu_type >= HEVC_NALU_TYPE_VPS && nalu_type <= HEVC_NALU_TYPE_SUFFIX_SEI) ) - { - /* Get the EBSP of the current NALU here. */ - ebsp_length -= consecutive_zero_byte_count; /* Any EBSP doesn't have zero bytes at the end. */ - uint64_t nalu_length = nalu_header.length + ebsp_length; - uint64_t possible_au_length = au->incomplete_length + HEVC_DEFAULT_NALU_LENGTH_SIZE + nalu_length; - if( lsmash_stream_buffers_get_buffer_size( sb ) < possible_au_length ) - { - if( hevc_supplement_buffer( hb, au, 2 * possible_au_length ) ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - next_short_start_code_pos = lsmash_stream_buffers_get_pos( sb ); - } - /* Move to the first byte of the current NALU. */ - read_back = (lsmash_stream_buffers_get_offset( sb ) < (nalu_length + consecutive_zero_byte_count)); - if( read_back ) - { - lsmash_fseek( sb->stream, info->ebsp_head_pos - nalu_header.length, SEEK_SET ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, nalu_length ); - if( lsmash_stream_buffers_get_valid_size( sb ) != nalu_length ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); -#if 0 - if( probe ) - fprintf( stderr, " ----Read Back\n" ); -#endif - } - else - lsmash_stream_buffers_seek( sb, -(nalu_length + consecutive_zero_byte_count), SEEK_CUR ); - if( nalu_type <= HEVC_NALU_TYPE_RSV_VCL31 ) - { - /* VCL NALU (slice) */ - hevc_slice_info_t prev_slice = *slice; - if( hevc_parse_slice_segment_header( info, &nalu_header, hb->rbsp, - lsmash_stream_buffers_get_pos( sb ) + nalu_header.length, ebsp_length ) ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - if( probe && info->hvcC_pending ) - { - /* Copy and append a Codec Specific info. */ - if( hevc_store_codec_specific( importer_info, &info->hvcC_param ) < 0 ) - return -1; - } - if( hevc_move_pending_hvcC_param( info ) < 0 ) - return -1; - if( prev_slice.present ) - { - /* Check whether the AU that contains the previous VCL NALU completed or not. */ - if( hevc_find_au_delimit_by_slice_info( info, slice, &prev_slice ) ) - { - /* The current NALU is the first VCL NALU of the primary coded picture of a new AU. - * Therefore, the previous slice belongs to the AU you want at this time. */ - hevc_update_picture_info( info, picture, &prev_slice, &info->sps, &info->sei ); - complete_au = hevc_complete_au( au, probe ); - } - else - hevc_update_picture_info_for_slice( info, picture, &prev_slice ); - } - hevc_append_nalu_to_au( au, lsmash_stream_buffers_get_pos( sb ), nalu_length, probe ); - slice->present = 1; - } - else - { - if( hevc_find_au_delimit_by_nalu_type( nalu_type, info->prev_nalu_type ) ) - { - /* The last slice belongs to the AU you want at this time. */ - hevc_update_picture_info( info, picture, slice, &info->sps, &info->sei ); - complete_au = hevc_complete_au( au, probe ); - } - else if( no_more ) - complete_au = hevc_complete_au( au, probe ); - switch( nalu_type ) - { - case HEVC_NALU_TYPE_PREFIX_SEI : - case HEVC_NALU_TYPE_SUFFIX_SEI : - { - uint8_t *sei_pos = lsmash_stream_buffers_get_pos( sb ); - if( hevc_parse_sei( info->bits, &info->vps, &info->sps, &info->sei, &nalu_header, - hb->rbsp, sei_pos + nalu_header.length, ebsp_length ) ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - hevc_append_nalu_to_au( au, sei_pos, nalu_length, probe ); - break; - } - case HEVC_NALU_TYPE_VPS : - if( hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_VPS, sb->pos, nalu_header.length + ebsp_length ) < 0 ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - break; - case HEVC_NALU_TYPE_SPS : - if( hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_SPS, sb->pos, nalu_header.length + ebsp_length ) < 0 ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - break; - case HEVC_NALU_TYPE_PPS : - if( hevc_try_to_append_dcr_nalu( info, HEVC_DCR_NALU_TYPE_PPS, sb->pos, nalu_header.length + ebsp_length ) < 0 ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - break; - case HEVC_NALU_TYPE_AUD : /* We drop access unit delimiters. */ - break; - default : - hevc_append_nalu_to_au( au, lsmash_stream_buffers_get_pos( sb ), nalu_length, probe ); - break; - } - if( info->hvcC_pending ) - importer_info->status = IMPORTER_CHANGE; - } - } - /* Move to the first byte of the next NALU. */ - if( read_back ) - { - lsmash_fseek( sb->stream, next_nalu_head_pos, SEEK_SET ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - } - else - lsmash_stream_buffers_set_pos( sb, next_short_start_code_pos + HEVC_SHORT_START_CODE_LENGTH ); - info->prev_nalu_type = nalu_type; - lsmash_stream_buffers_update( sb, 1 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - ebsp_length = 0; - no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !no_more ) - { - /* Check the next NALU header. */ - if( hevc_check_nalu_header( &nalu_header, sb, !!consecutive_zero_byte_count ) ) - return hevc_get_au_internal_failed( importer_info, au, &nalu_header, no_more_buf, complete_au ); - info->ebsp_head_pos = next_nalu_head_pos + nalu_header.length; - } - /* If there is no more data in the stream, and flushed chunk of NALUs, flush it as complete AU here. */ - else if( au->incomplete_length && au->length == 0 ) - { - hevc_update_picture_info( info, picture, slice, &info->sps, &info->sei ); - hevc_complete_au( au, probe ); - return hevc_get_au_internal_succeeded( importer_info, au, &nalu_header, no_more_buf ); - } - if( complete_au ) - return hevc_get_au_internal_succeeded( importer_info, au, &nalu_header, no_more_buf ); - consecutive_zero_byte_count = 0; - } -} - -static int hevc_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - hevc_importer_info_t *importer_info = (hevc_importer_info_t *)importer->info; - hevc_info_t *info = &importer_info->info; - importer_status current_status = importer_info->status; - if( current_status == IMPORTER_ERROR || buffered_sample->length < importer_info->max_au_length ) - return -1; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - if( hevc_get_access_unit_internal( importer_info, 0 ) ) - { - importer_info->status = IMPORTER_ERROR; - return -1; - } - if( importer_info->status == IMPORTER_CHANGE && !info->hvcC_pending ) - current_status = IMPORTER_CHANGE; - if( current_status == IMPORTER_CHANGE ) - { - /* Update the active summary. */ - lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( importer_info->hvcC_list, ++ importer_info->hvcC_number ); - if( !cs ) - return -1; - lsmash_hevc_specific_parameters_t *hvcC_param = (lsmash_hevc_specific_parameters_t *)cs->data.structured; - lsmash_video_summary_t *summary = hevc_create_summary( hvcC_param, &info->sps, importer_info->max_au_length ); - if( !summary ) - return -1; - lsmash_remove_entry( importer->summaries, track_number, lsmash_cleanup_summary ); - if( lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - return -1; - } - importer_info->status = IMPORTER_OK; - } - //hevc_sps_t *sps = &info->sps; - hevc_access_unit_t *au = &info->au; - hevc_picture_info_t *picture = &au->picture; - buffered_sample->dts = importer_info->ts_list.timestamp[ au->number - 1 ].dts; - buffered_sample->cts = importer_info->ts_list.timestamp[ au->number - 1 ].cts; - /* Set property of disposability. */ - if( picture->sublayer_nonref && au->TemporalId == importer_info->max_TemporalId ) - /* Sub-layer non-reference pictures are not referenced by subsequent pictures of - * the same sub-layer in decoding order. */ - buffered_sample->prop.disposable = ISOM_SAMPLE_IS_DISPOSABLE; - else - buffered_sample->prop.disposable = ISOM_SAMPLE_IS_NOT_DISPOSABLE; - /* Set property of leading. */ - if( picture->radl || picture->rasl ) - buffered_sample->prop.leading = picture->radl ? ISOM_SAMPLE_IS_DECODABLE_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING; - else - { - if( au->number < importer_info->num_undecodable ) - buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING; - else - { - if( picture->independent || buffered_sample->cts >= importer_info->last_intra_cts ) - buffered_sample->prop.leading = ISOM_SAMPLE_IS_NOT_LEADING; - else - buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING; - } - } - if( picture->independent ) - importer_info->last_intra_cts = buffered_sample->cts; - /* Set property of independence. */ - buffered_sample->prop.independent = picture->independent ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT; - buffered_sample->prop.redundant = ISOM_SAMPLE_HAS_NO_REDUNDANCY; - buffered_sample->prop.post_roll.identifier = picture->poc; - if( picture->random_accessible ) - { - if( picture->irap ) - { - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - if( picture->closed_rap ) - buffered_sample->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_CLOSED_RAP; - else - buffered_sample->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; - } - else if( picture->recovery_poc_cnt ) - { - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START; - buffered_sample->prop.post_roll.complete = picture->poc + picture->recovery_poc_cnt; - } - else - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; - } - buffered_sample->length = au->length; - memcpy( buffered_sample->data, au->data, au->length ); - return current_status; -} - -static int hevc_importer_probe( importer_t *importer ) -{ -#define HEVC_LONG_START_CODE_LENGTH 4 -#define HEVC_CHECK_NEXT_LONG_START_CODE( x ) (!(x)[0] && !(x)[1] && !(x)[2] && ((x)[3] == 0x01)) - hevc_importer_info_t *importer_info = create_hevc_importer_info( importer ); - if( !importer_info ) - return -1; - hevc_info_t *info = &importer_info->info; - hevc_stream_buffer_t *hb = &info->buffer; - lsmash_stream_buffers_t *sb = hb->sb; /* shall be equal to &importer->sb */ - /* Find the first start code. */ - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - while( 1 ) - { - /* The first NALU of an AU in decoding order shall have long start code (0x00000001). */ - if( HEVC_CHECK_NEXT_LONG_START_CODE( sb->pos ) ) - break; - /* If the first trial of finding long start code failed, we assume this stream is not byte stream format of H.264. */ - if( lsmash_stream_buffers_get_remainder( sb ) == HEVC_LONG_START_CODE_LENGTH ) - goto fail; - /* Invalid if encountered any value of non-zero before the first start code. */ - if( lsmash_stream_buffers_get_byte( sb ) ) - goto fail; - } - /* OK. It seems the stream has a long start code of H.264. */ - importer->info = importer_info; - lsmash_stream_buffers_seek( sb, HEVC_LONG_START_CODE_LENGTH, SEEK_CUR ); - uint64_t first_ebsp_head_pos = lsmash_stream_buffers_get_offset( sb ); - lsmash_stream_buffers_update( sb, 0 ); - hevc_nalu_header_t first_nalu_header; - if( hevc_check_nalu_header( &first_nalu_header, sb, 1 ) ) - goto fail; - if( lsmash_stream_buffers_get_remainder( sb ) == 0 ) - goto fail; /* It seems the stream ends at the first incomplete access unit. */ - first_ebsp_head_pos += first_nalu_header.length; /* EBSP doesn't include NALU header. */ - importer_info->status = IMPORTER_OK; - info->nalu_header = first_nalu_header; - info->ebsp_head_pos = first_ebsp_head_pos; - info->prev_nalu_type = HEVC_NALU_TYPE_UNKNOWN; - /* Parse all NALU in the stream for preparation of calculating timestamps. */ - uint32_t npt_alloc = (1 << 12) * sizeof(nal_pic_timing_t); - nal_pic_timing_t *npt = (nal_pic_timing_t *)lsmash_malloc( npt_alloc ); - if( !npt ) - goto fail; - uint32_t picture_stats[HEVC_PICTURE_TYPE_NONE + 1] = { 0 }; - uint32_t num_access_units = 0; - lsmash_log( importer, LSMASH_LOG_INFO, "Analyzing stream as HEVC\r" ); - while( importer_info->status != IMPORTER_EOF ) - { -#if 0 - lsmash_log( importer, LSMASH_LOG_INFO, "Analyzing stream as HEVC: %"PRIu32"\n", num_access_units + 1 ); -#endif - hevc_picture_info_t *picture = &info->au.picture; - hevc_picture_info_t prev_picture = *picture; - picture->first = (num_access_units == 0); - if( hevc_get_access_unit_internal( importer_info, 1 ) - || hevc_calculate_poc( info, &info->au.picture, &prev_picture ) ) - { - lsmash_free( npt ); - goto fail; - } - if( npt_alloc <= num_access_units * sizeof(nal_pic_timing_t) ) - { - uint32_t alloc = 2 * num_access_units * sizeof(nal_pic_timing_t); - nal_pic_timing_t *temp = (nal_pic_timing_t *)lsmash_realloc( npt, alloc ); - if( !temp ) - { - lsmash_free( npt ); - goto fail; - } - npt = temp; - npt_alloc = alloc; - } - importer_info->field_pic_present |= picture->field_coded; - npt[num_access_units].poc = picture->poc; - npt[num_access_units].delta = picture->delta; - npt[num_access_units].poc_delta = 1; - npt[num_access_units].reset = 0; - ++num_access_units; - importer_info->max_au_length = LSMASH_MAX( importer_info->max_au_length, info->au.length ); - importer_info->max_TemporalId = LSMASH_MAX( importer_info->max_TemporalId, info->au.TemporalId ); - if( picture->idr ) - ++picture_stats[HEVC_PICTURE_TYPE_IDR]; - else if( picture->type >= HEVC_PICTURE_TYPE_NONE ) - ++picture_stats[HEVC_PICTURE_TYPE_NONE]; - else - ++picture_stats[ picture->type ]; - } - fprintf( stderr, " \r" ); - lsmash_log( importer, LSMASH_LOG_INFO, - "IDR: %"PRIu32", I: %"PRIu32", P: %"PRIu32", B: %"PRIu32", Unknown: %"PRIu32"\n", - picture_stats[HEVC_PICTURE_TYPE_IDR], picture_stats[HEVC_PICTURE_TYPE_I], - picture_stats[HEVC_PICTURE_TYPE_I_P], picture_stats[HEVC_PICTURE_TYPE_I_P_B], - picture_stats[HEVC_PICTURE_TYPE_NONE]); - /* Copy and append the last Codec Specific info. */ - if( hevc_store_codec_specific( importer_info, &info->hvcC_param ) < 0 ) - return -1; - /* Set up the first summary. */ - lsmash_codec_specific_t *cs = (lsmash_codec_specific_t *)lsmash_get_entry_data( importer_info->hvcC_list, ++ importer_info->hvcC_number ); - if( !cs || !cs->data.structured ) - { - lsmash_free( npt ); - goto fail; - } - lsmash_hevc_specific_parameters_t *hvcC_param = (lsmash_hevc_specific_parameters_t *)cs->data.structured; - lsmash_video_summary_t *summary = hevc_create_summary( hvcC_param, &info->sps, importer_info->max_au_length ); - if( !summary || lsmash_add_entry( importer->summaries, summary ) ) - { - if( summary ) - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_free( npt ); - goto fail; - } - summary->sample_per_field = importer_info->field_pic_present; - /* */ - lsmash_media_ts_t *timestamp = lsmash_malloc( num_access_units * sizeof(lsmash_media_ts_t) ); - if( !timestamp ) - { - lsmash_free( npt ); - goto fail; - } - /* Count leading samples that are undecodable. */ - for( uint32_t i = 0; i < num_access_units; i++ ) - { - if( npt[i].poc == 0 ) - break; - ++ importer_info->num_undecodable; - } - /* Deduplicate POCs. */ - uint32_t max_composition_delay = 0; - nalu_deduplicate_poc( npt, &max_composition_delay, num_access_units, 15 ); - /* Generate timestamps. */ - nalu_generate_timestamps_from_poc( importer, timestamp, npt, - &importer_info->composition_reordering_present, - &importer_info->last_delta, - max_composition_delay, num_access_units ); - summary->timescale *= 2; /* We assume that picture timing is in field level. - * For HEVC, it seems time_scale is set in frame level basically. - * So multiply by 2 for reducing timebase and timescale. */ - nalu_reduce_timescale( timestamp, npt, &importer_info->last_delta, &summary->timescale, num_access_units ); - lsmash_free( npt ); - importer_info->ts_list.sample_count = num_access_units; - importer_info->ts_list.timestamp = timestamp; - /* Go back to EBSP of the first NALU. */ - lsmash_fseek( sb->stream, first_ebsp_head_pos, SEEK_SET ); - importer_info->status = IMPORTER_OK; - info->nalu_header = first_nalu_header; - info->ebsp_head_pos = first_ebsp_head_pos; - info->prev_nalu_type = HEVC_NALU_TYPE_UNKNOWN; - sb->no_more_read = 0; - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - uint8_t *temp_au = info->au.data; - uint8_t *temp_incomplete_au = info->au.incomplete_data; - memset( &info->au, 0, sizeof(hevc_access_unit_t) ); - info->au.data = temp_au; - info->au.incomplete_data = temp_incomplete_au; - memset( &info->slice, 0, sizeof(hevc_slice_info_t) ); - memset( &info->vps, 0, sizeof(hevc_vps_t) ); - memset( &info->sps, 0, sizeof(hevc_sps_t) ); - memset( &info->pps, 0, sizeof(hevc_pps_t) ); - for( int i = 0; i < HEVC_DCR_NALU_TYPE_NUM; i++ ) - lsmash_remove_entries( info->hvcC_param.parameter_arrays->ps_array[i].list, isom_remove_dcr_ps ); - lsmash_destroy_hevc_parameter_arrays( &info->hvcC_param_next ); - return 0; -fail: - remove_hevc_importer_info( importer_info ); - importer->info = NULL; - lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); - return -1; -#undef HEVC_LONG_START_CODE_LENGTH -#undef HEVC_CHECK_NEXT_LONG_START_CODE -} - -static uint32_t hevc_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - hevc_importer_info_t *info = (hevc_importer_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - return info->ts_list.sample_count - ? info->last_delta - : UINT32_MAX; /* arbitrary */ -} - -const static importer_functions hevc_importer = -{ - { "HEVC" }, - 1, - hevc_importer_probe, - hevc_importer_get_accessunit, - hevc_importer_get_last_delta, - hevc_importer_cleanup -}; - -/*************************************************************************** - SMPTE VC-1 importer (only for Advanced Profile) - SMPTE 421M-2006 - SMPTE RP 2025-2007 -***************************************************************************/ -#include "vc1.h" - -typedef struct -{ - importer_status status; - vc1_info_t info; - vc1_sequence_header_t first_sequence; - lsmash_media_ts_list_t ts_list; - uint8_t composition_reordering_present; - uint32_t max_au_length; - uint32_t num_undecodable; - uint64_t last_ref_intra_cts; -} vc1_importer_info_t; - -static void remove_vc1_importer_info( vc1_importer_info_t *info ) -{ - if( !info ) - return; - vc1_cleanup_parser( &info->info ); - if( info->ts_list.timestamp ) - lsmash_free( info->ts_list.timestamp ); - lsmash_free( info ); -} - -static void vc1_importer_cleanup( importer_t *importer ) -{ - debug_if( importer && importer->info ) - remove_vc1_importer_info( importer->info ); -} - -static vc1_importer_info_t *create_vc1_importer_info( importer_t *importer ) -{ - vc1_importer_info_t *info = lsmash_malloc_zero( sizeof(vc1_importer_info_t) ); - if( !info ) - return NULL; - if( vc1_setup_parser( &info->info, &importer->sb, 0, LSMASH_STREAM_BUFFERS_TYPE_FILE, importer->stream ) ) - { - remove_vc1_importer_info( info ); - return NULL; - } - return info; -} - -static inline int vc1_complete_au( vc1_access_unit_t *access_unit, vc1_picture_info_t *picture, int probe ) -{ - if( !picture->present ) - return 0; - if( !probe ) - memcpy( access_unit->data, access_unit->incomplete_data, access_unit->incomplete_data_length ); - access_unit->data_length = access_unit->incomplete_data_length; - access_unit->incomplete_data_length = 0; - vc1_update_au_property( access_unit, picture ); - return 1; -} - -static inline void vc1_append_ebdu_to_au( vc1_access_unit_t *access_unit, uint8_t *ebdu, uint32_t ebdu_length, int probe ) -{ - if( !probe ) - memcpy( access_unit->incomplete_data + access_unit->incomplete_data_length, ebdu, ebdu_length ); - /* Note: access_unit->incomplete_data_length shall be 0 immediately after AU has completed. - * Therefore, possible_au_length in vc1_get_access_unit_internal() can't be used here - * to avoid increasing AU length monotonously through the entire stream. */ - access_unit->incomplete_data_length += ebdu_length; -} - -static inline void vc1_get_au_internal_end( vc1_importer_info_t *info, vc1_access_unit_t *access_unit, uint8_t bdu_type, int no_more_buf ) -{ - info->status = info->info.buffer.sb->no_more_read && no_more_buf && (access_unit->incomplete_data_length == 0) - ? IMPORTER_EOF - : IMPORTER_OK; - info->info.bdu_type = bdu_type; -} - -static int vc1_get_au_internal_succeeded( vc1_importer_info_t *info, vc1_access_unit_t *access_unit, uint8_t bdu_type, int no_more_buf ) -{ - vc1_get_au_internal_end( info, access_unit, bdu_type, no_more_buf ); - access_unit->number += 1; - return 0; -} - -static int vc1_get_au_internal_failed( vc1_importer_info_t *info, vc1_access_unit_t *access_unit, uint8_t bdu_type, int no_more_buf, int complete_au ) -{ - vc1_get_au_internal_end( info, access_unit, bdu_type, no_more_buf ); - if( complete_au ) - access_unit->number += 1; - return -1; -} - -static int vc1_importer_get_access_unit_internal( vc1_importer_info_t *importer_info, int probe ) -{ - vc1_info_t *info = &importer_info->info; - vc1_access_unit_t *access_unit = &info->access_unit; - vc1_stream_buffer_t *hb = &info->buffer; - lsmash_stream_buffers_t *sb = hb->sb; - uint8_t bdu_type = info->bdu_type; - uint64_t consecutive_zero_byte_count = 0; - uint64_t ebdu_length = 0; - int no_more_buf = 0; - int complete_au = 0; - access_unit->data_length = 0; - while( 1 ) - { - lsmash_stream_buffers_update( sb, 2 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - int no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !vc1_check_next_start_code_prefix( sb->pos, sb->end ) && !no_more ) - { - if( lsmash_stream_buffers_get_byte( sb ) ) - consecutive_zero_byte_count = 0; - else - ++consecutive_zero_byte_count; - ++ebdu_length; - continue; - } - if( no_more && ebdu_length == 0 ) - { - /* For the last EBDU. - * This EBDU already has been appended into the latest access unit and parsed. */ - vc1_complete_au( access_unit, &info->picture, probe ); - return vc1_get_au_internal_succeeded( importer_info, access_unit, bdu_type, no_more_buf ); - } - ebdu_length += VC1_START_CODE_LENGTH; - uint64_t next_scs_file_offset = info->ebdu_head_pos + ebdu_length + !no_more * VC1_START_CODE_PREFIX_LENGTH; - /* Memorize position of beginning of the next EBDU in buffer. - * This is used when backward reading of stream doesn't occur. */ - uint8_t *next_ebdu_pos = lsmash_stream_buffers_get_pos( sb ); - int read_back = 0; -#if 0 - if( probe ) - { - fprintf( stderr, "BDU type: %"PRIu8" \n", bdu_type ); - fprintf( stderr, " EBDU position: %"PRIx64" \n", info->ebdu_head_pos ); - fprintf( stderr, " EBDU length: %"PRIx64" (%"PRIu64") \n", ebdu_length - consecutive_zero_byte_count, - ebdu_length - consecutive_zero_byte_count ); - fprintf( stderr, " consecutive_zero_byte_count: %"PRIx64" \n", consecutive_zero_byte_count ); - fprintf( stderr, " Next start code suffix position: %"PRIx64"\n", next_scs_file_offset ); - } -#endif - if( bdu_type >= 0x0A && bdu_type <= 0x0F ) - { - /* Get the current EBDU here. */ - ebdu_length -= consecutive_zero_byte_count; /* Any EBDU doesn't have zero bytes at the end. */ - uint64_t possible_au_length = access_unit->incomplete_data_length + ebdu_length; - if( lsmash_stream_buffers_get_buffer_size( sb ) < possible_au_length ) - { - if( vc1_supplement_buffer( hb, access_unit, 2 * possible_au_length ) ) - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); - next_ebdu_pos = lsmash_stream_buffers_get_pos( sb ); - } - /* Move to the first byte of the current EBDU. */ - read_back = (lsmash_stream_buffers_get_offset( sb ) < (ebdu_length + consecutive_zero_byte_count)); - if( read_back ) - { - lsmash_fseek( sb->stream, info->ebdu_head_pos, SEEK_SET ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, ebdu_length ); - if( lsmash_stream_buffers_get_valid_size( sb ) != ebdu_length ) - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); -#if 0 - if( probe ) - fprintf( stderr, " ----Read Back\n" ); -#endif - } - else - lsmash_stream_buffers_seek( sb, -(ebdu_length + consecutive_zero_byte_count), SEEK_CUR ); - /* Complete the current access unit if encountered delimiter of current access unit. */ - if( vc1_find_au_delimit_by_bdu_type( bdu_type, info->prev_bdu_type ) ) - /* The last video coded EBDU belongs to the access unit you want at this time. */ - complete_au = vc1_complete_au( access_unit, &info->picture, probe ); - /* Process EBDU by its BDU type and append it to access unit. */ - switch( bdu_type ) - { - /* FRM_SC: Frame start code - * FLD_SC: Field start code - * SLC_SC: Slice start code - * SEQ_SC: Sequence header start code - * EP_SC: Entry-point start code - * PIC_L: Picture layer - * SLC_L: Slice layer - * SEQ_L: Sequence layer - * EP_L: Entry-point layer */ - case 0x0D : /* Frame - * For the Progressive or Frame Interlace mode, shall signal the beginning of a new video frame. - * For the Field Interlace mode, shall signal the beginning of a sequence of two independently coded video fields. - * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][[SLC_SC][SLC_L] (optional)] ... */ - if( vc1_parse_advanced_picture( info->bits, &info->sequence, &info->picture, hb->rbdu, - lsmash_stream_buffers_get_pos( sb ), ebdu_length ) ) - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); - case 0x0C : /* Field - * Shall only be used for Field Interlaced frames - * and shall only be used to signal the beginning of the second field of the frame. - * [FRM_SC][PIC_L][FLD_SC][PIC_L][[SLC_SC][SLC_L] (optional)] ... - * Field start code is followed by INTERLACE_FIELD_PICTURE_FIELD2() which doesn't have info of its field picture type.*/ - break; - case 0x0B : /* Slice - * Shall not be used for start code of the first slice of a frame. - * Shall not be used for start code of the first slice of an interlace field coded picture. - * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][SLC_SC][SLC_L][[SLC_SC][SLC_L] (optional)] ... - * Slice layer may repeat frame header. We just ignore it. */ - info->dvc1_param.slice_present = 1; - break; - case 0x0E : /* Entry-point header - * Entry-point indicates the direct followed frame is a start of group of frames. - * Entry-point doesn't indicates the frame is a random access point when multiple sequence headers are present, - * since it is necessary to decode sequence header which subsequent frames belong to for decoding them. - * Entry point shall be followed by - * 1. I-picture - progressive or frame interlace - * 2. I/I-picture, I/P-picture, or P/I-picture - field interlace - * [[SEQ_SC][SEQ_L] (optional)][EP_SC][EP_L][FRM_SC][PIC_L] ... */ - if( vc1_parse_entry_point_header( info, lsmash_stream_buffers_get_pos( sb ), ebdu_length, probe ) ) - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); - /* Signal random access type of the frame that follows this entry-point header. */ - info->picture.closed_gop = info->entry_point.closed_entry_point; - info->picture.random_accessible = info->dvc1_param.multiple_sequence ? info->picture.start_of_sequence : 1; - break; - case 0x0F : /* Sequence header - * [SEQ_SC][SEQ_L][EP_SC][EP_L][FRM_SC][PIC_L] ... */ - if( vc1_parse_sequence_header( info, lsmash_stream_buffers_get_pos( sb ), ebdu_length, probe ) ) - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); - /* The frame that is the first frame after this sequence header shall be a random accessible point. */ - info->picture.start_of_sequence = 1; - if( probe && !importer_info->first_sequence.present ) - importer_info->first_sequence = info->sequence; - break; - default : /* End-of-sequence (0x0A) */ - break; - } - vc1_append_ebdu_to_au( access_unit, lsmash_stream_buffers_get_pos( sb ), ebdu_length, probe ); - } - else /* We don't support other BDU types such as user data yet. */ - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); - /* Move to the first byte of the next start code suffix. */ - if( read_back ) - { - lsmash_fseek( sb->stream, next_scs_file_offset, SEEK_SET ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - } - else - lsmash_stream_buffers_set_pos( sb, next_ebdu_pos + VC1_START_CODE_PREFIX_LENGTH ); - info->prev_bdu_type = bdu_type; - lsmash_stream_buffers_update( sb, 0 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - ebdu_length = 0; - no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !no_more ) - { - /* Check the next BDU type. */ - if( vc1_check_next_start_code_suffix( &bdu_type, &sb->pos ) ) - return vc1_get_au_internal_failed( importer_info, access_unit, bdu_type, no_more_buf, complete_au ); - info->ebdu_head_pos = next_scs_file_offset - VC1_START_CODE_PREFIX_LENGTH; - } - /* If there is no more data in the stream, and flushed chunk of EBDUs, flush it as complete AU here. */ - else if( access_unit->incomplete_data_length && access_unit->data_length == 0 ) - { - vc1_complete_au( access_unit, &info->picture, probe ); - return vc1_get_au_internal_succeeded( importer_info, access_unit, bdu_type, no_more_buf ); - } - if( complete_au ) - return vc1_get_au_internal_succeeded( importer_info, access_unit, bdu_type, no_more_buf ); - consecutive_zero_byte_count = 0; - } -} - -static int vc1_importer_get_accessunit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length ) - return -1; - if( !importer->info || track_number != 1 ) - return -1; - vc1_importer_info_t *importer_info = (vc1_importer_info_t *)importer->info; - vc1_info_t *info = &importer_info->info; - importer_status current_status = importer_info->status; - if( current_status == IMPORTER_ERROR || buffered_sample->length < importer_info->max_au_length ) - return -1; - if( current_status == IMPORTER_EOF ) - { - buffered_sample->length = 0; - return 0; - } - if( vc1_importer_get_access_unit_internal( importer_info, 0 ) ) - { - importer_info->status = IMPORTER_ERROR; - return -1; - } - vc1_access_unit_t *access_unit = &info->access_unit; - buffered_sample->dts = importer_info->ts_list.timestamp[access_unit->number - 1].dts; - buffered_sample->cts = importer_info->ts_list.timestamp[access_unit->number - 1].cts; - buffered_sample->prop.leading = access_unit->independent || access_unit->non_bipredictive || buffered_sample->cts >= importer_info->last_ref_intra_cts - ? ISOM_SAMPLE_IS_NOT_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING; - if( access_unit->independent && !access_unit->disposable ) - importer_info->last_ref_intra_cts = buffered_sample->cts; - if( importer_info->composition_reordering_present && !access_unit->disposable && !access_unit->closed_gop ) - buffered_sample->prop.allow_earlier = QT_SAMPLE_EARLIER_PTS_ALLOWED; - buffered_sample->prop.independent = access_unit->independent ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT; - buffered_sample->prop.disposable = access_unit->disposable ? ISOM_SAMPLE_IS_DISPOSABLE : ISOM_SAMPLE_IS_NOT_DISPOSABLE; - buffered_sample->prop.redundant = ISOM_SAMPLE_HAS_NO_REDUNDANCY; - if( access_unit->random_accessible ) - /* All random access point is a sync sample even if it's an open RAP. */ - buffered_sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - buffered_sample->length = access_unit->data_length; - memcpy( buffered_sample->data, access_unit->data, access_unit->data_length ); - return current_status; -} - -static lsmash_video_summary_t *vc1_create_summary( vc1_info_t *info, vc1_sequence_header_t *sequence, uint32_t max_au_length ) -{ - if( !info->sequence.present || !info->entry_point.present ) - return NULL; - lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); - if( !summary ) - return NULL; - lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1, - LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); - specific->data.unstructured = lsmash_create_vc1_specific_info( &info->dvc1_param, &specific->size ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( specific ); - return NULL; - } - summary->sample_type = ISOM_CODEC_TYPE_VC_1_VIDEO; - summary->max_au_length = max_au_length; - summary->timescale = sequence->framerate_numerator; - summary->timebase = sequence->framerate_denominator; - summary->vfr = !sequence->framerate_flag; - summary->sample_per_field = 0; - summary->width = sequence->disp_horiz_size; - summary->height = sequence->disp_vert_size; - summary->par_h = sequence->aspect_width; - summary->par_v = sequence->aspect_height; - summary->color.primaries_index = sequence->color_prim; - summary->color.transfer_index = sequence->transfer_char; - summary->color.matrix_index = sequence->matrix_coef; - return summary; -} - -static int vc1_importer_probe( importer_t *importer ) -{ -#define VC1_CHECK_FIRST_START_CODE( x ) (!(x)[0] && !(x)[1] && ((x)[2] == 0x01)) - /* Find the first start code. */ - vc1_importer_info_t *importer_info = create_vc1_importer_info( importer ); - if( !importer_info ) - return -1; - vc1_info_t *info = &importer_info->info; - vc1_stream_buffer_t *hb = &info->buffer; - lsmash_stream_buffers_t *sb = hb->sb; /* shall be equal to &importer->sb */ - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - while( 1 ) - { - /* The first EBDU in decoding order of the stream shall have start code (0x000001). */ - if( VC1_CHECK_FIRST_START_CODE( sb->pos ) ) - break; - /* If the first trial of finding start code of sequence header failed, we assume this stream is not byte stream format of VC-1. */ - if( lsmash_stream_buffers_get_remainder( sb ) == VC1_START_CODE_LENGTH ) - goto fail; - /* Invalid if encountered any value of non-zero before the first start code. */ - if( lsmash_stream_buffers_get_byte( sb ) ) - goto fail; - } - /* OK. It seems the stream has a sequence header of VC-1. */ - importer->info = importer_info; - uint64_t first_ebdu_head_pos = lsmash_stream_buffers_get_offset( sb ); - lsmash_stream_buffers_seek( sb, VC1_START_CODE_PREFIX_LENGTH, SEEK_CUR ); - lsmash_stream_buffers_update( sb, 0 ); - uint8_t first_bdu_type = lsmash_stream_buffers_get_byte( sb ); - if( lsmash_stream_buffers_get_remainder( sb ) == 0 ) - goto fail; /* It seems the stream ends at the first incomplete access unit. */ - importer_info->status = IMPORTER_OK; - info->bdu_type = first_bdu_type; - info->ebdu_head_pos = first_ebdu_head_pos; - /* Parse all EBDU in the stream for preparation of calculating timestamps. */ - uint32_t cts_alloc = (1 << 12) * sizeof(uint64_t); - uint64_t *cts = lsmash_malloc( cts_alloc ); - if( !cts ) - goto fail; - uint32_t num_access_units = 0; - uint32_t num_consecutive_b = 0; - lsmash_log( importer, LSMASH_LOG_INFO, "Analyzing stream as VC-1\r" ); - while( importer_info->status != IMPORTER_EOF ) - { -#if 0 - lsmash_log( importer, LSMASH_LOG_INFO, "Analyzing stream as VC-1: %"PRIu32"\n", num_access_units + 1 ); -#endif - if( vc1_importer_get_access_unit_internal( importer_info, 1 ) ) - { - lsmash_free( cts ); - goto fail; - } - /* In the case where B-pictures exist - * Decode order - * I[0]P[1]P[2]B[3]B[4]P[5]... - * DTS - * 0 1 2 3 4 5 ... - * Composition order - * I[0]P[1]B[3]B[4]P[2]P[5]... - * CTS - * 1 2 3 4 5 6 ... - * We assumes B or BI-pictures always be present in the stream here. */ - if( !info->access_unit.disposable ) - { - /* Apply CTS of the last B-picture plus 1 to the last non-B-picture. */ - if( num_access_units > num_consecutive_b ) - cts[ num_access_units - num_consecutive_b - 1 ] = num_access_units; - num_consecutive_b = 0; - } - else /* B or BI-picture */ - { - /* B and BI-pictures shall be output or displayed in the same order as they are encoded. */ - cts[ num_access_units ] = num_access_units; - ++num_consecutive_b; - info->dvc1_param.bframe_present = 1; - } - if( cts_alloc <= num_access_units * sizeof(uint64_t) ) - { - uint32_t alloc = 2 * num_access_units * sizeof(uint64_t); - uint64_t *temp = lsmash_realloc( cts, alloc ); - if( !temp ) - { - lsmash_free( cts ); - goto fail; - } - cts = temp; - cts_alloc = alloc; - } - importer_info->max_au_length = LSMASH_MAX( info->access_unit.data_length, importer_info->max_au_length ); - ++num_access_units; - } - if( num_access_units > num_consecutive_b ) - cts[ num_access_units - num_consecutive_b - 1 ] = num_access_units; - else - { - lsmash_free( cts ); - goto fail; - } - fprintf( stderr, " \r" ); - /* Construct timestamps. */ - lsmash_media_ts_t *timestamp = lsmash_malloc( num_access_units * sizeof(lsmash_media_ts_t) ); - if( !timestamp ) - { - lsmash_free( cts ); - goto fail; - } - for( uint32_t i = 1; i < num_access_units; i++ ) - if( cts[i] < cts[i - 1] ) - { - importer_info->composition_reordering_present = 1; - break; - } - if( importer_info->composition_reordering_present ) - for( uint32_t i = 0; i < num_access_units; i++ ) - { - timestamp[i].cts = cts[i]; - timestamp[i].dts = i; - } - else - for( uint32_t i = 0; i < num_access_units; i++ ) - timestamp[i].cts = timestamp[i].dts = i; - lsmash_free( cts ); -#if 0 - for( uint32_t i = 0; i < num_access_units; i++ ) - fprintf( stderr, "Timestamp[%"PRIu32"]: DTS=%"PRIu64", CTS=%"PRIu64"\n", i, timestamp[i].dts, timestamp[i].cts ); -#endif - lsmash_video_summary_t *summary = vc1_create_summary( info, &importer_info->first_sequence, importer_info->max_au_length ); - if( !summary || lsmash_add_entry( importer->summaries, summary ) ) - { - lsmash_free( timestamp ); - goto fail; - } - importer_info->ts_list.sample_count = num_access_units; - importer_info->ts_list.timestamp = timestamp; - /* Go back to layer of the first EBDU. */ - lsmash_fseek( sb->stream, first_ebdu_head_pos, SEEK_SET ); - importer_info->status = IMPORTER_OK; - info->bdu_type = first_bdu_type; - info->prev_bdu_type = 0; - sb->no_more_read = 0; - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_stream_buffers_read( sb, 0 ); - lsmash_stream_buffers_seek( sb, VC1_START_CODE_LENGTH, SEEK_SET ); - info->ebdu_head_pos = first_ebdu_head_pos; - uint8_t *temp_access_unit = info->access_unit.data; - uint8_t *temp_incomplete_access_unit = info->access_unit.incomplete_data; - memset( &info->access_unit, 0, sizeof(vc1_access_unit_t) ); - info->access_unit.data = temp_access_unit; - info->access_unit.incomplete_data = temp_incomplete_access_unit; - memset( &info->picture, 0, sizeof(vc1_picture_info_t) ); - return 0; -fail: - remove_vc1_importer_info( importer_info ); - importer->info = NULL; - lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); - return -1; -#undef VC1_CHECK_FIRST_START_CODE -} - -static uint32_t vc1_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - debug_if( !importer || !importer->info ) - return 0; - vc1_importer_info_t *info = (vc1_importer_info_t *)importer->info; - if( !info || track_number != 1 || info->status != IMPORTER_EOF ) - return 0; - return info->ts_list.sample_count - ? 1 - : UINT32_MAX; /* arbitrary */ -} - -const static importer_functions vc1_importer = -{ - { "VC-1" }, - 1, - vc1_importer_probe, - vc1_importer_get_accessunit, - vc1_importer_get_last_delta, - vc1_importer_cleanup -}; - -/*************************************************************************** - importer public interfaces -***************************************************************************/ - - -/******** importer listing table ********/ -const static importer_functions *importer_func_table[] = { - &mp4sys_adts_importer, - &mp4sys_mp3_importer, - &amr_importer, - &ac3_importer, - &eac3_importer, - &mp4sys_als_importer, - &dts_importer, - &h264_importer, - &hevc_importer, - &vc1_importer, - NULL, -}; - -/******** importer public functions ********/ - -void lsmash_importer_close( importer_t *importer ) -{ - if( !importer ) - return; - if( !importer->is_stdin && importer->stream ) - fclose( importer->stream ); - if( importer->funcs.cleanup ) - importer->funcs.cleanup( importer ); - lsmash_remove_list( importer->summaries, lsmash_cleanup_summary ); - lsmash_free( importer ); -} - -importer_t *lsmash_importer_open( const char *identifier, const char *format ) -{ - if( identifier == NULL ) - return NULL; - int auto_detect = ( format == NULL || !strcmp( format, "auto" ) ); - importer_t *importer = (importer_t *)lsmash_malloc_zero( sizeof(importer_t) ); - if( !importer ) - return NULL; - if( !strcmp( identifier, "-" ) ) - { - /* special treatment for stdin */ - if( auto_detect ) - { - lsmash_free( importer ); - return NULL; - } - importer->stream = stdin; - importer->is_stdin = 1; - } - else if( (importer->stream = lsmash_fopen( identifier, "rb" )) == NULL ) - { - lsmash_importer_close( importer ); - return NULL; - } - importer->summaries = lsmash_create_entry_list(); - if( !importer->summaries ) - { - lsmash_importer_close( importer ); - return NULL; - } - /* find importer */ - const importer_functions *funcs; - if( auto_detect ) - { - /* just rely on detector. */ - for( int i = 0; (funcs = importer_func_table[i]) != NULL; i++ ) - { - importer->class = &funcs->class; - 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 = importer_func_table[i]) != NULL; i++ ) - { - importer->class = &funcs->class; - if( strcmp( importer->class->name, format ) ) - continue; - if( funcs->probe( importer ) ) - funcs = NULL; - break; - } - } - if( !funcs ) - { - lsmash_importer_close( importer ); - return NULL; - } - importer->funcs = *funcs; - return importer; -} - -/* 0 if success, positive if changed, negative if failed */ -int lsmash_importer_get_access_unit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ) -{ - if( !importer || !importer->funcs.get_accessunit || !buffered_sample->data || buffered_sample->length == 0 ) - return -1; - return importer->funcs.get_accessunit( importer, track_number, buffered_sample ); -} - -/* Return 0 if failed, otherwise succeeded. */ -uint32_t lsmash_importer_get_last_delta( importer_t *importer, uint32_t track_number ) -{ - if( !importer || !importer->funcs.get_last_delta ) - return 0; - return importer->funcs.get_last_delta( importer, track_number ); -} - -uint32_t lsmash_importer_get_track_count( importer_t *importer ) -{ - if( !importer || !importer->summaries ) - return 0; - return importer->summaries->entry_count; -} - -lsmash_summary_t *lsmash_duplicate_summary( importer_t *importer, uint32_t track_number ) -{ - if( !importer ) - return NULL; - lsmash_summary_t *src_summary = lsmash_get_entry_data( importer->summaries, track_number ); - if( !src_summary ) - return NULL; - lsmash_summary_t *summary = lsmash_create_summary( src_summary->summary_type ); - if( !summary ) - return NULL; - lsmash_codec_specific_list_t *opaque = summary->opaque; - switch( src_summary->summary_type ) - { - case LSMASH_SUMMARY_TYPE_VIDEO : - *(lsmash_video_summary_t *)summary = *(lsmash_video_summary_t *)src_summary; - break; - case LSMASH_SUMMARY_TYPE_AUDIO : - *(lsmash_audio_summary_t *)summary = *(lsmash_audio_summary_t *)src_summary; - break; - default : - lsmash_cleanup_summary( summary ); - return NULL; - } - summary->opaque = opaque; - for( lsmash_entry_t *entry = src_summary->opaque->list.head; entry; entry = entry->next ) - { - lsmash_codec_specific_t *src_specific = (lsmash_codec_specific_t *)entry->data; - if( !src_specific ) - continue; - lsmash_codec_specific_t *dup = isom_duplicate_codec_specific_data( src_specific ); - if( lsmash_add_entry( &summary->opaque->list, dup ) ) - { - lsmash_cleanup_summary( (lsmash_summary_t *)summary ); - lsmash_destroy_codec_specific_data( dup ); - return NULL; - } - } - return summary; -} diff -Nru l-smash-1.9.1/importer.h l-smash-2.3.0/importer.h --- l-smash-1.9.1/importer.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/importer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -/***************************************************************************** - * importer.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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 importer_t; - -/* importing functions */ -importer_t *lsmash_importer_open( const char *identifier, const char *format ); -void lsmash_importer_close( importer_t *importer ); -int lsmash_importer_get_access_unit( importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample ); -uint32_t lsmash_importer_get_last_delta( importer_t *importer, uint32_t track_number ); -uint32_t lsmash_importer_get_track_count( importer_t *importer ); -lsmash_summary_t *lsmash_duplicate_summary( importer_t *importer, uint32_t track_number ); - -int mp4sys_amr_create_damr( lsmash_audio_summary_t *summary ); - -#endif /* #ifndef LSMASH_IMPORTER_INTERNAL */ - -#endif /* #ifndef LSMASH_IMPORTER_H */ diff -Nru l-smash-1.9.1/internal.h l-smash-2.3.0/internal.h --- l-smash-1.9.1/internal.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/internal.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/***************************************************************************** - * internal.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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 fseeko -#define lsmash_ftell ftello -#endif - -#include "lsmash.h" - -#endif diff -Nru l-smash-1.9.1/isom.c l-smash-2.3.0/isom.c --- l-smash-1.9.1/isom.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/isom.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,4621 +0,0 @@ -/***************************************************************************** - * isom.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 -#ifdef _WIN32 -#include -#endif - -#include "box.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "write.h" -#include "description.h" -#include "fragment.h" -#ifdef LSMASH_DEMUXER_ENABLED -#include "read.h" -#endif - -/*---- ----*/ -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; -} - -isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID ) -{ - if( track_ID == 0 - || !file - || !file->moov ) - return NULL; - for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - if( !trak - || !trak->tkhd ) - return NULL; - if( trak->tkhd->track_ID == track_ID ) - return trak; - } - return NULL; -} - -isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID ) -{ - if( track_ID == 0 || !mvex ) - return NULL; - for( lsmash_entry_t *entry = mvex->trex_list.head; entry; entry = entry->next ) - { - isom_trex_t *trex = (isom_trex_t *)entry->data; - if( !trex ) - return NULL; - if( trex->track_ID == track_ID ) - return trex; - } - return NULL; -} - -isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID ) -{ - if( track_ID == 0 || !moof ) - return NULL; - for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)entry->data; - if( !traf - || !traf->tfhd ) - return NULL; - if( traf->tfhd->track_ID == track_ID ) - return traf; - } - return NULL; -} - -isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID ) -{ - if( track_ID == 0 || !mfra ) - return NULL; - for( lsmash_entry_t *entry = mfra->tfra_list.head; entry; entry = entry->next ) - { - isom_tfra_t *tfra = (isom_tfra_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 ) -{ - assert( elst->file ); - isom_elst_entry_t *data = lsmash_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 ) ) - { - lsmash_free( data ); - return -1; - } - if( !elst->file->undefined_64_ver - && (data->segment_duration > UINT32_MAX - || data->media_time > INT32_MAX - || data->media_time < INT32_MIN) ) - elst->version = 1; - return 0; -} - -isom_dcr_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size ) -{ - isom_dcr_ps_entry_t *entry = lsmash_malloc( sizeof(isom_dcr_ps_entry_t) ); - if( !entry ) - return NULL; - entry->nalUnit = lsmash_memdup( ps, ps_size ); - if( !entry->nalUnit ) - { - lsmash_free( entry ); - return NULL; - } - entry->nalUnitLength = ps_size; - entry->unused = 0; - return entry; -} - -void isom_remove_dcr_ps( isom_dcr_ps_entry_t *ps ) -{ - if( !ps ) - return; - if( ps->nalUnit ) - lsmash_free( ps->nalUnit ); - lsmash_free( ps ); -} - -/* This function returns 0 if failed, sample_entry_number if succeeded. */ -int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary ) -{ - if( !root || !summary ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak - || !trak->file - || !trak->mdia - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stsd ) - return 0; - isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd; - lsmash_entry_list_t *list = &stsd->list; - int ret = -1; - lsmash_codec_type_t sample_type = ((lsmash_summary_t *)summary)->sample_type; - if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) ) - { - if( trak->mdia->minf->vmhd ) - ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary ); - else if( trak->mdia->minf->smhd ) - ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary ); - return ret < 0 ? 0 : list->entry_count; - } - static struct description_setup_table_tag - { - lsmash_codec_type_t type; - void *func; - } description_setup_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } }; - if( !description_setup_table[0].func ) - { - int i = 0; -#define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \ - description_setup_table[i++] = (struct description_setup_table_tag){ type, func } - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_setup_visual_description ); -#if 0 - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_setup_visual_description ); -#endif - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_setup_visual_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_setup_audio_description ); -#if 0 - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description ); -#endif - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_setup_audio_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_add_tx3g_description ); - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_add_qt_text_description ); -#if 0 - ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_add_mp4s_entry ); -#endif - } - for( int i = 0; description_setup_table[i].func; i++ ) - if( lsmash_check_codec_type_identical( sample_type, description_setup_table[i].type ) ) - { - if( isom_setup_visual_description == description_setup_table[i].func ) - ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary ); - else if( isom_setup_audio_description == description_setup_table[i].func ) - ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary ); - else if( isom_add_tx3g_description == description_setup_table[i].func ) - { - isom_tx3g_entry_t *tx3g = isom_add_tx3g_description( stsd ); - if( tx3g ) - { - tx3g->data_reference_index = 1; - ret = isom_add_ftab( tx3g ); - if( ret < 0 ) - isom_remove_box_by_itself( tx3g ); - } - } - else if( isom_add_qt_text_description == description_setup_table[i].func ) - { - isom_qt_text_entry_t *text = isom_add_qt_text_description( stsd ); - if( text ) - { - text->data_reference_index = 1; - ret = 0; - } - } - break; - } - return ret < 0 ? 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 = lsmash_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 ) ) - { - lsmash_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 = lsmash_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 ) ) - { - lsmash_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 = lsmash_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 ) ) - { - lsmash_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 == 0 ) - 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 = lsmash_malloc( sizeof(isom_stsz_entry_t) ); - if( !data ) - return -1; - data->entry_size = stsz->sample_size; - if( lsmash_add_entry( stsz->list, data ) ) - { - lsmash_free( data ); - return -1; - } - } - stsz->sample_size = 0; - } - isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) ); - if( !data ) - return -1; - data->entry_size = entry_size; - if( lsmash_add_entry( stsz->list, data ) ) - { - lsmash_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 = lsmash_malloc( sizeof(isom_stss_entry_t) ); - if( !data ) - return -1; - data->sample_number = sample_number; - if( lsmash_add_entry( stbl->stss->list, data ) ) - { - lsmash_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 = lsmash_malloc( sizeof(isom_stps_entry_t) ); - if( !data ) - return -1; - data->sample_number = sample_number; - if( lsmash_add_entry( stbl->stps->list, data ) ) - { - lsmash_free( data ); - return -1; - } - return 0; -} - -static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, uint8_t avc_extensions ) -{ - if( !prop || !parent ) - return -1; - isom_sdtp_t *sdtp = NULL; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) ) - sdtp = ((isom_stbl_t *)parent)->sdtp; - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - sdtp = ((isom_traf_t *)parent)->sdtp; - else - assert( 0 ); - if( !sdtp - || !sdtp->list ) - return -1; - isom_sdtp_entry_t *data = lsmash_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( sdtp->list, data ) ) - { - lsmash_free( data ); - return -1; - } - 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 = lsmash_malloc( sizeof(isom_co64_entry_t) ); - if( !data ) - return -1; - data->chunk_offset = chunk_offset; - if( lsmash_add_entry( stbl->stco->list, data ) ) - { - lsmash_free( data ); - return -1; - } - return 0; -} - -int isom_convert_stco_to_co64( isom_stbl_t *stbl ) -{ - /* backup stco */ - int ret = 0; - isom_stco_t *stco = stbl->stco; - stbl->stco = NULL; - if( isom_add_co64( stbl ) ) - { - ret = -1; - goto fail; - } - /* 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 ) ) - { - ret = -1; - goto fail; - } - } -fail: - isom_remove_box_by_itself( stco ); - return ret; -} - -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 ) < 0 ) - return -1; - return isom_add_co64_entry( stbl, chunk_offset ); - } - isom_stco_entry_t *data = lsmash_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 ) ) - { - lsmash_free( data ); - return -1; - } - return 0; -} - -static isom_sgpd_t *isom_get_sample_group_description_common( lsmash_entry_list_t *list, uint32_t grouping_type ) -{ - for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) - { - isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data; - if( !sgpd - || !sgpd->list ) - return NULL; - if( sgpd->grouping_type == grouping_type ) - return sgpd; - } - return NULL; -} - -static isom_sbgp_t *isom_get_sample_to_group_common( lsmash_entry_list_t *list, uint32_t grouping_type ) -{ - for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) - { - isom_sbgp_t *sbgp = (isom_sbgp_t *)entry->data; - if( !sbgp - || !sbgp->list ) - return NULL; - if( sbgp->grouping_type == grouping_type ) - return sbgp; - } - return NULL; -} - -isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ) -{ - return isom_get_sample_group_description_common( &stbl->sgpd_list, grouping_type ); -} - -isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ) -{ - return isom_get_sample_to_group_common( &stbl->sbgp_list, grouping_type ); -} - -isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type ) -{ - return isom_get_sample_group_description_common( &traf->sgpd_list, grouping_type ); -} - -isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type ) -{ - return isom_get_sample_to_group_common( &traf->sbgp_list, grouping_type ); -} - -static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_t *sgpd ) -{ - if( !sgpd ) - return NULL; - isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) ); - if( !data ) - return NULL; - data->description_length = 0; - data->num_leading_samples_known = 0; - data->num_leading_samples = 0; - if( lsmash_add_entry( sgpd->list, data ) ) - { - lsmash_free( data ); - return NULL; - } - return data; -} - -static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_t *sgpd, int16_t roll_distance ) -{ - if( !sgpd ) - return NULL; - isom_roll_entry_t *data = lsmash_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 ) ) - { - lsmash_free( data ); - return NULL; - } - return data; -} - -static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index ) -{ - if( !sbgp ) - return NULL; - isom_group_assignment_entry_t *data = lsmash_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 ) ) - { - lsmash_free( data ); - return NULL; - } - return data; -} - -int isom_check_compatibility( lsmash_file_t *file ) -{ - if( !file ) - return -1; - /* Clear flags for compatibility. */ - ptrdiff_t compat_offset = offsetof( lsmash_file_t, qt_compatible ); - memset( (int8_t *)file + compat_offset, 0, sizeof(lsmash_file_t) - compat_offset ); - file->min_isom_version = UINT8_MAX; /* undefined value */ - /* Check brand to decide mandatory boxes. */ - if( (!file->ftyp || file->ftyp->brand_count == 0) - && (!file->styp_list.head || !file->styp_list.head->data) ) - { - /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */ - if( file->moov - && file->moov->iods ) - { - file->mp4_version1 = 1; - file->isom_compatible = 1; - } - else - { - file->qt_compatible = 1; - file->undefined_64_ver = 1; - } - return 0; - } - for( uint32_t i = 0; i <= file->ftyp->brand_count; i++ ) - { - uint32_t brand = (i == file->ftyp->brand_count ? file->ftyp->major_brand : file->ftyp->compatible_brands[i]); - switch( brand ) - { - case ISOM_BRAND_TYPE_QT : - file->qt_compatible = 1; - break; - case ISOM_BRAND_TYPE_MP41 : - file->mp4_version1 = 1; - break; - case ISOM_BRAND_TYPE_MP42 : - file->mp4_version2 = 1; - break; - case ISOM_BRAND_TYPE_AVC1 : - case ISOM_BRAND_TYPE_ISOM : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 1 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 1 ); - break; - case ISOM_BRAND_TYPE_ISO2 : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 2 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 2 ); - break; - case ISOM_BRAND_TYPE_ISO3 : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 3 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 3 ); - break; - case ISOM_BRAND_TYPE_ISO4 : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 4 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 4 ); - break; - case ISOM_BRAND_TYPE_ISO5 : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 5 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 5 ); - break; - case ISOM_BRAND_TYPE_ISO6 : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 6 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 6 ); - break; - case ISOM_BRAND_TYPE_ISO7 : - file->max_isom_version = LSMASH_MAX( file->max_isom_version, 7 ); - file->min_isom_version = LSMASH_MIN( file->min_isom_version, 7 ); - break; - case ISOM_BRAND_TYPE_M4A : - case ISOM_BRAND_TYPE_M4B : - case ISOM_BRAND_TYPE_M4P : - case ISOM_BRAND_TYPE_M4V : - file->itunes_movie = 1; - break; - case ISOM_BRAND_TYPE_3GP4 : - file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 4 ); - break; - case ISOM_BRAND_TYPE_3GP5 : - file->max_3gpp_version = LSMASH_MAX( file->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 : - file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 6 ); - break; - case ISOM_BRAND_TYPE_3GP7 : - file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 7 ); - break; - case ISOM_BRAND_TYPE_3GP8 : - file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 8 ); - break; - case ISOM_BRAND_TYPE_3GE9 : - case ISOM_BRAND_TYPE_3GF9 : - case ISOM_BRAND_TYPE_3GG9 : - case ISOM_BRAND_TYPE_3GH9 : - case ISOM_BRAND_TYPE_3GM9 : - case ISOM_BRAND_TYPE_3GP9 : - case ISOM_BRAND_TYPE_3GR9 : - case ISOM_BRAND_TYPE_3GS9 : - case ISOM_BRAND_TYPE_3GT9 : - file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 9 ); - break; - default : - break; - } - switch( brand ) - { - 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 : - file->avc_extensions = 1; - break; - case ISOM_BRAND_TYPE_3GP4 : - case ISOM_BRAND_TYPE_3GP5 : - case ISOM_BRAND_TYPE_3GP6 : - case ISOM_BRAND_TYPE_3GP7 : - case ISOM_BRAND_TYPE_3GP8 : - case ISOM_BRAND_TYPE_3GP9 : - file->forbid_tref = 1; - break; - case ISOM_BRAND_TYPE_3GH9 : - case ISOM_BRAND_TYPE_3GM9 : - case ISOM_BRAND_TYPE_DASH : - case ISOM_BRAND_TYPE_DSMS : - case ISOM_BRAND_TYPE_LMSG : - case ISOM_BRAND_TYPE_MSDH : - case ISOM_BRAND_TYPE_MSIX : - case ISOM_BRAND_TYPE_SIMS : - file->media_segment = 1; - break; - default : - break; - } - } - file->isom_compatible = !file->qt_compatible - || file->mp4_version1 - || file->mp4_version2 - || file->itunes_movie - || file->max_3gpp_version; - file->undefined_64_ver = file->qt_compatible || file->itunes_movie; - if( file->flags & LSMASH_FILE_MODE_WRITE ) - { - /* Media Segment is incompatible with ISO Base Media File Format version 4 or former must be compatible with - * version 6 or later since it requires default-base-is-moof and Track Fragment Base Media Decode Time Box. */ - if( file->media_segment && (file->min_isom_version < 5 || (file->max_isom_version && file->max_isom_version < 6)) ) - return -1; - file->allow_moof_base = (file->max_isom_version >= 5 && file->min_isom_version >= 5) - || (file->max_isom_version == 0 && file->min_isom_version == UINT8_MAX && file->media_segment); - } - return 0; -} - -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 ) - 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_t *trak, uint32_t last_sample_delta ) -{ - if( !trak - || !trak->file - || !trak->cache - || !trak->mdia - || !trak->mdia->mdhd - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stts - || !trak->mdia->minf->stbl->stts->list ) - return -1; - lsmash_file_t *file = trak->file; - isom_mdhd_t *mdhd = trak->mdia->mdhd; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - isom_stts_t *stts = stbl->stts; - isom_ctts_t *ctts = stbl->ctts; - isom_cslg_t *cslg = stbl->cslg; - mdhd->duration = 0; - uint32_t sample_count = isom_get_sample_count( trak ); - if( sample_count == 0 ) - { - /* Return error if non-fragmented movie has no samples. */ - if( !file->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_tail( stts->list, 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; - uint64_t max2_cts = 0; - uint64_t min_cts = UINT64_MAX; - uint32_t max_offset = 0; - uint32_t min_offset = UINT32_MAX; - int32_t ctd_shift = trak->cache->timestamp.ctd_shift; - uint32_t j = 0; - uint32_t k = 0; - lsmash_entry_t *stts_entry = stts->list->head; - lsmash_entry_t *ctts_entry = ctts->list->head; - for( uint32_t i = 0; i < sample_count; i++ ) - { - if( !ctts_entry || !stts_entry ) - return -1; - isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data; - isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data; - if( !stts_data || !ctts_data ) - return -1; - uint64_t cts; - if( ctd_shift ) - { - /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */ - int32_t sample_offset = (int32_t)ctts_data->sample_offset; - cts = dts + sample_offset + ctd_shift; - max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset ); - min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset ); - } - else - { - cts = dts + ctts_data->sample_offset; - max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset ); - min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset ); - } - min_cts = LSMASH_MIN( min_cts, cts ); - if( max_cts < cts ) - { - max2_cts = max_cts; - max_cts = cts; - } - else if( max2_cts < cts ) - max2_cts = cts; - dts += stts_data->sample_delta; - /* If finished sample_count of current entry, move to next. */ - if( ++j == ctts_data->sample_count ) - { - ctts_entry = ctts_entry->next; - j = 0; - } - if( ++k == stts_data->sample_count ) - { - stts_entry = stts_entry->next; - k = 0; - } - } - dts -= last_stts_data->sample_delta; - if( file->fragment ) - /* Overall presentation is extended exceeding this initial movie. - * So, any players shall display the movie exceeding the durations - * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes. - * Samples up to the duration indicated in Movie Extends Header Box shall be displayed. - * In the absence of Movie Extends Header Box, all samples shall be displayed. */ - mdhd->duration += dts + last_sample_delta; - else - { - if( !last_sample_delta ) - { - /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */ - last_sample_delta = max_cts - max2_cts; - } - mdhd->duration = max_cts - min_cts + last_sample_delta; - /* To match dts and media duration, update stts and mdhd relatively. */ - if( mdhd->duration > dts ) - last_sample_delta = mdhd->duration - dts; - else - mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */ - } - if( isom_replace_last_sample_delta( stbl, last_sample_delta ) ) - return -1; - /* Explicit composition information and timeline shifting */ - if( cslg || file->qt_compatible || file->max_isom_version >= 4 ) - { - if( ctd_shift ) - { - /* Remove composition to decode timeline shift. */ - max_cts -= ctd_shift; - max2_cts -= ctd_shift; - min_cts -= ctd_shift; - } - int64_t composition_end_time = max_cts + (max_cts - max2_cts); - if( !file->fragment - && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX) - && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) ) - { - if( !cslg ) - { - if( isom_add_cslg( trak->mdia->minf->stbl ) ) - return -1; - cslg = stbl->cslg; - } - cslg->compositionToDTSShift = ctd_shift; - cslg->leastDecodeToDisplayDelta = min_offset; - cslg->greatestDecodeToDisplayDelta = max_offset; - cslg->compositionStartTime = min_cts; - cslg->compositionEndTime = composition_end_time; - } - else - { - if( cslg ) - lsmash_free( cslg ); - stbl->cslg = NULL; - } - } - } - if( mdhd->duration > UINT32_MAX && !file->undefined_64_ver ) - mdhd->version = 1; - return 0; -} - -static int isom_update_mvhd_duration( isom_moov_t *moov ) -{ - if( !moov - || !moov->mvhd - || !moov->mvhd->file ) - 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_t *data = (isom_trak_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->file->undefined_64_ver ) - mvhd->version = 1; - return 0; -} - -int isom_update_tkhd_duration( isom_trak_t *trak ) -{ - if( !trak - || !trak->tkhd - || !trak->file - || !trak->file->moov ) - return -1; - lsmash_file_t *file = trak->file; - isom_tkhd_t *tkhd = trak->tkhd; - tkhd->duration = 0; - if( file->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 - || !file->moov->mvhd - || trak->mdia->mdhd->timescale == 0 ) - return -1; - if( trak->mdia->mdhd->duration == 0 && isom_update_mdhd_duration( trak, 0 ) ) - return -1; - tkhd->duration = trak->mdia->mdhd->duration * ((double)file->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 && !file->undefined_64_ver ) - tkhd->version = 1; - if( !file->fragment && tkhd->duration == 0 ) - tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff; - return isom_update_mvhd_duration( file->moov ); -} - -int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - isom_trak_t *trak = isom_get_trak( file, 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 (!file->fragment && trak->edts && trak->edts->elst) - ? isom_update_mvhd_duration( file->moov ) /* Only update movie duration. */ - : isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */ -} - -static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number_in_entry, uint32_t sample_count_in_entry, lsmash_entry_t **entry ) -{ - if( *sample_number_in_entry != sample_count_in_entry ) - { - *sample_number_in_entry += 1; - return 0; - } - /* Precede the next entry. */ - *sample_number_in_entry = 1; - if( *entry ) - { - *entry = (*entry)->next; - if( *entry && !(*entry)->data ) - return -1; - } - return 0; -} - -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; - 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; - isom_stts_entry_t *stts_data = NULL; - isom_stsc_entry_t *stsc_data = NULL; - if( next_stsc_entry && !next_stsc_entry->data ) - return -1; - uint32_t rate = 0; - uint64_t dts = 0; - uint32_t time_wnd = 0; - uint32_t timescale = mdia->mdhd->timescale; - uint32_t chunk_number = 0; - uint32_t sample_number_in_stts = 1; - uint32_t sample_number_in_chunk = 1; - *bufferSizeDB = 0; - *maxBitrate = 0; - *avgBitrate = 0; - while( stts_entry ) - { - if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk ) - { - /* Move the next chunk. */ - sample_number_in_chunk = 1; - ++chunk_number; - /* Check if the next entry is broken. */ - while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number ) - { - /* Just skip broken next entry. */ - next_stsc_entry = next_stsc_entry->next; - if( next_stsc_entry && !next_stsc_entry->data ) - return -1; - } - /* Check if the next chunk belongs to the next sequence of chunks. */ - if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number ) - { - stsc_entry = next_stsc_entry; - next_stsc_entry = next_stsc_entry->next; - if( next_stsc_entry && !next_stsc_entry->data ) - return -1; - stsc_data = (isom_stsc_entry_t *)stsc_entry->data; - /* Check if the next contiguous chunks belong to given sample description. */ - if( stsc_data->sample_description_index != sample_description_index ) - { - /* Skip chunks which don't belong to given sample description. */ - uint32_t number_of_skips = 0; - uint32_t first_chunk = stsc_data->first_chunk; - uint32_t samples_per_chunk = stsc_data->samples_per_chunk; - while( next_stsc_entry ) - { - if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index ) - { - stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data; - number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk; - first_chunk = stsc_data->first_chunk; - samples_per_chunk = stsc_data->samples_per_chunk; - } - else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk ) - ; /* broken entry */ - else - break; - /* Just skip the next entry. */ - next_stsc_entry = next_stsc_entry->next; - if( next_stsc_entry && !next_stsc_entry->data ) - return -1; - } - if( !next_stsc_entry ) - break; /* There is no more chunks which don't belong to given sample description. */ - 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_entry ) - break; - stsz_entry = stsz_entry->next; - } - if( !stts_entry ) - break; - if( isom_increment_sample_number_in_entry( &sample_number_in_stts, ((isom_stts_entry_t *)stts_entry->data)->sample_count, &stts_entry ) ) - return -1; - } - if( (stsz->list && !stsz_entry) || !stts_entry ) - break; - chunk_number = stsc_data->first_chunk; - } - } - } - else - ++sample_number_in_chunk; - /* Get current sample's size. */ - 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; - /* Get current sample's DTS. */ - if( stts_data ) - dts += stts_data->sample_delta; - stts_data = (isom_stts_entry_t *)stts_entry->data; - if( !stts_data ) - return -1; - isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry ); - /* Calculate bitrate description. */ - if( *bufferSizeDB < size ) - *bufferSizeDB = size; - *avgBitrate += size; - rate += size; - if( dts > time_wnd + timescale ) - { - if( rate > *maxBitrate ) - *maxBitrate = rate; - time_wnd = dts; - rate = 0; - } - } - double duration = (double)mdia->mdhd->duration / timescale; - *avgBitrate = (uint32_t)(*avgBitrate / duration); - if( *maxBitrate == 0 ) - *maxBitrate = *avgBitrate; - /* Convert to bits per second. */ - *maxBitrate *= 8; - *avgBitrate *= 8; - return 0; -} - -int isom_update_bitrate_description( isom_mdia_t *mdia ) -{ - if( !mdia - || !mdia->mdhd - || !mdia->minf - || !mdia->minf->stbl ) - return -1; - isom_stbl_t *stbl = mdia->minf->stbl; - if( !stbl->stsd - || !stbl->stsz - || !stbl->stsc || !stbl->stsc->list - || !stbl->stts || !stbl->stts->list ) - return -1; - uint32_t sample_description_index = 0; - for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next ) - { - isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data; - if( !sample_entry ) - return -1; - ++sample_description_index; - uint32_t bufferSizeDB; - uint32_t maxBitrate; - uint32_t avgBitrate; - /* set bitrate info */ - lsmash_codec_type_t sample_type = (lsmash_codec_type_t)sample_entry->type; - if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HEV1_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 *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_BTRT ); - if( btrt ) - { - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) ) - return -1; - btrt->bufferSizeDB = bufferSizeDB; - btrt->maxBitrate = maxBitrate; - btrt->avgBitrate = avgBitrate; - } - } - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) ) - { - isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry; - if( !stsd_data ) - return -1; - isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS ); - if( !esds || !esds->ES ) - return -1; - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) ) - return -1; - /* FIXME: avgBitrate is 0 only if VBR in proper. */ - if( mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 ) ) - return -1; - } - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) ) - { - isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry; - if( !stsd_data ) - return -1; - isom_esds_t *esds = NULL; - if( ((isom_audio_entry_t *)sample_entry)->version ) - { - /* MPEG-4 Audio in QTFF */ - isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box_format( &stsd_data->extensions, QT_BOX_TYPE_WAVE ); - if( !wave ) - return -1; - esds = (isom_esds_t *)isom_get_extension_box_format( &wave->extensions, ISOM_BOX_TYPE_ESDS ); - } - else - esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS ); - if( !esds || !esds->ES ) - return -1; - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) ) - return -1; - /* FIXME: avgBitrate is 0 only if VBR in proper. */ - if( mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 ) ) - return -1; - } - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) ) - { - isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry; - if( !alac ) - return -1; - uint8_t *exdata = NULL; - uint32_t exdata_size = 0; - isom_box_t *alac_ext = isom_get_extension_box( &alac->extensions, QT_BOX_TYPE_WAVE ); - if( alac_ext ) - { - /* Apple Lossless Audio inside QuickTime file format - * Though average bitrate field we found is always set to 0 apparently, - * we set up maxFrameBytes and avgBitRate fields. */ - if( alac_ext->manager & LSMASH_BINARY_CODED_BOX ) - exdata = isom_get_child_box_position( alac_ext->binary, alac_ext->size, QT_BOX_TYPE_ALAC, &exdata_size ); - else - { - isom_wave_t *wave = (isom_wave_t *)alac_ext; - isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, QT_BOX_TYPE_ALAC ); - if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) ) - return -1; - exdata = wave_ext->binary; - exdata_size = wave_ext->size; - } - } - else - { - /* Apple Lossless Audio inside ISO Base Media file format */ - isom_box_t *ext = isom_get_extension_box( &alac->extensions, ISOM_BOX_TYPE_ALAC ); - if( !ext || !(ext->manager & LSMASH_BINARY_CODED_BOX) ) - return -1; - exdata = ext->binary; - exdata_size = ext->size; - } - if( !exdata || exdata_size < 36 ) - return -1; - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) ) - return -1; - exdata += 24; - /* maxFrameBytes */ - exdata[0] = (bufferSizeDB >> 24) & 0xff; - exdata[1] = (bufferSizeDB >> 16) & 0xff; - exdata[2] = (bufferSizeDB >> 8) & 0xff; - exdata[3] = bufferSizeDB & 0xff; - /* avgBitRate */ - exdata[4] = (avgBitrate >> 24) & 0xff; - exdata[5] = (avgBitrate >> 16) & 0xff; - exdata[6] = (avgBitrate >> 8) & 0xff; - exdata[7] = avgBitrate & 0xff; - } - else if( isom_is_waveform_audio( sample_type ) ) - { - isom_box_t *ext = isom_get_extension_box( &sample_entry->extensions, QT_BOX_TYPE_WAVE ); - if( !ext ) - return -1; - uint8_t *exdata = NULL; - uint32_t exdata_size = 0; - if( ext->manager & LSMASH_BINARY_CODED_BOX ) - exdata = isom_get_child_box_position( ext->binary, ext->size, sample_type, &exdata_size ); - else - { - isom_wave_t *wave = (isom_wave_t *)ext; - isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, sample_type ); - if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) ) - return -1; - exdata = wave_ext->binary; - exdata_size = wave_ext->size; - } - /* Check whether exdata is valid or not. */ - if( !exdata || exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 ) - return -1; - exdata += ISOM_BASEBOX_COMMON_SIZE; - uint16_t cbSize = exdata[16] | (exdata[17] << 8); - if( exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 + cbSize ) - return -1; - /* WAVEFORMATEX.nAvgBytesPerSec */ - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) < 0 ) - return -1; - uint32_t nAvgBytesPerSec = avgBitrate / 8; - exdata[ 8] = nAvgBytesPerSec & 0xff; - exdata[ 9] = (nAvgBytesPerSec >> 8) & 0xff; - exdata[10] = (nAvgBytesPerSec >> 16) & 0xff; - exdata[11] = (nAvgBytesPerSec >> 24) & 0xff; - if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FULLMP3_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_MP3_AUDIO ) ) - { - /* MPEGLAYER3WAVEFORMAT.nBlockSize */ - uint32_t nSamplesPerSec = exdata[ 4] | (exdata[ 5] << 8) | (exdata[6] << 16) | (exdata[7] << 24); - uint16_t nFramesPerBlock = exdata[26] | (exdata[27] << 8); - uint16_t padding = 0; /* FIXME? */ - uint16_t nBlockSize = (144 * (avgBitrate / nSamplesPerSec) + padding) * nFramesPerBlock; - exdata[24] = nBlockSize & 0xff; - exdata[25] = (nBlockSize >> 8) & 0xff; - } - } - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) - { - isom_audio_entry_t *dts_audio = (isom_audio_entry_t *)sample_entry; - if( !dts_audio ) - return -1; - isom_box_t *ext = isom_get_extension_box( &dts_audio->extensions, ISOM_BOX_TYPE_DDTS ); - if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 28) ) - return -1; - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) ) - return -1; - if( !stbl->stsz->list ) - maxBitrate = avgBitrate; - uint8_t *exdata = ext->binary + 12; - exdata[0] = (maxBitrate >> 24) & 0xff; - exdata[1] = (maxBitrate >> 16) & 0xff; - exdata[2] = (maxBitrate >> 8) & 0xff; - exdata[3] = maxBitrate & 0xff; - exdata[4] = (avgBitrate >> 24) & 0xff; - exdata[5] = (avgBitrate >> 16) & 0xff; - exdata[6] = (avgBitrate >> 8) & 0xff; - exdata[7] = avgBitrate & 0xff; - } - else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) ) - { - isom_audio_entry_t *eac3 = (isom_audio_entry_t *)sample_entry; - if( !eac3 ) - return -1; - isom_box_t *ext = isom_get_extension_box( &eac3->extensions, ISOM_BOX_TYPE_DEC3 ); - if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 10) ) - return -1; - uint16_t bitrate; - if( stbl->stsz->list ) - { - if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) ) - return -1; - bitrate = maxBitrate / 1000; /* Use maximum bitrate if VBR. */ - } - else - bitrate = stbl->stsz->sample_size * (eac3->samplerate >> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */ - uint8_t *exdata = ext->binary + 8; - exdata[0] = (bitrate >> 5) & 0xff; - exdata[1] = (bitrate & 0x1f) << 3; - } - } - return sample_description_index ? 0 : -1; -} - -static int isom_check_mandatory_boxes( lsmash_file_t *file ) -{ - if( !file - || !file->moov - || !file->moov->mvhd ) - return -1; - /* A movie requires at least one track. */ - if( !file->moov->trak_list.head ) - return -1; - for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_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( file->qt_compatible && !trak->mdia->minf->hdlr ) - return -1; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - if( !stbl->stsd->list.head ) - return -1; - if( !file->fragment - && (!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( !file->fragment ) - return 0; - if( !file->moov->mvex ) - return -1; - for( lsmash_entry_t *entry = file->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_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 == 0 ) - mdhd->creation_time = mdhd->modification_time = current_mp4time; - return 0; -} - -static int isom_set_track_creation_time( isom_trak_t *trak, uint64_t current_mp4time ) -{ - if( !trak - || !trak->tkhd ) - return -1; - isom_tkhd_t *tkhd = trak->tkhd; - if( tkhd->creation_time == 0 ) - 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_file_t *file ) -{ - if( !file - || !file->moov - || !file->moov->mvhd ) - return -1; - uint64_t current_mp4time = isom_get_current_mp4time(); - for( uint32_t i = 1; i <= file->moov->trak_list.entry_count; i++ ) - if( isom_set_track_creation_time( isom_get_trak( file, i ), current_mp4time ) ) - return -1; - isom_mvhd_t *mvhd = file->moov->mvhd; - if( mvhd->creation_time == 0 ) - mvhd->creation_time = mvhd->modification_time = current_mp4time; - return 0; -} - -int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type ) -{ - isom_box_t *parent = hdlr->parent; - lsmash_file_t *file = hdlr->file; - if( !parent || !file ) - return -1; - isom_mdia_t *mdia = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ? (isom_mdia_t *)parent : NULL; - isom_meta_t *meta = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ? (isom_meta_t *)parent - : lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ? (isom_meta_t *)parent : NULL; - uint32_t type = mdia ? (file->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA); - uint32_t subtype = media_type; - hdlr->componentType = type; - hdlr->componentSubtype = subtype; - char *type_name = NULL; - char *subtype_name = NULL; - uint8_t type_name_length = 0; - uint8_t subtype_name_length = 0; - if( mdia ) - type_name = "Media "; - else if( meta ) - type_name = "Metadata "; - else /* if( minf ) */ - type_name = "Data "; - type_name_length = strlen( type_name ); - struct - { - uint32_t subtype; - char *subtype_name; - uint8_t subtype_name_length; - } subtype_table[] = - { - { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 }, - { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video ", 6 }, - { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 }, - { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 }, - { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 }, - { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 }, - { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 }, - { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 }, - { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 }, - { subtype, "Unknown ", 8 } - }; - for( int i = 0; subtype_table[i].subtype; i++ ) - if( subtype == subtype_table[i].subtype ) - { - subtype_name = subtype_table[i].subtype_name; - subtype_name_length = subtype_table[i].subtype_name_length; - break; - } - uint32_t name_length = 15 + subtype_name_length + type_name_length + file->isom_compatible + file->qt_compatible; - uint8_t *name = lsmash_malloc( name_length ); - if( !name ) - return -1; - if( file->qt_compatible ) - name[0] = name_length & 0xff; - memcpy( name + file->qt_compatible, "L-SMASH ", 8 ); - memcpy( name + file->qt_compatible + 8, subtype_name, subtype_name_length ); - memcpy( name + file->qt_compatible + 8 + subtype_name_length, type_name, type_name_length ); - memcpy( name + file->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 ); - if( file->isom_compatible ) - name[name_length - 1] = 0; - hdlr->componentName = name; - hdlr->componentName_length = name_length; - return 0; -} - -/******************************* - public interfaces -*******************************/ - -/*---- track manipulators ----*/ - -void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root - || !root->file - || !root->file->moov ) - return; - for( lsmash_entry_t *entry = root->file->moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - if( !trak - || !trak->tkhd ) - return; - if( trak->tkhd->track_ID == track_ID ) - { - isom_remove_box_by_itself( trak ); - return; - } - } -} - -uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type ) -{ - if( !root || !root->file ) - return 0; - lsmash_file_t *file = root->file; - /* Don't allow to create a new track if the initial movie is already written. */ - if( (file->fragment && file->fragment->movie) - || (file->moov && (file->moov->manager & LSMASH_WRITTEN_BOX)) ) - return 0; - isom_trak_t *trak = isom_add_trak( file->moov ); - if( !trak - || !trak->file - || !trak->file->moov - || !trak->file->moov->mvhd ) - goto fail; - if( isom_add_tkhd( trak ) - || isom_add_mdia( trak ) - || isom_add_mdhd( trak->mdia ) - || isom_add_minf( trak->mdia ) - || isom_add_dinf( trak->mdia->minf ) - || isom_add_dref( trak->mdia->minf->dinf ) - || isom_add_stbl( trak->mdia->minf ) - || 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 ) ) - goto fail; - if( isom_add_hdlr( trak->mdia ) - || isom_setup_handler_reference( trak->mdia->hdlr, media_type ) ) - goto fail; - if( file->qt_compatible ) - { - if( isom_add_hdlr( trak->mdia->minf ) - || isom_setup_handler_reference( trak->mdia->minf->hdlr, QT_REFERENCE_HANDLER_TYPE_URL ) ) - goto fail; - } - switch( media_type ) - { - case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK : - if( isom_add_vmhd( trak->mdia->minf ) ) - goto fail; - trak->mdia->minf->vmhd->flags = 0x000001; - break; - case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK : - if( isom_add_smhd( trak->mdia->minf ) ) - goto fail; - break; - case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK : - if( isom_add_hmhd( trak->mdia->minf ) ) - goto fail; - break; - case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK : - if( file->qt_compatible || file->itunes_movie ) - { - if( isom_add_gmhd( trak->mdia->minf ) - || isom_add_gmin( trak->mdia->minf->gmhd ) - || isom_add_text( trak->mdia->minf->gmhd ) ) - return 0; - /* Default Text Media Information Box. */ - { - isom_text_t *text = trak->mdia->minf->gmhd->text; - text->matrix[0] = 0x00010000; - text->matrix[4] = 0x00010000; - text->matrix[8] = 0x40000000; - } - } - else - goto fail; /* We support only reference text media track for chapter yet. */ - break; - default : - if( isom_add_nmhd( trak->mdia->minf ) ) - goto fail; - break; - } - /* Default Track Header Box. */ - { - isom_tkhd_t *tkhd = trak->tkhd; - if( media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ) - tkhd->volume = 0x0100; - tkhd->matrix[0] = 0x00010000; - tkhd->matrix[4] = 0x00010000; - tkhd->matrix[8] = 0x40000000; - tkhd->duration = 0xffffffff; - tkhd->track_ID = trak->file->moov->mvhd->next_track_ID ++; - } - trak->mdia->mdhd->language = file->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED; - return trak->tkhd->track_ID; -fail: - isom_remove_box_by_itself( trak ); - return 0; -} - -uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number ) -{ - if( !root - || !root->file - || !root->file->moov ) - return 0; - isom_trak_t *trak = (isom_trak_t *)lsmash_get_entry_data( &root->file->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 ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->mdia - || !trak->mdia->hdlr - || !file->moov->mvhd ) - return -1; - /* Prepare Track Aperture Modes if required. */ - if( file->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_box_by_itself( trak->tapt ); - /* Set up Track Header. */ - uint32_t media_type = trak->mdia->hdlr->componentSubtype; - isom_tkhd_t *tkhd = trak->tkhd; - tkhd->flags = param->mode; - tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID; - tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration; - /* Template fields - * alternate_group, layer, volume and matrix - * According to 14496-14, these value are all set to defaut values in 14496-12. - * And when a file is read as an MPEG-4 file, these values shall be ignored. - * If a file complies with other specifications, then those fields may have non-default values - * as required by those other specifications. */ - if( param->alternate_group ) - { - if( file->qt_compatible || file->itunes_movie || file->max_3gpp_version >= 4 ) - tkhd->alternate_group = param->alternate_group; - else - { - tkhd->alternate_group = 0; - lsmash_log( NULL, LSMASH_LOG_WARNING, - "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" ); - } - } - else - tkhd->alternate_group = 0; - if( file->qt_compatible || file->itunes_movie ) - { - tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0; - tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0; - if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ) - for( int i = 0; i < 9; i++ ) - tkhd->matrix[i] = param->matrix[i]; - else - for( int i = 0; i < 9; i++ ) - tkhd->matrix[i] = 0; - } - else - { - tkhd->layer = 0; - tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0; - tkhd->matrix[0] = 0x00010000; - tkhd->matrix[1] = 0; - tkhd->matrix[2] = 0; - tkhd->matrix[3] = 0; - tkhd->matrix[4] = 0x00010000; - tkhd->matrix[5] = 0; - tkhd->matrix[6] = 0; - tkhd->matrix[7] = 0; - tkhd->matrix[8] = 0x40000000; - } - /* visual presentation size */ - tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0; - tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0; - /* Update next_track_ID if needed. */ - if( file->moov->mvhd->next_track_ID <= tkhd->track_ID ) - file->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 ) -{ - if( !root ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, 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_file_t *file, uint32_t track_ID, char *handler_name ) -{ - isom_trak_t *trak = isom_get_trak( file, 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 ) + file->isom_compatible + file->qt_compatible; - if( file->qt_compatible ) - name_length = LSMASH_MIN( name_length, 255 ); - if( name_length > hdlr->componentName_length && hdlr->componentName ) - name = lsmash_realloc( hdlr->componentName, name_length ); - else if( !hdlr->componentName ) - name = lsmash_malloc( name_length ); - else - name = hdlr->componentName; - if( !name ) - return -1; - if( file->qt_compatible ) - name[0] = name_length & 0xff; - memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) ); - if( file->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_file_t *file, uint32_t track_ID, char *handler_name ) -{ - isom_trak_t *trak = isom_get_trak( file, 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 ) + file->isom_compatible + file->qt_compatible; - if( file->qt_compatible ) - name_length = LSMASH_MIN( name_length, 255 ); - if( name_length > hdlr->componentName_length && hdlr->componentName ) - name = lsmash_realloc( hdlr->componentName, name_length ); - else if( !hdlr->componentName ) - name = lsmash_malloc( name_length ); - else - name = hdlr->componentName; - if( !name ) - return -1; - if( file->qt_compatible ) - name[0] = name_length & 0xff; - memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) ); - if( file->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 ) -{ - if( !root ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, 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 ) -{ - if( !root ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, 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 ) -{ - if( !root ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, 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 ) -{ - if( !root ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, 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->tail - || !trak->mdia->minf->stbl->stts->list->tail->data ) - return 0; - return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta; -} - -uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, 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; -} - -uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root ) - return 0; - lsmash_file_t *file = root->file; - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->mdia - || !trak->mdia->minf - || !trak->mdia->minf->stbl ) - return 0; - uint32_t sample_count = isom_get_sample_count( trak ); - if( sample_count == 0 ) - return 0; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - if( !stbl->stts || !stbl->stts->list - || !stbl->ctts || !stbl->ctts->list ) - return 0; - if( !(file->max_isom_version >= 4 && stbl->ctts->version == 1) && !file->qt_compatible ) - return 0; /* This movie shall not have composition to decode timeline shift. */ - lsmash_entry_t *stts_entry = stbl->stts->list->head; - lsmash_entry_t *ctts_entry = stbl->ctts->list->head; - if( !stts_entry || !ctts_entry ) - return 0; - uint64_t dts = 0; - uint64_t cts = 0; - uint32_t ctd_shift = 0; - uint32_t i = 0; - uint32_t j = 0; - for( uint32_t k = 0; k < sample_count; i++ ) - { - 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 0; - cts = dts + (int32_t)ctts_data->sample_offset; - if( dts > cts + ctd_shift ) - ctd_shift = dts - cts; - dts += stts_data->sample_delta; - if( ++i == stts_data->sample_count ) - { - stts_entry = stts_entry->next; - if( !stts_entry ) - return 0; - i = 0; - } - if( ++j == ctts_data->sample_count ) - { - ctts_entry = ctts_entry->next; - if( !ctts_entry ) - return 0; - j = 0; - } - } - return ctd_shift; -} - -uint16_t lsmash_pack_iso_language( char *iso_language ) -{ - if( !iso_language || strlen( iso_language ) != 3 ) - return 0; - return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] ); -} - -static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language ) -{ - if( !MAC_language ) - return -1; - int i = 0; - for( ; isom_languages[i].iso_name; i++ ) - if( ISO_language == isom_languages[i].iso_name ) - break; - if( !isom_languages[i].iso_name ) - return -1; - *MAC_language = isom_languages[i].mac_value; - return 0; -} - -static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language ) -{ - if( !ISO_language ) - return -1; - int i = 0; - for( ; isom_languages[i].iso_name; i++ ) - if( MAC_language == isom_languages[i].mac_value ) - break; - *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED; - return 0; -} - -static int isom_set_media_language( lsmash_file_t *file, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language ) -{ - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->mdia - || !trak->mdia->mdhd ) - return -1; - uint16_t language = 0; - if( file->isom_compatible ) - { - if( ISO_language ) - language = ISO_language; - else if( MAC_language ) - { - if( isom_mac2iso_language( MAC_language, &language ) ) - return -1; - } - else - language = ISOM_LANGUAGE_CODE_UNDEFINED; - } - else if( file->qt_compatible ) - { - if( ISO_language ) - { - if( isom_iso2mac_language( ISO_language, &language ) ) - return -1; - } - else - language = MAC_language; - } - else - return -1; - trak->mdia->mdhd->language = language; - return 0; -} - -int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type ) -{ - isom_sgpd_t *sgpd; - isom_sbgp_t *sbgp; - if( NULL == (sgpd = isom_add_sgpd( parent )) - || NULL == (sbgp = isom_add_sbgp( parent )) ) - return -1; - sbgp->grouping_type = grouping_type; - sgpd->grouping_type = grouping_type; - sgpd->version = 1; /* We use version 1 for Sample Group Description Box 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 0; -} - -static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type grouping_type ) -{ - lsmash_file_t *file = trak->file; - switch( grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - assert( file->max_isom_version >= 6 ); - break; - case ISOM_GROUP_TYPE_ROLL : - assert( file->avc_extensions || file->qt_compatible ); - break; - default : - assert( 0 ); - break; - } - if( isom_add_sample_grouping( (isom_box_t *)trak->mdia->minf->stbl, grouping_type ) < 0 ) - return -1; - if( trak->cache->fragment && file->max_isom_version >= 6 ) - switch( grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - trak->cache->fragment->rap_grouping = 1; - break; - case ISOM_GROUP_TYPE_ROLL : - trak->cache->fragment->roll_grouping = 1; - break; - default : - /* We don't consider other grouping types currently. */ - break; - } - 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 ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - isom_trak_t *trak = isom_get_trak( file, 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( file, track_ID, param->ISO_language, param->MAC_language ) ) - return -1; - if( param->media_handler_name - && isom_set_media_handler_name( file, track_ID, param->media_handler_name ) ) - return -1; - if( file->qt_compatible && param->data_handler_name - && isom_set_data_handler_name( file, track_ID, param->data_handler_name ) ) - return -1; - if( (file->avc_extensions || file->qt_compatible) && param->roll_grouping - && isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_ROLL ) ) - return -1; - if( (file->max_isom_version >= 6) && param->rap_grouping - && isom_create_sample_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 ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - isom_trak_t *trak = isom_get_trak( file, 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; - param->timescale = mdhd->timescale; - param->handler_type = trak->mdia->hdlr->componentSubtype; - param->duration = mdhd->duration; - /* Whether sample grouping present. */ - { - isom_sbgp_t *sbgp; - isom_sgpd_t *sgpd; - sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_ROLL ); - sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL ); - param->roll_grouping = sbgp && sgpd; - sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP ); - sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); - param->rap_grouping = sbgp && sgpd; - } - /* Get media language. */ - if( mdhd->language >= 0x800 ) - { - param->MAC_language = 0; - param->ISO_language = mdhd->language; - } - else - { - param->MAC_language = mdhd->language; - param->ISO_language = 0; - } - /* Get handler name(s). */ - isom_hdlr_t *hdlr = trak->mdia->hdlr; - int length = LSMASH_MIN( 255, hdlr->componentName_length ); - if( length ) - { - memcpy( param->media_handler_name_shadow, hdlr->componentName + file->qt_compatible, length ); - param->media_handler_name_shadow[length - 2 + file->isom_compatible + file->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 + file->qt_compatible, length ); - param->data_handler_name_shadow[length - 2 + file->isom_compatible + file->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 ----*/ - -void lsmash_discard_boxes( lsmash_root_t *root ) -{ - if( !root || !root->file ) - return; - isom_remove_all_extension_boxes( &root->file->extensions ); -} - -static void isom_remove_root( lsmash_root_t *root ) -{ - if( !root ) - return; - isom_remove_all_extension_boxes( &root->extensions ); - lsmash_free( root ); -} - -lsmash_root_t *lsmash_create_root( void ) -{ - lsmash_root_t *root = lsmash_malloc_zero( sizeof(lsmash_root_t) ); - if( !root ) - return NULL; - root->destruct = (isom_extension_destructor_t)isom_remove_root; - root->root = root; - return root; -} - -void lsmash_destroy_root( lsmash_root_t *root ) -{ - isom_remove_box_by_itself( root ); -} - -static int fread_wrapper( void *opaque, uint8_t *buf, int size ) -{ - int read_size = fread( buf, 1, size, (FILE *)opaque ); - if( read_size != size && !feof( (FILE *)opaque ) ) - return -1; - return read_size; -} - -static int fwrite_wrapper( void *opaque, uint8_t *buf, int size ) -{ - return fwrite( buf, 1, size, (FILE *)opaque ); -} - -static int64_t fseek_wrapper( void *opaque, int64_t offset, int whence ) -{ - if( lsmash_fseek( (FILE *)opaque, offset, whence ) != 0 ) - return -1; - return lsmash_ftell( (FILE *)opaque ); -} - -int lsmash_open_file -( - const char *filename, - int open_mode, - lsmash_file_parameters_t *param -) -{ - if( !filename || !param ) - return -1; - char mode[4] = { 0 }; - lsmash_file_mode file_mode; - if( open_mode == 0 ) - { - memcpy( mode, "w+b", 4 ); - file_mode = LSMASH_FILE_MODE_WRITE - | LSMASH_FILE_MODE_BOX - | LSMASH_FILE_MODE_INITIALIZATION - | LSMASH_FILE_MODE_MEDIA; - } -#ifdef LSMASH_DEMUXER_ENABLED - else if( open_mode == 1 ) - { - memcpy( mode, "rb", 3 ); - file_mode = LSMASH_FILE_MODE_READ; - } -#endif - if( !mode[0] ) - return -1; - FILE *stream = NULL; - int seekable = 1; - if( !strcmp( filename, "-" ) ) - { - if( file_mode & LSMASH_FILE_MODE_READ ) - stream = stdin; - else if( file_mode & LSMASH_FILE_MODE_WRITE ) - { - stream = stdout; - seekable = 0; - file_mode |= LSMASH_FILE_MODE_FRAGMENTED; - } - } - else - stream = lsmash_fopen( filename, mode ); - if( !stream ) - return -1; - memset( param, 0, sizeof(lsmash_file_parameters_t) ); - param->mode = file_mode; - param->opaque = (void *)stream; - param->read = fread_wrapper; - param->write = fwrite_wrapper; - param->seek = seekable ? fseek_wrapper : NULL; - param->major_brand = 0; - param->brands = NULL; - param->brand_count = 0; - param->minor_version = 0; - 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; - return 0; -} - -int lsmash_close_file -( - lsmash_file_parameters_t *param -) -{ - if( !param ) - return -1; - if( !param->opaque ) - return 0; - int ret = fclose( (FILE *)param->opaque ); - param->opaque = NULL; - return ret; -} - -static int isom_set_brands -( - lsmash_file_t *file, - lsmash_brand_type major_brand, - uint32_t minor_version, - lsmash_brand_type *brands, - uint32_t brand_count -) -{ - if( brand_count > 50 ) - return -1; /* We support setting brands up to 50. */ - if( major_brand == 0 || brand_count == 0 ) - { - /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */ - isom_remove_box_by_itself( file->ftyp ); - /* Anyway we use QTFF as a default file format. */ - file->qt_compatible = 1; - return 0; - } - isom_ftyp_t *ftyp; - if( file->flags & LSMASH_FILE_MODE_INITIALIZATION ) - { - /* Add File Type Box if absent yet. */ - if( !file->ftyp && isom_add_ftyp( file ) < 0 ) - return -1; - ftyp = file->ftyp; - } - else - { - /* Add Segment Type Box if absent yet. */ - ftyp = file->styp_list.head && file->styp_list.head->data - ? (isom_styp_t *)file->styp_list.head->data - : isom_add_styp( file ); - if( !ftyp ) - return -1; - } - /* Allocate an array of compatible brands. - * ISO/IEC 14496-12 doesn't forbid the absence of brands in the compatible brand list. - * For a reason of safety, however, we set at least one brand in the list. */ - size_t alloc_size = (brand_count ? brand_count : 1) * sizeof(uint32_t); - lsmash_brand_type *compatible_brands; - if( !file->compatible_brands ) - compatible_brands = lsmash_malloc( alloc_size ); - else - compatible_brands = lsmash_realloc( file->compatible_brands, alloc_size ); - if( !compatible_brands ) - return -1; - /* Set compatible brands. */ - if( brand_count ) - for( uint32_t i = 0; i < brand_count; i++ ) - compatible_brands[i] = brands[i]; - else - { - /* At least one compatible brand. */ - compatible_brands[0] = major_brand; - brand_count = 1; - } - file->compatible_brands = compatible_brands; - /* Duplicate an array of compatible brands. */ - lsmash_free( ftyp->compatible_brands ); - ftyp->compatible_brands = lsmash_memdup( compatible_brands, alloc_size ); - if( !ftyp->compatible_brands ) - { - lsmash_freep( &file->compatible_brands ); - return -1; - } - ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8 + brand_count * 4; - ftyp->major_brand = major_brand; - ftyp->minor_version = minor_version; - ftyp->brand_count = brand_count; - file->brand_count = brand_count; - return isom_check_compatibility( file ); -} - -lsmash_file_t *lsmash_set_file -( - lsmash_root_t *root, - lsmash_file_parameters_t *param -) -{ - if( !root || !param ) - return NULL; - if( root->file ) - /* Handling of multiple files is not supported yet. */ - return NULL; - lsmash_file_t *file = isom_add_file( root ); - if( !file ) - return NULL; - lsmash_bs_t *bs = lsmash_malloc_zero( sizeof(lsmash_bs_t) ); - if( !bs ) - goto fail; - file->bs = bs; - file->flags = param->mode; - file->bs->stream = param->opaque; - file->bs->read = param->read; - file->bs->write = param->write; - file->bs->seek = param->seek; - file->bs->unseekable = (param->seek == NULL); - file->max_chunk_duration = param->max_chunk_duration; - file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration ); - file->max_chunk_size = param->max_chunk_size; - file->max_read_size = param->max_read_size; - if( (file->flags & LSMASH_FILE_MODE_WRITE) - && (file->flags & LSMASH_FILE_MODE_BOX) ) - { - /* Establish the fragment handler if required. */ - if( file->flags & LSMASH_FILE_MODE_FRAGMENTED ) - { - file->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) ); - if( !file->fragment ) - goto fail; - file->fragment->pool = lsmash_create_entry_list(); - if( !file->fragment->pool ) - goto fail; - } - else if( file->bs->unseekable ) - /* For unseekable output operations, LSMASH_FILE_MODE_FRAGMENTED shall be set. */ - goto fail; - /* Establish file types. */ - if( isom_set_brands( file, param->major_brand, - param->minor_version, - param->brands, param->brand_count ) < 0 ) - goto fail; - /* Create the movie header if the initialization of the streams is required. */ - if( file->flags & LSMASH_FILE_MODE_INITIALIZATION ) - { - if( isom_add_moov( file ) < 0 - || isom_add_mvhd( file->moov ) < 0 ) - goto fail; - /* Default Movie Header Box. */ - isom_mvhd_t *mvhd = file->moov->mvhd; - mvhd->rate = 0x00010000; - mvhd->volume = 0x0100; - mvhd->matrix[0] = 0x00010000; - mvhd->matrix[4] = 0x00010000; - mvhd->matrix[8] = 0x40000000; - mvhd->next_track_ID = 1; - } - } - root->file = file; - return file; -fail: - isom_remove_box_by_itself( file ); - return NULL; -} - -int64_t lsmash_read_file -( - lsmash_file_t *file, - lsmash_file_parameters_t *param -) -{ -#ifdef LSMASH_DEMUXER_ENABLED - if( !file || !file->bs ) - return -1LL; - int64_t ret = -1; - if( file->flags & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP) ) - { - /* Get the file size if seekable when reading. */ - if( !file->bs->unseekable ) - { - ret = lsmash_bs_seek( file->bs, 0, SEEK_END ); - if( ret < 0 ) - return ret; - file->bs->written = ret; - lsmash_bs_seek( file->bs, 0, SEEK_SET ); - } - else - ret = 0; - /* Read whole boxes. */ - if( isom_read_file( file ) < 0 ) - return -1; - if( param ) - { - if( file->ftyp ) - { - /* file types */ - isom_ftyp_t *ftyp = file->ftyp; - param->major_brand = ftyp->major_brand ? ftyp->major_brand : ISOM_BRAND_TYPE_QT; - param->minor_version = ftyp->minor_version; - param->brands = file->compatible_brands; - param->brand_count = file->brand_count; - } - else if( file->styp_list.head && file->styp_list.head->data ) - { - /* segment types */ - isom_styp_t *styp = (isom_styp_t *)file->styp_list.head->data; - param->major_brand = styp->major_brand ? styp->major_brand : ISOM_BRAND_TYPE_QT; - param->minor_version = styp->minor_version; - param->brands = file->compatible_brands; - param->brand_count = file->brand_count; - } - else - { - param->major_brand = file->mp4_version1 ? ISOM_BRAND_TYPE_MP41 : ISOM_BRAND_TYPE_QT; - param->minor_version = 0; - param->brands = NULL; - param->brand_count = 0; - } - } - } - return ret; -#else - return -1LL; -#endif -} - -lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode mode ) -{ - if( !filename || ((mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_READ)) ) - return NULL; - int open_mode = -1; - if( mode & LSMASH_FILE_MODE_WRITE ) - open_mode = 0; -#ifdef LSMASH_DEMUXER_ENABLED - else if( mode & LSMASH_FILE_MODE_READ ) - open_mode = 1; -#endif - if( open_mode < 0 ) - return NULL; - lsmash_root_t *root = lsmash_create_root(); - if( !root ) - return NULL; - lsmash_file_parameters_t param; - if( lsmash_open_file( filename, open_mode, ¶m ) < 0 ) - { - lsmash_destroy_root( root ); - return NULL; - } - param.mode |= mode; - lsmash_file_t *file = lsmash_set_file( root, ¶m ); - if( !file || (open_mode == 1 && lsmash_read_file( file, ¶m ) < 0) ) - { - lsmash_close_file( ¶m ); - lsmash_destroy_root( root ); - return NULL; - } - /* Don't fclose() here. */ - param.opaque = NULL; - lsmash_close_file( ¶m ); - /* Call fclose() when calling lsmash_destroy_root(). */ - file->bc_fclose = 1; - return root; -} - -void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param ) -{ - memset( param, 0, sizeof(lsmash_movie_parameters_t) ); - 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 ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->moov->mvhd - || ((param->major_brand || (param->brands && param->number_of_brands)) - && isom_set_brands( file, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) < 0) ) - return -1; - isom_mvhd_t *mvhd = file->moov->mvhd; - if( param->max_chunk_duration > 0 ) - file->max_chunk_duration = param->max_chunk_duration; - if( param->max_async_tolerance > 0 ) - file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration ); - if( param->max_chunk_size ) - file->max_chunk_size = param->max_chunk_size; - if( param->max_read_size ) - file->max_read_size = param->max_read_size; - mvhd->timescale = param->timescale; - if( file->qt_compatible || file->itunes_movie ) - { - mvhd->rate = param->playback_rate; - mvhd->volume = param->playback_volume; - mvhd->previewTime = param->preview_time; - mvhd->previewDuration = param->preview_duration; - mvhd->posterTime = param->poster_time; - } - else - { - mvhd->rate = 0x00010000; - mvhd->volume = 0x0100; - mvhd->previewTime = 0; - mvhd->previewDuration = 0; - mvhd->posterTime = 0; - } - return 0; -} - -int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->moov->mvhd ) - return -1; - isom_mvhd_t *mvhd = file->moov->mvhd; - if( file->ftyp ) - { - isom_ftyp_t *ftyp = file->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 = file->max_chunk_duration; - param->max_async_tolerance = file->max_async_tolerance; - param->max_chunk_size = file->max_chunk_size; - param->max_read_size = file->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 = file->moov->trak_list.entry_count; - return 0; -} - -uint32_t lsmash_get_movie_timescale( lsmash_root_t *root ) -{ - if( !root - || !root->file - || !root->file->moov - || !root->file->moov->mvhd ) - return 0; - return root->file->moov->mvhd->timescale; -} - -static int isom_scan_trak_profileLevelIndication( isom_trak_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.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; - lsmash_codec_type_t sample_type = (lsmash_codec_type_t)sample_entry->type; - if( trak->mdia->minf->vmhd ) - { - if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO ) - || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) ) - { - /* FIXME: Do we have to arbitrate like audio? */ - if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED ) - *visual_pli = lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO ) - ? MP4SYS_OBJECT_TYPE_Parameter_Sets_H_264_ISO_14496_10 - : MP4SYS_VISUAL_PLI_H264_AVC; - } - else - *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED; - } - else if( trak->mdia->minf->smhd ) - { - if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) ) - { - isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry; -#ifdef LSMASH_DEMUXER_ENABLED - isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &audio->extensions, ISOM_BOX_TYPE_ESDS ); - if( !esds || !esds->ES ) - return -1; - if( !lsmash_check_codec_type_identical( audio->summary.sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) ) - /* This is needed when copying descriptions. */ - mp4sys_setup_summary_from_DecoderSpecificInfo( &audio->summary, esds->ES ); -#endif - *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( &audio->summary ) ); - } - else - /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */ - *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; - } - else - ; /* FIXME: Do we have to set OD_profileLevelIndication? */ - } - return 0; -} - -int isom_setup_iods( isom_moov_t *moov ) -{ - if( !moov->iods && isom_add_iods( moov ) ) - return -1; - isom_iods_t *iods = moov->iods; - iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */ - if( !iods->OD ) - goto fail; - 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_t *trak = (isom_trak_t *)entry->data; - if( !trak - || !trak->tkhd ) - goto fail; - if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) ) - goto fail; - if( mp4sys_add_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) ) - goto fail; - } - 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 ) ) - goto fail; - return 0; -fail: - isom_remove_box_by_itself( iods ); - return -1; -} - -int lsmash_create_object_descriptor( lsmash_root_t *root ) -{ - if( !root || !root->file ) - return -1; - lsmash_file_t *file = root->file; - /* Return error if this file is not compatible with MP4 file format. */ - if( !file->mp4_version1 && !file->mp4_version2 ) - return -1; - return isom_setup_iods( file->moov ); -} - -/*---- finishing functions ----*/ - -int isom_complement_data_reference( isom_minf_t *minf ) -{ - if( !minf->dinf - || !minf->dinf->dref ) - return -1; - /* Complement data referece if absent. */ - if( !minf->dinf->dref->list.head ) - { - isom_dref_entry_t *url = isom_add_dref_entry( minf->dinf->dref ); - if( !url ) - return -1; - url->flags = 0x000001; /* Media data is in the same file. */ - } - return 0; -} - -int isom_rearrange_boxes -( - lsmash_file_t *file, - lsmash_adhoc_remux_t *remux, - uint8_t *buf[2], - size_t read_num, - size_t size, - uint64_t read_pos, - uint64_t write_pos, - uint64_t file_size -) -{ - /* Copy-pastan */ - int buf_switch = 1; - lsmash_bs_t *bs = file->bs; - while( read_num == size ) - { - if( lsmash_bs_seek( bs, read_pos, SEEK_SET ) < 0 ) - return -1; - lsmash_bs_read_data( bs, buf[buf_switch], &read_num ); - read_pos = bs->offset; - buf_switch ^= 0x1; - if( lsmash_bs_seek( bs, write_pos, SEEK_SET ) < 0 ) - return -1; - if( lsmash_bs_write_data( bs, buf[buf_switch], size ) < 0 ) - return -1; - write_pos = bs->offset; - if( remux && remux->func ) - remux->func( remux->param, write_pos, file_size ); // FIXME: - } - if( lsmash_bs_write_data( bs, buf[buf_switch ^ 0x1], read_num ) < 0 ) - return -1; - if( remux && remux->func ) - remux->func( remux->param, file_size, file_size ); // FIXME: - return 0; -} - -int isom_establish_movie( lsmash_file_t *file ) -{ - if( isom_check_mandatory_boxes( file ) - || isom_set_movie_creation_time( file ) - || isom_update_box_size( file->moov ) == 0 ) - return -1; - return 0; -} - -int lsmash_finish_movie -( - lsmash_root_t *root, - lsmash_adhoc_remux_t *remux -) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->bs - || !file->moov ) - return -1; - if( file->fragment ) - return isom_finish_final_fragment_movie( file, remux ); - isom_moov_t *moov = file->moov; - for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - if( !trak - || !trak->cache - || !trak->tkhd - || !trak->mdia - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || isom_complement_data_reference( trak->mdia->minf ) < 0 ) - 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. */ - lsmash_edit_t edit; - edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) ); - edit.start_time = 0; - edit.rate = ISOM_EDIT_MODE_NORMAL; - if( lsmash_create_explicit_timeline_map( root, track_ID, edit ) ) - 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_tkhd_duration( trak ) < 0 ) - return -1; - if( isom_update_bitrate_description( trak->mdia ) < 0 ) - return -1; - } - if( file->mp4_version1 == 1 && isom_setup_iods( moov ) < 0 ) - return -1; - if( isom_establish_movie( file ) < 0 ) - return -1; - /* Write the size of Media Data Box here. */ - lsmash_bs_t *bs = file->bs; - file->mdat->manager &= ~LSMASH_INCOMPLETE_BOX; - if( isom_write_box( bs, (isom_box_t *)file->mdat ) < 0 ) - return -1; - /* Write the Movie Box and a Meta Box if no optimization for progressive download. */ - uint64_t meta_size = file->meta ? file->meta->size : 0; - if( !remux ) - { - if( isom_write_box( bs, (isom_box_t *)file->moov ) - || isom_write_box( bs, (isom_box_t *)file->meta ) ) - return -1; - file->size += moov->size + meta_size; - return 0; - } - /* stco->co64 conversion, depending on last chunk's offset */ - for( lsmash_entry_t *entry = moov->trak_list.head; entry; ) - { - isom_trak_t *trak = (isom_trak_t *)entry->data; - isom_stco_t *stco = trak->mdia->minf->stbl->stco; - if( !stco->list->tail ) - return -1; - if( stco->large_presentation - || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX ) - { - entry = entry->next; - continue; /* no need to convert stco into co64 */ - } - /* stco->co64 conversion */ - if( isom_convert_stco_to_co64( trak->mdia->minf->stbl ) < 0 - || isom_update_box_size( moov ) == 0 ) - return -1; - entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */ - } - /* now the amount of offset is fixed. */ - uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */ - /* buffer size must be at least mtf_size * 2 */ - remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 ); - /* Split to 2 buffers. */ - uint8_t *buf[2] = { NULL, NULL }; - if( (buf[0] = (uint8_t*)lsmash_malloc( remux->buffer_size )) == NULL ) - return -1; /* NOTE: i think we still can fallback to "return isom_write_moov();" here. */ - size_t size = remux->buffer_size / 2; - buf[1] = buf[0] + size; - /* 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_t *)entry->data)->mdia->minf->stbl->stco; - if( stco->large_presentation ) - for( lsmash_entry_t *co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next ) - ((isom_co64_entry_t *)co64_entry->data)->chunk_offset += mtf_size; - else - for( lsmash_entry_t *stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next ) - ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += mtf_size; - } - /* Backup starting area of mdat and write moov + meta there instead. */ - isom_mdat_t *mdat = file->mdat; - uint64_t total = file->size + mtf_size; - uint64_t placeholder_pos = file->free ? file->free->pos : mdat->pos; - if( lsmash_bs_seek( bs, placeholder_pos, SEEK_SET ) < 0 ) - goto fail; - size_t read_num = size; - lsmash_bs_read_data( bs, buf[0], &read_num ); - uint64_t read_pos = bs->offset; - /* Write moov + meta there instead. */ - if( lsmash_bs_seek( bs, placeholder_pos, SEEK_SET ) < 0 - || isom_write_box( bs, (isom_box_t *)file->moov ) < 0 - || isom_write_box( bs, (isom_box_t *)file->meta ) < 0 ) - goto fail; - uint64_t write_pos = bs->offset; - /* Update the positions */ - mdat->pos += mtf_size; - if( file->free ) - file->free->pos += mtf_size; - /* Move Media Data Box. */ - if( isom_rearrange_boxes( file, remux, buf, read_num, size, read_pos, write_pos, total ) < 0 ) - goto fail; - file->size += mtf_size; - lsmash_free( buf[0] ); - return 0; -fail: - lsmash_free( buf[0] ); - return -1; -} - -int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta ) -{ - if( !root || track_ID == 0 ) - return -1; - lsmash_file_t *file = root->file; - if( file->fragment - && file->fragment->movie ) - { - isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID ); - if( !traf - || !traf->cache - || !traf->tfhd ) - return -1; - return isom_set_fragment_last_duration( traf, sample_delta ); - } - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->cache - || !trak->mdia - || !trak->mdia->mdhd - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stsd - || !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 */ - /* Set the duration of the first sample. - * This duration is also the duration of the last sample. */ - 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; - int no_last = (sample_count > i); - isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data; - if( !last_stts_data ) - return -1; - /* Consider QuikcTime fixed compression audio. */ - isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, - trak->cache->chunk.sample_description_index ); - if( !audio ) - return -1; - if( (audio->manager & LSMASH_AUDIO_DESCRIPTION) - && (audio->manager & LSMASH_QTFF_BASE) - && (audio->version == 1) - && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) ) - { - if( audio->samplesPerPacket == 0 ) - return -1; - int exclude_last_sample = no_last ? 0 : 1; - uint32_t j = audio->samplesPerPacket; - for( lsmash_entry_t *entry = stts->list->tail; entry && j > 1; entry = entry->prev ) - { - isom_stts_entry_t *stts_data = (isom_stts_entry_t *)entry->data; - if( !stts_data ) - return -1; - for( uint32_t k = exclude_last_sample; k < stts_data->sample_count && j > 1; k++ ) - { - sample_delta -= stts_data->sample_delta; - --j; - } - exclude_last_sample = 0; - } - } - /* Set sample_delta. */ - if( no_last ) - { - /* The duration of the last sample is not set yet. */ - 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 ) < 0 ) - return -1; - } - /* The duration of the last sample is already set. Replace it with a new one. */ - else if( isom_replace_last_sample_delta( stbl, sample_delta ) < 0 ) - return -1; - return lsmash_update_track_duration( root, track_ID, sample_delta ); -} - -/*---- timeline manipulator ----*/ - -int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit ) -{ - if( !root || edit.duration == 0 || edit.start_time < -1 ) - return -1; - lsmash_file_t *file = root->file; - isom_trak_t *trak = isom_get_trak( file, 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, edit_number ); - if( !data ) - return -1; - data->segment_duration = edit.duration; - data->media_time = edit.start_time; - data->media_rate = edit.rate; - if( elst->pos == 0 || !file->fragment || file->bs->unseekable ) - 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 = file->bs; - uint64_t current_pos = bs->offset; - uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12); - lsmash_bs_seek( bs, 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)LSMASH_MIN( data->segment_duration, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, (uint32_t)data->media_time ); - } - lsmash_bs_put_be32( bs, data->media_rate ); - int ret = lsmash_bs_flush_buffer( bs ); - lsmash_bs_seek( bs, current_pos, SEEK_SET ); - return ret; -} - -int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit ) -{ - if( !root || edit.start_time < -1 ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak - || !trak->tkhd ) - return -1; - edit.duration = (edit.duration || root->file->fragment) ? edit.duration - : trak->tkhd->duration ? trak->tkhd->duration - : isom_update_tkhd_duration( trak ) < 0 ? 0 - : trak->tkhd->duration; - if( (!trak->edts && isom_add_edts( trak )) - || (!trak->edts->elst && isom_add_elst( trak->edts )) - || isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate ) ) - return -1; - return isom_update_tkhd_duration( trak ); -} - -int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit ) -{ - if( !root || !edit ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak ) - return -1; - if( !trak->edts - || !trak->edts->elst ) - { - /* no edits */ - edit->duration = 0; - edit->start_time = 0; - edit->rate = 0; - return 0; - } - isom_elst_entry_t *elst = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, edit_number ); - if( !elst ) - return -1; - edit->duration = elst->segment_duration; - edit->start_time = elst->media_time; - edit->rate = elst->media_rate; - return 0; -} - -uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak - || !trak->edts - || !trak->edts->elst - || !trak->edts->elst->list ) - return 0; - return trak->edts->elst->list->entry_count; -} - -/*---- create / modification time fields manipulators ----*/ - -int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, 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 ) -{ - if( !root ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, 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 ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->moov->mvhd ) - return -1; - isom_mvhd_t *mvhd = file->moov->mvhd; - mvhd->modification_time = isom_get_current_mp4time(); - /* overwrite strange creation_time */ - if( mvhd->creation_time > mvhd->modification_time ) - mvhd->creation_time = mvhd->modification_time; - return 0; -} - -/*---- sample manipulators ----*/ -lsmash_sample_t *lsmash_create_sample( uint32_t size ) -{ - lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) ); - if( !sample ) - return NULL; - if( size == 0 ) - return sample; - sample->data = lsmash_malloc( size ); - if( !sample->data ) - { - lsmash_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 == 0 ) - { - if( sample->data ) - lsmash_free( sample->data ); - sample->data = NULL; - sample->length = 0; - return 0; - } - if( size == sample->length ) - return 0; - uint8_t *data; - if( !sample->data ) - data = lsmash_malloc( size ); - else - data = lsmash_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 ) - lsmash_free( sample->data ); - lsmash_free( sample ); -} - -isom_sample_pool_t *isom_create_sample_pool( uint64_t size ) -{ - isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) ); - if( !pool ) - return NULL; - if( size == 0 ) - return pool; - pool->data = lsmash_malloc( size ); - if( !pool->data ) - { - lsmash_free( pool ); - return NULL; - } - pool->alloc = size; - return pool; -} - -void isom_remove_sample_pool( isom_sample_pool_t *pool ) -{ - if( !pool ) - return; - if( pool->data ) - lsmash_free( pool->data ); - lsmash_free( pool ); -} - -static uint32_t isom_add_size( isom_trak_t *trak, uint32_t sample_size ) -{ - if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) ) - return 0; - return isom_get_sample_count( trak ); -} - -static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts ) -{ - isom_stts_t *stts = stbl->stts; - if( stts->list->entry_count == 0 ) - { - if( isom_add_stts_entry( stbl, dts ) ) - return 0; - cache->dts = dts; - return dts; - } - if( dts <= cache->dts ) - return 0; - uint32_t sample_delta = dts - cache->dts; - isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data; - if( data->sample_delta == sample_delta ) - ++ data->sample_count; - else if( isom_add_stts_entry( stbl, sample_delta ) ) - return 0; - cache->dts = dts; - return sample_delta; -} - -static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts ) -{ - isom_ctts_t *ctts = stbl->ctts; - if( !ctts ) - { - if( cts == cache->dts ) - { - cache->cts = cts; - return 0; - } - /* Add ctts box and the first ctts entry. */ - if( isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) ) - return -1; - ctts = stbl->ctts; - isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data; - uint32_t sample_count = stbl->stsz->sample_count; - if( sample_count != 1 ) - { - data->sample_count = sample_count - 1; - if( isom_add_ctts_entry( stbl, cts - cache->dts ) ) - return -1; - } - else - data->sample_offset = cts; - cache->cts = cts; - return 0; - } - if( !ctts->list ) - return -1; - isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data; - uint32_t sample_offset = cts - cache->dts; - if( data->sample_offset == sample_offset ) - ++ data->sample_count; - else if( isom_add_ctts_entry( stbl, sample_offset ) ) - return -1; - cache->cts = cts; - return 0; -} - -static int isom_add_timestamp( isom_trak_t *trak, uint64_t dts, uint64_t cts ) -{ - if( !trak->cache - || !trak->mdia->minf->stbl->stts - || !trak->mdia->minf->stbl->stts->list ) - return -1; - lsmash_file_t *file = trak->file; - if( file->isom_compatible && file->qt_compatible && (cts - dts) > INT32_MAX ) - return -1; /* sample_offset is not compatible. */ - isom_stbl_t *stbl = trak->mdia->minf->stbl; - isom_timestamp_t *ts_cache = &trak->cache->timestamp; - uint32_t sample_count = isom_get_sample_count( trak ); - uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0; - if( sample_count > 1 && !sample_delta ) - return -1; - if( isom_add_cts( stbl, ts_cache, cts ) ) - return -1; - if( (cts + ts_cache->ctd_shift) < dts ) - { - if( (file->max_isom_version < 4 && !file->qt_compatible) /* Negative sample offset is not supported. */ - || (file->max_isom_version >= 4 && file->qt_compatible) /* ctts version 1 is not defined in QTFF. */ - || ((dts - cts) > INT32_MAX) ) /* Overflow */ - return -1; - ts_cache->ctd_shift = dts - cts; - if( stbl->ctts->version == 0 && !file->qt_compatible ) - stbl->ctts->version = 1; - } - if( trak->cache->fragment ) - { - isom_fragment_t *fragment_cache = trak->cache->fragment; - fragment_cache->last_duration = sample_delta; - fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts ); - } - return 0; -} - -static int isom_add_sync_point( isom_trak_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->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_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_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop ) -{ - if( !trak->file->qt_compatible ) - return 0; - if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) ) - return 0; - /* This sample is a partial sync sample. */ - 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_t *trak, lsmash_sample_property_t *prop ) -{ - if( !trak->file->qt_compatible && !trak->file->avc_extensions ) - return 0; - uint8_t avc_extensions = trak->file->avc_extensions; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - if( stbl->sdtp ) - return isom_add_sdtp_entry( (isom_box_t *)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( (isom_box_t *)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( (isom_box_t *)stbl, &null_prop, avc_extensions ) ) - return -1; - return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, avc_extensions ); -} - -int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment ) -{ - isom_rap_entry_t *rap = group->random_access; - if( !rap ) - return 0; - assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data ); - rap->num_leading_samples_known = num_leading_samples_known; - /* Avoid duplication of sample group descriptions. */ - uint32_t group_description_index = is_fragment ? 0x10001 : 1; - for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next ) - { - isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data; - if( !data ) - return -1; - if( rap->num_leading_samples_known == data->num_leading_samples_known - && rap->num_leading_samples == data->num_leading_samples ) - { - /* The same description already exists. - * Remove the latest random access entry. */ - lsmash_remove_entry_tail( sgpd->list, NULL ); - /* Replace assigned group_description_index with the one corresponding the same description. */ - if( group->assignment->group_description_index == 0 ) - { - /* We don't create consecutive sample groups not assigned to 'rap '. - * So the previous sample group shall be a group of 'rap ' if any. */ - if( group->prev_assignment ) - { - assert( group->prev_assignment->group_description_index ); - group->prev_assignment->group_description_index = group_description_index; - } - } - else - group->assignment->group_description_index = group_description_index; - break; - } - ++group_description_index; - } - group->random_access = NULL; - return 0; -} - -int isom_group_random_access( isom_box_t *parent, lsmash_sample_t *sample ) -{ - if( parent->file->max_isom_version < 6 ) - return 0; - isom_cache_t *cache; - isom_sbgp_t *sbgp; - isom_sgpd_t *sgpd; - uint32_t sample_count; - int is_fragment; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) - { - isom_trak_t *trak = (isom_trak_t *)parent; - cache = trak->cache; - sbgp = isom_get_sample_to_group ( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP ); - sgpd = isom_get_sample_group_description( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP ); - sample_count = isom_get_sample_count( trak ); - is_fragment = 0; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - { - isom_traf_t *traf = (isom_traf_t *)parent; - cache = traf->cache; - sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP ); - sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP ); - sample_count = cache->fragment->sample_count + 1; - is_fragment = 1; - } - else - { - assert( 0 ); - sbgp = NULL; - sgpd = NULL; - } - if( !sbgp || !sgpd ) - return 0; - lsmash_sample_property_t *prop = &sample->prop; - uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) - || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) - || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP) - || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete); - isom_rap_group_t *group = cache->rap; - if( !group ) - { - /* This sample is the first sample, create a grouping cache. */ - assert( sample_count == 1 ); - group = lsmash_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 + (is_fragment ? 0x10000 : 0) ); - } - else - { - /* The first sample is not always a random access point. */ - group->random_access = NULL; - group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); - } - if( !group->assignment ) - { - lsmash_free( group ); - return -1; - } - group->prev_assignment = NULL; - group->is_prev_rap = is_rap; - cache->rap = group; - return 0; - } - if( group->is_prev_rap ) - { - /* OK. here, the previous sample is a menber of 'rap '. */ - if( !is_rap ) - { - /* This sample isn't a member of 'rap ' and the previous sample is. - * So we create a new group and set 0 on its group_description_index. */ - group->prev_assignment = group->assignment; - group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); - if( !group->assignment ) - { - lsmash_free( group ); - return -1; - } - } - else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) ) - { - /* Create a new group since there is the possibility the next sample is a leading sample. - * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */ - if( isom_rap_grouping_established( group, 1, sgpd, is_fragment ) < 0 ) - return -1; - group->random_access = isom_add_rap_group_entry( sgpd ); - group->prev_assignment = group->assignment; - group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) ); - if( !group->assignment ) - { - lsmash_free( group ); - return -1; - } - } - else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */ - ++ group->assignment->sample_count; - } - else if( is_rap ) - { - /* This sample is a member of 'rap ' and the previous sample isn't. - * So we create a new group and set appropriate value on its group_description_index. */ - if( isom_rap_grouping_established( group, 1, sgpd, is_fragment ) < 0 ) - return -1; - group->random_access = isom_add_rap_group_entry( sgpd ); - group->prev_assignment = group->assignment; - group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) ); - if( !group->assignment ) - { - lsmash_free( group ); - return -1; - } - } - else /* The previous and current sample aren't a member of 'rap '. */ - ++ group->assignment->sample_count; - /* Obtain the property of the latest random access point group. */ - if( !is_rap && group->random_access ) - { - if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN ) - { - /* We can no longer know num_leading_samples in this group. */ - if( isom_rap_grouping_established( group, 0, sgpd, is_fragment ) < 0 ) - return -1; - } - else - { - if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING - || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING ) - ++ group->random_access->num_leading_samples; - /* no more consecutive leading samples in this group */ - else if( isom_rap_grouping_established( group, 1, sgpd, is_fragment ) < 0 ) - return -1; - } - } - group->is_prev_rap = is_rap; - return 0; -} - -static int isom_roll_grouping_established( isom_roll_group_t *group ) -{ - /* Avoid duplication of sample group descriptions. */ - isom_sgpd_t *sgpd = group->sgpd; - uint32_t group_description_index = group->is_fragment ? 0x10001 : 1; - for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) - { - isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data; - if( !data ) - return -1; - if( group->roll_distance == data->roll_distance ) - { - /* The same description already exists. - * Set the group_description_index corresponding the same description. */ - group->assignment->group_description_index = group_description_index; - return 0; - } - ++group_description_index; - } - /* Add a new roll recovery description. */ - if( !isom_add_roll_group_entry( sgpd, group->roll_distance ) ) - return -1; - group->assignment->group_description_index = sgpd->list->entry_count + (group->is_fragment ? 0x10000 : 0); - return 0; -} - -static int isom_deduplicate_roll_group( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) -{ - /* Deduplication */ - uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1; - isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_get_entry_data( sbgp->list, current_group_number - 1 ); - for( lsmash_entry_t *entry = pool->head; entry; ) - { - isom_roll_group_t *group = (isom_roll_group_t *)entry->data; - if( !group - || !group->assignment ) - return -1; - if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED ) - return 0; - if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index ) - { - /* Merge the current group with the previous. */ - lsmash_entry_t *next_entry = entry->next; - prev_assignment->sample_count += group->assignment->sample_count; - if( lsmash_remove_entry( sbgp->list, current_group_number, NULL ) - || lsmash_remove_entry_direct( pool, entry, NULL ) ) - return -1; - entry = next_entry; - } - else - { - entry = entry->next; - prev_assignment = group->assignment; - ++current_group_number; - } - } - return 0; -} - -/* Remove pooled caches that has become unnecessary. */ -static int isom_clean_roll_pool( lsmash_entry_list_t *pool ) -{ - for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head ) - { - isom_roll_group_t *group = (isom_roll_group_t *)entry->data; - if( !group ) - return -1; - if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED ) - return 0; - if( lsmash_remove_entry_direct( pool, entry, NULL ) ) - return -1; - } - return 0; -} - -static int isom_flush_roll_pool( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) -{ - for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) - { - isom_roll_group_t *group = (isom_roll_group_t *)entry->data; - if( !group ) - return -1; - if( group->delimited - && group->described == ROLL_DISTANCE_DETERMINED - && group->roll_distance != 0 - && isom_roll_grouping_established( group ) < 0 ) - return -1; - } - if( isom_deduplicate_roll_group( sbgp, pool ) < 0 ) - return -1; - return isom_clean_roll_pool( pool ); -} - -static int isom_all_recovery_described( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) -{ - for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) - { - isom_roll_group_t *group = (isom_roll_group_t *)entry->data; - if( !group ) - return -1; - group->described = ROLL_DISTANCE_DETERMINED; - } - return isom_flush_roll_pool( sbgp, pool ); -} - -int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool ) -{ - for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) - { - isom_roll_group_t *group = (isom_roll_group_t *)entry->data; - if( !group ) - return -1; - group->described = ROLL_DISTANCE_DETERMINED; - group->delimited = 1; - } - return isom_flush_roll_pool( sbgp, pool ); -} - -static isom_roll_entry_t *isom_get_roll_description -( - isom_roll_group_t *group -) -{ - uint32_t group_description_index = group->assignment->group_description_index; - if( group_description_index && group->is_fragment ) - { - assert( group_description_index > 0x10000 ); - group_description_index -= 0x10000; - } - return (isom_roll_entry_t *)lsmash_get_entry_data( group->sgpd->list, group_description_index ); -} - -int isom_group_roll_recovery( isom_box_t *parent, lsmash_sample_t *sample ) -{ - if( !parent->file->avc_extensions - && !parent->file->qt_compatible ) - return 0; - isom_cache_t *cache; - isom_sbgp_t *sbgp; - isom_sgpd_t *sgpd; - uint32_t sample_count; - int is_fragment; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) ) - { - isom_trak_t *trak = (isom_trak_t *)parent; - cache = trak->cache; - sbgp = isom_get_sample_to_group ( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_ROLL ); - sgpd = isom_get_sample_group_description( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_ROLL ); - sample_count = isom_get_sample_count( trak ); - is_fragment = 0; - } - else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - { - if( parent->file->max_isom_version < 6 ) - return 0; - isom_traf_t *traf = (isom_traf_t *)parent; - cache = traf->cache; - sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_ROLL ); - sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_ROLL ); - sample_count = cache->fragment->sample_count + 1; - is_fragment = 1; - } - else - { - assert( 0 ); - sbgp = NULL; - sgpd = NULL; - } - if( !sbgp || !sgpd ) - return 0; - lsmash_entry_list_t *pool = cache->roll.pool; - if( !pool ) - { - pool = lsmash_create_entry_list(); - if( !pool ) - return -1; - cache->roll.pool = pool; - } - lsmash_sample_property_t *prop = &sample->prop; - isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count ); - int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags ); - int valid_pre_roll = !is_recovery_start - && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) - && (prop->pre_roll.distance > 0) - && (prop->pre_roll.distance <= -INT16_MIN); - int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start); - if( !new_group ) - { - /* Check pre-roll distance. */ - assert( group->assignment && group->sgpd ); - isom_roll_entry_t *prev_roll = isom_get_roll_description( group ); - if( !prev_roll ) - new_group = valid_pre_roll; - else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) ) - /* Pre-roll distance is different from the previous. */ - new_group = 1; - } - if( new_group ) - { - if( group ) - group->delimited = 1; - else - assert( sample_count == 1 ); - /* Create a new group. */ - group = lsmash_malloc_zero( sizeof(isom_roll_group_t) ); - if( !group ) - return -1; - group->sgpd = sgpd; - group->prev_is_recovery_start = is_recovery_start; - group->is_fragment = is_fragment; - group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); - if( !group->assignment || lsmash_add_entry( pool, group ) ) - { - lsmash_free( group ); - return -1; - } - if( is_recovery_start ) - { - /* a member of non-roll or post-roll group */ - group->first_sample = sample_count; - group->recovery_point = prop->post_roll.complete; - } - else - { - group->described = ROLL_DISTANCE_DETERMINED; - if( valid_pre_roll ) - { - /* a member of pre-roll group */ - group->roll_distance = -prop->pre_roll.distance; - if( isom_roll_grouping_established( group ) < 0 ) - return -1; - } - else - /* a member of non-roll group */ - group->roll_distance = 0; - } - } - else - { - group->prev_is_recovery_start = is_recovery_start; - group->assignment->sample_count += 1; - } - /* If encountered a RAP, all recovery is completed here. */ - if( prop->ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC - | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP - | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) ) - return isom_all_recovery_described( sbgp, pool ); - /* Check whether this sample is a random access recovery point or not. */ - 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 == ROLL_DISTANCE_DETERMINED ) - continue; - if( group->described == ROLL_DISTANCE_INITIALIZED ) - { - isom_roll_entry_t *post_roll = isom_get_roll_description( group ); - if( post_roll && post_roll->roll_distance > 0 ) - { - if( group->rp_cts > sample->cts ) - /* Updated roll_distance for composition reordering. */ - ++ post_roll->roll_distance; - if( ++ group->wait_and_see_count >= MAX_ROLL_WAIT_AND_SEE_COUNT ) - group->described = ROLL_DISTANCE_DETERMINED; - } - } - else if( prop->post_roll.identifier == group->recovery_point ) - { - int16_t distance = sample_count - group->first_sample; - group->rp_cts = sample->cts; - group->roll_distance = distance; - /* 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'. - * The roll_distance may be updated later because of composition reordering. */ - group->described = ROLL_DISTANCE_INITIALIZED; - group->wait_and_see_count = 0; - /* All groups with uninitialized roll_distance 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 || group->described != ROLL_DISTANCE_INITIALIZED ) - continue; - group->described = ROLL_DISTANCE_DETERMINED; - } - /* Cache the CTS of the first recovery point in a subsegment. */ - if( cache->fragment - && cache->fragment->subsegment.first_rp_number == 0 ) - { - cache->fragment->subsegment.first_rp_number = sample_count; - cache->fragment->subsegment.first_rp_cts = sample->cts; - cache->fragment->subsegment.first_ed_cts = sample->cts; - cache->fragment->subsegment.decodable = 1; - } - } - else - /* Random Accessible Point */ - return isom_all_recovery_described( sbgp, pool ); - } - } - return isom_flush_roll_pool( sbgp, pool ); -} - -/* returns 1 if pooled samples must be flushed. */ -/* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk. - see lsmash_append_sample for detail. */ -static int isom_add_chunk( isom_trak_t *trak, lsmash_sample_t *sample ) -{ - if( !trak->file - || !trak->cache - || !trak->mdia->mdhd - || trak->mdia->mdhd->timescale == 0 - || !trak->mdia->minf->stbl->stsc - || !trak->mdia->minf->stbl->stsc->list ) - return -1; - lsmash_file_t *file = trak->file; - isom_chunk_t *current = &trak->cache->chunk; - if( !current->pool ) - { - /* Very initial settings, just once per track */ - current->pool = isom_create_sample_pool( 0 ); - if( !current->pool ) - return -1; - } - if( !current->pool->sample_count ) - { - /* Cannot decide whether we should flush the current sample or not here yet. */ - current->chunk_number += 1; - 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( (file->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale)) - && (file->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 file after a chunk is fully determined with its contents. */ - /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */ - isom_stbl_t *stbl = trak->mdia->minf->stbl; - isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL; - /* Create a new chunk sequence in this track if needed. */ - if( (!last_stsc_data - || current->pool->sample_count != last_stsc_data->samples_per_chunk - || current->sample_description_index != last_stsc_data->sample_description_index) - && isom_add_stsc_entry( stbl, current->chunk_number, - current->pool->sample_count, - current->sample_description_index ) ) - return -1; - /* Add a new chunk offset in this track. */ - uint64_t offset = file->size; - if( file->fragment ) - offset += ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size; - if( isom_add_stco_entry( stbl, offset ) ) - return -1; - /* Update and re-initialize cache, using the current sample */ - current->chunk_number += 1; - 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_file_t *file, isom_sample_pool_t *pool ) -{ - if( !file - || !file->mdat - || !file->bs - || !file->bs->stream ) - return -1; - lsmash_bs_put_bytes( file->bs, pool->size, pool->data ); - if( lsmash_bs_flush_buffer( file->bs ) ) - return -1; - file->mdat->media_size += pool->size; - file->size += pool->size; - pool->sample_count = 0; - pool->size = 0; - return 0; -} - -int isom_update_sample_tables( isom_trak_t *trak, lsmash_sample_t *sample, uint32_t *samples_per_packet ) -{ - isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index ); - if( (audio->manager & LSMASH_AUDIO_DESCRIPTION) - && (audio->manager & LSMASH_QTFF_BASE) - && (audio->version == 1) - && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) ) - { - /* Add entries of the sample table for each uncompressed sample. */ - uint64_t sample_duration = trak->mdia->mdhd->timescale / (audio->samplerate >> 16); - if( audio->samplesPerPacket == 0 || sample_duration == 0 ) - return -1; - uint64_t sample_dts = sample->dts; - uint64_t sample_cts = sample->cts; - for( uint32_t i = 0; i < audio->samplesPerPacket; i++ ) - { - /* Add a size of uncomressed audio and increment sample_count. - * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */ - uint32_t sample_count = isom_add_size( trak, 1 ); - if( sample_count == 0 ) - return -1; - /* Add a decoding timestamp and a composition timestamp. */ - if( isom_add_timestamp( trak, sample_dts, sample_cts ) < 0 ) - return -1; - sample_dts += sample_duration; - sample_cts += sample_duration; - } - *samples_per_packet = audio->samplesPerPacket; - } - else - { - /* Add a sample_size and increment sample_count. */ - uint32_t sample_count = isom_add_size( trak, sample->length ); - if( sample_count == 0 ) - return -1; - /* Add a decoding timestamp and a composition timestamp. */ - if( isom_add_timestamp( trak, sample->dts, sample->cts ) < 0 ) - return -1; - /* Add a sync point if needed. */ - if( isom_add_sync_point( trak, sample_count, &sample->prop ) < 0 ) - return -1; - /* Add a partial sync point if needed. */ - if( isom_add_partial_sync( trak, sample_count, &sample->prop ) < 0 ) - return -1; - /* Add leading, independent, disposable and redundant information if needed. */ - if( isom_add_dependency_type( trak, &sample->prop ) < 0 ) - return -1; - /* Group samples into random access point type if needed. */ - if( isom_group_random_access( (isom_box_t *)trak, sample ) < 0 ) - return -1; - /* Group samples into random access recovery point type if needed. */ - if( isom_group_roll_recovery( (isom_box_t *)trak, sample ) < 0 ) - return -1; - *samples_per_packet = 1; - } - /* Add a chunk if needed. */ - return isom_add_chunk( trak, sample ); -} - -static int isom_output_cached_chunk( isom_trak_t *trak ) -{ - isom_chunk_t *chunk = &trak->cache->chunk; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL; - /* Create a new chunk sequence in this track if needed. */ - if( (!last_stsc_data - || chunk->pool->sample_count != last_stsc_data->samples_per_chunk - || chunk->sample_description_index != last_stsc_data->sample_description_index) - && isom_add_stsc_entry( stbl, chunk->chunk_number, - chunk->pool->sample_count, - chunk->sample_description_index ) ) - return -1; - lsmash_file_t *file = trak->file; - if( file->fragment ) - { - /* Add a new chunk offset in this track. */ - if( isom_add_stco_entry( stbl, file->size + ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size ) ) - return -1; - return isom_append_fragment_track_run( file, chunk ); - } - /* Add a new chunk offset in this track. */ - if( isom_add_stco_entry( stbl, file->size ) ) - return -1; - /* Output pooled samples in this track. */ - return isom_write_pooled_samples( file, chunk->pool ); -} - -int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet ) -{ - uint64_t pool_size = pool->size + sample->length; - if( pool->alloc < pool_size ) - { - uint8_t *data; - uint64_t alloc = pool_size + (1<<16); - if( !pool->data ) - data = lsmash_malloc( alloc ); - else - data = lsmash_realloc( pool->data, alloc ); - if( !data ) - return -1; - pool->data = data; - pool->alloc = alloc; - } - memcpy( pool->data + pool->size, sample->data, sample->length ); - pool->size = pool_size; - pool->sample_count += samples_per_packet; - lsmash_delete_sample( sample ); - return 0; -} - -static int isom_append_sample_internal( isom_trak_t *trak, lsmash_sample_t *sample ) -{ - uint32_t samples_per_packet; - int flush = isom_update_sample_tables( trak, sample, &samples_per_packet ); - if( flush < 0 ) - return -1; - /* flush == 1 means pooled samples must be flushed. */ - lsmash_file_t *file = trak->file; - isom_sample_pool_t *current_pool = trak->cache->chunk.pool; - if( flush == 1 && isom_write_pooled_samples( file, current_pool ) ) - return -1; - /* Arbitration system between tracks with extremely scattering dts. - * Here, we check whether asynchronization between the tracks exceeds the tolerance. - * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed. - * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track. - * Note: you needn't read this loop until you grasp the basic handling of chunks. */ - double tolerance = file->max_async_tolerance; - for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next ) - { - isom_trak_t *other = (isom_trak_t *)entry->data; - if( trak == other ) - continue; - if( !other - || !other->cache - || !other->mdia - || !other->mdia->mdhd - || other->mdia->mdhd->timescale == 0 - || !other->mdia->minf - || !other->mdia->minf->stbl - || !other->mdia->minf->stbl->stsc - || !other->mdia->minf->stbl->stsc->list ) - return -1; - isom_chunk_t *chunk = &other->cache->chunk; - if( !chunk->pool || chunk->pool->sample_count == 0 ) - continue; - double diff = ((double)sample->dts / trak->mdia->mdhd->timescale) - - ((double)chunk->first_dts / other->mdia->mdhd->timescale); - if( diff > tolerance && isom_output_cached_chunk( other ) ) - return -1; - /* Note: we don't flush the cached chunk in the current track and the current sample here - * even if the conditional expression of '-diff > tolerance' meets. - * That's useless because appending a sample to another track would be a good equivalent. - * It's even harmful because it causes excess chunk division by calling - * isom_output_cached_chunk() which always generates a new chunk. - * Anyway some excess chunk division will be there, but rather less without it. - * To completely avoid this, we need to observe at least whether the current sample will be placed - * right next to the previous chunk of the same track or not. */ - } - /* anyway the current sample must be pooled. */ - return isom_pool_sample( current_pool, sample, samples_per_packet ); -} - -/* This function is for non-fragmented movie. */ -static int isom_append_sample( lsmash_file_t *file, uint32_t track_ID, lsmash_sample_t *sample ) -{ - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->file - || !trak->cache - || !trak->mdia - || !trak->mdia->mdhd - || trak->mdia->mdhd->timescale == 0 - || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stsd - || !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( !file->mdat ) - { - if( isom_add_mdat( file ) ) - return -1; - file->mdat->manager |= LSMASH_PLACEHOLDER; - if( isom_write_box( file->bs, (isom_box_t *)file->mdat ) < 0 ) - return -1; - assert( file->free ); - file->size += file->free->size + file->mdat->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 ) ) - { - uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket; - if( sample->length == frame_size ) - return isom_append_sample_internal( trak, sample ); - else if( sample->length < frame_size ) - return -1; - /* Append samples splitted into each LPCMFrame. */ - uint64_t dts = sample->dts; - uint64_t cts = sample->cts; - for( uint32_t offset = 0; offset < sample->length; offset += frame_size ) - { - lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size ); - if( !lpcm_sample ) - return -1; - memcpy( lpcm_sample->data, sample->data + offset, frame_size ); - lpcm_sample->dts = dts++; - lpcm_sample->cts = cts++; - lpcm_sample->prop = sample->prop; - lpcm_sample->index = sample->index; - if( isom_append_sample_internal( trak, lpcm_sample ) ) - { - lsmash_delete_sample( lpcm_sample ); - return -1; - } - } - lsmash_delete_sample( sample ); - return 0; - } - return isom_append_sample_internal( trak, sample ); -} - -static int isom_output_cache( isom_trak_t *trak ) -{ - isom_cache_t *cache = trak->cache; - if( cache->chunk.pool - && cache->chunk.pool->sample_count - && isom_output_cached_chunk( trak ) < 0 ) - return -1; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - for( lsmash_entry_t *entry = stbl->sgpd_list.head; entry; entry = entry->next ) - { - isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data; - if( !sgpd ) - return -1; - switch( sgpd->grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - { - isom_rap_group_t *group = cache->rap; - if( !group ) - { - if( stbl->file->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( !cache->roll.pool ) - { - if( stbl->file->fragment ) - continue; - else - return -1; - } - isom_sbgp_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL ); - if( !sbgp || isom_all_recovery_completed( sbgp, cache->roll.pool ) < 0 ) - return -1; - break; - default : - break; - } - } - return 0; -} - -int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) -{ - if( !root || !root->file ) - return -1; - lsmash_file_t *file = root->file; - if( file->fragment - && file->fragment->movie ) - return isom_flush_fragment_pooled_samples( file, track_ID, last_sample_delta ); - isom_trak_t *trak = isom_get_trak( file, 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 ) < 0 ) - return -1; - return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta ); -} - -int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - /* 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( !file - || !file->bs - || !sample - || !sample->data - || track_ID == 0 - || file->max_chunk_duration == 0 - || file->max_async_tolerance == 0 ) - return -1; - /* Write File Type Box here if it was not written yet. */ - if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) ) - { - if( isom_write_box( file->bs, (isom_box_t *)file->ftyp ) ) - return -1; - file->size += file->ftyp->size; - } - if( file->fragment - && file->fragment->pool ) - return isom_append_fragment_sample( file, track_ID, sample ); - return isom_append_sample( file, track_ID, sample ); -} - -/*---- misc functions ----*/ - -int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root ) - return -1; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak ) - return -1; - isom_remove_box_by_itself( trak->edts ); - return isom_update_tkhd_duration( trak ); -} - -void lsmash_delete_tyrant_chapter( lsmash_root_t *root ) -{ - if( !root - || !root->file - || !root->file->moov - || !root->file->moov->udta ) - return; - isom_remove_box_by_itself( root->file->moov->udta->chpl ); -} - -int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->isom_compatible - || (ISO_language && ISO_language < 0x800) - || !notice ) - return -1; - isom_udta_t *udta; - if( track_ID ) - { - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak || (!trak->udta && isom_add_udta( trak )) ) - return -1; - udta = trak->udta; - } - else - { - if( !file->moov->udta && isom_add_udta( file->moov ) ) - return -1; - udta = file->moov->udta; - } - assert( udta ); - for( lsmash_entry_t *entry = udta->cprt_list.head; entry; entry = entry->next ) - { - isom_cprt_t *cprt = (isom_cprt_t *)entry->data; - if( !cprt || cprt->language == ISO_language ) - return -1; - } - if( isom_add_cprt( udta ) ) - return -1; - isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list.tail->data; - cprt->language = ISO_language; - cprt->notice_length = strlen( notice ) + 1; - cprt->notice = lsmash_memdup( notice, cprt->notice_length ); - return 0; -} diff -Nru l-smash-1.9.1/lsmash.h l-smash-2.3.0/lsmash.h --- l-smash-1.9.1/lsmash.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/lsmash.h 2014-11-06 14:12:27.000000000 +0000 @@ -33,17 +33,51 @@ /**************************************************************************** * Version ****************************************************************************/ -#define LSMASH_VERSION_MAJOR 1 -#define LSMASH_VERSION_MINOR 9 -#define LSMASH_VERSION_MICRO 1 +#define LSMASH_VERSION_MAJOR 2 +#define LSMASH_VERSION_MINOR 3 +#define LSMASH_VERSION_MICRO 0 + +#define LSMASH_VERSION_INT( a, b, c ) (((a) << 16) | ((b) << 8) | (c)) + +#define LIBLSMASH_VERSION_INT LSMASH_VERSION_INT( LSMASH_VERSION_MAJOR, \ + LSMASH_VERSION_MINOR, \ + LSMASH_VERSION_MICRO ) -#define LSMASH_VERSION_INT( a, b, c ) ((a << 16) | (b << 8) | (c)) +/**************************************************************************** + * Error Values + ****************************************************************************/ +enum +{ + LSMASH_ERR_NAMELESS = -1, /* An error but not assigned to any following errors */ + LSMASH_ERR_MEMORY_ALLOC = -2, /* There is not enough room in the heap. */ + LSMASH_ERR_INVALID_DATA = -3, /* Invalid data was found. */ + LSMASH_ERR_FUNCTION_PARAM = -4, /* An error in the parameter list of the function */ + LSMASH_ERR_PATCH_WELCOME = -5, /* Not implemented yet, so patches welcome. */ + LSMASH_ERR_UNKNOWN = -6, /* Unknown error occured. */ +}; /**************************************************************************** * ROOT * The top-level opaque handler for whole file handling. ****************************************************************************/ typedef struct lsmash_root_tag lsmash_root_t; + +/* Allocate a ROOT. + * The allocated ROOT can be deallocate by lsmash_destroy_root(). + * + * Return the address of an allocated ROOT if successful. + * Return NULL otherwise. */ +lsmash_root_t *lsmash_create_root( void ); + +/* Deallocate a given ROOT. */ +void lsmash_destroy_root +( + lsmash_root_t *root /* the address of a ROOT you want to deallocate */ +); + +/**************************************************************************** + * File Layer + ****************************************************************************/ typedef struct lsmash_file_tag lsmash_file_t; typedef enum @@ -203,21 +237,22 @@ * 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. */ /** demuxing only **/ - uint64_t max_read_size; /* max size of reading from a chunk at a time. 4*1024*1024 (4MiB) is default value. */ + uint64_t max_read_size; /* max size of reading from the file at a time. 4*1024*1024 (4MiB) is default value. */ } lsmash_file_parameters_t; -/* Allocate a ROOT. - * The allocated ROOT can be deallocate by lsmash_destroy_root(). - * - * Return the address of an allocated ROOT if successful. - * Return NULL otherwise. */ -lsmash_root_t *lsmash_create_root( void ); +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; /* Open a file where the path is given. * And if successful, set up the parameters by 'open_mode'. * Here, the 'open_mode' parameter is either 0 or 1 as follows: * 0: Create a file for output/muxing operations. - * If a file with the same name already exists, its contents are discarded and the file is treated as a new file. + * If a file with the same name already exists, its contents are discarded and the file is treated as a new file. * If user specifies "-" for 'filename', operations are done on stdout. * The file types or segment types are set up as specified in 'param'. * 1: Open a file for input/demuxing operations. The file must exist. @@ -254,10 +289,7 @@ /* Associate a file with a ROOT and allocate the handle of that file. * The all allocated handles can be deallocated by lsmash_destroy_root(). - * - * Note: - * At present, the added file is only referenced by all tracks of the movie defined in the same file. - * External data references are not implemented yet, but will come in the near future. + * If the ROOT has no associated file yet, the first associated file is activated. * * Return the address of the allocated handle of the added file if successful. * Return NULL otherwise. */ @@ -278,38 +310,38 @@ lsmash_file_parameters_t *param ); -/* Open the movie file to which the path is given, and allocate and set up the ROOT of the file. - * The allocated ROOT can be deallocated by lsmash_destroy_root(). - * - * Users can specify "-" for 'filename'. - * In this case, - * (1) read from stdin when 'mode' contains LSMASH_FILE_MODE_READ, - * or - * (2) write into stdout when 'mode' contains LSMASH_FILE_MODE_WRITE_FRAGMENTED. - * - * Note that 'filename' must be encoded by UTF-8 if 'mode' contains LSMASH_FILE_MODE_WRITE. - * On Windows, lsmash_convert_ansi_to_utf8() may help you. - * - * WARNING: This function is deprecated! - * - * Return the address of an allocated ROOT of the file if successful. - * Return NULL otherwise. */ -lsmash_root_t *lsmash_open_movie +/* Deallocate all boxes within the current active file in a given ROOT. */ +void lsmash_discard_boxes ( - const char *filename, /* the path of a file you want to open. */ - lsmash_file_mode mode /* mode for opening file */ + lsmash_root_t *root /* the address of a ROOT you want to deallocate all boxes within the active file in it */ ); -/* Deallocate a given ROOT. */ -void lsmash_destroy_root +/* Activate a file associated with a ROOT. + * + * Return 0 if successful. + * Return a negative value otherwise. */ +int lsmash_activate_file ( - lsmash_root_t *root /* the address of a ROOT you want to deallocate */ + lsmash_root_t *root, + lsmash_file_t *file ); -/* Deallocate all boxes in a given ROOT. */ -void lsmash_discard_boxes +/* Switch from the current segment file to the following media segment file. + * After switching, the followed segment file can be closed unless that file is an initialization segment. + * + * The first followed segment file must be also an initialization segment. + * The second or later segment files must not be an initialization segment. + * For media segment files flagging LSMASH_FILE_MODE_INDEX, 'remux' must be set. + * + * Users shall call lsmash_flush_pooled_samples() for each track before calling this function. + * + * Return 0 if successful. + * Return a negative value otherwise. */ +int lsmash_switch_media_segment ( - lsmash_root_t *root /* the address of a ROOT you want to deallocate all boxes in it */ + lsmash_root_t *root, + lsmash_file_t *successor, + lsmash_adhoc_remux_t *remux ); /**************************************************************************** @@ -380,8 +412,8 @@ * Return NULL otherwise. */ void *lsmash_memdup ( - void *ptr, /* an address to the source of data to be copied */ - size_t size /* number of bytes to copy */ + const void *ptr, /* an address to the source of data to be copied */ + size_t size /* number of bytes to copy */ ); /* Deallocate a given memory block. @@ -527,6 +559,21 @@ lsmash_box_t *box ); +/* Add a box into 'parent' box as a child box. + * If the adding child box is known and its children (if any) are known, expand them into known + * struct formats for the internal references within the L-SMASH library. + * If this function succeed, the adding child box is deallocated and the address is invalid. + * Instead of that, this function replaces the invalid address with the valid one of the new + * allocated memory block representing the added and expanded child box. + * + * Return 0 if successful. + * Return a negative value otherwise. */ +int lsmash_add_box_ex +( + lsmash_box_t *parent, + lsmash_box_t **box +); + /* Deallocate a given box and its children. */ void lsmash_destroy_box ( @@ -600,7 +647,7 @@ typedef lsmash_box_type_t lsmash_codec_type_t; #define LSMASH_CODEC_TYPE_INITIALIZER LSMASH_BOX_TYPE_INITIALIZER -#define LSMASH_CODEC_TYPE_UNSPECIFIED ((lsmash_codec_type_t)static_lsmash_box_type_unspecified) +#define LSMASH_CODEC_TYPE_UNSPECIFIED LSMASH_BOX_TYPE_UNSPECIFIED #define DEFINE_ISOM_CODEC_TYPE( BOX_TYPE_NAME, BOX_TYPE_FOURCC ) \ static const lsmash_codec_type_t BOX_TYPE_NAME = LSMASH_ISO_BOX_TYPE_INITIALIZER( BOX_TYPE_FOURCC ) @@ -862,8 +909,8 @@ typedef union { void *always_null; /* LSMASH_CODEC_SPECIFIC_FORMAT_UNSPECIFIED */ - void *structured; /* LSMASH_CODEC_SPECIFIC_FORM_STRUCTURED */ - uint8_t *unstructured; /* LSMASH_CODEC_SPECIFIC_FORM_UNSTRUCTURED */ + void *structured; /* LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED */ + uint8_t *unstructured; /* LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED */ } lsmash_codec_specific_data_t; typedef void (*lsmash_codec_specific_destructor_t)( void * ); @@ -888,11 +935,13 @@ LSMASH_CODEC_SUPPORT_FLAG_REMUX = LSMASH_CODEC_SUPPORT_FLAG_MUX | LSMASH_CODEC_SUPPORT_FLAG_DEMUX, } lsmash_codec_support_flag; -#define LSMASH_BASE_SUMMARY \ - lsmash_summary_type summary_type; \ - lsmash_codec_type_t sample_type; \ - lsmash_codec_specific_list_t *opaque; \ - uint32_t max_au_length; /* buffer length for 1 access unit, typically max size of 1 audio/video frame */ +#define LSMASH_BASE_SUMMARY \ + lsmash_summary_type summary_type; \ + lsmash_codec_type_t sample_type; \ + lsmash_codec_specific_list_t *opaque; \ + uint32_t max_au_length; /* buffer length for 1 access unit, \ + * typically max size of 1 audio/video frame */ \ + uint32_t data_ref_index; /* the index of a data reference */ typedef struct { @@ -1027,10 +1076,10 @@ /**************************************************************************** * Audio Description Layer - * NOTE: Currently assuming AAC-LC. ****************************************************************************/ /* Audio Object Types */ -typedef enum { +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 */ @@ -1074,7 +1123,8 @@ } lsmash_mp4a_AudioObjectType; /* See ISO/IEC 14496-3 Signaling of SBR, SBR Signaling and Corresponding Decoder Behavior */ -typedef enum { +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. */ @@ -1084,21 +1134,26 @@ typedef struct { LSMASH_BASE_SUMMARY - // mp4a_audioProfileLevelIndication pli; /* I wonder we should have this or not. */ - lsmash_mp4a_AudioObjectType aot; /* Detailed codec type. If not mp4a, just ignored. */ - uint32_t frequency; /* the audio sampling rate (Hz) - * For some audio, this field is used as a nominal value. - * For instance, even if the stream is HE-AAC v1/SBR, this is base AAC's one. */ - uint32_t channels; /* the number of audio channels - * Even if the stream is HE-AAC v2/SBR+PS, this is base AAC's one. */ - uint32_t sample_size; /* For uncompressed audio, the number of bits in each uncompressed sample for a single channel - * For some compressed audio, such as audio that uses MDCT, this field shall be nonsense and then set to 16. */ - uint32_t samples_in_frame; /* the number of decoded PCM samples in an audio frame at 'frequency'. - * 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. */ - uint32_t bytes_per_frame; /* If constant, this value is set to the number of bytes per audio frame. - * Otherwise set to 0. */ + lsmash_mp4a_AudioObjectType aot; /* detailed codec type + * If neither ISOM_CODEC_TYPE_MP4A_AUDIO nor QT_CODEC_TYPE_MP4A_AUDIO, just ignored. */ + uint32_t frequency; /* the audio sampling rate (in Hz) at the default output playback + * For some audio, this field is used as a nominal value. + * For HE-AAC v1/SBR stream, this is base AAC's one. + * For ISOM_CODEC_TYPE_AC_3_AUDIO and ISOM_CODEC_TYPE_EC_3_AUDIO, this shall be + * equal to the sampling rate (in Hz) of the stream and the media timescale. */ + uint32_t channels; /* the number of audio channels at the default output playback + * Even if the stream is HE-AAC v2/SBR+PS, this is base AAC's one. */ + uint32_t sample_size; /* For uncompressed audio, + * the number of bits in each uncompressed sample for a single channel. + * For some compressed audio, such as audio that uses MDCT, + * N/A (not applicable), and may be set to 16. */ + uint32_t samples_in_frame; /* the number of decoded PCM samples in an audio frame at 'frequency' + * 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. */ + uint32_t bytes_per_frame; /* the number of bytes per audio frame + * If variable, shall be set to 0. */ } lsmash_audio_summary_t; /* Facilitate to make exdata (typically DecoderSpecificInfo or AudioSpecificConfig). */ @@ -1277,7 +1332,6 @@ typedef struct { LSMASH_BASE_SUMMARY - // mp4sys_visualProfileLevelIndication pli; /* I wonder we should have this or not. */ // lsmash_mp4v_VideoObjectType vot; /* Detailed codec type. If not mp4v, just ignored. */ uint32_t timescale; /* media timescale * User can't set this parameter manually. */ @@ -1432,14 +1486,17 @@ typedef struct { - uint8_t allow_earlier; + lsmash_random_access_flag ra_flags; /* random access flags */ + lsmash_post_roll_t post_roll; + lsmash_pre_roll_t pre_roll; + uint8_t allow_earlier; /* only for QuickTime file format */ uint8_t leading; uint8_t independent; uint8_t disposable; uint8_t redundant; - lsmash_random_access_flag ra_flags; - lsmash_post_roll_t post_roll; - lsmash_pre_roll_t pre_roll; + uint8_t reserved[3]; /* non-output + * broken link + * ??? */ } lsmash_sample_property_t; typedef struct @@ -1449,7 +1506,8 @@ uint8_t *data; /* sample data */ uint64_t dts; /* Decoding TimeStamp in units of media timescale */ uint64_t cts; /* Composition TimeStamp in units of media timescale */ - uint32_t index; + uint64_t pos; /* absolute file offset of sample data (read-only) */ + uint32_t index; /* index of sample description */ lsmash_sample_property_t prop; } lsmash_sample_t; @@ -1670,6 +1728,13 @@ PRIVATE char data_handler_name_shadow[256]; } lsmash_media_parameters_t; +typedef struct +{ + uint32_t index; /* the index of a data reference */ + char *location; /* URL; location of referenced media file */ + /* Probably, additional string fields such as thing to indicate URN will be added in the future. */ +} lsmash_data_reference_t; + /* Set all the given media parameters to default. */ void lsmash_initialize_media_parameters ( @@ -1792,6 +1857,70 @@ char *iso_language /* a string of ISO 639-2/T language code */ ); +/* Count the number of data references in a track. + * + * Return the number of data references in a track if no error. + * Return 0 otherwise. */ +uint32_t lsmash_count_data_reference +( + lsmash_root_t *root, + uint32_t track_ID +); + +/* Get the location of a data reference in a track by specifying the index in 'data_ref'. + * The string fields in 'data_ref' may be allocated if referencing external media data. + * If referencing self-contained media data, the all string fields are set to NULL. + * You can deallocate the allocated fields by lsmash_free(). + * Also you can deallocate all of the allocated fields by lsmash_cleanup_data_reference() at a time. + * + * Return 0 if successful. + * Return a negative value otherwise. */ +int lsmash_get_data_reference +( + lsmash_root_t *root, + uint32_t track_ID, + lsmash_data_reference_t *data_ref +); + +/* Deallocate all of allocated fields in a given data reference at a time. + * The deallocated fields are set to NULL. */ +void lsmash_cleanup_data_reference +( + lsmash_data_reference_t *data_ref +); + +/* Create a data reference in a track and specify its location on playback for writing. + * If no settings for data references in a track, the location of the first data reference is specified to + * the location of the same file implicitly. + * Note that referenced files shall be used as a media, i.e. LSMASH_FILE_MODE_MEDIA shall be set to the 'mode' + * in the lsmash_file_parameters_t before calling lsmash_set_file(). + * + * As restrictions of the libary, + * WARNING1: The box structured media files cannot be used as a reference data yet. + * WARNING2: The external media files cannot be used as a reference data for movie fragments yet. + * + * Return 0 if successful. + * Return a negative value otherwise. */ +int lsmash_create_data_reference +( + lsmash_root_t *root, + uint32_t track_ID, + lsmash_data_reference_t *data_ref, + lsmash_file_t *file +); + +/* Assign a data reference in a track to a read file. + * + * Return 0 if successful. + * Return a negative value otherwise. */ +int lsmash_assign_data_reference +( + lsmash_root_t *root, + uint32_t track_ID, + uint32_t data_ref_index, + lsmash_file_t *file +); + /**************************************************************************** * Track Layer ****************************************************************************/ @@ -2034,16 +2163,6 @@ ****************************************************************************/ typedef struct { - lsmash_brand_type major_brand; /* deprecated: the best used brand */ - lsmash_brand_type *brands; /* deprecated: the list of compatible brands */ - uint32_t number_of_brands; /* deprecated: the number of compatible brands used in the movie */ - uint32_t minor_version; /* deprecated: minor version of best used brand */ - double max_chunk_duration; /* deprecated: max duration per chunk in seconds. 0.5 is default value. */ - double max_async_tolerance; /* deprecated: - * 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; /* deprecated: max size per chunk in bytes. 4*1024*1024 (4MiB) is default value. */ - uint64_t max_read_size; /* deprecated: 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. */ @@ -2055,17 +2174,8 @@ int32_t preview_time; /* the time value in the movie at which the preview begins */ int32_t preview_duration; /* the duration of the movie preview in movie timescale units */ int32_t poster_time; /* the time value of the time of the movie poster */ - /* Any user shouldn't use the following parameter. */ - PRIVATE lsmash_brand_type brands_shadow[50]; } lsmash_movie_parameters_t; -typedef 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; - /* Set all the given movie parameters to default. */ void lsmash_initialize_movie_parameters ( @@ -2158,6 +2268,25 @@ lsmash_root_t *root ); +/* Count chapters in the chapter list (moov.udta.chpl). */ +uint32_t lsmash_count_tyrant_chapter +( + lsmash_root_t *root +); + +/* Retrieve a chapter entry from the chapter list (moov.udta.chpl). + * Returned pointer is owned by the ROOT structure, so user shall not + * modify or free it. + * + * Return chapter title string if successful, otherwise NULL. + */ +char *lsmash_get_tyrant_chapter +( + lsmash_root_t *root, + uint32_t index, /* index of chapter ( >= 1) */ + double *timestamp /* timestamp of the chapter entry (in seconds) */ +); + /**************************************************************************** * Fragments ****************************************************************************/ @@ -2470,7 +2599,12 @@ /**************************************************************************** * Tools for creating CODEC Specific Information Extensions (Magic Cookies) ****************************************************************************/ -/** MPEG-4 stream tools **/ +/* MPEG-4 Systems Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_MP4A_AUDIO + * QT_CODEC_TYPE_MP4A_AUDIO + * ISOM_CODEC_TYPE_MP4V_AUDIO + * ISOM_CODEC_TYPE_MP4S_AUDIO */ /* objectTypeIndication */ typedef enum { @@ -2562,9 +2696,9 @@ MP4SYS_STREAM_TYPE_StreamingText = 0x0D, /* StreamingText */ } lsmash_mp4sys_stream_type; -/* Decoder Specific Information - * an opaque container with information for a specific media decoder - * The existence and semantics of decoder specific information depends on the values of streamType and objectTypeIndication. */ +/* MPEG-4 Systems Decoder Specific Information + * an opaque container with information for a specific media decoder + * The existence and semantics of decoder specific information depends on the values of streamType and objectTypeIndication. */ typedef struct lsmash_mp4sys_decoder_specific_info_tag lsmash_mp4sys_decoder_specific_info_t; /* Note: bufferSizeDB, maxBitrate and avgBitrate are calculated internally when calling lsmash_finish_movie(). @@ -2614,7 +2748,13 @@ uint32_t *payload_length ); -/* AC-3 tools to make exdata (AC-3 specific info). */ +/* AC-3 Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_AC_3_AUDIO + * + * Unlike MPEG-4 Audio formats, the decoder does not require this for the initialization. + * Each AC-3 sample is self-contained. + * Users shall set the actual sample rate to 'frequency', which is a member of lsmash_audio_summary_t. */ typedef struct { uint8_t fscod; /* the same value as the fscod field in the AC-3 bitstream */ @@ -2638,11 +2778,17 @@ uint32_t *data_length ); -/* Enhanced AC-3 tools to make exdata (Enhanced AC-3 specific info). */ +/* Enhanced AC-3 Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_EC_3_AUDIO + * + * Unlike MPEG-4 Audio formats, the decoder does not require this for the initialization. + * Each Enhanced AC-3 sample is self-contained. + * Note that this cannot document reduced sample rates (24000, 22050 or 16000 Hz). + * Therefore, users shall set the actual sample rate to 'frequency', which is a member of lsmash_audio_summary_t. */ typedef struct { uint8_t fscod; /* the same value as the fscod field in the independent substream */ - uint8_t fscod2; /* Any user must not use this. */ uint8_t bsid; /* the same value as the bsid field in the independent substream */ uint8_t bsmod; /* the same value as the bsmod field in the independent substream * If the bsmod field is not present in the independent substream, this field shall be set to 0. */ @@ -2681,7 +2827,15 @@ uint32_t *data_length ); -/* DTS audio tools to make exdata (DTS specific info). */ +/* DTS Audio Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_DTSC_AUDIO + * ISOM_CODEC_TYPE_DTSH_AUDIO + * ISOM_CODEC_TYPE_DTSL_AUDIO + * ISOM_CODEC_TYPE_DTSE_AUDIO + * + * Unlike MPEG-4 Audio formats, the decoder does not require this for the initialization. + * Each DTS Audio sample is self-contained. */ typedef enum { DTS_CORE_SUBSTREAM_CORE_FLAG = 0x00000001, @@ -2783,7 +2937,10 @@ lsmash_dts_specific_parameters_t *param ); -/* Apple Lossless Audio tools */ +/* Apple Lossless Audio Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_ALAC_AUDIO + * QT_CODEC_TYPE_ALAC_AUDIO */ typedef struct { uint32_t frameLength; /* the frames per packet when no explicit frames per packet setting is present in the packet header @@ -2806,10 +2963,18 @@ uint32_t *data_length ); -/* MPEG-4 Bitrate information. - * Though you need not to set these fields manually since lsmash_finish_movie() calls the function - * that calculates these values internally, these fields are optional. - * Therefore, if you want to add this info, append this as an extension via LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE at least. */ +/* MPEG-4 Bitrate Information. + * Optional : + * ISOM_CODEC_TYPE_AVC1_VIDEO + * ISOM_CODEC_TYPE_AVC2_VIDEO + * ISOM_CODEC_TYPE_AVC3_VIDEO + * ISOM_CODEC_TYPE_AVC4_VIDEO + * ISOM_CODEC_TYPE_HVC1_VIDEO + * ISOM_CODEC_TYPE_HEV1_VIDEO + * + * Though you need not to set these fields manually since lsmash_finish_movie() calls the function + * that calculates these values internally, these fields are optional. + * Therefore, if you want to add this info, append this as an extension via LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE at least. */ typedef struct { uint32_t bufferSizeDB; /* the size of the decoding buffer for the elementary stream in bytes */ @@ -2827,9 +2992,15 @@ DCR_NALU_APPEND_POSSIBLE = 1, /* It is possible to append the NAL unit into the Decoder Configuration Record. */ } lsmash_dcr_nalu_appendable; -/* H.264 tools to make exdata (AVC specific info). - * All members in lsmash_h264_specific_parameters_t except for lengthSizeMinusOne shall be automatically set up - * when appending SPS NAL units by calling lsmash_append_h264_parameter_set(). */ +/* H.264/AVC Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_AVC1_VIDEO + * ISOM_CODEC_TYPE_AVC2_VIDEO + * ISOM_CODEC_TYPE_AVC3_VIDEO + * ISOM_CODEC_TYPE_AVC4_VIDEO + * + * All members in lsmash_h264_specific_parameters_t except for lengthSizeMinusOne shall be automatically set up + * when appending SPS NAL units by calling lsmash_append_h264_parameter_set(). */ typedef enum { H264_PARAMETER_SET_TYPE_SPS = 0, /* SPS (Sequence Parameter Set) */ @@ -2904,11 +3075,15 @@ uint32_t *data_length ); -/* HEVC tools to make exdata (HEVC specific info). - * All members in lsmash_hevc_specific_parameters_t except for avgFrameRate and lengthSizeMinusOne shall be - * automatically set up when appending VPS and SPS NAL units by calling lsmash_append_hevc_dcr_nalu(). - * It is recommended that you should append VPS, SPS and PPS in this order so that a parameter set can reference - * another parameter set. */ +/* H.265/HEVC Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_HVC1_VIDEO + * ISOM_CODEC_TYPE_HEV1_VIDEO + * + * All members in lsmash_hevc_specific_parameters_t except for avgFrameRate and lengthSizeMinusOne shall be + * automatically set up when appending VPS and SPS NAL units by calling lsmash_append_hevc_dcr_nalu(). + * It is recommended that you should append VPS, SPS and PPS in this order so that a parameter set can reference + * another parameter set. */ typedef enum { /* Parameter sets @@ -3052,7 +3227,11 @@ uint32_t *data_length ); -/* VC-1 tools to make exdata (VC-1 specific info). */ +/* VC-1 Specific Information + * Mandatory : + * ISOM_CODEC_TYPE_VC_1_VIDEO + * + * We support only advanced profile at present. */ typedef struct lsmash_vc1_header_tag lsmash_vc1_header_t; typedef struct @@ -3298,25 +3477,25 @@ * When audio stream has 3 or more number of channels, this extension shall be present. */ 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_UNKNOWN = (signed)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_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_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_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, @@ -3327,14 +3506,14 @@ 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_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_CENTER_SURROUND_DIRECT = 44, /* back center, non diffuse */ QT_CHANNEL_LABEL_HAPTIC = 45, @@ -3611,7 +3790,7 @@ QT_CHANNEL_LAYOUT_ALAC_7_1 = QT_CHANNEL_LAYOUT_MPEG_7_1_B, /* C Lc Rc L R Ls Rs 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 */ + QT_CHANNEL_LAYOUT_UNKNOWN = (signed)0xffff0000, /* needs to be ORed with the actual number of channels */ } lsmash_channel_layout_tag; typedef struct diff -Nru l-smash-1.9.1/Makefile l-smash-2.3.0/Makefile --- l-smash-1.9.1/Makefile 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/Makefile 2014-11-06 14:12:27.000000000 +0000 @@ -28,6 +28,9 @@ $(SHAREDLIBNAME): $(OBJS) $(LD) $(SO_LDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) -@ $(if $(STRIP), $(STRIP) -x $@) +ifeq ($(SHAREDLIBNAME), liblsmash.so.$(MAJVER)) + ln -s $(SHAREDLIBNAME) liblsmash.so +endif # $(TOOLS) is automatically generated as config.mak2 by configure. # The reason for having config.mak2 is for making this Makefile easy to read. @@ -46,7 +49,7 @@ install -d $(DESTDIR)$(libdir)/pkgconfig install -m 644 liblsmash.pc $(DESTDIR)$(libdir)/pkgconfig ifneq ($(STATICLIB),) - install -m644 $(STATICLIB) $(DESTDIR)$(libdir) + install -m 644 $(STATICLIB) $(DESTDIR)$(libdir) endif ifneq ($(SHAREDLIB),) ifneq ($(IMPLIB),) @@ -54,18 +57,21 @@ install -d $(DESTDIR)$(bindir) install -m 755 $(SHAREDLIB) $(DESTDIR)$(bindir) else - install -m 755 $(SHAREDLIB) $(DESTDIR)$(libdir) + install -m 644 $(SHAREDLIB) $(DESTDIR)$(libdir) +ifeq ($(SHAREDLIB), liblsmash.so.$(MAJVER)) + ln -s $(SHAREDLIB) $(DESTDIR)$(libdir)/liblsmash.so +endif endif endif #All objects should be deleted regardless of configure when uninstall/clean/distclean. uninstall: $(RM) $(DESTDIR)$(includedir)/lsmash.h - $(RM) $(addprefix $(DESTDIR)$(libdir)/, liblsmash.a liblsmash.dll.a liblsmash.so liblsmash.dylib pkgconfig/liblsmash.pc) + $(RM) $(addprefix $(DESTDIR)$(libdir)/, liblsmash.a liblsmash.dll.a liblsmash.so* liblsmash.dylib pkgconfig/liblsmash.pc) $(RM) $(addprefix $(DESTDIR)$(bindir)/, $(TOOLS_ALL) $(TOOLS_ALL:%=%.exe) liblsmash.dll cyglsmash.dll) clean: - $(RM) *.o *.a *.so *.dll *.dylib *.exe $(TOOLS_ALL) .depend + $(RM) */*.o *.a *.so* *.dll *.dylib $(addprefix cli/, *.exe $(TOOLS_ALL)) .depend distclean: clean $(RM) config.* *.pc diff -Nru l-smash-1.9.1/meta.c l-smash-2.3.0/meta.c --- l-smash-1.9.1/meta.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/meta.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,498 +0,0 @@ -/***************************************************************************** - * meta.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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" - -#include -#include - -#include "box.h" - -static isom_data_t *isom_add_metadata( lsmash_file_t *file, - lsmash_itunes_metadata_item item, - char *meaning_string, char *name_string ) -{ - assert( file && file->moov ); - if( ((item == ITUNES_METADATA_ITEM_CUSTOM) && (!meaning_string || !meaning_string[0]) ) - || (!file->moov->udta && isom_add_udta( file->moov )) - || (!file->moov->udta->meta && isom_add_meta( file->moov->udta )) - || (!file->moov->udta->meta->ilst && isom_add_ilst( file->moov->udta->meta )) ) - return NULL; - if( !file->moov->udta->meta->hdlr ) - { - if( isom_add_hdlr( file->moov->udta->meta ) - || isom_setup_handler_reference( file->moov->udta->meta->hdlr, ISOM_META_HANDLER_TYPE_ITUNES_METADATA ) ) - return NULL; - } - isom_ilst_t *ilst = file->moov->udta->meta->ilst; - if( isom_add_metaitem( ilst, item ) ) - return NULL; - isom_metaitem_t *metaitem = (isom_metaitem_t *)ilst->item_list.tail->data; - if( item == ITUNES_METADATA_ITEM_CUSTOM ) - { - if( isom_add_mean( metaitem ) ) - goto fail; - isom_mean_t *mean = metaitem->mean; - mean->meaning_string_length = strlen( meaning_string ); /* No null terminator */ - mean->meaning_string = lsmash_memdup( meaning_string, mean->meaning_string_length ); - if( !mean->meaning_string ) - goto fail; - if( name_string && name_string[0] ) - { - if( isom_add_name( metaitem ) ) - goto fail; - isom_name_t *name = metaitem->name; - name->name_length = strlen( name_string ); /* No null terminator */ - name->name = lsmash_memdup( name_string, name->name_length ); - if( !name->name ) - goto fail; - } - } - if( isom_add_data( metaitem ) ) - goto fail; - return metaitem->data; -fail: - isom_remove_box_by_itself( metaitem ); - return NULL; -} - -static int isom_set_itunes_metadata_string( lsmash_file_t *file, - lsmash_itunes_metadata_item item, - lsmash_itunes_metadata_value_t value, char *meaning, char *name ) -{ - uint32_t value_length = strlen( value.string ); - if( item == ITUNES_METADATA_ITEM_DESCRIPTION && value_length > 255 ) - item = ITUNES_METADATA_ITEM_LONG_DESCRIPTION; - isom_data_t *data = isom_add_metadata( file, item, meaning, name ); - if( !data ) - return -1; - data->type_code = ITUNES_METADATA_SUBTYPE_UTF8; - data->value_length = value_length; /* No null terminator */ - data->value = lsmash_memdup( value.string, data->value_length ); - if( !data->value ) - { - isom_ilst_t *ilst = file->moov->udta->meta->ilst; - isom_remove_box_by_itself( ilst->item_list.tail->data ); - return -1; - } - return 0; -} - -static int isom_set_itunes_metadata_integer( lsmash_file_t *file, - lsmash_itunes_metadata_item item, - lsmash_itunes_metadata_value_t value, char *meaning, char *name ) -{ - static const struct - { - lsmash_itunes_metadata_item item; - int length; - } metadata_code_type_table[] = - { - { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, 1 }, - { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, 2 }, - { ITUNES_METADATA_ITEM_CONTENT_RATING, 1 }, - { ITUNES_METADATA_ITEM_MEDIA_TYPE, 1 }, - { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, 2 }, - { ITUNES_METADATA_ITEM_TV_EPISODE, 4 }, - { ITUNES_METADATA_ITEM_TV_SEASON, 4 }, - { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, 1 }, - { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, 4 }, - { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, 4 }, - { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, 4 }, - { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, 4 }, - { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, 8 }, - { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, 4 }, - { ITUNES_METADATA_ITEM_CUSTOM, 8 }, - { 0, 0 } - }; - int i; - for( i = 0; metadata_code_type_table[i].item; i++ ) - if( item == metadata_code_type_table[i].item ) - break; - if( metadata_code_type_table[i].length == 0 ) - return -1; - isom_data_t *data = isom_add_metadata( file, item, meaning, name ); - if( !data ) - return -1; - if( item == ITUNES_METADATA_ITEM_PREDEFINED_GENRE ) - data->type_code = ITUNES_METADATA_SUBTYPE_IMPLICIT; - else - data->type_code = ITUNES_METADATA_SUBTYPE_INTEGER; - data->value_length = metadata_code_type_table[i].length; - uint8_t temp[8]; - for( i = 0; i < data->value_length; i++ ) - { - int shift = (data->value_length - i - 1) * 8; - temp[i] = (value.integer >> shift) & 0xff; - } - data->value = lsmash_memdup( temp, data->value_length ); - if( !data->value ) - { - isom_ilst_t *ilst = file->moov->udta->meta->ilst; - isom_remove_box_by_itself( ilst->item_list.tail->data ); - return -1; - } - return 0; -} - -static int isom_set_itunes_metadata_boolean( lsmash_file_t *file, - lsmash_itunes_metadata_item item, - lsmash_itunes_metadata_value_t value, char *meaning, char *name ) -{ - isom_data_t *data = isom_add_metadata( file, item, meaning, name ); - if( !data ) - return -1; - data->type_code = ITUNES_METADATA_SUBTYPE_INTEGER; - data->value_length = 1; - uint8_t temp = (uint8_t)value.boolean; - data->value = lsmash_memdup( &temp, 1 ); - if( !data->value ) - { - isom_ilst_t *ilst = file->moov->udta->meta->ilst; - isom_remove_box_by_itself( ilst->item_list.tail->data ); - return -1; - } - return 0; -} - -static int isom_set_itunes_metadata_binary( lsmash_file_t *file, - lsmash_itunes_metadata_item item, - lsmash_itunes_metadata_value_t value, char *meaning, char *name ) -{ - isom_data_t *data = isom_add_metadata( file, item, meaning, name ); - if( !data ) - return -1; - switch( item ) - { - case ITUNES_METADATA_ITEM_COVER_ART : - if( value.binary.subtype != ITUNES_METADATA_SUBTYPE_JPEG - && value.binary.subtype != ITUNES_METADATA_SUBTYPE_PNG - && value.binary.subtype != ITUNES_METADATA_SUBTYPE_BMP ) - return -1; - break; - case ITUNES_METADATA_ITEM_DISC_NUMBER : - case ITUNES_METADATA_ITEM_TRACK_NUMBER : - value.binary.subtype = ITUNES_METADATA_SUBTYPE_IMPLICIT; - break; - default : - break; - } - switch( value.binary.subtype ) - { - case ITUNES_METADATA_SUBTYPE_UUID : - if( value.binary.size != 16 ) - return -1; - break; - case ITUNES_METADATA_SUBTYPE_DURATION : - if( value.binary.size != 4 ) - return -1; - break; - case ITUNES_METADATA_SUBTYPE_TIME : - if( value.binary.size != 4 && value.binary.size != 8 ) - return -1; - break; - case ITUNES_METADATA_SUBTYPE_INTEGER : - if( value.binary.size != 1 && value.binary.size != 2 - && value.binary.size != 3 && value.binary.size != 4 - && value.binary.size != 8 ) - return -1; - break; - case ITUNES_METADATA_SUBTYPE_RIAAPA : - if( value.binary.size != 1 ) - return -1; - break; - default : - break; - } - data->type_code = value.binary.subtype; - data->value_length = value.binary.size; - data->value = lsmash_memdup( value.binary.data, value.binary.size ); - if( !data->value ) - { - isom_ilst_t *ilst = file->moov->udta->meta->ilst; - isom_remove_box_by_itself( ilst->item_list.tail->data ); - return -1; - } - return 0; -} - -int lsmash_set_itunes_metadata( lsmash_root_t *root, lsmash_itunes_metadata_t metadata ) -{ - if( !root || !root->file ) - return -1; - static const struct - { - lsmash_itunes_metadata_item item; - int (*func_set_itunes_metadata)( lsmash_file_t *, lsmash_itunes_metadata_item, lsmash_itunes_metadata_value_t, char *, char * ); - } itunes_metadata_function_mapping[] = - { - { ITUNES_METADATA_ITEM_ALBUM_NAME, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ARTIST, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_USER_COMMENT, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_RELEASE_DATE, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ENCODED_BY, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_USER_GENRE, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_GROUPING, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_LYRICS, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_TITLE, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_TRACK_SUBTITLE, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ENCODING_TOOL, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_COMPOSER, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ALBUM_ARTIST, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_PODCAST_CATEGORY, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_COPYRIGHT, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_DESCRIPTION, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_GROUPING_DRAFT, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_PODCAST_KEYWORD, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_LONG_DESCRIPTION, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_PURCHASE_DATE, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_TV_EPISODE_ID, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_TV_NETWORK, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_TV_SHOW_NAME, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_PURCHASE_ACCOUNT_ID, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ARTIST, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM_ARTIST, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_COMPOSER, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_NAME, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_SHOW, isom_set_itunes_metadata_string }, - { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_CONTENT_RATING, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_MEDIA_TYPE, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_TV_EPISODE, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_TV_SEASON, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, isom_set_itunes_metadata_integer }, - { ITUNES_METADATA_ITEM_DISC_COMPILATION, isom_set_itunes_metadata_boolean }, - { ITUNES_METADATA_ITEM_HIGH_DEFINITION_VIDEO, isom_set_itunes_metadata_boolean }, - { ITUNES_METADATA_ITEM_PODCAST, isom_set_itunes_metadata_boolean }, - { ITUNES_METADATA_ITEM_GAPLESS_PLAYBACK, isom_set_itunes_metadata_boolean }, - { ITUNES_METADATA_ITEM_COVER_ART, isom_set_itunes_metadata_binary }, - { ITUNES_METADATA_ITEM_DISC_NUMBER, isom_set_itunes_metadata_binary }, - { ITUNES_METADATA_ITEM_TRACK_NUMBER, isom_set_itunes_metadata_binary }, - { 0, NULL } - }; - lsmash_file_t *file = root->file; - for( int i = 0; itunes_metadata_function_mapping[i].func_set_itunes_metadata; i++ ) - if( metadata.item == itunes_metadata_function_mapping[i].item ) - return itunes_metadata_function_mapping[i].func_set_itunes_metadata( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); - if( metadata.item == ITUNES_METADATA_ITEM_CUSTOM ) - switch( metadata.type ) - { - case ITUNES_METADATA_TYPE_STRING : - return isom_set_itunes_metadata_string( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); - case ITUNES_METADATA_TYPE_INTEGER : - return isom_set_itunes_metadata_integer( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); - case ITUNES_METADATA_TYPE_BOOLEAN : - return isom_set_itunes_metadata_boolean( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); - case ITUNES_METADATA_TYPE_BINARY : - return isom_set_itunes_metadata_binary( file, metadata.item, metadata.value, metadata.meaning, metadata.name ); - default : - break; - } - return -1; -} - -static lsmash_itunes_metadata_type isom_get_itunes_metadata_type( lsmash_itunes_metadata_item item ) -{ - static const struct - { - lsmash_itunes_metadata_item item; - lsmash_itunes_metadata_type type; - } itunes_metadata_item_type_mapping[] = - { - { ITUNES_METADATA_ITEM_ALBUM_NAME, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ARTIST, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_USER_COMMENT, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_RELEASE_DATE, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ENCODED_BY, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_USER_GENRE, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_GROUPING, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_LYRICS, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_TITLE, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_TRACK_SUBTITLE, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ENCODING_TOOL, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_COMPOSER, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ALBUM_ARTIST, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_PODCAST_CATEGORY, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_COPYRIGHT, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_DESCRIPTION, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_GROUPING_DRAFT, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_PODCAST_KEYWORD, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_LONG_DESCRIPTION, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_PURCHASE_DATE, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_TV_EPISODE_ID, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_TV_NETWORK, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_TV_SHOW_NAME, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_PURCHASE_ACCOUNT_ID, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ARTIST, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM_ARTIST, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_COMPOSER, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_NAME, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_SHOW, ITUNES_METADATA_TYPE_STRING }, - { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_CONTENT_RATING, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_MEDIA_TYPE, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_TV_EPISODE, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_TV_SEASON, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, ITUNES_METADATA_TYPE_INTEGER }, - { ITUNES_METADATA_ITEM_DISC_COMPILATION, ITUNES_METADATA_TYPE_BOOLEAN }, - { ITUNES_METADATA_ITEM_HIGH_DEFINITION_VIDEO, ITUNES_METADATA_TYPE_BOOLEAN }, - { ITUNES_METADATA_ITEM_PODCAST, ITUNES_METADATA_TYPE_BOOLEAN }, - { ITUNES_METADATA_ITEM_GAPLESS_PLAYBACK, ITUNES_METADATA_TYPE_BOOLEAN }, - { ITUNES_METADATA_ITEM_COVER_ART, ITUNES_METADATA_TYPE_BINARY }, - { ITUNES_METADATA_ITEM_DISC_NUMBER, ITUNES_METADATA_TYPE_BINARY }, - { ITUNES_METADATA_ITEM_TRACK_NUMBER, ITUNES_METADATA_TYPE_BINARY }, - { 0, ITUNES_METADATA_TYPE_NONE } - }; - for( int i = 0; itunes_metadata_item_type_mapping[i].type != ITUNES_METADATA_TYPE_NONE; i++ ) - if( item == itunes_metadata_item_type_mapping[i].item ) - return itunes_metadata_item_type_mapping[i].type; - return ITUNES_METADATA_TYPE_NONE; -} - -int lsmash_get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata ) -{ - if( !metadata - || !root - || !root->file - || !root->file->moov - || !root->file->moov->udta - || !root->file->moov->udta->meta - || !root->file->moov->udta->meta->ilst ) - return -1; - isom_ilst_t *ilst = root->file->moov->udta->meta->ilst; - isom_metaitem_t *metaitem = (isom_metaitem_t *)lsmash_get_entry_data( &ilst->item_list, metadata_number ); - if( !metaitem || !metaitem->data || !metaitem->data->value || metaitem->data->value_length == 0 ) - return -1; - /* Get 'item'. */ - metadata->item = metaitem->type.fourcc; - /* Get 'type'. */ - metadata->type = isom_get_itunes_metadata_type( metadata->item ); - /* Get 'meaning'. */ - isom_mean_t *mean = metaitem->mean; - if( mean ) - { - uint8_t *temp = lsmash_malloc( mean->meaning_string_length + 1 ); - if( !temp ) - goto fail; - memcpy( temp, mean->meaning_string, mean->meaning_string_length ); - temp[ mean->meaning_string_length ] = 0; - metadata->meaning = (char *)temp; - } - else - metadata->meaning = NULL; - /* Get 'name'. */ - isom_name_t *name = metaitem->name; - if( name ) - { - uint8_t *temp = lsmash_malloc( name->name_length + 1 ); - if( !temp ) - goto fail; - memcpy( temp, name->name, name->name_length ); - temp[ name->name_length ] = 0; - metadata->name = (char *)temp; - } - else - metadata->name = NULL; - /* Get 'value'. */ - isom_data_t *data = metaitem->data; - switch( metadata->type ) - { - case ITUNES_METADATA_TYPE_STRING : - { - uint8_t *temp = lsmash_malloc( data->value_length + 1 ); - if( !temp ) - goto fail; - memcpy( temp, data->value, data->value_length ); - temp[ data->value_length ] = 0; - metadata->value.string = (char *)temp; - break; - } - case ITUNES_METADATA_TYPE_INTEGER : - if( data->value_length > 8 ) - return -1; - metadata->value.integer = 0; - for( uint32_t i = 0; i < data->value_length; i++ ) - { - int shift = (data->value_length - i - 1) * 8; - metadata->value.integer |= (uint64_t)data->value[i] << shift; - } - break; - case ITUNES_METADATA_TYPE_BOOLEAN : - metadata->value.boolean = !!data->value[0]; - break; - default : - metadata->type = ITUNES_METADATA_TYPE_BINARY; - metadata->value.binary.subtype = data->type_code; - metadata->value.binary.size = data->value_length; - metadata->value.binary.data = lsmash_memdup( data->value, data->value_length ); - if( !metadata->value.binary.data ) - goto fail; - break; - } - return 0; -fail: - lsmash_freep( &metadata->meaning ); - lsmash_freep( &metadata->name ); - return -1; -} - -uint32_t lsmash_count_itunes_metadata( lsmash_root_t *root ) -{ - if( !root - || !root->file - || !root->file->moov - || !root->file->moov->udta - || !root->file->moov->udta->meta - || !root->file->moov->udta->meta->ilst ) - return 0; - return root->file->moov->udta->meta->ilst->item_list.entry_count; -} - -void lsmash_cleanup_itunes_metadata( lsmash_itunes_metadata_t *metadata ) -{ - if( !metadata ) - return; - lsmash_freep( &metadata->meaning ); - lsmash_freep( &metadata->name ); - if( metadata->type == ITUNES_METADATA_TYPE_STRING ) - lsmash_freep( &metadata->value.string ); - else if( metadata->type == ITUNES_METADATA_TYPE_BINARY ) - lsmash_freep( &metadata->value.binary.data ); -} diff -Nru l-smash-1.9.1/mp4a.c l-smash-2.3.0/mp4a.c --- l-smash-1.9.1/mp4a.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/mp4a.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,936 +0,0 @@ -/***************************************************************************** - * mp4a.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 "box.h" -#include "description.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_sampling_frequency_table[13][5] = { - /* threshold, exact, idx_for_ga, idx_for_sbr, idx */ - { 92017, 96000, 0x0, 0xF, 0x0 }, /* SBR is not allowed */ - { 75132, 88200, 0x1, 0xF, 0x1 }, /* SBR is not allowed */ - { 55426, 64000, 0x2, 0xF, 0x2 }, /* SBR is not allowed */ - { 46009, 48000, 0x3, 0x0, 0x3 }, - { 37566, 44100, 0x4, 0x1, 0x4 }, - { 27713, 32000, 0x5, 0x2, 0x5 }, - { 23004, 24000, 0x6, 0x3, 0x6 }, - { 18783, 22050, 0x7, 0x4, 0x7 }, - { 13856, 16000, 0x8, 0x5, 0x8 }, - { 11502, 12000, 0x9, 0x6, 0x9 }, - { 9391, 11025, 0xA, 0x7, 0xA }, - { 8000, 8000, 0xB, 0x8, 0xB }, - { 0, 7350, 0xB, 0xF, 0xC } /* samplingFrequencyIndex for GASpecificConfig is 0xB (same as 8000Hz). */ -}; - -/* ISO/IEC 14496-3 Interface to ISO/IEC 14496-1 (MPEG-4 Systems), Syntax of AudioSpecificConfig(). */ -/* This structure is represent of regularized AudioSpecificConfig. */ -/* for actual definition, see Syntax of GetAudioObjectType() for audioObjectType and extensionAudioObjectType. */ -typedef struct { - lsmash_mp4a_aac_sbr_mode sbr_mode; /* L-SMASH's original, including sbrPresent flag. */ - lsmash_mp4a_AudioObjectType audioObjectType; - uint8_t samplingFrequencyIndex; - uint32_t samplingFrequency; - uint8_t channelConfiguration; - lsmash_mp4a_AudioObjectType extensionAudioObjectType; - uint8_t extensionSamplingFrequencyIndex; - uint8_t extensionSamplingFrequency; - /* if( audioObjectType in - #[ 1, 2, 3, 4, 6, 7, *17, *19, *20, *21, *22, *23 ] // GASpecificConfig, AAC relatives and TwinVQ, BSAC - [ 8 ] // CelpSpecificConfig, not supported - [ 9 ] // HvxcSpecificConfig, not supported - [ 12 ] // TTSSpecificConfig, not supported - [ 13, 14, 15, 16 ] // StructuredAudioSpecificConfig, notsupported - [ 24 ] // ErrorResilientCelpSpecificConfig, notsupported - [ 25 ] // ErrorResilientHvxcSpecificConfig, notsupported - [ 26, 27 ] // ParametricSpecificConfig, notsupported - [ 28 ] // SSCSpecificConfig, notsupported - #[ 32, 33, 34 ] // MPEG_1_2_SpecificConfig - [ 35 ] // DSTSpecificConfig, notsupported - ){ */ - void* deepAudioSpecificConfig; // L-SMASH's original name, reperesents such as GASpecificConfig. */ - /* } */ - /* - // error resilient stuff, not supported - if( audioObjectType in [17, 19, 20, 21, 22, 23, 24, 25, 26, 27] ){ - uint8_t epConfig // 2bit - if( epConfig == 2 || epConfig == 3 ){ - ErrorProtectionSpecificConfig(); - } - if( epConfig == 3 ){ - uint8_t directMapping; // 1bit, currently always 1. - if( !directMapping ){ - // tbd - } - } - } - */ -} mp4a_AudioSpecificConfig_t; - -/* ISO/IEC 14496-3 Decoder configuration (GASpecificConfig), Syntax of GASpecificConfig() */ -/* ISO/IEC 14496-3 GASpecificConfig(), Sampling frequency mapping */ -typedef struct { - uint8_t frameLengthFlag; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */ - uint8_t dependsOnCoreCoder; /* FIXME: used if scalable AAC. */ - /* - if( dependsOnCoreCoder ){ - uint16_t coreCoderDelay; // 14bits - } - */ - uint8_t extensionFlag; /* 1bit, 1 if ErrorResilience */ - /* if( !channelConfiguration ){ */ - void* program_config_element; /* currently not supported. */ - /* } */ - /* - // we do not support AAC_scalable - if( (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_AAC_scalable) || (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable) ){ - uint8_t layerNr; // 3bits - } - */ - /* - // we do not support special AACs - if( extensionFlag ){ - if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_BSAC ){ - uint8_t numOfSubFrame; // 5bits - uint8_t layer_length; // 11bits - } - if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP - || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD - ){ - uint8_t aacSectionDataResilienceFlag; // 1bit - uint8_t aacScalefactorDataResilienceFlag; // 1bit - uint8_t aacSpectralDataResilienceFlag; // 1bit - } - uint8_t extensionFlag3; // 1bit - if( extensionFlag3 ){ - // tbd in version 3 - } - } - */ -} mp4a_GASpecificConfig_t; - -/* ISO/IEC 14496-3 MPEG_1_2_SpecificConfig */ -typedef struct { - uint8_t extension; /* shall be 0. */ -} mp4a_MPEG_1_2_SpecificConfig_t; - -/* ISO/IEC 14496-3 ALSSpecificConfig */ -typedef struct -{ - uint32_t size; - uint8_t *data; - uint32_t samp_freq; - uint16_t channels; - uint8_t resolution; - uint8_t floating; - uint16_t frame_length; - uint16_t max_order; - uint8_t block_switching; - uint8_t bgmc_mode; - uint8_t RLSLMS; -} mp4a_ALSSpecificConfig_t; - -static inline void mp4a_remove_GASpecificConfig( mp4a_GASpecificConfig_t* gasc ) -{ - debug_if( !gasc ) - return; - if( gasc->program_config_element ) - lsmash_free( gasc->program_config_element ); - lsmash_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 ) - lsmash_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 ) - lsmash_free( asc->deepAudioSpecificConfig ); - break; - } - lsmash_free( asc ); -} - -/* ADIF/PCE(program config element) style GASpecificConfig is not not supported. */ -/* channelConfig/samplingFrequencyIndex will be used when we support ADIF/PCE style GASpecificConfig. */ -static mp4a_GASpecificConfig_t* mp4a_create_GASpecificConfig( uint8_t samplingFrequencyIndex, uint8_t channelConfig, lsmash_mp4a_AudioObjectType aot ) -{ - debug_if( aot != MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC - && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_SSR && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LTP - && aot != MP4A_AUDIO_OBJECT_TYPE_TwinVQ ) - return NULL; - if( samplingFrequencyIndex > 0xB || channelConfig == 0 || channelConfig == 7 ) - return NULL; - mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_GASpecificConfig_t) ); - if( !gasc ) - return NULL; - gasc->frameLengthFlag = 0; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */ - gasc->dependsOnCoreCoder = 0; /* FIXME: used if scalable AAC. */ - switch( aot ){ - case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN: - case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: - case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR: - case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP: - case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable: - case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: - gasc->extensionFlag = 0; - break; - /* currently never occures. */ - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable: - case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ: - case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD: - gasc->extensionFlag = 1; - break; - default: - gasc->extensionFlag = 0; - break; - } - return gasc; -} - -static mp4a_MPEG_1_2_SpecificConfig_t* mp4a_create_MPEG_1_2_SpecificConfig() -{ - mp4a_MPEG_1_2_SpecificConfig_t *mpeg_1_2_sc = (mp4a_MPEG_1_2_SpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_MPEG_1_2_SpecificConfig_t) ); - if( !mpeg_1_2_sc ) - return NULL; - mpeg_1_2_sc->extension = 0; /* shall be 0. */ - return mpeg_1_2_sc; -} - -static mp4a_ALSSpecificConfig_t *mp4a_create_ALSSpecificConfig( uint8_t *exdata, uint32_t exdata_length ) -{ - mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_ALSSpecificConfig_t) ); - if( !alssc ) - return NULL; - alssc->data = lsmash_memdup( exdata, exdata_length ); - if( !alssc->data ) - { - lsmash_free( alssc ); - return NULL; - } - alssc->size = exdata_length; - return alssc; -} - -/* Currently, only normal AAC, MPEG_1_2 are supported. - For AAC, other than normal AAC, such as AAC_scalable, ER_AAC_xxx, are not supported. - ADIF/PCE(program config element) style AudioSpecificConfig is not supported. - aot shall not be MP4A_AUDIO_OBJECT_TYPE_SBR even if you wish to signal SBR explicitly, use sbr_mode instead. - Frequency/channels shall be base AAC's one, even if SBR/PS. - If other than AAC with SBR, sbr_mode shall be MP4A_AAC_SBR_NOT_SPECIFIED. */ -mp4a_AudioSpecificConfig_t *mp4a_create_AudioSpecificConfig( - lsmash_mp4a_AudioObjectType aot, - uint32_t frequency, - uint32_t channels, - lsmash_mp4a_aac_sbr_mode sbr_mode, - uint8_t *exdata, - uint32_t exdata_length -) -{ - if( aot != MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC - && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_SSR && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LTP - && aot != MP4A_AUDIO_OBJECT_TYPE_TwinVQ && aot != MP4A_AUDIO_OBJECT_TYPE_Layer_1 - && aot != MP4A_AUDIO_OBJECT_TYPE_Layer_2 && aot != MP4A_AUDIO_OBJECT_TYPE_Layer_3 - && aot != MP4A_AUDIO_OBJECT_TYPE_ALS ) - return NULL; - if( frequency == 0 ) - return NULL; - - uint8_t channelConfig; - switch( channels ){ - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - channelConfig = channels; - break; - case 8: - channelConfig = 7; - break; - default: - return NULL; - } - - mp4a_AudioSpecificConfig_t *asc = (mp4a_AudioSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_AudioSpecificConfig_t) ); - if( !asc ) - return NULL; - - asc->sbr_mode = sbr_mode; - asc->audioObjectType = aot; - asc->channelConfiguration = channelConfig; - - uint8_t samplingFrequencyIndex = 0xF; - uint8_t i = 0x0; - if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED - || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN - || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_LC - || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_SSR - || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_LTP - || aot == MP4A_AUDIO_OBJECT_TYPE_SBR ) - { - while( frequency < mp4a_sampling_frequency_table[i][0] ) - i++; - asc->samplingFrequencyIndex = frequency == mp4a_sampling_frequency_table[i][1] ? i : 0xF; - asc->samplingFrequency = frequency; - samplingFrequencyIndex = mp4a_sampling_frequency_table[i][2]; - /* SBR settings */ - if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED ) - { - /* SBR limitation */ - /* see ISO/IEC 14496-3 Levels within the profiles / Levels for the High Efficiency AAC Profile */ - if( i < 0x3 ) - { - lsmash_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_HIERARCHICAL ) - { - asc->extensionSamplingFrequency = frequency * 2; - asc->extensionSamplingFrequencyIndex = i == 0xC ? 0xF : mp4a_sampling_frequency_table[i][3]; - } - else - { - asc->extensionSamplingFrequencyIndex = asc->samplingFrequencyIndex; - asc->extensionSamplingFrequency = asc->samplingFrequency; - } - } - else - { - while( i < 0xD && frequency != mp4a_sampling_frequency_table[i][1] ) - i++; - asc->samplingFrequencyIndex = i != 0xD ? i : 0xF; - asc->samplingFrequency = frequency; - asc->extensionAudioObjectType = MP4A_AUDIO_OBJECT_TYPE_NULL; - asc->extensionSamplingFrequencyIndex = asc->samplingFrequencyIndex; - asc->extensionSamplingFrequency = asc->samplingFrequency; - } - - switch( aot ) - { - case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN: - case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: - case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR: - case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP: - case MP4A_AUDIO_OBJECT_TYPE_SBR: -#if 0 /* FIXME: here, stop currently unsupported codecs. */ - case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable: - case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: /* NOTE: I think we already have a support for TwinVQ, but how to test this? */ - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable: - case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ: - case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD: -#endif - asc->deepAudioSpecificConfig = mp4a_create_GASpecificConfig( samplingFrequencyIndex, channelConfig, aot ); - break; - case MP4A_AUDIO_OBJECT_TYPE_Layer_1: - case MP4A_AUDIO_OBJECT_TYPE_Layer_2: - case MP4A_AUDIO_OBJECT_TYPE_Layer_3: - asc->deepAudioSpecificConfig = mp4a_create_MPEG_1_2_SpecificConfig(); - break; - case MP4A_AUDIO_OBJECT_TYPE_ALS: - asc->deepAudioSpecificConfig = mp4a_create_ALSSpecificConfig( exdata, exdata_length ); - break; - default: - break; /* this case is trapped below. */ - } - if( !asc->deepAudioSpecificConfig ) - { - lsmash_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, 1, gasc->frameLengthFlag ); - lsmash_bits_put( bits, 1, gasc->dependsOnCoreCoder ); - lsmash_bits_put( bits, 1, gasc->extensionFlag ); -} - -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, 1, mpeg_1_2_sc->extension ); /* shall be 0 */ -} - -static void mp4a_put_ALSSpecificConfig( lsmash_bits_t *bits, mp4a_ALSSpecificConfig_t *alssc ) -{ - debug_if( !bits || !alssc ) - return; - lsmash_bits_put( bits, 5, 0 ); /* fillBits for byte alignment */ - lsmash_bits_import_data( bits, alssc->data, alssc->size ); -} - -static inline void mp4a_put_AudioObjectType( lsmash_bits_t* bits, lsmash_mp4a_AudioObjectType aot ) -{ - if( aot > MP4A_AUDIO_OBJECT_TYPE_ESCAPE ) - { - lsmash_bits_put( bits, 5, MP4A_AUDIO_OBJECT_TYPE_ESCAPE ); - lsmash_bits_put( bits, 6, aot - MP4A_AUDIO_OBJECT_TYPE_ESCAPE - 1 ); - } - else - lsmash_bits_put( bits, 5, aot ); -} - -static inline void mp4a_put_SamplingFrequencyIndex( lsmash_bits_t* bits, uint8_t samplingFrequencyIndex, uint32_t samplingFrequency ) -{ - lsmash_bits_put( bits, 4, samplingFrequencyIndex ); - if( samplingFrequencyIndex == 0xF ) - lsmash_bits_put( bits, 24, samplingFrequency ); -} - -/* 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, 4, asc->channelConfiguration ); - if( asc->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL ) - { - mp4a_put_SamplingFrequencyIndex( &bits, asc->extensionSamplingFrequencyIndex, asc->extensionSamplingFrequency ); - mp4a_put_AudioObjectType( &bits, asc->audioObjectType ); - } - switch( asc->audioObjectType ){ - case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN: - case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: - case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR: - case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP: - case MP4A_AUDIO_OBJECT_TYPE_SBR: -#if 0 /* FIXME: here, stop currently unsupported codecs */ - case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable: - case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: /* NOTE: I think we already have a support for TwinVQ, but how to test this? */ - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable: - case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ: - case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC: - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD: -#endif - mp4a_put_GASpecificConfig( &bits, (mp4a_GASpecificConfig_t*)asc->deepAudioSpecificConfig ); - break; - case MP4A_AUDIO_OBJECT_TYPE_Layer_1: - case MP4A_AUDIO_OBJECT_TYPE_Layer_2: - case MP4A_AUDIO_OBJECT_TYPE_Layer_3: - mp4a_put_MPEG_1_2_SpecificConfig( &bits, (mp4a_MPEG_1_2_SpecificConfig_t*)asc->deepAudioSpecificConfig ); - break; - case MP4A_AUDIO_OBJECT_TYPE_ALS: - mp4a_put_ALSSpecificConfig( &bits, (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig ); - break; - default: - break; /* FIXME: do we have to return error? */ - } - - /* FIXME: Error Resiliant stuff omitted here. */ - - if( asc->sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE || asc->sbr_mode == MP4A_AAC_SBR_NONE ) - { - lsmash_bits_put( &bits, 11, 0x2b7 ); - 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, 1, 0x0 ); - else - { - lsmash_bits_put( &bits, 1, 0x1 ); - mp4a_put_SamplingFrequencyIndex( &bits, asc->extensionSamplingFrequencyIndex, asc->extensionSamplingFrequency ); - } - } - } - lsmash_bits_put_align( &bits ); -} - -static int mp4a_get_GASpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc ) -{ - mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_GASpecificConfig_t) ); - if( !gasc ) - return -1; - asc->deepAudioSpecificConfig = gasc; - gasc->frameLengthFlag = lsmash_bits_get( bits, 1 ); - gasc->dependsOnCoreCoder = lsmash_bits_get( bits, 1 ); - if( gasc->dependsOnCoreCoder ) - lsmash_bits_get( bits, 14 ); /* coreCoderDelay */ - gasc->extensionFlag = lsmash_bits_get( bits, 1 ); - return 0; -} - -static int mp4a_get_MPEG_1_2_SpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc ) -{ - lsmash_bits_get( bits, 1 ); - return 0; -} - -static int mp4a_get_ALSSpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc ) -{ - mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_ALSSpecificConfig_t) ); - if( !alssc ) - return -1; - asc->deepAudioSpecificConfig = alssc; - lsmash_bits_get( bits, 32 ); /* als_id */ - alssc->samp_freq = lsmash_bits_get( bits, 32 ); - lsmash_bits_get( bits, 32 ); /* samples */ - alssc->channels = lsmash_bits_get( bits, 16 ); - lsmash_bits_get( bits, 3 ); /* file_type */ - alssc->resolution = lsmash_bits_get( bits, 3 ); - alssc->floating = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 1 ); /* msb_first */ - alssc->frame_length = lsmash_bits_get( bits, 16 ); - lsmash_bits_get( bits, 8 ); /* random_access */ - lsmash_bits_get( bits, 2 ); /* ra_flag */ - lsmash_bits_get( bits, 1 ); /* adapt_order */ - lsmash_bits_get( bits, 2 ); /* coef_table */ - lsmash_bits_get( bits, 1 ); /* long_term_prediction */ - alssc->max_order = lsmash_bits_get( bits, 10 ); - alssc->block_switching = lsmash_bits_get( bits, 2 ); - alssc->bgmc_mode = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 1 ); /* sb_part */ - lsmash_bits_get( bits, 1 ); /* joint_stereo */ - lsmash_bits_get( bits, 1 ); /* mc_coding */ - lsmash_bits_get( bits, 1 ); /* chan_config */ - lsmash_bits_get( bits, 1 ); /* chan_sort */ - lsmash_bits_get( bits, 1 ); /* crc_enabled */ - alssc->RLSLMS = lsmash_bits_get( bits, 1 ); - return 0; -} - -static mp4a_AudioSpecificConfig_t *mp4a_get_AudioSpecificConfig( lsmash_bits_t *bits, uint8_t *dsi_payload, uint32_t dsi_payload_length ) -{ - if( lsmash_bits_import_data( bits, dsi_payload, dsi_payload_length ) ) - return NULL; - mp4a_AudioSpecificConfig_t *asc = (mp4a_AudioSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_AudioSpecificConfig_t) ); - if( !asc ) - return NULL; - asc->audioObjectType = lsmash_bits_get( bits, 5 ); - if( asc->audioObjectType == 31 ) - asc->extensionAudioObjectType = asc->audioObjectType += 1 + lsmash_bits_get( bits, 6 ); - asc->samplingFrequencyIndex = lsmash_bits_get( bits, 4 ); - if( asc->samplingFrequencyIndex == 0xf ) - asc->samplingFrequency = lsmash_bits_get( bits, 24 ); - asc->channelConfiguration = lsmash_bits_get( bits, 4 ); - int ret = 0; - switch( asc->audioObjectType ) - { - case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN : - case MP4A_AUDIO_OBJECT_TYPE_AAC_LC : - case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : - case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP : - case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable : - case MP4A_AUDIO_OBJECT_TYPE_TwinVQ : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable : - case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ : - case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD : - ret = mp4a_get_GASpecificConfig( bits, asc ); - break; - case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : - case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : - case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : - ret = mp4a_get_MPEG_1_2_SpecificConfig( bits, asc ); - break; - case MP4A_AUDIO_OBJECT_TYPE_ALS : - lsmash_bits_get( bits, 5 ); - ret = mp4a_get_ALSSpecificConfig( bits, asc ); - break; - default : - break; - } - if( ret ) - { - lsmash_free( asc ); - return NULL; - } - return asc; -} - -int mp4a_setup_summary_from_AudioSpecificConfig( lsmash_audio_summary_t *summary, uint8_t *dsi_payload, uint32_t dsi_payload_length ) -{ - lsmash_bits_t *bits = lsmash_bits_adhoc_create(); - if( !bits ) - return -1; - mp4a_AudioSpecificConfig_t *asc = mp4a_get_AudioSpecificConfig( bits, dsi_payload, dsi_payload_length ); - if( !asc ) - goto fail; - summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; - summary->aot = asc->audioObjectType; - switch( asc->audioObjectType ) - { - case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN : - case MP4A_AUDIO_OBJECT_TYPE_AAC_LC : - case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : - case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP : - case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable : - case MP4A_AUDIO_OBJECT_TYPE_TwinVQ : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable : - case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ : - case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC : - case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD : - case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : - case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : - case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : - if( asc->samplingFrequencyIndex == 0xf ) - summary->frequency = asc->samplingFrequency; - else - { - uint8_t i = 0x0; - while( i != 0xc ) - { - if( mp4a_sampling_frequency_table[i][2] == asc->samplingFrequencyIndex ) - { - summary->frequency = mp4a_sampling_frequency_table[i][1]; - break; - } - ++i; - } - if( i == 0xc ) - goto fail; - } - if( asc->channelConfiguration < 8 ) - summary->channels = asc->channelConfiguration != 7 ? asc->channelConfiguration : 8; - else - summary->channels = 0; /* reserved */ - summary->sample_size = 16; - switch( asc->audioObjectType ) - { - case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR : - summary->samples_in_frame = 1024; - break; - case MP4A_AUDIO_OBJECT_TYPE_Layer_1 : - summary->samples_in_frame = 384; - break; - case MP4A_AUDIO_OBJECT_TYPE_Layer_2 : - case MP4A_AUDIO_OBJECT_TYPE_Layer_3 : - summary->samples_in_frame = 1152; - break; - default : - summary->samples_in_frame = !((mp4a_GASpecificConfig_t *)asc->deepAudioSpecificConfig)->frameLengthFlag ? 1024 : 960; - break; - } - break; - case MP4A_AUDIO_OBJECT_TYPE_ALS : - { - mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig; - summary->frequency = alssc->samp_freq; - summary->channels = alssc->channels + 1; - summary->sample_size = (alssc->resolution + 1) * 8; - summary->samples_in_frame = alssc->frame_length + 1; - break; - } - default : - break; - } - mp4a_remove_AudioSpecificConfig( asc ); - lsmash_bits_adhoc_cleanup( bits ); - return 0; -fail: - mp4a_remove_AudioSpecificConfig( asc ); - lsmash_bits_adhoc_cleanup( bits ); - return -1; -} - -/* This function is very ad-hoc. */ -uint8_t *mp4a_export_AudioSpecificConfig( lsmash_mp4a_AudioObjectType aot, - uint32_t frequency, - uint32_t channels, - lsmash_mp4a_aac_sbr_mode sbr_mode, - uint8_t *exdata, - uint32_t exdata_length, - uint32_t *data_length ) -{ - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return NULL; - mp4a_AudioSpecificConfig_t *asc = mp4a_create_AudioSpecificConfig( aot, frequency, channels, sbr_mode, exdata, exdata_length ); - if( !asc ) - { - lsmash_bs_cleanup( bs ); - return NULL; - } - mp4a_put_AudioSpecificConfig( bs, asc ); - uint8_t *data = lsmash_bs_export_data( bs, data_length ); - mp4a_remove_AudioSpecificConfig( asc ); - lsmash_bs_cleanup( bs ); - if( !data ) - return NULL; - return data; -} - -/*************************************************************************** - 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->summary_type != LSMASH_SUMMARY_TYPE_AUDIO ) - return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */ - if( lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ) != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 ) - return MP4A_AUDIO_PLI_NOT_SPECIFIED; /* This is of audio stream, but not described in ISO/IEC 14496-3. */ - if( summary->channels == 0 || summary->frequency == 0 ) - return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */ - mp4a_audioProfileLevelIndication pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; - switch( summary->aot ) - { - case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: - if( summary->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL ) - { - /* NOTE: This is not strictly preferable, but accurate; just possibly over-estimated. - We do not expect to use MP4A_AAC_SBR_HIERARCHICAL mode without SBR, nor downsampled mode with SBR. */ - if( summary->channels <= 2 && summary->frequency <= 24000 ) - pli = MP4A_AUDIO_PLI_HE_AAC_L2; - else if( summary->channels <= 5 && summary->frequency <= 48000 ) - pli = MP4A_AUDIO_PLI_HE_AAC_L5; - else - pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; - break; - } - /* pretending plain AAC-LC, if actually HE-AAC. */ - static const uint32_t mp4sys_aac_pli_table[5][3] = { - /* channels, frequency, audioProfileLevelIndication */ - { 6, 96000, MP4A_AUDIO_PLI_AAC_L5 }, /* FIXME: 6ch is not strictly correct, but works in many case. */ - { 6, 48000, MP4A_AUDIO_PLI_AAC_L4 }, /* FIXME: 6ch is not strictly correct, but works in many case. */ - { 2, 48000, MP4A_AUDIO_PLI_AAC_L2 }, - { 2, 24000, MP4A_AUDIO_PLI_AAC_L1 }, - { 0, 0, MP4A_AUDIO_PLI_NOT_SPECIFIED } - }; - for( int i = 0; summary->channels <= mp4sys_aac_pli_table[i][0] && summary->frequency <= mp4sys_aac_pli_table[i][1] ; i++ ) - pli = mp4sys_aac_pli_table[i][2]; - break; - case MP4A_AUDIO_OBJECT_TYPE_ALS: - /* FIXME: this is not stricly. Summary shall carry max_order, block_switching, bgmc_mode and RLSLMS. */ - if( summary->channels <= 2 && summary->frequency <= 48000 && summary->sample_size <= 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 -Nru l-smash-1.9.1/mp4a.h l-smash-2.3.0/mp4a.h --- l-smash-1.9.1/mp4a.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/mp4a.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -/***************************************************************************** - * mp4a.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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, - uint8_t *exdata, - uint32_t exdata_length -); -void mp4a_put_AudioSpecificConfig( lsmash_bs_t* bs, mp4a_AudioSpecificConfig_t* asc ); -void mp4a_remove_AudioSpecificConfig( mp4a_AudioSpecificConfig_t* asc ); - -uint8_t *mp4a_export_AudioSpecificConfig( lsmash_mp4a_AudioObjectType aot, - uint32_t frequency, - uint32_t channels, - lsmash_mp4a_aac_sbr_mode sbr_mode, - uint8_t *exdata, - uint32_t exdata_length, - uint32_t *data_length ); - -/* export for importer */ -extern const uint32_t mp4a_sampling_frequency_table[13][5]; - -/* setup for summary */ -int mp4a_setup_summary_from_AudioSpecificConfig( lsmash_audio_summary_t *summary, uint8_t *dsi_payload, uint32_t dsi_payload_length ); - -/* profileLevelIndication relative functions. */ -mp4a_audioProfileLevelIndication mp4a_get_audioProfileLevelIndication( lsmash_audio_summary_t *summary ); -mp4a_audioProfileLevelIndication mp4a_max_audioProfileLevelIndication( - mp4a_audioProfileLevelIndication a, - mp4a_audioProfileLevelIndication b -); - -#endif - -#endif diff -Nru l-smash-1.9.1/mp4sys.c l-smash-2.3.0/mp4sys.c --- l-smash-1.9.1/mp4sys.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/mp4sys.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1349 +0,0 @@ -/***************************************************************************** - * mp4sys.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 - -#include "box.h" -#include "description.h" -#include "mp4a.h" -#define MP4SYS_INTERNAL -#include "mp4sys.h" - -/*************************************************************************** - MPEG-4 Systems -***************************************************************************/ - -#define ALWAYS_28BITS_LENGTH_CODING 1 // for some weird (but originator's) devices - -/* List of Class Tags for Descriptors */ -typedef enum { - MP4SYS_DESCRIPTOR_TAG_Forbidden = 0x00, /* Forbidden */ - MP4SYS_DESCRIPTOR_TAG_ObjectDescrTag = 0x01, /* ObjectDescrTag */ - MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag = 0x02, /* InitialObjectDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ES_DescrTag = 0x03, /* ES_DescrTag */ - MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag = 0x04, /* DecoderConfigDescrTag */ - MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag = 0x05, /* DecSpecificInfoTag */ - MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag = 0x06, /* SLConfigDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ContentIdentDescrTag = 0x07, /* ContentIdentDescrTag */ - MP4SYS_DESCRIPTOR_TAG_SupplContentIdentDescrTag = 0x08, /* SupplContentIdentDescrTag */ - MP4SYS_DESCRIPTOR_TAG_IPI_DescrPointerTag = 0x09, /* IPI_DescrPointerTag */ - MP4SYS_DESCRIPTOR_TAG_IPMP_DescrPointerTag = 0x0A, /* IPMP_DescrPointerTag */ - MP4SYS_DESCRIPTOR_TAG_IPMP_DescrTag = 0x0B, /* IPMP_DescrTag */ - MP4SYS_DESCRIPTOR_TAG_QoS_DescrTag = 0x0C, /* QoS_DescrTag */ - MP4SYS_DESCRIPTOR_TAG_RegistrationDescrTag = 0x0D, /* RegistrationDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag = 0x0E, /* ES_ID_IncTag */ - MP4SYS_DESCRIPTOR_TAG_ES_ID_RefTag = 0x0F, /* ES_ID_RefTag */ - MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag = 0x10, /* MP4_IOD_Tag, InitialObjectDescriptor for MP4 */ - MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag = 0x11, /* MP4_OD_Tag, ObjectDescriptor for MP4 */ - MP4SYS_DESCRIPTOR_TAG_IPI_DescrPointerRefTag = 0x12, /* IPI_DescrPointerRefTag */ - MP4SYS_DESCRIPTOR_TAG_ExtendedProfileLevelDescrTag = 0x13, /* ExtendedProfileLevelDescrTag */ - MP4SYS_DESCRIPTOR_TAG_profileLevelIndicationIndexDescrTag = 0x14, /* profileLevelIndicationIndexDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ContentClassificationDescrTag = 0x40, /* ContentClassificationDescrTag */ - MP4SYS_DESCRIPTOR_TAG_KeyWordDescrTag = 0x41, /* KeyWordDescrTag */ - MP4SYS_DESCRIPTOR_TAG_RatingDescrTag = 0x42, /* RatingDescrTag */ - MP4SYS_DESCRIPTOR_TAG_LanguageDescrTag = 0x43, /* LanguageDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ShortTextualDescrTag = 0x44, /* ShortTextualDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ExpandedTextualDescrTag = 0x45, /* ExpandedTextualDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ContentCreatorNameDescrTag = 0x46, /* ContentCreatorNameDescrTag */ - MP4SYS_DESCRIPTOR_TAG_ContentCreationDateDescrTag = 0x47, /* ContentCreationDateDescrTag */ - MP4SYS_DESCRIPTOR_TAG_OCICreatorNameDescrTag = 0x48, /* OCICreatorNameDescrTag */ - MP4SYS_DESCRIPTOR_TAG_OCICreationDateDescrTag = 0x49, /* OCICreationDateDescrTag */ - MP4SYS_DESCRIPTOR_TAG_SmpteCameraPositionDescrTag = 0x4A, /* SmpteCameraPositionDescrTag */ - MP4SYS_DESCRIPTOR_TAG_Forbidden1 = 0xFF, /* Forbidden */ -} mp4sys_descriptor_tag; -// MP4SYS_DESCRIPTOR_TAG_ES_DescrRemoveRefTag = 0x07, /* FIXME: (command tag), see 14496-14 Object Descriptors */ - -typedef struct { - uint32_t size; // 2^28 at most - mp4sys_descriptor_tag tag; -} mp4sys_descriptor_head_t; - -/* DecoderSpecificInfo */ -/* contents varies depends on ObjectTypeIndication and StreamType. */ -typedef struct { - mp4sys_descriptor_head_t header; - uint8_t* data; -} mp4sys_DecoderSpecificInfo_t; - -/* DecoderConfigDescriptor */ -typedef struct { - mp4sys_descriptor_head_t header; - lsmash_mp4sys_object_type_indication objectTypeIndication; - lsmash_mp4sys_stream_type streamType; - uint8_t upStream; /* bit(1), always 0 in this muxer, used for interactive contents. */ - uint8_t reserved; /* const bit(1), always 1. */ - uint32_t bufferSizeDB; /* maybe CPB size in bytes, NOT bits. */ - uint32_t maxBitrate; - uint32_t avgBitrate; /* 0 if VBR */ - mp4sys_DecoderSpecificInfo_t* decSpecificInfo; /* can be NULL. */ - /* 14496-1 seems to say if we are in IOD(InitialObjectDescriptor), we might use this. - See ExtensionProfileLevelDescr, The Initial Object Descriptor. - But I don't think this is mandatory despite 14496-1, because 14496-14 says, in OD or IOD, - we have to use ES_ID_Inc instead of ES_Descriptor, which does not have DecoderConfigDescriptor. */ - // profileLevelIndicationIndexDescriptor profileLevelIndicationIndexDescr [0..255]; -} mp4sys_DecoderConfigDescriptor_t; - -/* SLConfigDescriptor */ -typedef struct { - mp4sys_descriptor_head_t header; - uint8_t predefined; /* default the values from a set of predefined parameter sets as detailed below. - * 0x00 : Custum - * 0x01 : null SL packet header - * 0x02 : Reserved for use in MP4 files - * 0x03 - 0xFF : Reserved for ISO use - * MP4 file that does not use URL_Flag shall have constant value 0x02. */ - /* Custom values - * The following fields are placed if predefined == 0x00. */ - unsigned useAccessUnitStartFlag : 1; - unsigned useAccessUnitEndFlag : 1; - unsigned useRandomAccessPointFlag : 1; - unsigned hasRandomAccessUnitsOnlyFlag : 1; - unsigned usePaddingFlag : 1; - unsigned useTimeStampsFlag : 1; - unsigned useIdleFlag : 1; - unsigned durationFlag : 1; - uint32_t timeStampResolution; - uint32_t OCRResolution; - uint8_t timeStampLength; - uint8_t OCRLength; - uint8_t AU_Length; - uint8_t instantBitrateLength; - unsigned degradationPriorityLength : 4; - unsigned AU_seqNumLength : 5; - unsigned packetSeqNumLength : 5; - unsigned reserved : 2; - /* The following fields are placed if durationFlag is true. */ - uint32_t timeScale; - uint16_t accessUnitDuration; - uint16_t compositionUnitDuration; - /* The following fields are placed if useTimeStampsFlag is false. */ - uint64_t startDecodingTimeStamp; - uint64_t startCompositionTimeStamp; -} mp4sys_SLConfigDescriptor_t; - -/* ES_Descriptor */ -typedef struct mp4sys_ES_Descriptor_t -{ - 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 ) - lsmash_free( esd->decConfigDescr->decSpecificInfo->data ); - lsmash_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 ); - lsmash_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; - lsmash_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 ); - lsmash_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 ); - lsmash_free( od ); - return 0; -} - -int mp4sys_add_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd, void* dsi_payload, uint32_t dsi_payload_length ) -{ - if( !esd || !esd->decConfigDescr || dsi_payload == NULL || dsi_payload_length == 0 ) - return -1; - mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderSpecificInfo_t) ); - if( !dsi ) - return -1; - dsi->header.tag = MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag; - dsi->data = lsmash_memdup( dsi_payload, dsi_payload_length ); - if( !dsi->data ) - { - lsmash_free( dsi ); - return -1; - } - dsi->header.size = dsi_payload_length; - debug_if( mp4sys_remove_DecoderSpecificInfo( esd ) ) - { - lsmash_free( dsi->data ); - lsmash_free( dsi ); - return -1; - } - esd->decConfigDescr->decSpecificInfo = dsi; - return 0; -} - -/* - bufferSizeDB is byte unit, NOT bit unit. - avgBitrate is 0 if VBR -*/ -int mp4sys_add_DecoderConfigDescriptor( - mp4sys_ES_Descriptor_t* esd, - lsmash_mp4sys_object_type_indication objectTypeIndication, - lsmash_mp4sys_stream_type streamType, - uint32_t bufferSizeDB, - uint32_t maxBitrate, - uint32_t avgBitrate -){ - if( !esd ) - return -1; - mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderConfigDescriptor_t) ); - if( !dcd ) - return -1; - dcd->header.tag = MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag; - dcd->objectTypeIndication = objectTypeIndication; - dcd->streamType = streamType; - dcd->reserved = 1; - dcd->bufferSizeDB = bufferSizeDB; - dcd->maxBitrate = maxBitrate; - dcd->avgBitrate = avgBitrate; - debug_if( mp4sys_remove_DecoderConfigDescriptor( esd ) ) - { - lsmash_free( dcd ); - return -1; - } - esd->decConfigDescr = dcd; - return 0; -} - -/* - bufferSizeDB is byte unit, NOT bit unit. - avgBitrate is 0 if VBR -*/ -int mp4sys_update_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t* esd, uint32_t bufferSizeDB, uint32_t maxBitrate, uint32_t avgBitrate ) -{ - if( !esd || !esd->decConfigDescr ) - return -1; - mp4sys_DecoderConfigDescriptor_t* dcd = esd->decConfigDescr; - dcd->bufferSizeDB = bufferSizeDB; - dcd->maxBitrate = maxBitrate; - dcd->avgBitrate = avgBitrate; - return 0; -} - -int mp4sys_add_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd ) -{ - if( !esd ) - return -1; - mp4sys_SLConfigDescriptor_t *slcd = (mp4sys_SLConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_SLConfigDescriptor_t) ); - if( !slcd ) - return -1; - slcd->header.tag = MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag; - slcd->predefined = 0x02; /* MP4 file which does not use URL_Flag shall have constant value 0x02 */ - slcd->useTimeStampsFlag = 1; - debug_if( mp4sys_remove_SLConfigDescriptor( esd ) ) - { - lsmash_free( slcd ); - return -1; - } - esd->slConfigDescr = slcd; - return 0; -} - -/* ES_ID of the ES Descriptor is stored as 0 when the ES Descriptor is built into sample descriptions in MP4 file format - * since the lower 16 bits of the track_ID is used, instead of ES_ID, for the identifier of the elemental stream within the track. */ -mp4sys_ES_Descriptor_t* mp4sys_create_ES_Descriptor( uint16_t ES_ID ) -{ - mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_ES_Descriptor_t) ); - if( !esd ) - return NULL; - esd->header.tag = MP4SYS_DESCRIPTOR_TAG_ES_DescrTag; - esd->ES_ID = ES_ID; - return esd; -} - -/* NOTE: This is only for MP4_IOD and MP4_OD, not for ISO Base Media's ObjectDescriptor and InitialObjectDescriptor */ -int mp4sys_add_ES_ID_Inc( mp4sys_ObjectDescriptor_t* od, uint32_t Track_ID ) -{ - if( !od ) - return -1; - if( !od->esDescr && ( od->esDescr = lsmash_create_entry_list() ) == NULL ) - return -1; - mp4sys_ES_ID_Inc_t *es_id_inc = (mp4sys_ES_ID_Inc_t *)lsmash_malloc_zero( sizeof(mp4sys_ES_ID_Inc_t) ); - if( !es_id_inc ) - return -1; - es_id_inc->header.tag = MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag; - es_id_inc->Track_ID = Track_ID; - if( lsmash_add_entry( od->esDescr, es_id_inc ) ) - { - lsmash_free( es_id_inc ); - return -1; - } - return 0; -} - -/* NOTE: This is only for MP4_OD, not for ISO Base Media's ObjectDescriptor */ -mp4sys_ObjectDescriptor_t* mp4sys_create_ObjectDescriptor( uint16_t ObjectDescriptorID ) -{ - mp4sys_ObjectDescriptor_t *od = (mp4sys_ObjectDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_ObjectDescriptor_t) ); - if( !od ) - return NULL; - od->header.tag = MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag; - od->ObjectDescriptorID = ObjectDescriptorID; - od->includeInlineProfileLevelFlag = 1; /* 1 as part of reserved flag. */ - od->ODProfileLevelIndication = MP4SYS_OD_PLI_NONE_REQUIRED; - od->sceneProfileLevelIndication = MP4SYS_SCENE_PLI_NONE_REQUIRED; - od->audioProfileLevelIndication = MP4A_AUDIO_PLI_NONE_REQUIRED; - od->visualProfileLevelIndication = MP4SYS_VISUAL_PLI_NONE_REQUIRED; - od->graphicsProfileLevelIndication = MP4SYS_GRAPHICS_PLI_NONE_REQUIRED; - return od; -} - -/* NOTE: This is only for MP4_IOD, not for Iso Base Media's InitialObjectDescriptor */ -int mp4sys_to_InitialObjectDescriptor( - mp4sys_ObjectDescriptor_t* od, - uint8_t include_inline_pli, - mp4sys_ODProfileLevelIndication od_pli, - mp4sys_sceneProfileLevelIndication scene_pli, - mp4a_audioProfileLevelIndication audio_pli, - mp4sys_visualProfileLevelIndication visual_pli, - mp4sys_graphicsProfileLevelIndication graph_pli -){ - if( !od ) - return -1; - od->header.tag = MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag; - od->includeInlineProfileLevelFlag = include_inline_pli; - od->ODProfileLevelIndication = od_pli; - od->sceneProfileLevelIndication = scene_pli; - od->audioProfileLevelIndication = audio_pli; - od->visualProfileLevelIndication = visual_pli; - od->graphicsProfileLevelIndication = graph_pli; - return 0; -} - -/* returns total size of descriptor, including header, 2 at least */ -static inline uint32_t mp4sys_get_descriptor_size( uint32_t payload_size_in_byte ) -{ -#if ALWAYS_28BITS_LENGTH_CODING - return payload_size_in_byte + 4 + 1; /* +4 means 28bits length coding, +1 means tag's space */ -#else - /* descriptor length will be split into 7bits - see 14496-1 Expandable classes and Length encoding of descriptors and commands */ - uint32_t i; - for( i = 1; payload_size_in_byte >> ( 7 * i ); i++ ); - return payload_size_in_byte + i + 1; /* +1 means tag's space */ -#endif -} - -static uint32_t mp4sys_update_DecoderSpecificInfo_size( mp4sys_ES_Descriptor_t* esd ) -{ - debug_if( !esd || !esd->decConfigDescr ) - return 0; - if( !esd->decConfigDescr->decSpecificInfo ) - return 0; - /* no need to update, header.size is already set */ - return mp4sys_get_descriptor_size( esd->decConfigDescr->decSpecificInfo->header.size ); -} - -static uint32_t mp4sys_update_DecoderConfigDescriptor_size( mp4sys_ES_Descriptor_t* esd ) -{ - debug_if( !esd ) - return 0; - if( !esd->decConfigDescr ) - return 0; - uint32_t size = 13; - size += mp4sys_update_DecoderSpecificInfo_size( esd ); - esd->decConfigDescr->header.size = size; - return mp4sys_get_descriptor_size( size ); -} - -static uint32_t mp4sys_update_SLConfigDescriptor_size( mp4sys_ES_Descriptor_t* esd ) -{ - debug_if( !esd ) - return 0; - if( !esd->slConfigDescr ) - return 0; - mp4sys_SLConfigDescriptor_t *slcd = esd->slConfigDescr; - uint32_t size = 1; - if( slcd->predefined == 0x00 ) - size += 15; - if( slcd->durationFlag ) - size += 8; - if( !slcd->useTimeStampsFlag ) - size += (2 * slcd->timeStampLength + 7) / 8; - esd->slConfigDescr->header.size = size; - return mp4sys_get_descriptor_size( size ); -} - -uint32_t mp4sys_update_ES_Descriptor_size( mp4sys_ES_Descriptor_t* esd ) -{ - if( !esd ) - return 0; - uint32_t size = 3; - if( esd->streamDependenceFlag ) - size += 2; - if( esd->URL_Flag ) - size += 1 + esd->URLlength; - if( esd->OCRstreamFlag ) - size += 2; - size += mp4sys_update_DecoderConfigDescriptor_size( esd ); - size += mp4sys_update_SLConfigDescriptor_size( esd ); - esd->header.size = size; - return mp4sys_get_descriptor_size( size ); -} - -static uint32_t mp4sys_update_ES_ID_Inc_size( mp4sys_ES_ID_Inc_t* es_id_inc ) -{ - debug_if( !es_id_inc ) - return 0; - es_id_inc->header.size = 4; - return mp4sys_get_descriptor_size( es_id_inc->header.size ); -} - -/* This function works as aggregate of ES_ID_Incs, so this function itself updates no size information */ -static uint32_t mp4sys_update_ES_ID_Incs_size( mp4sys_ObjectDescriptor_t* od ) -{ - debug_if( !od ) - return 0; - if( !od->esDescr ) - return 0; - uint32_t size = 0; - for( lsmash_entry_t *entry = od->esDescr->head; entry; entry = entry->next ) - size += mp4sys_update_ES_ID_Inc_size( (mp4sys_ES_ID_Inc_t*)entry->data ); - return size; -} - -uint32_t mp4sys_update_ObjectDescriptor_size( mp4sys_ObjectDescriptor_t* od ) -{ - if( !od ) - return 0; - uint32_t size = od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ? 7 : 2; - size += mp4sys_update_ES_ID_Incs_size( od ); - od->header.size = size; - return mp4sys_get_descriptor_size( size ); -} - -static int mp4sys_put_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t* header ) -{ - debug_if( !bs || !header ) - return -1; - lsmash_bs_put_byte( bs, header->tag ); - /* descriptor length will be splitted into 7bits - see 14496-1 Expandable classes and Length encoding of descriptors and commands */ -#if ALWAYS_28BITS_LENGTH_CODING - lsmash_bs_put_byte( bs, ( header->size >> 21 ) | 0x80 ); - lsmash_bs_put_byte( bs, ( header->size >> 14 ) | 0x80 ); - lsmash_bs_put_byte( bs, ( header->size >> 7 ) | 0x80 ); -#else - for( uint32_t i = mp4sys_get_descriptor_size( header->size ) - header->size - 2; i; i-- ){ - lsmash_bs_put_byte( bs, ( header->size >> ( 7 * i ) ) | 0x80 ); - } -#endif - lsmash_bs_put_byte( bs, header->size & 0x7F ); - return 0; -} - -static int mp4sys_put_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->header.size, dsi->data ); - return 0; -} - -static int mp4sys_put_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 ); - return mp4sys_put_DecoderSpecificInfo( bs, dcd->decSpecificInfo ); - /* here, profileLevelIndicationIndexDescriptor is omitted */ -} - -static int mp4sys_put_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_SLConfigDescriptor_t* slcd ) -{ - debug_if( !bs ) - return -1; - if( !slcd ) - return 0; - debug_if( mp4sys_put_descriptor_header( bs, &slcd->header ) ) - return -1; - lsmash_bs_put_byte( bs, slcd->predefined ); - if( slcd->predefined == 0x00 ) - { - uint8_t temp8; - temp8 = slcd->useAccessUnitStartFlag << 7; - temp8 |= slcd->useAccessUnitEndFlag << 6; - temp8 |= slcd->useRandomAccessPointFlag << 5; - temp8 |= slcd->hasRandomAccessUnitsOnlyFlag << 4; - temp8 |= slcd->usePaddingFlag << 3; - temp8 |= slcd->useTimeStampsFlag << 2; - temp8 |= slcd->useIdleFlag << 1; - temp8 |= slcd->durationFlag; - lsmash_bs_put_byte( bs, temp8 ); - lsmash_bs_put_be32( bs, slcd->timeStampResolution ); - lsmash_bs_put_be32( bs, slcd->OCRResolution ); - lsmash_bs_put_byte( bs, slcd->timeStampLength ); - lsmash_bs_put_byte( bs, slcd->OCRLength ); - lsmash_bs_put_byte( bs, slcd->AU_Length ); - lsmash_bs_put_byte( bs, slcd->instantBitrateLength ); - uint16_t temp16; - temp16 = slcd->degradationPriorityLength << 12; - temp16 |= slcd->AU_seqNumLength << 7; - temp16 |= slcd->packetSeqNumLength << 2; - temp16 |= slcd->reserved; - lsmash_bs_put_be16( bs, temp16 ); - } - if( slcd->durationFlag ) - { - lsmash_bs_put_be32( bs, slcd->timeScale ); - lsmash_bs_put_be16( bs, slcd->accessUnitDuration ); - lsmash_bs_put_be16( bs, slcd->compositionUnitDuration ); - } - if( !slcd->useTimeStampsFlag ) - { - lsmash_bits_t *bits = lsmash_bits_create( bs ); - if( !bits ) - return -1; - lsmash_bits_put( bits, slcd->timeStampLength, slcd->startDecodingTimeStamp ); - lsmash_bits_put( bits, slcd->timeStampLength, slcd->startCompositionTimeStamp ); - lsmash_bits_put_align( bits ); - lsmash_bits_cleanup( bits ); - } - return 0; -} - -int mp4sys_put_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->URLlength, esd->URLstring ); - } - 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( mp4sys_put_DecoderConfigDescriptor( bs, esd->decConfigDescr ) ) - return -1; - return mp4sys_put_SLConfigDescriptor( bs, esd->slConfigDescr ); -} - -int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) -{ - return mp4sys_put_ES_Descriptor( bs, esd ); -} - -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 ) - if( mp4sys_put_ES_ID_Inc( bs, (mp4sys_ES_ID_Inc_t*)entry->data ) < 0 ) - return -1; - return 0; -} - -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 ); - } - 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_print_descriptor_header( FILE *fp, mp4sys_descriptor_head_t *header, int indent ) -{ - static const char *descriptor_names_table[256] = - { - "Forbidden", - "ObjectDescriptor", - "InitialObjectDescriptor", - "ES_Descriptor", - "DecoderConfigDescriptor", - "DecoderSpecificInfo", - "SLConfigDescriptor", - [0x0E] = "ES_ID_Inc", - [0x0F] = "ES_ID_Ref", - [0x10] = "MP4_IOD", - [0x11] = "MP4_OD" - }; - if( descriptor_names_table[ header->tag ] ) - lsmash_ifprintf( fp, indent, "[tag = 0x%02"PRIx8": %s]\n", header->tag, descriptor_names_table[ header->tag ] ); - else - lsmash_ifprintf( fp, indent, "[tag = 0x%02"PRIx8"]\n", header->tag ); - lsmash_ifprintf( fp, ++indent, "expandableClassSize = %"PRIu32"\n", header->size ); -} - -static void mp4sys_print_DecoderConfigDescriptor( FILE *fp, mp4sys_DecoderConfigDescriptor_t *dcd, int indent ) -{ - static const char *object_type_indication_descriptions_table[256] = - { - "Forbidden", - "Systems ISO/IEC 14496-1 (a)", - "Systems ISO/IEC 14496-1 (b)", - "Interaction Stream", - "Systems ISO/IEC 14496-1 Extended BIFS Configuration", - "Systems ISO/IEC 14496-1 AFX", - "Font Data Stream", - "Synthesized Texture Stream", - "Streaming Text Stream", - "LASeR Stream", - "Simple Aggregation Format (SAF) Stream", - [0x20] = "Visual ISO/IEC 14496-2", - [0x21] = "Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10", - [0x22] = "Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10", - [0x40] = "Audio ISO/IEC 14496-3", - [0x60] = "Visual ISO/IEC 13818-2 Simple Profile", - [0x61] = "Visual ISO/IEC 13818-2 Main Profile", - [0x62] = "Visual ISO/IEC 13818-2 SNR Profile", - [0x63] = "Visual ISO/IEC 13818-2 Spatial Profile", - [0x64] = "Visual ISO/IEC 13818-2 High Profile", - [0x65] = "Visual ISO/IEC 13818-2 422 Profile", - [0x66] = "Audio ISO/IEC 13818-7 Main Profile", - [0x67] = "Audio ISO/IEC 13818-7 LowComplexity Profile", - [0x68] = "Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile", - [0x69] = "Audio ISO/IEC 13818-3", - [0x6A] = "Visual ISO/IEC 11172-2", - [0x6B] = "Audio ISO/IEC 11172-3", - [0x6C] = "Visual ISO/IEC 10918-1", - [0x6D] = "Portable Network Graphics", - [0x6E] = "Visual ISO/IEC 15444-1 (JPEG 2000)", - [0xA0] = "EVRC Voice", - [0xA1] = "SMV Voice", - [0xA2] = "3GPP2 Compact Multimedia Format (CMF)", - [0xA3] = "SMPTE VC-1 Video", - [0xA4] = "Dirac Video Coder", - [0xA5] = "AC-3 Audio", - [0xA6] = "Enhanced AC-3 audio", - [0xA7] = "DRA Audio", - [0xA8] = "ITU G.719 Audio", - [0xA9] = "DTS Coherent Acoustics audio", - [0xAA] = "DTS-HD High Resolution Audio", - [0xAB] = "DTS-HD Master Audio", - [0xAC] = "DTS Express low bit rate audio", - [0xE1] = "13K Voice", - [0xFF] = "no object type specified" - }; - static const char *stream_type_descriptions_table[64] = - { - "Forbidden", - "ObjectDescriptorStream", - "ClockReferenceStream", - "SceneDescriptionStream", - "VisualStream", - "AudioStream", - "MPEG7Stream", - "IPMPStream", - "ObjectContentInfoStream", - "MPEGJStream", - "Interaction Stream", - "IPMPToolStream", - "FontDataStream", - "StreamingText" - }; - mp4sys_print_descriptor_header( fp, &dcd->header, indent++ ); - if( object_type_indication_descriptions_table[ dcd->objectTypeIndication ] ) - lsmash_ifprintf( fp, indent, "objectTypeIndication = 0x%02"PRIx8" (%s)\n", dcd->objectTypeIndication, object_type_indication_descriptions_table[ dcd->objectTypeIndication ] ); - else - lsmash_ifprintf( fp, indent, "objectTypeIndication = 0x%02"PRIx8"\n", dcd->objectTypeIndication ); - if( stream_type_descriptions_table[ dcd->streamType ] ) - lsmash_ifprintf( fp, indent, "streamType = 0x%02"PRIx8" (%s)\n", dcd->streamType, stream_type_descriptions_table[ dcd->streamType ] ); - else - lsmash_ifprintf( fp, indent, "streamType = 0x%02"PRIx8"\n", dcd->streamType ); - lsmash_ifprintf( fp, indent, "upStream = %"PRIu8"\n", dcd->upStream ); - lsmash_ifprintf( fp, indent, "reserved = %"PRIu8"\n", dcd->reserved ); - lsmash_ifprintf( fp, indent, "bufferSizeDB = %"PRIu32"\n", dcd->bufferSizeDB ); - lsmash_ifprintf( fp, indent, "maxBitrate = %"PRIu32"\n", dcd->maxBitrate ); - lsmash_ifprintf( fp, indent, "avgBitrate = %"PRIu32"%s\n", dcd->avgBitrate, dcd->avgBitrate ? "" : " (variable bitrate)" ); -} - -static void mp4sys_print_SLConfigDescriptor( FILE *fp, mp4sys_SLConfigDescriptor_t *slcd, int indent ) -{ - mp4sys_print_descriptor_header( fp, &slcd->header, indent++ ); - lsmash_ifprintf( fp, indent, "predefined = %"PRIu8"\n", slcd->predefined ); - if( slcd->predefined == 0 ) - { - lsmash_ifprintf( fp, indent, "useAccessUnitStartFlag = %"PRIu8"\n", slcd->useAccessUnitStartFlag ); - lsmash_ifprintf( fp, indent, "useAccessUnitEndFlag = %"PRIu8"\n", slcd->useAccessUnitEndFlag ); - lsmash_ifprintf( fp, indent, "useRandomAccessPointFlag = %"PRIu8"\n", slcd->useRandomAccessPointFlag ); - lsmash_ifprintf( fp, indent, "hasRandomAccessUnitsOnlyFlag = %"PRIu8"\n", slcd->hasRandomAccessUnitsOnlyFlag ); - lsmash_ifprintf( fp, indent, "usePaddingFlag = %"PRIu8"\n", slcd->usePaddingFlag ); - lsmash_ifprintf( fp, indent, "useTimeStampsFlag = %"PRIu8"\n", slcd->useTimeStampsFlag ); - lsmash_ifprintf( fp, indent, "useIdleFlag = %"PRIu8"\n", slcd->useIdleFlag ); - lsmash_ifprintf( fp, indent, "durationFlag = %"PRIu8"\n", slcd->durationFlag ); - lsmash_ifprintf( fp, indent, "timeStampResolution = %"PRIu32"\n", slcd->timeStampResolution ); - lsmash_ifprintf( fp, indent, "OCRResolution = %"PRIu32"\n", slcd->OCRResolution ); - lsmash_ifprintf( fp, indent, "timeStampLength = %"PRIu8"\n", slcd->timeStampLength ); - lsmash_ifprintf( fp, indent, "OCRLength = %"PRIu8"\n", slcd->OCRLength ); - lsmash_ifprintf( fp, indent, "AU_Length = %"PRIu8"\n", slcd->AU_Length ); - lsmash_ifprintf( fp, indent, "instantBitrateLength = %"PRIu8"\n", slcd->instantBitrateLength ); - lsmash_ifprintf( fp, indent, "degradationPriorityLength = %"PRIu8"\n", slcd->degradationPriorityLength ); - lsmash_ifprintf( fp, indent, "AU_seqNumLength = %"PRIu8"\n", slcd->AU_seqNumLength ); - lsmash_ifprintf( fp, indent, "packetSeqNumLength = %"PRIu8"\n", slcd->packetSeqNumLength ); - lsmash_ifprintf( fp, indent, "reserved = 0x%01"PRIx8"\n", slcd->reserved ); - } - if( slcd->durationFlag ) - { - lsmash_ifprintf( fp, indent, "timeScale = %"PRIu32"\n", slcd->timeScale ); - lsmash_ifprintf( fp, indent, "accessUnitDuration = %"PRIu16"\n", slcd->accessUnitDuration ); - lsmash_ifprintf( fp, indent, "compositionUnitDuration = %"PRIu16"\n", slcd->compositionUnitDuration ); - } - if( !slcd->useTimeStampsFlag ) - { - lsmash_ifprintf( fp, indent, "startDecodingTimeStamp = %"PRIu64"\n", slcd->startDecodingTimeStamp ); - lsmash_ifprintf( fp, indent, "startCompositionTimeStamp = %"PRIu64"\n", slcd->startCompositionTimeStamp ); - } -} - -static void mp4sys_print_ES_Descriptor( FILE *fp, mp4sys_ES_Descriptor_t *esd, int indent ) -{ - mp4sys_print_descriptor_header( fp, &esd->header, indent++ ); - lsmash_ifprintf( fp, indent, "ES_ID = %"PRIu16"\n", esd->ES_ID ); - lsmash_ifprintf( fp, indent, "streamDependenceFlag = %"PRIu8"\n", esd->streamDependenceFlag ); - lsmash_ifprintf( fp, indent, "URL_Flag = %"PRIu8"\n", esd->URL_Flag ); - lsmash_ifprintf( fp, indent, "OCRstreamFlag = %"PRIu8"\n", esd->OCRstreamFlag ); - lsmash_ifprintf( fp, indent, "streamPriority = %"PRIu8"\n", esd->streamPriority ); - if( esd->streamDependenceFlag ) - lsmash_ifprintf( fp, indent, "dependsOn_ES_ID = %"PRIu16"\n", esd->dependsOn_ES_ID ); - if( esd->URL_Flag ) - { - lsmash_ifprintf( fp, indent, "URLlength = %"PRIu8"\n", esd->URLlength ); - char URLstring[256] = { 0 }; - memcpy( URLstring, esd->URLstring, esd->URLlength ); - lsmash_ifprintf( fp, indent, "URLlength = %s\n", URLstring ); - } - if( esd->OCRstreamFlag ) - lsmash_ifprintf( fp, indent, "OCR_ES_Id = %"PRIu16"\n", esd->OCR_ES_Id ); - mp4sys_print_DecoderConfigDescriptor( fp, esd->decConfigDescr, indent ); - mp4sys_print_SLConfigDescriptor( fp, esd->slConfigDescr, indent ); -} - -int mp4sys_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - assert( fp && file && box && !(box->manager & LSMASH_BINARY_CODED_BOX) ); - isom_esds_t *esds = (isom_esds_t *)box; - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: Elemental Stream Descriptor Box]\n", isom_4cc2str( esds->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", esds->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", esds->size ); - lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", esds->version ); - lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", esds->flags & 0x00ffffff ); - mp4sys_print_ES_Descriptor( fp, esds->ES, indent ); - return 0; -} -#endif /* LSMASH_DEMUXER_ENABLED */ - -static void mp4sys_get_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t* header ) -{ - header->tag = lsmash_bs_get_byte( bs ); - uint8_t temp = lsmash_bs_get_byte( bs ); - int nextByte = temp & 0x80; - uint32_t sizeOfInstance = temp & 0x7F; - while( nextByte ) - { - temp = lsmash_bs_get_byte( bs ); - nextByte = temp & 0x80; - sizeOfInstance = (sizeOfInstance << 7) | (temp & 0x7F); - } - header->size = sizeOfInstance; -} - -static int mp4sys_get_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) -{ - mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderSpecificInfo_t) ); - if( !dsi ) - return -1; - esd->decConfigDescr->decSpecificInfo = dsi; - mp4sys_get_descriptor_header( bs, &dsi->header ); - if( dsi->header.size ) - { - dsi->data = lsmash_bs_get_bytes( bs, dsi->header.size ); - if( !dsi->data ) - { - mp4sys_remove_DecoderSpecificInfo( esd ); - return -1; - } - } - return 0; -} - -static int mp4sys_get_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) -{ - mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderConfigDescriptor_t) ); - if( !dcd ) - return -1; - esd->decConfigDescr = dcd; - mp4sys_get_descriptor_header( bs, &dcd->header ); - dcd->objectTypeIndication = lsmash_bs_get_byte( bs ); - uint8_t temp = lsmash_bs_get_byte( bs ); - dcd->streamType = (temp >> 2) & 0x3F; - dcd->upStream = (temp >> 1) & 0x01; - dcd->reserved = temp & 0x01; - dcd->bufferSizeDB = lsmash_bs_get_be24( bs ); - dcd->maxBitrate = lsmash_bs_get_be32( bs ); - dcd->avgBitrate = lsmash_bs_get_be32( bs ); - if( dcd->header.size > 13 - && mp4sys_get_DecoderSpecificInfo( bs, esd ) ) - { - mp4sys_remove_DecoderConfigDescriptor( esd ); - return -1; - } - return 0; -} - -static int mp4sys_get_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) -{ - mp4sys_SLConfigDescriptor_t *slcd = (mp4sys_SLConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_SLConfigDescriptor_t) ); - if( !slcd ) - return -1; - esd->slConfigDescr = slcd; - mp4sys_get_descriptor_header( bs, &slcd->header ); - slcd->predefined = lsmash_bs_get_byte( bs ); - if( slcd->predefined == 0x00 ) - { - uint8_t temp8 = lsmash_bs_get_byte( bs ); - slcd->useAccessUnitStartFlag = (temp8 >> 7) & 0x01; - slcd->useAccessUnitEndFlag = (temp8 >> 6) & 0x01; - slcd->useRandomAccessPointFlag = (temp8 >> 5) & 0x01; - slcd->hasRandomAccessUnitsOnlyFlag = (temp8 >> 4) & 0x01; - slcd->usePaddingFlag = (temp8 >> 3) & 0x01; - slcd->useTimeStampsFlag = (temp8 >> 2) & 0x01; - slcd->useIdleFlag = (temp8 >> 1) & 0x01; - slcd->durationFlag = temp8 & 0x01; - slcd->timeStampResolution = lsmash_bs_get_be32( bs ); - slcd->OCRResolution = lsmash_bs_get_be32( bs ); - slcd->timeStampLength = lsmash_bs_get_byte( bs ); - slcd->OCRLength = lsmash_bs_get_byte( bs ); - slcd->AU_Length = lsmash_bs_get_byte( bs ); - slcd->instantBitrateLength = lsmash_bs_get_byte( bs ); - uint16_t temp16 = lsmash_bs_get_be16( bs ); - slcd->degradationPriorityLength = (temp16 >> 12) & 0x0F; - slcd->AU_seqNumLength = (temp16 >> 7) & 0x1F; - slcd->packetSeqNumLength = (temp16 >> 2) & 0x1F; - slcd->reserved = temp16 & 0x03; - } - else if( slcd->predefined == 0x01 ) - { - slcd->timeStampResolution = 1000; - slcd->timeStampLength = 32; - } - else if( slcd->predefined == 0x02 ) - slcd->useTimeStampsFlag = 1; - if( slcd->durationFlag ) - { - slcd->timeScale = lsmash_bs_get_be32( bs ); - slcd->accessUnitDuration = lsmash_bs_get_be16( bs ); - slcd->compositionUnitDuration = lsmash_bs_get_be16( bs ); - } - if( !slcd->useTimeStampsFlag ) - { - lsmash_bits_t *bits = lsmash_bits_create( bs ); - if( !bits ) - return -1; - slcd->startDecodingTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength ); - slcd->startCompositionTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength ); - lsmash_bits_cleanup( bits ); - } - return 0; -} - -mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs ) -{ - mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_ES_Descriptor_t) ); - if( !esd ) - return NULL; - mp4sys_get_descriptor_header( bs, &esd->header ); - esd->ES_ID = lsmash_bs_get_be16( bs ); - uint8_t temp = lsmash_bs_get_byte( bs ); - esd->streamDependenceFlag = (temp >> 7) & 0x01; - esd->URL_Flag = (temp >> 6) & 0x01; - esd->OCRstreamFlag = (temp >> 5) & 0x01; - esd->streamPriority = temp & 0x1F; - if( esd->streamDependenceFlag ) - esd->dependsOn_ES_ID = lsmash_bs_get_be16( bs ); - if( esd->URL_Flag ) - { - esd->URLlength = lsmash_bs_get_byte( bs ); - for( uint8_t i = 0; i < esd->URLlength; i++ ) - esd->URLstring[i] = lsmash_bs_get_byte( bs ); - } - if( esd->OCRstreamFlag ) - esd->OCR_ES_Id = lsmash_bs_get_be16( bs ); - if( mp4sys_get_DecoderConfigDescriptor( bs, esd ) - || mp4sys_get_SLConfigDescriptor( bs, esd ) ) - { - mp4sys_remove_ES_Descriptor( esd ); - return NULL; - } - return esd; -} - -static uint8_t *mp4sys_export_DecoderSpecificInfo( mp4sys_ES_Descriptor_t *esd, uint32_t *dsi_payload_length ) -{ - if( !esd || !esd->decConfigDescr || !esd->decConfigDescr->decSpecificInfo ) - return NULL; - mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)esd->decConfigDescr->decSpecificInfo; - uint8_t *dsi_payload = NULL; - /* DecoderSpecificInfo can be absent. */ - if( dsi->header.size ) - { - dsi_payload = lsmash_memdup( dsi->data, dsi->header.size ); - if( !dsi_payload ) - return NULL; - } - if( dsi_payload_length ) - *dsi_payload_length = dsi->header.size; - return dsi_payload; -} - -/* Sumamry is needed to decide ProfileLevelIndication. - * Currently, support audio's only. */ -int mp4sys_setup_summary_from_DecoderSpecificInfo( lsmash_audio_summary_t *summary, mp4sys_ES_Descriptor_t *esd ) -{ - uint32_t dsi_payload_length = UINT32_MAX; /* arbitrary */ - uint8_t *dsi_payload = mp4sys_export_DecoderSpecificInfo( esd, &dsi_payload_length ); - if( !dsi_payload && dsi_payload_length ) - return -1; - if( dsi_payload_length && mp4a_setup_summary_from_AudioSpecificConfig( summary, dsi_payload, dsi_payload_length ) ) - { - lsmash_free( dsi_payload ); - return -1; - } - return 0; -} - -/**** 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; -} - -int lsmash_set_mp4sys_decoder_specific_info( lsmash_mp4sys_decoder_parameters_t *param, uint8_t *payload, uint32_t payload_length ) -{ - if( !param || !payload || payload_length == 0 ) - return -1; - if( !param->dsi ) - { - param->dsi = lsmash_malloc_zero( sizeof(lsmash_mp4sys_decoder_specific_info_t) ); - if( !param->dsi ) - return -1; - } - else - { - if( param->dsi->payload ) - { - lsmash_free( param->dsi->payload ); - param->dsi->payload = NULL; - } - param->dsi->payload_length = 0; - } - param->dsi->payload = lsmash_memdup( payload, payload_length ); - if( !param->dsi->payload ) - return -1; - param->dsi->payload_length = payload_length; - return 0; -} - -void lsmash_destroy_mp4sys_decoder_specific_info( lsmash_mp4sys_decoder_parameters_t *param ) -{ - if( !param || !param->dsi ) - return; - if( param->dsi->payload ) - lsmash_free( param->dsi->payload ); - lsmash_free( param->dsi ); - param->dsi = NULL; -} - -void mp4sys_destruct_decoder_config( void *data ) -{ - if( !data ) - return; - lsmash_destroy_mp4sys_decoder_specific_info( data ); - lsmash_free( data ); -} - -uint8_t *lsmash_create_mp4sys_decoder_config( lsmash_mp4sys_decoder_parameters_t *param, uint32_t *data_length ) -{ - if( !param || !data_length ) - return NULL; - mp4sys_ES_Descriptor_params_t esd_param = { 0 }; - esd_param.ES_ID = 0; /* Within sample description, ES_ID is stored as 0. */ - esd_param.objectTypeIndication = param->objectTypeIndication; - esd_param.streamType = param->streamType; - esd_param.bufferSizeDB = param->bufferSizeDB; - esd_param.maxBitrate = param->maxBitrate; - esd_param.avgBitrate = param->avgBitrate; - if( param->dsi && param->dsi->payload && param->dsi->payload_length ) - { - esd_param.dsi_payload = param->dsi->payload; - esd_param.dsi_payload_length = param->dsi->payload_length; - } - mp4sys_ES_Descriptor_t *esd = mp4sys_setup_ES_Descriptor( &esd_param ); - if( !esd ) - return NULL; - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - { - mp4sys_remove_ES_Descriptor( esd ); - return NULL; - } - lsmash_bs_put_be32( bs, ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ES_Descriptor_size( esd ) ); - lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_ESDS.fourcc ); - lsmash_bs_put_be32( bs, 0 ); - mp4sys_put_ES_Descriptor( bs, esd ); - mp4sys_remove_ES_Descriptor( esd ); - uint8_t *data = lsmash_bs_export_data( bs, data_length ); - lsmash_bs_cleanup( bs ); - if( !data ) - return NULL; - /* Update box size. */ - data[0] = ((*data_length) >> 24) & 0xff; - data[1] = ((*data_length) >> 16) & 0xff; - data[2] = ((*data_length) >> 8) & 0xff; - data[3] = (*data_length) & 0xff; - return data; -} - -int mp4sys_construct_decoder_config( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < ISOM_FULLBOX_COMMON_SIZE + 23 ) - return -1; - lsmash_mp4sys_decoder_parameters_t *param = (lsmash_mp4sys_decoder_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - data += 4; /* Skip version and flags. */ - lsmash_bs_t *bs = lsmash_bs_create(); - if( !bs ) - return -1; - if( lsmash_bs_import_data( bs, data, src->size - (data - src->data.unstructured) ) ) - { - lsmash_bs_cleanup( bs ); - return -1; - } - mp4sys_ES_Descriptor_t *esd = mp4sys_get_ES_Descriptor( bs ); - lsmash_bs_cleanup( bs ); - if( !esd || !esd->decConfigDescr ) - return -1; - mp4sys_DecoderConfigDescriptor_t *dcd = esd->decConfigDescr; - param->objectTypeIndication = dcd->objectTypeIndication; - param->streamType = dcd->streamType; - param->bufferSizeDB = dcd->bufferSizeDB; - param->maxBitrate = dcd->maxBitrate; - param->avgBitrate = dcd->avgBitrate; - mp4sys_DecoderSpecificInfo_t *dsi = dcd->decSpecificInfo; - if( dsi && dsi->header.size && dsi->data - && lsmash_set_mp4sys_decoder_specific_info( param, dsi->data, dsi->header.size ) ) - { - mp4sys_remove_ES_Descriptor( esd ); - return -1; - } - mp4sys_remove_ES_Descriptor( esd ); - return 0; -} - -int mp4sys_copy_decoder_config( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); - assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); - lsmash_mp4sys_decoder_parameters_t *src_data = (lsmash_mp4sys_decoder_parameters_t *)src->data.structured; - lsmash_mp4sys_decoder_parameters_t *dst_data = (lsmash_mp4sys_decoder_parameters_t *)dst->data.structured; - lsmash_destroy_mp4sys_decoder_specific_info( dst_data ); - *dst_data = *src_data; - dst_data->dsi = NULL; - if( !src_data->dsi || !src_data->dsi->payload || src_data->dsi->payload_length == 0 ) - return 0; - return lsmash_set_mp4sys_decoder_specific_info( dst_data, src_data->dsi->payload, src_data->dsi->payload_length ); -} - -lsmash_mp4sys_object_type_indication lsmash_mp4sys_get_object_type_indication( lsmash_summary_t *summary ) -{ - if( !summary ) - return MP4SYS_OBJECT_TYPE_Forbidden; - lsmash_codec_specific_t *orig = isom_get_codec_specific( summary->opaque, LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG ); - if( !orig ) - return MP4SYS_OBJECT_TYPE_Forbidden; - /* Found decoder configuration. - * Let's get objectTypeIndication. */ - lsmash_mp4sys_object_type_indication objectTypeIndication; - if( orig->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ) - objectTypeIndication = ((lsmash_mp4sys_decoder_parameters_t *)orig->data.structured)->objectTypeIndication; - else - { - lsmash_codec_specific_t *conv = lsmash_convert_codec_specific_format( orig, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( !conv ) - return MP4SYS_OBJECT_TYPE_Forbidden; - objectTypeIndication = ((lsmash_mp4sys_decoder_parameters_t *)conv->data.structured)->objectTypeIndication; - lsmash_destroy_codec_specific_data( conv ); - } - return objectTypeIndication; -} - -int lsmash_get_mp4sys_decoder_specific_info( lsmash_mp4sys_decoder_parameters_t *param, uint8_t **payload, uint32_t *payload_length ) -{ - if( !param || !payload || !payload_length ) - return -1; - if( !param->dsi || !param->dsi->payload || param->dsi->payload_length == 0 ) - { - *payload = NULL; - *payload_length = 0; - return 0; - } - uint8_t *temp = lsmash_memdup( param->dsi->payload, param->dsi->payload_length ); - if( !temp ) - return -1; - *payload = temp; - *payload_length = param->dsi->payload_length; - return 0; -} diff -Nru l-smash-1.9.1/mp4sys.h l-smash-2.3.0/mp4sys.h --- l-smash-1.9.1/mp4sys.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/mp4sys.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,211 +0,0 @@ -/***************************************************************************** - * mp4sys.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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:2010 */ - /* 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; - -struct lsmash_mp4sys_decoder_specific_info_tag -{ - uint8_t *payload; - uint32_t payload_length; -}; - -#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 of the ES Descriptor is stored as 0 when the ES Descriptor is built into sample descriptions in MP4 file format - * since the lower 16 bits of the track_ID is used, instead of ES_ID, for the identifier of the elemental stream within the track. */ -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_put_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ); -int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ); -int mp4sys_write_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od ); - -int mp4sys_update_DecoderConfigDescriptor( - mp4sys_ES_Descriptor_t* esd, - uint32_t bufferSizeDB, - uint32_t maxBitrate, - uint32_t avgBitrate -); - -#ifdef LSMASH_DEMUXER_ENABLED -mp4sys_ES_Descriptor_t *mp4sys_duplicate_ES_Descriptor( mp4sys_ES_Descriptor_t *src ); -#endif - -mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs ); - -int mp4sys_setup_summary_from_DecoderSpecificInfo( lsmash_audio_summary_t *summary, mp4sys_ES_Descriptor_t *esd ); - -/* 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 -Nru l-smash-1.9.1/muxer.c l-smash-2.3.0/muxer.c --- l-smash-1.9.1/muxer.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/muxer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1224 +0,0 @@ -/***************************************************************************** - * muxer.c: - ***************************************************************************** - * Copyright (C) 2010-2014 L-SMASH project - * - * Authors: Yusuke Nakamura - * 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 -#include -#include -#include -#include - -#include "lsmash.h" -#include "importer.h" -#include "cli.h" - -#include "config.h" - -#define MAX_NUM_OF_BRANDS 50 -#define MAX_NUM_OF_INPUTS 10 -#define MAX_NUM_OF_TRACKS 1 - -typedef struct -{ - char *album_name; - char *artist; - char *comment; - char *release_date; - char *encoder; - char *genre; - char *lyrics; - char *title; - char *composer; - char *album_artist; - char *copyright; - char *description; - char *grouping; - uint32_t beats_per_minute; -} itunes_metadata_t; - -typedef struct -{ - int help; - int version; - int isom; - int isom_version; - int itunes_movie; - int qtff; - int brand_3gx; - int optimize_pd; - int timeline_shift; - uint32_t interleave; - uint32_t num_of_brands; - uint32_t brands[MAX_NUM_OF_BRANDS]; - uint32_t major_brand; - uint32_t minor_version; - uint32_t num_of_inputs; - uint32_t chap_track; - char *chap_file; - int add_bom_to_chpl; - char *copyright_notice; - uint16_t copyright_language; - itunes_metadata_t itunes_metadata; - uint16_t default_language; -} option_t; - -typedef struct -{ - char *raws; - int disable; - int sbr; - int user_fps; - uint32_t fps_num; - uint32_t fps_den; - uint32_t encoder_delay; - int16_t alternate_group; - uint16_t ISO_language; - uint16_t copyright_language; - char *copyright_notice; - char *handler_name; -} input_track_option_t; - -typedef struct -{ - lsmash_summary_t *summary; - input_track_option_t opt; - int active; -} input_track_t; - -typedef struct -{ - char *whole_track_option; - int num_of_track_delimiters; -} input_option_t; - -typedef struct -{ - input_option_t opt; - char *file_name; - importer_t *importer; - input_track_t track[MAX_NUM_OF_TRACKS]; - uint32_t num_of_tracks; - uint32_t num_of_active_tracks; - uint32_t current_track_number; -} input_t; - -typedef struct -{ - lsmash_summary_t *summary; - lsmash_sample_t *sample; - int active; - uint32_t track_ID; - uint32_t timescale; - uint32_t timebase; - uint32_t sample_entry; - uint32_t current_sample_number; - uint32_t ctd_shift; - uint32_t priming_samples; - uint32_t last_delta; - uint64_t prev_dts; - int64_t start_offset; - double dts; -} output_track_t; - -typedef struct -{ - output_track_t *track; - uint32_t num_of_tracks; - uint32_t current_track_number; -} output_movie_t; - -typedef struct -{ - char *name; - lsmash_file_t *fh; - lsmash_file_parameters_t param; - output_movie_t movie; -} output_file_t; - -typedef struct -{ - lsmash_root_t *root; - output_file_t file; -} output_t; - -typedef struct -{ - option_t opt; - output_t output; - input_t input[MAX_NUM_OF_INPUTS]; - uint32_t num_of_inputs; -} muxer_t; - -static void cleanup_muxer( muxer_t *muxer ) -{ - if( !muxer ) - return; - output_t *output = &muxer->output; - lsmash_close_file( &output->file.param ); - lsmash_destroy_root( output->root ); - if( output->file.movie.track ) - { - for( uint32_t i = 0; i < output->file.movie.num_of_tracks; i++ ) - { - output_track_t *out_track = &output->file.movie.track[i]; - lsmash_delete_sample( out_track->sample ); - } - lsmash_free( output->file.movie.track ); - } - for( uint32_t i = 0; i < muxer->num_of_inputs; i++ ) - { - input_t *input = &muxer->input[i]; - lsmash_importer_close( input->importer ); - for( uint32_t j = 0; j < input->num_of_tracks; j++ ) - lsmash_cleanup_summary( input->track[j].summary ); - } -} - -#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) -#define REFRESH_CONSOLE eprintf( " \r" ) - -static int muxer_error( muxer_t *muxer, const char *message, ... ) -{ - cleanup_muxer( muxer ); - REFRESH_CONSOLE; - eprintf( "Error: " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -static int error_message( const char *message, ... ) -{ - REFRESH_CONSOLE; - eprintf( "Error: " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -static void display_version( void ) -{ - eprintf( "\n" - "L-SMASH isom/mov multiplexer rev%s %s\n" - "Built on %s %s\n" - "Copyright (C) 2010-2014 L-SMASH project\n", - LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); -} - -static void display_help( void ) -{ - display_version(); - eprintf( "\n" - "Usage: muxer [global_options] -i input1 [-i input2 -i input3 ...] -o output\n" - "Global options:\n" - " --help Display help\n" - " --version Display version information\n" - " --optimize-pd Optimize for progressive download\n" - " --interleave Specify time interval for media interleaving in milliseconds\n" - " --file-format Specify output file format\n" - " Multiple file format can be specified by comma separators\n" - " The first is applied as the best used one\n" - " --isom-version Specify maximum compatible ISO Base Media version\n" - " --shift-timeline Enable composition to decode timeline shift\n" - " --chapter Set chapters from the file.\n" - " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n" - " in the chapter list. (experimental)\n" - " --chapter-track Set which track the chapter applies to.\n" - " This option takes effect only when reference\n" - " chapter is available.\n" - " If this option is not used, it defaults to 1.\n" - " --copyright-notice Specify copyright notice with or without language (latter string)\n" - " is or /\n" - " --language Specify the default language for all the output tracks.\n" - " This option is overridden by the track options.\n" - "Output file formats:\n" - " mp4, mov, 3gp, 3g2, m4a, m4v\n" - "\n" - "Track options:\n" - " disable Disable this track\n" - " fps= Specify video framerate\n" - " is or /\n" - " language= Specify media language\n" - " alternate-group= Specify alternate group\n" - " encoder-delay= Represent audio encoder delay (priming samples) explicitly\n" - " copyright= Specify copyright notice with or without language (latter string)\n" - " is or /\n" - " handler= Set media handler name\n" - " sbr Enable backward-compatible SBR explicit signaling mode\n" - "How to use track options:\n" - " -i input?[track_option1],[track_option2]...\n" - "\n" - "iTunes Metadata:\n" - " --album-name Album name\n" - " --artist Artist\n" - " --comment User comment\n" - " --release-date Release date (YYYY-MM-DD)\n" - " --encoder Person or company that encoded the recording\n" - " --genre Genre\n" - " --lyrics Lyrics\n" - " --title Title or song name\n" - " --composer Composer\n" - " --album-artist Artist for the whole album (if different than the individual tracks)\n" - " --copyright Copyright\n" - " --description Description\n" - " --grouping Grouping\n" - " --tempo Beats per minute\n" ); -} - -static int muxer_usage_error( void ) -{ - display_help(); - return -1; -} - -#define MUXER_ERR( ... ) muxer_error( &muxer, __VA_ARGS__ ) -#define ERROR_MSG( ... ) error_message( __VA_ARGS__ ) -#define MUXER_USAGE_ERR() muxer_usage_error(); - -static int add_brand( option_t *opt, uint32_t brand ) -{ - if( opt->num_of_brands > MAX_NUM_OF_BRANDS ) - return -1; - /* Avoid duplication. */ - for( uint32_t i = 0; i < opt->num_of_brands; i++ ) - if( opt->brands[i] == brand ) - return -2; - opt->brands[opt->num_of_brands ++] = brand; - return 0; -} - -static int setup_isom_version( option_t *opt ) -{ - add_brand( opt, ISOM_BRAND_TYPE_ISOM ); - if( opt->isom_version > 6 ) - return ERROR_MSG( "unknown ISO Base Media version.\n" ); -#define SET_ISOM_VERSION( version ) \ - if( opt->isom_version >= version ) \ - add_brand( opt, ISOM_BRAND_TYPE_ISO##version ) - SET_ISOM_VERSION( 2 ); - SET_ISOM_VERSION( 3 ); - SET_ISOM_VERSION( 4 ); - SET_ISOM_VERSION( 5 ); - SET_ISOM_VERSION( 6 ); -#undef SET_ISOM_VERSION - return 0; -} - -static int decide_brands( option_t *opt ) -{ - if( opt->num_of_brands == 0 ) - { - /* default file format */ - opt->major_brand = ISOM_BRAND_TYPE_MP42; - opt->minor_version = 0x00000000; - add_brand( opt, ISOM_BRAND_TYPE_MP42 ); - add_brand( opt, ISOM_BRAND_TYPE_MP41 ); - add_brand( opt, ISOM_BRAND_TYPE_ISOM ); - opt->isom = 1; - eprintf( "MP4 muxing mode\n" ); - return setup_isom_version( opt ); - } - opt->major_brand = opt->brands[0]; /* Pick the first brand as major brand. */ - for( uint32_t i = 0; i < opt->num_of_brands; i++ ) - { - switch( opt->brands[i] ) - { - case ISOM_BRAND_TYPE_3GP6 : - /* When being compatible with 3gp6, also compatible with 3g2a. */ - add_brand( opt, ISOM_BRAND_TYPE_3G2A ); - opt->brand_3gx = 1; - break; - case ISOM_BRAND_TYPE_3G2A : - opt->brand_3gx = 2; - break; - case ISOM_BRAND_TYPE_QT : - opt->qtff = 1; - break; - case ISOM_BRAND_TYPE_M4A : - case ISOM_BRAND_TYPE_M4V : - opt->itunes_movie = 1; - case ISOM_BRAND_TYPE_MP42 : - add_brand( opt, ISOM_BRAND_TYPE_MP42 ); - add_brand( opt, ISOM_BRAND_TYPE_MP41 ); - break; - default : - break; - } - if( opt->brands[i] != ISOM_BRAND_TYPE_QT ) - opt->isom = 1; - } - switch( opt->major_brand ) - { - case ISOM_BRAND_TYPE_MP42 : - opt->minor_version = 0x00000000; - eprintf( "MP4 muxing mode\n" ); - break; - case ISOM_BRAND_TYPE_M4A : - case ISOM_BRAND_TYPE_M4V : - opt->minor_version = 0x00000000; - eprintf( "iTunes MP4 muxing mode\n" ); - break; - case ISOM_BRAND_TYPE_3GP6 : - opt->minor_version = 0x00000000; /* means, 3gp(3gp6) 6.0.0 : "6" is not included in minor_version. */ - eprintf( "3GPP muxing mode\n" ); - break; - case ISOM_BRAND_TYPE_3G2A : - opt->minor_version = 0x00010000; /* means, 3g2(3g2a) 1.0.0 : a == 1 */ - eprintf( "3GPP2 muxing mode\n" ); - break; - case ISOM_BRAND_TYPE_QT : - opt->minor_version = 0x00000000; /* We don't know exact version of the spec to use QTFF features. */ - eprintf( "QuickTime file format muxing mode\n" ); - break; - default : - break; - } - /* Set up ISO Base Media version. */ - if( opt->isom ) - setup_isom_version( opt ); - if( opt->num_of_brands > MAX_NUM_OF_BRANDS ) - return ERROR_MSG( "exceed the maximum number of brands we can deal with.\n" ); - return 0; -} - -static int parse_global_options( int argc, char **argv, muxer_t *muxer ) -{ - if ( argc < 2 ) - return -1; - else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) - { - muxer->opt.help = 1; - return 0; - } - else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) - { - muxer->opt.version = 1; - return 0; - } - else if( argc < 5 ) - return -1; - uint32_t i = 1; - option_t *opt = &muxer->opt; - opt->chap_track = 1; - opt->add_bom_to_chpl = 0; - while( argc > i && *argv[i] == '-' ) - { -#define CHECK_NEXT_ARG if( argc == ++i ) return -1 - if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) - { - CHECK_NEXT_ARG; - if( opt->num_of_inputs + 1 > MAX_NUM_OF_INPUTS ) - return ERROR_MSG( "exceed the maximum number of input files.\n" ); - input_t *input = &muxer->input[opt->num_of_inputs]; - input_option_t *input_movie_opt = &input->opt; - char *p = argv[i]; - while( *p ) - input_movie_opt->num_of_track_delimiters += (*p++ == '?'); - if( input_movie_opt->num_of_track_delimiters > MAX_NUM_OF_TRACKS ) - return ERROR_MSG( "you specified options to exceed the maximum number of tracks per input files.\n" ); - input->file_name = strtok( argv[i], "?" ); - input_movie_opt->whole_track_option = strtok( NULL, "" ); - if( input_movie_opt->num_of_track_delimiters ) - { - input_track_option_t *track_opt = &input->track[0].opt; - track_opt->raws = strtok( input_movie_opt->whole_track_option, "?" ); -#if (MAX_NUM_OF_TRACKS - 1) - for( uint32_t j = 1; j < input_movie_opt->num_of_track_delimiters; j++ ) - { - track_opt = &input->track[j].opt; - track_opt->raws = strtok( NULL, "?" ); - } -#endif - } - ++ opt->num_of_inputs; - } - else if( !strcasecmp( argv[i], "-o" ) || !strcasecmp( argv[i], "--output" ) ) - { - CHECK_NEXT_ARG; - muxer->output.file.name = argv[i]; - } - else if( !strcasecmp( argv[i], "--optimize-pd" ) ) - opt->optimize_pd = 1; - else if( !strcasecmp( argv[i], "--interleave" ) ) - { - CHECK_NEXT_ARG; - if( opt->interleave ) - return ERROR_MSG( "you specified --interleave twice.\n" ); - opt->interleave = atoi( argv[i] ); - } - else if( !strcasecmp( argv[i], "--file-format" ) ) - { - CHECK_NEXT_ARG; - static const struct - { - uint32_t brand_4cc; - char *file_format; - } file_format_list[] - = { - { ISOM_BRAND_TYPE_MP42, "mp4" }, - { ISOM_BRAND_TYPE_QT, "mov" }, - { ISOM_BRAND_TYPE_3GP6, "3gp" }, - { ISOM_BRAND_TYPE_3G2A, "3g2" }, - { ISOM_BRAND_TYPE_M4A, "m4a" }, - { ISOM_BRAND_TYPE_M4V, "m4v" }, - { 0, NULL } - }; - char *file_format = NULL; - while( (file_format = strtok( file_format ? NULL : argv[i], "," )) != NULL ) - { - int j; - for( j = 0; file_format_list[j].file_format; j++ ) - if( !strcmp( file_format, file_format_list[j].file_format ) ) - { - int ret = add_brand( opt, file_format_list[j].brand_4cc ); - if( ret == -2 ) - return ERROR_MSG( "you specified same output file format twice.\n" ); - else if( ret == -1 ) - return ERROR_MSG( "exceed the maximum number of brands we can deal with.\n" ); - break; - } - if( !file_format_list[j].file_format ) - return MUXER_USAGE_ERR(); - } - } - else if( !strcasecmp( argv[i], "--isom-version" ) ) - { - CHECK_NEXT_ARG; - if( opt->isom_version ) - return ERROR_MSG( "you specified --isom-version twice.\n" ); - opt->isom_version = atoi( argv[i] ); - } - else if( !strcasecmp( argv[i], "--shift-timeline" ) ) - opt->timeline_shift = 1; - else if( !strcasecmp( argv[i], "--chapter" ) ) - { - CHECK_NEXT_ARG; - opt->chap_file = argv[i]; - } - else if( !strcasecmp( argv[i], "--chapter-track" ) ) - { - CHECK_NEXT_ARG; - opt->chap_track = atoi( argv[i] ); - if( !opt->chap_track ) - return ERROR_MSG( "%s is an invalid track number.\n", argv[i] ); - } - else if( !strcasecmp( argv[i], "--chpl-with-bom" ) ) - opt->add_bom_to_chpl = 1; - else if( !strcasecmp( argv[i], "--copyright-notice" ) ) - { - CHECK_NEXT_ARG; - if( opt->copyright_notice ) - return ERROR_MSG( "you specified --copyright-notice twice.\n" ); - opt->copyright_notice = argv[i]; - char *language = opt->copyright_notice; - while( *language ) - { - if( *language == '/' ) - { - *language++ = '\0'; - break; - } - ++language; - } - opt->copyright_language = language ? lsmash_pack_iso_language( language ) : ISOM_LANGUAGE_CODE_UNDEFINED; - } - /* iTunes metadata */ -#define CHECK_ITUNES_METADATA_ARG_STRING( argument, value ) \ - else if( !strcasecmp( argv[i], "--"#argument ) ) \ - { \ - CHECK_NEXT_ARG; \ - if( opt->itunes_metadata.value ) \ - return ERROR_MSG( "you specified --"#argument" twice.\n" ); \ - opt->itunes_metadata.value = argv[i]; \ - } - CHECK_ITUNES_METADATA_ARG_STRING( album-name, album_name ) - CHECK_ITUNES_METADATA_ARG_STRING( artist, artist ) - CHECK_ITUNES_METADATA_ARG_STRING( comment, comment ) - CHECK_ITUNES_METADATA_ARG_STRING( release-date, release_date ) - CHECK_ITUNES_METADATA_ARG_STRING( encoder, encoder ) - CHECK_ITUNES_METADATA_ARG_STRING( genre, genre ) - CHECK_ITUNES_METADATA_ARG_STRING( lyrics, lyrics ) - CHECK_ITUNES_METADATA_ARG_STRING( title, title ) - CHECK_ITUNES_METADATA_ARG_STRING( composer, composer ) - CHECK_ITUNES_METADATA_ARG_STRING( album-artist, album_artist ) - CHECK_ITUNES_METADATA_ARG_STRING( copyright, copyright ) - CHECK_ITUNES_METADATA_ARG_STRING( description, description ) - CHECK_ITUNES_METADATA_ARG_STRING( grouping, grouping ) -#undef CHECK_ITUNES_METADATA_ARG_STRING - else if( !strcasecmp( argv[i], "--tempo" ) ) - { - CHECK_NEXT_ARG; - if( opt->itunes_metadata.beats_per_minute ) - return ERROR_MSG( "you specified --tempo twice.\n" ); - opt->itunes_metadata.beats_per_minute = atoi( argv[i] ); - } - else if( !strcasecmp( argv[i], "--language" ) ) - { - CHECK_NEXT_ARG; - opt->default_language = lsmash_pack_iso_language( argv[i] ); - } -#undef CHECK_NEXT_ARG - else - return ERROR_MSG( "you specified invalid option: %s.\n", argv[i] ); - ++i; - } - if( !muxer->output.file.name ) - return ERROR_MSG( "output file name is not specified.\n" ); - if( decide_brands( opt ) ) - return ERROR_MSG( "failed to set up output file format.\n" ); - if( opt->timeline_shift && !opt->qtff && opt->isom_version < 4 ) - return ERROR_MSG( "timeline shift requires --file-format mov, or --isom-version 4 or later.\n" ); - muxer->num_of_inputs = opt->num_of_inputs; - return 0; -} - -static int parse_track_options( input_t *input ) -{ - for( input->current_track_number = 1; - input->current_track_number <= input->num_of_tracks; - input->current_track_number ++ ) - { - input_track_t *in_track = &input->track[input->current_track_number - 1]; - input_track_option_t *track_opt = &in_track->opt; - if( track_opt->raws == NULL ) - break; -#if 0 - if( !strchr( track_opt->raws, ':' ) - || strchr( track_opt->raws, ':' ) == track_opt->raws ) - return ERROR_MSG( "track number is not specified in %s\n", track_opt->raws ); - if( strchr( track_opt->raws, ':' ) != strrchr( track_opt->raws, ':' ) ) - return ERROR_MSG( "multiple colons inside one track option in %s.\n", track_opt->raws ); - uint32_t track_number = atoi( strtok( track_opt->raws, ":" ) ); - if( track_number == 0 || track_number > MAX_NUM_OF_TRACKS ) - return ERROR_MSG( "%s is an invalid track number %"PRIu32".\n", strtok( track_opt->raws, ":" ), track_number ); - in_track = &input->track[track_number - 1]; - track_opt = &in_track->opt; - char *track_option; - while( (track_option = strtok( NULL, "," )) != NULL ) -#else - char *track_option = NULL; - while( (track_option = strtok( track_option ? NULL : track_opt->raws, "," )) != NULL ) -#endif - { - if( strchr( track_option, '=' ) != strrchr( track_option, '=' ) ) - return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option ); - if( strstr( track_option, "disable" ) ) - track_opt->disable = 1; - else if( strstr( track_option, "alternate-group=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - track_opt->alternate_group = atoi( track_parameter ); - } - else if( strstr( track_option, "encoder-delay=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - track_opt->encoder_delay = atoi( track_parameter ); - } - else if( strstr( track_option, "language=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - track_opt->ISO_language = lsmash_pack_iso_language( track_parameter ); - } - else if( strstr( track_option, "fps=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - if( sscanf( track_parameter, "%"SCNu32"/%"SCNu32, &track_opt->fps_num, &track_opt->fps_den ) == 1 ) - { - track_opt->fps_num = atoi( track_parameter ); - track_opt->fps_den = 1; - } - track_opt->user_fps = 1; - } - else if( strstr( track_option, "copyright=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - track_opt->copyright_notice = track_parameter; - while( *track_parameter ) - { - if( *track_parameter == '/' ) - { - *track_parameter++ = '\0'; - break; - } - ++track_parameter; - } - track_opt->copyright_language = track_parameter ? lsmash_pack_iso_language( track_parameter ) : ISOM_LANGUAGE_CODE_UNDEFINED; - } - else if( strstr( track_option, "handler=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - track_opt->handler_name = track_parameter; - } - else if( strstr( track_option, "sbr" ) ) - track_opt->sbr = 1; - else - return ERROR_MSG( "unknown track option %s\n", track_option ); - } - } - return 0; -} - -static void display_codec_name( lsmash_codec_type_t codec_type, uint32_t track_number ) -{ -#define DISPLAY_CODEC_NAME( codec, codec_name ) \ - else if( lsmash_check_codec_type_identical( codec_type, codec ) ) \ - eprintf( "Track %"PRIu32": "#codec_name"\n", track_number ) - if( 0 ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_AVC1_VIDEO, H.264 Advanced Video Coding ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_HVC1_VIDEO, H.265 High Efficiency Video Coding (for testing purposes only) ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_VC_1_VIDEO, SMPTE VC-1 Advanced Profile ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_MP4A_AUDIO, MPEG-4 Audio ); - DISPLAY_CODEC_NAME( QT_CODEC_TYPE_MP4A_AUDIO, MPEG-4 Audio ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_AC_3_AUDIO, AC-3 ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_EC_3_AUDIO, Enhanced AC-3 ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSC_AUDIO, DTS ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSE_AUDIO, DTS LBR ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSH_AUDIO, DTS-HD ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSL_AUDIO, DTS-HD Lossless ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_SAWB_AUDIO, Wideband AMR voice ); - DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_SAMR_AUDIO, Narrowband AMR voice ); -#undef DISPLAY_CODEC_NAME -} - -static int open_input_files( muxer_t *muxer ) -{ - output_movie_t *out_movie = &muxer->output.file.movie; - option_t *opt = &muxer->opt; - for( uint32_t current_input_number = 1; current_input_number <= muxer->num_of_inputs; current_input_number++ ) - { - input_t *input = &muxer->input[current_input_number - 1]; - /* Initialize importer framework. */ - input->importer = lsmash_importer_open( input->file_name, "auto" ); - if( !input->importer ) - return ERROR_MSG( "failed to open input file.\n" ); - input->num_of_tracks = lsmash_importer_get_track_count( input->importer ); - if( input->num_of_tracks == 0 ) - return ERROR_MSG( "there is no valid track in input file.\n" ); - if( opt->default_language ) - for( int i = 0; i < input->num_of_tracks; i ++ ) - input->track[i].opt.ISO_language = opt->default_language; - /* Parse track options */ - if( parse_track_options( input ) ) - return ERROR_MSG( "failed to parse track options.\n" ); - /* Activate tracks by CODEC type. */ - for( input->current_track_number = 1; - input->current_track_number <= input->num_of_tracks; - input->current_track_number ++ ) - { - input_track_t *in_track = &input->track[input->current_track_number - 1]; - in_track->summary = lsmash_duplicate_summary( input->importer, input->current_track_number ); - if( !in_track->summary ) - return ERROR_MSG( "failed to get input summary.\n" ); - /* Check codec type. */ - lsmash_codec_type_t codec_type = in_track->summary->sample_type; - in_track->active = 1; - if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) ) - { - if( opt->isom ) - add_brand( opt, ISOM_BRAND_TYPE_AVC1 ); - } -#if 0 - else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) ) - { - if( !opt->isom && opt->qtff ) - return ERROR_MSG( "the input seems HEVC, at present available only for ISO Base Media file format.\n" ); - } -#endif - else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_VC_1_VIDEO ) ) - { - if( !opt->isom && opt->qtff ) - return ERROR_MSG( "the input seems VC-1, at present available only for ISO Base Media file format.\n" ); - } - else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) - || lsmash_check_codec_type_identical( codec_type, QT_CODEC_TYPE_MP4A_AUDIO ) ) - /* Do nothing. */; - else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_AC_3_AUDIO ) - || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) ) - { - if( !opt->isom && opt->qtff ) - return ERROR_MSG( "the input seems (Enhanced) AC-3, at present available only for ISO Base Media file format.\n" ); - add_brand( opt, ISOM_BRAND_TYPE_DBY1 ); - } - else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSC_AUDIO ) - || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSE_AUDIO ) - || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSH_AUDIO ) - || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) ) - { - if( !opt->isom && opt->qtff ) - return ERROR_MSG( "the input seems DTS(-HD) Audio, at present available only for ISO Base Media file format.\n" ); - } - else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_SAWB_AUDIO ) - || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_SAMR_AUDIO ) ) - { - if( !opt->brand_3gx ) - return ERROR_MSG( "the input seems AMR-NB/WB, available for 3GPP(2) file format.\n" ); - } - else - { - lsmash_cleanup_summary( in_track->summary ); - in_track->summary = NULL; - in_track->active = 0; - } - if( in_track->active ) - { - ++ input->num_of_active_tracks; - display_codec_name( codec_type, out_movie->num_of_tracks + input->num_of_active_tracks ); - } - } - out_movie->num_of_tracks += input->num_of_active_tracks; - } - if( out_movie->num_of_tracks == 0 ) - return ERROR_MSG( "there is no media that can be stored in output movie.\n" ); - return 0; -} - -static int set_itunes_metadata( output_t *output, option_t *opt ) -{ - if( !opt->itunes_movie ) - return 0; - itunes_metadata_t *metadata = &opt->itunes_metadata; -#define SET_ITUNES_METADATA( item, type, value ) \ - if( value \ - && lsmash_set_itunes_metadata( output->root, (lsmash_itunes_metadata_t){ item, ITUNES_METADATA_TYPE_NONE, { .type = value }, NULL, NULL } ) ) \ - return -1 - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ENCODING_TOOL, string, "L-SMASH" ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ALBUM_NAME, string, metadata->album_name ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ARTIST, string, metadata->artist ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_USER_COMMENT, string, metadata->comment ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_RELEASE_DATE, string, metadata->release_date ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ENCODED_BY, string, metadata->encoder ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_USER_GENRE, string, metadata->genre ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_LYRICS, string, metadata->lyrics ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_TITLE, string, metadata->title ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_COMPOSER, string, metadata->composer ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ALBUM_ARTIST, string, metadata->album_artist ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_COPYRIGHT, string, metadata->copyright ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_DESCRIPTION, string, metadata->description ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_GROUPING, string, metadata->grouping ); - SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, integer, metadata->beats_per_minute ); -#undef SET_ITUNES_METADATA - return 0; -} - -static int prepare_output( muxer_t *muxer ) -{ - option_t *opt = &muxer->opt; - output_t *output = &muxer->output; - output_file_t *out_file = &output->file; - output_movie_t *out_movie = &out_file->movie; - /* Allocate output tracks. */ - out_movie->track = lsmash_malloc( out_movie->num_of_tracks * sizeof(output_track_t) ); - if( !out_movie->track ) - return ERROR_MSG( "failed to allocate output tracks.\n" ); - memset( out_movie->track, 0, out_movie->num_of_tracks * sizeof(output_track_t) ); - /* Initialize L-SMASH muxer */ - output->root = lsmash_create_root(); - if( !output->root ) - return ERROR_MSG( "failed to create a ROOT.\n" ); - lsmash_file_parameters_t *file_param = &out_file->param; - if( lsmash_open_file( out_file->name, 0, file_param ) < 0 ) - return ERROR_MSG( "failed to open an output file.\n" ); - file_param->major_brand = opt->major_brand; - file_param->brands = opt->brands; - file_param->brand_count = opt->num_of_brands; - file_param->minor_version = opt->minor_version; - if( opt->interleave ) - file_param->max_chunk_duration = opt->interleave * 1e-3; - out_file->fh = lsmash_set_file( output->root, file_param ); - if( !out_file->fh ) - return ERROR_MSG( "failed to add an output file into a ROOT.\n" ); - /* Initialize movie */ - lsmash_movie_parameters_t movie_param; - lsmash_initialize_movie_parameters( &movie_param ); - if( lsmash_set_movie_parameters( output->root, &movie_param ) ) - return ERROR_MSG( "failed to set movie parameters.\n" ); - if( opt->copyright_notice - && lsmash_set_copyright( output->root, 0, opt->copyright_language, opt->copyright_notice ) ) - return ERROR_MSG( "failed to set a copyright notice for the entire movie.\n" ); - if( set_itunes_metadata( output, opt ) ) - return ERROR_MSG( "failed to set iTunes metadata.\n" ); - out_movie->current_track_number = 1; - for( uint32_t current_input_number = 1; current_input_number <= muxer->num_of_inputs; current_input_number++ ) - { - input_t *input = &muxer->input[current_input_number - 1]; - for( input->current_track_number = 1; - input->current_track_number <= input->num_of_tracks; - input->current_track_number ++ ) - { - input_track_t *in_track = &input->track[ input->current_track_number - 1 ]; - if( !in_track->active ) - continue; - input_track_option_t *track_opt = &in_track->opt; - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - /* Set up 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( !track_opt->disable ) - track_param.mode |= ISOM_TRACK_ENABLED; - if( opt->qtff ) - track_param.mode |= QT_TRACK_IN_POSTER; - 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; - switch( in_track->summary->summary_type ) - { - case LSMASH_SUMMARY_TYPE_VIDEO : - { - out_track->track_ID = lsmash_create_track( output->root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ); - if( !out_track->track_ID ) - return ERROR_MSG( "failed to create a track.\n" ); - lsmash_video_summary_t *summary = (lsmash_video_summary_t *)in_track->summary; - uint64_t display_width = summary->width << 16; - uint64_t display_height = summary->height << 16; - if( summary->par_h && summary->par_v ) - { - double sar = (double)summary->par_h / summary->par_v; - if( sar > 1.0 ) - display_width *= sar; - else - display_height /= sar; - } - track_param.display_width = display_width; - track_param.display_height = display_height; - /* Initialize media */ - uint32_t timescale = 25; /* default value */ - uint32_t timebase = 1; /* default value */ - if( track_opt->user_fps ) - { - timescale = track_opt->fps_num << (!!summary->sample_per_field); - timebase = track_opt->fps_den; - } - else if( !summary->vfr ) - { - if( lsmash_check_codec_type_identical( summary->sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) - || lsmash_check_codec_type_identical( summary->sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) ) - { - uint32_t compare_timebase = summary->timebase; - uint32_t compare_timescale = summary->timescale; - static const struct - { - uint32_t timescale; - uint32_t timebase; - } well_known_fps[] - = { - { 24000, 1001 }, { 30000, 1001 }, { 60000, 1001 }, { 120000, 1001 }, { 72000, 1001 }, - { 25, 1 }, { 50, 1 }, { 24, 1 }, { 30, 1 }, { 60, 1 }, { 120, 1 }, { 72, 1 }, { 0, 0 } - }; - for( int i = 0; well_known_fps[i].timescale; i++ ) - if( well_known_fps[i].timescale == compare_timescale - && well_known_fps[i].timebase == compare_timebase ) - { - timescale = well_known_fps[i].timescale; - timebase = well_known_fps[i].timebase; - break; - } - lsmash_codec_specific_t *bitrate = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE, - LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); - if( bitrate ) - lsmash_add_codec_specific_data( in_track->summary, bitrate ); - lsmash_destroy_codec_specific_data( bitrate ); - } - else - { - timescale = summary->timescale; - timebase = summary->timebase; - } - } - media_param.timescale = timescale; - media_param.media_handler_name = track_opt->handler_name ? track_opt->handler_name : "L-SMASH Video Handler"; - media_param.roll_grouping = 1; - media_param.rap_grouping = opt->isom_version >= 6; - out_track->timescale = timescale; - out_track->timebase = timebase; - break; - } - case LSMASH_SUMMARY_TYPE_AUDIO : - { - out_track->track_ID = lsmash_create_track( output->root, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ); - if( !out_track->track_ID ) - return ERROR_MSG( "failed to create a track.\n" ); - lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)in_track->summary; - if( track_opt->sbr ) - { - /* Check if explicit SBR is valid or not. */ - if( lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ) != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 ) - return ERROR_MSG( "--sbr is only valid with MPEG-4 Audio.\n" ); - summary->sbr_mode = MP4A_AAC_SBR_BACKWARD_COMPATIBLE; - if( lsmash_setup_AudioSpecificConfig( summary ) ) - return ERROR_MSG( "failed to set SBR mode.\n" ); - } - media_param.timescale = summary->frequency; - media_param.media_handler_name = track_opt->handler_name ? track_opt->handler_name : "L-SMASH Audio Handler"; - media_param.roll_grouping = (opt->isom_version >= 2 || opt->qtff); - out_track->priming_samples = track_opt->encoder_delay; - out_track->timescale = summary->frequency; - out_track->timebase = 1; - break; - } - default : - return ERROR_MSG( "not supported stream type.\n" ); - } - /* Reset the movie timescale in order to match the media timescale if only one track is there. */ - if( muxer->num_of_inputs == 1 - && current_input_number == 1 - && input->current_track_number == 1 ) - { - movie_param.timescale = media_param.timescale; - if( lsmash_set_movie_parameters( output->root, &movie_param ) ) - return ERROR_MSG( "failed to set movie parameters.\n" ); - } - /* Set copyright information. */ - if( track_opt->copyright_notice - && lsmash_set_copyright( output->root, out_track->track_ID, track_opt->copyright_language, track_opt->copyright_notice ) ) - return ERROR_MSG( "failed to set a copyright notice.\n" ); - /* Set track parameters. */ - if( lsmash_set_track_parameters( output->root, out_track->track_ID, &track_param ) ) - return ERROR_MSG( "failed to set track parameters.\n" ); - /* Set media parameters. */ - if( lsmash_set_media_parameters( output->root, out_track->track_ID, &media_param ) ) - return ERROR_MSG( "failed to set media parameters.\n" ); - out_track->summary = in_track->summary; - out_track->sample_entry = lsmash_add_sample_entry( output->root, out_track->track_ID, out_track->summary ); - if( !out_track->sample_entry ) - return ERROR_MSG( "failed to add sample description entry.\n" ); - out_track->active = 1; - ++ out_movie->current_track_number; - } - input->current_track_number = 1; - } - out_movie->current_track_number = 1; - return 0; -} - -static void set_reference_chapter_track( output_t *output, option_t *opt ) -{ - if( !opt->chap_file || (!opt->qtff && !opt->itunes_movie) || (opt->brand_3gx == 1) ) - return; - lsmash_create_reference_chapter_track( output->root, opt->chap_track, opt->chap_file ); -} - -static int do_mux( muxer_t *muxer ) -{ -#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) - option_t *opt = &muxer->opt; - output_t *output = &muxer->output; - output_movie_t *out_movie = &output->file.movie; - set_reference_chapter_track( output, opt ); - double largest_dts = 0; - uint32_t current_input_number = 1; - uint32_t num_consecutive_sample_skip = 0; - uint32_t num_active_input_tracks = out_movie->num_of_tracks; - uint64_t total_media_size = 0; - uint8_t sample_count = 0; - while( 1 ) - { - input_t *input = &muxer->input[current_input_number - 1]; - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - if( out_track->active ) - { - lsmash_sample_t *sample = out_track->sample; - /* Get a new sample data if the track doesn't hold any one. */ - if( !sample ) - { - /* Allocate sample buffer. */ - sample = lsmash_create_sample( out_track->summary->max_au_length ); - if( !sample ) - return ERROR_MSG( "failed to alloc memory for buffer.\n" ); - /* lsmash_importer_get_access_unit() returns 1 if there're any changes in stream's properties. */ - int ret = lsmash_importer_get_access_unit( input->importer, input->current_track_number, sample ); - if( ret == -1 ) - { - lsmash_delete_sample( sample ); - ERROR_MSG( "failed to get a frame from input file. Maybe corrupted.\n" - "Aborting muxing operation and trying to let output be valid file.\n" ); - break; - } - else if( ret == 1 ) - { - input_track_t *in_track = &input->track[input->current_track_number - 1]; - lsmash_cleanup_summary( in_track->summary ); - in_track->summary = lsmash_duplicate_summary( input->importer, input->current_track_number ); - out_track->summary = in_track->summary; - out_track->sample_entry = lsmash_add_sample_entry( output->root, out_track->track_ID, out_track->summary ); - if( !out_track->sample_entry ) - { - ERROR_MSG( "failed to add sample description entry.\n" ); - break; - } - } - if( sample->length == 0 ) - { - /* No more appendable samples in this track. */ - lsmash_delete_sample( sample ); - sample = NULL; - out_track->active = 0; - out_track->last_delta = lsmash_importer_get_last_delta( input->importer, input->current_track_number ); - if( out_track->last_delta == 0 ) - ERROR_MSG( "failed to get the last sample delta.\n" ); - out_track->last_delta *= out_track->timebase; - if( --num_active_input_tracks == 0 ) - break; /* Reached the end of whole tracks. */ - } - else - { - sample->index = out_track->sample_entry; - sample->dts *= out_track->timebase; - sample->cts *= out_track->timebase; - if( opt->timeline_shift ) - { - if( out_track->current_sample_number == 0 ) - out_track->ctd_shift = sample->cts; - sample->cts -= out_track->ctd_shift; - } - out_track->dts = (double)sample->dts / out_track->timescale; - out_track->sample = sample; - } - } - if( sample ) - { - /* Append a sample if meeting a condition. */ - if( out_track->dts <= largest_dts || num_consecutive_sample_skip == num_active_input_tracks ) - { - uint64_t sample_size = sample->length; /* sample might be deleted internally after appending. */ - uint64_t sample_dts = sample->dts; /* same as above */ - uint64_t sample_cts = sample->cts; /* same as above */ - if( lsmash_append_sample( output->root, out_track->track_ID, sample ) ) - return ERROR_MSG( "failed to append a sample.\n" ); - if( out_track->current_sample_number == 0 ) - out_track->start_offset = sample_cts; - else - out_track->last_delta = sample_dts - out_track->prev_dts; /* for any changes in stream's properties */ - out_track->prev_dts = sample_dts; - out_track->sample = NULL; - largest_dts = LSMASH_MAX( largest_dts, out_track->dts ); - total_media_size += sample_size; - ++ out_track->current_sample_number; - num_consecutive_sample_skip = 0; - /* Print, per 256 samples, total size of imported media. */ - if( ++sample_count == 0 ) - { - REFRESH_CONSOLE; - eprintf( "Importing: %"PRIu64" bytes\r", total_media_size ); - } - } - else - ++num_consecutive_sample_skip; /* Skip appendig sample. */ - } - } - if( ++ out_movie->current_track_number > out_movie->num_of_tracks ) - out_movie->current_track_number = 1; /* Back the first output track. */ - /* Move the next track. */ - if( ++ input->current_track_number > input->num_of_tracks ) - { - /* Move the next input movie. */ - input->current_track_number = 1; - if( ++ current_input_number > muxer->num_of_inputs ) - current_input_number = 1; /* Back the first input movie. */ - } - } - for( out_movie->current_track_number = 1; - out_movie->current_track_number <= out_movie->num_of_tracks; - out_movie->current_track_number ++ ) - { - /* Close track. */ - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, out_track->last_delta ) ) - ERROR_MSG( "failed to flush the rest of samples.\n" ); - /* Create edit list. - * Don't trust media duration. It's just duration of media, not duration of track presentation. - * Calculation of presentation duration by DTS is reliable since this muxer handles CFR only. */ - uint64_t actual_duration = out_track->prev_dts + out_track->last_delta - out_track->priming_samples; - lsmash_edit_t edit; - edit.duration = actual_duration * ((double)lsmash_get_movie_timescale( output->root ) / out_track->timescale); - edit.start_time = out_track->priming_samples + out_track->start_offset; - edit.rate = ISOM_EDIT_MODE_NORMAL; - if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, edit ) ) - ERROR_MSG( "failed to set timeline map.\n" ); - } - return 0; -#undef LSMASH_MAX -} - -static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size ) -{ - REFRESH_CONSOLE; - eprintf( "Finalizing: [%5.2lf%%]\r", total_movie_size ? ((double)written_movie_size / total_movie_size) * 100.0 : 0 ); - return 0; -} - -static int finish_movie( output_t *output, option_t *opt ) -{ - /* Set chapter list. */ - if( opt->chap_file ) - lsmash_set_tyrant_chapter( output->root, opt->chap_file, opt->add_bom_to_chpl ); - /* Close movie. */ - REFRESH_CONSOLE; - if( opt->optimize_pd ) - { - lsmash_adhoc_remux_t moov_to_front; - moov_to_front.func = moov_to_front_callback; - moov_to_front.buffer_size = 4*1024*1024; /* 4MiB */ - moov_to_front.param = NULL; - return lsmash_finish_movie( output->root, &moov_to_front ); - } - if( lsmash_finish_movie( output->root, NULL ) ) - return -1; - return lsmash_write_lsmash_indicator( output->root ); -} - -int main( int argc, char *argv[] ) -{ - muxer_t muxer = { { 0 } }; - lsmash_get_mainargs( &argc, &argv ); - if( parse_global_options( argc, argv, &muxer ) ) - return MUXER_USAGE_ERR(); - if( muxer.opt.help ) - { - display_help(); - cleanup_muxer( &muxer ); - return 0; - } - else if( muxer.opt.version ) - { - display_version(); - cleanup_muxer( &muxer ); - return 0; - } - if( open_input_files( &muxer ) ) - return MUXER_ERR( "failed to open input files.\n" ); - if( prepare_output( &muxer ) ) - return MUXER_ERR( "failed to set up preparation for output.\n" ); - if( do_mux( &muxer ) ) - return MUXER_ERR( "failed to do muxing.\n" ); - if( finish_movie( &muxer.output, &muxer.opt ) ) - return MUXER_ERR( "failed to finish movie.\n" ); - REFRESH_CONSOLE; - eprintf( "Muxing completed!\n" ); - cleanup_muxer( &muxer ); /* including lsmash_destroy_root() */ - return 0; -} diff -Nru l-smash-1.9.1/nalu.h l-smash-2.3.0/nalu.h --- l-smash-1.9.1/nalu.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/nalu.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,224 +0,0 @@ -/***************************************************************************** - * nalu.h: - ***************************************************************************** - * Copyright (C) 2013-2014 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. */ - -static inline uint64_t nalu_get_codeNum -( - lsmash_bits_t *bits -) -{ - uint32_t leadingZeroBits = 0; - for( int b = 0; !b; leadingZeroBits++ ) - b = lsmash_bits_get( bits, 1 ); - --leadingZeroBits; - return ((uint64_t)1 << leadingZeroBits) - 1 + lsmash_bits_get( bits, leadingZeroBits ); -} - -static inline uint64_t nalu_decode_exp_golomb_ue -( - uint64_t codeNum -) -{ - return codeNum; -} - -static inline int64_t nalu_decode_exp_golomb_se -( - uint64_t codeNum -) -{ - if( codeNum & 1 ) - return (int64_t)((codeNum >> 1) + 1); - return -1 * (int64_t)(codeNum >> 1); -} - -static inline uint64_t nalu_get_exp_golomb_ue -( - lsmash_bits_t *bits -) -{ - uint64_t codeNum = nalu_get_codeNum( bits ); - return nalu_decode_exp_golomb_ue( codeNum ); -} - -static inline uint64_t nalu_get_exp_golomb_se -( - lsmash_bits_t *bits -) -{ - uint64_t codeNum = nalu_get_codeNum( bits ); - return nalu_decode_exp_golomb_se( codeNum ); -} - -/* Convert EBSP (Encapsulated Byte Sequence Packets) to RBSP (Raw Byte Sequence Packets). */ -static inline uint8_t *nalu_remove_emulation_prevention -( - uint8_t *src, - uint64_t src_length, - uint8_t *dst -) -{ - uint8_t *src_end = src + src_length; - while( src < src_end ) - if( ((src + 2) < src_end) && !src[0] && !src[1] && (src[2] == 0x03) ) - { - /* 0x000003 -> 0x0000 */ - *dst++ = *src++; - *dst++ = *src++; - src++; /* Skip emulation_prevention_three_byte (0x03). */ - } - else - *dst++ = *src++; - return dst; -} - -static inline int nalu_import_rbsp_from_ebsp -( - lsmash_bits_t *bits, - uint8_t *rbsp_buffer, - uint8_t *ebsp, - uint64_t ebsp_size -) -{ - uint8_t *rbsp_start = rbsp_buffer; - uint8_t *rbsp_end = nalu_remove_emulation_prevention( ebsp, ebsp_size, rbsp_buffer ); - uint64_t rbsp_length = rbsp_end - rbsp_start; - return lsmash_bits_import_data( bits, rbsp_start, rbsp_length ); -} - -static inline int nalu_check_more_rbsp_data -( - lsmash_bits_t *bits -) -{ - lsmash_bs_t *bs = bits->bs; - lsmash_buffer_t *buffer = &bs->buffer; - if( buffer->pos < buffer->store && !(bits->store == 0 && (buffer->store == buffer->pos + 1)) ) - return 1; /* rbsp_trailing_bits will be placed at the next or later byte. - * Note: bs->pos points at the next byte if bits->store isn't empty. */ - if( bits->store == 0 ) - { - if( buffer->store == buffer->pos + 1 ) - return buffer->data[ buffer->pos ] != 0x80; - /* No rbsp_trailing_bits is present in RBSP data. */ - bs->error = 1; - return 0; - } - /* Check whether remainder of bits is identical to rbsp_trailing_bits. */ - uint8_t remainder_bits = bits->cache & ~(~0U << bits->store); - uint8_t rbsp_trailing_bits = 1U << (bits->store - 1); - return remainder_bits != rbsp_trailing_bits; -} - -static inline int nalu_get_max_ps_length -( - lsmash_entry_list_t *ps_list, - uint32_t *max_ps_length -) -{ - *max_ps_length = 0; - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return -1; - if( ps->unused ) - continue; - *max_ps_length = LSMASH_MAX( *max_ps_length, ps->nalUnitLength ); - } - return 0; -} - -static inline int nalu_get_ps_count -( - lsmash_entry_list_t *ps_list, - uint32_t *ps_count -) -{ - *ps_count = 0; - for( lsmash_entry_t *entry = ps_list ? ps_list->head : NULL; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return -1; - if( ps->unused ) - continue; - ++(*ps_count); - } - return 0; -} - -static inline int nalu_check_same_ps_existence -( - lsmash_entry_list_t *ps_list, - void *ps_data, - uint32_t ps_length -) -{ - for( lsmash_entry_t *entry = ps_list->head; entry; entry = entry->next ) - { - isom_dcr_ps_entry_t *ps = (isom_dcr_ps_entry_t *)entry->data; - if( !ps ) - return -1; - if( ps->unused ) - continue; - if( ps->nalUnitLength == ps_length && !memcmp( ps->nalUnit, ps_data, ps_length ) ) - return 1; /* The same parameter set already exists. */ - } - return 0; -} - -static inline int nalu_get_dcr_ps -( - lsmash_bs_t *bs, - lsmash_entry_list_t *list, - uint8_t entry_count -) -{ - for( uint8_t i = 0; i < entry_count; i++ ) - { - isom_dcr_ps_entry_t *data = lsmash_malloc( sizeof(isom_dcr_ps_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->nalUnitLength = lsmash_bs_get_be16( bs ); - data->nalUnit = lsmash_bs_get_bytes( bs, data->nalUnitLength ); - if( !data->nalUnit ) - { - lsmash_remove_entries( list, isom_remove_dcr_ps ); - return -1; - } - } - return 0; -} - -static inline int nalu_check_next_short_start_code -( - uint8_t *buf_pos, - uint8_t *buf_end -) -{ - return ((buf_pos + 2) < buf_end) && !buf_pos[0] && !buf_pos[1] && (buf_pos[2] == 0x01); -} diff -Nru l-smash-1.9.1/osdep.c l-smash-2.3.0/osdep.c --- l-smash-1.9.1/osdep.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/osdep.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -/***************************************************************************** - * osdep.c: - ***************************************************************************** - * Copyright (C) 2013-2014 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 "common/osdep.h" -#include -#include - -#ifdef _WIN32 -#include -#endif - -#ifdef _WIN32 - -int lsmash_string_to_wchar( int cp, const char *from, wchar_t **to ) -{ - int nc = MultiByteToWideChar( cp, 0, from, -1, 0, 0 ); - if( nc == 0 ) - return 0; - *to = lsmash_malloc( nc * sizeof(wchar_t) ); - MultiByteToWideChar( cp, 0, from, -1, *to, nc ); - return nc; -} - -int lsmash_string_from_wchar( int cp, const wchar_t *from, char **to ) -{ - int nc = WideCharToMultiByte( cp, 0, from, -1, 0, 0, 0, 0 ); - if( nc == 0 ) - return 0; - *to = lsmash_malloc( nc * sizeof(char) ); - WideCharToMultiByte( cp, 0, from, -1, *to, nc, 0, 0 ); - return nc; -} - -FILE *lsmash_win32_fopen( const char *name, const char *mode ) -{ - wchar_t *wname, *wmode; - lsmash_string_to_wchar( CP_UTF8, name, &wname ); - lsmash_string_to_wchar( CP_UTF8, mode, &wmode ); - FILE *fp = _wfopen( wname, wmode ); - if( !fp ) - fp = fopen( name, mode ); - lsmash_free( wname ); - lsmash_free( wmode ); - return fp; -} - -#endif - diff -Nru l-smash-1.9.1/print.c l-smash-2.3.0/print.c --- l-smash-1.9.1/print.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/print.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2649 +0,0 @@ -/***************************************************************************** - * print.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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. */ - -#ifdef LSMASH_DEMUXER_ENABLED - -#include "internal.h" /* must be placed first */ - -#include -#include -#include -#include /* for isom_iprintf */ - -#include "box.h" - - -typedef int (*isom_print_box_t)( FILE *, lsmash_file_t *, isom_box_t *, int ); - -typedef struct -{ - int level; - isom_box_t *box; - isom_print_box_t func; -} isom_print_entry_t; - -static void isom_ifprintf_duration( FILE *fp, int indent, char *field_name, uint64_t duration, uint32_t timescale ) -{ - if( !timescale ) - { - lsmash_ifprintf( fp, indent, "duration = %"PRIu64"\n", duration ); - return; - } - int dur = duration / timescale; - int hour = (dur / 3600) % 24; - int min = (dur / 60) % 60; - int sec = dur % 60; - int ms = ((double)duration / timescale - (hour * 3600 + min * 60 + sec)) * 1e3 + 0.5; - static char str[32]; - sprintf( str, "%02d:%02d:%02d.%03d", hour, min, sec, ms ); - lsmash_ifprintf( fp, indent, "%s = %"PRIu64" (%s)\n", field_name, duration, str ); -} - -static char *isom_mp4time2utc( uint64_t mp4time ) -{ - int year_offset = mp4time / 31536000; - int leap_years = year_offset / 4 + ((mp4time / 86400) > 366); /* 1904 itself is leap year */ - int day = (mp4time / 86400) - (year_offset * 365) - leap_years + 1; - while( day < 1 ) - { - --year_offset; - leap_years = year_offset / 4 + ((mp4time / 86400) > 366); - day = (mp4time / 86400) - (year_offset * 365) - leap_years + 1; - } - int year = 1904 + year_offset; - int is_leap = (!(year % 4) && (year % 100)) || !(year % 400); - static const int month_days[13] = { 29, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - int month; - for( month = 1; month <= 12; month++ ) - { - int i = (month == 2 && is_leap) ? 0 : month; - if( day <= month_days[i] ) - break; - day -= month_days[i]; - } - int hour = (mp4time / 3600) % 24; - int min = (mp4time / 60) % 60; - int sec = mp4time % 60; - static char utc[64]; - sprintf( utc, "UTC %d/%02d/%02d, %02d:%02d:%02d\n", year, month, day, hour, min, sec ); - return utc; -} - -static void isom_ifprintf_matrix( FILE *fp, int indent, int32_t *matrix ) -{ - lsmash_ifprintf( fp, indent, "| a, b, u | | %f, %f, %f |\n", lsmash_fixed2double( matrix[0], 16 ), - lsmash_fixed2double( matrix[1], 16 ), - lsmash_fixed2double( matrix[2], 30 ) ); - lsmash_ifprintf( fp, indent, "| c, d, v | = | %f, %f, %f |\n", lsmash_fixed2double( matrix[3], 16 ), - lsmash_fixed2double( matrix[4], 16 ), - lsmash_fixed2double( matrix[5], 30 ) ); - lsmash_ifprintf( fp, indent, "| x, y, w | | %f, %f, %f |\n", lsmash_fixed2double( matrix[6], 16 ), - lsmash_fixed2double( matrix[7], 16 ), - lsmash_fixed2double( matrix[8], 30 ) ); -} - -static void isom_ifprintf_rgb_color( FILE *fp, int indent, uint16_t *color ) -{ - lsmash_ifprintf( fp, indent, "{ R, G, B } = { %"PRIu16", %"PRIu16", %"PRIu16" }\n", color[0], color[1], color[2] ); -} - -static void isom_ifprintf_rgba_color( FILE *fp, int indent, uint8_t *color ) -{ - lsmash_ifprintf( fp, indent, "{ R, G, B, A } = { %"PRIu8", %"PRIu8", %"PRIu8", %"PRIu8" }\n", color[0], color[1], color[2], color[3] ); -} - -static char *isom_unpack_iso_language( uint16_t language ) -{ - static char unpacked[4]; - unpacked[0] = ((language >> 10) & 0x1f) + 0x60; - unpacked[1] = ((language >> 5) & 0x1f) + 0x60; - unpacked[2] = ( language & 0x1f) + 0x60; - unpacked[3] = 0; - return unpacked; -} - -static void isom_ifprintf_sample_description_common_reserved( FILE *fp, int indent, uint8_t *reserved ) -{ - uint64_t temp = ((uint64_t)reserved[0] << 40) - | ((uint64_t)reserved[1] << 32) - | ((uint64_t)reserved[2] << 24) - | ((uint64_t)reserved[3] << 16) - | ((uint64_t)reserved[4] << 8) - | (uint64_t)reserved[5]; - lsmash_ifprintf( fp, indent, "reserved = 0x%012"PRIx64"\n", temp ); -} - -static void isom_ifprintf_sample_flags( FILE *fp, int indent, char *field_name, 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_ifprintf( fp, indent++, "%s = 0x%08"PRIx32"\n", field_name, temp ); - if( flags->is_leading & ISOM_SAMPLE_IS_UNDECODABLE_LEADING ) lsmash_ifprintf( fp, indent, "undecodable leading\n" ); - else if( flags->is_leading & ISOM_SAMPLE_IS_NOT_LEADING ) lsmash_ifprintf( fp, indent, "non-leading\n" ); - else if( flags->is_leading & ISOM_SAMPLE_IS_DECODABLE_LEADING ) lsmash_ifprintf( fp, indent, "decodable leading\n" ); - if( flags->sample_depends_on & ISOM_SAMPLE_IS_INDEPENDENT ) lsmash_ifprintf( fp, indent, "independent\n" ); - else if( flags->sample_depends_on & ISOM_SAMPLE_IS_NOT_INDEPENDENT ) lsmash_ifprintf( fp, indent, "dependent\n" ); - if( flags->sample_is_depended_on & ISOM_SAMPLE_IS_NOT_DISPOSABLE ) lsmash_ifprintf( fp, indent, "non-disposable\n" ); - else if( flags->sample_is_depended_on & ISOM_SAMPLE_IS_DISPOSABLE ) lsmash_ifprintf( fp, indent, "disposable\n" ); - if( flags->sample_has_redundancy & ISOM_SAMPLE_HAS_REDUNDANCY ) lsmash_ifprintf( fp, indent, "redundant\n" ); - else if( flags->sample_has_redundancy & ISOM_SAMPLE_HAS_NO_REDUNDANCY ) lsmash_ifprintf( fp, indent, "non-redundant\n" ); - if( flags->sample_padding_value ) - lsmash_ifprintf( fp, indent, "padding_bits = %"PRIu8"\n", flags->sample_padding_value ); - lsmash_ifprintf( fp, indent, flags->sample_is_non_sync_sample ? "non-sync sample\n" : "sync sample\n" ); - lsmash_ifprintf( fp, indent, "degradation_priority = %"PRIu16"\n", flags->sample_degradation_priority ); -} - -static inline int isom_print_simple( FILE *fp, isom_box_t *box, int level, char *name ) -{ - int indent = level; - if( box->type.fourcc != ISOM_BOX_TYPE_UUID.fourcc ) - { - lsmash_ifprintf( fp, indent++, "[%s: %s]\n", isom_4cc2str( box->type.fourcc ), name ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - } - else - { - lsmash_ifprintf( fp, indent++, "[uuid: UUID Box]\n" ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - lsmash_ifprintf( fp, indent++, "usertype\n" ); - lsmash_ifprintf( fp, indent, "type = %s\n", isom_4cc2str( box->type.user.fourcc ) ); - lsmash_ifprintf( fp, indent, "name = %s\n", name ); - lsmash_ifprintf( fp, indent, "uuid = 0x%08"PRIx32"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"0x%08"PRIx32"\n", - box->type.user.fourcc, - (box->type.user.id[0] << 8) | box->type.user.id[1], (box->type.user.id[2] << 8) | box->type.user.id[3], - (box->type.user.id[4] << 8) | box->type.user.id[5], (box->type.user.id[6] << 8) | box->type.user.id[7], - (box->type.user.id[8] << 24) | (box->type.user.id[9] << 16) | (box->type.user.id[10] << 8) | box->type.user.id[11] ); - } - return 0; -} - -static void isom_print_basebox_common( FILE *fp, int indent, isom_box_t *box, char *name ) -{ - isom_print_simple( fp, box, indent, name ); -} - -static void isom_print_fullbox_common( FILE *fp, int indent, isom_box_t *box, char *name ) -{ - isom_print_simple( fp, box, indent++, name ); - lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", box->version ); - lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", box->flags & 0x00ffffff ); -} - -static void isom_print_box_common( FILE *fp, int indent, isom_box_t *box, char *name ) -{ - isom_box_t *parent = box->parent; - if( parent && lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) - { - isom_print_basebox_common( fp, indent, box, name ); - return; - } - if( isom_is_fullbox( box ) ) - isom_print_fullbox_common( fp, indent, box, name ); - else - isom_print_basebox_common( fp, indent, box, name ); -} - -static int isom_print_unknown( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - int indent = level; - if( box->type.fourcc != ISOM_BOX_TYPE_UUID.fourcc ) - { - lsmash_ifprintf( fp, indent++, "[%s]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - } - else - { - lsmash_ifprintf( fp, indent++, "[uuid: UUID Box]\n" ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - lsmash_ifprintf( fp, indent++, "usertype\n" ); - lsmash_ifprintf( fp, indent, "type = %s\n", isom_4cc2str( box->type.user.fourcc ) ); - lsmash_ifprintf( fp, indent, "uuid = 0x%08"PRIx32"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"%08"PRIx32"\n", - box->type.user.fourcc, - (box->type.user.id[0] << 8) | box->type.user.id[1], (box->type.user.id[2] << 8) | box->type.user.id[3], - (box->type.user.id[4] << 8) | box->type.user.id[5], (box->type.user.id[6] << 8) | box->type.user.id[7], - (box->type.user.id[8] << 24) | (box->type.user.id[9] << 16) | (box->type.user.id[10] << 8) | box->type.user.id[11] ); - } - return 0; -} - -static void isom_print_brand_description( FILE *fp, lsmash_brand_type brand ) -{ - if( brand == 0 ) - return; - static const struct - { - lsmash_brand_type brand; - char *description; - } brand_description_table[] = - { - { ISOM_BRAND_TYPE_3G2A, "3GPP2" }, - { ISOM_BRAND_TYPE_3GE6, "3GPP Release 6 Extended Presentation Profile" }, - { ISOM_BRAND_TYPE_3GE9, "3GPP Release 9 Extended Presentation Profile" }, - { ISOM_BRAND_TYPE_3GF9, "3GPP Release 9 File-delivery Server Profile" }, - { ISOM_BRAND_TYPE_3GG6, "3GPP Release 6 General Profile" }, - { ISOM_BRAND_TYPE_3GG9, "3GPP Release 9 General Profile" }, - { ISOM_BRAND_TYPE_3GH9, "3GPP Release 9 Adaptive Streaming Profile" }, - { ISOM_BRAND_TYPE_3GM9, "3GPP Release 9 Media Segment Profile" }, - { ISOM_BRAND_TYPE_3GP4, "3GPP Release 4" }, - { ISOM_BRAND_TYPE_3GP5, "3GPP Release 5" }, - { ISOM_BRAND_TYPE_3GP6, "3GPP Release 6 Basic Profile" }, - { ISOM_BRAND_TYPE_3GP7, "3GPP Release 7" }, - { ISOM_BRAND_TYPE_3GP8, "3GPP Release 8" }, - { ISOM_BRAND_TYPE_3GP9, "3GPP Release 9 Basic Profile" }, - { ISOM_BRAND_TYPE_3GR6, "3GPP Release 6 Progressive Download Profile" }, - { ISOM_BRAND_TYPE_3GR9, "3GPP Release 9 Progressive Download Profile" }, - { ISOM_BRAND_TYPE_3GS6, "3GPP Release 6 Streaming Server Profile" }, - { ISOM_BRAND_TYPE_3GS9, "3GPP Release 9 Streaming Server Profile" }, - { ISOM_BRAND_TYPE_3GT9, "3GPP Release 9 Media Stream Recording Profile" }, - { ISOM_BRAND_TYPE_ARRI, "ARRI Digital Camera" }, - { ISOM_BRAND_TYPE_CAEP, "Canon Digital Camera" }, - { ISOM_BRAND_TYPE_CDES, "Convergent Designs" }, - { ISOM_BRAND_TYPE_LCAG, "Leica digital camera" }, - { ISOM_BRAND_TYPE_M4A , "iTunes MPEG-4 audio protected or not" }, - { ISOM_BRAND_TYPE_M4B , "iTunes AudioBook protected or not" }, - { ISOM_BRAND_TYPE_M4P , "MPEG-4 protected audio" }, - { ISOM_BRAND_TYPE_M4V , "MPEG-4 protected audio+video" }, - { ISOM_BRAND_TYPE_MFSM, "Media File for Samsung video Metadata" }, - { ISOM_BRAND_TYPE_MPPI, "Photo Player Multimedia Application Format" }, - { ISOM_BRAND_TYPE_ROSS, "Ross Video" }, - { ISOM_BRAND_TYPE_AVC1, "Advanced Video Coding extensions" }, - { ISOM_BRAND_TYPE_BBXM, "Blinkbox Master File" }, - { ISOM_BRAND_TYPE_CAQV, "Casio Digital Camera" }, - { ISOM_BRAND_TYPE_CCFF, "Common container file format" }, - { ISOM_BRAND_TYPE_DA0A, "DMB AF" }, - { ISOM_BRAND_TYPE_DA0B, "DMB AF" }, - { ISOM_BRAND_TYPE_DA1A, "DMB AF" }, - { ISOM_BRAND_TYPE_DA1B, "DMB AF" }, - { ISOM_BRAND_TYPE_DA2A, "DMB AF" }, - { ISOM_BRAND_TYPE_DA2B, "DMB AF" }, - { ISOM_BRAND_TYPE_DA3A, "DMB AF" }, - { ISOM_BRAND_TYPE_DA3B, "DMB AF" }, - { ISOM_BRAND_TYPE_DASH, "Indexed self-initializing Media Segment" }, - { ISOM_BRAND_TYPE_DBY1, "MP4 files with Dolby content" }, - { ISOM_BRAND_TYPE_DMB1, "DMB AF" }, - { ISOM_BRAND_TYPE_DSMS, "Self-initializing Media Segment" }, - { ISOM_BRAND_TYPE_DV1A, "DMB AF" }, - { ISOM_BRAND_TYPE_DV1B, "DMB AF" }, - { ISOM_BRAND_TYPE_DV2A, "DMB AF" }, - { ISOM_BRAND_TYPE_DV2B, "DMB AF" }, - { ISOM_BRAND_TYPE_DV3A, "DMB AF" }, - { ISOM_BRAND_TYPE_DV3B, "DMB AF" }, - { ISOM_BRAND_TYPE_DVR1, "DVB RTP" }, - { ISOM_BRAND_TYPE_DVT1, "DVB Transport Stream" }, - { ISOM_BRAND_TYPE_IFRM, "Apple iFrame" }, - { ISOM_BRAND_TYPE_ISC2, "Files encrypted according to ISMACryp 2.0" }, - { ISOM_BRAND_TYPE_ISO2, "ISO Base Media file format version 2" }, - { ISOM_BRAND_TYPE_ISO3, "ISO Base Media file format version 3" }, - { ISOM_BRAND_TYPE_ISO4, "ISO Base Media file format version 4" }, - { ISOM_BRAND_TYPE_ISO5, "ISO Base Media file format version 5" }, - { ISOM_BRAND_TYPE_ISO6, "ISO Base Media file format version 6" }, - { ISOM_BRAND_TYPE_ISO7, "ISO Base Media file format version 7" }, - { ISOM_BRAND_TYPE_ISOM, "ISO Base Media file format version 1" }, - { ISOM_BRAND_TYPE_JPSI, "The JPSearch data interchange format" }, - { ISOM_BRAND_TYPE_LMSG, "last Media Segment indicator" }, - { ISOM_BRAND_TYPE_MJ2S, "Motion JPEG 2000 simple profile" }, - { ISOM_BRAND_TYPE_MJP2, "Motion JPEG 2000, general profile" }, - { ISOM_BRAND_TYPE_MP21, "MPEG-21" }, - { ISOM_BRAND_TYPE_MP41, "MP4 version 1" }, - { ISOM_BRAND_TYPE_MP42, "MP4 version 2" }, - { ISOM_BRAND_TYPE_MP71, "MPEG-7 file-level metadata" }, - { ISOM_BRAND_TYPE_MSDH, "Media Segment" }, - { ISOM_BRAND_TYPE_MSIX, "Indexed Media Segment" }, - { ISOM_BRAND_TYPE_NIKO, "Nikon Digital Camera" }, - { ISOM_BRAND_TYPE_ODCF, "OMA DCF" }, - { ISOM_BRAND_TYPE_OPF2, "OMA PDCF" }, - { ISOM_BRAND_TYPE_OPX2, "OMA Adapted PDCF" }, - { ISOM_BRAND_TYPE_PANA, "Panasonic Digital Camera" }, - { ISOM_BRAND_TYPE_PIFF, "Protected Interoperable File Format" }, - { ISOM_BRAND_TYPE_PNVI, "Panasonic Video Intercom" }, - { ISOM_BRAND_TYPE_QT , "QuickTime file format" }, - { ISOM_BRAND_TYPE_RISX, "Representation Index Segment" }, - { ISOM_BRAND_TYPE_SDV , "SD Video" }, - { ISOM_BRAND_TYPE_SIMS, "Sub-Indexed Media Segment" }, - { ISOM_BRAND_TYPE_SISX, "Single Index Segment" }, - { ISOM_BRAND_TYPE_SSSS, "Subsegment Index Segment" }, - { 0, NULL } - }; - for( int i = 0; brand_description_table[i].description; i++ ) - if( brand == brand_description_table[i].brand ) - { - fprintf( fp, " : %s\n", brand_description_table[i].description ); - return; - } - fprintf( fp, "\n" ); -} - -static void isom_print_file_type -( - FILE *fp, - int indent, - uint32_t major_brand, - uint32_t minor_version, - uint32_t brand_count, - uint32_t *compatible_brands -) -{ - lsmash_ifprintf( fp, indent, "major_brand = %s", isom_4cc2str( major_brand ) ); - isom_print_brand_description( fp, major_brand ); - lsmash_ifprintf( fp, indent, "minor_version = %"PRIu32"\n", minor_version ); - lsmash_ifprintf( fp, indent++, "compatible_brands\n" ); - for( uint32_t i = 0; i < brand_count; i++ ) - { - lsmash_ifprintf( fp, indent, "brand[%"PRIu32"] = %s", i, isom_4cc2str( compatible_brands[i] ) ); - isom_print_brand_description( fp, compatible_brands[i] ); - } -} - -static int isom_print_ftyp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_ftyp_t *ftyp = (isom_ftyp_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "File Type Box" ); - isom_print_file_type( fp, indent, ftyp->major_brand, ftyp->minor_version, ftyp->brand_count, ftyp->compatible_brands ); - return 0; -} - -static int isom_print_styp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - /* Print 'valid' if this box is the first box in a file. */ - int valid; - if( file - && file->print - && file->print->head - && file->print->head->data ) - valid = (box == ((isom_print_entry_t *)file->print->head->data)->box); - else - valid = 0; - char *name = valid ? "Segment Type Box (valid)" : "Segment Type Box"; - isom_styp_t *styp = (isom_styp_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, name ); - isom_print_file_type( fp, indent, styp->major_brand, styp->minor_version, styp->brand_count, styp->compatible_brands ); - return 0; -} - -static int isom_print_sidx( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_sidx_t *)box)->list ) - return -1; - isom_sidx_t *sidx = (isom_sidx_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Segment Index Box" ); - lsmash_ifprintf( fp, indent, "reference_ID = %"PRIu32"\n", sidx->reference_ID ); - lsmash_ifprintf( fp, indent, "timescale = %"PRIu32"\n", sidx->timescale ); - lsmash_ifprintf( fp, indent, "earliest_presentation_time = %"PRIu64"\n", sidx->earliest_presentation_time ); - lsmash_ifprintf( fp, indent, "first_offset = %"PRIu64"\n", sidx->first_offset ); - lsmash_ifprintf( fp, indent, "reserved = %"PRIu16"\n", sidx->reserved ); - lsmash_ifprintf( fp, indent, "reference_count = %"PRIu16"\n", sidx->reference_count ); - uint32_t i = 0; - for( lsmash_entry_t *entry = sidx->list->head; entry; entry = entry->next ) - { - isom_sidx_referenced_item_t *data = (isom_sidx_referenced_item_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "reference_type = %"PRIu8" (%s)\n", data->reference_type, data->reference_type ? "index" : "media" ); - lsmash_ifprintf( fp, indent, "reference_size = %"PRIu32"\n", data->reference_size ); - lsmash_ifprintf( fp, indent, "subsegment_duration = %"PRIu32"\n", data->subsegment_duration ); - lsmash_ifprintf( fp, indent, "starts_with_SAP = %"PRIu8"%s\n", data->starts_with_SAP, data->starts_with_SAP ? " (yes)" : "" ); - lsmash_ifprintf( fp, indent, "SAP_type = %"PRIu8"%s\n", data->SAP_type, data->SAP_type == 0 ? " (unknown)" : "" ); - lsmash_ifprintf( fp, indent--, "SAP_delta_time = %"PRIu32"\n", data->SAP_delta_time ); - } - return 0; -} - -static int isom_print_moov( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Movie Box" ); -} - -static int isom_print_mvhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_mvhd_t *mvhd = (isom_mvhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Movie Header Box" ); - lsmash_ifprintf( fp, indent, "creation_time = %s", isom_mp4time2utc( mvhd->creation_time ) ); - lsmash_ifprintf( fp, indent, "modification_time = %s", isom_mp4time2utc( mvhd->modification_time ) ); - lsmash_ifprintf( fp, indent, "timescale = %"PRIu32"\n", mvhd->timescale ); - isom_ifprintf_duration( fp, indent, "duration", mvhd->duration, mvhd->timescale ); - lsmash_ifprintf( fp, indent, "rate = %f\n", lsmash_fixed2double( mvhd->rate, 16 ) ); - lsmash_ifprintf( fp, indent, "volume = %f\n", lsmash_fixed2double( mvhd->volume, 8 ) ); - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", mvhd->reserved ); - if( file->qt_compatible ) - { - lsmash_ifprintf( fp, indent, "preferredLong1 = 0x%08"PRIx32"\n", mvhd->preferredLong[0] ); - lsmash_ifprintf( fp, indent, "preferredLong2 = 0x%08"PRIx32"\n", mvhd->preferredLong[1] ); - lsmash_ifprintf( fp, indent, "transformation matrix\n" ); - isom_ifprintf_matrix( fp, indent + 1, mvhd->matrix ); - lsmash_ifprintf( fp, indent, "previewTime = %"PRId32"\n", mvhd->previewTime ); - lsmash_ifprintf( fp, indent, "previewDuration = %"PRId32"\n", mvhd->previewDuration ); - lsmash_ifprintf( fp, indent, "posterTime = %"PRId32"\n", mvhd->posterTime ); - lsmash_ifprintf( fp, indent, "selectionTime = %"PRId32"\n", mvhd->selectionTime ); - lsmash_ifprintf( fp, indent, "selectionDuration = %"PRId32"\n", mvhd->selectionDuration ); - lsmash_ifprintf( fp, indent, "currentTime = %"PRId32"\n", mvhd->currentTime ); - } - else - { - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", mvhd->preferredLong[0] ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", mvhd->preferredLong[1] ); - lsmash_ifprintf( fp, indent, "transformation matrix\n" ); - isom_ifprintf_matrix( fp, indent + 1, mvhd->matrix ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->previewTime ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->previewDuration ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->posterTime ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->selectionTime ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->selectionDuration ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->currentTime ); - } - lsmash_ifprintf( fp, indent, "next_track_ID = %"PRIu32"\n", mvhd->next_track_ID ); - return 0; -} - -static void isom_pring_qt_color_table( FILE *fp, int indent, isom_qt_color_table_t *color_table ) -{ - isom_qt_color_array_t *array = color_table->array; - if( !array ) - return; - lsmash_ifprintf( fp, indent, "ctSeed = %"PRIu32"\n", color_table->seed ); - lsmash_ifprintf( fp, indent, "ctFlags = 0x%04"PRIx16"\n", color_table->flags ); - lsmash_ifprintf( fp, indent, "ctSize = %"PRIu16"\n", color_table->size ); - lsmash_ifprintf( fp, indent++, "ctTable\n" ); - for( uint16_t i = 0; i <= color_table->size; i++ ) - lsmash_ifprintf( fp, indent, - "color[%"PRIu16"] = { 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16" }\n", - i, array[i].value, array[i].r, array[i].g, array[i].b ); -} - -static int isom_print_ctab( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_ctab_t *ctab = (isom_ctab_t *)box; - int indent = level; - isom_print_box_common( fp, indent, box, "Color Table Box" ); - isom_pring_qt_color_table( fp, indent + 1, &ctab->color_table ); - return 0; -} - -static int isom_print_iods( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Object Descriptor Box" ); -} - -static int isom_print_trak( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Track Box" ); -} - -static int isom_print_tkhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_tkhd_t *tkhd = (isom_tkhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Header Box" ); - ++indent; - if( tkhd->flags & ISOM_TRACK_ENABLED ) - lsmash_ifprintf( fp, indent, "Track enabled\n" ); - else - lsmash_ifprintf( fp, indent, "Track disabled\n" ); - if( tkhd->flags & ISOM_TRACK_IN_MOVIE ) - lsmash_ifprintf( fp, indent, "Track in movie\n" ); - if( tkhd->flags & ISOM_TRACK_IN_PREVIEW ) - lsmash_ifprintf( fp, indent, "Track in preview\n" ); - if( file->qt_compatible && (tkhd->flags & QT_TRACK_IN_POSTER) ) - lsmash_ifprintf( fp, indent, "Track in poster\n" ); - lsmash_ifprintf( fp, --indent, "creation_time = %s", isom_mp4time2utc( tkhd->creation_time ) ); - lsmash_ifprintf( fp, indent, "modification_time = %s", isom_mp4time2utc( tkhd->modification_time ) ); - lsmash_ifprintf( fp, indent, "track_ID = %"PRIu32"\n", tkhd->track_ID ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved1 ); - if( file && file->moov && file->moov->mvhd ) - isom_ifprintf_duration( fp, indent, "duration", tkhd->duration, file->moov->mvhd->timescale ); - else - isom_ifprintf_duration( fp, indent, "duration", tkhd->duration, 0 ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved2[0] ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved2[1] ); - lsmash_ifprintf( fp, indent, "layer = %"PRId16"\n", tkhd->layer ); - lsmash_ifprintf( fp, indent, "alternate_group = %"PRId16"\n", tkhd->alternate_group ); - lsmash_ifprintf( fp, indent, "volume = %f\n", lsmash_fixed2double( tkhd->volume, 8 ) ); - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", tkhd->reserved3 ); - lsmash_ifprintf( fp, indent, "transformation matrix\n" ); - isom_ifprintf_matrix( fp, indent + 1, tkhd->matrix ); - lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( tkhd->width, 16 ) ); - lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( tkhd->height, 16 ) ); - return 0; -} - -static int isom_print_tapt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Track Aperture Mode Dimensions Box" ); -} - -static int isom_print_clef( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_clef_t *clef = (isom_clef_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Clean Aperture Dimensions Box" ); - lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( clef->width, 16 ) ); - lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( clef->height, 16 ) ); - return 0; -} - -static int isom_print_prof( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_prof_t *prof = (isom_prof_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Production Aperture Dimensions Box" ); - lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( prof->width, 16 ) ); - lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( prof->height, 16 ) ); - return 0; -} - -static int isom_print_enof( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_enof_t *enof = (isom_enof_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Encoded Pixels Dimensions Box" ); - lsmash_ifprintf( fp, indent, "width = %f\n", lsmash_fixed2double( enof->width, 16 ) ); - lsmash_ifprintf( fp, indent, "height = %f\n", lsmash_fixed2double( enof->height, 16 ) ); - return 0; -} - -static int isom_print_edts( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Edit Box" ); -} - -static int isom_print_elst( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_elst_t *elst = (isom_elst_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Edit List Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", elst->list->entry_count ); - for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next ) - { - isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "segment_duration = %"PRIu64"\n", data->segment_duration ); - lsmash_ifprintf( fp, indent, "media_time = %"PRId64"\n", data->media_time ); - lsmash_ifprintf( fp, indent--, "media_rate = %f\n", lsmash_fixed2double( data->media_rate, 16 ) ); - } - return 0; -} - -static int isom_print_tref( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Track Reference Box" ); -} - -static int isom_print_track_reference_type( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_tref_type_t *ref = (isom_tref_type_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Reference Type Box" ); - for( uint32_t i = 0; i < ref->ref_count; i++ ) - lsmash_ifprintf( fp, indent, "track_ID[%"PRIu32"] = %"PRIu32"\n", i, ref->track_ID[i] ); - return 0; -} - -static int isom_print_mdia( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Media Box" ); -} - -static int isom_print_mdhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_mdhd_t *mdhd = (isom_mdhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Media Header Box" ); - lsmash_ifprintf( fp, indent, "creation_time = %s", isom_mp4time2utc( mdhd->creation_time ) ); - lsmash_ifprintf( fp, indent, "modification_time = %s", isom_mp4time2utc( mdhd->modification_time ) ); - lsmash_ifprintf( fp, indent, "timescale = %"PRIu32"\n", mdhd->timescale ); - isom_ifprintf_duration( fp, indent, "duration", mdhd->duration, mdhd->timescale ); - if( mdhd->language >= 0x800 ) - lsmash_ifprintf( fp, indent, "language = %s\n", isom_unpack_iso_language( mdhd->language ) ); - else - lsmash_ifprintf( fp, indent, "language = %"PRIu16"\n", mdhd->language ); - if( file->qt_compatible ) - lsmash_ifprintf( fp, indent, "quality = %"PRId16"\n", mdhd->quality ); - else - lsmash_ifprintf( fp, indent, "pre_defined = 0x%04"PRIx16"\n", mdhd->quality ); - return 0; -} - -static int isom_print_hdlr( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_hdlr_t *hdlr = (isom_hdlr_t *)box; - int indent = level; - char str[hdlr->componentName_length + 1]; - memcpy( str, hdlr->componentName, hdlr->componentName_length ); - str[hdlr->componentName_length] = 0; - isom_print_box_common( fp, indent++, box, "Handler Reference Box" ); - if( file->qt_compatible ) - { - lsmash_ifprintf( fp, indent, "componentType = %s\n", isom_4cc2str( hdlr->componentType ) ); - lsmash_ifprintf( fp, indent, "componentSubtype = %s\n", isom_4cc2str( hdlr->componentSubtype ) ); - lsmash_ifprintf( fp, indent, "componentManufacturer = %s\n", isom_4cc2str( hdlr->componentManufacturer ) ); - lsmash_ifprintf( fp, indent, "componentFlags = 0x%08"PRIx32"\n", hdlr->componentFlags ); - lsmash_ifprintf( fp, indent, "componentFlagsMask = 0x%08"PRIx32"\n", hdlr->componentFlagsMask ); - if( hdlr->componentName_length ) - lsmash_ifprintf( fp, indent, "componentName = %s\n", &str[1] ); - else - lsmash_ifprintf( fp, indent, "componentName = \n" ); - } - else - { - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", hdlr->componentType ); - lsmash_ifprintf( fp, indent, "handler_type = %s\n", isom_4cc2str( hdlr->componentSubtype ) ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentManufacturer ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentFlags ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentFlagsMask ); - lsmash_ifprintf( fp, indent, "name = %s\n", str ); - } - return 0; -} - -static int isom_print_minf( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Media Information Box" ); -} - -static int isom_print_vmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_vmhd_t *vmhd = (isom_vmhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Video Media Header Box" ); - lsmash_ifprintf( fp, indent, "graphicsmode = %"PRIu16"\n", vmhd->graphicsmode ); - lsmash_ifprintf( fp, indent, "opcolor\n" ); - isom_ifprintf_rgb_color( fp, indent + 1, vmhd->opcolor ); - return 0; -} - -static int isom_print_smhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_smhd_t *smhd = (isom_smhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Sound Media Header Box" ); - lsmash_ifprintf( fp, indent, "balance = %f\n", lsmash_fixed2double( smhd->balance, 8 ) ); - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", smhd->reserved ); - return 0; -} - -static int isom_print_hmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_hmhd_t *hmhd = (isom_hmhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Hint Media Header Box" ); - lsmash_ifprintf( fp, indent, "maxPDUsize = %"PRIu16"\n", hmhd->maxPDUsize ); - lsmash_ifprintf( fp, indent, "avgPDUsize = %"PRIu16"\n", hmhd->avgPDUsize ); - lsmash_ifprintf( fp, indent, "maxbitrate = %"PRIu32"\n", hmhd->maxbitrate ); - lsmash_ifprintf( fp, indent, "avgbitrate = %"PRIu32"\n", hmhd->avgbitrate ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", hmhd->reserved ); - return 0; -} - -static int isom_print_nmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Null Media Header Box" ); -} - -static int isom_print_gmhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Generic Media Information Header Box" ); -} - -static int isom_print_gmin( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_gmin_t *gmin = (isom_gmin_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Generic Media Information Box" ); - lsmash_ifprintf( fp, indent, "graphicsmode = %"PRIu16"\n", gmin->graphicsmode ); - lsmash_ifprintf( fp, indent, "opcolor\n" ); - isom_ifprintf_rgb_color( fp, indent + 1, gmin->opcolor ); - lsmash_ifprintf( fp, indent, "balance = %f\n", lsmash_fixed2double( gmin->balance, 8 ) ); - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", gmin->reserved ); - return 0; -} - -static int isom_print_text( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_text_t *text = (isom_text_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Text Media Information Box" ); - lsmash_ifprintf( fp, indent, "Unknown matrix\n" ); - isom_ifprintf_matrix( fp, indent + 1, text->matrix ); - return 0; -} - -static int isom_print_dinf( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Data Information Box" ); -} - -static int isom_print_dref( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_dref_t *dref = (isom_dref_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Data Reference Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu16"\n", dref->list.entry_count ); - return 0; -} - -static int isom_print_url( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_dref_entry_t *url = (isom_dref_entry_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Data Entry Url Box" ); - if( url->flags & 0x000001 ) - lsmash_ifprintf( fp, indent, "location = in the same file\n" ); - else - lsmash_ifprintf( fp, indent, "location = %s\n", url->location ); - return 0; -} - -static int isom_print_stbl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Sample Table Box" ); -} - -static int isom_print_stsd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_stsd_t *stsd = (isom_stsd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Sample Description Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stsd->entry_count ); - return 0; -} - -static int isom_print_visual_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_visual_entry_t *visual = (isom_visual_entry_t *)box; - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: Visual Description]\n", isom_4cc2str( visual->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", visual->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", visual->size ); - isom_ifprintf_sample_description_common_reserved( fp, indent, visual->reserved ); - lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", visual->data_reference_index ); - if( file->qt_compatible ) - { - lsmash_ifprintf( fp, indent, "version = %"PRId16"\n", visual->version ); - lsmash_ifprintf( fp, indent, "revision_level = %"PRId16"\n", visual->revision_level ); - lsmash_ifprintf( fp, indent, "vendor = %s\n", isom_4cc2str( visual->vendor ) ); - lsmash_ifprintf( fp, indent, "temporalQuality = %"PRIu32"\n", visual->temporalQuality ); - lsmash_ifprintf( fp, indent, "spatialQuality = %"PRIu32"\n", visual->spatialQuality ); - lsmash_ifprintf( fp, indent, "width = %"PRIu16"\n", visual->width ); - lsmash_ifprintf( fp, indent, "height = %"PRIu16"\n", visual->height ); - lsmash_ifprintf( fp, indent, "horizresolution = %f\n", lsmash_fixed2double( visual->horizresolution, 16 ) ); - lsmash_ifprintf( fp, indent, "vertresolution = %f\n", lsmash_fixed2double( visual->vertresolution, 16 ) ); - lsmash_ifprintf( fp, indent, "dataSize = %"PRIu32"\n", visual->dataSize ); - lsmash_ifprintf( fp, indent, "frame_count = %"PRIu16"\n", visual->frame_count ); - lsmash_ifprintf( fp, indent, "compressorname_length = %"PRIu8"\n", visual->compressorname[0] ); - lsmash_ifprintf( fp, indent, "compressorname = %s\n", visual->compressorname + 1 ); - lsmash_ifprintf( fp, indent, "depth = 0x%04"PRIx16, visual->depth ); - if( visual->depth == 32 ) - fprintf( fp, " (colour with alpha)\n" ); - else if( visual->depth >= 33 && visual->depth <= 40 ) - fprintf( fp, " (grayscale with no alpha)\n" ); - else - fprintf( fp, "\n" ); - lsmash_ifprintf( fp, indent, "color_table_ID = %"PRId16"\n", visual->color_table_ID ); - if( visual->color_table_ID == 0 ) - isom_pring_qt_color_table( fp, indent, &visual->color_table ); - } - else - { - lsmash_ifprintf( fp, indent, "pre_defined = 0x%04"PRIx16"\n", visual->version ); - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", visual->revision_level ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", visual->vendor ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", visual->temporalQuality ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%08"PRIx32"\n", visual->spatialQuality ); - lsmash_ifprintf( fp, indent, "width = %"PRIu16"\n", visual->width ); - lsmash_ifprintf( fp, indent, "height = %"PRIu16"\n", visual->height ); - lsmash_ifprintf( fp, indent, "horizresolution = %f\n", lsmash_fixed2double( visual->horizresolution, 16 ) ); - lsmash_ifprintf( fp, indent, "vertresolution = %f\n", lsmash_fixed2double( visual->vertresolution, 16 ) ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", visual->dataSize ); - lsmash_ifprintf( fp, indent, "frame_count = %"PRIu16"\n", visual->frame_count ); - lsmash_ifprintf( fp, indent, "compressorname_length = %"PRIu8"\n", visual->compressorname[0] ); - lsmash_ifprintf( fp, indent, "compressorname = %s\n", visual->compressorname + 1 ); - lsmash_ifprintf( fp, indent, "depth = 0x%04"PRIx16, visual->depth ); - if( visual->depth == 0x0018 ) - fprintf( fp, " (colour with no alpha)\n" ); - else if( visual->depth == 0x0028 ) - fprintf( fp, " (grayscale with no alpha)\n" ); - else if( visual->depth == 0x0020 ) - fprintf( fp, " (gray or colour with alpha)\n" ); - else - fprintf( fp, "\n" ); - lsmash_ifprintf( fp, indent, "pre_defined = 0x%04"PRIx16"\n", visual->color_table_ID ); - } - return 0; -} - -static int isom_print_glbl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_glbl_t *glbl = (isom_glbl_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Global Header Box" ); - if( glbl->header_data ) - { - lsmash_ifprintf( fp, indent, "global_header[]\n" ); - for( uint32_t i = 0; i < glbl->header_size; i += 8 ) - { - lsmash_ifprintf( fp, indent + 1, "" ); - for( uint32_t j = 0; ; j++ ) - if( j == 7 || (i + j == glbl->header_size - 1) ) - { - fprintf( fp, "0x%02"PRIx8"\n", glbl->header_data[i + j] ); - break; - } - else - fprintf( fp, "0x%02"PRIx8" ", glbl->header_data[i + j] ); - } - } - return 0; -} - -static int isom_print_clap( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_clap_t *clap = (isom_clap_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Clean Aperture Box" ); - lsmash_ifprintf( fp, indent, "cleanApertureWidthN = %"PRIu32"\n", clap->cleanApertureWidthN ); - lsmash_ifprintf( fp, indent, "cleanApertureWidthD = %"PRIu32"\n", clap->cleanApertureWidthD ); - lsmash_ifprintf( fp, indent, "cleanApertureHeightN = %"PRIu32"\n", clap->cleanApertureHeightN ); - lsmash_ifprintf( fp, indent, "cleanApertureHeightD = %"PRIu32"\n", clap->cleanApertureHeightD ); - lsmash_ifprintf( fp, indent, "horizOffN = %"PRId32"\n", clap->horizOffN ); - lsmash_ifprintf( fp, indent, "horizOffD = %"PRIu32"\n", clap->horizOffD ); - lsmash_ifprintf( fp, indent, "vertOffN = %"PRId32"\n", clap->vertOffN ); - lsmash_ifprintf( fp, indent, "vertOffD = %"PRIu32"\n", clap->vertOffD ); - return 0; -} - -static int isom_print_pasp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_pasp_t *pasp = (isom_pasp_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Pixel Aspect Ratio Box" ); - lsmash_ifprintf( fp, indent, "hSpacing = %"PRIu32"\n", pasp->hSpacing ); - lsmash_ifprintf( fp, indent, "vSpacing = %"PRIu32"\n", pasp->vSpacing ); - return 0; -} - -static int isom_print_colr( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_colr_t *colr = (isom_colr_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, colr->manager & LSMASH_QTFF_BASE ? "Color Parameter Box" : "Colour Information Box" ); - lsmash_ifprintf( fp, indent, "color_parameter_type = %s\n", isom_4cc2str( colr->color_parameter_type ) ); - if( colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_NCLC - || colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) - { - lsmash_ifprintf( fp, indent, "primaries_index = %"PRIu16"\n", colr->primaries_index ); - lsmash_ifprintf( fp, indent, "transfer_function_index = %"PRIu16"\n", colr->transfer_function_index ); - lsmash_ifprintf( fp, indent, "matrix_index = %"PRIu16"\n", colr->matrix_index ); - if( colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) - { - if( colr->manager & LSMASH_INCOMPLETE_BOX ) - { - lsmash_ifprintf( fp, indent, "full_range_flag = N/A\n" ); - lsmash_ifprintf( fp, indent, "reserved = N/A\n" ); - } - else - { - lsmash_ifprintf( fp, indent, "full_range_flag = %"PRIu8"\n", colr->full_range_flag ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx8"\n", colr->reserved ); - } - } - } - return 0; -} - -static int isom_print_gama( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_gama_t *gama = (isom_gama_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Gamma Level Box" ); - if( gama->level == 0x00023333 ) - lsmash_ifprintf( fp, indent, "level = 2.2 (standard television video gamma)\n" ); - else - { - lsmash_ifprintf( fp, indent, "level = %f", lsmash_fixed2double( gama->level, 16 ) ); - if( gama->level == 0 ) - fprintf( fp, " (platform's standard gamma)" ); - else if( gama->level == 0xffffffff ) - fprintf( fp, " (no gamma-correction)" ); - fprintf( fp, "\n" ); - } - return 0; -} - -static int isom_print_fiel( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_fiel_t *fiel = (isom_fiel_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Field/Frame Information Box" ); - lsmash_ifprintf( fp, indent, "fields = %"PRIu8" (%s)\n", fiel->fields, fiel->fields > 1 ? "interlaced" : "progressive scan" ); - lsmash_ifprintf( fp, indent, "detail = %"PRIu8, fiel->detail ); - if( fiel->fields > 1 ) - { - static const char *field_orderings[5] = - { "unknown", "temporal top first", "temporal bottom first", "spatial first line early", "spatial first line late" }; - int ordering = 0; - if( fiel->fields == 2 ) - { - if( fiel->detail == QT_FIELD_ORDERINGS_TEMPORAL_TOP_FIRST ) - ordering = 1; - else if( fiel->detail == QT_FIELD_ORDERINGS_TEMPORAL_BOTTOM_FIRST ) - ordering = 2; - else if( fiel->detail == QT_FIELD_ORDERINGS_SPATIAL_FIRST_LINE_EARLY ) - ordering = 3; - else if( fiel->detail == QT_FIELD_ORDERINGS_SPATIAL_FIRST_LINE_LATE ) - ordering = 4; - } - fprintf( fp, " (%s)\n", field_orderings[ordering] ); - } - else - fprintf( fp, "\n" ); - return 0; -} - -static int isom_print_cspc( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_cspc_t *cspc = (isom_cspc_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Colorspace Box" ); - static const struct - { - lsmash_qt_pixel_format pixel_format; - char *description; - } unprintable_pixel_format_table[] = - { - { QT_PIXEL_FORMAT_TYPE_1_MONOCHROME, "1 bit indexed" }, - { QT_PIXEL_FORMAT_TYPE_2_INDEXED, "2 bit indexed" }, - { QT_PIXEL_FORMAT_TYPE_4_INDEXED, "4 bit indexed" }, - { QT_PIXEL_FORMAT_TYPE_8_INDEXED, "8 bit indexed" }, - { QT_PIXEL_FORMAT_TYPE_1_INDEXED_GRAY_WHITE_IS_ZERO, "1 bit indexed gray, white is zero" }, - { QT_PIXEL_FORMAT_TYPE_2_INDEXED_GRAY_WHITE_IS_ZERO, "2 bit indexed gray, white is zero" }, - { QT_PIXEL_FORMAT_TYPE_4_INDEXED_GRAY_WHITE_IS_ZERO, "4 bit indexed gray, white is zero" }, - { QT_PIXEL_FORMAT_TYPE_8_INDEXED_GRAY_WHITE_IS_ZERO, "8 bit indexed gray, white is zero" }, - { QT_PIXEL_FORMAT_TYPE_16BE555, "16 bit BE RGB 555" }, - { QT_PIXEL_FORMAT_TYPE_24RGB, "24 bit RGB" }, - { QT_PIXEL_FORMAT_TYPE_32ARGB, "32 bit ARGB" }, - { 0, NULL } - }; - for( int i = 0; unprintable_pixel_format_table[i].pixel_format; i++ ) - if( cspc->pixel_format == unprintable_pixel_format_table[i].pixel_format ) - { - lsmash_ifprintf( fp, indent, "pixel_format = 0x%08"PRIx32" (%s)\n", cspc->pixel_format, unprintable_pixel_format_table[i].description ); - return 0; - } - lsmash_ifprintf( fp, indent, "pixel_format = %s\n", isom_4cc2str( cspc->pixel_format ) ); - return 0; -} - -static int isom_print_sgbt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_sgbt_t *sgbt = (isom_sgbt_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Significant Bits Box" ); - lsmash_ifprintf( fp, indent, "significantBits = %"PRIu8"\n", sgbt->significantBits ); - return 0; -} - -static int isom_print_stsl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_stsl_t *stsl = (isom_stsl_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Sample Scale Box" ); - lsmash_ifprintf( fp, indent, "constraint_flag = %s\n", (stsl->constraint_flag & 0x01) ? "on" : "off" ); - lsmash_ifprintf( fp, indent, "scale_method = " ); - if( stsl->scale_method == ISOM_SCALE_METHOD_FILL ) - fprintf( fp, "'fill'\n" ); - else if( stsl->scale_method == ISOM_SCALE_METHOD_HIDDEN ) - fprintf( fp, "'hidden'\n" ); - else if( stsl->scale_method == ISOM_SCALE_METHOD_MEET ) - fprintf( fp, "'meet'\n" ); - else if( stsl->scale_method == ISOM_SCALE_METHOD_SLICE_X ) - fprintf( fp, "'slice' in the x-coodinate\n" ); - else if( stsl->scale_method == ISOM_SCALE_METHOD_SLICE_Y ) - fprintf( fp, "'slice' in the y-coodinate\n" ); - lsmash_ifprintf( fp, indent, "display_center_x = %"PRIu16"\n", stsl->display_center_x ); - lsmash_ifprintf( fp, indent, "display_center_y = %"PRIu16"\n", stsl->display_center_y ); - return 0; -} - -static int isom_print_audio_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_audio_entry_t *audio = (isom_audio_entry_t *)box; - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: Audio Description]\n", isom_4cc2str( audio->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", audio->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", audio->size ); - isom_ifprintf_sample_description_common_reserved( fp, indent, audio->reserved ); - lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", audio->data_reference_index ); - if( file->qt_compatible ) - { - lsmash_ifprintf( fp, indent, "version = %"PRId16"\n", audio->version ); - lsmash_ifprintf( fp, indent, "revision_level = %"PRId16"\n", audio->revision_level ); - lsmash_ifprintf( fp, indent, "vendor = %s\n", isom_4cc2str( audio->vendor ) ); - lsmash_ifprintf( fp, indent, "channelcount = %"PRIu16"\n", audio->channelcount ); - lsmash_ifprintf( fp, indent, "samplesize = %"PRIu16"\n", audio->samplesize ); - lsmash_ifprintf( fp, indent, "compression_ID = %"PRId16"\n", audio->compression_ID ); - lsmash_ifprintf( fp, indent, "packet_size = %"PRIu16"\n", audio->packet_size ); - } - else - { - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", audio->version ); - lsmash_ifprintf( fp, indent, "reserved = 0x%04"PRIx16"\n", audio->revision_level ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", audio->vendor ); - lsmash_ifprintf( fp, indent, "channelcount = %"PRIu16"\n", audio->channelcount ); - lsmash_ifprintf( fp, indent, "samplesize = %"PRIu16"\n", audio->samplesize ); - lsmash_ifprintf( fp, indent, "pre_defined = %"PRId16"\n", audio->compression_ID ); - lsmash_ifprintf( fp, indent, "reserved = %"PRIu16"\n", audio->packet_size ); - } - lsmash_ifprintf( fp, indent, "samplerate = %f\n", lsmash_fixed2double( audio->samplerate, 16 ) ); - if( audio->version == 1 && (audio->manager & LSMASH_QTFF_BASE) ) - { - lsmash_ifprintf( fp, indent, "samplesPerPacket = %"PRIu32"\n", audio->samplesPerPacket ); - lsmash_ifprintf( fp, indent, "bytesPerPacket = %"PRIu32"\n", audio->bytesPerPacket ); - lsmash_ifprintf( fp, indent, "bytesPerFrame = %"PRIu32"\n", audio->bytesPerFrame ); - lsmash_ifprintf( fp, indent, "bytesPerSample = %"PRIu32"\n", audio->bytesPerSample ); - } - else if( audio->version == 2 ) - { - lsmash_ifprintf( fp, indent, "sizeOfStructOnly = %"PRIu32"\n", audio->sizeOfStructOnly ); - lsmash_ifprintf( fp, indent, "audioSampleRate = %lf\n", lsmash_int2float64( audio->audioSampleRate ) ); - lsmash_ifprintf( fp, indent, "numAudioChannels = %"PRIu32"\n", audio->numAudioChannels ); - lsmash_ifprintf( fp, indent, "always7F000000 = 0x%08"PRIx32"\n", audio->always7F000000 ); - lsmash_ifprintf( fp, indent, "constBitsPerChannel = %"PRIu32"\n", audio->constBitsPerChannel ); - lsmash_ifprintf( fp, indent++, "formatSpecificFlags = 0x%08"PRIx32"\n", audio->formatSpecificFlags ); - if( isom_is_lpcm_audio( audio ) ) - { - lsmash_ifprintf( fp, indent, "sample format: " ); - if( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_FLOAT ) - fprintf( fp, "floating point\n" ); - else - { - fprintf( fp, "integer\n" ); - lsmash_ifprintf( fp, indent, "signedness: " ); - fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER ? "signed\n" : "unsigned\n" ); - } - if( audio->constBytesPerAudioPacket != 1 ) - { - lsmash_ifprintf( fp, indent, "endianness: " ); - fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN ? "big\n" : "little\n" ); - } - lsmash_ifprintf( fp, indent, "packed: " ); - if( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_PACKED ) - fprintf( fp, "yes\n" ); - else - { - fprintf( fp, "no\n" ); - lsmash_ifprintf( fp, indent, "alignment: " ); - fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH ? "high\n" : "low\n" ); - } - if( audio->numAudioChannels > 1 ) - { - lsmash_ifprintf( fp, indent, "interleved: " ); - fprintf( fp, audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED ? "no\n" : "yes\n" ); - } - } - lsmash_ifprintf( fp, --indent, "constBytesPerAudioPacket = %"PRIu32"\n", audio->constBytesPerAudioPacket ); - lsmash_ifprintf( fp, indent, "constLPCMFramesPerAudioPacket = %"PRIu32"\n", audio->constLPCMFramesPerAudioPacket ); - } - return 0; -} - -static int isom_print_wave( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Sound Information Decompression Parameters Box" ); -} - -static int isom_print_frma( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_frma_t *frma = (isom_frma_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Format Box" ); - lsmash_ifprintf( fp, indent, "data_format = %s\n", isom_4cc2str( frma->data_format ) ); - return 0; -} - -static int isom_print_enda( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_enda_t *enda = (isom_enda_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Audio Endian Box" ); - lsmash_ifprintf( fp, indent, "littleEndian = %s\n", enda->littleEndian ? "yes" : "no" ); - return 0; -} - -static int isom_print_terminator( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_terminator_t *terminator = (isom_terminator_t *)box; - int indent = level; - lsmash_ifprintf( fp, indent++, "[0x00000000: Terminator Box]\n" ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", terminator->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", terminator->size ); - return 0; -} - -static int isom_print_chan( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_chan_t *chan = (isom_chan_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Audio Channel Layout Box" ); - lsmash_ifprintf( fp, indent, "channelLayoutTag = 0x%08"PRIx32"\n", chan->channelLayoutTag ); - lsmash_ifprintf( fp, indent, "channelBitmap = 0x%08"PRIx32"\n", chan->channelBitmap ); - lsmash_ifprintf( fp, indent, "numberChannelDescriptions = %"PRIu32"\n", chan->numberChannelDescriptions ); - if( chan->numberChannelDescriptions ) - { - isom_channel_description_t *desc = chan->channelDescriptions; - for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) - { - lsmash_ifprintf( fp, indent++, "ChannelDescriptions[%"PRIu32"]\n", i ); - lsmash_ifprintf( fp, indent, "channelLabel = 0x%08"PRIx32"\n", desc->channelLabel ); - lsmash_ifprintf( fp, indent, "channelFlags = 0x%08"PRIx32"\n", desc->channelFlags ); - for( int j = 0; j < 3; j++ ) - lsmash_ifprintf( fp, indent, "coordinates[%d] = %f\n", j, lsmash_int2float32( desc->coordinates[j] ) ); - --indent; - } - } - return 0; -} - -static int isom_print_srat( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_srat_t *srat = (isom_srat_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Sampling Rate Box" ); - lsmash_ifprintf( fp, indent, "sampling_rate = %"PRIu32"\n", srat->sampling_rate ); - return 0; -} - -static int isom_print_text_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)box; - int indent = level; - lsmash_ifprintf( fp, indent++, "[text: QuickTime Text Description]\n" ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", text->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", text->size ); - isom_ifprintf_sample_description_common_reserved( fp, indent, text->reserved ); - lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", text->data_reference_index ); - lsmash_ifprintf( fp, indent, "displayFlags = 0x%08"PRId32"\n", text->displayFlags ); - lsmash_ifprintf( fp, indent, "textJustification = %"PRId32"\n", text->textJustification ); - lsmash_ifprintf( fp, indent, "bgColor\n" ); - isom_ifprintf_rgb_color( fp, indent + 1, text->bgColor ); - lsmash_ifprintf( fp, indent, "top = %"PRId16"\n", text->top ); - lsmash_ifprintf( fp, indent, "left = %"PRId16"\n", text->left ); - lsmash_ifprintf( fp, indent, "bottom = %"PRId16"\n", text->bottom ); - lsmash_ifprintf( fp, indent, "right = %"PRId16"\n", text->right ); - lsmash_ifprintf( fp, indent, "scrpStartChar = %"PRId32"\n", text->scrpStartChar ); - lsmash_ifprintf( fp, indent, "scrpHeight = %"PRId16"\n", text->scrpHeight ); - lsmash_ifprintf( fp, indent, "scrpAscent = %"PRId16"\n", text->scrpAscent ); - lsmash_ifprintf( fp, indent, "scrpFont = %"PRId16"\n", text->scrpFont ); - lsmash_ifprintf( fp, indent, "scrpFace = %"PRIu16"\n", text->scrpFace ); - lsmash_ifprintf( fp, indent, "scrpSize = %"PRId16"\n", text->scrpSize ); - lsmash_ifprintf( fp, indent, "scrpColor\n" ); - isom_ifprintf_rgb_color( fp, indent + 1, text->scrpColor ); - if( text->font_name_length ) - lsmash_ifprintf( fp, indent, "font_name = %s\n", text->font_name ); - return 0; -} - -static int isom_print_tx3g_description( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)box; - int indent = level; - lsmash_ifprintf( fp, indent++, "[tx3g: Timed Text Description]\n" ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", tx3g->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", tx3g->size ); - isom_ifprintf_sample_description_common_reserved( fp, indent, tx3g->reserved ); - lsmash_ifprintf( fp, indent, "data_reference_index = %"PRIu16"\n", tx3g->data_reference_index ); - lsmash_ifprintf( fp, indent, "displayFlags = 0x%08"PRId32"\n", tx3g->displayFlags ); - lsmash_ifprintf( fp, indent, "horizontal_justification = %"PRId8"\n", tx3g->horizontal_justification ); - lsmash_ifprintf( fp, indent, "vertical_justification = %"PRId8"\n", tx3g->vertical_justification ); - lsmash_ifprintf( fp, indent, "background_color_rgba\n" ); - isom_ifprintf_rgba_color( fp, indent + 1, tx3g->background_color_rgba ); - lsmash_ifprintf( fp, indent, "top = %"PRId16"\n", tx3g->top ); - lsmash_ifprintf( fp, indent, "left = %"PRId16"\n", tx3g->left ); - lsmash_ifprintf( fp, indent, "bottom = %"PRId16"\n", tx3g->bottom ); - lsmash_ifprintf( fp, indent, "right = %"PRId16"\n", tx3g->right ); - lsmash_ifprintf( fp, indent, "startChar = %"PRIu16"\n", tx3g->startChar ); - lsmash_ifprintf( fp, indent, "endChar = %"PRIu16"\n", tx3g->endChar ); - lsmash_ifprintf( fp, indent, "font_ID = %"PRIu16"\n", tx3g->font_ID ); - lsmash_ifprintf( fp, indent, "face_style_flags = %"PRIu8"\n", tx3g->face_style_flags ); - lsmash_ifprintf( fp, indent, "font_size = %"PRIu8"\n", tx3g->font_size ); - lsmash_ifprintf( fp, indent, "text_color_rgba\n" ); - isom_ifprintf_rgba_color( fp, indent + 1, tx3g->text_color_rgba ); - return 0; -} - -static int isom_print_ftab( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_ftab_t *)box)->list ) - return -1; - isom_ftab_t *ftab = (isom_ftab_t *)box; - int indent = level; - uint16_t i = 0; - isom_print_box_common( fp, indent++, box, "Font Table Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu16"\n", ftab->list->entry_count ); - for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next ) - { - isom_font_record_t *data = (isom_font_record_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu16"]\n", i++ ); - lsmash_ifprintf( fp, indent, "font_ID = %"PRIu16"\n", data->font_ID ); - if( data->font_name_length ) - lsmash_ifprintf( fp, indent, "font_name = %s\n", data->font_name ); - --indent; - } - return 0; -} - -static int isom_print_sample_description_extesion( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - extern int mp4sys_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int h264_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int hevc_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int h264_print_bitrate( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int vc1_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int ac3_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int eac3_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int dts_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - extern int alac_print_codec_specific( FILE *, lsmash_file_t *, isom_box_t *, int ); - static struct print_description_extension_table_tag - { - lsmash_box_type_t type; - int (*print_func)( FILE *, lsmash_file_t *, isom_box_t *, int ); - } print_description_extension_table[32] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } }; - if( !print_description_extension_table[0].print_func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( type, func ) \ - print_description_extension_table[i++] = (struct print_description_extension_table_tag){ type, func } - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_print_clap ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_print_pasp ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_print_stsl ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_print_colr ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_print_colr ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_print_gama ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_print_fiel ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_print_cspc ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_print_sgbt ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, isom_print_ctab ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_print_glbl ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_print_wave ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_print_chan ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, mp4sys_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_AVCC, h264_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, h264_print_bitrate ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_HVCC, hevc_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DVC1, vc1_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DAC3, ac3_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DEC3, eac3_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_DDTS, dts_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_ALAC, alac_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, isom_print_ftab ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_ESDS, mp4sys_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( QT_BOX_TYPE_ALAC, alac_print_codec_specific ); - ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); -#undef ADD_PRINT_DESCRIPTION_EXTENSION_TABLE_ELEMENT - } - for( int i = 0; print_description_extension_table[i].print_func; i++ ) - if( lsmash_check_box_type_identical( box->type, print_description_extension_table[i].type ) ) - return print_description_extension_table[i].print_func( fp, file, box, level ); - return isom_print_unknown( fp, file, box, level ); -} - -static int isom_print_stts( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_stts_t *)box)->list ) - return -1; - isom_stts_t *stts = (isom_stts_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Decoding Time to Sample Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stts->list->entry_count ); - for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next ) - { - isom_stts_entry_t *data = (isom_stts_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); - lsmash_ifprintf( fp, indent--, "sample_delta = %"PRIu32"\n", data->sample_delta ); - } - return 0; -} - -static int isom_print_ctts( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_ctts_t *)box)->list ) - return -1; - isom_ctts_t *ctts = (isom_ctts_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Composition Time to Sample Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", ctts->list->entry_count ); - if( file->qt_compatible || ctts->version == 1 ) - for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) - { - isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); - lsmash_ifprintf( fp, indent--, "sample_offset = %"PRId32"\n", (union {uint32_t ui; int32_t si;}){ data->sample_offset }.si ); - } - else - for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) - { - isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); - lsmash_ifprintf( fp, indent--, "sample_offset = %"PRIu32"\n", data->sample_offset ); - } - return 0; -} - -static int isom_print_cslg( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_cslg_t *cslg = (isom_cslg_t *)box; - int indent = level; - if( file->qt_compatible ) - { - isom_print_box_common( fp, indent++, box, "Composition Shift Least Greatest Box" ); - lsmash_ifprintf( fp, indent, "compositionOffsetToDTDDeltaShift = %"PRId32"\n", cslg->compositionToDTSShift ); - lsmash_ifprintf( fp, indent, "leastDecodeToDisplayDelta = %"PRId32"\n", cslg->leastDecodeToDisplayDelta ); - lsmash_ifprintf( fp, indent, "greatestDecodeToDisplayDelta = %"PRId32"\n", cslg->greatestDecodeToDisplayDelta ); - lsmash_ifprintf( fp, indent, "displayStartTime = %"PRId32"\n", cslg->compositionStartTime ); - lsmash_ifprintf( fp, indent, "displayEndTime = %"PRId32"\n", cslg->compositionEndTime ); - } - else - { - isom_print_box_common( fp, indent++, box, "Composition to Decode Box" ); - lsmash_ifprintf( fp, indent, "compositionToDTSShift = %"PRId32"\n", cslg->compositionToDTSShift ); - lsmash_ifprintf( fp, indent, "leastDecodeToDisplayDelta = %"PRId32"\n", cslg->leastDecodeToDisplayDelta ); - lsmash_ifprintf( fp, indent, "greatestDecodeToDisplayDelta = %"PRId32"\n", cslg->greatestDecodeToDisplayDelta ); - lsmash_ifprintf( fp, indent, "compositionStartTime = %"PRId32"\n", cslg->compositionStartTime ); - lsmash_ifprintf( fp, indent, "compositionEndTime = %"PRId32"\n", cslg->compositionEndTime ); - } - return 0; -} - -static int isom_print_stss( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_stss_t *)box)->list ) - return -1; - isom_stss_t *stss = (isom_stss_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Sync Sample Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stss->list->entry_count ); - for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next ) - lsmash_ifprintf( fp, indent, "sample_number[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stss_entry_t *)entry->data)->sample_number ); - return 0; -} - -static int isom_print_stps( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_stps_t *)box)->list ) - return -1; - isom_stps_t *stps = (isom_stps_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Partial Sync Sample Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stps->list->entry_count ); - for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next ) - lsmash_ifprintf( fp, indent, "sample_number[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stps_entry_t *)entry->data)->sample_number ); - return 0; -} - -static int isom_print_sdtp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_sdtp_t *)box)->list ) - return -1; - isom_sdtp_t *sdtp = (isom_sdtp_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Independent and Disposable Samples Box" ); - for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next ) - { - isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - if( data->is_leading || data->sample_depends_on || data->sample_is_depended_on || data->sample_has_redundancy ) - { - if( file->avc_extensions ) - { - if( data->is_leading & ISOM_SAMPLE_IS_UNDECODABLE_LEADING ) - lsmash_ifprintf( fp, indent, "undecodable leading\n" ); - else if( data->is_leading & ISOM_SAMPLE_IS_NOT_LEADING ) - lsmash_ifprintf( fp, indent, "non-leading\n" ); - else if( data->is_leading & ISOM_SAMPLE_IS_DECODABLE_LEADING ) - lsmash_ifprintf( fp, indent, "decodable leading\n" ); - } - else if( data->is_leading & QT_SAMPLE_EARLIER_PTS_ALLOWED ) - lsmash_ifprintf( fp, indent, "early display times allowed\n" ); - if( data->sample_depends_on & ISOM_SAMPLE_IS_INDEPENDENT ) - lsmash_ifprintf( fp, indent, "independent\n" ); - else if( data->sample_depends_on & ISOM_SAMPLE_IS_NOT_INDEPENDENT ) - lsmash_ifprintf( fp, indent, "dependent\n" ); - if( data->sample_is_depended_on & ISOM_SAMPLE_IS_NOT_DISPOSABLE ) - lsmash_ifprintf( fp, indent, "non-disposable\n" ); - else if( data->sample_is_depended_on & ISOM_SAMPLE_IS_DISPOSABLE ) - lsmash_ifprintf( fp, indent, "disposable\n" ); - if( data->sample_has_redundancy & ISOM_SAMPLE_HAS_REDUNDANCY ) - lsmash_ifprintf( fp, indent, "redundant\n" ); - else if( data->sample_has_redundancy & ISOM_SAMPLE_HAS_NO_REDUNDANCY ) - lsmash_ifprintf( fp, indent, "non-redundant\n" ); - } - else - lsmash_ifprintf( fp, indent, "no description\n" ); - --indent; - } - return 0; -} - -static int isom_print_stsc( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_stsc_t *)box)->list ) - return -1; - isom_stsc_t *stsc = (isom_stsc_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Sample To Chunk Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stsc->list->entry_count ); - for( lsmash_entry_t *entry = stsc->list->head; entry; entry = entry->next ) - { - isom_stsc_entry_t *data = (isom_stsc_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "first_chunk = %"PRIu32"\n", data->first_chunk ); - lsmash_ifprintf( fp, indent, "samples_per_chunk = %"PRIu32"\n", data->samples_per_chunk ); - lsmash_ifprintf( fp, indent--, "sample_description_index = %"PRIu32"\n", data->sample_description_index ); - } - return 0; -} - -static int isom_print_stsz( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_stsz_t *stsz = (isom_stsz_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Sample Size Box" ); - if( !stsz->sample_size ) - lsmash_ifprintf( fp, indent, "sample_size = 0 (variable)\n" ); - else - lsmash_ifprintf( fp, indent, "sample_size = %"PRIu32" (constant)\n", stsz->sample_size ); - lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", stsz->sample_count ); - if( !stsz->sample_size && stsz->list ) - for( lsmash_entry_t *entry = stsz->list->head; entry; entry = entry->next ) - { - isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; - 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 ) - return -1; - isom_stco_t *stco = (isom_stco_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Chunk Offset Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", stco->list->entry_count ); - if( lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_STCO ) ) - { - for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) - lsmash_ifprintf( fp, indent, "chunk_offset[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stco_entry_t *)entry->data)->chunk_offset ); - } - else - { - for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) - lsmash_ifprintf( fp, indent, "chunk_offset[%"PRIu32"] = %"PRIu64"\n", i++, ((isom_co64_entry_t *)entry->data)->chunk_offset ); - } - return 0; -} - -static int isom_print_sgpd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_sgpd_t *)box)->list ) - return -1; - isom_sgpd_t *sgpd = (isom_sgpd_t *)box; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Sample Group Description Box" ); - lsmash_ifprintf( fp, indent, "grouping_type = %s\n", isom_4cc2str( sgpd->grouping_type ) ); - if( sgpd->version == 1 ) - { - lsmash_ifprintf( fp, indent, "default_length = %"PRIu32, sgpd->default_length ); - fprintf( fp, " %s\n", sgpd->default_length ? "(constant)" : "(variable)" ); - } - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", sgpd->list->entry_count ); - switch( sgpd->grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) - { - if( sgpd->version == 1 && !sgpd->default_length ) - lsmash_ifprintf( fp, indent, "description_length[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_rap_entry_t *)entry->data)->description_length ); - else - { - isom_rap_entry_t *rap = (isom_rap_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "num_leading_samples_known = %"PRIu8"\n", rap->num_leading_samples_known ); - lsmash_ifprintf( fp, indent--, "num_leading_samples = %"PRIu8"\n", rap->num_leading_samples ); - } - } - break; - case ISOM_GROUP_TYPE_ROLL : - for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) - { - if( sgpd->version == 1 && !sgpd->default_length ) - lsmash_ifprintf( fp, indent, "description_length[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_roll_entry_t *)entry->data)->description_length ); - else - lsmash_ifprintf( fp, indent, "roll_distance[%"PRIu32"] = %"PRId16"\n", i++, ((isom_roll_entry_t *)entry->data)->roll_distance ); - } - break; - default : - break; - } - return 0; -} - -static int isom_print_sbgp( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_sbgp_t *)box)->list ) - return -1; - isom_sbgp_t *sbgp = (isom_sbgp_t *)box; - int indent = level; - int is_fragment = sbgp->parent && lsmash_check_box_type_identical( sbgp->parent->type, ISOM_BOX_TYPE_TRAF ); - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Sample to Group Box" ); - lsmash_ifprintf( fp, indent, "grouping_type = %s\n", isom_4cc2str( sbgp->grouping_type ) ); - if( sbgp->version == 1 ) - lsmash_ifprintf( fp, indent, "grouping_type_parameter = %s\n", isom_4cc2str( sbgp->grouping_type_parameter ) ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", 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; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", data->sample_count ); - lsmash_ifprintf( fp, indent--, "group_description_index = %"PRIu32, data->group_description_index ); - if( is_fragment && data->group_description_index >= 0x10000 ) - fprintf( fp, " (i.e. %"PRIu32" for this fragment-local group)", data->group_description_index - 0x10000 ); - if( !data->group_description_index ) - fprintf( fp, " (not in this grouping type)\n" ); - else - fprintf( fp, "\n" ); - } - return 0; -} - -static int isom_print_udta( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "User Data Box" ); -} - -static int isom_print_chpl( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_chpl_t *chpl = (isom_chpl_t *)box; - uint32_t timescale; - if( !chpl->version ) - { - if( !file || !file->moov || !file->moov->mvhd ) - return -1; - timescale = file->moov->mvhd->timescale; - } - else - timescale = 10000000; - int indent = level; - uint32_t i = 0; - isom_print_box_common( fp, indent++, box, "Chapter List Box" ); - if( chpl->version == 1 ) - { - lsmash_ifprintf( fp, indent, "unknown = 0x%02"PRIx8"\n", chpl->unknown ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", chpl->list->entry_count ); - } - else - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu8"\n", (uint8_t)chpl->list->entry_count ); - for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next ) - { - isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data; - int64_t start_time = data->start_time / timescale; - int hh = start_time / 3600; - int mm = (start_time / 60) % 60; - int ss = start_time % 60; - int ms = ((data->start_time / (double)timescale) - hh * 3600 - mm * 60 - ss) * 1e3 + 0.5; - int with_bom = 0; - if( !memcmp( data->chapter_name, "\xEF\xBB\xBF", 3 ) ) /* detect BOM */ - { - data->chapter_name += 3; - with_bom = 1; - } - lsmash_ifprintf( fp, indent++, "chapter[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "start_time = %02d:%02d:%02d.%03d\n", hh, mm, ss, ms ); - lsmash_ifprintf( fp, indent--, with_bom ? "chapter_name = %s ( it has BOM in it )\n" : "chapter_name = %s\n", data->chapter_name ); - } - return 0; -} - -static int isom_print_meta( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - int indent = level; - if( !(box->manager & LSMASH_QTFF_BASE) ) - { - isom_print_basebox_common( fp, indent++, box, "Meta Box" ); - lsmash_ifprintf( fp, indent, "version = %"PRIu8"\n", box->version ); - lsmash_ifprintf( fp, indent, "flags = 0x%06"PRIx32"\n", box->flags & 0x00ffffff ); - } - else - isom_print_basebox_common( fp, indent, box, "Metadata Box" ); - return 0; -} - -static int isom_print_keys( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - if( !((isom_keys_t *)box)->list ) - return -1; - isom_keys_t *keys = (isom_keys_t *)box; - int indent = level; - uint32_t i = 1; - isom_print_box_common( fp, indent++, box, "Metadata Item Keys Box" ); - lsmash_ifprintf( fp, indent, "entry_count = %"PRIu32"\n", keys->list->entry_count ); - for( lsmash_entry_t *entry = keys->list->head; entry; entry = entry->next ) - { - isom_keys_entry_t *data = (isom_keys_entry_t *)entry->data; - lsmash_ifprintf( fp, indent++, "[key %"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "key_size = %"PRIu32"\n", data->key_size ); - lsmash_ifprintf( fp, indent, "key_namespace = %s\n", isom_4cc2str( data->key_namespace ) ); - uint32_t value_length = data->key_size - 8; - char str[value_length + 1]; - memcpy( str, data->key_value, value_length ); - str[value_length] = 0; - lsmash_ifprintf( fp, indent--, "key_value = %s\n", str ); - } - return 0; -} - -static int isom_print_ilst( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Metadata Item List Box" ); -} - -static int isom_print_metaitem( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_metaitem_t *metaitem = (isom_metaitem_t *)box; - if( box->parent && box->parent->parent && (box->parent->parent->manager & LSMASH_QTFF_BASE) ) - { - int indent = level; - lsmash_ifprintf( fp, indent++, "[key_index %"PRIu32": Metadata Item Box]\n", box->type.fourcc ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - return 0; - } - static const struct - { - lsmash_itunes_metadata_item item; - char *name; - } metaitem_table[] = - { - { ITUNES_METADATA_ITEM_ALBUM_NAME, "Album Name" }, - { ITUNES_METADATA_ITEM_ARTIST, "Artist" }, - { ITUNES_METADATA_ITEM_USER_COMMENT, "User Comment" }, - { ITUNES_METADATA_ITEM_RELEASE_DATE, "Release Date" }, - { ITUNES_METADATA_ITEM_ENCODED_BY, "Encoded By" }, - { ITUNES_METADATA_ITEM_USER_GENRE, "User Genre" }, - { ITUNES_METADATA_ITEM_GROUPING, "Grouping" }, - { ITUNES_METADATA_ITEM_LYRICS, "Lyrics" }, - { ITUNES_METADATA_ITEM_TITLE, "Title" }, - { ITUNES_METADATA_ITEM_ENCODING_TOOL, "Encoding Tool" }, - { ITUNES_METADATA_ITEM_COMPOSER, "Composer" }, - { ITUNES_METADATA_ITEM_ALBUM_ARTIST, "Album Artist" }, - { ITUNES_METADATA_ITEM_PODCAST_CATEGORY, "Podcast Category" }, - { ITUNES_METADATA_ITEM_COVER_ART, "Cover Art" }, - { ITUNES_METADATA_ITEM_DISC_COMPILATION, "Disc Compilation" }, - { ITUNES_METADATA_ITEM_COPYRIGHT, "Copyright" }, - { ITUNES_METADATA_ITEM_DESCRIPTION, "Description" }, - { ITUNES_METADATA_ITEM_DISC_NUMBER, "Disc Number" }, - { ITUNES_METADATA_ITEM_EPISODE_GLOBAL_ID, "Episode Global Unique ID" }, - { ITUNES_METADATA_ITEM_PREDEFINED_GENRE, "Pre-defined Genre" }, - { ITUNES_METADATA_ITEM_GROUPING_DRAFT, "Grouping (Overall work like TIT1 in ID3)" }, - { ITUNES_METADATA_ITEM_HIGH_DEFINITION_VIDEO, "High Definition Video" }, - { ITUNES_METADATA_ITEM_PODCAST_KEYWORD, "Podcast Keyword" }, - { ITUNES_METADATA_ITEM_LONG_DESCRIPTION, "Long Description" }, - { ITUNES_METADATA_ITEM_PODCAST, "Podcast" }, - { ITUNES_METADATA_ITEM_GAPLESS_PLAYBACK, "Gapless Playback" }, - { ITUNES_METADATA_ITEM_PURCHASE_DATE, "Purchase Date" }, - { ITUNES_METADATA_ITEM_PODCAST_URL, "Podcast URL" }, - { ITUNES_METADATA_ITEM_CONTENT_RATING, "Content Rating" }, - { ITUNES_METADATA_ITEM_MEDIA_TYPE, "Media Type" }, - { ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, "Beats Per Minute" }, - { ITUNES_METADATA_ITEM_TRACK_NUMBER, "Track Number" }, - { ITUNES_METADATA_ITEM_TV_EPISODE_ID, "TV Episode ID" }, - { ITUNES_METADATA_ITEM_TV_EPISODE, "TV Episode" }, - { ITUNES_METADATA_ITEM_TV_NETWORK, "TV Network" }, - { ITUNES_METADATA_ITEM_TV_SHOW_NAME, "TV Show Name" }, - { ITUNES_METADATA_ITEM_TV_SEASON, "TV Season" }, - { ITUNES_METADATA_ITEM_ITUNES_PURCHASE_ACCOUNT_ID, "iTunes Account Used for Purchase" }, - { ITUNES_METADATA_ITEM_ITUNES_ACCOUNT_TYPE, "iTunes Account Type" }, - { ITUNES_METADATA_ITEM_ITUNES_ARTIST_ID, "iTunes Artist ID" }, - { ITUNES_METADATA_ITEM_ITUNES_COMPOSER_ID, "iTunes Composer ID" }, - { ITUNES_METADATA_ITEM_ITUNES_CATALOG_ID, "iTunes Catalog ID" }, - { ITUNES_METADATA_ITEM_ITUNES_TV_GENRE_ID, "iTunes TV Genre ID" }, - { ITUNES_METADATA_ITEM_ITUNES_PLAYLIST_ID, "iTunes Playlist ID" }, - { ITUNES_METADATA_ITEM_ITUNES_COUNTRY_CODE, "iTunes Country Code" }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM, "Sort Album" }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ARTIST, "Sort Artist" }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_ALBUM_ARTIST, "Sort Album Artist" }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_COMPOSER, "Sort Composer" }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_NAME, "Sort Name" }, - { ITUNES_METADATA_ITEM_ITUNES_SORT_SHOW, "Sort Show" }, - { ITUNES_METADATA_ITEM_CUSTOM, "Custom Metadata Item" }, - { 0, NULL } - }; - char *name = NULL; - int i; - for( i = 0; metaitem_table[i].name; i++ ) - if( metaitem->type.fourcc == metaitem_table[i].item ) - { - name = metaitem_table[i].name; - break; - } - if( !name ) - name = "Unknown"; - uint32_t name_length = strlen( name ); - uint32_t display_name_length = name_length + 20; - char display_name[display_name_length + 1]; - memcpy( display_name, "Metadata Item Box (", 19 ); - memcpy( display_name + 19, name, name_length ); - display_name[display_name_length - 1] = ')'; - display_name[display_name_length] = 0; - return isom_print_simple( fp, box, level, display_name ); -} - -static int isom_print_name( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_name_t *name = (isom_name_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Name Box" ); - char str[name->name_length + 1]; - memcpy( str, name->name, name->name_length ); - str[name->name_length] = 0; - lsmash_ifprintf( fp, indent, "name = %s\n", str ); - return 0; -} - -static int isom_print_mean( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_mean_t *mean = (isom_mean_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Mean Box" ); - char str[mean->meaning_string_length + 1]; - memcpy( str, mean->meaning_string, mean->meaning_string_length ); - str[mean->meaning_string_length] = 0; - lsmash_ifprintf( fp, indent, "meaning_string = %s\n", str ); - return 0; -} - -static int isom_print_data( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_data_t *data = (isom_data_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Data Box" ); - if( box->parent && box->parent->parent && box->parent->parent->parent - && (box->parent->parent->parent->manager & LSMASH_QTFF_BASE) ) - { - uint32_t type_set_indicator = data->reserved >> 8; - uint32_t well_known_type = ((data->reserved << 16) | (data->type_set_identifier << 8) | data->type_code) & 0xffffff; - char *well_known_type_name; - static const struct - { - uint32_t type; - char *name; - } well_known_type_table[] = - { - { 0, "reserved" }, - { 1, "UTF-8" }, - { 2, "UTF-16 BE" }, - { 3, "S/JIS" }, - { 4, "UTF-8 sort" }, - { 5, "UTF-16 sort" }, - { 13, "JPEG in a JFIF wrapper" }, - { 14, "PNG in a PNG wrapper" }, - { 21, "BE Signed Integer" }, - { 22, "BE Unsigned Integer" }, - { 23, "BE Float32" }, - { 24, "BE Float64" }, - { 27, "BMP (Windows bitmap format graphics)" }, - { 28, "QuickTime Metadata box" }, - { UINT32_MAX } - }; - int table_index; - for( table_index = 0; well_known_type_table[table_index].type != UINT32_MAX; table_index++ ) - if( well_known_type == well_known_type_table[table_index].type ) - { - well_known_type_name = well_known_type_table[table_index].name; - break; - } - if( well_known_type_table[table_index].type == UINT32_MAX ) - well_known_type_name = "Unknown"; - lsmash_ifprintf( fp, indent, "type_set_indicator = %"PRIu8"\n", type_set_indicator ); - lsmash_ifprintf( fp, indent, "well_known_type = %"PRIu32" (%s)\n", well_known_type, well_known_type_name ); - lsmash_ifprintf( fp, indent, "locale_indicator = %"PRIu32"\n", data->the_locale ); - if( data->value_length == 0 ) - { - lsmash_ifprintf( fp, indent, "value = (null)\n" ); - return 0; - } - if( well_known_type == 1 ) - { - /* UTF-8 without any count or null terminator */ - char str[data->value_length + 1]; - memcpy( str, data->value, data->value_length ); - str[data->value_length] = 0; - lsmash_ifprintf( fp, indent, "value = %s\n", str ); - } - else if( well_known_type == 13 || well_known_type == 14 || well_known_type == 27 ) - lsmash_ifprintf( fp, indent, "value = (binary data)\n" ); - else if( well_known_type == 21 && data->value_length <= 4 ) - /* a big-endian signed integer in 1,2,3 or 4 bytes */ - goto show_in_signed_integer; - else if( well_known_type == 22 && data->value_length <= 4 ) - { - /* a big-endian unsigned integer in 1,2,3 or 4 bytes */ - uint32_t integer = data->value[0]; - for( uint32_t i = 1; i < data->value_length; i++ ) - integer = (integer << 8) | data->value[i]; - lsmash_ifprintf( fp, indent, "value = %"PRIu32"\n", integer ); - } - else if( well_known_type == 23 && data->value_length == 4 ) - { - /* a big-endian 32-bit floating point value (IEEE754) */ - uint32_t float32 = (data->value[0] << 24) | (data->value[1] << 16) | (data->value[2] << 8) | data->value[3]; - lsmash_ifprintf( fp, indent, "value = %f\n", lsmash_int2float32( float32 ) ); - } - else if( well_known_type == 24 && data->value_length == 8 ) - { - /* a big-endian 64-bit floating point value (IEEE754) */ - uint64_t float64 = ((uint64_t)data->value[0] << 56) | ((uint64_t)data->value[1] << 48) - | ((uint64_t)data->value[2] << 40) | ((uint64_t)data->value[3] << 32) - | ((uint64_t)data->value[4] << 24) | ((uint64_t)data->value[5] << 16) - | ((uint64_t)data->value[6] << 8) | (uint64_t)data->value[7]; - lsmash_ifprintf( fp, indent, "value = %lf\n", lsmash_int2float64( float64 ) ); - } - else - goto show_in_binary; - } - else - { - char *basic_data_type_name; - static const struct - { - uint32_t type; - char *name; - } basic_data_type_table[] = - { - { 0, "Implicit" }, - { 1, "UTF-8" }, - { 2, "UTF-16 BE" }, - { 3, "S/JIS" }, - { 6, "HTML" }, - { 7, "XML" }, - { 8, "UUID" }, - { 9, "ISRC" }, - { 10, "MI3P" }, - { 12, "GIF" }, - { 13, "JPEG in a JFIF wrapper" }, - { 14, "PNG in a PNG wrapper" }, - { 15, "URL" }, - { 16, "duration" }, - { 17, "date/time" }, - { 18, "Genres" }, - { 21, "BE Signed Integer" }, - { 24, "RIAA-PA (RIAA Parental advisory)" }, - { 25, "UPC (Universal Product Code)" }, - { 27, "BMP (Windows bitmap format graphics)" }, - { UINT32_MAX } - }; - int table_index; - for( table_index = 0; basic_data_type_table[table_index].type != UINT32_MAX; table_index++ ) - if( data->type_code == basic_data_type_table[table_index].type ) - { - basic_data_type_name = basic_data_type_table[table_index].name; - break; - } - if( basic_data_type_table[table_index].type == UINT32_MAX ) - basic_data_type_name = "Unknown"; - lsmash_ifprintf( fp, indent, "reserved = %"PRIu16"\n", data->reserved ); - lsmash_ifprintf( fp, indent, "type_set_identifier = %"PRIu8"%s\n", - data->type_set_identifier, - data->type_set_identifier ? "" : " (basic type set)" ); - lsmash_ifprintf( fp, indent, "type_code = %"PRIu8" (%s)\n", data->type_code, basic_data_type_name ); - lsmash_ifprintf( fp, indent, "the_locale = %"PRIu32"\n", data->the_locale ); - if( data->value_length == 0 ) - { - lsmash_ifprintf( fp, indent, "value = (null)\n" ); - return 0; - } - if( data->type_code == 6 || data->type_code == 7 - || data->type_code == 12 || data->type_code == 13 - || data->type_code == 14 || data->type_code == 27 ) - lsmash_ifprintf( fp, indent, "value = (binary data)\n" ); - else if( data->type_code == 8 && data->value_length == 16 ) - /* UUID */ - lsmash_ifprintf( fp, indent, "value = 0x%08"PRIx32"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"-%04"PRIx16"0x%08"PRIx32"\n", - (data->value[ 0] << 24) | (data->value[ 1] << 16) | (data->value[ 2] << 8) | data->value[ 3], - (data->value[ 4] << 8) | data->value[ 5], - (data->value[ 6] << 8) | data->value[ 7], - (data->value[ 8] << 8) | data->value[ 9], - (data->value[10] << 8) | data->value[11], - (data->value[12] << 24) | (data->value[13] << 16) | (data->value[14] << 8) | data->value[15] ); - else if( data->type_code == 16 && data->value_length == 4 ) - { - /* duration in milliseconds */ - uint32_t duration = (data->value[0] << 24) | (data->value[1] << 16) | (data->value[2] << 8) | data->value[3]; - lsmash_ifprintf( fp, indent, "value = %"PRIu32" milliseconds\n", duration ); - } - else if( data->type_code == 17 && (data->value_length == 4 || data->value_length == 8) ) - { - /* UTC, counting seconds since midnight on 1 January, 1904 */ - uint64_t mp4time = (data->value[0] << 24) | (data->value[1] << 16) | (data->value[2] << 8) | data->value[3]; - if( data->value_length == 8 ) - { - /* 64bit */ - mp4time <<= 32; - mp4time |= (data->value[4] << 24) | (data->value[5] << 16) | (data->value[6] << 8) | data->value[7]; - } - isom_mp4time2utc( mp4time ); - } - else if( data->type_code == 21 && data->value_length <= 8 ) - /* a big-endian signed integer in 1,2,3,4 or 8 bytes */ - goto show_in_signed_integer; - else if( data->type_code == 24 ) - { - /* RIAA-PA (RIAA Parental advisory) 8-bit integer */ - lsmash_ifprintf( fp, indent, "value = %"PRIu8, data->value[0] ); - if( data->value[0] == -1 ) - fprintf( fp, " (no)" ); - else if( data->value[0] == 1 ) - fprintf( fp, " (yes)" ); - else if( data->value[0] == 0 ) - fprintf( fp, " (unspecified)" ); - fprintf( fp, "\n" ); - } - else if( data->type_code == 1 || data->type_code == 2 || data->type_code == 3 - || data->type_code == 9 || data->type_code == 10 || data->type_code == 15 - || data->type_code == 25 ) - { - /* String */ - char str[data->value_length + 1]; - memcpy( str, data->value, data->value_length ); - str[data->value_length] = 0; - lsmash_ifprintf( fp, indent, "value = %s\n", str ); - } - else - goto show_in_binary; - } - return 0; -show_in_signed_integer:; - uint64_t integer = data->value[0]; - uint64_t max_value = 0xff; - for( uint32_t i = 1; i < data->value_length; i++ ) - { - integer = (integer << 8) | data->value[i]; - max_value = (max_value << 8) | 0xff; - } - lsmash_ifprintf( fp, indent, "value = %"PRId64"\n", (int64_t)(integer | (integer > (max_value >> 1) ? ~max_value : 0)) ); - return 0; -show_in_binary: - lsmash_ifprintf( fp, indent, "value = " ); - if( data->value_length ) - { - fprintf( fp, "0x" ); - for( uint32_t i = 0; i < data->value_length; i++ ) - fprintf( fp, "%02"PRIx8, data->value[i] ); - } - fprintf( fp, "\n" ); - return 0; -} - -static int isom_print_WLOC( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_WLOC_t *WLOC = (isom_WLOC_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Window Location Box" ); - lsmash_ifprintf( fp, indent, "x = %"PRIu16"\n", WLOC->x ); - lsmash_ifprintf( fp, indent, "y = %"PRIu16"\n", WLOC->y ); - return 0; -} - -static int isom_print_LOOP( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_LOOP_t *LOOP = (isom_LOOP_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Looping Box" ); - lsmash_ifprintf( fp, indent, "looping_mode = %"PRIu32, LOOP->looping_mode ); - switch( LOOP->looping_mode ) - { - case 0 : - fprintf( fp, " (none)\n" ); - break; - case 1 : - fprintf( fp, " (looping)\n" ); - break; - case 2 : - fprintf( fp, " (palindromic looping)\n" ); - break; - default : - fprintf( fp, "\n" ); - break; - } - return 0; -} - -static int isom_print_SelO( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_SelO_t *SelO = (isom_SelO_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Play Selection Only Box" ); - lsmash_ifprintf( fp, indent, "selection_only = %"PRIu8"\n", SelO->selection_only ); - return 0; -} - -static int isom_print_AllF( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_AllF_t *AllF = (isom_AllF_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Play All Frames Box" ); - lsmash_ifprintf( fp, indent, "play_all_frames = %"PRIu8"\n", AllF->play_all_frames ); - return 0; -} - -static int isom_print_cprt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_cprt_t *cprt = (isom_cprt_t *)box; - int indent = level; - char str[cprt->notice_length + 1]; - memcpy( str, cprt->notice, cprt->notice_length ); - str[cprt->notice_length] = 0; - isom_print_box_common( fp, indent++, box, "Copyright Box" ); - lsmash_ifprintf( fp, indent, "language = %s\n", isom_unpack_iso_language( cprt->language ) ); - lsmash_ifprintf( fp, indent, "notice = %s\n", str ); - return 0; -} - -static int isom_print_mvex( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Movie Extends Box" ); -} - -static int isom_print_mehd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_mehd_t *mehd = (isom_mehd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Movie Extends Header Box" ); - if( file && file->moov && file->moov->mvhd ) - isom_ifprintf_duration( fp, indent, "fragment_duration", mehd->fragment_duration, file->moov->mvhd->timescale ); - else - isom_ifprintf_duration( fp, indent, "fragment_duration", mehd->fragment_duration, 0 ); - return 0; -} - -static int isom_print_trex( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_trex_t *trex = (isom_trex_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Extends Box" ); - lsmash_ifprintf( fp, indent, "track_ID = %"PRIu32"\n", trex->track_ID ); - lsmash_ifprintf( fp, indent, "default_sample_description_index = %"PRIu32"\n", trex->default_sample_description_index ); - lsmash_ifprintf( fp, indent, "default_sample_duration = %"PRIu32"\n", trex->default_sample_duration ); - lsmash_ifprintf( fp, indent, "default_sample_size = %"PRIu32"\n", trex->default_sample_size ); - isom_ifprintf_sample_flags( fp, indent, "default_sample_flags", &trex->default_sample_flags ); - return 0; -} - -static int isom_print_moof( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Movie Fragment Box" ); -} - -static int isom_print_mfhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_mfhd_t *mfhd = (isom_mfhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Movie Fragment Header Box" ); - lsmash_ifprintf( fp, indent, "sequence_number = %"PRIu32"\n", mfhd->sequence_number ); - return 0; -} - -static int isom_print_traf( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Track Fragment Box" ); -} - -static int isom_print_tfhd( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_tfhd_t *tfhd = (isom_tfhd_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Fragment Header Box" ); - ++indent; - if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) lsmash_ifprintf( fp, indent, "base-data-offset-present\n" ); - if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) lsmash_ifprintf( fp, indent, "sample-description-index-present\n" ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) lsmash_ifprintf( fp, indent, "default-sample-duration-present\n" ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) lsmash_ifprintf( fp, indent, "default-sample-size-present\n" ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) lsmash_ifprintf( fp, indent, "default-sample-flags-present\n" ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF ) lsmash_ifprintf( fp, indent, "default-base-is-moof\n" ); - lsmash_ifprintf( fp, --indent, "track_ID = %"PRIu32"\n", tfhd->track_ID ); - if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) - lsmash_ifprintf( fp, indent, "base_data_offset = %"PRIu64"\n", tfhd->base_data_offset ); - if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) - lsmash_ifprintf( fp, indent, "sample_description_index = %"PRIu32"\n", tfhd->sample_description_index ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) - lsmash_ifprintf( fp, indent, "default_sample_duration = %"PRIu32"\n", tfhd->default_sample_duration ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) - lsmash_ifprintf( fp, indent, "default_sample_size = %"PRIu32"\n", tfhd->default_sample_size ); - if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) - isom_ifprintf_sample_flags( fp, indent, "default_sample_flags", &tfhd->default_sample_flags ); - return 0; -} - -static int isom_print_tfdt( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_tfdt_t *tfdt = (isom_tfdt_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Fragment Base Media Decode Time Box" ); - lsmash_ifprintf( fp, indent, "baseMediaDecodeTime = %"PRIu64"\n", tfdt->baseMediaDecodeTime ); - return 0; -} - -static int isom_print_trun( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_trun_t *trun = (isom_trun_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Fragment Run Box" ); - ++indent; - if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) lsmash_ifprintf( fp, indent, "data-offset-present\n" ); - if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) lsmash_ifprintf( fp, indent, "first-sample-flags-present\n" ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) lsmash_ifprintf( fp, indent, "sample-duration-present\n" ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) lsmash_ifprintf( fp, indent, "sample-size-present\n" ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) lsmash_ifprintf( fp, indent, "sample-flags-present\n" ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) lsmash_ifprintf( fp, indent, "sample-composition-time-offsets-present\n" ); - lsmash_ifprintf( fp, --indent, "sample_count = %"PRIu32"\n", trun->sample_count ); - if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) - lsmash_ifprintf( fp, indent, "data_offset = %"PRId32"\n", trun->data_offset ); - if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) - isom_ifprintf_sample_flags( fp, indent, "first_sample_flags", &trun->first_sample_flags ); - if( trun->optional ) - { - uint32_t i = 0; - for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next ) - { - isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)entry->data; - lsmash_ifprintf( fp, indent++, "sample[%"PRIu32"]\n", i++ ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) - lsmash_ifprintf( fp, indent, "sample_duration = %"PRIu32"\n", row->sample_duration ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) - lsmash_ifprintf( fp, indent, "sample_size = %"PRIu32"\n", row->sample_size ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) - isom_ifprintf_sample_flags( fp, indent, "sample_flags", &row->sample_flags ); - if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) - { - if( trun->version == 0 ) - lsmash_ifprintf( fp, indent, "sample_composition_time_offset = %"PRIu32"\n", - row->sample_composition_time_offset ); - else - lsmash_ifprintf( fp, indent, "sample_composition_time_offset = %"PRId32"\n", - (union {uint32_t ui; int32_t si;}){ row->sample_composition_time_offset }.si ); - } - --indent; - } - } - return 0; -} - -static int isom_print_free( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Free Space Box" ); -} - -static int isom_print_mdat( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Media Data Box" ); -} - -static int isom_print_mfra( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - return isom_print_simple( fp, box, level, "Movie Fragment Random Access Box" ); -} - -static int isom_print_tfra( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_tfra_t *tfra = (isom_tfra_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Track Fragment Random Access Box" ); - lsmash_ifprintf( fp, indent, "track_ID = %"PRIu32"\n", tfra->track_ID ); - lsmash_ifprintf( fp, indent, "reserved = 0x%08"PRIx32"\n", tfra->reserved ); - lsmash_ifprintf( fp, indent, "length_size_of_traf_num = %"PRIu8"\n", tfra->length_size_of_traf_num ); - lsmash_ifprintf( fp, indent, "length_size_of_trun_num = %"PRIu8"\n", tfra->length_size_of_trun_num ); - lsmash_ifprintf( fp, indent, "length_size_of_sample_num = %"PRIu8"\n", tfra->length_size_of_sample_num ); - lsmash_ifprintf( fp, indent, "number_of_entry = %"PRIu32"\n", tfra->number_of_entry ); - if( tfra->list ) - { - uint32_t i = 0; - 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; - lsmash_ifprintf( fp, indent++, "entry[%"PRIu32"]\n", i++ ); - lsmash_ifprintf( fp, indent, "time = %"PRIu64"\n", data->time ); - lsmash_ifprintf( fp, indent, "moof_offset = %"PRIu64"\n", data->moof_offset ); - lsmash_ifprintf( fp, indent, "traf_number = %"PRIu32"\n", data->traf_number ); - lsmash_ifprintf( fp, indent, "trun_number = %"PRIu32"\n", data->trun_number ); - lsmash_ifprintf( fp, indent, "sample_number = %"PRIu32"\n", data->sample_number ); - --indent; - } - } - return 0; -} - -static int isom_print_mfro( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - isom_mfro_t *mfro = (isom_mfro_t *)box; - int indent = level; - isom_print_box_common( fp, indent++, box, "Movie Fragment Random Access Offset Box" ); - lsmash_ifprintf( fp, indent, "size = %"PRIu32"\n", mfro->length ); - return 0; -} - -int lsmash_print_movie( lsmash_root_t *root, const char *filename ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->print - || !(file->flags & LSMASH_FILE_MODE_DUMP) ) - return -1; - FILE *destination; - if( !strcmp( filename, "-" ) ) - destination = stdout; - else - destination = lsmash_fopen( filename, "wb" ); - fprintf( destination, "[File]\n" ); - fprintf( destination, " size = %"PRIu64"\n", file->size ); - for( lsmash_entry_t *entry = file->print->head; entry; entry = entry->next ) - { - isom_print_entry_t *data = (isom_print_entry_t *)entry->data; - if( !data || !data->box || data->func( destination, file, data->box, data->level ) ) - { - fclose( destination ); - return -1; - } - } - fclose( destination ); - return 0; -} - -static isom_print_box_t isom_select_print_func( isom_box_t *box ) -{ - if( box->manager & LSMASH_UNKNOWN_BOX ) - return isom_print_unknown; - if( box->parent ) - { - isom_box_t *parent = box->parent; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) - { - lsmash_codec_type_t sample_type = (lsmash_codec_type_t)box->type; - if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) ) - { - if( box->manager & LSMASH_VIDEO_DESCRIPTION ) - return isom_print_visual_description; - else if( box->manager & LSMASH_AUDIO_DESCRIPTION ) - return isom_print_audio_description; - } - static struct print_description_table_tag - { - lsmash_codec_type_t type; - isom_print_box_t func; - } print_description_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } }; - if( !print_description_table[0].func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( type, func ) print_description_table[i++] = (struct print_description_table_tag){ type, func } - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_print_visual_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_QDM2_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_QDMC_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CDX2_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_CDX4_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVCA_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_DVI_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_IMA4_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_VDVA_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_MP3_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_print_audio_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_print_text_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_print_tx3g_description ); - ADD_PRINT_DESCRIPTION_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL ); -#undef ADD_PRINT_DESCRIPTION_TABLE_ELEMENT - } - for( int i = 0; print_description_table[i].func; i++ ) - if( lsmash_check_codec_type_identical( sample_type, print_description_table[i].type ) ) - return print_description_table[i].func; - return isom_print_unknown; - } - if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) - { - if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_FRMA ) ) - return isom_print_frma; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ENDA ) ) - return isom_print_enda; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_TERMINATOR ) ) - return isom_print_terminator; - else - return isom_print_sample_description_extesion; - } - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TREF ) ) - return isom_print_track_reference_type; - if( parent->parent ) - { - if( lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_STSD ) ) - return isom_print_sample_description_extesion; - else if( lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_ILST ) - || lsmash_check_box_type_identical( parent->parent->type, QT_BOX_TYPE_ILST ) ) - { - if( parent->type.fourcc == LSMASH_4CC( '-', '-', '-', '-' ) ) - { - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_MEAN ) ) - return isom_print_mean; - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_NAME ) ) - return isom_print_name; - } - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_DATA ) ) - return isom_print_data; - } - } - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_ILST ) - || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) - return isom_print_metaitem; - } - static struct print_box_table_tag - { - lsmash_box_type_t type; - isom_print_box_t func; - } print_box_table[128] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } }; - if( !print_box_table[0].func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_PRINT_BOX_TABLE_ELEMENT( type, func ) print_box_table[i++] = (struct print_box_table_tag){ type, func } - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_FTYP, isom_print_ftyp ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STYP, isom_print_styp ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SIDX, isom_print_sidx ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOV, isom_print_moov ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MVHD, isom_print_mvhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_IODS, isom_print_iods ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, isom_print_ctab ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAK, isom_print_trak ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TKHD, isom_print_tkhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_TAPT, isom_print_tapt ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CLEF, isom_print_clef ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_PROF, isom_print_prof ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_ENOF, isom_print_enof ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_EDTS, isom_print_edts ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_ELST, isom_print_elst ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TREF, isom_print_tref ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MDIA, isom_print_mdia ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MDHD, isom_print_mdhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_HDLR, isom_print_hdlr ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MINF, isom_print_minf ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_VMHD, isom_print_vmhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SMHD, isom_print_smhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_HMHD, isom_print_hmhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_NMHD, isom_print_nmhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GMHD, isom_print_gmhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GMIN, isom_print_gmin ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_TEXT, isom_print_text ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_DINF, isom_print_dinf ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_DREF, isom_print_dref ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_URL, isom_print_url ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STBL, isom_print_stbl ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSD, isom_print_stsd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_print_clap ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_print_pasp ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_print_colr ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_print_colr ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_print_glbl ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_print_gama ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_print_fiel ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_print_cspc ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_print_sgbt ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_print_stsl ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_print_wave ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_print_chan ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SRAT, isom_print_srat ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, isom_print_ftab ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STTS, isom_print_stts ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CTTS, isom_print_ctts ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CSLG, isom_print_cslg ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSS, isom_print_stss ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_STPS, isom_print_stps ); - 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_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 ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SBGP, isom_print_sbgp ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_UDTA, isom_print_udta ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CHPL, isom_print_chpl ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_WLOC, isom_print_WLOC ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_LOOP, isom_print_LOOP ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_SELO, isom_print_SelO ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_ALLF, isom_print_AllF ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CPRT, isom_print_cprt ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MVEX, isom_print_mvex ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MEHD, isom_print_mehd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TREX, isom_print_trex ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOF, isom_print_moof ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MFHD, isom_print_mfhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAF, isom_print_traf ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TFHD, isom_print_tfhd ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TFDT, isom_print_tfdt ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TRUN, isom_print_trun ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_FREE, isom_print_free ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SKIP, isom_print_free ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MDAT, isom_print_mdat ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_KEYS, isom_print_keys ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_META, isom_print_meta ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_ILST, isom_print_ilst ); - ADD_PRINT_BOX_TABLE_ELEMENT( QT_BOX_TYPE_ILST, isom_print_ilst ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRA, isom_print_mfra ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_TFRA, isom_print_tfra ); - ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRO, isom_print_mfro ); - ADD_PRINT_BOX_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); -#undef ADD_PRINT_BOX_TABLE_ELEMENT - } - for( int i = 0; print_box_table[i].func; i++ ) - if( lsmash_check_box_type_identical( box->type, print_box_table[i].type ) ) - return print_box_table[i].func; - return isom_print_unknown; -} - -int isom_add_print_func( lsmash_file_t *file, void *box, int level ) -{ - if( !(file->flags & LSMASH_FILE_MODE_DUMP) ) - return 0; - isom_print_entry_t *data = lsmash_malloc( sizeof(isom_print_entry_t) ); - if( !data ) - return -1; - data->level = level; - data->box = (isom_box_t *)box; - data->func = isom_select_print_func( (isom_box_t *)box ); - if( !data->func || lsmash_add_entry( file->print, data ) ) - { - lsmash_free( data ); - return -1; - } - return 0; -} - -static void isom_remove_print_func( isom_print_entry_t *data ) -{ - if( !data || !data->box ) - return; - if( data->box->manager & LSMASH_ABSENT_IN_ROOT ) - { - /* free flagged box */ - if( data->box->destruct ) - data->box->destruct( data->box ); - else - lsmash_free( data->box ); - } - lsmash_free( data ); -} - -void isom_remove_print_funcs( lsmash_file_t *file ) -{ - lsmash_remove_list( file->print, isom_remove_print_func ); - file->print = NULL; -} - -#endif /* LSMASH_DEMUXER_ENABLED */ diff -Nru l-smash-1.9.1/print.h l-smash-2.3.0/print.h --- l-smash-1.9.1/print.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/print.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/***************************************************************************** - * print.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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_PRINT_H -#define LSMASH_PRINT_H - -int isom_add_print_func( lsmash_file_t *file, void *box, int level ); -void isom_remove_print_funcs( lsmash_file_t *file ); - -#endif /* LSMASH_PRINT_H */ diff -Nru l-smash-1.9.1/read.c l-smash-2.3.0/read.c --- l-smash-1.9.1/read.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/read.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,3061 +0,0 @@ -/***************************************************************************** - * read.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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. */ - -#ifdef LSMASH_DEMUXER_ENABLED - -#include "internal.h" /* must be placed first */ - -#include -#include -#include - -#include "box.h" -#include "print.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "description.h" - -static int isom_read_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos, int level ); - -static int isom_bs_read_box_common( lsmash_bs_t *bs, isom_box_t *box, uint32_t read_size ) -{ - assert( bs && box && box->file ); - /* Read size and type. */ - int ret = lsmash_bs_read( bs, read_size ); - if( ret < 0 ) - return -1; - else if( ret < read_size ) /* EOF */ - return 1; - box->size = lsmash_bs_get_be32( bs ); - box->type.fourcc = lsmash_bs_get_be32( bs ); - /* Read more bytes if needed. */ - int uuidbox = (box->type.fourcc == ISOM_BOX_TYPE_UUID.fourcc); - int more_read_size = 8 * (box->size == 1) + 16 * uuidbox; - if( more_read_size > 0 - && more_read_size > lsmash_bs_read( bs, more_read_size ) ) - return -1; - /* If size is set to 1, the actual size is repersented in the next 8 bytes. - * If size is set to 0, this box ends at the end of the stream. */ - if( box->size == 1 ) - box->size = lsmash_bs_get_be64( bs ); - else if( box->size == 0 ) - box->manager |= LSMASH_LAST_BOX; - /* Here, we don't set up extended box type fields if this box is not a UUID Box. */ - if( uuidbox ) - { - /* Get UUID. */ - lsmash_box_type_t *type = &box->type; - uint64_t temp64 = lsmash_bs_get_be64( bs ); - type->user.fourcc = (temp64 >> 32) & 0xffffffff; - type->user.id[0] = (temp64 >> 24) & 0xff; - type->user.id[1] = (temp64 >> 16) & 0xff; - type->user.id[2] = (temp64 >> 8) & 0xff; - type->user.id[3] = temp64 & 0xff; - temp64 = lsmash_bs_get_be64( bs ); - type->user.id[4] = (temp64 >> 56) & 0xff; - type->user.id[5] = (temp64 >> 48) & 0xff; - type->user.id[6] = (temp64 >> 40) & 0xff; - type->user.id[7] = (temp64 >> 32) & 0xff; - type->user.id[8] = (temp64 >> 24) & 0xff; - type->user.id[9] = (temp64 >> 16) & 0xff; - type->user.id[10] = (temp64 >> 8) & 0xff; - type->user.id[11] = temp64 & 0xff; - } - return 0; -} - -static int isom_read_fullbox_common_extension( lsmash_bs_t *bs, isom_box_t *box ) -{ - if( !isom_is_fullbox( box ) ) - return 0; - /* Get version and flags. */ - if( lsmash_bs_read( bs, 4 ) < 0 ) - return -1; - box->version = lsmash_bs_get_byte( bs ); - box->flags = lsmash_bs_get_be24( bs ); - box->manager |= LSMASH_FULLBOX; - return 0; -} - -/* Don't copy destructor since a destructor is defined as box specific. */ -static void isom_basebox_common_copy( isom_box_t *dst, isom_box_t *src ) -{ - dst->root = src->root; - dst->file = src->file; - dst->parent = src->parent; - dst->manager = src->manager; - dst->pos = src->pos; - dst->size = src->size; - dst->type = src->type; -} - -static void isom_fullbox_common_copy( isom_box_t *dst, isom_box_t *src ) -{ - dst->root = src->root; - dst->file = src->file; - dst->parent = src->parent; - dst->manager = src->manager; - dst->pos = src->pos; - dst->size = src->size; - dst->type = src->type; - dst->version = src->version; - dst->flags = src->flags; -} - -static void isom_box_common_copy( void *dst, void *src ) -{ - if( src && lsmash_check_box_type_identical( ((isom_box_t *)src)->type, ISOM_BOX_TYPE_STSD ) ) - { - isom_basebox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); - return; - } - if( isom_is_fullbox( src ) ) - isom_fullbox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); - else - isom_basebox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); -} - -static uint64_t isom_read_box_rest( lsmash_bs_t *bs, isom_box_t *box ) -{ - if( box->manager & LSMASH_LAST_BOX ) - { - uint64_t init_bs_store = bs->buffer.store; - while( 1 ) - { - int ret = lsmash_bs_read( bs, 1 ); - if( ret <= 0 ) - { - if( ret < 0 ) - /* This box may end incompletely at the end of the stream. */ - box->manager |= LSMASH_INCOMPLETE_BOX; - break; - } - } - return bs->buffer.store - init_bs_store; - } - uint64_t read_size = box->size - lsmash_bs_get_pos( bs ); - int ret = lsmash_bs_read( bs, read_size ); - if( ret < 0 ) - return 0; - if( box->size != bs->buffer.store ) - { - /* This box ends incompletely at the end of the stream. */ - box->manager |= LSMASH_INCOMPLETE_BOX; - bs->error = 1; - return -1; - } - return ret; -} - -static void isom_skip_box_rest( lsmash_bs_t *bs, isom_box_t *box ) -{ - if( box->manager & LSMASH_LAST_BOX ) - { - box->size = (box->manager & LSMASH_FULLBOX) ? ISOM_FULLBOX_COMMON_SIZE : ISOM_BASEBOX_COMMON_SIZE; - uint64_t start = bs->offset; - if( !bs->unseekable ) - lsmash_bs_seek( bs, 0, SEEK_END ); - else - while( lsmash_bs_read_c( bs ) != EOF ); - uint64_t end = bs->offset; - box->size += end - start; - return; - } - uint64_t skip_bytes = box->size - lsmash_bs_get_pos( bs ); - if( !bs->unseekable ) - { - uint64_t start = bs->offset; - lsmash_bs_seek( bs, skip_bytes, SEEK_CUR ); - if( lsmash_bs_read_c( bs ) == EOF ) - { - lsmash_bs_seek( bs, 0, SEEK_END ); - uint64_t end = bs->offset; - if( end - start != skip_bytes ) - bs->error = 1; /* not match size */ - lsmash_bs_read_c( bs ); /* Set EOF flag. - * FIXME: I think lsmash_bs_t should have its own EOF flag. */ - return; - } - lsmash_bs_seek( bs, -1, SEEK_CUR ); - return; - } - for( uint64_t i = 0; i < skip_bytes; i++ ) - if( lsmash_bs_read_c( bs ) == EOF ) - { - /* not match size */ - bs->error = 1; - return; - } -} - -static void isom_check_box_size( lsmash_bs_t *bs, isom_box_t *box ) -{ - if( box->manager & LSMASH_LAST_BOX ) - { - box->size = bs->buffer.store; - return; - } - uint64_t pos = lsmash_bs_get_pos( bs ); - if( box->size >= pos ) - return; - printf( "[%s] box has extra bytes: %"PRId64"\n", isom_4cc2str( box->type.fourcc ), pos - box->size ); - box->size = pos; -} - -static int isom_read_children( lsmash_file_t *file, isom_box_t *box, void *parent, int level ) -{ - int ret; - lsmash_bs_t *bs = file->bs; - isom_box_t *parent_box = (isom_box_t *)parent; - uint64_t parent_pos = lsmash_bs_get_pos( bs ); - while( !(ret = isom_read_box( file, box, parent_box, parent_pos, level )) ) - { - parent_pos += box->size; - if( parent_box->size <= parent_pos || bs->error ) - break; - } - box->size = parent_pos; /* for file size */ - return ret; -} - -static int isom_read_unknown_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - lsmash_bs_t *bs = file->bs; - uint64_t read_size = isom_read_box_rest( bs, box ); - if( box->manager & LSMASH_INCOMPLETE_BOX ) - return -1; - isom_unknown_box_t *unknown = lsmash_malloc_zero( sizeof(isom_unknown_box_t) ); - if( !unknown ) - return -1; - if( lsmash_add_entry( &parent->extensions, unknown ) ) - { - lsmash_free( unknown ); - return -1; - } - isom_box_common_copy( unknown, box ); - unknown->manager |= LSMASH_UNKNOWN_BOX; - unknown->destruct = (isom_extension_destructor_t)isom_remove_unknown_box; - if( read_size ) - { - unknown->unknown_field = lsmash_bs_get_bytes( bs, read_size ); - if( unknown->unknown_field ) - unknown->unknown_size = read_size; - else - unknown->manager |= LSMASH_INCOMPLETE_BOX; - } - if( !(file->flags & LSMASH_FILE_MODE_DUMP) ) - return 0; - /* Create a dummy for dump. */ - isom_box_t *dummy = lsmash_malloc_zero( sizeof(isom_box_t) ); - if( !dummy ) - return -1; - box->manager |= LSMASH_ABSENT_IN_ROOT; - isom_box_common_copy( dummy, box ); - if( isom_add_print_func( file, dummy, level ) ) - { - lsmash_free( dummy ); - return -1; - } - return 0; -} - -#define isom_add_box( box_name, parent_type ) \ - if( isom_add_##box_name( (parent_type *)parent ) ) \ - return -1; \ - isom_##box_name##_t *box_name = (isom_##box_name##_t *)parent->extensions.tail->data; - -#define isom_add_box_return_pointer( box_name, parent_type ) \ - isom_##box_name##_t *box_name = isom_add_##box_name( (parent_type *)parent ); \ - if( !box_name ) \ - return -1; - -static int isom_read_ftyp( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) || ((lsmash_file_t *)parent)->ftyp ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( ftyp, lsmash_file_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - ftyp->major_brand = lsmash_bs_get_be32( bs ); - ftyp->minor_version = lsmash_bs_get_be32( bs ); - uint64_t pos = lsmash_bs_get_pos( bs ); - ftyp->brand_count = box->size > pos ? (box->size - pos) / sizeof(uint32_t) : 0; - size_t alloc_size = ftyp->brand_count * sizeof(uint32_t); - ftyp->compatible_brands = ftyp->brand_count ? lsmash_malloc( alloc_size ) : NULL; - if( ftyp->brand_count && !ftyp->compatible_brands ) - return -1; - for( uint32_t i = 0; i < ftyp->brand_count; i++ ) - ftyp->compatible_brands[i] = lsmash_bs_get_be32( bs ); - if( !file->compatible_brands && ftyp->compatible_brands ) - { - file->compatible_brands = lsmash_memdup( ftyp->compatible_brands, alloc_size ); - if( !file->compatible_brands ) - return -1; - file->brand_count = ftyp->brand_count; - } - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( ftyp, box ); - return isom_add_print_func( file, ftyp, level ); -} - -static int isom_read_styp( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( styp, lsmash_file_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - styp->major_brand = lsmash_bs_get_be32( bs ); - styp->minor_version = lsmash_bs_get_be32( bs ); - uint64_t pos = lsmash_bs_get_pos( bs ); - styp->brand_count = box->size > pos ? (box->size - pos) / sizeof(uint32_t) : 0; - size_t alloc_size = styp->brand_count * sizeof(uint32_t); - styp->compatible_brands = styp->brand_count ? lsmash_malloc( alloc_size ) : NULL; - if( styp->brand_count && !styp->compatible_brands ) - return -1; - for( uint32_t i = 0; i < styp->brand_count; i++ ) - styp->compatible_brands[i] = lsmash_bs_get_be32( bs ); - if( !file->compatible_brands && styp->compatible_brands ) - { - file->compatible_brands = lsmash_memdup( styp->compatible_brands, alloc_size ); - if( !file->compatible_brands ) - return -1; - file->brand_count = styp->brand_count; - } - file->flags |= LSMASH_FILE_MODE_SEGMENT; - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( styp, box ); - return isom_add_print_func( file, styp, level ); -} - -static int isom_read_sidx( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( sidx, lsmash_file_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - sidx->reference_ID = lsmash_bs_get_be32( bs ); - sidx->timescale = lsmash_bs_get_be32( bs ); - if( sidx->version == 0 ) - { - sidx->earliest_presentation_time = lsmash_bs_get_be32( bs ); - sidx->first_offset = lsmash_bs_get_be32( bs ); - } - else - { - sidx->earliest_presentation_time = lsmash_bs_get_be64( bs ); - sidx->first_offset = lsmash_bs_get_be64( bs ); - } - sidx->reserved = lsmash_bs_get_be16( bs ); - sidx->reference_count = lsmash_bs_get_be16( bs ); - for( uint64_t pos = lsmash_bs_get_pos( bs ); - pos < box->size && sidx->list->entry_count < sidx->reference_count; - pos = lsmash_bs_get_pos( bs ) ) - { - isom_sidx_referenced_item_t *data = lsmash_malloc( sizeof(isom_sidx_referenced_item_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( sidx->list, data ) ) - { - lsmash_free( data ); - return -1; - } - uint32_t temp32; - temp32 = lsmash_bs_get_be32( bs ); - data->reference_type = (temp32 >> 31) & 0x00000001; - data->reference_size = temp32 & 0x7FFFFFFF; - data->subsegment_duration = lsmash_bs_get_be32( bs ); - temp32 = lsmash_bs_get_be32( bs ); - data->starts_with_SAP = (temp32 >> 31) & 0x00000001; - data->SAP_type = (temp32 >> 28) & 0x00000007; - data->SAP_delta_time = temp32 & 0x0FFFFFFF; - } - box->size = lsmash_bs_get_pos( bs ); - file->flags |= LSMASH_FILE_MODE_INDEX; - isom_box_common_copy( sidx, box ); - return isom_add_print_func( file, sidx, level ); -} - -static int isom_read_moov( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) || ((lsmash_file_t *)parent)->moov ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( moov, lsmash_file_t ); - file->flags |= LSMASH_FILE_MODE_INITIALIZATION; - isom_box_common_copy( moov, box ); - if( isom_add_print_func( file, moov, level ) ) - return -1; - return isom_read_children( file, box, moov, level ); -} - -static int isom_read_mvhd( 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_MOOV ) || ((isom_moov_t *)parent)->mvhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mvhd, isom_moov_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - if( box->version ) - { - mvhd->creation_time = lsmash_bs_get_be64( bs ); - mvhd->modification_time = lsmash_bs_get_be64( bs ); - mvhd->timescale = lsmash_bs_get_be32( bs ); - mvhd->duration = lsmash_bs_get_be64( bs ); - } - else - { - mvhd->creation_time = lsmash_bs_get_be32( bs ); - mvhd->modification_time = lsmash_bs_get_be32( bs ); - mvhd->timescale = lsmash_bs_get_be32( bs ); - mvhd->duration = lsmash_bs_get_be32( bs ); - } - mvhd->rate = lsmash_bs_get_be32( bs ); - mvhd->volume = lsmash_bs_get_be16( bs ); - mvhd->reserved = lsmash_bs_get_be16( bs ); - mvhd->preferredLong[0] = lsmash_bs_get_be32( bs ); - mvhd->preferredLong[1] = lsmash_bs_get_be32( bs ); - for( int i = 0; i < 9; i++ ) - mvhd->matrix[i] = lsmash_bs_get_be32( bs ); - mvhd->previewTime = lsmash_bs_get_be32( bs ); - mvhd->previewDuration = lsmash_bs_get_be32( bs ); - mvhd->posterTime = lsmash_bs_get_be32( bs ); - mvhd->selectionTime = lsmash_bs_get_be32( bs ); - mvhd->selectionDuration = lsmash_bs_get_be32( bs ); - mvhd->currentTime = lsmash_bs_get_be32( bs ); - mvhd->next_track_ID = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( mvhd, box ); - return isom_add_print_func( file, mvhd, level ); -} - -static int isom_read_iods( 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_MOOV ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_iods_t *iods = lsmash_malloc_zero( sizeof(isom_iods_t) ); - if( !iods ) - return -1; - lsmash_bs_t *bs = file->bs; - isom_skip_box_rest( bs, box ); - box->manager |= LSMASH_ABSENT_IN_ROOT; - iods->destruct = (isom_extension_destructor_t)lsmash_free; - isom_box_common_copy( iods, box ); - if( isom_add_print_func( file, iods, level ) ) - { - isom_remove_box_by_itself( iods ); - return -1; - } - return 0; -} - -static int isom_read_qt_color_table( lsmash_bs_t *bs, isom_qt_color_table_t *color_table ) -{ - if( lsmash_bs_read( bs, 8 ) < 0 ) - return -1; - color_table->seed = lsmash_bs_get_be32( bs ); - color_table->flags = lsmash_bs_get_be16( bs ); - color_table->size = lsmash_bs_get_be16( bs ); - if( lsmash_bs_read( bs, (color_table->size + 1) * 8 ) < 0 ) - return -1; - isom_qt_color_array_t *array = lsmash_malloc_zero( (color_table->size + 1) * sizeof(isom_qt_color_array_t) ); - if( !array ) - return -1; - color_table->array = array; - for( uint16_t i = 0; i <= color_table->size; i++ ) - { - uint64_t color = lsmash_bs_get_be64( bs ); - array[i].value = (color >> 48) & 0xffff; - array[i].r = (color >> 32) & 0xffff; - array[i].g = (color >> 16) & 0xffff; - array[i].b = color & 0xffff; - } - return 0; -} - -static int isom_read_ctab( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box( ctab, isom_moov_t ); - lsmash_bs_t *bs = file->bs; - if( isom_read_qt_color_table( bs, &ctab->color_table ) ) - return -1; - isom_box_common_copy( ctab, box ); - return isom_add_print_func( file, ctab, level ); -} - -static int isom_read_trak( 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_MOOV ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( trak, isom_moov_t ); - box->parent = parent; - box->root = file->root; - box->file = file; - isom_box_common_copy( trak, box ); - if( isom_add_print_func( file, trak, level ) ) - return -1; - return isom_read_children( file, box, trak, level ); -} - -static int isom_read_tkhd( 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_TRAK ) || ((isom_trak_t *)parent)->tkhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( tkhd, isom_trak_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - if( box->version ) - { - tkhd->creation_time = lsmash_bs_get_be64( bs ); - tkhd->modification_time = lsmash_bs_get_be64( bs ); - tkhd->track_ID = lsmash_bs_get_be32( bs ); - tkhd->reserved1 = lsmash_bs_get_be32( bs ); - tkhd->duration = lsmash_bs_get_be64( bs ); - } - else - { - tkhd->creation_time = lsmash_bs_get_be32( bs ); - tkhd->modification_time = lsmash_bs_get_be32( bs ); - tkhd->track_ID = lsmash_bs_get_be32( bs ); - tkhd->reserved1 = lsmash_bs_get_be32( bs ); - tkhd->duration = lsmash_bs_get_be32( bs ); - } - tkhd->reserved2[0] = lsmash_bs_get_be32( bs ); - tkhd->reserved2[1] = lsmash_bs_get_be32( bs ); - tkhd->layer = lsmash_bs_get_be16( bs ); - tkhd->alternate_group = lsmash_bs_get_be16( bs ); - tkhd->volume = lsmash_bs_get_be16( bs ); - tkhd->reserved3 = lsmash_bs_get_be16( bs ); - for( int i = 0; i < 9; i++ ) - tkhd->matrix[i] = lsmash_bs_get_be32( bs ); - tkhd->width = lsmash_bs_get_be32( bs ); - tkhd->height = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( tkhd, box ); - return isom_add_print_func( file, tkhd, level ); -} - -static int isom_read_tapt( 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_TRAK ) || ((isom_trak_t *)parent)->tapt ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( tapt, isom_trak_t ); - isom_box_common_copy( tapt, box ); - if( isom_add_print_func( file, tapt, level ) ) - return -1; - return isom_read_children( file, box, tapt, level ); -} - -static int isom_read_clef( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_TAPT ) || ((isom_tapt_t *)parent)->clef ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( clef, isom_tapt_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - clef->width = lsmash_bs_get_be32( bs ); - clef->height = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( clef, box ); - return isom_add_print_func( file, clef, level ); -} - -static int isom_read_prof( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_TAPT ) || ((isom_tapt_t *)parent)->prof ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( prof, isom_tapt_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - prof->width = lsmash_bs_get_be32( bs ); - prof->height = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( prof, box ); - return isom_add_print_func( file, prof, level ); -} - -static int isom_read_enof( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_TAPT ) || ((isom_tapt_t *)parent)->enof ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( enof, isom_tapt_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - enof->width = lsmash_bs_get_be32( bs ); - enof->height = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( enof, box ); - return isom_add_print_func( file, enof, level ); -} - -static int isom_read_edts( 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_TRAK ) || ((isom_trak_t *)parent)->edts ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( edts, isom_trak_t ); - isom_box_common_copy( edts, box ); - if( isom_add_print_func( file, edts, level ) ) - return -1; - return isom_read_children( file, box, edts, level ); -} - -static int isom_read_elst( 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_EDTS ) || ((isom_edts_t *)parent)->elst ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( elst, isom_edts_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && elst->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( elst->list, data ) ) - { - lsmash_free( data ); - return -1; - } - if( box->version == 1 ) - { - data->segment_duration = lsmash_bs_get_be64( bs ); - data->media_time = (int64_t)lsmash_bs_get_be64( bs ); - } - else - { - data->segment_duration = lsmash_bs_get_be32( bs ); - data->media_time = (int32_t)lsmash_bs_get_be32( bs ); - } - data->media_rate = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( elst, box ); - return isom_add_print_func( file, elst, level ); -} - -static int isom_read_tref( 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_TRAK ) || ((isom_trak_t *)parent)->tref ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( tref, isom_trak_t ); - isom_box_common_copy( tref, box ); - if( isom_add_print_func( file, tref, level ) ) - return -1; - return isom_read_children( file, box, tref, level ); -} - -static int isom_read_track_reference_type( 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_TREF ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_tref_type_t *ref = isom_add_track_reference_type( (isom_tref_t *)parent, box->type.fourcc ); - if( !ref ) - return -1; - lsmash_bs_t *bs = file->bs; - ref->ref_count = (box->size - lsmash_bs_get_pos( bs ) ) / sizeof(uint32_t); - if( ref->ref_count ) - { - ref->track_ID = lsmash_malloc( ref->ref_count * sizeof(uint32_t) ); - if( !ref->track_ID ) - { - ref->ref_count = 0; - return -1; - } - isom_read_box_rest( bs, box ); - for( uint32_t i = 0; i < ref->ref_count; i++ ) - ref->track_ID[i] = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( ref, box ); - return isom_add_print_func( file, ref, level ); -} - -static int isom_read_mdia( 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_TRAK ) || ((isom_trak_t *)parent)->mdia ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mdia, isom_trak_t ); - isom_box_common_copy( mdia, box ); - if( isom_add_print_func( file, mdia, level ) ) - return -1; - return isom_read_children( file, box, mdia, level ); -} - -static int isom_read_mdhd( 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_MDIA ) || ((isom_mdia_t *)parent)->mdhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mdhd, isom_mdia_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - if( box->version ) - { - mdhd->creation_time = lsmash_bs_get_be64( bs ); - mdhd->modification_time = lsmash_bs_get_be64( bs ); - mdhd->timescale = lsmash_bs_get_be32( bs ); - mdhd->duration = lsmash_bs_get_be64( bs ); - } - else - { - mdhd->creation_time = lsmash_bs_get_be32( bs ); - mdhd->modification_time = lsmash_bs_get_be32( bs ); - mdhd->timescale = lsmash_bs_get_be32( bs ); - mdhd->duration = lsmash_bs_get_be32( bs ); - } - mdhd->language = lsmash_bs_get_be16( bs ); - mdhd->quality = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( mdhd, box ); - return isom_add_print_func( file, mdhd, level ); -} - -static int isom_read_hdlr( 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_MDIA ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) - && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF )) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) && ((isom_mdia_t *)parent)->hdlr) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) && ((isom_meta_t *)parent)->hdlr) - || (lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) && ((isom_meta_t *)parent)->hdlr) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) && ((isom_minf_t *)parent)->hdlr) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( hdlr, void ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - hdlr->componentType = lsmash_bs_get_be32( bs ); - hdlr->componentSubtype = lsmash_bs_get_be32( bs ); - hdlr->componentManufacturer = lsmash_bs_get_be32( bs ); - hdlr->componentFlags = lsmash_bs_get_be32( bs ); - hdlr->componentFlagsMask = lsmash_bs_get_be32( bs ); - uint64_t pos = lsmash_bs_get_pos( bs ); - hdlr->componentName_length = box->size - pos; - if( hdlr->componentName_length ) - { - hdlr->componentName = lsmash_malloc( hdlr->componentName_length ); - if( !hdlr->componentName ) - return -1; - for( uint32_t i = 0; pos < box->size; pos = lsmash_bs_get_pos( bs ) ) - hdlr->componentName[i++] = lsmash_bs_get_byte( bs ); - } - box->size = pos; - isom_box_common_copy( hdlr, box ); - return isom_add_print_func( file, hdlr, level ); -} - -static int isom_read_minf( 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_MDIA ) || ((isom_mdia_t *)parent)->minf ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( minf, isom_mdia_t ); - isom_box_common_copy( minf, box ); - if( isom_add_print_func( file, minf, level ) ) - return -1; - return isom_read_children( file, box, minf, level ); -} - -static int isom_read_vmhd( 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_MINF ) || ((isom_minf_t *)parent)->vmhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( vmhd, isom_minf_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - vmhd->graphicsmode = lsmash_bs_get_be16( bs ); - for( int i = 0; i < 3; i++ ) - vmhd->opcolor[i] = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( vmhd, box ); - return isom_add_print_func( file, vmhd, level ); -} - -static int isom_read_smhd( 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_MINF ) || ((isom_minf_t *)parent)->smhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( smhd, isom_minf_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - smhd->balance = lsmash_bs_get_be16( bs ); - smhd->reserved = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( smhd, box ); - return isom_add_print_func( file, smhd, level ); -} - -static int isom_read_hmhd( 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_MINF ) || ((isom_minf_t *)parent)->hmhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( hmhd, isom_minf_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - hmhd->maxPDUsize = lsmash_bs_get_be16( bs ); - hmhd->avgPDUsize = lsmash_bs_get_be16( bs ); - hmhd->maxbitrate = lsmash_bs_get_be32( bs ); - hmhd->avgbitrate = lsmash_bs_get_be32( bs ); - hmhd->reserved = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( hmhd, box ); - return isom_add_print_func( file, hmhd, level ); -} - -static int isom_read_nmhd( 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_MINF ) || ((isom_minf_t *)parent)->nmhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( nmhd, isom_minf_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( nmhd, box ); - return isom_add_print_func( file, nmhd, level ); -} - -static int isom_read_gmhd( 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_MINF ) || ((isom_minf_t *)parent)->gmhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( gmhd, isom_minf_t ); - isom_box_common_copy( gmhd, box ); - if( isom_add_print_func( file, gmhd, level ) ) - return -1; - return isom_read_children( file, box, gmhd, level ); -} - -static int isom_read_gmin( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_GMHD ) || ((isom_gmhd_t *)parent)->gmin ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( gmin, isom_gmhd_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - gmin->graphicsmode = lsmash_bs_get_be16( bs ); - for( int i = 0; i < 3; i++ ) - gmin->opcolor[i] = lsmash_bs_get_be16( bs ); - gmin->balance = lsmash_bs_get_be16( bs ); - gmin->reserved = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( gmin, box ); - return isom_add_print_func( file, gmin, level ); -} - -static int isom_read_text( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_GMHD ) || ((isom_gmhd_t *)parent)->text ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( text, isom_gmhd_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - for( int i = 0; i < 9; i++ ) - text->matrix[i] = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( text, box ); - return isom_add_print_func( file, text, level ); -} - -static int isom_read_dinf( 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_MINF ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) - && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META )) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MINF ) && ((isom_minf_t *)parent)->dinf) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) && ((isom_meta_t *)parent)->dinf) - || (lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) && ((isom_meta_t *)parent)->dinf) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( dinf, void ); - isom_box_common_copy( dinf, box ); - if( isom_add_print_func( file, dinf, level ) ) - return -1; - return isom_read_children( file, box, dinf, level ); -} - -static int isom_read_dref( 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_DINF ) || ((isom_dinf_t *)parent)->dref ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( dref, isom_dinf_t ); - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_read( bs, 4 ) < 0 ) - return -1; - dref->list.entry_count = lsmash_bs_get_be32( bs ); - isom_box_common_copy( dref, box ); - if( isom_add_print_func( file, dref, level ) ) - return -1; - return isom_read_children( file, box, dref, level ); -} - -static int isom_read_url( 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_DREF ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_dref_t *dref = (isom_dref_t *)parent; - if( !dref->list.head ) - dref->list.entry_count = 0; /* discard entry_count gotten from the file */ - isom_dref_entry_t *url = isom_add_dref_entry( dref ); - if( !url ) - return -1; - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint64_t pos = lsmash_bs_get_pos( bs ); - url->location_length = box->size - pos; - if( url->location_length ) - { - url->location = lsmash_malloc( url->location_length ); - if( !url->location ) - return -1; - for( uint32_t i = 0; pos < box->size; pos = lsmash_bs_get_pos( bs ) ) - url->location[i++] = lsmash_bs_get_byte( bs ); - } - box->size = pos; - box->parent = parent; - isom_box_common_copy( url, box ); - return isom_add_print_func( file, url, level ); -} - -static int isom_read_stbl( 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_MINF ) || ((isom_minf_t *)parent)->stbl ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stbl, isom_minf_t ); - isom_box_common_copy( stbl, box ); - if( isom_add_print_func( file, stbl, level ) ) - return -1; - return isom_read_children( file, box, stbl, level ); -} - -static int isom_read_stsd( 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)->stsd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stsd, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_read( bs, 4 ) < 0 ) - return -1; - stsd->entry_count = lsmash_bs_get_be32( bs ); - isom_box_common_copy( stsd, box ); - if( isom_add_print_func( file, stsd, level ) ) - return -1; - int ret = 0; - uint64_t stsd_pos = lsmash_bs_get_pos( bs ); - for( uint32_t i = 0; i < stsd->entry_count || (stsd_pos + ISOM_BASEBOX_COMMON_SIZE) <= stsd->size; i++ ) - { - ret = isom_read_box( file, box, (isom_box_t *)stsd, stsd_pos, level ); - if( ret ) - break; - stsd_pos += box->size; - if( stsd->size <= stsd_pos || bs->error ) - break; - } - if( stsd->size < stsd_pos ) - { - printf( "[stsd] box has extra bytes: %"PRId64"\n", stsd_pos - stsd->size ); - stsd->size = stsd_pos; - } - box->size = stsd->size; - return ret; -} - -static int isom_read_codec_specific( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t exdata_length; - void *exdata = lsmash_bs_export_data( bs, &exdata_length ); - if( (!exdata && exdata_length) || exdata_length != box->size ) - return -1; - isom_box_t *ext = lsmash_malloc( sizeof(isom_box_t) ); - if( !ext ) - { - lsmash_free( exdata ); - return -1; - } - isom_basebox_common_copy( (isom_box_t *)ext, box ); - ext->manager |= LSMASH_BINARY_CODED_BOX; - ext->binary = exdata; - ext->destruct = exdata ? lsmash_free : NULL; - if( lsmash_add_entry( &parent->extensions, ext ) ) - { - isom_remove_extension_box( ext ); - return -1; - } - return isom_add_print_func( file, ext, level ); -} - -static void *isom_sample_description_alloc( lsmash_codec_type_t sample_type ) -{ - static struct description_alloc_table_tag - { - lsmash_codec_type_t type; - size_t alloc_size; - } description_alloc_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, 0 } }; - if( description_alloc_table[0].alloc_size == 0 ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( type, alloc_size ) \ - description_alloc_table[i++] = (struct description_alloc_table_tag){ type, alloc_size } - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, sizeof(isom_visual_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_QDM2_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_QDMC_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CDX2_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_CDX4_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVCA_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_DVI_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_IMA4_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_RAW_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_VDVA_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_MP3_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, sizeof(isom_audio_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, sizeof(isom_tx3g_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, sizeof(isom_qt_text_entry_t) ); - ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, 0 ); -#undef ADD_DESCRIPTION_ALLOC_TABLE_ELEMENT - } - for( int i = 0; description_alloc_table[i].alloc_size; i++ ) - if( lsmash_check_codec_type_identical( sample_type, description_alloc_table[i].type ) ) - return lsmash_malloc_zero( description_alloc_table[i].alloc_size ); - return NULL; -} - -static void *isom_add_description( lsmash_codec_type_t sample_type, isom_stsd_t *stsd ) -{ - void *sample = isom_sample_description_alloc( sample_type ); - if( !sample ) - return NULL; - if( lsmash_add_entry( &stsd->list, sample ) ) - { - lsmash_free( sample ); - return NULL; - } - if( lsmash_add_entry( &stsd->extensions, sample ) ) - { - lsmash_remove_entry_tail( &stsd->list, lsmash_free ); - return NULL; - } - ((isom_box_t *)sample)->destruct = (isom_extension_destructor_t)isom_remove_sample_description; - return sample; -} - -static int isom_read_visual_description( 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_STSD ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_visual_entry_t *visual = (isom_visual_entry_t *)isom_add_description( (lsmash_codec_type_t)box->type, (isom_stsd_t *)parent ); - if( !visual ) - return -1; - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_read( bs, 78 ) < 0 ) - return -1; - for( int i = 0; i < 6; i++ ) - visual->reserved[i] = lsmash_bs_get_byte( bs ); - visual->data_reference_index = lsmash_bs_get_be16( bs ); - visual->version = lsmash_bs_get_be16( bs ); - visual->revision_level = lsmash_bs_get_be16( bs ); - visual->vendor = lsmash_bs_get_be32( bs ); - visual->temporalQuality = lsmash_bs_get_be32( bs ); - visual->spatialQuality = lsmash_bs_get_be32( bs ); - visual->width = lsmash_bs_get_be16( bs ); - visual->height = lsmash_bs_get_be16( bs ); - visual->horizresolution = lsmash_bs_get_be32( bs ); - visual->vertresolution = lsmash_bs_get_be32( bs ); - visual->dataSize = lsmash_bs_get_be32( bs ); - visual->frame_count = lsmash_bs_get_be16( bs ); - for( int i = 0; i < 32; i++ ) - visual->compressorname[i] = lsmash_bs_get_byte( bs ); - visual->depth = lsmash_bs_get_be16( bs ); - visual->color_table_ID = lsmash_bs_get_be16( bs ); - if( visual->color_table_ID == 0 - && lsmash_bs_get_pos( bs ) < box->size - && isom_read_qt_color_table( bs, &visual->color_table ) ) - return -1; - box->parent = parent; - box->manager |= LSMASH_VIDEO_DESCRIPTION; - isom_box_common_copy( visual, box ); - if( isom_add_print_func( file, visual, level ) ) - return -1; - return isom_read_children( file, box, visual, level ); -} - -static int isom_read_esds( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, (lsmash_box_type_t)ISOM_CODEC_TYPE_MP4V_VIDEO ) - && !lsmash_check_box_type_identical( parent->type, (lsmash_box_type_t)ISOM_CODEC_TYPE_MP4A_AUDIO ) - && !lsmash_check_box_type_identical( parent->type, (lsmash_box_type_t)ISOM_CODEC_TYPE_M4AE_AUDIO ) - && !lsmash_check_box_type_identical( parent->type, (lsmash_box_type_t)ISOM_CODEC_TYPE_MP4S_SYSTEM ) - && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) - return isom_read_unknown_box( file, box, parent, level ); - if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) - { - box->type = QT_BOX_TYPE_ESDS; - if( parent->parent && lsmash_check_box_type_identical( parent->parent->type, (lsmash_box_type_t)ISOM_CODEC_TYPE_MP4A_AUDIO ) ) - parent->parent->type = QT_CODEC_TYPE_MP4A_AUDIO; - } - else - box->type = ISOM_BOX_TYPE_ESDS; - isom_add_box_return_pointer( esds, void ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - esds->ES = mp4sys_get_ES_Descriptor( bs ); - if( !esds->ES ) - { - lsmash_free( esds ); - return -1; - } - isom_box_common_copy( esds, box ); - isom_basebox_common_copy( (isom_box_t *)esds, box ); - return isom_add_print_func( file, esds, level ); -} - -static int isom_read_btrt( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( btrt, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - btrt->bufferSizeDB = lsmash_bs_get_be32( bs ); - btrt->maxBitrate = lsmash_bs_get_be32( bs ); - btrt->avgBitrate = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( btrt, box ); - return isom_add_print_func( file, btrt, level ); -} - -static int isom_read_glbl( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( glbl, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t header_size = box->size - ISOM_BASEBOX_COMMON_SIZE; - if( header_size ) - { - glbl->header_data = lsmash_malloc( header_size ); - if( !glbl->header_data ) - return -1; - for( uint32_t i = 0; i < header_size; i++ ) - glbl->header_data[i] = lsmash_bs_get_byte( bs ); - } - glbl->header_size = header_size; - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( glbl, box ); - return isom_add_print_func( file, glbl, level ); -} - -static int isom_read_clap( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( clap, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - clap->cleanApertureWidthN = lsmash_bs_get_be32( bs ); - clap->cleanApertureWidthD = lsmash_bs_get_be32( bs ); - clap->cleanApertureHeightN = lsmash_bs_get_be32( bs ); - clap->cleanApertureHeightD = lsmash_bs_get_be32( bs ); - clap->horizOffN = lsmash_bs_get_be32( bs ); - clap->horizOffD = lsmash_bs_get_be32( bs ); - clap->vertOffN = lsmash_bs_get_be32( bs ); - clap->vertOffD = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( clap, box ); - return isom_add_print_func( file, clap, level ); -} - -static int isom_read_pasp( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( pasp, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - pasp->hSpacing = lsmash_bs_get_be32( bs ); - pasp->vSpacing = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( pasp, box ); - return isom_add_print_func( file, pasp, level ); -} - -static int isom_read_colr( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( colr, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - colr->color_parameter_type = lsmash_bs_get_be32( bs ); - if( colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_NCLC - || colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) - { - colr->primaries_index = lsmash_bs_get_be16( bs ); - colr->transfer_function_index = lsmash_bs_get_be16( bs ); - colr->matrix_index = lsmash_bs_get_be16( bs ); - if( colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) - { - if( lsmash_bs_get_pos( bs ) < box->size ) - { - uint8_t temp8 = lsmash_bs_get_byte( bs ); - colr->full_range_flag = (temp8 >> 7) & 0x01; - colr->reserved = temp8 & 0x7f; - } - else - { - /* It seems this box is broken or incomplete. */ - box->manager |= LSMASH_INCOMPLETE_BOX; - colr->full_range_flag = 0; - colr->reserved = 0; - } - } - else - box->manager |= LSMASH_QTFF_BASE; - } - box->size = lsmash_bs_get_pos( bs ); - box->type = (box->manager & LSMASH_QTFF_BASE) ? QT_BOX_TYPE_COLR : ISOM_BOX_TYPE_COLR; - isom_box_common_copy( colr, box ); - return isom_add_print_func( file, colr, level ); -} - -static int isom_read_gama( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( gama, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - gama->level = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( gama, box ); - return isom_add_print_func( file, gama, level ); -} - -static int isom_read_fiel( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( fiel, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - fiel->fields = lsmash_bs_get_byte( bs ); - fiel->detail = lsmash_bs_get_byte( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( fiel, box ); - return isom_add_print_func( file, fiel, level ); -} - -static int isom_read_cspc( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( cspc, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - cspc->pixel_format = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( cspc, box ); - return isom_add_print_func( file, cspc, level ); -} - -static int isom_read_sgbt( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( sgbt, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - sgbt->significantBits = lsmash_bs_get_byte( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( sgbt, box ); - return isom_add_print_func( file, sgbt, level ); -} - -static int isom_read_stsl( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( stsl, isom_visual_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - stsl->constraint_flag = lsmash_bs_get_byte( bs ); - stsl->scale_method = lsmash_bs_get_byte( bs ); - stsl->display_center_x = lsmash_bs_get_be16( bs ); - stsl->display_center_y = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( stsl, box ); - return isom_add_print_func( file, stsl, level ); -} - -static int isom_read_audio_description( 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_STSD ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_audio_entry_t *audio = (isom_audio_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); - if( !audio ) - return -1; - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_read( bs, 28 ) < 0 ) - return -1; - for( int i = 0; i < 6; i++ ) - audio->reserved[i] = lsmash_bs_get_byte( bs ); - audio->data_reference_index = lsmash_bs_get_be16( bs ); - audio->version = lsmash_bs_get_be16( bs ); - audio->revision_level = lsmash_bs_get_be16( bs ); - audio->vendor = lsmash_bs_get_be32( bs ); - audio->channelcount = lsmash_bs_get_be16( bs ); - audio->samplesize = lsmash_bs_get_be16( bs ); - audio->compression_ID = lsmash_bs_get_be16( bs ); - audio->packet_size = lsmash_bs_get_be16( bs ); - audio->samplerate = lsmash_bs_get_be32( bs ); - if( audio->version == 1 ) - { - if( ((isom_stsd_t *)parent)->version == 0 ) - { - if( lsmash_bs_read( bs, 16 ) < 0 ) - return -1; - audio->samplesPerPacket = lsmash_bs_get_be32( bs ); - audio->bytesPerPacket = lsmash_bs_get_be32( bs ); - audio->bytesPerFrame = lsmash_bs_get_be32( bs ); - audio->bytesPerSample = lsmash_bs_get_be32( bs ); - box->manager |= LSMASH_QTFF_BASE; - } - else - /* AudioSampleEntryV1 has no additional fields. */ - box->manager &= ~LSMASH_QTFF_BASE; - } - else if( audio->version == 2 ) - { - if( lsmash_bs_read( bs, 36 ) < 0 ) - return -1; - audio->sizeOfStructOnly = lsmash_bs_get_be32( bs ); - audio->audioSampleRate = lsmash_bs_get_be64( bs ); - audio->numAudioChannels = lsmash_bs_get_be32( bs ); - audio->always7F000000 = lsmash_bs_get_be32( bs ); - audio->constBitsPerChannel = lsmash_bs_get_be32( bs ); - audio->formatSpecificFlags = lsmash_bs_get_be32( bs ); - audio->constBytesPerAudioPacket = lsmash_bs_get_be32( bs ); - audio->constLPCMFramesPerAudioPacket = lsmash_bs_get_be32( bs ); - box->manager |= LSMASH_QTFF_BASE; - } - box->parent = parent; - box->manager |= LSMASH_AUDIO_DESCRIPTION; - isom_box_common_copy( audio, box ); - if( isom_add_print_func( file, audio, level ) ) - return -1; - return isom_read_children( file, box, audio, level ); -} - -static int isom_read_wave( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( wave, isom_audio_entry_t ); - isom_box_common_copy( wave, box ); - if( isom_add_print_func( file, wave, level ) ) - return -1; - return isom_read_children( file, box, wave, level ); -} - -static int isom_read_frma( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) || ((isom_wave_t *)parent)->frma ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( frma, isom_wave_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - frma->data_format = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( frma, box ); - return isom_add_print_func( file, frma, level ); -} - -static int isom_read_enda( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) || ((isom_wave_t *)parent)->enda ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( enda, isom_wave_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - enda->littleEndian = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( enda, box ); - return isom_add_print_func( file, enda, level ); -} - -static int isom_read_terminator( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) || ((isom_wave_t *)parent)->terminator ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( terminator, isom_wave_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( terminator, box ); - return isom_add_print_func( file, terminator, level ); -} - -static int isom_read_chan( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( chan, isom_audio_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - chan->channelLayoutTag = lsmash_bs_get_be32( bs ); - chan->channelBitmap = lsmash_bs_get_be32( bs ); - chan->numberChannelDescriptions = lsmash_bs_get_be32( bs ); - if( chan->numberChannelDescriptions ) - { - isom_channel_description_t *desc = lsmash_malloc( chan->numberChannelDescriptions * sizeof(isom_channel_description_t) ); - if( !desc ) - return -1; - chan->channelDescriptions = desc; - for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) - { - desc->channelLabel = lsmash_bs_get_be32( bs ); - desc->channelFlags = lsmash_bs_get_be32( bs ); - for( int j = 0; j < 3; j++ ) - desc->coordinates[j] = lsmash_bs_get_be32( bs ); - } - } - isom_box_common_copy( chan, box ); - return isom_add_print_func( file, chan, level ); -} - -static int isom_read_srat( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_add_box_return_pointer( srat, isom_audio_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - srat->sampling_rate = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( srat, box ); - return isom_add_print_func( file, srat, level ); -} - -static int isom_read_qt_text_description( 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_STSD ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_qt_text_entry_t *text = (isom_qt_text_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); - if( !text ) - return -1; - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_read( bs, 51 ) < 0 ) - return -1; - for( int i = 0; i < 6; i++ ) - text->reserved[i] = lsmash_bs_get_byte( bs ); - text->data_reference_index = lsmash_bs_get_be16( bs ); - text->displayFlags = lsmash_bs_get_be32( bs ); - text->textJustification = lsmash_bs_get_be32( bs ); - for( int i = 0; i < 3; i++ ) - text->bgColor[i] = lsmash_bs_get_be16( bs ); - text->top = lsmash_bs_get_be16( bs ); - text->left = lsmash_bs_get_be16( bs ); - text->bottom = lsmash_bs_get_be16( bs ); - text->right = lsmash_bs_get_be16( bs ); - text->scrpStartChar = lsmash_bs_get_be32( bs ); - text->scrpHeight = lsmash_bs_get_be16( bs ); - text->scrpAscent = lsmash_bs_get_be16( bs ); - text->scrpFont = lsmash_bs_get_be16( bs ); - text->scrpFace = lsmash_bs_get_be16( bs ); - text->scrpSize = lsmash_bs_get_be16( bs ); - for( int i = 0; i < 3; i++ ) - text->scrpColor[i] = lsmash_bs_get_be16( bs ); - text->font_name_length = lsmash_bs_get_byte( bs ); - if( text->font_name_length ) - { - if( lsmash_bs_read( bs, text->font_name_length ) < 0 ) - return -1; - text->font_name = lsmash_malloc( text->font_name_length + 1 ); - if( !text->font_name ) - return -1; - for( uint8_t i = 0; i < text->font_name_length; i++ ) - text->font_name[i] = lsmash_bs_get_byte( bs ); - text->font_name[text->font_name_length] = '\0'; - } - box->parent = parent; - isom_box_common_copy( text, box ); - if( isom_add_print_func( file, text, level ) ) - return -1; - return isom_read_children( file, box, text, level ); -} - -static int isom_read_tx3g_description( 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_STSD ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)isom_add_description( box->type, (isom_stsd_t *)parent ); - if( !tx3g ) - return -1; - lsmash_bs_t *bs = file->bs; - if( lsmash_bs_read( bs, 38 ) < 0 ) - return -1; - for( int i = 0; i < 6; i++ ) - tx3g->reserved[i] = lsmash_bs_get_byte( bs ); - tx3g->data_reference_index = lsmash_bs_get_be16( bs ); - tx3g->displayFlags = lsmash_bs_get_be32( bs ); - tx3g->horizontal_justification = lsmash_bs_get_byte( bs ); - tx3g->vertical_justification = lsmash_bs_get_byte( bs ); - for( int i = 0; i < 4; i++ ) - tx3g->background_color_rgba[i] = lsmash_bs_get_byte( bs ); - tx3g->top = lsmash_bs_get_be16( bs ); - tx3g->left = lsmash_bs_get_be16( bs ); - tx3g->bottom = lsmash_bs_get_be16( bs ); - tx3g->right = lsmash_bs_get_be16( bs ); - tx3g->startChar = lsmash_bs_get_be16( bs ); - tx3g->endChar = lsmash_bs_get_be16( bs ); - tx3g->font_ID = lsmash_bs_get_be16( bs ); - tx3g->face_style_flags = lsmash_bs_get_byte( bs ); - tx3g->font_size = lsmash_bs_get_byte( bs ); - for( int i = 0; i < 4; i++ ) - tx3g->text_color_rgba[i] = lsmash_bs_get_byte( bs ); - box->parent = parent; - isom_box_common_copy( tx3g, box ); - if( isom_add_print_func( file, tx3g, level ) ) - return -1; - return isom_read_children( file, box, tx3g, level ); -} - -static int isom_read_ftab( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, (lsmash_box_type_t)ISOM_CODEC_TYPE_TX3G_TEXT ) - || ((isom_tx3g_entry_t *)parent)->ftab ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( ftab, isom_tx3g_entry_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be16( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && ftab->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_font_record_t *data = lsmash_malloc_zero( sizeof(isom_font_record_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( ftab->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->font_ID = lsmash_bs_get_be16( bs ); - data->font_name_length = lsmash_bs_get_byte( bs ); - if( data->font_name_length ) - { - data->font_name = lsmash_malloc( data->font_name_length + 1 ); - if( !data->font_name ) - return -1; - for( uint8_t i = 0; i < data->font_name_length; i++ ) - data->font_name[i] = lsmash_bs_get_byte( bs ); - data->font_name[data->font_name_length] = '\0'; - } - } - isom_check_box_size( bs, box ); - isom_box_common_copy( ftab, box ); - return isom_add_print_func( file, ftab, level ); -} - -static int isom_read_stts( 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)->stts ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stts, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && stts->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stts->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->sample_count = lsmash_bs_get_be32( bs ); - data->sample_delta = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( stts, box ); - return isom_add_print_func( file, stts, level ); -} - -static int isom_read_ctts( 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)->ctts ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( ctts, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && ctts->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( ctts->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->sample_count = lsmash_bs_get_be32( bs ); - data->sample_offset = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( ctts, box ); - return isom_add_print_func( file, ctts, level ); -} - -static int isom_read_cslg( 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)->cslg ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( cslg, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - cslg->compositionToDTSShift = lsmash_bs_get_be32( bs ); - cslg->leastDecodeToDisplayDelta = lsmash_bs_get_be32( bs ); - cslg->greatestDecodeToDisplayDelta = lsmash_bs_get_be32( bs ); - cslg->compositionStartTime = lsmash_bs_get_be32( bs ); - cslg->compositionEndTime = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( cslg, box ); - return isom_add_print_func( file, cslg, level ); -} - -static int isom_read_stss( 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)->stss ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stss, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && stss->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stss->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->sample_number = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( stss, box ); - return isom_add_print_func( file, stss, level ); -} - -static int isom_read_stps( 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)->stps ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stps, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && stps->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stps->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->sample_number = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( stps, box ); - return isom_add_print_func( file, stps, level ); -} - -static int isom_read_sdtp( 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 ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF )) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) && ((isom_stbl_t *)parent)->sdtp) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) && ((isom_traf_t *)parent)->sdtp)) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( sdtp, isom_box_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) - { - isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( sdtp->list, data ) ) - { - lsmash_free( data ); - return -1; - } - uint8_t temp = lsmash_bs_get_byte( bs ); - data->is_leading = (temp >> 6) & 0x3; - data->sample_depends_on = (temp >> 4) & 0x3; - data->sample_is_depended_on = (temp >> 2) & 0x3; - data->sample_has_redundancy = temp & 0x3; - } - isom_box_common_copy( sdtp, box ); - return isom_add_print_func( file, sdtp, level ); -} - -static int isom_read_stsc( 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)->stsc ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stsc, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && stsc->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stsc->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->first_chunk = lsmash_bs_get_be32( bs ); - data->samples_per_chunk = lsmash_bs_get_be32( bs ); - data->sample_description_index = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( stsc, box ); - return isom_add_print_func( file, stsc, level ); -} - -static int isom_read_stsz( 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)->stsz ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( stsz, isom_stbl_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - stsz->sample_size = lsmash_bs_get_be32( bs ); - stsz->sample_count = lsmash_bs_get_be32( bs ); - uint64_t pos = lsmash_bs_get_pos( bs ); - if( pos < box->size ) - { - stsz->list = lsmash_create_entry_list(); - if( !stsz->list ) - return -1; - for( ; pos < box->size && stsz->list->entry_count < stsz->sample_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stsz->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->entry_size = lsmash_bs_get_be32( bs ); - } - } - isom_check_box_size( bs, box ); - isom_box_common_copy( stsz, box ); - return isom_add_print_func( file, stsz, level ); -} - -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 ) - return isom_read_unknown_box( file, box, parent, level ); - box->type = lsmash_form_iso_box_type( box->type.fourcc ); - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_STCO ) ) - { - if( isom_add_stco( (isom_stbl_t *)parent ) ) - return -1; - } - else - { - if( isom_add_co64( (isom_stbl_t *)parent ) ) - return -1; - } - isom_stco_t *stco = (isom_stco_t *)parent->extensions.tail->data; - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_STCO ) ) - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && stco->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stco->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->chunk_offset = lsmash_bs_get_be32( bs ); - } - else - { - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && stco->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( stco->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->chunk_offset = lsmash_bs_get_be64( bs ); - } - } - isom_check_box_size( bs, box ); - isom_box_common_copy( stco, box ); - return isom_add_print_func( file, stco, level ); -} - -static int isom_read_sgpd( 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 ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( sgpd, void ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - sgpd->grouping_type = lsmash_bs_get_be32( bs ); - if( box->version == 1 ) - sgpd->default_length = lsmash_bs_get_be32( bs ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - switch( sgpd->grouping_type ) - { - case ISOM_GROUP_TYPE_RAP : - { - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && sgpd->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( sgpd->list, data ) ) - { - lsmash_free( data ); - return -1; - } - memset( data, 0, sizeof(isom_rap_entry_t) ); - /* We don't know groups decided by variable description length. If encountering, skip getting of bytes of it. */ - if( box->version == 1 && !sgpd->default_length ) - data->description_length = lsmash_bs_get_be32( bs ); - else - { - uint8_t temp = lsmash_bs_get_byte( bs ); - data->num_leading_samples_known = (temp >> 7) & 0x01; - data->num_leading_samples = temp & 0x7f; - } - } - isom_check_box_size( bs, box ); - break; - } - case ISOM_GROUP_TYPE_ROLL : - { - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && sgpd->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_roll_entry_t *data = lsmash_malloc( sizeof(isom_roll_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( sgpd->list, data ) ) - { - lsmash_free( data ); - return -1; - } - memset( data, 0, sizeof(isom_roll_entry_t) ); - /* We don't know groups decided by variable description length. If encountering, skip getting of bytes of it. */ - if( box->version == 1 && !sgpd->default_length ) - data->description_length = lsmash_bs_get_be32( bs ); - else - data->roll_distance = lsmash_bs_get_be16( bs ); - } - isom_check_box_size( bs, box ); - break; - } - default : - break; - } - isom_box_common_copy( sgpd, box ); - return isom_add_print_func( file, sgpd, level ); -} - -static int isom_read_sbgp( 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 ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( sbgp, void ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - sbgp->grouping_type = lsmash_bs_get_be32( bs ); - if( box->version == 1 ) - sbgp->grouping_type_parameter = lsmash_bs_get_be32( bs ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && sbgp->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_group_assignment_entry_t *data = lsmash_malloc( sizeof(isom_group_assignment_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( sbgp->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->sample_count = lsmash_bs_get_be32( bs ); - data->group_description_index = lsmash_bs_get_be32( bs ); - } - isom_check_box_size( bs, box ); - isom_box_common_copy( sbgp, box ); - return isom_add_print_func( file, sbgp, level ); -} - -static int isom_read_udta( 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_MOOV ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK )) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) && ((isom_moov_t *)parent)->udta) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) && ((isom_trak_t *)parent)->udta) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( udta, void ); - isom_box_common_copy( udta, box ); - if( isom_add_print_func( file, udta, level ) ) - return -1; - return isom_read_children( file, box, udta, level ); -} - -static int isom_read_chpl( 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_UDTA ) || ((isom_udta_t *)parent)->chpl ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( chpl, isom_udta_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count; - if( box->version == 1 ) - { - chpl->unknown = lsmash_bs_get_byte( bs ); - entry_count = lsmash_bs_get_be32( bs ); - } - else - entry_count = lsmash_bs_get_byte( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && chpl->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_chpl_entry_t *data = lsmash_malloc( sizeof(isom_chpl_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( chpl->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->start_time = lsmash_bs_get_be64( bs ); - data->chapter_name_length = lsmash_bs_get_byte( bs ); - data->chapter_name = lsmash_malloc( data->chapter_name_length + 1 ); - if( !data->chapter_name ) - { - lsmash_free( data ); - return -1; - } - for( uint8_t i = 0; i < data->chapter_name_length; i++ ) - data->chapter_name[i] = lsmash_bs_get_byte( bs ); - data->chapter_name[data->chapter_name_length] = '\0'; - } - isom_check_box_size( bs, box ); - isom_box_common_copy( chpl, box ); - return isom_add_print_func( file, chpl, level ); -} - -static int isom_read_mvex( 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_MOOV ) || ((isom_moov_t *)parent)->mvex ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mvex, isom_moov_t ); - file->flags |= LSMASH_FILE_MODE_FRAGMENTED; - isom_box_common_copy( mvex, box ); - if( isom_add_print_func( file, mvex, level ) ) - return -1; - return isom_read_children( file, box, mvex, level ); -} - -static int isom_read_mehd( 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_MVEX ) || ((isom_mvex_t *)parent)->mehd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mehd, isom_mvex_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - if( box->version == 1 ) - mehd->fragment_duration = lsmash_bs_get_be64( bs ); - else - mehd->fragment_duration = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( mehd, box ); - return isom_add_print_func( file, mehd, level ); -} - -static isom_sample_flags_t isom_bs_get_sample_flags( lsmash_bs_t *bs ) -{ - uint32_t temp = lsmash_bs_get_be32( bs ); - isom_sample_flags_t flags; - flags.reserved = (temp >> 28) & 0xf; - flags.is_leading = (temp >> 26) & 0x3; - flags.sample_depends_on = (temp >> 24) & 0x3; - flags.sample_is_depended_on = (temp >> 22) & 0x3; - flags.sample_has_redundancy = (temp >> 20) & 0x3; - flags.sample_padding_value = (temp >> 17) & 0x7; - flags.sample_is_non_sync_sample = (temp >> 16) & 0x1; - flags.sample_degradation_priority = temp & 0xffff; - return flags; -} - -static int isom_read_trex( 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_MVEX ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( trex, isom_mvex_t ); - box->parent = parent; - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - trex->track_ID = lsmash_bs_get_be32( bs ); - trex->default_sample_description_index = lsmash_bs_get_be32( bs ); - trex->default_sample_duration = lsmash_bs_get_be32( bs ); - trex->default_sample_size = lsmash_bs_get_be32( bs ); - trex->default_sample_flags = isom_bs_get_sample_flags( bs ); - isom_box_common_copy( trex, box ); - return isom_add_print_func( file, trex, level ); -} - -static int isom_read_moof( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( moof, lsmash_file_t ); - box->parent = parent; - isom_box_common_copy( moof, box ); - if( isom_add_print_func( file, moof, level ) ) - return -1; - return isom_read_children( file, box, moof, level ); -} - -static int isom_read_mfhd( 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_MOOF ) || ((isom_moof_t *)parent)->mfhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mfhd, isom_moof_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - mfhd->sequence_number = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( mfhd, box ); - return isom_add_print_func( file, mfhd, level ); -} - -static int isom_read_traf( 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_MOOF ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( traf, isom_moof_t ); - box->parent = parent; - isom_box_common_copy( traf, box ); - if( isom_add_print_func( file, traf, level ) ) - return -1; - return isom_read_children( file, box, traf, level ); -} - -static int isom_read_tfhd( 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_TRAF ) || ((isom_traf_t *)parent)->tfhd ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( tfhd, isom_traf_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - tfhd->track_ID = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) tfhd->base_data_offset = lsmash_bs_get_be64( bs ); - if( box->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) tfhd->sample_description_index = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) tfhd->default_sample_duration = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) tfhd->default_sample_size = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) tfhd->default_sample_flags = isom_bs_get_sample_flags( bs ); - isom_check_box_size( bs, box ); - isom_box_common_copy( tfhd, box ); - return isom_add_print_func( file, tfhd, level ); -} - -static int isom_read_tfdt( 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_TRAF ) || ((isom_traf_t *)parent)->tfdt ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( tfdt, isom_traf_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - if( box->version == 1 ) - tfdt->baseMediaDecodeTime = lsmash_bs_get_be64( bs ); - else - tfdt->baseMediaDecodeTime = lsmash_bs_get_be32( bs ); - isom_check_box_size( bs, box ); - isom_box_common_copy( tfdt, box ); - return isom_add_print_func( file, tfdt, level ); -} - -static int isom_read_trun( 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_TRAF ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( trun, isom_traf_t ); - box->parent = parent; - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - int has_optional_rows = ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT - | ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT - | ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT - | ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT; - has_optional_rows &= box->flags; - trun->sample_count = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) trun->data_offset = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) trun->first_sample_flags = isom_bs_get_sample_flags( bs ); - if( trun->sample_count && has_optional_rows ) - { - trun->optional = lsmash_create_entry_list(); - if( !trun->optional ) - return -1; - for( uint32_t i = 0; i < trun->sample_count; i++ ) - { - isom_trun_optional_row_t *data = lsmash_malloc( sizeof(isom_trun_optional_row_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( trun->optional, data ) ) - { - lsmash_free( data ); - return -1; - } - if( box->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) data->sample_duration = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) data->sample_size = lsmash_bs_get_be32( bs ); - if( box->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) data->sample_flags = isom_bs_get_sample_flags( bs ); - if( box->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) data->sample_composition_time_offset = lsmash_bs_get_be32( bs ); - } - } - isom_check_box_size( bs, box ); - isom_box_common_copy( trun, box ); - return isom_add_print_func( file, trun, level ); -} - -static int isom_read_free( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - isom_box_t *skip = lsmash_malloc_zero( sizeof(isom_box_t) ); - if( !skip ) - return -1; - lsmash_bs_t *bs = file->bs; - isom_skip_box_rest( bs, box ); - skip->destruct = (isom_extension_destructor_t)lsmash_free; - box->manager |= LSMASH_ABSENT_IN_ROOT; - isom_box_common_copy( skip, box ); - if( isom_add_print_func( file, skip, level ) ) - { - lsmash_free( skip ); - return -1; - } - return 0; -} - -static int isom_read_mdat( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_box_t *mdat = lsmash_malloc_zero( sizeof(isom_box_t) ); - if( !mdat ) - return -1; - lsmash_bs_t *bs = file->bs; - isom_skip_box_rest( bs, box ); - mdat->destruct = (isom_extension_destructor_t)lsmash_free; - box->manager |= LSMASH_ABSENT_IN_ROOT; - file->flags |= LSMASH_FILE_MODE_MEDIA; - isom_box_common_copy( mdat, box ); - if( isom_add_print_func( file, mdat, level ) ) - { - lsmash_free( mdat ); - return -1; - } - return 0; -} - -static int isom_read_meta( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( (!lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) - && !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_UDTA )) - || (lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) && ((lsmash_file_t *)parent)->meta) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) && ((isom_moov_t *)parent)->meta) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) && ((isom_trak_t *)parent)->meta) - || (lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_UDTA ) && ((isom_udta_t *)parent)->meta) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( meta, void ); - isom_box_common_copy( meta, box ); - if( isom_add_print_func( file, meta, level ) ) - return -1; - return isom_read_children( file, box, meta, level ); -} - -static int isom_read_keys( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( (!lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) && !(parent->manager & LSMASH_QTFF_BASE)) - || ((isom_meta_t *)parent)->keys ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( keys, isom_meta_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - uint32_t entry_count = lsmash_bs_get_be32( bs ); - uint64_t pos; - for( pos = lsmash_bs_get_pos( bs ); pos < box->size && keys->list->entry_count < entry_count; pos = lsmash_bs_get_pos( bs ) ) - { - isom_keys_entry_t *data = lsmash_malloc( sizeof(isom_keys_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( keys->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->key_size = lsmash_bs_get_be32( bs ); - data->key_namespace = lsmash_bs_get_be32( bs ); - if( data->key_size > 8 ) - { - data->key_value = lsmash_bs_get_bytes( bs, data->key_size - 8 ); - if( !data->key_value ) - return -1; - } - else - data->key_value = NULL; - } - isom_check_box_size( bs, box ); - isom_box_common_copy( keys, box ); - return isom_add_print_func( file, keys, level ); -} - -static int isom_read_ilst( 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_META ) - && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META )) - || ((isom_meta_t *)parent)->ilst ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( ilst, isom_meta_t ); - isom_box_common_copy( ilst, box ); - if( isom_add_print_func( file, ilst, level ) ) - return -1; - return isom_read_children( file, box, ilst, level ); -} - -static int isom_read_metaitem( 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_ILST ) - && !lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) - return isom_read_unknown_box( file, box, parent, level ); - if( isom_add_metaitem( (isom_ilst_t *)parent, box->type.fourcc ) ) - return -1; - isom_metaitem_t *metaitem = (isom_metaitem_t *)parent->extensions.tail->data; - box->parent = parent; - isom_box_common_copy( metaitem, box ); - if( isom_add_print_func( file, metaitem, level ) ) - return -1; - return isom_read_children( file, box, metaitem, level ); -} - -static int isom_read_mean( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( parent->type.fourcc != ITUNES_METADATA_ITEM_CUSTOM || ((isom_metaitem_t *)parent)->mean ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mean, isom_metaitem_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - mean->meaning_string_length = box->size - lsmash_bs_get_pos( bs ); - mean->meaning_string = lsmash_bs_get_bytes( bs, mean->meaning_string_length ); - if( !mean->meaning_string ) - return -1; - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( mean, box ); - return isom_add_print_func( file, mean, level ); -} - -static int isom_read_name( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( parent->type.fourcc != ITUNES_METADATA_ITEM_CUSTOM || ((isom_metaitem_t *)parent)->name ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( name, isom_metaitem_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - name->name_length = box->size - lsmash_bs_get_pos( bs ); - name->name = lsmash_bs_get_bytes( bs, name->name_length ); - if( !name->name ) - return -1; - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( name, box ); - return isom_add_print_func( file, name, level ); -} - -static int isom_read_data( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( ((isom_metaitem_t *)parent)->data ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( data, isom_metaitem_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - data->value_length = box->size - lsmash_bs_get_pos( bs ) - 8; - data->reserved = lsmash_bs_get_be16( bs ); - data->type_set_identifier = lsmash_bs_get_byte( bs ); - data->type_code = lsmash_bs_get_byte( bs ); - data->the_locale = lsmash_bs_get_be32( bs ); - if( data->value_length ) - { - data->value = lsmash_bs_get_bytes( bs, data->value_length ); - if( !data->value ) - return -1; - } - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( data, box ); - return isom_add_print_func( file, data, level ); -} - -static int isom_read_WLOC( 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_UDTA ) || ((isom_udta_t *)parent)->WLOC ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( WLOC, isom_udta_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - WLOC->x = lsmash_bs_get_be16( bs ); - WLOC->y = lsmash_bs_get_be16( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( WLOC, box ); - return isom_add_print_func( file, WLOC, level ); -} - -static int isom_read_LOOP( 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_UDTA ) || ((isom_udta_t *)parent)->LOOP ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( LOOP, isom_udta_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - LOOP->looping_mode = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( LOOP, box ); - return isom_add_print_func( file, LOOP, level ); -} - -static int isom_read_SelO( 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_UDTA ) || ((isom_udta_t *)parent)->SelO ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( SelO, isom_udta_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - SelO->selection_only = lsmash_bs_get_byte( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( SelO, box ); - return isom_add_print_func( file, SelO, level ); -} - -static int isom_read_AllF( 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_UDTA ) || ((isom_udta_t *)parent)->AllF ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( AllF, isom_udta_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - AllF->play_all_frames = lsmash_bs_get_byte( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( AllF, box ); - return isom_add_print_func( file, AllF, level ); -} - -static int isom_read_cprt( 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_UDTA ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( cprt, isom_udta_t ); - box->parent = parent; - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - cprt->language = lsmash_bs_get_be16( bs ); - cprt->notice_length = box->size - (ISOM_FULLBOX_COMMON_SIZE + 2); - if( cprt->notice_length ) - { - cprt->notice = lsmash_bs_get_bytes( bs, cprt->notice_length ); - if( !cprt->notice ) - { - cprt->notice_length = 0; - return -1; - } - } - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( cprt, box ); - return isom_add_print_func( file, cprt, level ); -} - -static int isom_read_mfra( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) -{ - if( !lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) || ((lsmash_file_t *)parent)->mfra ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mfra, lsmash_file_t ); - isom_box_common_copy( mfra, box ); - if( isom_add_print_func( file, mfra, level ) ) - return -1; - return isom_read_children( file, box, mfra, level ); -} - -static int isom_read_tfra( 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_MFRA ) ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box_return_pointer( tfra, isom_mfra_t ); - box->parent = parent; - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - tfra->track_ID = lsmash_bs_get_be32( bs ); - uint32_t temp = lsmash_bs_get_be32( bs ); - tfra->number_of_entry = lsmash_bs_get_be32( bs ); - tfra->reserved = (temp >> 6) & 0x3ffffff; - tfra->length_size_of_traf_num = (temp >> 4) & 0x3; - tfra->length_size_of_trun_num = (temp >> 2) & 0x3; - tfra->length_size_of_sample_num = temp & 0x3; - if( tfra->number_of_entry ) - { - tfra->list = lsmash_create_entry_list(); - if( !tfra->list ) - return -1; - uint64_t (*bs_get_funcs[5])( lsmash_bs_t * ) = - { - lsmash_bs_get_byte_to_64, - lsmash_bs_get_be16_to_64, - lsmash_bs_get_be24_to_64, - lsmash_bs_get_be32_to_64, - lsmash_bs_get_be64 - }; - uint64_t (*bs_put_time) ( lsmash_bs_t * ) = bs_get_funcs[ 3 + (box->version == 1) ]; - uint64_t (*bs_put_moof_offset) ( lsmash_bs_t * ) = bs_get_funcs[ 3 + (box->version == 1) ]; - uint64_t (*bs_put_traf_number) ( lsmash_bs_t * ) = bs_get_funcs[ tfra->length_size_of_traf_num ]; - uint64_t (*bs_put_trun_number) ( lsmash_bs_t * ) = bs_get_funcs[ tfra->length_size_of_trun_num ]; - uint64_t (*bs_put_sample_number)( lsmash_bs_t * ) = bs_get_funcs[ tfra->length_size_of_sample_num ]; - for( uint32_t i = 0; i < tfra->number_of_entry; i++ ) - { - isom_tfra_location_time_entry_t *data = lsmash_malloc( sizeof(isom_tfra_location_time_entry_t) ); - if( !data ) - return -1; - if( lsmash_add_entry( tfra->list, data ) ) - { - lsmash_free( data ); - return -1; - } - data->time = bs_put_time ( bs ); - data->moof_offset = bs_put_moof_offset ( bs ); - data->traf_number = bs_put_traf_number ( bs ); - data->trun_number = bs_put_trun_number ( bs ); - data->sample_number = bs_put_sample_number( bs ); - } - } - isom_check_box_size( bs, box ); - isom_box_common_copy( tfra, box ); - return isom_add_print_func( file, tfra, level ); -} - -static int isom_read_mfro( 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_MFRA ) || ((isom_mfra_t *)parent)->mfro ) - return isom_read_unknown_box( file, box, parent, level ); - isom_add_box( mfro, isom_mfra_t ); - lsmash_bs_t *bs = file->bs; - isom_read_box_rest( bs, box ); - mfro->length = lsmash_bs_get_be32( bs ); - box->size = lsmash_bs_get_pos( bs ); - isom_box_common_copy( mfro, box ); - return isom_add_print_func( file, mfro, level ); -} - -static int isom_check_qtff_meta( lsmash_buffer_t *buffer ) -{ - if( buffer->store < ISOM_FULLBOX_COMMON_SIZE - || LSMASH_4CC( buffer->data[4], buffer->data[5], buffer->data[6], buffer->data[7] ) != ISOM_BOX_TYPE_META.fourcc ) - return 0; /* Obviously, not 'meta' box */ - if( !((buffer->data[8] << 24) | (buffer->data[9] << 16) | (buffer->data[10] << 8) | buffer->data[11]) ) - return 0; /* If this field is 0, this shall be 'meta' box of ISO. */ - return 1; /* OK. This shall be 'meta' box of QTFF. */ -} - -static int isom_read_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos, int level ) -{ - lsmash_bs_t *bs = file->bs; - memset( box, 0, sizeof(isom_box_t) ); - assert( parent && parent->root && parent->file ); - box->root = parent->root; - box->file = parent->file; - box->parent = parent; - if( parent->size < parent_pos + ISOM_BASEBOX_COMMON_SIZE ) - { - /* skip extra bytes */ - uint64_t rest_size = parent->size - parent_pos; - if( !bs->unseekable ) - lsmash_bs_seek( bs, rest_size, SEEK_CUR ); - else - for( uint64_t i = 0; i < rest_size; i++ ) - if( lsmash_bs_read_c( bs ) == EOF ) - break; - box->size = rest_size; - return 0; - } - uint32_t read_size; - if( isom_check_qtff_meta( &bs->buffer ) ) - { - /* 'meta' box of QTFF is not extended from FullBox. - * Reuse the last 4 bytes as the size of the current box. */ - parent->manager |= LSMASH_QTFF_BASE; /* identifier of 'meta' box of QTFF */ - parent->manager &= ~LSMASH_FULLBOX; - parent->type = QT_BOX_TYPE_META; - parent->version = 0; - parent->flags = 0; - lsmash_buffer_t *buffer = &bs->buffer; - memcpy( buffer->data, buffer->data + buffer->store - 4, 4 ); - memset( buffer->data + 4, 0, buffer->store - 4 ); - buffer->store = 4; - buffer->pos = 0; - read_size = ISOM_BASEBOX_COMMON_SIZE - 4; - box->pos = bs->offset - 4; - } - else - { - lsmash_bs_empty( bs ); - read_size = ISOM_BASEBOX_COMMON_SIZE; - box->pos = bs->offset; - } - int ret = -1; - if( !!(ret = isom_bs_read_box_common( bs, box, read_size )) ) - return ret; /* return if reached EOF */ - ++level; - lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ) = NULL; - int (*reader_func)( lsmash_file_t *, isom_box_t *, isom_box_t *, int ) = NULL; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) - { - /* Check whether CODEC is RAW Video/Audio encapsulated in QTFF. */ - if( box->type.fourcc == LSMASH_CODEC_TYPE_RAW.fourcc ) - { - if( ((isom_minf_t *)parent->parent->parent)->vmhd ) - { - form_box_type_func = lsmash_form_qtff_box_type; - reader_func = isom_read_visual_description; - } - else if( ((isom_minf_t *)parent->parent->parent)->smhd ) - { - form_box_type_func = lsmash_form_qtff_box_type; - reader_func = isom_read_audio_description; - } - goto read_box; - } - static struct description_reader_table_tag - { - lsmash_compact_box_type_t fourcc; - lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ); - int (*reader_func)( lsmash_file_t *, isom_box_t *, isom_box_t *, int ); - } description_reader_table[160] = { { 0, NULL, NULL } }; - if( !description_reader_table[0].reader_func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_DESCRIPTION_READER_TABLE_ELEMENT( type, form_box_type_func, reader_func ) \ - description_reader_table[i++] = (struct description_reader_table_tag){ type.fourcc, form_box_type_func, reader_func } - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, lsmash_form_iso_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - //ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, lsmash_form_qtff_box_type, isom_read_visual_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - //ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, lsmash_form_iso_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_QDM2_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_QDMC_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CDX2_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_CDX4_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCA_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_DVI_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_IMA4_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_VDVA_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_MP3_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, lsmash_form_qtff_box_type, isom_read_audio_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, lsmash_form_qtff_box_type, isom_read_qt_text_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, lsmash_form_iso_box_type, isom_read_tx3g_description ); - ADD_DESCRIPTION_READER_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL, NULL ); -#undef ADD_DESCRIPTION_READER_TABLE_ELEMENT - } - for( int i = 0; description_reader_table[i].reader_func; i++ ) - if( box->type.fourcc == description_reader_table[i].fourcc ) - { - form_box_type_func = description_reader_table[i].form_box_type_func; - reader_func = description_reader_table[i].reader_func; - goto read_box; - } - goto read_box; - } - if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) - { - form_box_type_func = lsmash_form_qtff_box_type; - if( box->type.fourcc == QT_BOX_TYPE_FRMA.fourcc ) reader_func = isom_read_frma; - else if( box->type.fourcc == QT_BOX_TYPE_ENDA.fourcc ) reader_func = isom_read_enda; - else if( box->type.fourcc == QT_BOX_TYPE_ESDS.fourcc ) reader_func = isom_read_esds; - else if( box->type.fourcc == QT_BOX_TYPE_CHAN.fourcc ) reader_func = isom_read_chan; - else if( box->type.fourcc == QT_BOX_TYPE_TERMINATOR.fourcc ) reader_func = isom_read_terminator; - else reader_func = isom_read_codec_specific; - goto read_box; - } - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TREF ) ) - { - form_box_type_func = lsmash_form_iso_box_type; - reader_func = isom_read_track_reference_type; - goto read_box; - } - static struct box_reader_table_tag - { - lsmash_compact_box_type_t fourcc; - lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ); - int (*reader_func)( lsmash_file_t *, isom_box_t *, isom_box_t *, int ); - } box_reader_table[128] = { { 0, NULL, NULL } }; - if( !box_reader_table[0].reader_func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_BOX_READER_TABLE_ELEMENT( type, form_box_type_func, reader_func ) \ - box_reader_table[i++] = (struct box_reader_table_tag){ type.fourcc, form_box_type_func, reader_func } - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTYP, lsmash_form_iso_box_type, isom_read_ftyp ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STYP, lsmash_form_iso_box_type, isom_read_styp ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SIDX, lsmash_form_iso_box_type, isom_read_sidx ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOV, lsmash_form_iso_box_type, isom_read_moov ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVHD, lsmash_form_iso_box_type, isom_read_mvhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_IODS, lsmash_form_iso_box_type, isom_read_iods ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, lsmash_form_qtff_box_type, isom_read_ctab ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, lsmash_form_iso_box_type, isom_read_esds ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAK, lsmash_form_iso_box_type, isom_read_trak ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TKHD, lsmash_form_iso_box_type, isom_read_tkhd ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_TAPT, lsmash_form_qtff_box_type, isom_read_tapt ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CLEF, lsmash_form_qtff_box_type, isom_read_clef ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_PROF, lsmash_form_qtff_box_type, isom_read_prof ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_ENOF, lsmash_form_qtff_box_type, isom_read_enof ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_EDTS, lsmash_form_iso_box_type, isom_read_edts ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_ELST, lsmash_form_iso_box_type, isom_read_elst ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREF, lsmash_form_iso_box_type, isom_read_tref ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDIA, lsmash_form_iso_box_type, isom_read_mdia ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDHD, lsmash_form_iso_box_type, isom_read_mdhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_HDLR, lsmash_form_iso_box_type, isom_read_hdlr ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MINF, lsmash_form_iso_box_type, isom_read_minf ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_VMHD, lsmash_form_iso_box_type, isom_read_vmhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SMHD, lsmash_form_iso_box_type, isom_read_smhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_HMHD, lsmash_form_iso_box_type, isom_read_hmhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_NMHD, lsmash_form_iso_box_type, isom_read_nmhd ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GMHD, lsmash_form_qtff_box_type, isom_read_gmhd ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GMIN, lsmash_form_qtff_box_type, isom_read_gmin ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_TEXT, lsmash_form_qtff_box_type, isom_read_text ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_DINF, lsmash_form_iso_box_type, isom_read_dinf ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_DREF, lsmash_form_iso_box_type, isom_read_dref ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_URL, lsmash_form_iso_box_type, isom_read_url ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STBL, lsmash_form_iso_box_type, isom_read_stbl ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSD, lsmash_form_iso_box_type, isom_read_stsd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, lsmash_form_iso_box_type, isom_read_btrt ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, lsmash_form_iso_box_type, isom_read_colr ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, lsmash_form_iso_box_type, isom_read_clap ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, lsmash_form_iso_box_type, isom_read_pasp ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, lsmash_form_qtff_box_type, isom_read_glbl ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, lsmash_form_qtff_box_type, isom_read_gama ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, lsmash_form_qtff_box_type, isom_read_fiel ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, lsmash_form_qtff_box_type, isom_read_cspc ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, lsmash_form_qtff_box_type, isom_read_sgbt ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, lsmash_form_iso_box_type, isom_read_stsl ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, lsmash_form_qtff_box_type, isom_read_wave ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, lsmash_form_qtff_box_type, isom_read_chan ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SRAT, lsmash_form_iso_box_type, isom_read_srat ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, lsmash_form_iso_box_type, isom_read_ftab ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STTS, lsmash_form_iso_box_type, isom_read_stts ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CTTS, lsmash_form_iso_box_type, isom_read_ctts ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CSLG, lsmash_form_iso_box_type, isom_read_cslg ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSS, lsmash_form_iso_box_type, isom_read_stss ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_STPS, lsmash_form_qtff_box_type, isom_read_stps ); - 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_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 ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SBGP, lsmash_form_iso_box_type, isom_read_sbgp ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_UDTA, lsmash_form_iso_box_type, isom_read_udta ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CHPL, lsmash_form_iso_box_type, isom_read_chpl ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_WLOC, lsmash_form_qtff_box_type, isom_read_WLOC ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_LOOP, lsmash_form_qtff_box_type, isom_read_LOOP ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_SELO, lsmash_form_qtff_box_type, isom_read_SelO ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_ALLF, lsmash_form_qtff_box_type, isom_read_AllF ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVEX, lsmash_form_iso_box_type, isom_read_mvex ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MEHD, lsmash_form_iso_box_type, isom_read_mehd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREX, lsmash_form_iso_box_type, isom_read_trex ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOF, lsmash_form_iso_box_type, isom_read_moof ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFHD, lsmash_form_iso_box_type, isom_read_mfhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAF, lsmash_form_iso_box_type, isom_read_traf ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFHD, lsmash_form_iso_box_type, isom_read_tfhd ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFDT, lsmash_form_iso_box_type, isom_read_tfdt ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRUN, lsmash_form_iso_box_type, isom_read_trun ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_FREE, lsmash_form_iso_box_type, isom_read_free ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SKIP, lsmash_form_iso_box_type, isom_read_free ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDAT, lsmash_form_iso_box_type, isom_read_mdat ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_META, lsmash_form_iso_box_type, isom_read_meta ); - ADD_BOX_READER_TABLE_ELEMENT( QT_BOX_TYPE_KEYS, lsmash_form_qtff_box_type, isom_read_keys ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRA, lsmash_form_iso_box_type, isom_read_mfra ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFRA, lsmash_form_iso_box_type, isom_read_tfra ); - ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRO, lsmash_form_iso_box_type, isom_read_mfro ); - ADD_BOX_READER_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL, NULL ); -#undef ADD_BOX_READER_TABLE_ELEMENT - } - for( int i = 0; box_reader_table[i].reader_func; i++ ) - if( box->type.fourcc == box_reader_table[i].fourcc ) - { - form_box_type_func = box_reader_table[i].form_box_type_func; - reader_func = box_reader_table[i].reader_func; - goto read_box; - } - if( box->type.fourcc == ISOM_BOX_TYPE_ILST.fourcc ) - { - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ) - form_box_type_func = lsmash_form_iso_box_type; - else if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ) - form_box_type_func = lsmash_form_qtff_box_type; - if( form_box_type_func ) - { - reader_func = isom_read_ilst; - goto read_box; - } - } - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_ILST ) ) - form_box_type_func = lsmash_form_iso_box_type; - else if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) - form_box_type_func = lsmash_form_qtff_box_type; - if( form_box_type_func ) - { - reader_func = isom_read_metaitem; - goto read_box; - } - if( parent->parent && parent->parent->type.fourcc == ISOM_BOX_TYPE_ILST.fourcc ) - { - if( box->type.fourcc == ISOM_BOX_TYPE_MEAN.fourcc ) - reader_func = isom_read_mean; - else if( box->type.fourcc == ISOM_BOX_TYPE_NAME.fourcc ) - reader_func = isom_read_name; - else if( box->type.fourcc == ISOM_BOX_TYPE_DATA.fourcc ) - reader_func = isom_read_data; - if( reader_func ) - { - form_box_type_func = lsmash_form_iso_box_type; - goto read_box; - } - } - else if( box->type.fourcc == ISOM_BOX_TYPE_CPRT.fourcc ) - { - /* Avoid confusing udta.cprt with ilst.cprt. */ - form_box_type_func = lsmash_form_iso_box_type; - reader_func = isom_read_cprt; - goto read_box; - } - if( parent->parent && lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_STSD ) ) - { - static struct codec_specific_marker_table_tag - { - lsmash_compact_box_type_t fourcc; - lsmash_box_type_t (*form_box_type_func)( lsmash_compact_box_type_t ); - } codec_specific_marker_table[16] = { { 0, NULL } }; - if( !codec_specific_marker_table[0].form_box_type_func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( type, form_box_type_func ) \ - codec_specific_marker_table[i++] = (struct codec_specific_marker_table_tag){ type.fourcc, form_box_type_func } - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_ALAC, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_AVCC, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DAC3, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DAMR, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DDTS, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DEC3, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_DVC1, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( ISOM_BOX_TYPE_HVCC, lsmash_form_iso_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, lsmash_form_qtff_box_type ); - ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); -#undef ADD_CODEC_SPECIFIC_MARKER_TABLE_ELEMENT - } - for( int i = 0; codec_specific_marker_table[i].form_box_type_func; i++ ) - if( box->type.fourcc == codec_specific_marker_table[i].fourcc ) - { - form_box_type_func = codec_specific_marker_table[i].form_box_type_func; - break; - } - reader_func = isom_read_codec_specific; - } -read_box: - if( form_box_type_func ) - box->type = form_box_type_func( box->type.fourcc ); - if( isom_read_fullbox_common_extension( bs, box ) ) - return -1; - return reader_func - ? reader_func( file, box, parent, level ) - : isom_read_unknown_box( file, box, parent, level ); -} - -int isom_read_file( lsmash_file_t *file ) -{ - lsmash_bs_t *bs = file->bs; - if( !bs ) - return -1; - isom_box_t box; - if( file->flags & LSMASH_FILE_MODE_DUMP ) - { - file->print = lsmash_create_entry_list(); - if( !file->print ) - return -1; - } - file->size = UINT64_MAX; - int ret = isom_read_children( file, &box, file, 0 ); - file->size = box.size; - lsmash_bs_empty( bs ); - bs->error = 0; /* Clear error flag. */ - if( ret < 0 ) - return ret; - return isom_check_compatibility( file ); -} - -#endif /* LSMASH_DEMUXER_ENABLED */ diff -Nru l-smash-1.9.1/read.h l-smash-2.3.0/read.h --- l-smash-1.9.1/read.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/read.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/***************************************************************************** - * read.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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_READ_H -#define LSMASH_READ_H - -int isom_read_file( lsmash_file_t *file ); - -#endif /* LSMASH_READ_H */ diff -Nru l-smash-1.9.1/remuxer.c l-smash-2.3.0/remuxer.c --- l-smash-1.9.1/remuxer.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/remuxer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1392 +0,0 @@ -/***************************************************************************** - * remuxer.c: - ***************************************************************************** - * Copyright (C) 2011-2014 L-SMASH project - * - * Authors: Yusuke Nakamura - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - *****************************************************************************/ - -/* This file is available under an ISC license. */ - -#include -#include -#include -#include -#include - -#include "lsmash.h" -#include "cli.h" - -#include "config.h" - -typedef struct -{ - uint32_t track_ID; - uint32_t last_sample_delta; - uint32_t current_sample_number; - uint32_t *summary_remap; - uint64_t skip_dt_interval; - uint64_t last_sample_dts; - lsmash_track_parameters_t track_param; - lsmash_media_parameters_t media_param; -} output_track_t; - -typedef struct -{ - output_track_t *track; - lsmash_movie_parameters_t param; - uint32_t num_tracks; - uint32_t current_track_number; -} output_movie_t; - -typedef struct -{ - lsmash_file_t *fh; - lsmash_file_parameters_t param; - output_movie_t movie; -} output_file_t; - -typedef struct -{ - lsmash_root_t *root; - output_file_t file; -} output_t; - -typedef struct -{ - int active; - lsmash_summary_t *summary; -} input_summary_t; - -typedef struct -{ - int active; - lsmash_sample_t *sample; - double dts; - uint64_t composition_delay; - uint64_t skip_duration; - int reach_end_of_media_timeline; - uint32_t track_ID; - uint32_t last_sample_delta; - uint32_t current_sample_number; - uint32_t current_sample_index; - uint32_t num_summaries; - input_summary_t *summaries; - lsmash_track_parameters_t track_param; - lsmash_media_parameters_t media_param; -} input_track_t; - -typedef struct -{ - input_track_t *track; - lsmash_itunes_metadata_t *itunes_metadata; - lsmash_movie_parameters_t param; - uint32_t movie_ID; - uint32_t num_tracks; - uint32_t num_itunes_metadata; - uint32_t current_track_number; -} input_movie_t; - -typedef struct -{ - lsmash_file_t *fh; - lsmash_file_parameters_t param; - input_movie_t movie; -} input_file_t; - -typedef struct -{ - lsmash_root_t *root; - input_file_t file; -} input_t; - -typedef struct -{ - char *raw_track_option; - int remove; - int disable; - int16_t alternate_group; - uint16_t ISO_language; - uint32_t seek; - int consider_rap; - char *handler_name; -} track_media_option; - -typedef struct -{ - output_t *output; - input_t *input; - track_media_option **track_option; - int num_input; - int add_bom_to_chpl; - int ref_chap_available; - uint32_t chap_track; - char *chap_file; - uint16_t default_language; - uint32_t fragment_base_track; - int dash; -} remuxer_t; - -typedef struct -{ - char *whole_track_option; - int num_track_delimiter; -} file_option; - -static void cleanup_input_movie( input_t *input ) -{ - if( !input ) - return; - input_movie_t *in_movie = &input->file.movie; - if( in_movie->itunes_metadata ) - { - for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ ) - lsmash_cleanup_itunes_metadata( &in_movie->itunes_metadata[i] ); - lsmash_freep( &in_movie->itunes_metadata ); - } - if( in_movie->track ) - { - for( uint32_t i = 0; i < in_movie->num_tracks; i++ ) - { - input_track_t *in_track = &in_movie->track[i]; - if( in_track->summaries ) - { - for( uint32_t j = 0; j < in_track->num_summaries; j++ ) - lsmash_cleanup_summary( in_track->summaries[j].summary ); - lsmash_free( in_track->summaries ); - } - } - lsmash_freep( &in_movie->track ); - } - lsmash_close_file( &input->file.param ); - lsmash_destroy_root( input->root ); - input->root = NULL; -} - -static void cleanup_output_movie( output_t *output ) -{ - if( !output ) - return; - output_movie_t *out_movie = &output->file.movie; - if( out_movie->track ) - { - for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) - lsmash_free( out_movie->track[i].summary_remap ); - lsmash_freep( &out_movie->track ); - } - lsmash_close_file( &output->file.param ); - lsmash_destroy_root( output->root ); - output->root = NULL; -} - -static void cleanup_remuxer( remuxer_t *remuxer ) -{ - for( int i = 0; i < remuxer->num_input; i++ ) - { - cleanup_input_movie( &remuxer->input[i] ); - if( remuxer->track_option[i] ) - lsmash_free( remuxer->track_option[i] ); - } - cleanup_output_movie( remuxer->output ); -} - -#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) -#define REFRESH_CONSOLE eprintf( " \r" ) - -static int remuxer_error( remuxer_t *remuxer, const char *message, ... ) -{ - cleanup_remuxer( remuxer ); - REFRESH_CONSOLE; - eprintf( "[Error] " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -static int error_message( const char *message, ... ) -{ - REFRESH_CONSOLE; - eprintf( "[Error] " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -static int warning_message( const char *message, ... ) -{ - REFRESH_CONSOLE; - eprintf( "[Warning] " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -#define REMUXER_ERR( ... ) remuxer_error( &remuxer, __VA_ARGS__ ) -#define ERROR_MSG( ... ) error_message( __VA_ARGS__ ) -#define WARNING_MSG( ... ) warning_message( __VA_ARGS__ ) - -static void display_version( void ) -{ - eprintf( "\n" - "L-SMASH isom/mov re-muliplexer rev%s %s\n" - "Built on %s %s\n" - "Copyright (C) 2011-2014 L-SMASH project\n", - LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); -} - -static void display_help( void ) -{ - display_version(); - eprintf( "\n" - "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\n" - "Global options:\n" - " --help Display help.\n" - " --version Display version information.\n" - " --chapter Set chapters from the file.\n" - " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n" - " in the chapter list. (experimental)\n" - " --chapter-track Set which track the chapter applies to.\n" - " This option takes effect only when reference\n" - " chapter is available.\n" - " If this option is not used, it defaults to 1.\n" - " --language Specify the default language for all the output tracks.\n" - " This option is overridden by the track options.\n" - " --fragment Enable fragmentation per random accessible point.\n" - " Set which track the fragmentation is based on.\n" - " --dash Construct Indexed self-initializing Media Segment.\n" - " This option requires --fragment.\n" - "Track options:\n" - " remove Remove this track\n" - " disable Disable this track\n" - " language= Specify media language\n" - " alternate-group= Specify alternate group\n" - " handler= Set media handler name\n" - " seek= Specify starting point in media\n" - " safe-seek= Same as seek except for considering random accessible point\n" - " Media starts from the closest random accessible point\n" - "How to use track options:\n" - " -i input?[track_number1]:[track_option1],[track_option2]?[track_number2]:...\n" - "For example:\n" - " remuxer -i input1 -i input2?2:alternate-group=1?3:language=jpn,alternate-group=1 -o output\n" ); -} - -static char *duplicate_string( char *src ) -{ - if( !src ) - return NULL; - int dst_size = strlen( src ) + 1; - char *dst = lsmash_malloc( dst_size ); - if( !dst ) - return NULL; - memcpy( dst, src, dst_size ); - return dst; -} - -static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata ) -{ - memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) ); - if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) ) - return -1; - lsmash_itunes_metadata_t shadow = *metadata; - metadata->meaning = NULL; - metadata->name = NULL; - memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) ); - if( shadow.meaning ) - { - metadata->meaning = duplicate_string( shadow.meaning ); - if( !metadata->meaning ) - return -1; - } - if( shadow.name ) - { - metadata->name = duplicate_string( shadow.name ); - if( !metadata->name ) - goto fail; - } - if( shadow.type == ITUNES_METADATA_TYPE_STRING ) - { - metadata->value.string = duplicate_string( shadow.value.string ); - if( !metadata->value.string ) - goto fail; - } - else if( shadow.type == ITUNES_METADATA_TYPE_BINARY ) - { - metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size ); - if( !metadata->value.binary.data ) - goto fail; - memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size ); - metadata->value.binary.size = shadow.value.binary.size; - metadata->value.binary.subtype = shadow.value.binary.subtype; - } - else - metadata->value = shadow.value; - return 0; -fail: - lsmash_freep( &metadata->meaning ); - lsmash_freep( &metadata->name ); - return -1; -} - -static int get_movie( input_t *input, char *input_name ) -{ - if( !strcmp( input_name, "-" ) ) - return ERROR_MSG( "standard input not supported.\n" ); - /* Read an input file. */ - input->root = lsmash_create_root(); - if( !input->root ) - return ERROR_MSG( "failed to create a ROOT for an input file.\n" ); - input_file_t *in_file = &input->file; - if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 ) - return ERROR_MSG( "failed to open an input file.\n" ); - in_file->fh = lsmash_set_file( input->root, &in_file->param ); - if( !in_file->fh ) - return ERROR_MSG( "failed to add an input file into a ROOT.\n" ); - if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 ) - return ERROR_MSG( "failed to read an input file\n" ); - /* Get iTunes metadata. */ - input_movie_t *in_movie = &in_file->movie; - in_movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root ); - if( in_movie->num_itunes_metadata ) - { - in_movie->itunes_metadata = lsmash_malloc( in_movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) ); - if( !in_movie->itunes_metadata ) - return ERROR_MSG( "failed to alloc iTunes metadata.\n" ); - uint32_t itunes_metadata_count = 0; - for( uint32_t i = 1; i <= in_movie->num_itunes_metadata; i++ ) - { - if( get_itunes_metadata( input->root, i, &in_movie->itunes_metadata[itunes_metadata_count] ) ) - { - WARNING_MSG( "failed to get an iTunes metadata.\n" ); - continue; - } - ++itunes_metadata_count; - } - in_movie->num_itunes_metadata = itunes_metadata_count; - } - in_movie->current_track_number = 1; - lsmash_initialize_movie_parameters( &in_movie->param ); - if( lsmash_get_movie_parameters( input->root, &in_movie->param ) ) - return ERROR_MSG( "failed to get movie parameters.\n" ); - uint32_t num_tracks = in_movie->num_tracks = in_movie->param.number_of_tracks; - /* Create tracks. */ - input_track_t *in_track = in_movie->track = lsmash_malloc( num_tracks * sizeof(input_track_t) ); - if( !in_track ) - return ERROR_MSG( "failed to alloc input tracks.\n" ); - memset( in_track, 0, num_tracks * sizeof(input_track_t) ); - for( uint32_t i = 0; i < num_tracks; i++ ) - { - in_track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 ); - if( !in_track[i].track_ID ) - return ERROR_MSG( "failed to get track_ID.\n" ); - } - for( uint32_t i = 0; i < num_tracks; i++ ) - { - lsmash_initialize_track_parameters( &in_track[i].track_param ); - if( lsmash_get_track_parameters( input->root, in_track[i].track_ID, &in_track[i].track_param ) ) - { - WARNING_MSG( "failed to get track parameters.\n" ); - continue; - } - lsmash_initialize_media_parameters( &in_track[i].media_param ); - if( lsmash_get_media_parameters( input->root, in_track[i].track_ID, &in_track[i].media_param ) ) - { - WARNING_MSG( "failed to get media parameters.\n" ); - continue; - } - if( lsmash_construct_timeline( input->root, in_track[i].track_ID ) ) - { - WARNING_MSG( "failed to construct timeline.\n" ); - continue; - } - if( lsmash_get_last_sample_delta_from_media_timeline( input->root, in_track[i].track_ID, &in_track[i].last_sample_delta ) ) - { - WARNING_MSG( "failed to get the last sample delta.\n" ); - continue; - } - in_track[i].num_summaries = lsmash_count_summary( input->root, in_track[i].track_ID ); - if( in_track[i].num_summaries == 0 ) - { - WARNING_MSG( "failed to find valid summaries.\n" ); - continue; - } - in_track[i].summaries = lsmash_malloc( in_track[i].num_summaries * sizeof(input_summary_t) ); - if( !in_track[i].summaries ) - return ERROR_MSG( "failed to alloc input summaries.\n" ); - memset( in_track[i].summaries, 0, in_track[i].num_summaries * sizeof(input_summary_t) ); - for( uint32_t j = 0; j < in_track[i].num_summaries; j++ ) - { - lsmash_summary_t *summary = lsmash_get_summary( input->root, in_track[i].track_ID, j + 1 ); - if( !summary ) - { - WARNING_MSG( "failed to get a summary.\n" ); - continue; - } - if( !LSMASH_FLAGS_SATISFIED( lsmash_check_codec_support( summary->sample_type ), LSMASH_CODEC_SUPPORT_FLAG_REMUX ) ) - { - lsmash_cleanup_summary( summary ); - WARNING_MSG( "no support to remux this stream.\n" ); - continue; - } - in_track[i].summaries[j].summary = summary; - in_track[i].summaries[j].active = 1; - } - in_track[i].active = 1; - in_track[i].current_sample_number = 1; - in_track[i].sample = NULL; - in_track[i].dts = 0; - in_track[i].composition_delay = 0; - in_track[i].skip_duration = 0; - } - lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) ); - return 0; -} - -static int parse_track_option( remuxer_t *remuxer ) -{ - track_media_option **track = remuxer->track_option; - for( int i = 0; i < remuxer->num_input; i++ ) - for( uint32_t j = 0; j < remuxer->input[i].file.movie.num_tracks; j++ ) - { - track_media_option *current_track_opt = &track[i][j]; - if( current_track_opt->raw_track_option == NULL ) - break; - if( !strchr( current_track_opt->raw_track_option, ':' ) - || strchr( current_track_opt->raw_track_option, ':' ) == current_track_opt->raw_track_option ) - return ERROR_MSG( "track number is not specified in %s\n", current_track_opt->raw_track_option ); - if( strchr( current_track_opt->raw_track_option, ':' ) != strrchr( current_track_opt->raw_track_option, ':' ) ) - return ERROR_MSG( "multiple colons inside one track option in %s.\n", current_track_opt->raw_track_option ); - uint32_t track_number = atoi( strtok( current_track_opt->raw_track_option, ":" ) ); - if( track_number == 0 ) - return ERROR_MSG( "%s is an invalid track number.\n", strtok( current_track_opt->raw_track_option, ":" ) ); - if( track_number > remuxer->input[i].file.movie.num_tracks ) - return ERROR_MSG( "%d is an invalid track number.\n", track_number ); - char *track_option; - while( (track_option = strtok( NULL, "," )) != NULL ) - { - if( strchr( track_option, '=' ) != strrchr( track_option, '=' ) ) - return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option ); - current_track_opt = &track[i][track_number - 1]; - if( strstr( track_option, "remove" ) ) - { - current_track_opt->remove = 1; - /* No need to parse track options for this track anymore. */ - break; - } - else if( strstr( track_option, "disable" ) ) - current_track_opt->disable = 1; - else if( strstr( track_option, "alternate-group=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - current_track_opt->alternate_group = atoi( track_parameter ); - } - else if( strstr( track_option, "language=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - current_track_opt->ISO_language = lsmash_pack_iso_language( track_parameter ); - } - else if( strstr( track_option, "handler=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - current_track_opt->handler_name = track_parameter; - } - else if( strstr( track_option, "safe-seek=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - current_track_opt->seek = atoi( track_parameter ); - current_track_opt->consider_rap = 1; - } - else if( strstr( track_option, "seek=" ) ) - { - char *track_parameter = strchr( track_option, '=' ) + 1; - current_track_opt->seek = atoi( track_parameter ); - } - else - return ERROR_MSG( "unknown track option %s\n", track_option ); - } - } - return 0; -} - -static int parse_cli_option( int argc, char **argv, remuxer_t *remuxer ) -{ - input_t *input = remuxer->input; - track_media_option **track_option = remuxer->track_option; - file_option input_file_option[ remuxer->num_input ]; - int input_movie_number = 0; - for( int i = 1; i < argc ; i++ ) - { - /* Get input movies. */ - if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) /* input file */ - { - if( ++i == argc ) - return ERROR_MSG( "-i requires an argument.\n" ); - input_file_option[input_movie_number].num_track_delimiter = 0; - char *p = argv[i]; - while( *p ) - input_file_option[input_movie_number].num_track_delimiter += (*p++ == '?'); - if( get_movie( &input[input_movie_number], strtok( argv[i], "?" ) ) ) - return ERROR_MSG( "failed to get input movie.\n" ); - uint32_t num_tracks = input[input_movie_number].file.movie.num_tracks; - track_option[input_movie_number] = lsmash_malloc( num_tracks * sizeof(track_media_option) ); - if( !track_option[input_movie_number] ) - return ERROR_MSG( "couldn't allocate memory.\n" ); - memset( track_option[input_movie_number], 0, num_tracks * sizeof(track_media_option) ); - input_file_option[input_movie_number].whole_track_option = strtok( NULL, "" ); - input[input_movie_number].file.movie.movie_ID = input_movie_number + 1; - ++input_movie_number; - } - /* Create output movie. */ - else if( !strcasecmp( argv[i], "-o" ) || !strcasecmp( argv[i], "--output" ) ) /* output file */ - { - if( ++i == argc ) - return ERROR_MSG( "-o requires an argument.\n" ); - output_t *output = remuxer->output; - output->root = lsmash_create_root(); - if( !output->root ) - return ERROR_MSG( "failed to create a ROOT.\n" ); - if( lsmash_open_file( argv[i], 0, &output->file.param ) < 0 ) - return ERROR_MSG( "failed to open an output file.\n" ); - } - else if( !strcasecmp( argv[i], "--chapter" ) ) /* chapter file */ - { - if( ++i == argc ) - return ERROR_MSG( "--chapter requires an argument.\n" ); - remuxer->chap_file = argv[i]; - } - else if( !strcasecmp( argv[i], "--chpl-with-bom" ) ) - remuxer->add_bom_to_chpl = 1; - else if( !strcasecmp( argv[i], "--chapter-track" ) ) /* track to apply reference chapter to */ - { - if( ++i == argc ) - return ERROR_MSG( "--chapter-track requires an argument.\n" ); - remuxer->chap_track = atoi( argv[i] ); - if( !remuxer->chap_track ) - return ERROR_MSG( "%s is an invalid track number.\n", argv[i] ); - } - else if( !strcasecmp( argv[i], "--language" ) ) - { - if( ++i == argc ) - return ERROR_MSG( "--chapter requires an argument.\n" ); - remuxer->default_language = lsmash_pack_iso_language( argv[i] ); - } - else if( !strcasecmp( argv[i], "--fragment" ) ) - { - if( ++i == argc ) - return ERROR_MSG( "--fragment requires an argument.\n" ); - remuxer->fragment_base_track = atoi( argv[i] ); - if( remuxer->fragment_base_track == 0 ) - return ERROR_MSG( "%s is an invalid track number.\n", argv[i] ); - } - else if( !strcasecmp( argv[i], "--dash" ) ) - remuxer->dash = 1; - else - return ERROR_MSG( "unkown option found: %s\n", argv[i] ); - } - if( !remuxer->output->root ) - return ERROR_MSG( "output file name is not specified.\n" ); - /* Parse track options */ - /* Get the current track and media parameters */ - for( int i = 0; i < remuxer->num_input; i++ ) - for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ ) - { - input_track_t *in_track = &input[i].file.movie.track[j]; - if( !in_track->active ) - continue; - track_option[i][j].alternate_group = in_track->track_param.alternate_group; - track_option[i][j].ISO_language = in_track->media_param.ISO_language; - track_option[i][j].handler_name = in_track->media_param.media_handler_name; - } - /* Set the default language */ - if( remuxer->default_language ) - for( int i = 0; i < remuxer->num_input; i++ ) - for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ ) - track_option[i][j].ISO_language = remuxer->default_language; - /* Get the track and media parameters specified by users */ - for( int i = 0; i < remuxer->num_input; i++ ) - { - if( input_file_option[i].num_track_delimiter > input[i].file.movie.num_tracks ) - return ERROR_MSG( "more track options specified than the actual number of the tracks (%"PRIu32").\n", - input[i].file.movie.num_tracks ); - if( input_file_option[i].num_track_delimiter ) - { - track_option[i][0].raw_track_option = strtok( input_file_option[i].whole_track_option, "?" ); - for( int j = 1; j < input_file_option[i].num_track_delimiter ; j++ ) - track_option[i][j].raw_track_option = strtok( NULL, "?" ); - } - } - if( parse_track_option( remuxer ) ) - return ERROR_MSG( "failed to parse track options.\n" ); - return 0; -} - -static void replace_with_valid_brand( remuxer_t *remuxer ) -{ - static const lsmash_brand_type brand_filter_list[] = - { - ISOM_BRAND_TYPE_3G2A, - ISOM_BRAND_TYPE_3GG6, - ISOM_BRAND_TYPE_3GG9, - ISOM_BRAND_TYPE_3GP4, - ISOM_BRAND_TYPE_3GP5, - ISOM_BRAND_TYPE_3GP6, - ISOM_BRAND_TYPE_3GP7, - ISOM_BRAND_TYPE_3GP8, - ISOM_BRAND_TYPE_3GP9, - ISOM_BRAND_TYPE_3GR6, - ISOM_BRAND_TYPE_3GR9, - ISOM_BRAND_TYPE_M4A , - ISOM_BRAND_TYPE_M4B , - ISOM_BRAND_TYPE_M4V , - ISOM_BRAND_TYPE_AVC1, - ISOM_BRAND_TYPE_DBY1, - ISOM_BRAND_TYPE_ISO2, - ISOM_BRAND_TYPE_ISO3, - ISOM_BRAND_TYPE_ISO4, - ISOM_BRAND_TYPE_ISO5, - ISOM_BRAND_TYPE_ISO6, - ISOM_BRAND_TYPE_ISO7, - ISOM_BRAND_TYPE_ISOM, - ISOM_BRAND_TYPE_MP41, - ISOM_BRAND_TYPE_MP42, - ISOM_BRAND_TYPE_QT , - 0 - }; - input_t *input = remuxer->input; - /* Check the number of video and audio tracks, and the number of video - * and audio sample descriptions for the restrictions of 3GPP Basic Profile. - * - the maximum number of tracks shall be one for video (or alternatively - * one for scene description), one for audio and one for text - * - the maximum number of sample entries shall be one per track for video - * and audio (but unrestricted for text and scene description) */ - uint32_t video_track_count = 0; - uint32_t audio_track_count = 0; - uint32_t video_num_summaries = 0; - uint32_t audio_num_summaries = 0; - for( int i = 0; i < remuxer->num_input; i++ ) - { - input_movie_t *movie = &input[i].file.movie; - for( int j = 0; j < movie->num_tracks; j++ ) - { - if( movie->track[j].media_param.handler_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ) - { - if( ++video_track_count == 1 ) - video_num_summaries = movie->track[j].num_summaries; - } - else if( movie->track[j].media_param.handler_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ) - { - if( ++audio_track_count == 1 ) - audio_num_summaries = movie->track[j].num_summaries; - } - } - } - for( int i = 0; i < remuxer->num_input; i++ ) - for( uint32_t j = 0; j <= input[i].file.param.brand_count; j++ ) - { - int invalid = 1; - uint32_t *brand = j ? &input[i].file.param.brands[j - 1] : &input[i].file.param.major_brand; - uint32_t *version = j ? NULL : &input[i].file.param.minor_version; - for( int k = 0; brand_filter_list[k]; k++ ) - { - if( *brand == brand_filter_list[k] ) - { - if( ((*brand >> 24) & 0xFF) == '3' - && ((*brand >> 16) & 0xFF) == 'g' - && (((*brand >> 8) & 0xFF) == 'p' || ((*brand >> 8) & 0xFF) == 'r') ) - { - if( remuxer->fragment_base_track == 0 /* Movie fragments are not allowed in '3gp4' and '3gp5'. */ - && video_track_count <= 1 && audio_track_count <= 1 - && video_num_summaries <= 1 && audio_num_summaries <= 1 ) - continue; - /* Replace with the General Profile for maximum compatibility. */ - if( (*brand & 0xFF) < '6' ) - { - /* 3GPP version 6.7.0 General Profile */ - *brand = ISOM_BRAND_TYPE_3GG6; - if( version ) - *version = 0x00000700; - } - else - *brand = LSMASH_4CC( '3', 'g', 'g', *brand & 0xFF ); - } - if( (remuxer->output->file.param.mode & LSMASH_FILE_MODE_INDEX) - && (*brand == ISOM_BRAND_TYPE_AVC1 - || (((*brand >> 24) & 0xFF) == 'i' - && ((*brand >> 16) & 0xFF) == 's' - && ((*brand >> 8) & 0xFF) == 'o' - && ((*brand & 0xFF) == 'm' || (*brand & 0xFF) < '6'))) ) - *brand = ISOM_BRAND_TYPE_ISO6; - invalid = 0; - break; - } - } - if( invalid ) - { - /* Replace with the 'mp42' brand. */ - *brand = ISOM_BRAND_TYPE_MP42; - if( version ) - *version = 0; - } - } -} - -static int set_movie_parameters( remuxer_t *remuxer ) -{ - int num_input = remuxer->num_input; - input_t *input = remuxer->input; - output_t *output = remuxer->output; - output_file_t *out_file = &output->file; - if( remuxer->fragment_base_track ) - out_file->param.mode |= LSMASH_FILE_MODE_FRAGMENTED; - if( remuxer->dash ) - { - if( remuxer->fragment_base_track ) - out_file->param.mode |= LSMASH_FILE_MODE_INDEX; - else - WARNING_MSG( "--dash requiers --fragment.\n" ); - } - replace_with_valid_brand( remuxer ); - if( remuxer->dash ) - { - out_file->param.major_brand = ISOM_BRAND_TYPE_DASH; - out_file->param.minor_version = 0; - } - else - { - /* Pick the most used major_brands. */ - lsmash_brand_type major_brand [num_input]; - uint32_t minor_version [num_input]; - uint32_t major_brand_count[num_input]; - uint32_t num_major_brand = 0; - for( int i = 0; i < num_input; i++ ) - { - major_brand [num_major_brand] = input[i].file.param.major_brand; - minor_version [num_major_brand] = input[i].file.param.minor_version; - major_brand_count[num_major_brand] = 0; - for( int j = 0; j < num_input; j++ ) - if( (major_brand [num_major_brand] == input[j].file.param.major_brand) - && (minor_version[num_major_brand] == input[j].file.param.minor_version) ) - { - if( i <= j ) - ++major_brand_count[num_major_brand]; - else - { - /* This major_brand already exists. Skip this. */ - major_brand_count[num_major_brand] = 0; - --num_major_brand; - break; - } - } - ++num_major_brand; - } - uint32_t most_used_count = 0; - for( uint32_t i = 0; i < num_major_brand; i++ ) - if( major_brand_count[i] > most_used_count ) - { - most_used_count = major_brand_count[i]; - out_file->param.major_brand = major_brand [i]; - out_file->param.minor_version = minor_version[i]; - } - } - /* Deduplicate compatible brands. */ - uint32_t num_input_brands = num_input + (remuxer->dash ? 1 : 0); - for( int i = 0; i < num_input; i++ ) - num_input_brands += input[i].file.param.brand_count; - lsmash_brand_type input_brands[num_input_brands]; - num_input_brands = 0; - if( remuxer->dash ) - input_brands[num_input_brands++] = ISOM_BRAND_TYPE_DASH; - for( int i = 0; i < num_input; i++ ) - { - input_brands[num_input_brands++] = input[i].file.param.major_brand; - for( uint32_t j = 0; j < input[i].file.param.brand_count; j++ ) - if( input[i].file.param.brands[j] ) - input_brands[num_input_brands++] = input[i].file.param.brands[j]; - } - lsmash_brand_type output_brands[num_input_brands]; - uint32_t num_output_brands = 0; - for( uint32_t i = 0; i < num_input_brands; i++ ) - { - output_brands[num_output_brands] = input_brands[i]; - for( uint32_t j = 0; j < num_output_brands; j++ ) - if( output_brands[num_output_brands] == output_brands[j] ) - { - /* This brand already exists. Skip this. */ - --num_output_brands; - break; - } - ++num_output_brands; - } - out_file->param.brand_count = num_output_brands; - out_file->param.brands = output_brands; - /* Set up a file. */ - out_file->fh = lsmash_set_file( output->root, &out_file->param ); - if( !out_file->fh ) - return ERROR_MSG( "failed to add an output file into a ROOT.\n" ); - /* Check whether a reference chapter track is allowed or not. */ - if( remuxer->chap_file ) - for( uint32_t i = 0; i < out_file->param.brand_count; i++ ) - { - uint32_t brand = out_file->param.brands[i]; - /* According to the restrictions of 3GPP Basic Profile, - * - there shall be no references between tracks, e.g., a scene description track - * shall not refer to a media track since all tracks are on equal footing and - * played in parallel by a conforming player. - * Therefore, the referenced chapter track is forbidden to use for 3GPP Basic Profile. */ - if( ((brand >> 24) & 0xFF) == '3' - && ((brand >> 16) & 0xFF) == 'g' - && ((brand >> 8) & 0xFF) == 'p' ) - break; - /* QuickTime file and iTunes MP4 file can contain the referenced chapter track. */ - if( brand == ISOM_BRAND_TYPE_QT || brand == ISOM_BRAND_TYPE_M4A - || brand == ISOM_BRAND_TYPE_M4B || brand == ISOM_BRAND_TYPE_M4P - || brand == ISOM_BRAND_TYPE_M4V ) - { - remuxer->ref_chap_available = 1; - break; - } - } - /* Set the movie timescale in order to match the media timescale if only one track is there. */ - lsmash_initialize_movie_parameters( &out_file->movie.param ); - if( out_file->movie.num_tracks == 1 ) - for( int i = 0; i < remuxer->num_input; i++ ) - { - input_movie_t *in_movie = &input[i].file.movie; - for( uint32_t j = 0; j < in_movie->num_tracks; j++ ) - if( in_movie->track[j].active ) - { - out_file->movie.param.timescale = in_movie->track[j].media_param.timescale; - break; - } - } - return lsmash_set_movie_parameters( output->root, &out_file->movie.param ); -} - -static void set_itunes_metadata( output_t *output, input_t *input, int num_input ) -{ - for( int i = 0; i < num_input; i++ ) - for( uint32_t j = 0; j < input[i].file.movie.num_itunes_metadata; j++ ) - if( lsmash_set_itunes_metadata( output->root, input[i].file.movie.itunes_metadata[j] ) ) - { - WARNING_MSG( "failed to set an iTunes metadata.\n" ); - continue; - } -} - -static int set_starting_point( input_t *input, input_track_t *in_track, uint32_t seek_point, int consider_rap ) -{ - if( seek_point == 0 ) - return 0; - uint32_t rap_number; - if( lsmash_get_closest_random_accessible_point_from_media_timeline( input->root, in_track->track_ID, 1, &rap_number ) ) - { - if( consider_rap ) - return ERROR_MSG( "failed to get the first random accessible point.\n" ); - else - { - WARNING_MSG( "no random access point!\n" ); - /* Set number of the first sample to be muxed. */ - in_track->current_sample_number = seek_point; - return 0; - } - } - /* Get composition delay. */ - uint64_t rap_dts; - uint64_t rap_cts; - uint32_t ctd_shift; - if( lsmash_get_dts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_dts ) ) - return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" ); - if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_cts ) ) - return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" ); - if( lsmash_get_composition_to_decode_shift_from_media_timeline( input->root, in_track->track_ID, &ctd_shift ) ) - return ERROR_MSG( "failed to get composition to decode timeline shfit.\n" ); - in_track->composition_delay = rap_cts - rap_dts + ctd_shift; - /* Check if starting point is random accessible. */ - if( lsmash_get_closest_random_accessible_point_from_media_timeline( input->root, in_track->track_ID, seek_point, &rap_number ) ) - return ERROR_MSG( "failed to get a random accessible point.\n" ); - if( rap_number != seek_point ) - { - WARNING_MSG( "starting point you specified is not a random accessible point.\n" ); - if( consider_rap ) - { - /* Get duration that should be skipped. */ - if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_cts ) ) - return ERROR_MSG( "failed to get CTS of the closest and past random accessible sample of starting point.\n" ); - uint64_t seek_cts; - if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, seek_point, &seek_cts ) ) - return ERROR_MSG( "failed to get CTS of starting point.\n" ); - if( rap_cts < seek_cts ) - in_track->skip_duration = seek_cts - rap_cts; - } - } - /* Set number of the first sample to be muxed. */ - in_track->current_sample_number = consider_rap ? rap_number : seek_point; - return 0; -} - -static void exclude_invalid_output_track( output_t *output, output_track_t *out_track, - input_movie_t *in_movie, input_track_t *in_track, - const char *message, ... ) -{ - REFRESH_CONSOLE; - eprintf( "[Warning] in %"PRIu32"/%"PRIu32" -> out %"PRIu32": ", in_movie->movie_ID, in_track->track_ID, out_track->track_ID ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - lsmash_delete_track( output->root, out_track->track_ID ); - -- output->file.movie.num_tracks; - in_track->active = 0; -} - -static int prepare_output( remuxer_t *remuxer ) -{ - input_t *input = remuxer->input; - output_t *output = remuxer->output; - output_movie_t *out_movie = &output->file.movie; - /* Count the number of output tracks. */ - for( int i = 0; i < remuxer->num_input; i++ ) - out_movie->num_tracks += input[i].file.movie.num_tracks; - for( int i = 0; i < remuxer->num_input; i++ ) - { - input_movie_t *in_movie = &input[i].file.movie; - for( uint32_t j = 0; j < in_movie->num_tracks; j++ ) - { - /* Don't remux tracks specified as 'remove' by a user. */ - if( remuxer->track_option[i][j].remove ) - in_movie->track[j].active = 0; - if( !in_movie->track[j].active ) - -- out_movie->num_tracks; - } - } - if( set_movie_parameters( remuxer ) ) - return ERROR_MSG( "failed to set output movie parameters.\n" ); - set_itunes_metadata( output, input, remuxer->num_input ); - /* Allocate output tracks. */ - out_movie->track = lsmash_malloc( out_movie->num_tracks * sizeof(output_track_t) ); - if( !out_movie->track ) - return ERROR_MSG( "failed to alloc output tracks.\n" ); - out_movie->current_track_number = 1; - for( int i = 0; i < remuxer->num_input; i++ ) - { - input_movie_t *in_movie = &input[i].file.movie; - for( uint32_t j = 0; j < in_movie->num_tracks; j++ ) - { - track_media_option *current_track_opt = &remuxer->track_option[i][j]; - input_track_t *in_track = &in_movie->track[j]; - if( !in_track->active ) - continue; - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - out_track->summary_remap = lsmash_malloc( in_track->num_summaries * sizeof(uint32_t) ); - if( !out_track->summary_remap ) - return ERROR_MSG( "failed to create summary mapping for a track.\n" ); - memset( out_track->summary_remap, 0, in_track->num_summaries * sizeof(uint32_t) ); - out_track->track_ID = lsmash_create_track( output->root, in_track->media_param.handler_type ); - if( !out_track->track_ID ) - return ERROR_MSG( "failed to create a track.\n" ); - /* Copy track and media parameters except for track_ID. */ - 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; - 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 ) ) - { - exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set track parameters.\n" ); - continue; - } - if( lsmash_set_media_parameters( output->root, out_track->track_ID, &out_track->media_param ) ) - { - exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set media parameters.\n" ); - continue; - } - uint32_t valid_summary_count = 0; - for( uint32_t k = 0; k < in_track->num_summaries; k++ ) - { - if( !in_track->summaries[k].active ) - { - out_track->summary_remap[k] = 0; - continue; - } - lsmash_summary_t *summary = in_track->summaries[k].summary; - if( lsmash_add_sample_entry( output->root, out_track->track_ID, summary ) == 0 ) - { - WARNING_MSG( "failed to append a summary.\n" ); - lsmash_cleanup_summary( summary ); - in_track->summaries[k].summary = NULL; - in_track->summaries[k].active = 0; - out_track->summary_remap[k] = 0; - continue; - } - out_track->summary_remap[k] = ++valid_summary_count; - } - if( valid_summary_count == 0 ) - { - exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to append all summaries.\n" ); - continue; - } - out_track->last_sample_delta = in_track->last_sample_delta; - if( set_starting_point( input, in_track, current_track_opt->seek, current_track_opt->consider_rap ) ) - { - exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set starting point.\n" ); - continue; - } - out_track->current_sample_number = 1; - out_track->skip_dt_interval = 0; - out_track->last_sample_dts = 0; - ++ out_movie->current_track_number; - } - } - if( out_movie->num_tracks == 0 ) - return ERROR_MSG( "failed to create the output movie.\n" ); - out_movie->current_track_number = 1; - return 0; -} - -static void set_reference_chapter_track( remuxer_t *remuxer ) -{ - if( remuxer->ref_chap_available ) - lsmash_create_reference_chapter_track( remuxer->output->root, remuxer->chap_track, remuxer->chap_file ); -} - -static int flush_movie_fragment( remuxer_t *remuxer ) -{ - input_t *inputs = remuxer->input; - output_t *output = remuxer->output; - output_movie_t *out_movie = &output->file.movie; - uint32_t out_current_track_number = 1; - for( uint32_t i = 1; i <= remuxer->num_input; i++ ) - { - input_t *in = &inputs[i - 1]; - input_movie_t *in_movie = &in->file.movie; - for( uint32_t j = 1; j <= in_movie->num_tracks; j++ ) - { - input_track_t *in_track = &in_movie->track[j - 1]; - if( !in_track->active ) - continue; - output_track_t *out_track = &out_movie->track[out_current_track_number - 1]; - if( !in_track->reach_end_of_media_timeline ) - { - lsmash_sample_t sample; - if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number, &sample ) < 0 ) - return ERROR_MSG( "failed to get the information of the next sample.\n" ); - uint64_t sample_dts = sample.dts - out_track->skip_dt_interval; - if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, sample_dts - out_track->last_sample_dts ) < 0 ) - return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" ); - } - else - if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, out_track->last_sample_delta ) < 0 ) - return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" ); - if( ++out_current_track_number > out_movie->num_tracks ) - break; - } - } - return 0; -} - -static int do_remux( remuxer_t *remuxer ) -{ -#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) - input_t *inputs = remuxer->input; - output_t *output = remuxer->output; - output_movie_t *out_movie = &output->file.movie; - set_reference_chapter_track( remuxer ); - double largest_dts = 0; - uint32_t input_movie_number = 1; - uint32_t num_consecutive_sample_skip = 0; - uint32_t num_active_input_tracks = out_movie->num_tracks; - uint64_t total_media_size = 0; - uint8_t sample_count = 0; - uint8_t pending_flush_fragments = (remuxer->fragment_base_track != 0); - while( 1 ) - { - input_t *in = &inputs[input_movie_number - 1]; - input_movie_t *in_movie = &in->file.movie; - input_track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ]; - if( !in_track->active ) - { - /* Move the next track. */ - if( ++ in_movie->current_track_number > in_movie->num_tracks ) - { - /* Move the next input movie. */ - in_movie->current_track_number = 1; - ++input_movie_number; - } - if( input_movie_number > remuxer->num_input ) - input_movie_number = 1; /* Back the first input movie. */ - continue; - } - /* Try append a sample in an input track where we didn't reach the end of media timeline. */ - if( !in_track->reach_end_of_media_timeline ) - { - lsmash_sample_t *sample = in_track->sample; - /* Get a new sample data if the track doesn't hold any one. */ - if( !sample ) - { - sample = lsmash_get_sample_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number ); - if( sample ) - { - in_track->sample = sample; - in_track->dts = (double)sample->dts / in_track->media_param.timescale; - /* Adapt the sample description index. */ - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries - : sample->index == 0 ? 1 - : sample->index; - sample->index = out_track->summary_remap[ sample->index - 1 ]; - if( in_track->current_sample_index == 0 ) - in_track->current_sample_index = sample->index; - } - else - { - if( lsmash_check_sample_existence_in_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number ) ) - { - ERROR_MSG( "failed to get a sample.\n" ); - break; - } - /* No more appendable samples in this track. */ - in_track->sample = NULL; - in_track->reach_end_of_media_timeline = 1; - if( --num_active_input_tracks == 0 ) - break; /* end of muxing */ - } - } - if( sample ) - { - /* Flushing the active movie fragment is pending until random accessible point sample within all active tracks are ready. */ - if( remuxer->fragment_base_track ) - { - if( pending_flush_fragments == 0 ) - pending_flush_fragments = (remuxer->fragment_base_track == out_movie->current_track_number - && sample->prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE); - else if( num_consecutive_sample_skip == num_active_input_tracks ) - { - if( flush_movie_fragment( remuxer ) < 0 ) - { - ERROR_MSG( "failed to flush a movie fragment.\n" ); - break; - } - if( lsmash_create_fragment_movie( output->root ) < 0 ) - { - ERROR_MSG( "failed to create a movie fragment.\n" ); - break; - } - pending_flush_fragments = 0; - } - } - /* Append a sample if meeting a condition. */ - if( pending_flush_fragments == 0 - ? in_track->dts <= largest_dts || num_consecutive_sample_skip == num_active_input_tracks - : sample->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE && sample->index == in_track->current_sample_index ) - { - if( sample->index ) - { - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - /* The first DTS must be 0. */ - if( out_track->current_sample_number == 1 ) - out_track->skip_dt_interval = sample->dts; - if( out_track->skip_dt_interval ) - { - sample->dts -= out_track->skip_dt_interval; - sample->cts -= out_track->skip_dt_interval; - } - uint64_t sample_size = sample->length; /* sample might be deleted internally after appending. */ - uint64_t last_sample_dts = sample->dts; /* same as above */ - uint32_t sample_index = sample->index; /* same as above */ - /* Append a sample into output movie. */ - if( lsmash_append_sample( output->root, out_track->track_ID, sample ) ) - { - lsmash_delete_sample( sample ); - return ERROR_MSG( "failed to append a sample.\n" ); - } - largest_dts = LSMASH_MAX( largest_dts, in_track->dts ); - in_track->sample = NULL; - in_track->current_sample_number += 1; - in_track->current_sample_index = sample_index; - out_track->current_sample_number += 1; - out_track->last_sample_dts = last_sample_dts; - num_consecutive_sample_skip = 0; - total_media_size += sample_size; - /* Print, per 256 samples, total size of imported media. */ - if( ++sample_count == 0 ) - eprintf( "Importing: %"PRIu64" bytes\r", total_media_size ); - } - else - { - lsmash_delete_sample( sample ); - in_track->sample = NULL; - in_track->current_sample_number += 1; - } - } - else - ++num_consecutive_sample_skip; /* Skip appendig sample. */ - } - } - /* Move the next track. */ - if( ++ in_movie->current_track_number > in_movie->num_tracks ) - { - /* Move the next input movie. */ - in_movie->current_track_number = 1; - ++input_movie_number; - } - if( input_movie_number > remuxer->num_input ) - input_movie_number = 1; /* Back the first input movie. */ - if( ++ out_movie->current_track_number > out_movie->num_tracks ) - out_movie->current_track_number = 1; /* Back the first track in the output movie. */ - } - for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) - if( lsmash_flush_pooled_samples( output->root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) ) - return ERROR_MSG( "failed to flush samples.\n" ); - return 0; -#undef LSMASH_MAX -} - -static int construct_timeline_maps( remuxer_t *remuxer ) -{ - input_t *input = remuxer->input; - output_t *output = remuxer->output; - output_movie_t *out_movie = &output->file.movie; - track_media_option **track_option = remuxer->track_option; - out_movie->current_track_number = 1; - for( int i = 0; i < remuxer->num_input; i++ ) - for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ ) - { - input_track_t *in_track = &input[i].file.movie.track[j]; - if( !in_track->active ) - continue; - output_track_t *out_track = &out_movie->track[ out_movie->current_track_number ++ - 1 ]; - if( track_option[i][j].seek ) - { - /* Reconstruct timeline maps. */ - if( lsmash_delete_explicit_timeline_map( output->root, out_track->track_ID ) ) - return ERROR_MSG( "failed to delete explicit timeline maps.\n" ); - uint32_t movie_timescale = lsmash_get_movie_timescale( output->root ); - uint32_t media_timescale = lsmash_get_media_timescale( output->root, out_track->track_ID ); - if( !media_timescale ) - return ERROR_MSG( "media timescale is broken.\n" ); - double timescale_convert_multiplier = (double)movie_timescale / media_timescale; - lsmash_edit_t edit; - edit.start_time = in_track->composition_delay + in_track->skip_duration; - if( edit.start_time ) - { - uint64_t empty_duration = edit.start_time + lsmash_get_composition_to_decode_shift( output->root, out_track->track_ID ); - lsmash_edit_t empty_edit; - empty_edit.duration = empty_duration * timescale_convert_multiplier + 0.5; - empty_edit.start_time = ISOM_EDIT_MODE_EMPTY; - empty_edit.rate = ISOM_EDIT_MODE_NORMAL; - if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, empty_edit ) ) - return ERROR_MSG( "failed to create a empty duration.\n" ); - } - if( remuxer->fragment_base_track == 0 ) - edit.duration = (out_track->last_sample_dts + out_track->last_sample_delta - in_track->skip_duration) * timescale_convert_multiplier; - else - edit.duration = ISOM_EDIT_DURATION_IMPLICIT; - edit.rate = ISOM_EDIT_MODE_NORMAL; - if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, edit ) ) - return ERROR_MSG( "failed to create a explicit timeline map.\n" ); - } - else if( lsmash_copy_timeline_map( output->root, out_track->track_ID, input[i].root, in_track->track_ID ) ) - return ERROR_MSG( "failed to copy timeline maps.\n" ); - } - out_movie->current_track_number = 1; - return 0; -} - -static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size ) -{ - REFRESH_CONSOLE; - eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 ); - return 0; -} - -static int finish_movie( remuxer_t *remuxer ) -{ - output_t *output = remuxer->output; - /* Set chapter list */ - if( remuxer->chap_file ) - lsmash_set_tyrant_chapter( output->root, remuxer->chap_file, remuxer->add_bom_to_chpl ); - /* Finish muxing. */ - lsmash_adhoc_remux_t moov_to_front; - moov_to_front.func = moov_to_front_callback; - moov_to_front.buffer_size = 4*1024*1024; /* 4MiB */ - moov_to_front.param = NULL; - REFRESH_CONSOLE; - if( lsmash_finish_movie( output->root, &moov_to_front ) ) - return -1; - return remuxer->fragment_base_track ? 0 : lsmash_write_lsmash_indicator( output->root ); -} - -int main( int argc, char *argv[] ) -{ - if ( argc < 2 ) - { - display_help(); - return -1; - } - else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) - { - display_help(); - return 0; - } - else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) - { - display_version(); - return 0; - } - else if( argc < 5 ) - { - display_help(); - return -1; - } - - lsmash_get_mainargs( &argc, &argv ); - int num_input = 0; - for( int i = 1 ; i < argc ; i++ ) - if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) - num_input++; - if( !num_input ) - return ERROR_MSG( "no input file specified.\n" ); - output_t output = { 0 }; - input_t input[ num_input ]; - track_media_option *track_option[ num_input ]; - remuxer_t remuxer = { &output, input, track_option, num_input, 0, 0, 1, NULL, 0 }; - memset( input, 0, num_input * sizeof(input_t) ); - memset( track_option, 0, num_input * sizeof(track_media_option *) ); - if( parse_cli_option( argc, argv, &remuxer ) ) - return REMUXER_ERR( "failed to parse command line options.\n" ); - if( prepare_output( &remuxer ) ) - return REMUXER_ERR( "failed to set up preparation for output.\n" ); - if( remuxer.fragment_base_track && construct_timeline_maps( &remuxer ) ) - return REMUXER_ERR( "failed to construct timeline maps.\n" ); - if( do_remux( &remuxer ) ) - return REMUXER_ERR( "failed to remux movies.\n" ); - if( remuxer.fragment_base_track == 0 && construct_timeline_maps( &remuxer ) ) - return REMUXER_ERR( "failed to construct timeline maps.\n" ); - if( finish_movie( &remuxer ) ) - return REMUXER_ERR( "failed to finish output movie.\n" ); - REFRESH_CONSOLE; - eprintf( "Remuxing completed!\n" ); - cleanup_remuxer( &remuxer ); - return 0; -} diff -Nru l-smash-1.9.1/summary.c l-smash-2.3.0/summary.c --- l-smash-1.9.1/summary.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/summary.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,338 +0,0 @@ -/***************************************************************************** - * summary.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 "importer.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "box.h" -#include "description.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(); - if( !bs ) - return -1; - mp4a_AudioSpecificConfig_t *asc = - mp4a_create_AudioSpecificConfig( summary->aot, - summary->frequency, - summary->channels, - summary->sbr_mode, - NULL,/*FIXME*/ - 0 /*FIXME*/); - 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; - lsmash_codec_specific_t *specific = lsmash_malloc_zero( sizeof(lsmash_codec_specific_t) ); - if( !specific ) - { - lsmash_free( new_asc ); - return -1; - } - specific->type = LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN; - specific->format = LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED; - specific->destruct = (lsmash_codec_specific_destructor_t)lsmash_free; - specific->size = new_length; - specific->data.unstructured = lsmash_memdup( new_asc, new_length ); - if( !specific->data.unstructured - || lsmash_add_entry( &summary->opaque->list, specific ) ) - { - lsmash_free( new_asc ); - lsmash_destroy_codec_specific_data( specific ); - return -1; - } - return 0; -} - -lsmash_summary_t *lsmash_create_summary( lsmash_summary_type summary_type ) -{ - size_t summary_size; - switch( summary_type ) - { - case LSMASH_SUMMARY_TYPE_VIDEO : - summary_size = sizeof(lsmash_video_summary_t); - break; - case LSMASH_SUMMARY_TYPE_AUDIO : - summary_size = sizeof(lsmash_audio_summary_t); - break; - default : - summary_size = sizeof(lsmash_summary_t); - return NULL; - } - lsmash_summary_t *summary = (lsmash_summary_t *)lsmash_malloc_zero( summary_size ); - if( !summary ) - return NULL; - summary->opaque = (lsmash_codec_specific_list_t *)lsmash_malloc_zero( sizeof(lsmash_codec_specific_list_t) ); - if( !summary->opaque ) - { - lsmash_free( summary ); - return NULL; - } - summary->summary_type = summary_type; - return summary; -} - -void lsmash_cleanup_summary( lsmash_summary_t *summary ) -{ - if( !summary ) - return; - if( summary->opaque ) - { - for( lsmash_entry_t *entry = summary->opaque->list.head; entry; ) - { - lsmash_entry_t *next = entry->next; - lsmash_destroy_codec_specific_data( (lsmash_codec_specific_t *)entry->data ); - lsmash_free( entry ); - entry = next; - } - lsmash_free( summary->opaque ); - } - lsmash_free( summary ); -} - -int lsmash_add_codec_specific_data( lsmash_summary_t *summary, lsmash_codec_specific_t *specific ) -{ - if( !summary || !summary->opaque || !specific ) - return -1; - lsmash_codec_specific_t *dup = isom_duplicate_codec_specific_data( specific ); - if( !dup ) - return -1; - if( lsmash_add_entry( &summary->opaque->list, dup ) ) - { - lsmash_destroy_codec_specific_data( dup ); - return -1; - } - return 0; -} - -uint32_t lsmash_count_summary( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root || track_ID == 0 ) - return 0; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->hdlr - || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd ) - return 0; - return trak->mdia->minf->stbl->stsd->list.entry_count; -} - -lsmash_summary_t *lsmash_get_summary( lsmash_root_t *root, uint32_t track_ID, uint32_t description_number ) -{ - if( !root || track_ID == 0 || description_number == 0 ) - return NULL; - isom_trak_t *trak = isom_get_trak( root->file, track_ID ); - if( !trak - || !trak->mdia - || !trak->mdia->mdhd || !trak->mdia->hdlr || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stsd ) - return NULL; - isom_minf_t *minf = trak->mdia->minf; - isom_stsd_t *stsd = minf->stbl->stsd; - uint32_t i = 1; - for( lsmash_entry_t *entry = stsd->list.head; entry; entry = entry->next ) - { - if( i != description_number ) - { - ++i; - continue; - } - isom_sample_entry_t *sample_entry = entry->data; - if( !sample_entry ) - return NULL; - if( minf->vmhd ) - return isom_create_video_summary_from_description( sample_entry ); - else if( minf->smhd ) - return isom_create_audio_summary_from_description( sample_entry ); - else - return NULL; - } - return NULL; -} - -int lsmash_compare_summary( lsmash_summary_t *a, lsmash_summary_t *b ) -{ - if( !a || !b ) - return -1; - if( a->summary_type != b->summary_type - || !lsmash_check_box_type_identical( a->sample_type, b->sample_type ) ) - return 1; - if( a->summary_type == LSMASH_SUMMARY_TYPE_VIDEO ) - { - lsmash_video_summary_t *in_video = (lsmash_video_summary_t *)a; - lsmash_video_summary_t *out_video = (lsmash_video_summary_t *)b; - if( in_video->width != out_video->width - || in_video->height != out_video->height - || in_video->depth != out_video->depth - || in_video->par_h != out_video->par_h - || in_video->par_v != out_video->par_v - || memcmp( in_video->compressorname, out_video->compressorname, strlen( in_video->compressorname ) ) - || in_video->clap.width.n != out_video->clap.width.n - || in_video->clap.width.d != out_video->clap.width.d - || in_video->clap.height.n != out_video->clap.height.n - || in_video->clap.height.d != out_video->clap.height.d - || in_video->clap.horizontal_offset.n != out_video->clap.horizontal_offset.n - || in_video->clap.horizontal_offset.d != out_video->clap.horizontal_offset.d - || in_video->clap.vertical_offset.n != out_video->clap.vertical_offset.n - || in_video->clap.vertical_offset.d != out_video->clap.vertical_offset.d - || in_video->color.primaries_index != out_video->color.primaries_index - || in_video->color.transfer_index != out_video->color.transfer_index - || in_video->color.matrix_index != out_video->color.matrix_index - || in_video->color.full_range != out_video->color.full_range ) - return 1; - } - else if( a->summary_type == LSMASH_SUMMARY_TYPE_AUDIO ) - { - lsmash_audio_summary_t *in_audio = (lsmash_audio_summary_t *)a; - lsmash_audio_summary_t *out_audio = (lsmash_audio_summary_t *)b; - if( in_audio->frequency != out_audio->frequency - || in_audio->channels != out_audio->channels - || in_audio->sample_size != out_audio->sample_size - || in_audio->samples_in_frame != out_audio->samples_in_frame ) - return 1; - } - return isom_compare_opaque_extensions( a, b ); -} - -lsmash_codec_support_flag lsmash_check_codec_support( lsmash_codec_type_t sample_type ) -{ - static struct codec_support_table_tag - { - lsmash_codec_type_t type; - lsmash_codec_support_flag flags; - } codec_support_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, LSMASH_CODEC_SUPPORT_FLAG_NONE } }; - if( lsmash_check_codec_type_identical( codec_support_table[0].type, LSMASH_CODEC_TYPE_UNSPECIFIED ) ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_CODEC_SUPPORT_TABLE_ELEMENT( type, flags ) \ - codec_support_table[i++] = (struct codec_support_table_tag){ type, flags } - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_QCLP_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_DEMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ALAC_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RAW_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_MUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RAW_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, LSMASH_CODEC_SUPPORT_FLAG_REMUX ); - ADD_CODEC_SUPPORT_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, LSMASH_CODEC_SUPPORT_FLAG_NONE ); - } - for( int i = 0; !lsmash_check_codec_type_identical( codec_support_table[i].type, LSMASH_CODEC_TYPE_UNSPECIFIED ); i++ ) - if( lsmash_check_codec_type_identical( sample_type, codec_support_table[i].type ) ) - return codec_support_table[i].flags; - return LSMASH_CODEC_SUPPORT_FLAG_NONE; -} diff -Nru l-smash-1.9.1/timeline.c l-smash-2.3.0/timeline.c --- l-smash-1.9.1/timeline.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/timeline.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1963 +0,0 @@ -/***************************************************************************** - * timeline.c: - ***************************************************************************** - * Copyright (C) 2011-2014 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. */ - -#ifdef LSMASH_DEMUXER_ENABLED - -#include "internal.h" /* must be placed first */ - -#include -#include -#include - -#include "box.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "description.h" - -#define NO_RANDOM_ACCESS_POINT 0xffffffff - -typedef struct -{ - uint64_t data_offset; - uint64_t length; - uint32_t number; -} isom_portable_chunk_t; - -typedef struct -{ - uint64_t pos; - uint32_t duration; - uint32_t offset; - uint32_t length; - uint32_t index; - isom_portable_chunk_t *chunk; - lsmash_sample_property_t prop; -} isom_sample_info_t; - -typedef struct -{ - uint64_t pos; /* position of the first sample in this bunch */ - uint32_t duration; /* duration in media timescale each sample has */ - uint32_t offset; /* offset between composition time and decoding time each sample has */ - uint32_t length; /* data size each sample has */ - uint32_t index; /* sample_description_index applied to each sample */ - isom_portable_chunk_t *chunk; /* chunk samples belong to */ - lsmash_sample_property_t prop; /* property applied to each sample */ - uint32_t sample_count; /* number of samples in this bunch */ -} isom_lpcm_bunch_t; - -static const lsmash_class_t lsmash_timeline_class = -{ - "timeline" -}; - -typedef struct isom_timeline_tag isom_timeline_t; - -struct isom_timeline_tag -{ - const lsmash_class_t *class; - uint32_t track_ID; - uint32_t movie_timescale; - uint32_t media_timescale; - uint32_t sample_count; - uint32_t max_sample_size; - uint32_t ctd_shift; /* shift from composition to decode timeline */ - uint64_t media_duration; - uint64_t track_duration; - uint32_t last_accessed_sample_number; - uint32_t last_accessed_chunk_number; - uint64_t last_accessed_sample_dts; - uint64_t last_accessed_offset; - uint32_t last_accessed_lpcm_bunch_number; - uint32_t last_accessed_lpcm_bunch_duration; - uint32_t last_accessed_lpcm_bunch_sample_count; - uint32_t last_accessed_lpcm_bunch_first_sample_number; - uint64_t last_accessed_lpcm_bunch_dts; - uint64_t last_read_size; - uint32_t last_accessed_chunk_alloc_size; - void *last_accessed_chunk_data; /* may not contain all of a chunk */ - lsmash_entry_list_t edit_list [1]; /* list of edits */ - lsmash_entry_list_t chunk_list[1]; /* list of chunks */ - lsmash_entry_list_t info_list [1]; /* list of sample info */ - lsmash_entry_list_t bunch_list[1]; /* list of LPCM bunch */ - int (*get_dts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts ); - int (*get_cts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts ); - int (*get_sample_duration)( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration ); - lsmash_sample_t *(*get_sample)( lsmash_file_t *file, isom_timeline_t *timeline, uint32_t sample_number ); - int (*get_sample_info)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample ); - int (*get_sample_property)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop ); - int (*check_sample_existence)( isom_timeline_t *timeline, uint32_t sample_number ); -}; - -static isom_timeline_t *isom_get_timeline( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !track_ID || !root || !root->file || !root->file->timeline ) - return NULL; - for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next ) - { - isom_timeline_t *timeline = (isom_timeline_t *)entry->data; - if( !timeline ) - return NULL; - if( timeline->track_ID == track_ID ) - return timeline; - } - return NULL; -} - -static isom_timeline_t *isom_create_timeline( void ) -{ - isom_timeline_t *timeline = lsmash_malloc_zero( sizeof(isom_timeline_t) ); - if( !timeline ) - return NULL; - timeline->class = &lsmash_timeline_class; - lsmash_init_entry_list( timeline->edit_list ); - lsmash_init_entry_list( timeline->chunk_list ); - lsmash_init_entry_list( timeline->info_list ); - lsmash_init_entry_list( timeline->bunch_list ); - return timeline; -} - -static void isom_destruct_timeline_direct( isom_timeline_t *timeline ) -{ - if( !timeline ) - return; - if( timeline->last_accessed_chunk_data ) - lsmash_free( timeline->last_accessed_chunk_data ); - lsmash_remove_entries( timeline->edit_list, NULL ); - lsmash_remove_entries( timeline->chunk_list, NULL ); /* chunk data must be already freed. */ - lsmash_remove_entries( timeline->info_list, NULL ); - lsmash_remove_entries( timeline->bunch_list, NULL ); - lsmash_free( timeline ); -} - -void isom_remove_timelines( lsmash_file_t *file ) -{ - if( !file - || !file->timeline ) - return; - lsmash_remove_list( file->timeline, isom_destruct_timeline_direct ); -} - -void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID ) -{ - if( track_ID == 0 - || !root - || !root->file - || !root->file->timeline ) - return; - for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next ) - { - isom_timeline_t *timeline = (isom_timeline_t *)entry->data; - if( !timeline ) - continue; - if( timeline->track_ID == track_ID ) - { - lsmash_remove_entry_direct( root->file->timeline, entry, isom_destruct_timeline_direct ); - break; - } - } -} - -static void isom_get_qt_fixed_comp_audio_sample_quants -( - isom_timeline_t *timeline, - isom_sample_entry_t *description, - uint32_t *samples_per_packet, - uint32_t *constant_sample_size -) -{ - isom_audio_entry_t *audio = (isom_audio_entry_t *)description; - if( audio->version == 0 ) - { - uint32_t dummy; - if( !isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, samples_per_packet, constant_sample_size, &dummy ) ) - { - /* LPCM */ - if( !isom_is_lpcm_audio( audio ) ) - lsmash_log( timeline, LSMASH_LOG_WARNING, "unsupported implicit sample table!\n" ); - *samples_per_packet = 1; - *constant_sample_size = (audio->samplesize * audio->channelcount) / 8; - } - } - else if( audio->version == 1 ) - { - *samples_per_packet = audio->samplesPerPacket; - *constant_sample_size = audio->bytesPerFrame; - } - else /* if( audio->version == 2 ) */ - { - *samples_per_packet = audio->constLPCMFramesPerAudioPacket; - *constant_sample_size = audio->constBytesPerAudioPacket; - } -} - -static int isom_is_qt_fixed_compressed_audio -( - isom_sample_entry_t *description -) -{ - if( (description->manager & LSMASH_VIDEO_DESCRIPTION) || !isom_is_qt_audio( description->type ) ) - return 0; - /* LPCM is a special case of fixed compression. */ - return (((isom_audio_entry_t *)description)->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION); -} - -static int isom_add_sample_info_entry( isom_timeline_t *timeline, isom_sample_info_t *src_info ) -{ - isom_sample_info_t *dst_info = lsmash_malloc( sizeof(isom_sample_info_t) ); - if( !dst_info ) - return -1; - if( lsmash_add_entry( timeline->info_list, dst_info ) ) - { - lsmash_free( dst_info ); - return -1; - } - *dst_info = *src_info; - return 0; -} - -static int isom_add_lpcm_bunch_entry( isom_timeline_t *timeline, isom_lpcm_bunch_t *src_bunch ) -{ - isom_lpcm_bunch_t *dst_bunch = lsmash_malloc( sizeof(isom_lpcm_bunch_t) ); - if( !dst_bunch ) - return -1; - if( lsmash_add_entry( timeline->bunch_list, dst_bunch ) ) - { - lsmash_free( dst_bunch ); - return -1; - } - *dst_bunch = *src_bunch; - return 0; -} - -static int isom_add_portable_chunk_entry( isom_timeline_t *timeline, isom_portable_chunk_t *src_chunk ) -{ - isom_portable_chunk_t *dst_chunk = lsmash_malloc( sizeof(isom_portable_chunk_t) ); - if( !dst_chunk ) - return -1; - if( lsmash_add_entry( timeline->chunk_list, dst_chunk ) ) - { - lsmash_free( dst_chunk ); - return -1; - } - *dst_chunk = *src_chunk; - return 0; -} - -static int isom_compare_lpcm_sample_info( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info ) -{ - return info->duration != bunch->duration - || info->offset != bunch->offset - || info->length != bunch->length - || info->index != bunch->index - || info->chunk != bunch->chunk; -} - -static void isom_update_bunch( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info ) -{ - bunch->pos = info->pos; - bunch->duration = info->duration; - bunch->offset = info->offset; - bunch->length = info->length; - bunch->index = info->index; - bunch->chunk = info->chunk; - bunch->prop = info->prop; - bunch->sample_count = 1; -} - -static isom_lpcm_bunch_t *isom_get_bunch( isom_timeline_t *timeline, uint32_t sample_number ) -{ - if( sample_number >= timeline->last_accessed_lpcm_bunch_first_sample_number - && sample_number < timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count ) - /* Get from the last accessed LPCM bunch. */ - return (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, timeline->last_accessed_lpcm_bunch_number ); - uint32_t first_sample_number_in_next_bunch; - uint32_t bunch_number = 1; - uint64_t bunch_dts; - if( timeline->last_accessed_lpcm_bunch_first_sample_number - && timeline->last_accessed_lpcm_bunch_first_sample_number <= sample_number ) - { - first_sample_number_in_next_bunch = timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count; - bunch_number += timeline->last_accessed_lpcm_bunch_number; - bunch_dts = timeline->last_accessed_lpcm_bunch_dts - + timeline->last_accessed_lpcm_bunch_duration * timeline->last_accessed_lpcm_bunch_sample_count; - } - else - { - /* Seek from the first LPCM bunch. */ - first_sample_number_in_next_bunch = 1; - bunch_dts = 0; - } - isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ ); - if( !bunch ) - return NULL; - first_sample_number_in_next_bunch += bunch->sample_count; - while( sample_number >= first_sample_number_in_next_bunch ) - { - bunch_dts += bunch->duration * bunch->sample_count; - bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ ); - if( !bunch ) - return NULL; - first_sample_number_in_next_bunch += bunch->sample_count; - } - timeline->last_accessed_lpcm_bunch_dts = bunch_dts; - timeline->last_accessed_lpcm_bunch_number = bunch_number - 1; - timeline->last_accessed_lpcm_bunch_duration = bunch->duration; - timeline->last_accessed_lpcm_bunch_sample_count = bunch->sample_count; - timeline->last_accessed_lpcm_bunch_first_sample_number = first_sample_number_in_next_bunch - bunch->sample_count; - return bunch; -} - -static int isom_get_dts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts ) -{ - if( sample_number == timeline->last_accessed_sample_number ) - *dts = timeline->last_accessed_sample_dts; - else if( sample_number == 1 ) - *dts = 0; - else if( sample_number == timeline->last_accessed_sample_number + 1 ) - { - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number ); - if( !info ) - return -1; - *dts = timeline->last_accessed_sample_dts + info->duration; - } - else if( sample_number == timeline->last_accessed_sample_number - 1 ) - { - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 ); - if( !info ) - return -1; - *dts = timeline->last_accessed_sample_dts - info->duration; - } - else - { - *dts = 0; - uint32_t distance = sample_number - 1; - lsmash_entry_t *entry; - for( entry = timeline->info_list->head; entry; entry = entry->next ) - { - isom_sample_info_t *info = (isom_sample_info_t *)entry->data; - if( !info ) - return -1; - if( distance-- == 0 ) - break; - *dts += info->duration; - } - if( !entry ) - return -1; - } - /* Note: last_accessed_sample_number is always updated together with last_accessed_sample_dts, and vice versa. */ - timeline->last_accessed_sample_dts = *dts; - timeline->last_accessed_sample_number = sample_number; - return 0; -} - -static int isom_get_cts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts ) -{ - if( isom_get_dts_from_info_list( timeline, sample_number, cts ) ) - return -1; - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); - if( !info ) - return -1; - *cts = timeline->ctd_shift ? (*cts + (int32_t)info->offset) : (*cts + info->offset); - return 0; -} - -static int isom_get_dts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts ) -{ - isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); - if( !bunch ) - return -1; - *dts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration; - return 0; -} - -static int isom_get_cts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts ) -{ - isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); - if( !bunch ) - return -1; - *cts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration + bunch->offset; - return 0; -} - -static int isom_get_sample_duration_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration ) -{ - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); - if( !info ) - return -1; - *sample_duration = info->duration; - return 0; -} - -static int isom_get_sample_duration_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration ) -{ - isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); - if( !bunch ) - return -1; - *sample_duration = bunch->duration; - return 0; -} - -static int isom_check_sample_existence_in_info_list( isom_timeline_t *timeline, uint32_t sample_number ) -{ - return !!lsmash_get_entry_data( timeline->info_list, sample_number ); -} - -static int isom_check_sample_existence_in_bunch_list( isom_timeline_t *timeline, uint32_t sample_number ) -{ - return !!isom_get_bunch( timeline, sample_number ); -} - -static lsmash_sample_t *isom_read_sample_data_from_stream( lsmash_file_t *file, isom_timeline_t *timeline, isom_portable_chunk_t *chunk, - uint32_t sample_length, uint64_t sample_pos ) -{ - if( (timeline->last_accessed_chunk_number != chunk->number) - || (timeline->last_accessed_offset > sample_pos) - || (timeline->last_accessed_offset + timeline->last_read_size < sample_pos + sample_length) ) - { - /* Realloc if an update of max_read_size exceeds the current allocated data size. */ - if( file->max_read_size > timeline->last_accessed_chunk_alloc_size ) - { - void *temp = lsmash_realloc( timeline->last_accessed_chunk_data, file->max_read_size ); - if( temp ) - timeline->last_accessed_chunk_data = temp; - else - { - file->max_read_size = timeline->last_accessed_chunk_alloc_size; - lsmash_log( timeline, LSMASH_LOG_WARNING, "Memory re-allocation by the new max_read_size failed.\n" ); - } - } - /* Read data of a chunk in the stream. */ - uint64_t read_size; - uint64_t seek_pos; - if( file->max_read_size >= chunk->length ) - { - /* Read by a chunk size. */ - read_size = chunk->length; - seek_pos = chunk->data_offset; - } - else - { - /* Read by a sample size. */ - read_size = LSMASH_MAX( file->max_read_size, sample_length ); - seek_pos = sample_pos; - } - lsmash_bs_t *bs = file->bs; - lsmash_bs_seek( bs, seek_pos, SEEK_SET ); - lsmash_bs_empty( bs ); - if( lsmash_bs_read( bs, read_size ) < 0 ) - return NULL; - timeline->last_accessed_chunk_number = chunk->number; - timeline->last_accessed_offset = seek_pos; - timeline->last_read_size = LSMASH_MIN( read_size, bs->buffer.store ); - memcpy( timeline->last_accessed_chunk_data, bs->buffer.data, timeline->last_read_size ); - lsmash_bs_empty( bs ); - } - lsmash_sample_t *sample = lsmash_create_sample( 0 ); - if( !sample ) - return NULL; - uint64_t offset_from_seek = sample_pos - timeline->last_accessed_offset; - sample->data = lsmash_memdup( timeline->last_accessed_chunk_data + offset_from_seek, sample_length ); - if( !sample->data ) - { - lsmash_delete_sample( sample ); - return NULL; - } - return sample; -} - -static lsmash_sample_t *isom_get_lpcm_sample_from_media_timeline( lsmash_file_t *file, isom_timeline_t *timeline, uint32_t sample_number ) -{ - isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); - if( !bunch ) - return NULL; - /* Get data of a sample from the stream. */ - isom_portable_chunk_t *chunk = bunch->chunk; - uint32_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number; - uint64_t sample_pos = bunch->pos + sample_number_offset * bunch->length; - lsmash_sample_t *sample = isom_read_sample_data_from_stream( file, timeline, chunk, bunch->length, sample_pos ); - if( !sample ) - return NULL; - /* Get sample info. */ - sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration; - sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset); - sample->length = bunch->length; - sample->index = bunch->index; - sample->prop = bunch->prop; - return sample; -} - -static lsmash_sample_t *isom_get_sample_from_media_timeline( lsmash_file_t *file, isom_timeline_t *timeline, uint32_t sample_number ) -{ - uint64_t dts; - if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) ) - return NULL; - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); - if( !info ) - return NULL; - /* Get data of a sample from the stream. */ - isom_portable_chunk_t *chunk = info->chunk; - lsmash_sample_t *sample = isom_read_sample_data_from_stream( file, timeline, chunk, info->length, info->pos ); - if( !sample ) - return NULL; - /* Get sample info. */ - sample->dts = dts; - sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset); - sample->length = info->length; - sample->index = info->index; - sample->prop = info->prop; - return sample; -} - -static int isom_get_lpcm_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample ) -{ - isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number ); - if( !bunch ) - return -1; - uint32_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number; - sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration; - sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset); - sample->length = bunch->length; - sample->index = bunch->index; - sample->prop = bunch->prop; - return 0; -} - -static int isom_get_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample ) -{ - uint64_t dts; - if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) ) - return -1; - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); - if( !info ) - return -1; - sample->dts = dts; - sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset); - sample->length = info->length; - sample->index = info->index; - sample->prop = info->prop; - return 0; -} - -static int isom_get_lpcm_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop ) -{ - memset( prop, 0, sizeof(lsmash_sample_property_t) ); - prop->ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - return 0; -} - -static int isom_get_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop ) -{ - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number ); - if( !info ) - return -1; - *prop = info->prop; - return 0; -} - -static inline void isom_increment_sample_number_in_entry -( - uint32_t *sample_number_in_entry, - lsmash_entry_t **entry, - uint32_t sample_count -) -{ - if( *sample_number_in_entry == sample_count ) - { - *sample_number_in_entry = 1; - *entry = (*entry)->next; - } - else - *sample_number_in_entry += 1; -} - -static inline isom_sgpd_t *isom_select_appropriate_sgpd -( - isom_sgpd_t *sgpd, - isom_sgpd_t *sgpd_frag, - uint32_t *group_description_index -) -{ - if( sgpd_frag && *group_description_index >= 0x10000 ) - { - /* The specification doesn't define 0x10000 explicitly, however says that there must be fewer than - * 65536 group definitions for this track and grouping type in the sample table in the Movie Box. - * So, we assume 0x10000 is equivalent to 0. */ - *group_description_index -= 0x10000; - return sgpd_frag; - } - else - return sgpd; -} - -static int isom_get_roll_recovery_grouping_info -( - isom_timeline_t *timeline, - lsmash_entry_t **sbgp_roll_entry, - isom_sgpd_t *sgpd_roll, - isom_sgpd_t *sgpd_frag_roll, - uint32_t *sample_number_in_sbgp_roll_entry, - isom_sample_info_t *info, - uint32_t sample_number -) -{ - isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_roll_entry)->data; - if( !assignment ) - return -1; - if( assignment->group_description_index ) - { - uint32_t group_description_index = assignment->group_description_index; - isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_roll, sgpd_frag_roll, &group_description_index ); - isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index ); - if( roll_data ) - { - if( roll_data->roll_distance > 0 ) - { - /* post-roll */ - info->prop.post_roll.complete = sample_number + roll_data->roll_distance; - if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START; - } - else if( roll_data->roll_distance < 0 ) - { - /* pre-roll */ - info->prop.pre_roll.distance = -roll_data->roll_distance; - if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_PRE_ROLL_END; - } - } - else if( *sample_number_in_sbgp_roll_entry == 1 && group_description_index ) - lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of roll recoveries is not found in the Sample Group Description Box.\n" ); - } - isom_increment_sample_number_in_entry( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment->sample_count ); - return 0; -} - -static int isom_get_random_access_point_grouping_info -( - isom_timeline_t *timeline, - lsmash_entry_t **sbgp_rap_entry, - isom_sgpd_t *sgpd_rap, - isom_sgpd_t *sgpd_frag_rap, - uint32_t *sample_number_in_sbgp_rap_entry, - isom_sample_info_t *info, - uint32_t *distance -) -{ - isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_rap_entry)->data; - if( !assignment ) - return -1; - if( assignment->group_description_index && (info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) ) - { - uint32_t group_description_index = assignment->group_description_index; - isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_rap, sgpd_frag_rap, &group_description_index ); - isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index ); - if( rap_data ) - { - /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */ - info->prop.ra_flags |= (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples) - ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_OPEN_RAP - : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP; - *distance = 0; - } - else if( *sample_number_in_sbgp_rap_entry == 1 && group_description_index ) - lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of random access points is not found in the Sample Group Description Box.\n" ); - } - isom_increment_sample_number_in_entry( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment->sample_count ); - return 0; -} - -int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID ) -{ - if( !root ) - return -1; - lsmash_file_t *file = root->file; - if( !file - || !file->moov - || !file->moov->mvhd - || file->moov->mvhd->timescale == 0 ) - return -1; - /* Get track by track_ID. */ - isom_trak_t *trak = isom_get_trak( file, track_ID ); - if( !trak - || !trak->tkhd - || !trak->mdia - || !trak->mdia->mdhd - || trak->mdia->mdhd->timescale == 0 - || !trak->mdia->minf - || !trak->mdia->minf->stbl ) - return -1; - /* Create a timeline list if it doesn't exist. */ - if( !file->timeline ) - { - file->timeline = lsmash_create_entry_list(); - if( !file->timeline ) - return -1; - } - /* Create a timeline. */ - isom_timeline_t *timeline = isom_create_timeline(); - if( !timeline ) - return -1; - timeline->track_ID = track_ID; - timeline->movie_timescale = file->moov->mvhd->timescale; - timeline->media_timescale = trak->mdia->mdhd->timescale; - timeline->track_duration = trak->tkhd->duration; - /* Preparation for construction. */ - isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL; - isom_stbl_t *stbl = trak->mdia->minf->stbl; - isom_stsd_t *stsd = stbl->stsd; - isom_stts_t *stts = stbl->stts; - isom_ctts_t *ctts = stbl->ctts; - isom_stss_t *stss = stbl->stss; - isom_stps_t *stps = stbl->stps; - isom_sdtp_t *sdtp = stbl->sdtp; - isom_stsc_t *stsc = stbl->stsc; - isom_stsz_t *stsz = stbl->stsz; - isom_stco_t *stco = stbl->stco; - isom_sgpd_t *sgpd_roll = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL ); - isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); - isom_sbgp_t *sbgp_roll = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL ); - isom_sbgp_t *sbgp_rap = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP ); - lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL; - lsmash_entry_t *stsd_entry = stsd ? stsd->list. head : NULL; - lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL; - lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL; - 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 && stsz->list ? stsz->list->head : NULL; - lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL; - lsmash_entry_t *stco_entry = stco && stco->list ? stco->list->head : NULL; - lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL; - lsmash_entry_t *sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL; - lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL; - isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL; - isom_sample_entry_t *description = stsd_entry ? (isom_sample_entry_t *)stsd_entry->data : NULL; - int movie_framemts_present = (file->moov->mvex && file->moof_list.head); - if( !description ) - goto fail; - if( !movie_framemts_present && (!stts_entry || !stsc_entry || !stco_entry || !stco_entry->data || (next_stsc_entry && !next_stsc_entry->data)) ) - goto fail; - int all_sync = !stss; - int large_presentation = stco->large_presentation || lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_CO64 ); - int is_lpcm_audio = isom_is_lpcm_audio( description ); - int is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description ); - int iso_sdtp = file->max_isom_version >= 2 || file->avc_extensions; - int allow_negative_sample_offset = ctts && ((file->max_isom_version >= 4 && ctts->version == 1) || file->qt_compatible); - uint32_t sample_number_in_stts_entry = 1; - uint32_t sample_number_in_ctts_entry = 1; - uint32_t sample_number_in_sbgp_roll_entry = 1; - uint32_t sample_number_in_sbgp_rap_entry = 1; - uint64_t dts = 0; - uint32_t chunk_number = 1; - uint64_t offset_from_chunk = 0; - uint64_t data_offset = stco_entry && stco_entry->data - ? large_presentation - ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset - : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset - : 0; - uint32_t samples_per_packet; - uint32_t constant_sample_size; - if( is_qt_fixed_comp_audio ) - isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size ); - else - { - samples_per_packet = 1; - constant_sample_size = stsz->sample_size; - } - uint32_t sample_number = samples_per_packet; - uint32_t sample_number_in_chunk = samples_per_packet; - /* Copy edits. */ - while( elst_entry ) - { - isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) ); - if( !edit - || lsmash_add_entry( timeline->edit_list, edit ) ) - goto fail; - elst_entry = elst_entry->next; - } - /* Check what the first 2-bits of sample dependency means. - * This check is for chimera of ISO Base Media and QTFF. */ - if( iso_sdtp && sdtp_entry ) - { - while( sdtp_entry ) - { - isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data; - if( !sdtp_data ) - goto fail; - if( sdtp_data->is_leading > 1 ) - break; /* Apparently, it's defined under ISO Base Media. */ - if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) ) - { - /* Obviously, it's not defined under ISO Base Media. */ - iso_sdtp = 0; - break; - } - sdtp_entry = sdtp_entry->next; - } - sdtp_entry = sdtp->list->head; - } - /* Construct media timeline. */ - isom_portable_chunk_t chunk; - chunk.data_offset = data_offset; - chunk.length = 0; - chunk.number = chunk_number; - if( isom_add_portable_chunk_entry( timeline, &chunk ) ) - goto fail; - uint32_t distance = NO_RANDOM_ACCESS_POINT; - uint32_t last_duration = UINT32_MAX; - uint32_t packet_number = 1; - isom_lpcm_bunch_t bunch = { 0 }; - while( sample_number <= stsz->sample_count ) - { - isom_sample_info_t info = { 0 }; - /* Get sample duration and sample offset. */ - for( uint32_t i = 0; i < samples_per_packet; i++ ) - { - /* sample duration */ - if( stts_entry ) - { - isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data; - if( !stts_data ) - goto fail; - isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count ); - last_duration = stts_data->sample_delta; - } - info.duration += last_duration; - dts += last_duration; - /* sample offset */ - uint32_t sample_offset; - if( ctts_entry ) - { - isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data; - if( !ctts_data ) - goto fail; - isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count ); - sample_offset = ctts_data->sample_offset; - if( allow_negative_sample_offset ) - { - uint64_t cts = dts + (int32_t)sample_offset; - if( (cts + timeline->ctd_shift) < dts ) - timeline->ctd_shift = dts - cts; - } - } - else - sample_offset = 0; - if( i == 0 ) - info.offset = sample_offset; - } - timeline->media_duration += info.duration; - if( !is_qt_fixed_comp_audio ) - { - /* Check whether sync sample or not. */ - if( stss_entry ) - { - isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data; - if( !stss_data ) - goto fail; - if( sample_number == stss_data->sample_number ) - { - info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - stss_entry = stss_entry->next; - distance = 0; - } - } - else if( all_sync ) - /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation - * though all of them could be marked as a sync sample. */ - info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - /* Check whether partial sync sample or not. */ - if( stps_entry ) - { - isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data; - if( !stps_data ) - goto fail; - if( sample_number == stps_data->sample_number ) - { - info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP; - stps_entry = stps_entry->next; - distance = 0; - } - } - /* Get sample dependency info. */ - if( sdtp_entry ) - { - isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data; - if( !sdtp_data ) - goto fail; - if( iso_sdtp ) - info.prop.leading = sdtp_data->is_leading; - else - info.prop.allow_earlier = sdtp_data->is_leading; - info.prop.independent = sdtp_data->sample_depends_on; - info.prop.disposable = sdtp_data->sample_is_depended_on; - info.prop.redundant = sdtp_data->sample_has_redundancy; - sdtp_entry = sdtp_entry->next; - } - /* Get roll recovery grouping info. */ - if( sbgp_roll_entry - && isom_get_roll_recovery_grouping_info( timeline, - &sbgp_roll_entry, sgpd_roll, NULL, - &sample_number_in_sbgp_roll_entry, - &info, sample_number ) < 0 ) - goto fail; - info.prop.post_roll.identifier = sample_number; - /* Get random access point grouping info. */ - if( sbgp_rap_entry - && isom_get_random_access_point_grouping_info( timeline, - &sbgp_rap_entry, sgpd_rap, NULL, - &sample_number_in_sbgp_rap_entry, - &info, &distance ) < 0 ) - goto fail; - /* Set up distance from the previous random access point. */ - if( distance != NO_RANDOM_ACCESS_POINT ) - { - if( info.prop.pre_roll.distance == 0 ) - info.prop.pre_roll.distance = distance; - ++distance; - } - } - else - /* All uncompressed and non-variable compressed audio frame is a sync sample. */ - info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - /* Get size of sample in the stream. */ - if( is_qt_fixed_comp_audio || !stsz_entry ) - info.length = constant_sample_size; - else - { - if( !stsz_entry->data ) - goto fail; - info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size; - stsz_entry = stsz_entry->next; - } - timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length ); - /* Get chunk info. */ - info.pos = data_offset; - info.index = stsc_data->sample_description_index; - info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data; - offset_from_chunk += info.length; - if( sample_number_in_chunk == stsc_data->samples_per_chunk ) - { - /* Set the length of the last chunk. */ - if( info.chunk ) - info.chunk->length = offset_from_chunk; - /* Move the next chunk. */ - if( stco_entry ) - stco_entry = stco_entry->next; - if( stco_entry - && stco_entry->data ) - data_offset = large_presentation - ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset - : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset; - chunk.data_offset = data_offset; - chunk.length = 0; - chunk.number = ++chunk_number; - if( isom_add_portable_chunk_entry( timeline, &chunk ) ) - goto fail; - offset_from_chunk = 0; - /* Check if the next entry is broken. */ - while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk ) - { - /* Just skip broken next entry. */ - lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" ); - lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" ); - next_stsc_entry = next_stsc_entry->next; - if( next_stsc_entry - && !next_stsc_entry->data ) - goto fail; - } - /* Check if the next chunk belongs to the next sequence of chunks. */ - if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk ) - { - stsc_entry = next_stsc_entry; - next_stsc_entry = next_stsc_entry->next; - if( next_stsc_entry - && !next_stsc_entry->data ) - goto fail; - stsc_data = (isom_stsc_entry_t *)stsc_entry->data; - /* Update sample description. */ - description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data->sample_description_index ); - is_lpcm_audio = isom_is_lpcm_audio( description ); - is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description ); - if( is_qt_fixed_comp_audio ) - isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size ); - else - { - samples_per_packet = 1; - constant_sample_size = stsz->sample_size; - } - } - sample_number_in_chunk = samples_per_packet; - } - else - { - data_offset += info.length; - sample_number_in_chunk += samples_per_packet; - } - /* OK. Let's add its info. */ - if( is_lpcm_audio ) - { - if( sample_number == samples_per_packet ) - isom_update_bunch( &bunch, &info ); - else if( isom_compare_lpcm_sample_info( &bunch, &info ) ) - { - if( isom_add_lpcm_bunch_entry( timeline, &bunch ) ) - goto fail; - isom_update_bunch( &bunch, &info ); - } - else - ++ bunch.sample_count; - } - else if( isom_add_sample_info_entry( timeline, &info ) ) - goto fail; - if( timeline->info_list->entry_count && timeline->bunch_list->entry_count ) - { - lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" ); - goto fail; - } - sample_number += samples_per_packet; - packet_number += 1; - } - isom_portable_chunk_t *last_chunk = lsmash_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count ); - if( last_chunk ) - { - if( offset_from_chunk ) - last_chunk->length = offset_from_chunk; - else - { - /* Remove the last invalid chunk. */ - lsmash_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count, NULL ); - --chunk_number; - } - } - uint32_t sample_count = packet_number - 1; - if( movie_framemts_present ) - { - isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID ); - lsmash_entry_t *tfra_entry = tfra && tfra->list ? tfra->list->head : NULL; - isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL; - chunk.data_offset = 0; - chunk.length = 0; - /* Movie fragments */ - for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next ) - { - isom_moof_t *moof = (isom_moof_t *)moof_entry->data; - if( !moof ) - goto fail; - uint64_t last_sample_end_pos = 0; - /* Track fragments */ - uint32_t traf_number = 1; - for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next ) - { - isom_traf_t *traf = (isom_traf_t *)traf_entry->data; - if( !traf ) - goto fail; - isom_tfhd_t *tfhd = traf->tfhd; - if( !tfhd ) - goto fail; - isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID ); - if( !trex ) - goto fail; - /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */ - if( !traf->trun_list.head ) - { - ++traf_number; - continue; - } - /* Get base_data_offset. */ - uint64_t base_data_offset; - if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) - base_data_offset = tfhd->base_data_offset; - else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head ) - base_data_offset = moof->pos; - else - base_data_offset = last_sample_end_pos; - /* sample grouping */ - isom_sgpd_t *sgpd_frag_roll = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_ROLL ); - isom_sgpd_t *sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP ); - sbgp_roll = isom_get_fragment_sample_to_group( traf, ISOM_GROUP_TYPE_ROLL ); - sbgp_rap = isom_get_fragment_sample_to_group( traf, ISOM_GROUP_TYPE_RAP ); - sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL; - sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL; - int need_data_offset_only = (tfhd->track_ID != track_ID); - /* Track runs */ - uint32_t trun_number = 1; - for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next ) - { - isom_trun_t *trun = (isom_trun_t *)trun_entry->data; - if( !trun ) - goto fail; - if( trun->sample_count == 0 ) - { - ++trun_number; - continue; - } - /* Get data_offset. */ - if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) - data_offset = trun->data_offset + base_data_offset; - else if( trun_entry == traf->trun_list.head ) - data_offset = base_data_offset; - else - data_offset = last_sample_end_pos; - /* */ - uint32_t sample_description_index = 0; - isom_sdtp_entry_t *sdtp_data = NULL; - if( !need_data_offset_only ) - { - /* Each track run can be considered as a chunk. - * Here, we consider physically consecutive track runs as one chunk. */ - if( chunk.data_offset + chunk.length != data_offset ) - { - chunk.data_offset = data_offset; - chunk.length = 0; - chunk.number = ++chunk_number; - if( isom_add_portable_chunk_entry( timeline, &chunk ) ) - goto fail; - } - /* Get sample_description_index of this track fragment. */ - if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) - sample_description_index = tfhd->sample_description_index; - else - sample_description_index = trex->default_sample_description_index; - description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, sample_description_index ); - is_lpcm_audio = isom_is_lpcm_audio( description ); - /* Get dependency info for this track fragment. */ - sdtp_entry = traf->sdtp && traf->sdtp->list ? traf->sdtp->list->head : NULL; - sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL; - } - /* Get info of each sample. */ - lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL; - sample_number = 1; - while( sample_number <= trun->sample_count ) - { - isom_sample_info_t info = { 0 }; - isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL; - /* Get sample_size */ - if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) ) - info.length = row->sample_size; - else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) - info.length = tfhd->default_sample_size; - else - info.length = trex->default_sample_size; - if( !need_data_offset_only ) - { - info.pos = data_offset; - info.index = sample_description_index; - info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data; - info.chunk->length += info.length; - /* Get sample_duration. */ - if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) ) - info.duration = row->sample_duration; - else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) - info.duration = tfhd->default_sample_duration; - else - info.duration = trex->default_sample_duration; - /* Get composition time offset. */ - if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) ) - { - info.offset = row->sample_composition_time_offset; - /* Check composition to decode timeline shift. */ - if( file->max_isom_version >= 6 && trun->version != 0 ) - { - uint64_t cts = dts + (int32_t)info.offset; - if( (cts + timeline->ctd_shift) < dts ) - timeline->ctd_shift = dts - cts; - } - } - else - info.offset = 0; - dts += info.duration; - /* Update media duration and maximun sample size. */ - timeline->media_duration += info.duration; - timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length ); - if( !is_lpcm_audio ) - { - /* Get sample_flags. */ - isom_sample_flags_t sample_flags; - if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) ) - sample_flags = trun->first_sample_flags; - else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) ) - sample_flags = row->sample_flags; - else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) - sample_flags = tfhd->default_sample_flags; - else - sample_flags = trex->default_sample_flags; - if( sdtp_data ) - { - /* Independent and Disposable Samples Box overrides the information from sample_flags. - * There is no description in the specification about this, but the intention should be such a thing. - * The ground is that sample_flags is placed in media layer - * while Independent and Disposable Samples Box is placed in track or presentation layer. */ - info.prop.leading = sdtp_data->is_leading; - info.prop.independent = sdtp_data->sample_depends_on; - info.prop.disposable = sdtp_data->sample_is_depended_on; - info.prop.redundant = sdtp_data->sample_has_redundancy; - if( sdtp_entry ) - sdtp_entry = sdtp_entry->next; - sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL; - } - else - { - info.prop.leading = sample_flags.is_leading; - info.prop.independent = sample_flags.sample_depends_on; - info.prop.disposable = sample_flags.sample_is_depended_on; - info.prop.redundant = sample_flags.sample_has_redundancy; - } - /* Check this sample is a sync sample or not. - * Note: all sync sample shall be independent. */ - if( !sample_flags.sample_is_non_sync_sample - && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT ) - { - info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - distance = 0; - } - /* Get roll recovery grouping info. */ - uint32_t roll_id = sample_count + sample_number; - if( sbgp_roll_entry - && isom_get_roll_recovery_grouping_info( timeline, - &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll, - &sample_number_in_sbgp_roll_entry, - &info, roll_id ) < 0 ) - goto fail; - info.prop.post_roll.identifier = roll_id; - /* Get random access point grouping info. */ - if( sbgp_rap_entry - && isom_get_random_access_point_grouping_info( timeline, - &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap, - &sample_number_in_sbgp_rap_entry, - &info, &distance ) < 0 ) - goto fail; - /* Get the location of the sync sample from 'tfra' if it is not set up yet. - * Note: there is no guarantee that its entries are placed in a specific order. */ - if( tfra ) - { - if( tfra->number_of_entry == 0 - && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - if( rap - && rap->moof_offset == moof->pos - && rap->traf_number == traf_number - && rap->trun_number == trun_number - && rap->sample_number == sample_number ) - { - if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - if( tfra_entry ) - tfra_entry = tfra_entry->next; - rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL; - } - } - /* Set up distance from the previous random access point. */ - if( distance != NO_RANDOM_ACCESS_POINT ) - { - if( info.prop.pre_roll.distance == 0 ) - info.prop.pre_roll.distance = distance; - ++distance; - } - /* OK. Let's add its info. */ - if( isom_add_sample_info_entry( timeline, &info ) ) - goto fail; - } - else - { - /* All LPCMFrame is a sync sample. */ - info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - /* OK. Let's add its info. */ - if( sample_count == 0 && sample_number == 1 ) - isom_update_bunch( &bunch, &info ); - else if( isom_compare_lpcm_sample_info( &bunch, &info ) ) - { - if( isom_add_lpcm_bunch_entry( timeline, &bunch ) ) - goto fail; - isom_update_bunch( &bunch, &info ); - } - else - ++ bunch.sample_count; - } - if( timeline-> info_list->entry_count - && timeline->bunch_list->entry_count ) - { - lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" ); - goto fail; - } - } - data_offset += info.length; - last_sample_end_pos = data_offset; - if( row_entry ) - row_entry = row_entry->next; - ++sample_number; - } - if( !need_data_offset_only ) - sample_count += sample_number - 1; - ++trun_number; - } /* Track runs */ - ++traf_number; - } /* Track fragments */ - } /* Movie fragments */ - } - else if( timeline->chunk_list->entry_count == 0 ) - goto fail; /* No samples in this track. */ - if( bunch.sample_count && isom_add_lpcm_bunch_entry( timeline, &bunch ) ) - goto fail; - if( lsmash_add_entry( file->timeline, timeline ) ) - goto fail; - timeline->last_accessed_chunk_alloc_size = LSMASH_MAX( file->max_read_size, timeline->max_sample_size ); - timeline->last_accessed_chunk_data = lsmash_malloc_zero( timeline->last_accessed_chunk_alloc_size ); - if( !timeline->last_accessed_chunk_data ) - goto fail; - /* Finish timeline construction. */ - timeline->sample_count = sample_count; - if( timeline->info_list->entry_count ) - { - timeline->get_dts = isom_get_dts_from_info_list; - timeline->get_cts = isom_get_cts_from_info_list; - timeline->get_sample_duration = isom_get_sample_duration_from_info_list; - timeline->check_sample_existence = isom_check_sample_existence_in_info_list; - timeline->get_sample = isom_get_sample_from_media_timeline; - timeline->get_sample_info = isom_get_sample_info_from_media_timeline; - timeline->get_sample_property = isom_get_sample_property_from_media_timeline; - } - else - { - timeline->get_dts = isom_get_dts_from_bunch_list; - timeline->get_cts = isom_get_cts_from_bunch_list; - timeline->get_sample_duration = isom_get_sample_duration_from_bunch_list; - timeline->check_sample_existence = isom_check_sample_existence_in_bunch_list; - timeline->get_sample = isom_get_lpcm_sample_from_media_timeline; - timeline->get_sample_info = isom_get_lpcm_sample_info_from_media_timeline; - timeline->get_sample_property = isom_get_lpcm_sample_property_from_media_timeline; - } - return 0; -fail: - isom_destruct_timeline_direct( timeline ); - return -1; -} - -int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts ) -{ - if( !sample_number || !dts ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline || sample_number > timeline->sample_count ) - return -1; - return timeline->get_dts( timeline, sample_number, dts ); -} - -int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts ) -{ - if( !sample_number || !cts ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline || sample_number > timeline->sample_count ) - return -1; - return timeline->get_cts( timeline, sample_number, cts ); -} - -lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number ) -{ - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - return timeline ? timeline->get_sample( root->file, timeline, sample_number ) : NULL; -} - -int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample ) -{ - if( !sample ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1; -} - -int lsmash_get_sample_property_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_property_t *prop ) -{ - if( !prop ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1; -} - -int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift ) -{ - if( !ctd_shift ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline ) - return -1; - *ctd_shift = timeline->ctd_shift; - return 0; -} - -static inline int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number ) -{ - lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number-- ); - if( !entry - || !entry->data ) - return -1; - isom_sample_info_t *info = (isom_sample_info_t *)entry->data; - while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - { - entry = entry->prev; - if( !entry - || !entry->data ) - return -1; - info = (isom_sample_info_t *)entry->data; - --sample_number; - } - *rap_number = sample_number + 1; - return 0; -} - -static inline int isom_get_closest_future_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number ) -{ - lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number++ ); - if( !entry - || !entry->data ) - return -1; - isom_sample_info_t *info = (isom_sample_info_t *)entry->data; - while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) - { - entry = entry->next; - if( !entry - || !entry->data ) - return -1; - info = (isom_sample_info_t *)entry->data; - ++sample_number; - } - *rap_number = sample_number - 1; - return 0; -} - -static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number ) -{ - if( !timeline ) - return -1; - if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number ) - && isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number ) ) - return -1; - return 0; -} - -int lsmash_get_closest_random_accessible_point_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *rap_number ) -{ - if( sample_number == 0 || !rap_number ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( timeline->info_list->entry_count == 0 ) - { - *rap_number = sample_number; /* All LPCM is sync sample. */ - return 0; - } - return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number ); -} - -int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, - uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance ) -{ - if( sample_number == 0 ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( timeline->info_list->entry_count == 0 ) - { - /* All LPCM is sync sample. */ - *rap_number = sample_number; - if( ra_flags ) - *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC; - if( leading ) - *leading = 0; - if( distance ) - *distance = 0; - return 0; - } - if( isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number ) ) - return -1; - isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, *rap_number ); - if( !info ) - return -1; - if( ra_flags ) - *ra_flags = info->prop.ra_flags; - if( leading ) - *leading = 0; - if( distance ) - *distance = 0; - if( sample_number < *rap_number ) - /* Impossible to desire to decode the sample of given number correctly. */ - return 0; - else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) ) - { - if( leading ) - { - /* Count leading samples. */ - uint32_t current_sample_number = *rap_number + 1; - uint64_t dts; - if( isom_get_dts_from_info_list( timeline, *rap_number, &dts ) ) - return -1; - uint64_t rap_cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset); - do - { - dts += info->duration; - if( rap_cts <= dts ) - break; /* leading samples of this random accessible point must not be present more. */ - info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, current_sample_number++ ); - if( !info ) - break; - uint64_t cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset); - if( rap_cts > cts ) - ++ *leading; - } while( 1 ); - } - if( !distance || sample_number == *rap_number ) - return 0; - /* Measure distance from the first closest non-recovery random accessible point to the second. */ - uint32_t prev_rap_number = *rap_number; - do - { - if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) ) - /* The previous random accessible point is not present. */ - return 0; - info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number ); - if( !info ) - return -1; - if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) ) - { - /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */ - *distance = *rap_number - prev_rap_number; - return 0; - } - } while( 1 ); - } - if( !distance ) - return 0; - /* Calculate roll-distance. */ - if( info->prop.pre_roll.distance ) - { - /* Pre-roll recovery */ - uint32_t prev_rap_number = *rap_number; - do - { - if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) - && *rap_number < info->prop.pre_roll.distance ) - { - /* The previous random accessible point is not present. - * And sample of given number might be not able to decoded correctly. */ - *distance = 0; - return 0; - } - if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number ) - { - /* - * |<---- pre-roll distance ---->| - * |<--------- distance -------->| - * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * ^ ^ ^ ^ - * random accessible point starting point random accessible point given sample - * (complete) - */ - *distance = info->prop.pre_roll.distance; - return 0; - } - else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) ) - { - /* - * |<------------ pre-roll distance ------------------>| - * |<------ distance ------->| - * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * ^ ^ ^ ^ - * random accessible point random accessible point given sample - * (starting point) (complete) - */ - *distance = *rap_number - prev_rap_number; - return 0; - } - } while( 1 ); - } - /* Post-roll recovery */ - if( sample_number >= info->prop.post_roll.complete ) - /* - * |<----- post-roll distance ----->| - * (distance = 0) - * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * ^ ^ ^ - * random accessible point complete given sample - * (starting point) - */ - return 0; - uint32_t prev_rap_number = *rap_number; - do - { - if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) ) - /* The previous random accessible point is not present. */ - return 0; - info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number ); - if( !info ) - return -1; - if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete ) - { - *distance = *rap_number - prev_rap_number; - return 0; - } - } while( 1 ); -} - -int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number ) -{ - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0; -} - -int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta ) -{ - if( !last_sample_delta ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1; -} - -int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta ) -{ - if( !sample_delta ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1; -} - -uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID ) -{ - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline ) - return 0; - return timeline->sample_count; -} - -uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID ) -{ - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline ) - return 0; - return timeline->max_sample_size; -} - -uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID ) -{ - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline ) - return 0; - return timeline->media_duration; -} - -int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID ) -{ - if( !dst || !src ) - return -1; - lsmash_file_t *dst_file = dst->file; - isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID ); - if( !dst_file->moov - || !dst_file->moov->mvhd - || dst_file->moov->mvhd->timescale == 0 - || !dst_trak - || !dst_trak->mdia - || !dst_trak->mdia->mdhd - || dst_trak->mdia->mdhd->timescale == 0 - || !dst_trak->mdia->minf - || !dst_trak->mdia->minf->stbl ) - return -1; - if( dst_trak->edts - && dst_trak->edts->elst ) - lsmash_remove_entries( dst_trak->edts->elst->list, NULL ); - uint32_t src_movie_timescale; - uint32_t src_media_timescale; - uint64_t src_track_duration; - uint64_t src_media_duration; - int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time. - * Therefore, call this function as later as possible. */ - lsmash_entry_t *src_entry; - lsmash_file_t *src_file = src->file; - isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID ); - if( !src_trak - || !src_trak->edts - || !src_trak->edts->elst - || !src_trak->edts->elst->list ) - { - /* Get from timeline instead of boxes. */ - isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID ); - if( !src_timeline - || src_timeline->movie_timescale == 0 - || src_timeline->media_timescale == 0 - || !src_timeline->edit_list ) - return -1; - src_movie_timescale = src_timeline->movie_timescale; - src_media_timescale = src_timeline->media_timescale; - src_track_duration = src_timeline->track_duration; - src_media_duration = src_timeline->media_duration; - src_ctd_shift = src_timeline->ctd_shift; - src_entry = src_timeline->edit_list->head; - } - else - { - if( !src_file->moov - || !src_file->moov->mvhd - || src_file->moov->mvhd->timescale == 0 - || !src_trak->tkhd - || !src_trak->mdia - || !src_trak->mdia->mdhd - || src_trak->mdia->mdhd->timescale == 0 - || !src_trak->mdia->minf - || !src_trak->mdia->minf->stbl ) - return -1; - src_movie_timescale = src_file->moov->mvhd->timescale; - src_media_timescale = src_trak->mdia->mdhd->timescale; - src_track_duration = src_trak->tkhd->duration; - src_media_duration = src_trak->mdia->mdhd->duration; - src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0; - src_entry = src_trak->edts->elst->list->head; - } - if( !src_entry ) - return 0; - /* Generate edit list if absent in destination. */ - if( (!dst_trak->edts && isom_add_edts( dst_trak )) - || (!dst_trak->edts->elst && isom_add_elst( dst_trak->edts )) ) - return -1; - uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale; - uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale; - int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0; - int32_t media_time_shift = src_ctd_shift - dst_ctd_shift; - lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list; - lsmash_entry_t *src_head = src_entry; - while( src_entry ) - { - isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data; - if( !src_data ) - return -1; - uint64_t segment_duration; - if( src_data->segment_duration == 0 && !dst_file->fragment ) - { - /* The 0-duration edit makes no sence for non-fragmented movie file. */ - if( src_entry == src_head ) - /* Set an appropriate duration from the source track. */ - segment_duration = src_track_duration - ? src_track_duration - : src_media_duration * ((double)src_movie_timescale / src_media_timescale); - else - /* Two or more 0-duration edits make no sence. Just skip them. */ - continue; - } - else - segment_duration = src_data->segment_duration; - isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) ); - if( !dst_data ) - return -1; - dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5; - dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5; - dst_data->media_rate = src_data->media_rate; - if( lsmash_add_entry( dst_list, dst_data ) ) - { - lsmash_free( dst_data ); - return -1; - } - src_entry = src_entry->next; - } - return 0; -} - -int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list ) -{ - if( !root || !root->file || !ts_list ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline ) - return -1; - if( timeline->info_list->entry_count == 0 ) - { - lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" ); - return -1; - } - if( ts_list->sample_count != timeline->info_list->entry_count ) - return -1; /* Number of samples must be same. */ - lsmash_media_ts_t *ts = ts_list->timestamp; - if( ts[0].dts ) - return -1; /* DTS must start from value zero. */ - /* Update DTSs. */ - uint32_t sample_count = ts_list->sample_count; - uint32_t i; - if( timeline->info_list->entry_count > 1 ) - { - i = 1; - lsmash_entry_t *entry = timeline->info_list->head; - isom_sample_info_t *info; - while( i < sample_count ) - { - info = (isom_sample_info_t *)entry->data; - if( !info || (ts[i].dts < ts[i - 1].dts) ) - return -1; - info->duration = ts[i].dts - ts[i - 1].dts; - entry = entry->next; - ++i; - } - if( i > 1 ) - { - if( !entry - || !entry->data ) - return -1; - /* Copy the previous duration. */ - ((isom_sample_info_t *)entry->data)->duration = info->duration; - } - else - return -1; /* Irregular case: sample_count this timeline has is incorrect. */ - } - else /* still image */ - ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX; - /* Update CTSs. - * ToDo: hint track must not have any sample_offset. */ - i = 0; - timeline->ctd_shift = 0; - for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next ) - { - isom_sample_info_t *info = (isom_sample_info_t *)entry->data; - if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts ) - timeline->ctd_shift = ts[i].dts - ts[i].cts; - info->offset = ts[i].cts - ts[i].dts; - ++i; - } - if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) ) - return -1; /* Don't allow composition to decode timeline shift. */ - return 0; -} - -int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list ) -{ - if( !ts_list ) - return -1; - isom_timeline_t *timeline = isom_get_timeline( root, track_ID ); - if( !timeline ) - return -1; - uint32_t sample_count = timeline->info_list->entry_count; - if( !sample_count ) - { - ts_list->sample_count = 0; - ts_list->timestamp = NULL; - return 0; - } - lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) ); - if( !ts ) - return -1; - uint64_t dts = 0; - uint32_t i = 0; - if( timeline->info_list->entry_count ) - for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next ) - { - isom_sample_info_t *info = (isom_sample_info_t *)entry->data; - if( !info ) - { - lsmash_free( ts ); - return -1; - } - ts[i].dts = dts; - ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset); - dts += info->duration; - ++i; - } - else - for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next ) - { - isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data; - if( !bunch ) - { - lsmash_free( ts ); - return -1; - } - for( uint32_t j = 0; j < bunch->sample_count; j++ ) - { - ts[i].dts = dts; - ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)bunch->offset) : (dts + bunch->offset); - dts += bunch->duration; - ++i; - } - } - ts_list->sample_count = sample_count; - ts_list->timestamp = ts; - return 0; -} - -void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list ) -{ - if( !ts_list ) - return; - if( ts_list->timestamp ) - { - lsmash_free( ts_list->timestamp ); - ts_list->timestamp = NULL; - } - ts_list->sample_count = 0; -} - -static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b ) -{ - int64_t diff = (int64_t)(a->dts - b->dts); - return diff > 0 ? 1 : (diff == 0 ? 0 : -1); -} - -void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list ) -{ - if( !ts_list ) - return; - qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts ); -} - -static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b ) -{ - int64_t diff = (int64_t)(a->cts - b->cts); - return diff > 0 ? 1 : (diff == 0 ? 0 : -1); -} - -void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list ) -{ - if( !ts_list ) - return; - qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts ); -} - -int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay ) -{ - if( !ts_list || !max_sample_delay ) - return -1; - lsmash_media_ts_t *orig_ts = ts_list->timestamp; - lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) ); - if( !ts ) - return -1; - ts_list->timestamp = ts; - *max_sample_delay = 0; - for( uint32_t i = 0; i < ts_list->sample_count; i++ ) - { - ts[i].cts = orig_ts[i].cts; /* for sorting */ - ts[i].dts = i; - } - lsmash_sort_timestamps_composition_order( ts_list ); - for( uint32_t i = 0; i < ts_list->sample_count; i++ ) - if( i < ts[i].dts ) - { - uint32_t sample_delay = ts[i].dts - i; - *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay ); - } - lsmash_free( ts ); - ts_list->timestamp = orig_ts; - return 0; -} - -#endif /* LSMASH_DEMUXER_ENABLED */ diff -Nru l-smash-1.9.1/timelineeditor.c l-smash-2.3.0/timelineeditor.c --- l-smash-1.9.1/timelineeditor.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/timelineeditor.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1219 +0,0 @@ -/***************************************************************************** - * timelineeditor.c: - ***************************************************************************** - * Copyright (C) 2011-2014 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" - -#include -#include -#include -#include -#include -#include - -#include "lsmash.h" -#include "cli.h" - -#include "config.h" - -#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) - -#define eprintf( ... ) fprintf( stderr, __VA_ARGS__ ) -#define REFRESH_CONSOLE eprintf( " \r" ) - -typedef struct -{ - int active; - lsmash_summary_t *summary; -} summary_t; - -typedef struct -{ - int active; - uint32_t track_ID; - uint32_t last_sample_delta; - uint32_t current_sample_number; - int reach_end_of_media_timeline; - uint32_t *summary_remap; - uint32_t num_summaries; - summary_t *summaries; - lsmash_track_parameters_t track_param; - lsmash_media_parameters_t media_param; -} track_t; - -typedef struct -{ - lsmash_itunes_metadata_t *itunes_metadata; - track_t *track; - lsmash_movie_parameters_t param; - uint32_t num_tracks; - uint32_t num_itunes_metadata; - uint32_t current_track_number; -} movie_t; - -typedef struct -{ - lsmash_file_t *fh; - lsmash_file_parameters_t param; - movie_t movie; -} file_t; - -typedef struct -{ - lsmash_root_t *root; - file_t file; -} root_t; - -typedef struct -{ - FILE *file; - uint64_t *ts; - uint32_t sample_count; - int auto_media_timescale; - int auto_media_timebase; - uint64_t media_timescale; - uint64_t media_timebase; - uint64_t duration; - uint64_t composition_delay; - uint64_t empty_delay; -} timecode_t; - -typedef struct -{ - root_t *output; - root_t *input; - timecode_t *timecode; -} movie_io_t; - -typedef struct -{ - uint32_t track_number; - uint32_t media_timescale; - uint32_t media_timebase; - uint32_t skip_duration; - uint32_t empty_delay; - int dts_compression; -} opt_t; - -static void cleanup_root( root_t *h ) -{ - if( !h ) - return; - movie_t *movie = &h->file.movie; - if( movie->itunes_metadata ) - { - for( uint32_t i = 0; i < movie->num_itunes_metadata; i++ ) - { - lsmash_itunes_metadata_t *metadata = &movie->itunes_metadata[i]; - if( metadata->type == ITUNES_METADATA_TYPE_STRING ) - { - if( metadata->value.string ) - lsmash_free( metadata->value.string ); - } - else if( metadata->type == ITUNES_METADATA_TYPE_BINARY ) - if( metadata->value.binary.data ) - lsmash_free( metadata->value.binary.data ); - if( metadata->meaning ) - lsmash_free( metadata->meaning ); - if( metadata->name ) - lsmash_free( metadata->name ); - } - lsmash_freep( &movie->itunes_metadata ); - } - if( movie->track ) - lsmash_freep( &movie->track ); - lsmash_close_file( &h->file.param ); - lsmash_destroy_root( h->root ); - h->root = NULL; -} - -static void cleanup_timecode( timecode_t *timecode ) -{ - if( !timecode ) - return; - if( timecode->file ) - { - fclose( timecode->file ); - timecode->file = NULL; - } - if( timecode->ts ) - lsmash_freep( &timecode->ts ); -} - -static int error_message( const char* message, ... ) -{ - REFRESH_CONSOLE; - eprintf( "Error: " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -static int warning_message( const char* message, ... ) -{ - REFRESH_CONSOLE; - eprintf( "Warning: " ); - va_list args; - va_start( args, message ); - vfprintf( stderr, message, args ); - va_end( args ); - return -1; -} - -static int timelineeditor_error( movie_io_t *io, const char *message, ... ) -{ - cleanup_root( io->input ); - cleanup_root( io->output ); - cleanup_timecode( io->timecode ); - va_list args; - va_start( args, message ); - error_message( message, args ); - va_end( args ); - return -1; -} - -#define TIMELINEEDITOR_ERR( ... ) timelineeditor_error( &io, __VA_ARGS__ ) -#define ERROR_MSG( ... ) error_message( __VA_ARGS__ ) -#define WARNING_MSG( ... ) warning_message( __VA_ARGS__ ) - -static char *duplicate_string( char *src ) -{ - if( !src ) - return NULL; - int dst_size = strlen( src ) + 1; - char *dst = lsmash_malloc( dst_size ); - if( !dst ) - return NULL; - memcpy( dst, src, dst_size ); - return dst; -} - -static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata ) -{ - memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) ); - if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) ) - return -1; - lsmash_itunes_metadata_t shadow = *metadata; - metadata->meaning = NULL; - metadata->name = NULL; - memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) ); - if( shadow.meaning ) - { - metadata->meaning = duplicate_string( shadow.meaning ); - if( !metadata->meaning ) - return -1; - } - if( shadow.name ) - { - metadata->name = duplicate_string( shadow.name ); - if( !metadata->name ) - goto fail; - } - if( shadow.type == ITUNES_METADATA_TYPE_STRING ) - { - metadata->value.string = duplicate_string( shadow.value.string ); - if( !metadata->value.string ) - goto fail; - } - else if( shadow.type == ITUNES_METADATA_TYPE_BINARY ) - { - metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size ); - if( !metadata->value.binary.data ) - goto fail; - memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size ); - } - return 0; -fail: - if( metadata->meaning ) - lsmash_free( metadata->meaning ); - if( metadata->name ) - lsmash_free( metadata->name ); - return -1; -} - -static int get_summaries( root_t *input, track_t *track ) -{ - track->num_summaries = lsmash_count_summary( input->root, track->track_ID ); - if( track->num_summaries == 0 ) - return ERROR_MSG( "Failed to get find valid summaries.\n" ); - track->summaries = lsmash_malloc( track->num_summaries * sizeof(summary_t) ); - if( !track->summaries ) - return ERROR_MSG( "failed to alloc input summaries.\n" ); - memset( track->summaries, 0, track->num_summaries * sizeof(summary_t) ); - for( uint32_t j = 0; j < track->num_summaries; j++ ) - { - lsmash_summary_t *summary = lsmash_get_summary( input->root, track->track_ID, j + 1 ); - if( !summary ) - { - WARNING_MSG( "failed to get a summary.\n" ); - continue; - } - track->summaries[j].summary = summary; - track->summaries[j].active = 1; - } - return 0; -} - -static int get_movie( root_t *input, char *input_name ) -{ - if( !strcmp( input_name, "-" ) ) - return ERROR_MSG( "Standard input not supported.\n" ); - input->root = lsmash_create_root(); - if( !input->root ) - return ERROR_MSG( "failed to create a ROOT for an input file.\n" ); - file_t *in_file = &input->file; - if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 ) - return ERROR_MSG( "failed to open an input file.\n" ); - in_file->fh = lsmash_set_file( input->root, &in_file->param ); - if( !in_file->fh ) - return ERROR_MSG( "failed to add an input file into a ROOT.\n" ); - if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 ) - return ERROR_MSG( "failed to read an input file\n" ); - movie_t *movie = &in_file->movie; - movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root ); - if( movie->num_itunes_metadata ) - { - movie->itunes_metadata = lsmash_malloc( movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) ); - if( !movie->itunes_metadata ) - return ERROR_MSG( "failed to alloc iTunes metadata.\n" ); - uint32_t itunes_metadata_count = 0; - for( uint32_t i = 1; i <= movie->num_itunes_metadata; i++ ) - { - if( get_itunes_metadata( input->root, i, &movie->itunes_metadata[itunes_metadata_count] ) ) - { - WARNING_MSG( "failed to get an iTunes metadata.\n" ); - continue; - } - ++itunes_metadata_count; - } - movie->num_itunes_metadata = itunes_metadata_count; - } - lsmash_initialize_movie_parameters( &movie->param ); - lsmash_get_movie_parameters( input->root, &movie->param ); - movie->num_tracks = movie->param.number_of_tracks; - movie->current_track_number = 1; - /* Create tracks. */ - track_t *track = movie->track = lsmash_malloc( movie->num_tracks * sizeof(track_t) ); - if( !track ) - return ERROR_MSG( "Failed to alloc input tracks.\n" ); - memset( track, 0, movie->num_tracks * sizeof(track_t) ); - for( uint32_t i = 0; i < movie->num_tracks; i++ ) - { - track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 ); - if( !track[i].track_ID ) - return ERROR_MSG( "Failed to get track_ID.\n" ); - } - for( uint32_t i = 0; i < movie->num_tracks; i++ ) - { - lsmash_initialize_track_parameters( &track[i].track_param ); - if( lsmash_get_track_parameters( input->root, track[i].track_ID, &track[i].track_param ) ) - { - WARNING_MSG( "failed to get track parameters.\n" ); - continue; - } - lsmash_initialize_media_parameters( &track[i].media_param ); - if( lsmash_get_media_parameters( input->root, track[i].track_ID, &track[i].media_param ) ) - { - WARNING_MSG( "failed to get media parameters.\n" ); - continue; - } - if( lsmash_construct_timeline( input->root, track[i].track_ID ) ) - { - WARNING_MSG( "failed to construct timeline.\n" ); - continue; - } - if( lsmash_get_last_sample_delta_from_media_timeline( input->root, track[i].track_ID, &track[i].last_sample_delta ) ) - { - WARNING_MSG( "failed to get the last sample delta.\n" ); - continue; - } - if( get_summaries( input, &track[i] ) ) - { - WARNING_MSG( "failed to get valid summaries.\n" ); - continue; - } - track[i].active = 1; - track[i].current_sample_number = 1; - } - lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) ); - return 0; -} - -static inline uint64_t get_gcd( uint64_t a, uint64_t b ) -{ - if( !b ) - return a; - while( 1 ) - { - uint64_t c = a % b; - if( !c ) - return b; - a = b; - b = c; - } -} - -static inline uint64_t get_lcm( uint64_t a, uint64_t b ) -{ - if( !a ) - return 0; - return (a / get_gcd( a, b )) * b; -} - -static uint64_t get_media_timebase( lsmash_media_ts_list_t *ts_list ) -{ - uint64_t timebase = ts_list->timestamp[0].cts; - for( uint32_t i = 1; i < ts_list->sample_count; i++ ) - timebase = get_gcd( timebase, ts_list->timestamp[i].cts ); - for( uint32_t i = 0; i < ts_list->sample_count; i++ ) - timebase = get_gcd( timebase, ts_list->timestamp[i].dts ); - return timebase; -} - -static inline double sigexp10( double value, double *exponent ) -{ - /* This function separates significand and exp10 from double floating point. */ - *exponent = 1; - while( value < 1 ) - { - value *= 10; - *exponent /= 10; - } - while( value >= 10 ) - { - value /= 10; - *exponent *= 10; - } - return value; -} - -#define DOUBLE_EPSILON 5e-6 -#define MATROSKA_TIMESCALE 1000000000 -#define SKIP_LINE_CHARACTER( x ) ((x) == '#' || (x) == '\n' || (x) == '\r') - -static double correct_fps( double fps, timecode_t *timecode ) -{ - int i = 1; - uint64_t fps_num, fps_den; - double exponent; - double fps_sig = sigexp10( fps, &exponent ); - while( 1 ) - { - fps_den = i * timecode->media_timebase; - fps_num = round( fps_den * fps_sig ) * exponent; - if( fps_num > UINT32_MAX ) - return ERROR_MSG( "framerate correction failed.\n" - "Specify an appropriate timebase manually or remake timecode file.\n" ); - if( fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON ) - break; - ++i; - } - if( timecode->auto_media_timescale ) - { - timecode->media_timescale = timecode->media_timescale - ? get_lcm( timecode->media_timescale, fps_num ) - : fps_num; - if( timecode->media_timescale > UINT32_MAX ) - timecode->auto_media_timescale = 0; - } - return (double)fps_num / fps_den; -} - -static int try_matroska_timescale( double *fps_array, timecode_t *timecode, uint32_t num_loops ) -{ - timecode->media_timebase = 0; - timecode->media_timescale = MATROSKA_TIMESCALE; - for( uint32_t i = 0; i < num_loops; i++ ) - { - uint64_t fps_den; - double exponent; - double fps_sig = sigexp10( fps_array[i], &exponent ); - fps_den = round( MATROSKA_TIMESCALE / fps_sig ) / exponent; - timecode->media_timebase = fps_den && timecode->media_timebase - ? get_gcd( timecode->media_timebase, fps_den ) - : fps_den; - if( timecode->media_timebase > UINT32_MAX || !timecode->media_timebase ) - return ERROR_MSG( "Automatic media timescale generation failed.\n" - "Specify media timescale manually.\n" ); - } - return 0; -} - -static int parse_timecode( timecode_t *timecode, uint32_t sample_count ) -{ - int tcfv; - int ret = fscanf( timecode->file, "# timecode format v%d", &tcfv ); - if( ret != 1 || (tcfv != 1 && tcfv != 2) ) - return ERROR_MSG( "Unsupported timecode format\n" ); - char buff[256]; - double *timecode_array = NULL; - if( tcfv == 1 ) - { - double assume_fps = 0; - /* Get assumed framerate. */ - while( fgets( buff, sizeof(buff), timecode->file ) ) - { - if( SKIP_LINE_CHARACTER( buff[0] ) ) - continue; - if( sscanf( buff, "assume %lf", &assume_fps ) != 1 - && sscanf( buff, "Assume %lf", &assume_fps ) != 1 ) - return ERROR_MSG( "Assumed fps not found\n" ); - break; - } - if( assume_fps <= 0 ) - return ERROR_MSG( "Invalid assumed fps\n" ); - uint64_t file_pos = ftell( timecode->file ); - /* Check whether valid or not and count number of sequences. */ - uint32_t num_sequences = 0; - int64_t start, end; - int64_t prev_start = -1, prev_end = -1; - double sequence_fps; - while( fgets( buff, sizeof(buff), timecode->file ) ) - { - if( SKIP_LINE_CHARACTER( buff[0] ) ) - continue; - ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps ); - if( ret != 3 && ret != EOF ) - return ERROR_MSG( "Invalid input timecode file\n" ); - if( start > end || start <= prev_start || end <= prev_end || sequence_fps <= 0 ) - return ERROR_MSG( "Invalid input timecode file\n" ); - prev_start = start; - prev_end = end; - if( timecode->auto_media_timescale || timecode->auto_media_timebase ) - ++num_sequences; - } - fseek( timecode->file, file_pos, SEEK_SET ); - /* Preparation storing timecodes. */ - double fps_array[ (timecode->auto_media_timescale || timecode->auto_media_timebase) * num_sequences + 1 ]; - double corrected_assume_fps = correct_fps( assume_fps, timecode ); - if( corrected_assume_fps < 0 ) - return ERROR_MSG( "Failed to correct the assumed framerate\n" ); - timecode_array = lsmash_malloc( sample_count * sizeof(double) ); - if( !timecode_array ) - return ERROR_MSG( "Failed to alloc timecodes\n" ); - timecode_array[0] = 0; - num_sequences = 0; - uint32_t i = 0; - while( i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ) ) - { - if( SKIP_LINE_CHARACTER( buff[0] ) ) - continue; - ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps ); - if( ret != 3 ) - start = end = sample_count - 1; - for( ; i < start && i < sample_count - 1; i++ ) - timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; - if( i < sample_count - 1 ) - { - if( timecode->auto_media_timescale || timecode->auto_media_timebase ) - fps_array[num_sequences++] = sequence_fps; - sequence_fps = correct_fps( sequence_fps, timecode ); - if( sequence_fps < 0 ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Failed to correct the framerate of a sequence.\n" ); - } - for( i = start; i <= end && i < sample_count - 1; i++ ) - timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps; - } - } - for( ; i < sample_count - 1; i++ ) - timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; - if( timecode->auto_media_timescale || timecode->auto_media_timebase ) - fps_array[num_sequences] = assume_fps; - /* Assume matroska timebase if automatic timescale generation isn't done yet. */ - if( timecode->auto_media_timebase && !timecode->auto_media_timescale ) - { - double exponent; - double assume_fps_sig, sequence_fps_sig; - if( try_matroska_timescale( fps_array, timecode, num_sequences + 1 ) < 0 ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Failed to try matroska timescale.\n" ); - } - fseek( timecode->file, file_pos, SEEK_SET ); - assume_fps_sig = sigexp10( assume_fps, &exponent ); - corrected_assume_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / assume_fps_sig ) / exponent ); - for( i = 0; i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ); ) - { - if( SKIP_LINE_CHARACTER( buff[0] ) ) - continue; - ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps ); - if( ret != 3 ) - start = end = sample_count - 1; - sequence_fps_sig = sigexp10( sequence_fps, &exponent ); - sequence_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / sequence_fps_sig ) / exponent ); - for( ; i < start && i < sample_count - 1; i++ ) - timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; - for( i = start; i <= end && i < sample_count - 1; i++ ) - timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps; - } - for( ; i < sample_count - 1; i++ ) - timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps; - } - } - else /* tcfv == 2 */ - { - uint32_t num_timecodes = 0; - uint64_t file_pos = ftell( timecode->file ); - while( fgets( buff, sizeof(buff), timecode->file ) ) - { - if( SKIP_LINE_CHARACTER( buff[0] ) ) - { - if( !num_timecodes ) - file_pos = ftell( timecode->file ); - continue; - } - ++num_timecodes; - } - if( !num_timecodes ) - return ERROR_MSG( "No timecodes!\n" ); - if( sample_count > num_timecodes ) - return ERROR_MSG( "Lack number of timecodes.\n" ); - fseek( timecode->file, file_pos, SEEK_SET ); - timecode_array = lsmash_malloc( sample_count * sizeof(uint64_t) ); - if( !timecode_array ) - return ERROR_MSG( "Failed to alloc timecodes.\n" ); - uint32_t i = 0; - if( fgets( buff, sizeof(buff), timecode->file ) ) - { - ret = sscanf( buff, "%lf", &timecode_array[0] ); - if( ret != 1 ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Invalid timecode number: 0\n" ); - } - timecode_array[i++] *= 1e-3; /* Timescale of timecode format v2 is 1000. */ - while( i < sample_count && fgets( buff, sizeof(buff), timecode->file ) ) - { - if( SKIP_LINE_CHARACTER( buff[0] ) ) - continue; - ret = sscanf( buff, "%lf", &timecode_array[i] ); - timecode_array[i] *= 1e-3; /* Timescale of timecode format v2 is 1000. */ - if( ret != 1 || timecode_array[i] <= timecode_array[i - 1] ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Invalid input timecode.\n" ); - } - ++i; - } - } - if( i < sample_count ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Failed to get timecodes.\n" ); - } - /* Generate media timescale automatically if needed. */ - if( sample_count != 1 && timecode->auto_media_timescale ) - { - double fps_array[sample_count - 1]; - for( i = 0; i < sample_count - 1; i++ ) - { - fps_array[i] = 1 / (timecode_array[i + 1] - timecode_array[i]); - if( timecode->auto_media_timescale ) - { - int j = 1; - uint64_t fps_num, fps_den; - double exponent; - double fps_sig = sigexp10( fps_array[i], &exponent ); - while( 1 ) - { - fps_den = j * timecode->media_timebase; - fps_num = round( fps_den * fps_sig ) * exponent; - if( fps_num > UINT32_MAX - || fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON ) - break; - ++j; - } - timecode->media_timescale = fps_num && timecode->media_timescale - ? get_lcm( timecode->media_timescale, fps_num ) - : fps_num; - if( timecode->media_timescale > UINT32_MAX ) - { - timecode->auto_media_timescale = 0; - continue; /* Don't break because all framerate is needed for try_matroska_timescale. */ - } - } - } - if( timecode->auto_media_timebase && !timecode->auto_media_timescale - && try_matroska_timescale( fps_array, timecode, sample_count - 1 ) < 0 ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Failed to try matroska timescale.\n" ); - } - } - } - if( timecode->auto_media_timescale || timecode->auto_media_timebase ) - { - uint64_t reduce = get_gcd( timecode->media_timebase, timecode->media_timescale ); - timecode->media_timebase /= reduce; - timecode->media_timescale /= reduce; - } - else if( timecode->media_timescale > UINT32_MAX || !timecode->media_timescale ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Failed to generate media timescale automatically.\n" - "Specify an appropriate media timescale manually.\n" ); - } - uint32_t timescale = timecode->media_timescale; - uint32_t timebase = timecode->media_timebase; - double delay_tc = timecode_array[0]; - timecode->empty_delay = ((uint64_t)(delay_tc * ((double)timescale / timebase) + 0.5)) * timebase; - timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) ); - if( !timecode->ts ) - { - lsmash_free( timecode_array ); - return ERROR_MSG( "Failed to allocate timestamps.\n" ); - } - timecode->ts[0] = 0; - for( uint32_t i = 1; i < sample_count; i++ ) - { - timecode->ts[i] = ((uint64_t)((timecode_array[i] - delay_tc) * ((double)timescale / timebase) + 0.5)) * timebase; - if( timecode->ts[i] <= timecode->ts[i - 1] ) - { - lsmash_free( timecode_array ); - lsmash_free( timecode->ts ); - timecode->ts = NULL; - return ERROR_MSG( "Invalid timecode.\n" ); - } - } - lsmash_free( timecode_array ); - return 0; -} - -#undef DOUBLE_EPSILON -#undef MATROSKA_TIMESCALE -#undef SKIP_LINE_CHARACTER - -static int edit_media_timeline( root_t *input, timecode_t *timecode, opt_t *opt ) -{ - if( !timecode->file && !opt->media_timescale && !opt->media_timebase && !opt->dts_compression ) - return 0; - track_t *in_track = &input->file.movie.track[opt->track_number - 1]; - uint32_t track_ID = in_track->track_ID; - lsmash_media_ts_list_t ts_list; - if( lsmash_get_media_timestamps( input->root, track_ID, &ts_list ) ) - return ERROR_MSG( "Failed to get media timestamps.\n" ); - uint64_t timebase = get_media_timebase( &ts_list ); - if( !timebase ) - return ERROR_MSG( "Failed to get media timebase.\n" ); - lsmash_media_ts_t *timestamp = ts_list.timestamp; - uint32_t sample_count = ts_list.sample_count; - uint32_t orig_timebase = timebase; - uint32_t timescale; - double timebase_convert_multiplier; - if( opt->media_timescale || opt->media_timebase ) - { - uint32_t orig_timescale = in_track->media_param.timescale; - timescale = opt->media_timescale ? opt->media_timescale : orig_timescale; - timebase = opt->media_timebase ? opt->media_timebase : orig_timebase; - if( !opt->media_timescale && opt->media_timebase && (timebase > orig_timebase) ) - timescale = timescale * ((double)timebase / orig_timebase) + 0.5; - timebase_convert_multiplier = ((double)timescale / orig_timescale) * ((double)orig_timebase / timebase); - } - else - { - /* Reduce timescale and timebase. */ - timescale = in_track->media_param.timescale; - uint64_t reduce = get_gcd( timescale, timebase ); - timescale /= reduce; - timebase /= reduce; - timebase_convert_multiplier = 1; - } - /* Parse timecode file. */ - if( timecode->file ) - { - timecode->auto_media_timescale = !opt->media_timescale; - timecode->auto_media_timebase = !opt->media_timebase; - timecode->media_timescale = timecode->auto_media_timescale ? 0 : timescale; - timecode->media_timebase = timebase; - if( parse_timecode( timecode, sample_count ) ) - return ERROR_MSG( "Failed to parse timecode file.\n" ); - timescale = timecode->media_timescale; - timebase = timecode->media_timebase; - } - /* Get maximum composition sample delay for DTS generation. */ - uint32_t sample_delay; - if( lsmash_get_max_sample_delay( &ts_list, &sample_delay ) ) - return ERROR_MSG( "Failed to get maximum composition sample delay.\n" ); - if( sample_delay ) /* Reorder composition order. */ - lsmash_sort_timestamps_composition_order( &ts_list ); - if( !timecode->file ) - { - /* Genarate timestamps timescale converted. */ - timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) ); - if( !timecode->ts ) - return ERROR_MSG( "Failed to alloc timestamps\n" ); - for( uint32_t i = 0; i < sample_count; i++ ) - { - timecode->ts[i] = (timestamp[i].cts - timestamp[0].cts) / orig_timebase; - timecode->ts[i] = ((uint64_t)(timecode->ts[i] * timebase_convert_multiplier + 0.5)) * timebase; - if( i && (timecode->ts[i] <= timecode->ts[i - 1]) ) - return ERROR_MSG( "Invalid timescale conversion.\n" ); - } - } - if( sample_delay ) - { - /* If media timescale is specified, disable DTS compression multiplier. */ - uint32_t dts_compression_multiplier = opt->dts_compression * !opt->media_timescale * sample_delay + 1; - uint64_t initial_delta = timecode->ts[1]; - timescale *= dts_compression_multiplier; - if( dts_compression_multiplier > 1 ) - for( uint32_t i = 0; i < sample_count; i++ ) - timecode->ts[i] *= dts_compression_multiplier; - /* Generate CTS. */ - uint64_t sample_delay_time = timecode->composition_delay = opt->dts_compression ? 0 : timecode->ts[sample_delay]; - for( uint32_t i = 0; i < sample_count; i++ ) - timestamp[i].cts = timecode->ts[i] + sample_delay_time; - /* Reorder decode order and generate new DTS from CTS. */ - lsmash_sort_timestamps_decoding_order( &ts_list ); - uint64_t prev_reordered_cts[sample_delay]; - for( uint32_t i = 0; i <= sample_delay; i++ ) - { - if( !opt->dts_compression ) - timestamp[i].dts = timecode->ts[i]; - else - { - timestamp[i].dts = (i * initial_delta) / (!!opt->media_timescale * sample_delay + 1); - if( i && (timestamp[i].dts <= timestamp[i - 1].dts) ) - return ERROR_MSG( "Failed to do DTS compression.\n" ); - } - prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time; - } - for( uint32_t i = sample_delay + 1; i < sample_count; i++ ) - { - timestamp[i].dts = prev_reordered_cts[ (i - sample_delay) % sample_delay ]; - prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time; - } - } - else - for( uint32_t i = 0; i < sample_count; i++ ) - timestamp[i].cts = timestamp[i].dts = timecode->ts[i]; - if( sample_count > 1 ) - { - in_track->last_sample_delta = timecode->ts[sample_count - 1] - timecode->ts[sample_count - 2]; - timecode->duration = timecode->ts[sample_count - 1] + in_track->last_sample_delta; - } - else /* still image */ - timecode->duration = in_track->last_sample_delta = UINT32_MAX; - in_track->media_param.timescale = timescale; - if( lsmash_set_media_timestamps( input->root, track_ID, &ts_list ) ) - return ERROR_MSG( "Failed to set media timestamps.\n" ); - lsmash_delete_media_timestamps( &ts_list ); - return 0; -} - -static int check_white_brand( lsmash_brand_type brand ) -{ - static const lsmash_brand_type brand_white_list[] = - { - ISOM_BRAND_TYPE_3G2A, - ISOM_BRAND_TYPE_3GG6, - ISOM_BRAND_TYPE_3GG9, - ISOM_BRAND_TYPE_3GP4, - ISOM_BRAND_TYPE_3GP5, - ISOM_BRAND_TYPE_3GP6, - ISOM_BRAND_TYPE_3GP7, - ISOM_BRAND_TYPE_3GP8, - ISOM_BRAND_TYPE_3GP9, - ISOM_BRAND_TYPE_3GR6, - ISOM_BRAND_TYPE_3GR9, - ISOM_BRAND_TYPE_M4A , - ISOM_BRAND_TYPE_M4B , - ISOM_BRAND_TYPE_M4V , - ISOM_BRAND_TYPE_AVC1, - ISOM_BRAND_TYPE_DBY1, - ISOM_BRAND_TYPE_ISO2, - ISOM_BRAND_TYPE_ISO3, - ISOM_BRAND_TYPE_ISO4, - ISOM_BRAND_TYPE_ISO5, - ISOM_BRAND_TYPE_ISO6, - ISOM_BRAND_TYPE_ISOM, - ISOM_BRAND_TYPE_MP41, - ISOM_BRAND_TYPE_MP42, - ISOM_BRAND_TYPE_QT , - 0 - }; - for( int i = 0; brand_white_list[i]; i++ ) - if( brand == brand_white_list[i] ) - return 1; - return 0; -} - -static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size ) -{ - eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 ); - return 0; -} - -static void display_version( void ) -{ - eprintf( "\n" - "L-SMASH isom/mov timeline editor rev%s %s\n" - "Built on %s %s\n" - "Copyright (C) 2011-2014 L-SMASH project\n", - LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ ); -} - -static void display_help( void ) -{ - display_version(); - eprintf( "\n" - "Usage: timelineeditor [options] input output\n" - " options:\n" - " --help Display help\n" - " --version Display version information\n" - " --track Specify track number to edit [1]\n" - " --timecode Specify timecode file to edit timeline\n" - " --media-timescale Specify media timescale to convert\n" - " --media-timebase Specify media timebase to convert\n" - " --skip Skip start of media presentation in milliseconds\n" - " --delay Insert blank clip before actual media presentation in milliseconds\n" - " --dts-compression Eliminate composition delay with DTS hack\n" - " Multiply media timescale and timebase automatically\n" ); -} - -int main( int argc, char *argv[] ) -{ - if ( argc < 2 ) - { - display_help(); - return -1; - } - else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) - { - display_help(); - return 0; - } - else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) - { - display_version(); - return 0; - } - else if( argc < 3 ) - { - display_help(); - return -1; - } - root_t output = { 0 }; - root_t input = { 0 }; - timecode_t timecode = { 0 }; - movie_io_t io = { &output, &input, &timecode }; - opt_t opt = { 1, 0, 0, 0, 0, 0 }; - /* Parse options. */ - lsmash_get_mainargs( &argc, &argv ); - int argn = 1; - while( argn < argc - 2 ) - { - if( !strcasecmp( argv[argn], "--track" ) ) - { - opt.track_number = atoi( argv[++argn] ); - if( !opt.track_number ) - return TIMELINEEDITOR_ERR( "Invalid track number.\n" ); - ++argn; - } - else if( !strcasecmp( argv[argn], "--timecode" ) ) - { - timecode.file = lsmash_fopen( argv[++argn], "rb" ); - if( !timecode.file ) - return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" ); - ++argn; - } - else if( !strcasecmp( argv[argn], "--media-timescale" ) ) - { - opt.media_timescale = atoi( argv[++argn] ); - if( !opt.media_timescale ) - return TIMELINEEDITOR_ERR( "Invalid media timescale.\n" ); - ++argn; - } - else if( !strcasecmp( argv[argn], "--media-timebase" ) ) - { - opt.media_timebase = atoi( argv[++argn] ); - if( !opt.media_timebase ) - return TIMELINEEDITOR_ERR( "Invalid media timebase.\n" ); - ++argn; - } - else if( !strcasecmp( argv[argn], "--skip" ) ) - { - opt.skip_duration = atoi( argv[++argn] ); - if( !opt.skip_duration ) - return TIMELINEEDITOR_ERR( "Invalid skip duration.\n" ); - ++argn; - } - else if( !strcasecmp( argv[argn], "--delay" ) ) - { - opt.empty_delay = atoi( argv[++argn] ); - if( !opt.empty_delay ) - return TIMELINEEDITOR_ERR( "Invalid delay time.\n" ); - ++argn; - } - else if( !strcasecmp( argv[argn], "--dts-compression" ) ) - { - opt.dts_compression = 1; - ++argn; - } - else - return TIMELINEEDITOR_ERR( "Invalid option.\n" ); - } - if( argn > argc - 2 ) - return TIMELINEEDITOR_ERR( "Invalid arguments.\n" ); - /* Get input movies. */ - if( get_movie( &input, argv[argn++] ) ) - return TIMELINEEDITOR_ERR( "Failed to get input movie.\n" ); - movie_t *in_movie = &input.file.movie; - if( opt.track_number && (opt.track_number > in_movie->num_tracks) ) - return TIMELINEEDITOR_ERR( "Invalid track number.\n" ); - /* Create output movie. */ - file_t *out_file = &output.file; - output.root = lsmash_create_root(); - if( !output.root ) - return TIMELINEEDITOR_ERR( "failed to create a ROOT for an output file.\n" ); - if( lsmash_open_file( argv[argn], 0, &out_file->param ) < 0 ) - return TIMELINEEDITOR_ERR( "failed to open an output file.\n" ); - file_t *in_file = &input.file; - out_file->param.major_brand = in_file->param.major_brand; - out_file->param.minor_version = in_file->param.minor_version; - out_file->param.brands = in_file->param.brands; - out_file->param.brand_count = in_file->param.brand_count; - out_file->param.max_chunk_duration = 0.5; - out_file->param.max_async_tolerance = 2.0; - out_file->param.max_chunk_size = 4*1024*1024; - if( !check_white_brand( out_file->param.major_brand ) ) - { - /* Replace with whitelisted brand 'mp42'. */ - out_file->param.major_brand = ISOM_BRAND_TYPE_MP42; - out_file->param.minor_version = 0; - uint32_t i; - for( i = 0; i < out_file->param.brand_count; i++ ) - if( out_file->param.brands[i] == ISOM_BRAND_TYPE_MP42 ) - break; - if( i == out_file->param.brand_count ) - { - /* Add 'mp42' into the list of compatible brands. */ - out_file->param.brands = lsmash_malloc( (i + 1) * sizeof(lsmash_brand_type) ); - if( out_file->param.brands ) - { - memcpy( out_file->param.brands, in_file->param.brands, i * sizeof(lsmash_brand_type) ); - out_file->param.brands[i] = ISOM_BRAND_TYPE_MP42; - } - } - } - out_file->fh = lsmash_set_file( output.root, &out_file->param ); - if( !out_file->fh ) - return TIMELINEEDITOR_ERR( "failed to add an output file into a ROOT.\n" ); - if( out_file->param.brands != in_file->param.brands ) - lsmash_freep( &out_file->param.brands ); - /* Set movie parameters. */ - movie_t *out_movie = &out_file->movie; - out_movie->param = in_movie->param; /* Copy movie parameters. */ - if( in_movie->num_tracks == 1 ) - out_movie->param.timescale = in_movie->track[0].media_param.timescale; - if( lsmash_set_movie_parameters( output.root, &out_movie->param ) ) - return TIMELINEEDITOR_ERR( "Failed to set output movie parameters.\n" ); - /* Set iTunes metadata. */ - for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ ) - if( lsmash_set_itunes_metadata( output.root, in_movie->itunes_metadata[i] ) ) - { - WARNING_MSG( "failed to set an iTunes metadata.\n" ); - continue; - } - /* Create tracks of the output movie. */ - out_movie->track = lsmash_malloc( in_movie->num_tracks * sizeof(track_t) ); - if( !out_movie->track ) - return TIMELINEEDITOR_ERR( "Failed to alloc output tracks.\n" ); - /* Edit timeline. */ - if( edit_media_timeline( &input, &timecode, &opt ) ) - return TIMELINEEDITOR_ERR( "Failed to edit timeline.\n" ); - out_movie->num_tracks = in_movie->num_tracks; - out_movie->current_track_number = 1; - for( uint32_t i = 0; i < in_movie->num_tracks; i++ ) - { - track_t *in_track = &in_movie->track[i]; - if( !in_track->active ) - { - -- out_movie->num_tracks; - continue; - } - track_t *out_track = &out_movie->track[i]; - out_track->summary_remap = lsmash_malloc( in_track->num_summaries * sizeof(uint32_t) ); - if( !out_track->summary_remap ) - return TIMELINEEDITOR_ERR( "failed to create summary mapping for a track.\n" ); - memset( out_track->summary_remap, 0, in_track->num_summaries * sizeof(uint32_t) ); - out_track->track_ID = lsmash_create_track( output.root, in_track->media_param.handler_type ); - if( !out_track->track_ID ) - return TIMELINEEDITOR_ERR( "Failed to create a track.\n" ); - /* Copy track and media parameters except for track_ID. */ - out_track->track_param = in_track->track_param; - out_track->media_param = in_track->media_param; - out_track->track_param.track_ID = out_track->track_ID; - if( lsmash_set_track_parameters( output.root, out_track->track_ID, &out_track->track_param ) ) - return TIMELINEEDITOR_ERR( "Failed to set track parameters.\n" ); - if( lsmash_set_media_parameters( output.root, out_track->track_ID, &out_track->media_param ) ) - return TIMELINEEDITOR_ERR( "Failed to set media parameters.\n" ); - uint32_t valid_summary_count = 0; - for( uint32_t k = 0; k < in_track->num_summaries; k++ ) - { - if( !in_track->summaries[k].active ) - { - out_track->summary_remap[k] = 0; - continue; - } - lsmash_summary_t *summary = in_track->summaries[k].summary; - if( lsmash_add_sample_entry( output.root, out_track->track_ID, summary ) == 0 ) - { - WARNING_MSG( "failed to append a summary.\n" ); - lsmash_cleanup_summary( summary ); - in_track->summaries[k].summary = NULL; - in_track->summaries[k].active = 0; - out_track->summary_remap[k] = 0; - continue; - } - out_track->summary_remap[k] = ++valid_summary_count; - } - if( valid_summary_count == 0 ) - return TIMELINEEDITOR_ERR( "failed to append all summaries.\n" ); - out_track->last_sample_delta = in_track->last_sample_delta; - out_track->current_sample_number = 1; - out_track->reach_end_of_media_timeline = 0; - } - /* Start muxing. */ - double largest_dts = 0; - uint32_t num_consecutive_sample_skip = 0; - uint32_t num_active_input_tracks = out_movie->num_tracks; - uint64_t total_media_size = 0; - uint8_t sample_count = 0; - while( 1 ) - { - track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ]; - /* Try append a sample in an input track where we didn't reach the end of media timeline. */ - if( !in_track->reach_end_of_media_timeline ) - { - track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ]; - uint32_t in_track_ID = in_track->track_ID; - uint32_t out_track_ID = out_track->track_ID; - uint32_t input_media_timescale = in_track->media_param.timescale; - /* Get a DTS from a track in an input movie. */ - uint64_t dts; - if( lsmash_get_dts_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number, &dts ) ) - { - if( lsmash_check_sample_existence_in_media_timeline( input.root, in_track_ID, in_track->current_sample_number ) ) - return TIMELINEEDITOR_ERR( "Failed to get the DTS.\n" ); - else - { - in_track->reach_end_of_media_timeline = 1; - if( --num_active_input_tracks == 0 ) - break; /* end of muxing */ - } - } - /* Get and append a sample if it's good time. */ - else if( ((double)dts / input_media_timescale) <= largest_dts - || num_consecutive_sample_skip == num_active_input_tracks ) - { - /* Get an actual sample data from a track in an input movie. */ - lsmash_sample_t *sample = lsmash_get_sample_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number ); - if( !sample ) - return TIMELINEEDITOR_ERR( "Failed to get sample.\n" ); - sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries - : sample->index == 0 ? 1 - : sample->index; - sample->index = out_track->summary_remap[ sample->index - 1 ]; - if( sample->index ) - { - /* Append sample into output movie. */ - uint64_t sample_size = sample->length; /* sample will be deleted internally after appending. */ - if( lsmash_append_sample( output.root, out_track_ID, sample ) ) - { - lsmash_delete_sample( sample ); - return TIMELINEEDITOR_ERR( "Failed to append a sample.\n" ); - } - largest_dts = LSMASH_MAX( largest_dts, (double)dts / input_media_timescale ); - total_media_size += sample_size; - ++ in_track->current_sample_number; - num_consecutive_sample_skip = 0; - /* Print, per 256 samples, total size of imported media. */ - if( ++sample_count == 0 ) - eprintf( "Importing: %"PRIu64" bytes\r", total_media_size ); - } - } - else - ++num_consecutive_sample_skip; /* Skip appendig sample. */ - } - /* Move the next track. */ - if( ++ in_movie->current_track_number > in_movie->num_tracks ) - in_movie->current_track_number = 1; /* Back the first track. */ - if( ++ out_movie->current_track_number > out_movie->num_tracks ) - out_movie->current_track_number = 1; /* Back the first track in the output movie. */ - } - for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) - if( lsmash_flush_pooled_samples( output.root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) ) - return TIMELINEEDITOR_ERR( "Failed to flush samples.\n" ); - /* Copy timeline maps. */ - for( uint32_t i = 0; i < out_movie->num_tracks; i++ ) - if( lsmash_copy_timeline_map( output.root, out_movie->track[i].track_ID, input.root, in_movie->track[i].track_ID ) ) - return TIMELINEEDITOR_ERR( "Failed to copy a timeline map.\n" ); - /* Edit timeline map. */ - if( argc > 3 ) - { - track_t *out_track = &out_movie->track[ opt.track_number - 1 ]; - uint32_t track_ID = out_track->track_ID; - uint32_t movie_timescale = lsmash_get_movie_timescale( output.root ); - uint32_t media_timescale = lsmash_get_media_timescale( output.root, track_ID ); - uint64_t empty_delay = timecode.empty_delay + (uint64_t)(opt.empty_delay * (1e-3 * media_timescale) + 0.5); - uint64_t duration = timecode.duration + empty_delay; - if( lsmash_delete_explicit_timeline_map( output.root, track_ID ) ) - return TIMELINEEDITOR_ERR( "Failed to delete explicit timeline maps.\n" ); - if( timecode.empty_delay ) - { - lsmash_edit_t empty_edit; - empty_edit.duration = ((double)timecode.empty_delay / media_timescale) * movie_timescale; - empty_edit.start_time = ISOM_EDIT_MODE_EMPTY; - empty_edit.rate = ISOM_EDIT_MODE_NORMAL; - if( lsmash_create_explicit_timeline_map( output.root, track_ID, empty_edit ) ) - return TIMELINEEDITOR_ERR( "Failed to create a empty duration.\n" ); - duration = ((double)duration / media_timescale) * movie_timescale; - duration -= empty_edit.duration; - } - else - duration = ((double)duration / media_timescale) * movie_timescale; - lsmash_edit_t edit; - edit.duration = duration; - edit.start_time = timecode.composition_delay + (uint64_t)(opt.skip_duration * (1e-3 * media_timescale) + 0.5); - edit.rate = ISOM_EDIT_MODE_NORMAL; - if( lsmash_create_explicit_timeline_map( output.root, track_ID, edit ) ) - return TIMELINEEDITOR_ERR( "Failed to create a explicit timeline map.\n" ); - } - /* Finish muxing. */ - lsmash_adhoc_remux_t moov_to_front; - moov_to_front.func = moov_to_front_callback; - moov_to_front.buffer_size = 4*1024*1024; - moov_to_front.param = NULL; - eprintf( " \r" ); - if( lsmash_finish_movie( output.root, &moov_to_front ) - || lsmash_write_lsmash_indicator( output.root ) ) - return TIMELINEEDITOR_ERR( "Failed to finish output movie.\n" ); - cleanup_root( io.input ); - cleanup_root( io.output ); - cleanup_timecode( io.timecode ); - eprintf( "Timeline editing completed! \n" ); - return 0; -} diff -Nru l-smash-1.9.1/timeline.h l-smash-2.3.0/timeline.h --- l-smash-1.9.1/timeline.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/timeline.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/***************************************************************************** - * timeline.h: - ***************************************************************************** - * Copyright (C) 2011-2014 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_TIMELINE_H -#define LSMASH_TIMELINE_H - -void isom_remove_timelines( lsmash_file_t *file ); - -#endif /* LSMASH_TIMELINE_H */ diff -Nru l-smash-1.9.1/.travis.yml l-smash-2.3.0/.travis.yml --- l-smash-1.9.1/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ l-smash-2.3.0/.travis.yml 2014-11-06 14:12:27.000000000 +0000 @@ -0,0 +1,11 @@ +language: c +compiler: gcc +script: + - "./configure --disable-static --prefix=$PWD/tmp" + - "make && make install" + - "make distclean && ./configure --prefix=$PWD/tmp" + - "make lib && make install-lib" + - "LD_LIBRARY_PATH=$PWD/tmp/lib $PWD/tmp/bin/boxdumper --help" + - "LD_LIBRARY_PATH=$PWD/tmp/lib $PWD/tmp/bin/muxer --help" + - "LD_LIBRARY_PATH=$PWD/tmp/lib $PWD/tmp/bin/remuxer --help" + - "LD_LIBRARY_PATH=$PWD/tmp/lib $PWD/tmp/bin/timelineeditor --help" diff -Nru l-smash-1.9.1/utils.c l-smash-2.3.0/utils.c --- l-smash-1.9.1/utils.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/utils.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1226 +0,0 @@ -/***************************************************************************** - * utils.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 -#include -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#include "utils.h" - -uint64_t lsmash_bs_get_pos( lsmash_bs_t *bs ) -{ - return bs->buffer.pos; -} - -void lsmash_bs_empty( lsmash_bs_t *bs ) -{ - if( !bs ) - return; - memset( bs->buffer.data, 0, bs->buffer.alloc ); - bs->buffer.store = 0; - bs->buffer.pos = 0; -} - -void lsmash_bs_free( lsmash_bs_t *bs ) -{ - if( bs->buffer.data ) - lsmash_free( bs->buffer.data ); - bs->buffer.data = NULL; - bs->buffer.alloc = 0; - bs->buffer.store = 0; - bs->buffer.pos = 0; -} - -void lsmash_bs_alloc( lsmash_bs_t *bs, uint64_t size ) -{ - if( (bs->buffer.alloc >= size) || bs->error ) - return; - uint64_t alloc = size + (1<<16); - uint8_t *data; - if( !bs->buffer.data ) - data = lsmash_malloc( alloc ); - else - data = lsmash_realloc( bs->buffer.data, alloc ); - if( !data ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return; - } - bs->buffer.data = data; - bs->buffer.alloc = alloc; -} - -int64_t lsmash_bs_seek( lsmash_bs_t *bs, int64_t offset, int whence ) -{ - if( bs->unseekable ) - return -1; - int64_t ret = bs->seek( bs->stream, offset, whence ); - if( ret < 0 ) - return ret; - if( whence == SEEK_SET ) - { - assert( offset >= 0 ); - if( bs->written < offset ) - bs->offset = bs->written; - else - bs->offset = offset; - } - else if( whence == SEEK_CUR ) - { - if( offset < 0 && bs->offset < -offset ) - bs->offset = 0; - else if( offset > 0 && bs->written < bs->offset + offset ) - bs->offset = bs->written; - else - bs->offset += offset; - } - else if( whence == SEEK_END ) - { - assert( offset <= 0 ); - if( bs->written < -offset ) - bs->offset = 0; - else - bs->offset = bs->written + offset; - } - return ret; -} - -/*---- bitstream writer ----*/ -void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value ) -{ - lsmash_bs_alloc( bs, bs->buffer.store + 1 ); - if( bs->error ) - return; - bs->buffer.data[ bs->buffer.store ++ ] = value; -} - -void lsmash_bs_put_bytes( lsmash_bs_t *bs, uint32_t size, void *value ) -{ - if( !size || !value ) - return; - lsmash_bs_alloc( bs, bs->buffer.store + size ); - if( bs->error ) - return; - memcpy( bs->buffer.data + bs->buffer.store, value, size ); - bs->buffer.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) ); -} - -void lsmash_bs_put_le16( lsmash_bs_t *bs, uint16_t value ) -{ - lsmash_bs_put_byte( bs, (uint8_t)(value&0xff) ); - lsmash_bs_put_byte( bs, (uint8_t)((value>>8)&0xff) ); -} - -void lsmash_bs_put_le32( lsmash_bs_t *bs, uint32_t value ) -{ - lsmash_bs_put_le16( bs, (uint16_t)(value&0xffff) ); - lsmash_bs_put_le16( bs, (uint16_t)((value>>16)&0xffff) ); -} - -int lsmash_bs_flush_buffer( lsmash_bs_t *bs ) -{ - if( !bs ) - return -1; - if( bs->buffer.store == 0 - || bs->buffer.data == NULL ) - return 0; - if( bs->error || !bs->stream - || bs->write( bs->stream, bs->buffer.data, bs->buffer.store ) != bs->buffer.store ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return -1; - } - bs->written += bs->buffer.store; - bs->offset += bs->buffer.store; - bs->buffer.store = 0; - return 0; -} - -size_t lsmash_bs_write_data( lsmash_bs_t *bs, uint8_t *buf, size_t size ) -{ - if( !bs || size > INT_MAX ) - return -1; - if( !buf || size == 0 ) - return 0; - if( bs->error || !bs->stream ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return -1; - } - int write_size = bs->write( bs->stream, buf, size ); - bs->written += write_size; - bs->offset += write_size; - return write_size != size ? -1 : 0; -} - -lsmash_bs_t *lsmash_bs_create( void ) -{ - lsmash_bs_t* bs = lsmash_malloc_zero( sizeof(lsmash_bs_t) ); - if( !bs ) - return NULL; - bs->unseekable = 1; - return bs; -} - -void lsmash_bs_cleanup( lsmash_bs_t *bs ) -{ - if( !bs ) - return; - lsmash_bs_free( bs ); - lsmash_free( bs ); -} - -void *lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t *length ) -{ - if( !bs || !bs->buffer.data || bs->buffer.store == 0 || bs->error ) - return NULL; - void *buf = lsmash_memdup( bs->buffer.data, bs->buffer.store ); - if( !buf ) - return NULL; - if( length ) - *length = bs->buffer.store; - return buf; -} -/*---- ----*/ - -/*---- bitstream reader ----*/ -uint8_t lsmash_bs_show_byte( lsmash_bs_t *bs, uint32_t offset ) -{ - if( bs->error || !bs->buffer.data ) - return 0; - if( bs->buffer.pos + offset > bs->buffer.store ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return 0; - } - return bs->buffer.data[ bs->buffer.pos + offset ]; -} - -uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs ) -{ - if( bs->error || !bs->buffer.data ) - return 0; - if( bs->buffer.pos + 1 > bs->buffer.store ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return 0; - } - return bs->buffer.data[ bs->buffer.pos ++ ]; -} - -void lsmash_bs_skip_bytes( lsmash_bs_t *bs, uint32_t size ) -{ - if( bs->error || size == 0 ) - return; - if( bs->buffer.pos + size > bs->buffer.store ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return; - } - bs->buffer.pos += size; -} - -uint8_t *lsmash_bs_get_bytes( lsmash_bs_t *bs, uint32_t size ) -{ - if( bs->error || size == 0 ) - return NULL; - if( bs->buffer.pos + size > bs->buffer.store ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return NULL; - } - uint8_t *value = lsmash_memdup( bs->buffer.data + bs->buffer.pos, size ); - if( !value ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return NULL; - } - bs->buffer.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( lsmash_bs_t *bs, uint32_t size ) -{ - if( !bs || size > INT_MAX ) - return -1; - if( size == 0 ) - return 0; - lsmash_bs_alloc( bs, bs->buffer.store + size ); - if( bs->error || !bs->stream ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return -1; - } - int read_size = bs->read( bs->stream, bs->buffer.data + bs->buffer.store, size ); - if( read_size < 0 ) - { - bs->error = 1; - return -1; - } - bs->buffer.store += read_size; - bs->offset += read_size; - return read_size; -} - -int lsmash_bs_read_data( lsmash_bs_t *bs, uint8_t *buf, size_t *size ) -{ - if( !bs || !size || *size > INT_MAX ) - return -1; - if( !buf || *size == 0 ) - return 0; - if( bs->error || !bs->stream ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return -1; - } - int read_size = bs->read( bs->stream, buf, *size ); - if( read_size < 0 ) - { - bs->error = 1; - return -1; - } - bs->offset += read_size; - *size = read_size; - return 0; -} - -int lsmash_bs_read_c( lsmash_bs_t *bs ) -{ - if( !bs ) - return -1; - if( bs->error || !bs->stream ) - { - lsmash_bs_free( bs ); - bs->error = 1; - return -1; - } - uint8_t c; - int read_size = bs->read( bs->stream, &c, 1 ); - if( read_size != 1 ) - { - if( read_size == 0 ) - return EOF; - bs->error = 1; - return -1; - } - bs->offset += 1; - return c; -} - -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->buffer.store + length ); - if( bs->error || !bs->buffer.data ) /* means, failed to alloc. */ - { - lsmash_bs_free( bs ); - return -1; - } - memcpy( bs->buffer.data + bs->buffer.store, data, length ); - bs->buffer.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 *)lsmash_malloc( sizeof(lsmash_bits_t) ); - if( !bits ) - return NULL; - lsmash_bits_init( bits, bs ); - return bits; -} - -void lsmash_bits_empty( lsmash_bits_t *bits ) -{ - debug_if( !bits ) - return; - lsmash_bs_empty( bits->bs ); - bits->store = 0; - bits->cache = 0; -} - -#define BITS_IN_BYTE 8 -void lsmash_bits_put_align( lsmash_bits_t *bits ) -{ - debug_if( !bits ) - return; - if( !bits->store ) - return; - lsmash_bs_put_byte( bits->bs, bits->cache << ( BITS_IN_BYTE - bits->store ) ); -} - -void lsmash_bits_get_align( lsmash_bits_t *bits ) -{ - debug_if( !bits ) - return; - bits->store = 0; - bits->cache = 0; -} - -/* Must be used ONLY for bits struct created with isom_create_bits. - Otherwise, just lsmash_free() the bits struct. */ -void lsmash_bits_cleanup( lsmash_bits_t *bits ) -{ - debug_if( !bits ) - return; - lsmash_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 ) ); -} - -void lsmash_bits_put( lsmash_bits_t *bits, uint32_t width, uint64_t value ) -{ - 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; - } -} - -uint64_t lsmash_bits_get( lsmash_bits_t *bits, uint32_t width ) -{ - debug_if( !bits || !width ) - return 0; - uint64_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(); - 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 = lsmash_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 = lsmash_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( !list || !entry ) - return -1; - if( !eliminator ) - eliminator = lsmash_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; - } - lsmash_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 ); -} - -int lsmash_remove_entry_tail( lsmash_entry_list_t *list, void *eliminator ) -{ - return lsmash_remove_entry_direct( list, list->tail, eliminator ); -} - -void lsmash_remove_entries( lsmash_entry_list_t *list, void *eliminator ) -{ - if( !list ) - return; - if( !eliminator ) - eliminator = lsmash_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 ); - lsmash_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 ); - lsmash_free( list ); -} - -lsmash_entry_t *lsmash_get_entry( lsmash_entry_list_t *list, uint32_t entry_number ) -{ - if( !list || !entry_number || entry_number > list->entry_count ) - return NULL; - int shortcut = 1; - lsmash_entry_t *entry = NULL; - if( list->last_accessed_entry ) - { - if( entry_number == list->last_accessed_number ) - entry = list->last_accessed_entry; - else if( entry_number == list->last_accessed_number + 1 ) - entry = list->last_accessed_entry->next; - else if( entry_number == list->last_accessed_number - 1 ) - entry = list->last_accessed_entry->prev; - else - shortcut = 0; - } - else - shortcut = 0; - if( !shortcut ) - { - if( entry_number <= (list->entry_count >> 1) ) - { - /* Look for from the head. */ - uint32_t distance_plus_one = entry_number; - for( entry = list->head; entry && --distance_plus_one; entry = entry->next ); - } - else - { - /* Look for from the tail. */ - uint32_t distance = list->entry_count - entry_number; - for( entry = list->tail; entry && distance--; entry = entry->prev ); - } - } - if( entry ) - { - list->last_accessed_entry = entry; - list->last_accessed_number = entry_number; - } - return entry; -} - -void *lsmash_get_entry_data( lsmash_entry_list_t *list, uint32_t entry_number ) -{ - lsmash_entry_t *entry = lsmash_get_entry( list, entry_number ); - return entry ? entry->data : NULL; -} -/*---- ----*/ - -/*---- type ----*/ -double lsmash_fixed2double( uint64_t value, int frac_width ) -{ - return value / (double)(1ULL << frac_width); -} - -float lsmash_int2float32( uint32_t value ) -{ - return (union {uint32_t i; float f;}){value}.f; -} - -double lsmash_int2float64( uint64_t value ) -{ - return (union {uint64_t i; double d;}){value}.d; -} -/*---- ----*/ - -/*---- allocator ----*/ -void *lsmash_malloc( size_t size ) -{ - return malloc( size ); -} - -void *lsmash_malloc_zero( size_t size ) -{ - if( !size ) - return NULL; - void *p = malloc( size ); - if( !p ) - return NULL; - memset( p, 0, size ); - return p; -} - -void *lsmash_realloc( void *ptr, size_t size ) -{ - return realloc( ptr, size ); -} - -void *lsmash_memdup( void *ptr, size_t size ) -{ - if( !size ) - return NULL; - void *dst = malloc( size ); - if( !dst ) - return NULL; - memcpy( dst, ptr, size ); - return dst; -} - -void lsmash_free( void *ptr ) -{ - /* free() shall do nothing if a given address is NULL. */ - free( ptr ); -} - -void lsmash_freep( void *ptrptr ) -{ - if( !ptrptr ) - return; - void **ptr = (void **)ptrptr; - free( *ptr ); - *ptr = NULL; -} - -lsmash_multiple_buffers_t *lsmash_create_multiple_buffers( uint32_t number_of_buffers, uint32_t buffer_size ) -{ - if( (uint64_t)number_of_buffers * buffer_size > UINT32_MAX ) - return NULL; - lsmash_multiple_buffers_t *multiple_buffer = lsmash_malloc( sizeof(lsmash_multiple_buffers_t) ); - if( !multiple_buffer ) - return NULL; - multiple_buffer->buffers = lsmash_malloc( number_of_buffers * buffer_size ); - if( !multiple_buffer->buffers ) - { - lsmash_free( multiple_buffer ); - return NULL; - } - multiple_buffer->number_of_buffers = number_of_buffers; - multiple_buffer->buffer_size = buffer_size; - return multiple_buffer; -} - -void *lsmash_withdraw_buffer( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_number ) -{ - if( !multiple_buffer || !buffer_number || buffer_number > multiple_buffer->number_of_buffers ) - return NULL; - return multiple_buffer->buffers + (buffer_number - 1) * multiple_buffer->buffer_size; -} - -lsmash_multiple_buffers_t *lsmash_resize_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_size ) -{ - if( !multiple_buffer ) - return NULL; - if( buffer_size == multiple_buffer->buffer_size ) - return multiple_buffer; - if( (uint64_t)multiple_buffer->number_of_buffers * buffer_size > UINT32_MAX ) - return NULL; - void *temp; - if( buffer_size > multiple_buffer->buffer_size ) - { - temp = lsmash_realloc( multiple_buffer->buffers, multiple_buffer->number_of_buffers * buffer_size ); - if( !temp ) - return NULL; - for( uint32_t i = multiple_buffer->number_of_buffers - 1; i ; i-- ) - memmove( temp + buffer_size, temp + i * multiple_buffer->buffer_size, multiple_buffer->buffer_size ); - } - else - { - for( uint32_t i = 1; i < multiple_buffer->number_of_buffers; i++ ) - memmove( multiple_buffer->buffers + buffer_size, multiple_buffer->buffers + i * multiple_buffer->buffer_size, multiple_buffer->buffer_size ); - temp = lsmash_realloc( multiple_buffer->buffers, multiple_buffer->number_of_buffers * buffer_size ); - if( !temp ) - return NULL; - } - multiple_buffer->buffers = temp; - multiple_buffer->buffer_size = buffer_size; - return multiple_buffer; -} - -void lsmash_destroy_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer ) -{ - if( !multiple_buffer ) - return; - if( multiple_buffer->buffers ) - lsmash_free( multiple_buffer->buffers ); - lsmash_free( multiple_buffer ); -} - -void lsmash_stream_buffers_cleanup( lsmash_stream_buffers_t *sb ) -{ - if( !sb ) - return; - lsmash_destroy_multiple_buffers( sb->bank ); - sb->bank = NULL; - sb->start = NULL; - sb->end = NULL; - sb->pos = NULL; - sb->update = NULL; - sb->no_more_read = 0; -} - -size_t lsmash_stream_buffers_update( lsmash_stream_buffers_t *sb, uint32_t anticipation_bytes ) -{ - assert( sb && sb->update ); - return sb->update( sb, anticipation_bytes ); -} - -int lsmash_stream_buffers_is_eos( lsmash_stream_buffers_t *sb ) -{ - assert( sb ); - return sb->no_more_read; -} - -uint32_t lsmash_stream_buffers_get_buffer_size( lsmash_stream_buffers_t *sb ) -{ - assert( sb ); - return sb->bank ? sb->bank->buffer_size : 0; -} - -size_t lsmash_stream_buffers_get_valid_size( lsmash_stream_buffers_t *sb ) -{ - assert( sb && sb->start && sb->end ); - return (uintptr_t)sb->end - (uintptr_t)sb->start; -} - -uint8_t lsmash_stream_buffers_get_byte( lsmash_stream_buffers_t *sb ) -{ - assert( sb && sb->pos ); - return *(sb->pos ++); -} - -void lsmash_stream_buffers_seek( lsmash_stream_buffers_t *sb, intptr_t offset, int whence ) -{ - assert( sb && sb->pos ); - if( whence == SEEK_SET ) - { - assert( sb->start && offset >= 0 ); - sb->pos = sb->start + offset; - } - else if( whence == SEEK_CUR ) - sb->pos += offset; - else if( whence == SEEK_END ) - { - assert( sb->end && offset <= 0 ); - sb->pos = sb->end + offset; - } -} - -void lsmash_stream_buffers_set_pos( lsmash_stream_buffers_t *sb, uint8_t *pos ) -{ - assert( sb && sb->pos ); - sb->pos = pos; -} - -uint8_t *lsmash_stream_buffers_get_pos( lsmash_stream_buffers_t *sb ) -{ - assert( sb && sb->pos ); - return sb->pos; -} - -size_t lsmash_stream_buffers_get_offset( lsmash_stream_buffers_t *sb ) -{ - assert( sb && sb->pos && sb->start ); - return (uintptr_t)sb->pos - (uintptr_t)sb->start; -} - -size_t lsmash_stream_buffers_get_remainder( lsmash_stream_buffers_t *sb ) -{ - assert( sb && sb->pos && sb->end ); - return sb->end > sb->pos ? (uintptr_t)sb->end - (uintptr_t)sb->pos : 0; -} - -size_t lsmash_stream_buffers_read( lsmash_stream_buffers_t *sb, size_t read_size ) -{ - assert( sb && sb->pos && sb->bank && sb->stream && sb->type == LSMASH_STREAM_BUFFERS_TYPE_FILE ); - if( read_size == 0 ) - read_size = sb->bank->buffer_size; - size_t size = fread( sb->pos, 1, read_size, sb->stream ); - sb->end = sb->pos + size; - sb->no_more_read = size == 0 ? feof( (FILE *)sb->stream ) : 0; - return size; -} - -void lsmash_data_string_copy( lsmash_stream_buffers_t *sb, lsmash_data_string_handler_t *dsh, size_t size, uint32_t pos ) -{ - assert( sb && sb->pos && dsh && dsh->data ); - if( pos + size > dsh->data_length ) - size = dsh->data_length - pos; - if( size > 0 ) - memcpy( sb->pos, dsh->data + pos, size ); - dsh->consumed_length = pos + size; - dsh->remainder_length = dsh->data_length - dsh->consumed_length; - sb->end = sb->pos + size; - sb->no_more_read = (dsh->remainder_length == 0); -} - -void lsmash_stream_buffers_memcpy( uint8_t *data, lsmash_stream_buffers_t *sb, size_t size ) -{ - assert( sb && sb->pos && sb->end ); - if( sb->pos + size > sb->end ) - size = sb->end - sb->pos; - if( size == 0 ) - return; - memcpy( data, sb->pos, size ); - sb->pos += size; -} - -static size_t stream_buffers_update_file( lsmash_stream_buffers_t *sb, uint32_t anticipation_bytes ) -{ - assert( anticipation_bytes < sb->bank->buffer_size ); - uint32_t remainder_bytes = sb->end - sb->pos; - if( sb->no_more_read || remainder_bytes > anticipation_bytes ) - return remainder_bytes; - if( sb->start != sb->pos ) - /* Move unused data to the head of buffer. */ - memmove( sb->start, sb->pos, remainder_bytes ); - /* Read and store the next data into the buffer. - * Move the position of buffer on the head. */ - lsmash_stream_buffers_seek( sb, remainder_bytes, SEEK_SET ); - size_t read_size = lsmash_stream_buffers_read( sb, sb->bank->buffer_size - remainder_bytes ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - sb->no_more_read = read_size == 0 ? feof( (FILE *)sb->stream ) : 0; - return lsmash_stream_buffers_get_remainder( sb ); -} - -static size_t stream_buffers_update_data_string( lsmash_stream_buffers_t *sb, uint32_t anticipation_bytes ) -{ - assert( anticipation_bytes < sb->bank->buffer_size ); - uint32_t remainder_bytes = sb->end - sb->pos; - if( sb->no_more_read || remainder_bytes > anticipation_bytes ) - return remainder_bytes; - if( sb->start != sb->pos ) - /* Move unused data to the head of buffer. */ - memmove( sb->start, sb->pos, remainder_bytes ); - /* Read and store the next data into the buffer. - * Move the position of buffer on the head. */ - lsmash_data_string_handler_t *dsh = (lsmash_data_string_handler_t *)sb->stream; - uint32_t consumed_data_length = LSMASH_MIN( dsh->remainder_length, sb->bank->buffer_size - remainder_bytes ); - lsmash_stream_buffers_seek( sb, remainder_bytes, SEEK_SET ); - lsmash_data_string_copy( sb, dsh, consumed_data_length, dsh->consumed_length ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - sb->no_more_read = (dsh->remainder_length == 0); - return lsmash_stream_buffers_get_remainder( sb ); -} - -void lsmash_stream_buffers_setup( lsmash_stream_buffers_t *sb, lsmash_stream_buffers_type type, void *stream ) -{ - assert( sb ); - sb->type = type; - sb->stream = stream; - if( type == LSMASH_STREAM_BUFFERS_TYPE_FILE ) - sb->update = stream_buffers_update_file; - else if( type == LSMASH_STREAM_BUFFERS_TYPE_DATA_STRING ) - sb->update = stream_buffers_update_data_string; -} -/*---- ----*/ - -/*---- others ----*/ -void lsmash_log -( - void *hp, - lsmash_log_level level, - const char *message, - ... -) -{ - char *prefix; - va_list args; - va_start( args, message ); - switch( level ) - { - case LSMASH_LOG_ERROR: - prefix = "Error"; - break; - case LSMASH_LOG_WARNING: - prefix = "Warning"; - break; - case LSMASH_LOG_INFO: - prefix = "Info"; - break; - default: - prefix = "Unknown"; - break; - } - /* Dereference lsmash_class_t pointer if hp is non-NULL. */ - lsmash_class_t *class = hp ? (lsmash_class_t *)*(intptr_t *)hp : NULL; - if( class ) - fprintf( stderr, "[%s: %s]: ", class->name, prefix ); - else - fprintf( stderr, "[%s]: ", prefix ); - vfprintf( stderr, message, args ); - va_end( args ); -} - -uint32_t lsmash_count_bits -( - uint32_t bits -) -{ - bits = (bits & 0x55555555) + ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - bits = (bits & 0x0f0f0f0f) + ((bits >> 4) & 0x0f0f0f0f); - bits = (bits & 0x00ff00ff) + ((bits >> 8) & 0x00ff00ff); - return (bits & 0x0000ffff) + ((bits >> 16) & 0x0000ffff); -} - -void lsmash_ifprintf -( - FILE *fp, - int indent, - const char *format, ... -) -{ - va_list args; - va_start( args, format ); - if( indent <= 10 ) - { - static const char *indent_string[] = - { - "", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " " - }; - fprintf( fp, "%s", indent_string[indent] ); - } - else - for( int i = 0; i < indent; i++ ) - fprintf( fp, " " ); - vfprintf( fp, format, args ); - va_end( args ); -} - -int lsmash_ceil_log2 -( - uint64_t value -) -{ - int length = 0; - while( value > (1ULL << length) ) - ++length; - return length; -} - -/* for qsort function */ -int lsmash_compare_dts -( - const lsmash_media_ts_t *a, - const lsmash_media_ts_t *b -) -{ - int64_t diff = (int64_t)(a->dts - b->dts); - return diff > 0 ? 1 : (diff == 0 ? 0 : -1); -} - -int lsmash_compare_cts -( - const lsmash_media_ts_t *a, - const lsmash_media_ts_t *b -) -{ - int64_t diff = (int64_t)(a->cts - b->cts); - return diff > 0 ? 1 : (diff == 0 ? 0 : -1); -} - -#ifdef _WIN32 -int lsmash_convert_ansi_to_utf8( const char *ansi, char *utf8, int length ) -{ - int len0 = MultiByteToWideChar( CP_THREAD_ACP, 0, ansi, -1, 0, 0 ); - wchar_t *buff = lsmash_malloc( len0 * sizeof(wchar_t) ); - if( !buff ) - return 0; - int len1 = MultiByteToWideChar( CP_THREAD_ACP, 0, ansi, -1, buff, len0 ); - if( len0 != len1 ) - goto convert_fail; - len0 = WideCharToMultiByte( CP_UTF8, 0, buff, -1, 0, 0, 0, 0 ); - if( len0 > length - 1 ) - goto convert_fail; - len1 = WideCharToMultiByte( CP_UTF8, 0, buff, -1, utf8, length, 0, 0 ); - lsmash_free( buff ); - if( len0 != len1 ) - return 0; - return len1; -convert_fail: - lsmash_free( buff ); - return 0; -} -#endif diff -Nru l-smash-1.9.1/utils.h l-smash-2.3.0/utils.h --- l-smash-1.9.1/utils.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/utils.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,340 +0,0 @@ -/***************************************************************************** - * utils.h: - ***************************************************************************** - * Copyright (C) 2010-2014 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)) - -typedef struct -{ - char *name; -} lsmash_class_t; - -/*---- bytestream ----*/ -typedef struct -{ - 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 */ -} lsmash_buffer_t; - -typedef struct -{ - void *stream; /* I/O stream */ - uint8_t error; - uint8_t unseekable; - uint64_t written; /* the number of bytes written into 'stream' already */ - uint64_t offset; /* the current position in the 'stream' - * the number of bytes from the beginning */ - lsmash_buffer_t buffer; - int (*read) ( void *opaque, uint8_t *buf, int size ); - int (*write)( void *opaque, uint8_t *buf, int size ); - int64_t (*seek) ( void *opaque, int64_t offset, int whence ); -} 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( void ); -void lsmash_bs_cleanup( lsmash_bs_t *bs ); -int64_t lsmash_bs_seek( lsmash_bs_t *bs, int64_t offset, int whence ); - -/*---- bytestream writer ----*/ -void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value ); -void lsmash_bs_put_bytes( lsmash_bs_t *bs, uint32_t size, void *value ); -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 ); -void lsmash_bs_put_le16( lsmash_bs_t *bs, uint16_t value ); -void lsmash_bs_put_le32( lsmash_bs_t *bs, uint32_t value ); -int lsmash_bs_flush_buffer( lsmash_bs_t *bs ); -size_t lsmash_bs_write_data( lsmash_bs_t *bs, uint8_t *buf, size_t size ); -void *lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t *length ); - -/*---- bytestream reader ----*/ -uint8_t lsmash_bs_show_byte( lsmash_bs_t *bs, uint32_t offset ); -uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs ); -void lsmash_bs_skip_bytes( lsmash_bs_t *bs, uint32_t size ); -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( lsmash_bs_t *bs, uint32_t size ); -int lsmash_bs_read_data( lsmash_bs_t *bs, uint8_t *buf, size_t *size ); -int lsmash_bs_read_c( lsmash_bs_t *bs ); -int lsmash_bs_import_data( lsmash_bs_t *bs, void *data, uint32_t length ); - -/*---- bitstream ----*/ -typedef struct { - lsmash_bs_t* bs; - uint8_t store; - uint8_t cache; -} lsmash_bits_t; - -void lsmash_bits_init( lsmash_bits_t* bits, lsmash_bs_t *bs ); -lsmash_bits_t *lsmash_bits_create( lsmash_bs_t *bs ); -void lsmash_bits_empty( lsmash_bits_t *bits ); -void lsmash_bits_put_align( lsmash_bits_t *bits ); -void lsmash_bits_get_align( lsmash_bits_t *bits ); -void lsmash_bits_cleanup( lsmash_bits_t *bits ); - -/*---- bitstream writer ----*/ -void lsmash_bits_put( lsmash_bits_t *bits, uint32_t width, uint64_t value ); -uint64_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 ); -int lsmash_remove_entry_tail( lsmash_entry_list_t *list, 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 ----*/ -typedef struct -{ - uint32_t number_of_buffers; - uint32_t buffer_size; - void *buffers; -} lsmash_multiple_buffers_t; - -lsmash_multiple_buffers_t *lsmash_create_multiple_buffers( uint32_t number_of_buffers, uint32_t buffer_size ); -void *lsmash_withdraw_buffer( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_number ); -lsmash_multiple_buffers_t *lsmash_resize_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer, uint32_t buffer_size ); -void lsmash_destroy_multiple_buffers( lsmash_multiple_buffers_t *multiple_buffer ); - -typedef enum -{ - LSMASH_STREAM_BUFFERS_TYPE_NONE = 0, - LSMASH_STREAM_BUFFERS_TYPE_FILE, /* -> FILE */ - LSMASH_STREAM_BUFFERS_TYPE_DATA_STRING, /* -> lsmash_data_string_handler_t */ -} lsmash_stream_buffers_type; - -typedef struct lsmash_stream_buffers_tag lsmash_stream_buffers_t; -struct lsmash_stream_buffers_tag -{ - lsmash_stream_buffers_type type; - void *stream; - lsmash_multiple_buffers_t *bank; - uint8_t *start; - uint8_t *end; - uint8_t *pos; - size_t (*update)( lsmash_stream_buffers_t *, uint32_t ); - int no_more_read; -}; - -typedef struct -{ - uint8_t *data; - uint32_t data_length; - uint32_t remainder_length; - uint32_t consumed_length; /* overall consumed length */ -} lsmash_data_string_handler_t; - -void lsmash_stream_buffers_setup( lsmash_stream_buffers_t *sb, lsmash_stream_buffers_type type, void *stream ); -void lsmash_stream_buffers_cleanup( lsmash_stream_buffers_t *sb ); /* 'type' and 'stream' are not touched. */ -size_t lsmash_stream_buffers_update( lsmash_stream_buffers_t *sb, uint32_t anticipation_bytes ); -int lsmash_stream_buffers_is_eos( lsmash_stream_buffers_t *sb ); -uint32_t lsmash_stream_buffers_get_buffer_size( lsmash_stream_buffers_t *sb ); -size_t lsmash_stream_buffers_get_valid_size( lsmash_stream_buffers_t *sb ); -uint8_t lsmash_stream_buffers_get_byte( lsmash_stream_buffers_t *sb ); -void lsmash_stream_buffers_seek( lsmash_stream_buffers_t *sb, intptr_t offset, int whence ); -void lsmash_stream_buffers_set_pos( lsmash_stream_buffers_t *sb, uint8_t *pos ); -uint8_t *lsmash_stream_buffers_get_pos( lsmash_stream_buffers_t *sb ); -size_t lsmash_stream_buffers_get_offset( lsmash_stream_buffers_t *sb ); -size_t lsmash_stream_buffers_get_remainder( lsmash_stream_buffers_t *sb ); -size_t lsmash_stream_buffers_read( lsmash_stream_buffers_t *sb, size_t read_size ); -void lsmash_stream_buffers_memcpy( uint8_t *data, lsmash_stream_buffers_t *sb, size_t size ); -void lsmash_data_string_copy( lsmash_stream_buffers_t *sb, lsmash_data_string_handler_t *dsh, size_t size, uint32_t pos ); - -/*---- others ----*/ -typedef enum -{ - LSMASH_LOG_ERROR, - LSMASH_LOG_WARNING, - LSMASH_LOG_INFO, -} lsmash_log_level; - -typedef struct -{ - uint64_t n; - uint64_t d; -} lsmash_rational_u64_t; - -typedef struct -{ - int64_t n; - uint64_t d; -} lsmash_rational_s64_t; - -void lsmash_log -( - void *hp, - lsmash_log_level level, - const char *message, - ... -); - -uint32_t lsmash_count_bits -( - uint32_t bits -); - -void lsmash_ifprintf -( - FILE *fp, - int indent, - const char *format, ... -); - -int lsmash_ceil_log2 -( - uint64_t value -); - -int lsmash_compare_dts -( - const lsmash_media_ts_t *a, - const lsmash_media_ts_t *b -); - -int lsmash_compare_cts -( - const lsmash_media_ts_t *a, - const lsmash_media_ts_t *b -); - -static inline uint64_t lsmash_get_gcd -( - uint64_t a, - uint64_t b -) -{ - if( !b ) - return a; - while( 1 ) - { - uint64_t c = a % b; - if( !c ) - return b; - a = b; - b = c; - } -} - -static inline uint64_t lsmash_get_lcm -( - uint64_t a, - uint64_t b -) -{ - if( !a ) - return 0; - return (a / lsmash_get_gcd( a, b )) * b; -} - -static inline void lsmash_reduce_fraction -( - uint64_t *a, - uint64_t *b -) -{ - if( !a || !b ) - return; - uint64_t gcd = lsmash_get_gcd( *a, *b ); - if( gcd ) - { - *a /= gcd; - *b /= gcd; - } -} - -static inline void lsmash_reduce_fraction_su -( - int64_t *a, - uint64_t *b -) -{ - if( !a || !b ) - return; - uint64_t c = *a > 0 ? *a : -(*a); - uint64_t gcd = lsmash_get_gcd( c, *b ); - if( gcd ) - { - c /= gcd; - *b /= gcd; - *a = *a > 0 ? c : -c; - } -} - -#endif diff -Nru l-smash-1.9.1/vc1.c l-smash-2.3.0/vc1.c --- l-smash-1.9.1/vc1.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/vc1.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,959 +0,0 @@ -/***************************************************************************** - * vc1.c: - ***************************************************************************** - * Copyright (C) 2012-2014 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" - -#include -#include -#include - -#include "box.h" - -/*************************************************************************** - SMPTE 421M-2006 - SMPTE RP 2025-2007 -***************************************************************************/ -#include "vc1.h" - -#define IF_INVALID_VALUE( x ) if( x ) - -struct lsmash_vc1_header_tag -{ - uint8_t *ebdu; - uint32_t ebdu_size; -}; - -typedef enum -{ - VC1_ADVANCED_PICTURE_TYPE_P = 0x0, /* 0b0 */ - VC1_ADVANCED_PICTURE_TYPE_B = 0x2, /* 0b10 */ - VC1_ADVANCED_PICTURE_TYPE_I = 0x6, /* 0b110 */ - VC1_ADVANCED_PICTURE_TYPE_BI = 0xE, /* 0b1110 */ - VC1_ADVANCED_PICTURE_TYPE_SKIPPED = 0xF, /* 0b1111 */ -} vc1_picture_type; - -typedef enum -{ - VC1_ADVANCED_FIELD_PICTURE_TYPE_II = 0x0, /* 0b000 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_IP = 0x1, /* 0b001 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_PI = 0x2, /* 0b010 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_PP = 0x3, /* 0b011 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_BB = 0x4, /* 0b100 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_BBI = 0x5, /* 0b101 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_BIB = 0x6, /* 0b110 */ - VC1_ADVANCED_FIELD_PICTURE_TYPE_BIBI = 0x7, /* 0b111 */ -} vc1_field_picture_type; - -typedef enum -{ - VC1_FRAME_CODING_MODE_PROGRESSIVE = 0x0, /* 0b0 */ - VC1_FRAME_CODING_MODE_FRAME_INTERLACE = 0x2, /* 0b10 */ - VC1_FRAME_CODING_MODE_FIELD_INTERLACE = 0x3, /* 0b11 */ -} vc1_frame_coding_mode; - -static void vc1_destroy_header( lsmash_vc1_header_t *hdr ) -{ - if( !hdr ) - return; - if( hdr->ebdu ) - lsmash_free( hdr->ebdu ); - lsmash_free( hdr ); -} - -void lsmash_destroy_vc1_headers( lsmash_vc1_specific_parameters_t *param ) -{ - if( !param ) - return; - vc1_destroy_header( param->seqhdr ); - vc1_destroy_header( param->ephdr ); - param->seqhdr = NULL; - param->ephdr = NULL; -} - -void vc1_destruct_specific_data( void *data ) -{ - if( !data ) - return; - lsmash_destroy_vc1_headers( data ); - lsmash_free( data ); -} - -void vc1_cleanup_parser( vc1_info_t *info ) -{ - if( !info ) - return; - lsmash_destroy_vc1_headers( &info->dvc1_param ); - lsmash_stream_buffers_cleanup( info->buffer.sb ); - lsmash_bits_adhoc_cleanup( info->bits ); - info->bits = NULL; -} - -int vc1_setup_parser -( - vc1_info_t *info, - lsmash_stream_buffers_t *sb, - int parse_only, - lsmash_stream_buffers_type type, - void *stream -) -{ - assert( sb ); - if( !info ) - return -1; - memset( info, 0, sizeof(vc1_info_t) ); - vc1_stream_buffer_t *hb = &info->buffer; - hb->sb = sb; - lsmash_stream_buffers_setup( sb, type, stream ); - sb->bank = lsmash_create_multiple_buffers( parse_only ? 2 : 4, VC1_DEFAULT_BUFFER_SIZE ); - if( !sb->bank ) - return -1; - sb->start = lsmash_withdraw_buffer( sb->bank, 1 ); - hb->rbdu = lsmash_withdraw_buffer( sb->bank, 2 ); - sb->pos = sb->start; - sb->end = sb->start; - if( !parse_only ) - { - info->access_unit.data = lsmash_withdraw_buffer( sb->bank, 3 ); - info->access_unit.incomplete_data = lsmash_withdraw_buffer( sb->bank, 4 ); - } - info->bits = lsmash_bits_adhoc_create(); - if( !info->bits ) - { - lsmash_stream_buffers_cleanup( sb ); - return -1; - } - return 0; -} - -static inline uint8_t vc1_get_vlc( lsmash_bits_t *bits, int length ) -{ - uint8_t value = 0; - for( int i = 0; i < length; i++ ) - if( lsmash_bits_get( bits, 1 ) ) - value = (value << 1) | 1; - else - { - value = value << 1; - break; - } - return value; -} - -/* Convert EBDU (Encapsulated Byte Data Unit) to RBDU (Raw Byte Data Unit). */ -static uint8_t *vc1_remove_emulation_prevention( uint8_t *src, uint64_t src_length, uint8_t *dst ) -{ - uint8_t *src_end = src + src_length; - while( src < src_end ) - if( ((src + 2) < src_end) && !src[0] && !src[1] && (src[2] == 0x03) ) - { - /* 0x000003 -> 0x0000 */ - *dst++ = *src++; - *dst++ = *src++; - src++; /* Skip emulation_prevention_three_byte (0x03). */ - } - else - *dst++ = *src++; - return dst; -} - -static int vc1_import_rbdu_from_ebdu( lsmash_bits_t *bits, uint8_t *rbdu_buffer, uint8_t *ebdu, uint64_t ebdu_size ) -{ - uint8_t *rbdu_start = rbdu_buffer; - uint8_t *rbdu_end = vc1_remove_emulation_prevention( ebdu, ebdu_size, rbdu_buffer ); - uint64_t rbdu_length = rbdu_end - rbdu_start; - return lsmash_bits_import_data( bits, rbdu_start, rbdu_length ); -} - -static void vc1_parse_hrd_param( lsmash_bits_t *bits, vc1_hrd_param_t *hrd_param ) -{ - hrd_param->hrd_num_leaky_buckets = lsmash_bits_get( bits, 5 ); - lsmash_bits_get( bits, 4 ); /* bitrate_exponent */ - lsmash_bits_get( bits, 4 ); /* buffer_size_exponent */ - for( uint8_t i = 0; i < hrd_param->hrd_num_leaky_buckets; i++ ) - { - lsmash_bits_get( bits, 16 ); /* hrd_rate */ - lsmash_bits_get( bits, 16 ); /* hrd_buffer */ - } -} - -int vc1_parse_sequence_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int try_append ) -{ - lsmash_bits_t *bits = info->bits; - vc1_sequence_header_t *sequence = &info->sequence; - if( vc1_import_rbdu_from_ebdu( bits, info->buffer.rbdu, ebdu + VC1_START_CODE_LENGTH, ebdu_size ) ) - return -1; - memset( sequence, 0, sizeof(vc1_sequence_header_t) ); - sequence->profile = lsmash_bits_get( bits, 2 ); - if( sequence->profile != 3 ) - return -1; /* SMPTE Reserved */ - sequence->level = lsmash_bits_get( bits, 3 ); - if( sequence->level > 4 ) - return -1; /* SMPTE Reserved */ - sequence->colordiff_format = lsmash_bits_get( bits, 2 ); - if( sequence->colordiff_format != 1 ) - return -1; /* SMPTE Reserved */ - lsmash_bits_get( bits, 9 ); /* frmrtq_postproc (3) - * bitrtq_postproc (5) - * postproc_flag (1) */ - sequence->max_coded_width = lsmash_bits_get( bits, 12 ); - sequence->max_coded_height = lsmash_bits_get( bits, 12 ); - lsmash_bits_get( bits, 1 ); /* pulldown */ - sequence->interlace = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 4 ); /* tfcntrflag (1) - * finterpflag (1) - * reserved (1) - * psf (1) */ - if( lsmash_bits_get( bits, 1 ) ) /* display_ext */ - { - sequence->disp_horiz_size = lsmash_bits_get( bits, 14 ) + 1; - sequence->disp_vert_size = lsmash_bits_get( bits, 14 ) + 1; - if( lsmash_bits_get( bits, 1 ) ) /* aspect_ratio_flag */ - { - uint8_t aspect_ratio = lsmash_bits_get( bits, 4 ); - if( aspect_ratio == 15 ) - { - sequence->aspect_width = lsmash_bits_get( bits, 8 ) + 1; /* aspect_horiz_size */ - sequence->aspect_height = lsmash_bits_get( bits, 8 ) + 1; /* aspect_vert_size */ - } - else - { - static const struct - { - uint32_t aspect_width; - uint32_t aspect_height; - } vc1_aspect_ratio[15] = - { - { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, { 40, 33 }, { 24, 11 }, - { 20, 11 }, { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, - { 0, 0 } /* SMPTE Reserved */ - }; - sequence->aspect_width = vc1_aspect_ratio[ aspect_ratio ].aspect_width; - sequence->aspect_height = vc1_aspect_ratio[ aspect_ratio ].aspect_height; - } - } - sequence->framerate_flag = lsmash_bits_get( bits, 1 ); - if( sequence->framerate_flag ) - { - if( lsmash_bits_get( bits, 1 ) ) /* framerateind */ - { - sequence->framerate_numerator = lsmash_bits_get( bits, 16 ) + 1; - sequence->framerate_denominator = 32; - } - else - { - static const uint32_t vc1_frameratenr_table[8] = { 0, 24, 25, 30, 50, 60, 48, 72 }; - uint8_t frameratenr = lsmash_bits_get( bits, 8 ); - IF_INVALID_VALUE( frameratenr == 0 ) - return -1; /* Forbidden */ - if( frameratenr > 7 ) - return -1; /* SMPTE Reserved */ - uint8_t frameratedr = lsmash_bits_get( bits, 4 ); - if( frameratedr != 1 && frameratedr != 2 ) - return -1; /* 0: Forbidden, 3-15: SMPTE Reserved */ - if( frameratedr == 1 ) - { - sequence->framerate_numerator = vc1_frameratenr_table[ frameratenr ]; - sequence->framerate_denominator = 1; - } - else - { - sequence->framerate_numerator = vc1_frameratenr_table[ frameratenr ] * 1000; - sequence->framerate_denominator = 1001; - } - } - } - if( lsmash_bits_get( bits, 1 ) ) /* color_format_flag */ - { - sequence->color_prim = lsmash_bits_get( bits, 8 ); - sequence->transfer_char = lsmash_bits_get( bits, 8 ); - sequence->matrix_coef = lsmash_bits_get( bits, 8 ); - } - sequence->hrd_param_flag = lsmash_bits_get( bits, 1 ); - if( sequence->hrd_param_flag ) - vc1_parse_hrd_param( bits, &sequence->hrd_param ); - } - /* '1' and stuffing bits ('0's) */ - IF_INVALID_VALUE( !lsmash_bits_get( bits, 1 ) ) - return -1; - lsmash_bits_empty( bits ); - /* Preparation for creating VC1SpecificBox */ - if( try_append ) - { - /* Update some specific parameters. */ - lsmash_vc1_specific_parameters_t *param = &info->dvc1_param; - lsmash_vc1_header_t *seqhdr = param->seqhdr; - if( !seqhdr ) - { - seqhdr = lsmash_malloc( sizeof(lsmash_vc1_header_t) ); - if( !seqhdr ) - return -1; - seqhdr->ebdu = lsmash_memdup( ebdu, ebdu_size ); - if( !seqhdr->ebdu ) - { - lsmash_free( seqhdr ); - return -1; - } - seqhdr->ebdu_size = ebdu_size; - param->seqhdr = seqhdr; - } - else if( seqhdr && seqhdr->ebdu && (seqhdr->ebdu_size == ebdu_size) ) - param->multiple_sequence |= !!memcmp( ebdu, seqhdr->ebdu, seqhdr->ebdu_size ); - param->profile = sequence->profile << 2; - param->level = LSMASH_MAX( param->level, sequence->level ); - param->interlaced |= sequence->interlace; - uint32_t framerate = sequence->framerate_flag - ? ((double)sequence->framerate_numerator / sequence->framerate_denominator) + 0.5 - : 0xffffffff; /* 0xffffffff means framerate is unknown or unspecified. */ - if( param->framerate == 0 ) - param->framerate = framerate; - else if( param->framerate != framerate ) - param->framerate = 0xffffffff; - } - info->sequence.present = 1; - return bits->bs->error ? -1 : 0; -} - -int vc1_parse_entry_point_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int try_append ) -{ - lsmash_bits_t *bits = info->bits; - vc1_sequence_header_t *sequence = &info->sequence; - vc1_entry_point_t *entry_point = &info->entry_point; - if( vc1_import_rbdu_from_ebdu( bits, info->buffer.rbdu, ebdu + VC1_START_CODE_LENGTH, ebdu_size ) ) - return -1; - memset( entry_point, 0, sizeof(vc1_entry_point_t) ); - uint8_t broken_link_flag = lsmash_bits_get( bits, 1 ); /* 0: no concatenation between the current and the previous entry points - * 1: concatenated and needed to discard B-pictures */ - entry_point->closed_entry_point = lsmash_bits_get( bits, 1 ); /* 0: Open RAP, 1: Closed RAP */ - IF_INVALID_VALUE( broken_link_flag && entry_point->closed_entry_point ) - return -1; /* invalid combination */ - lsmash_bits_get( bits, 4 ); /* panscan_flag (1) - * refdist_flag (1) - * loopfilter (1) - * fastuvmc (1) */ - uint8_t extended_mv = lsmash_bits_get( bits, 1 ); - lsmash_bits_get( bits, 6 ); /* dquant (2) - * vstransform (1) - * overlap (1) - * quantizer (2) */ - if( sequence->hrd_param_flag ) - for( uint8_t i = 0; i < sequence->hrd_param.hrd_num_leaky_buckets; i++ ) - lsmash_bits_get( bits, 8 ); /* hrd_full */ - /* Decide coded size here. - * The correct formula is defined in Amendment 2:2011 to SMPTE ST 421M:2006. - * Don't use the formula specified in SMPTE 421M-2006. */ - uint16_t coded_width; - uint16_t coded_height; - if( lsmash_bits_get( bits, 1 ) ) /* coded_size_flag */ - { - coded_width = lsmash_bits_get( bits, 12 ); - coded_height = lsmash_bits_get( bits, 12 ); - } - else - { - coded_width = sequence->max_coded_width; - coded_height = sequence->max_coded_height; - } - coded_width = 2 * (coded_width + 1); /* corrected */ - coded_height = 2 * (coded_height + 1); /* corrected */ - if( sequence->disp_horiz_size == 0 || sequence->disp_vert_size == 0 ) - { - sequence->disp_horiz_size = coded_width; - sequence->disp_vert_size = coded_height; - } - /* */ - if( extended_mv ) - lsmash_bits_get( bits, 1 ); /* extended_dmv */ - if( lsmash_bits_get( bits, 1 ) ) /* range_mapy_flag */ - lsmash_bits_get( bits, 3 ); /* range_mapy */ - if( lsmash_bits_get( bits, 1 ) ) /* range_mapuv_flag */ - lsmash_bits_get( bits, 3 ); /* range_mapuv */ - /* '1' and stuffing bits ('0's) */ - IF_INVALID_VALUE( !lsmash_bits_get( bits, 1 ) ) - return -1; - lsmash_bits_empty( bits ); - /* Preparation for creating VC1SpecificBox */ - if( try_append ) - { - lsmash_vc1_specific_parameters_t *param = &info->dvc1_param; - lsmash_vc1_header_t *ephdr = param->ephdr; - if( !ephdr ) - { - ephdr = lsmash_malloc( sizeof(lsmash_vc1_header_t) ); - if( !ephdr ) - return -1; - ephdr->ebdu = lsmash_memdup( ebdu, ebdu_size ); - if( !ephdr->ebdu ) - { - lsmash_free( ephdr ); - return -1; - } - ephdr->ebdu_size = ebdu_size; - param->ephdr = ephdr; - } - else if( ephdr && ephdr->ebdu && (ephdr->ebdu_size == ebdu_size) ) - param->multiple_entry |= !!memcmp( ebdu, ephdr->ebdu, ephdr->ebdu_size ); - } - info->entry_point.present = 1; - return bits->bs->error ? -1 : 0; -} - -int vc1_parse_advanced_picture( lsmash_bits_t *bits, - vc1_sequence_header_t *sequence, vc1_picture_info_t *picture, - uint8_t *rbdu_buffer, uint8_t *ebdu, uint64_t ebdu_size ) -{ - if( vc1_import_rbdu_from_ebdu( bits, rbdu_buffer, ebdu + VC1_START_CODE_LENGTH, ebdu_size ) ) - return -1; - if( sequence->interlace ) - picture->frame_coding_mode = vc1_get_vlc( bits, 2 ); - else - picture->frame_coding_mode = 0; - if( picture->frame_coding_mode != 0x3 ) - picture->type = vc1_get_vlc( bits, 4 ); /* ptype (variable length) */ - else - picture->type = lsmash_bits_get( bits, 3 ); /* fptype (3) */ - picture->present = 1; - lsmash_bits_empty( bits ); - return bits->bs->error ? -1 : 0; -} - -void vc1_update_au_property( vc1_access_unit_t *access_unit, vc1_picture_info_t *picture ) -{ - access_unit->random_accessible = picture->random_accessible; - access_unit->closed_gop = picture->closed_gop; - /* I-picture - * Be coded using information only from itself. (independent) - * All the macroblocks in an I-picture are intra-coded. - * P-picture - * Be coded using motion compensated prediction from past reference fields or frame. - * Can contain macroblocks that are inter-coded (i.e. coded using prediction) and macroblocks that are intra-coded. - * B-picture - * Be coded using motion compensated prediction from past and/or future reference fields or frames. (bi-predictive) - * Cannot be used for predicting any other picture. (disposable) - * BI-picture - * All the macroblocks in BI-picture are intra-coded. (independent) - * Cannot be used for predicting any other picture. (disposable) */ - if( picture->frame_coding_mode == 0x3 ) - { - /* field interlace */ - access_unit->independent = picture->type == VC1_ADVANCED_FIELD_PICTURE_TYPE_II || picture->type == VC1_ADVANCED_FIELD_PICTURE_TYPE_BIBI; - access_unit->non_bipredictive = picture->type < VC1_ADVANCED_FIELD_PICTURE_TYPE_BB || picture->type == VC1_ADVANCED_FIELD_PICTURE_TYPE_BIBI; - access_unit->disposable = picture->type >= VC1_ADVANCED_FIELD_PICTURE_TYPE_BB; - } - else - { - /* frame progressive/interlace */ - access_unit->independent = picture->type == VC1_ADVANCED_PICTURE_TYPE_I || picture->type == VC1_ADVANCED_PICTURE_TYPE_BI; - access_unit->non_bipredictive = picture->type != VC1_ADVANCED_PICTURE_TYPE_B; - access_unit->disposable = picture->type == VC1_ADVANCED_PICTURE_TYPE_B || picture->type == VC1_ADVANCED_PICTURE_TYPE_BI; - } - picture->present = 0; - picture->type = 0; - picture->closed_gop = 0; - picture->start_of_sequence = 0; - picture->random_accessible = 0; -} - -int vc1_find_au_delimit_by_bdu_type( uint8_t bdu_type, uint8_t prev_bdu_type ) -{ - /* In any access unit, EBDU with smaller least significant 8-bits of BDU type doesn't precede EBDU with larger one. - * Therefore, the condition: (bdu_type 0xF) > (prev_bdu_type & 0xF) is more precisely. - * No two or more frame start codes shall be in the same access unit. */ - return bdu_type > prev_bdu_type || (bdu_type == 0x0D && prev_bdu_type == 0x0D); -} - -int vc1_supplement_buffer( vc1_stream_buffer_t *hb, vc1_access_unit_t *access_unit, uint32_t size ) -{ - lsmash_stream_buffers_t *sb = hb->sb; - uint32_t buffer_pos_offset = sb->pos - sb->start; - uint32_t buffer_valid_length = sb->end - sb->start; - lsmash_multiple_buffers_t *bank = lsmash_resize_multiple_buffers( sb->bank, size ); - if( !bank ) - return -1; - sb->bank = bank; - sb->start = lsmash_withdraw_buffer( bank, 1 ); - hb->rbdu = lsmash_withdraw_buffer( bank, 2 ); - sb->pos = sb->start + buffer_pos_offset; - sb->end = sb->start + buffer_valid_length; - if( access_unit && bank->number_of_buffers == 4 ) - { - access_unit->data = lsmash_withdraw_buffer( bank, 3 ); - access_unit->incomplete_data = lsmash_withdraw_buffer( bank, 4 ); - } - return 0; -} - -uint8_t *lsmash_create_vc1_specific_info( lsmash_vc1_specific_parameters_t *param, uint32_t *data_length ) -{ - if( !param || !data_length ) - return NULL; - if( !param->seqhdr && !param->ephdr ) - return NULL; - /* Calculate enough buffer size. */ - lsmash_vc1_header_t *seqhdr = param->seqhdr; - lsmash_vc1_header_t *ephdr = param->ephdr; - uint32_t buffer_size = ISOM_BASEBOX_COMMON_SIZE + 7 + seqhdr->ebdu_size + ephdr->ebdu_size; - /* Set up bitstream writer. */ - uint8_t buffer[buffer_size]; - lsmash_bits_t bits = { 0 }; - lsmash_bs_t bs = { 0 }; - bs.buffer.data = buffer; - bs.buffer.alloc = buffer_size; - lsmash_bits_init( &bits, &bs ); - /* Create a VC1SpecificBox */ - lsmash_bits_put( &bits, 32, 0 ); /* box size */ - lsmash_bits_put( &bits, 32, ISOM_BOX_TYPE_DVC1.fourcc ); /* box type: 'dvc1' */ - lsmash_bits_put( &bits, 4, param->profile ); /* profile */ - lsmash_bits_put( &bits, 3, param->level ); /* level */ - lsmash_bits_put( &bits, 1, 0 ); /* reserved */ - /* VC1AdvDecSpecStruc (for Advanced Profile) */ - lsmash_bits_put( &bits, 3, param->level ); /* level (identical to the previous level field) */ - lsmash_bits_put( &bits, 1, param->cbr ); /* cbr */ - lsmash_bits_put( &bits, 6, 0 ); /* reserved */ - lsmash_bits_put( &bits, 1, !param->interlaced ); /* no_interlace */ - lsmash_bits_put( &bits, 1, !param->multiple_sequence ); /* no_multiple_seq */ - lsmash_bits_put( &bits, 1, !param->multiple_entry ); /* no_multiple_entry */ - lsmash_bits_put( &bits, 1, !param->slice_present ); /* no_slice_code */ - lsmash_bits_put( &bits, 1, !param->bframe_present ); /* no_bframe */ - lsmash_bits_put( &bits, 1, 0 ); /* reserved */ - lsmash_bits_put( &bits, 32, param->framerate ); /* framerate */ - /* seqhdr_ephdr[] */ - for( uint32_t i = 0; i < seqhdr->ebdu_size; i++ ) - lsmash_bits_put( &bits, 8, *(seqhdr->ebdu + i) ); - for( uint32_t i = 0; i < ephdr->ebdu_size; i++ ) - lsmash_bits_put( &bits, 8, *(ephdr->ebdu + i) ); - /* */ - uint8_t *data = lsmash_bits_export_data( &bits, data_length ); - /* Update box size. */ - data[0] = ((*data_length) >> 24) & 0xff; - data[1] = ((*data_length) >> 16) & 0xff; - data[2] = ((*data_length) >> 8) & 0xff; - data[3] = (*data_length) & 0xff; - return data; -} - -static int vc1_try_to_put_header( lsmash_vc1_header_t **p_hdr, uint8_t *multiple_hdr, void *hdr_data, uint32_t hdr_length ) -{ - lsmash_vc1_header_t *hdr = *p_hdr; - if( !hdr ) - { - hdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); - if( !hdr ) - return -1; - } - else if( hdr->ebdu ) - { - *multiple_hdr |= hdr->ebdu_size == hdr_length ? !!memcmp( hdr_data, hdr->ebdu, hdr->ebdu_size ) : 1; - return 0; - } - hdr->ebdu = lsmash_memdup( hdr_data, hdr_length ); - hdr->ebdu_size = hdr->ebdu ? hdr_length : 0; - *p_hdr = hdr; - return hdr->ebdu ? 0 : -1; -} - -int lsmash_put_vc1_header( lsmash_vc1_specific_parameters_t *param, void *hdr_data, uint32_t hdr_length ) -{ - if( !param || !hdr_data || hdr_length < 5 ) - return -1; - /* Check start code prefix (0x000001). */ - uint8_t *data = (uint8_t *)hdr_data; - if( data[0] != 0x00 || data[1] != 0x00 || data[2] != 0x01 ) - return -1; - if( data[3] == 0x0F ) /* sequence header */ - return vc1_try_to_put_header( ¶m->seqhdr, ¶m->multiple_sequence, hdr_data, hdr_length ); - else if( data[3] == 0x0E ) /* entry point header */ - return vc1_try_to_put_header( ¶m->ephdr, ¶m->multiple_entry, hdr_data, hdr_length ); - return -1; -} - -static int vc1_parse_succeeded( vc1_info_t *info, lsmash_vc1_specific_parameters_t *param ) -{ - int ret; - if( info->sequence.present && info->entry_point.present ) - { - *param = info->dvc1_param; - /* Avoid freeing headers. */ - info->dvc1_param.seqhdr = NULL; - info->dvc1_param.ephdr = NULL; - ret = 0; - } - else - ret = -1; - vc1_cleanup_parser( info ); - return ret; -} - -static inline int vc1_parse_failed( vc1_info_t *info ) -{ - vc1_cleanup_parser( info ); - return -1; -} - -int lsmash_setup_vc1_specific_parameters_from_access_unit( lsmash_vc1_specific_parameters_t *param, uint8_t *data, uint32_t data_length ) -{ - if( !param || !data || data_length == 0 ) - return -1; - vc1_info_t handler = { { 0 } }; - vc1_info_t *info = &handler; - lsmash_stream_buffers_t _sb = { LSMASH_STREAM_BUFFERS_TYPE_NONE }; - lsmash_stream_buffers_t *sb = &_sb; - lsmash_data_string_handler_t stream = { 0 }; - stream.data = data; - stream.data_length = data_length; - stream.remainder_length = data_length; - if( vc1_setup_parser( info, sb, 1, LSMASH_STREAM_BUFFERS_TYPE_DATA_STRING, &stream ) ) - return vc1_parse_failed( info ); - info->dvc1_param = *param; - vc1_stream_buffer_t *hb = &info->buffer; - uint8_t bdu_type = 0xFF; /* 0xFF is a forbidden value. */ - uint64_t consecutive_zero_byte_count = 0; - uint64_t ebdu_length = 0; - int no_more_buf = 0; - while( 1 ) - { - lsmash_stream_buffers_update( sb, 2 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - int no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !vc1_check_next_start_code_prefix( sb->pos, sb->end ) && !no_more ) - { - if( lsmash_stream_buffers_get_byte( sb ) ) - consecutive_zero_byte_count = 0; - else - ++consecutive_zero_byte_count; - ++ebdu_length; - continue; - } - if( no_more && ebdu_length == 0 ) - /* For the last EBDU. This EBDU already has been parsed. */ - return vc1_parse_succeeded( info, param ); - ebdu_length += VC1_START_CODE_LENGTH; - uint64_t next_scs_file_offset = info->ebdu_head_pos + ebdu_length + !no_more * VC1_START_CODE_PREFIX_LENGTH; - /* Memorize position of beginning of the next EBDU in buffer. - * This is used when backward reading of stream doesn't occur. */ - uint8_t *next_ebdu_pos = lsmash_stream_buffers_get_pos( sb ); - int read_back = 0; - if( bdu_type >= 0x0A && bdu_type <= 0x0F ) - { - /* Get the current EBDU here. */ - ebdu_length -= consecutive_zero_byte_count; /* Any EBDU doesn't have zero bytes at the end. */ - if( lsmash_stream_buffers_get_buffer_size( sb ) < ebdu_length ) - { - if( vc1_supplement_buffer( hb, NULL, 2 * ebdu_length ) ) - return vc1_parse_failed( info ); - next_ebdu_pos = lsmash_stream_buffers_get_pos( sb ); - } - /* Move to the first byte of the current EBDU. */ - read_back = (lsmash_stream_buffers_get_offset( sb ) < (ebdu_length + consecutive_zero_byte_count)); - if( read_back ) - { - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_data_string_copy( sb, &stream, ebdu_length, info->ebdu_head_pos ); - } - else - lsmash_stream_buffers_seek( sb, -(ebdu_length + consecutive_zero_byte_count), SEEK_CUR ); - /* Complete the current access unit if encountered delimiter of current access unit. */ - if( vc1_find_au_delimit_by_bdu_type( bdu_type, info->prev_bdu_type ) ) - /* The last video coded EBDU belongs to the access unit you want at this time. */ - return vc1_parse_succeeded( info, param ); - /* Process EBDU by its BDU type and append it to access unit. */ - switch( bdu_type ) - { - /* FRM_SC: Frame start code - * FLD_SC: Field start code - * SLC_SC: Slice start code - * SEQ_SC: Sequence header start code - * EP_SC: Entry-point start code - * PIC_L: Picture layer - * SLC_L: Slice layer - * SEQ_L: Sequence layer - * EP_L: Entry-point layer */ - case 0x0D : /* Frame - * For the Progressive or Frame Interlace mode, shall signal the beginning of a new video frame. - * For the Field Interlace mode, shall signal the beginning of a sequence of two independently coded video fields. - * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][[SLC_SC][SLC_L] (optional)] ... */ - { - vc1_picture_info_t *picture = &info->picture; - if( vc1_parse_advanced_picture( info->bits, &info->sequence, picture, hb->rbdu, - lsmash_stream_buffers_get_pos( sb ), ebdu_length ) ) - return vc1_parse_failed( info ); - info->dvc1_param.bframe_present |= picture->frame_coding_mode == 0x3 - ? picture->type >= VC1_ADVANCED_FIELD_PICTURE_TYPE_BB - : picture->type == VC1_ADVANCED_PICTURE_TYPE_B || picture->type == VC1_ADVANCED_PICTURE_TYPE_BI; - } - case 0x0C : /* Field - * Shall only be used for Field Interlaced frames - * and shall only be used to signal the beginning of the second field of the frame. - * [FRM_SC][PIC_L][FLD_SC][PIC_L][[SLC_SC][SLC_L] (optional)] ... - * Field start code is followed by INTERLACE_FIELD_PICTURE_FIELD2() which doesn't have info of its field picture type.*/ - break; - case 0x0B : /* Slice - * Shall not be used for start code of the first slice of a frame. - * Shall not be used for start code of the first slice of an interlace field coded picture. - * [FRM_SC][PIC_L][[FLD_SC][PIC_L] (optional)][SLC_SC][SLC_L][[SLC_SC][SLC_L] (optional)] ... - * Slice layer may repeat frame header. We just ignore it. */ - info->dvc1_param.slice_present = 1; - break; - case 0x0E : /* Entry-point header - * Entry-point indicates the direct followed frame is a start of group of frames. - * Entry-point doesn't indicates the frame is a random access point when multiple sequence headers are present, - * since it is necessary to decode sequence header which subsequent frames belong to for decoding them. - * Entry point shall be followed by - * 1. I-picture - progressive or frame interlace - * 2. I/I-picture, I/P-picture, or P/I-picture - field interlace - * [[SEQ_SC][SEQ_L] (optional)][EP_SC][EP_L][FRM_SC][PIC_L] ... */ - if( vc1_parse_entry_point_header( info, lsmash_stream_buffers_get_pos( sb ), ebdu_length, 1 ) ) - return vc1_parse_failed( info ); - break; - case 0x0F : /* Sequence header - * [SEQ_SC][SEQ_L][EP_SC][EP_L][FRM_SC][PIC_L] ... */ - if( vc1_parse_sequence_header( info, lsmash_stream_buffers_get_pos( sb ), ebdu_length, 1 ) ) - return vc1_parse_failed( info ); - break; - default : /* End-of-sequence (0x0A) */ - break; - } - } - /* Move to the first byte of the next start code suffix. */ - if( read_back ) - { - uint64_t consumed_data_length = LSMASH_MIN( stream.remainder_length, lsmash_stream_buffers_get_buffer_size( sb ) ); - lsmash_stream_buffers_seek( sb, 0, SEEK_SET ); - lsmash_data_string_copy( sb, &stream, consumed_data_length, next_scs_file_offset ); - } - else - lsmash_stream_buffers_set_pos( sb, next_ebdu_pos + VC1_START_CODE_PREFIX_LENGTH ); - info->prev_bdu_type = bdu_type; - lsmash_stream_buffers_update( sb, 0 ); - no_more_buf = (lsmash_stream_buffers_get_remainder( sb ) == 0); - ebdu_length = 0; - no_more = lsmash_stream_buffers_is_eos( sb ) && no_more_buf; - if( !no_more ) - { - /* Check the next BDU type. */ - if( vc1_check_next_start_code_suffix( &bdu_type, &sb->pos ) ) - return vc1_parse_failed( info ); - info->ebdu_head_pos = next_scs_file_offset - VC1_START_CODE_PREFIX_LENGTH; - } - else - return vc1_parse_succeeded( info, param ); - consecutive_zero_byte_count = 0; - } -} - -int vc1_construct_specific_parameters( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( dst && dst->data.structured && src && src->data.unstructured ); - if( src->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) - return -1; - lsmash_vc1_specific_parameters_t *param = (lsmash_vc1_specific_parameters_t *)dst->data.structured; - uint8_t *data = src->data.unstructured; - uint64_t size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - data += ISOM_BASEBOX_COMMON_SIZE; - if( size == 1 ) - { - size = ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) - | ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | ((uint64_t)data[6] << 8) | (uint64_t)data[7]; - data += 8; - } - if( size != src->size ) - return -1; - param->profile = (data[0] >> 4) & 0x0F; - if( param->profile != 12 ) - return -1; /* We don't support profile other than 12 (Advanced profile). */ - param->level = (data[0] >> 1) & 0x07; - param->cbr = (data[1] >> 4) & 0x01; - param->interlaced = !((data[2] >> 5) & 0x01); - param->multiple_sequence = !((data[2] >> 4) & 0x01); - param->multiple_entry = !((data[2] >> 3) & 0x01); - param->slice_present = !((data[2] >> 2) & 0x01); - param->bframe_present = !((data[2] >> 1) & 0x01); - param->framerate = (data[3] << 24) | (data[4] << 16) | (data[5] << 8) | data[6]; - /* Try to get seqhdr_ephdr[]. */ - if( !param->seqhdr ) - { - param->seqhdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); - if( !param->seqhdr ) - return -1; - } - if( !param->ephdr ) - { - param->ephdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); - if( !param->ephdr ) - return -1; - } - lsmash_vc1_header_t *seqhdr = param->seqhdr; - lsmash_vc1_header_t *ephdr = param->ephdr; - data += 7; - uint8_t *pos = data; - uint8_t *end = src->data.unstructured + src->size; - /* Find the start point of Sequence header EBDU. */ - while( pos < end ) - { - if( vc1_check_next_start_code_prefix( pos, end ) && (pos + 3 < end) && *(pos + 3) == 0x0F ) - { - seqhdr->ebdu_size = 4; - pos += 4; - break; - } - ++pos; - } - /* Find the end point of Sequence header EBDU. */ - while( pos < end ) - { - if( vc1_check_next_start_code_prefix( pos, end ) ) - break; - ++ seqhdr->ebdu_size; - } - /* Find the start point of Entry-point header EBDU. */ - while( pos < end ) - { - if( vc1_check_next_start_code_prefix( pos, end ) && (pos + 3 < end) && *(pos + 3) == 0x0E ) - { - ephdr->ebdu_size = 4; - pos += 4; - break; - } - ++pos; - } - /* Find the end point of Entry-point header EBDU. */ - while( pos < end ) - { - if( vc1_check_next_start_code_prefix( pos, end ) ) - break; - ++ ephdr->ebdu_size; - } - /* Append the Sequence header EBDU and Entry-point header EBDU if present. */ - if( seqhdr->ebdu_size ) - { - if( seqhdr->ebdu ) - lsmash_free( seqhdr->ebdu ); - seqhdr->ebdu = lsmash_memdup( data, seqhdr->ebdu_size ); - if( !seqhdr->ebdu ) - return -1; - } - if( ephdr->ebdu_size ) - { - if( ephdr->ebdu ) - lsmash_free( ephdr->ebdu ); - ephdr->ebdu = lsmash_memdup( data, ephdr->ebdu_size ); - if( !ephdr->ebdu ) - return -1; - } - return 0; -} - -int vc1_copy_codec_specific( lsmash_codec_specific_t *dst, lsmash_codec_specific_t *src ) -{ - assert( src && src->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && src->data.structured ); - assert( dst && dst->format == LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED && dst->data.structured ); - lsmash_vc1_specific_parameters_t *src_data = (lsmash_vc1_specific_parameters_t *)src->data.structured; - lsmash_vc1_specific_parameters_t *dst_data = (lsmash_vc1_specific_parameters_t *)dst->data.structured; - lsmash_destroy_vc1_headers( dst_data ); - *dst_data = *src_data; - if( !src_data->seqhdr && !src_data->ephdr ) - return 0; - if( src_data->seqhdr ) - { - dst_data->seqhdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); - if( !dst_data->seqhdr ) - return -1; - if( src_data->seqhdr->ebdu_size ) - { - dst_data->seqhdr->ebdu = lsmash_memdup( src_data->seqhdr->ebdu, src_data->seqhdr->ebdu_size ); - if( !dst_data->seqhdr->ebdu ) - { - lsmash_destroy_vc1_headers( dst_data ); - return -1; - } - } - dst_data->seqhdr->ebdu_size = src_data->seqhdr->ebdu_size; - } - if( src_data->ephdr ) - { - dst_data->ephdr = lsmash_malloc_zero( sizeof(lsmash_vc1_header_t) ); - if( !dst_data->ephdr ) - { - lsmash_destroy_vc1_headers( dst_data ); - return -1; - } - if( src_data->ephdr->ebdu_size ) - { - dst_data->ephdr->ebdu = lsmash_memdup( src_data->ephdr->ebdu, src_data->ephdr->ebdu_size ); - if( !dst_data->ephdr->ebdu ) - { - lsmash_destroy_vc1_headers( dst_data ); - return -1; - } - } - dst_data->ephdr->ebdu_size = src_data->ephdr->ebdu_size; - } - return 0; -} - -int vc1_print_codec_specific( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) -{ - assert( fp && file && box && (box->manager & LSMASH_BINARY_CODED_BOX) ); - int indent = level; - lsmash_ifprintf( fp, indent++, "[%s: VC1 Specific Box]\n", isom_4cc2str( box->type.fourcc ) ); - lsmash_ifprintf( fp, indent, "position = %"PRIu64"\n", box->pos ); - lsmash_ifprintf( fp, indent, "size = %"PRIu64"\n", box->size ); - if( box->size < ISOM_BASEBOX_COMMON_SIZE + 7 ) - return -1; - uint8_t *data = box->binary; - isom_skip_box_common( &data ); - uint8_t profile = (data[0] >> 4) & 0x0F; - if( profile != 12 ) - return 0; /* We don't support profile other than 12 (Advanced profile). */ - lsmash_ifprintf( fp, indent, "profile = %"PRIu8"\n", profile ); - lsmash_ifprintf( fp, indent, "level = %"PRIu8"\n", (data[0] >> 1) & 0x07 ); - lsmash_ifprintf( fp, indent, "reserved = %"PRIu8"\n", data[0] & 0x01 ); - lsmash_ifprintf( fp, indent, "level = %"PRIu8"\n", (data[1] >> 5) & 0x07 ); - lsmash_ifprintf( fp, indent, "cbr = %"PRIu8"\n", (data[1] >> 4) & 0x01 ); - lsmash_ifprintf( fp, indent, "reserved1 = 0x%02"PRIx8"\n", (data[1] & 0x0F) | ((data[2] >> 6) & 0x03) ); - lsmash_ifprintf( fp, indent, "no_interlace = %"PRIu8"\n", (data[2] >> 5) & 0x01 ); - lsmash_ifprintf( fp, indent, "no_multiple_seq = %"PRIu8"\n", (data[2] >> 4) & 0x01 ); - lsmash_ifprintf( fp, indent, "no_multiple_entry = %"PRIu8"\n", (data[2] >> 3) & 0x01 ); - lsmash_ifprintf( fp, indent, "no_slice_code = %"PRIu8"\n", (data[2] >> 2) & 0x01 ); - lsmash_ifprintf( fp, indent, "no_bframe = %"PRIu8"\n", (data[2] >> 1) & 0x01 ); - lsmash_ifprintf( fp, indent, "reserved2 = %"PRIu8"\n", data[2] & 0x01 ); - uint32_t framerate = (data[3] << 24) | (data[4] << 16) | (data[5] << 8) | data[6]; - lsmash_ifprintf( fp, indent, "framerate = %"PRIu32"\n", framerate ); - uint32_t seqhdr_ephdr_size = box->size - (data - box->binary + 7); - if( seqhdr_ephdr_size ) - { - lsmash_ifprintf( fp, indent, "seqhdr_ephdr[]\n" ); - data += 7; - for( uint32_t i = 0; i < seqhdr_ephdr_size; i += 8 ) - { - lsmash_ifprintf( fp, indent + 1, "" ); - for( uint32_t j = 0; ; j++ ) - if( j == 7 || (i + j == seqhdr_ephdr_size - 1) ) - { - fprintf( fp, "0x%02"PRIx8"\n", data[i + j] ); - break; - } - else - fprintf( fp, "0x%02"PRIx8" ", data[i + j] ); - } - } - return 0; -} diff -Nru l-smash-1.9.1/vc1.h l-smash-2.3.0/vc1.h --- l-smash-1.9.1/vc1.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/vc1.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/***************************************************************************** - * vc1.h: - ***************************************************************************** - * Copyright (C) 2012-2014 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. */ - -#define VC1_DEFAULT_BUFFER_SIZE (1<<16) -#define VC1_START_CODE_PREFIX_LENGTH 3 /* 0x000001 */ -#define VC1_START_CODE_SUFFIX_LENGTH 1 /* BDU type */ -#define VC1_START_CODE_LENGTH (VC1_START_CODE_PREFIX_LENGTH + VC1_START_CODE_SUFFIX_LENGTH) /* = 4 */ - -typedef struct -{ - uint8_t hrd_num_leaky_buckets; -} vc1_hrd_param_t; - -typedef struct -{ - uint8_t present; - uint8_t profile; - uint8_t level; - uint8_t colordiff_format; /* currently 4:2:0 only */ - uint8_t interlace; - uint8_t color_prim; - uint8_t transfer_char; - uint8_t matrix_coef; - uint8_t hrd_param_flag; - uint8_t aspect_width; - uint8_t aspect_height; - uint8_t framerate_flag; - uint32_t framerate_numerator; - uint32_t framerate_denominator; - uint16_t max_coded_width; - uint16_t max_coded_height; - uint16_t disp_horiz_size; - uint16_t disp_vert_size; - vc1_hrd_param_t hrd_param; -} vc1_sequence_header_t; - -typedef struct -{ - uint8_t present; - uint8_t closed_entry_point; -} vc1_entry_point_t; - -typedef struct -{ - uint8_t present; - uint8_t frame_coding_mode; - uint8_t type; - uint8_t closed_gop; - uint8_t start_of_sequence; - uint8_t random_accessible; -} vc1_picture_info_t; - -typedef struct -{ - uint8_t random_accessible; - uint8_t closed_gop; - uint8_t independent; - uint8_t non_bipredictive; - uint8_t disposable; - uint8_t *data; - uint32_t data_length; - uint8_t *incomplete_data; - uint32_t incomplete_data_length; - uint32_t number; -} vc1_access_unit_t; - -typedef struct vc1_info_tag vc1_info_t; - -typedef struct -{ - lsmash_stream_buffers_t *sb; - uint8_t *rbdu; -} vc1_stream_buffer_t; - -struct vc1_info_tag -{ - lsmash_vc1_specific_parameters_t dvc1_param; - vc1_sequence_header_t sequence; - vc1_entry_point_t entry_point; - vc1_picture_info_t picture; - vc1_access_unit_t access_unit; - uint8_t bdu_type; - uint8_t prev_bdu_type; - uint64_t ebdu_head_pos; - lsmash_bits_t *bits; - vc1_stream_buffer_t buffer; -}; - -int vc1_setup_parser -( - vc1_info_t *info, - lsmash_stream_buffers_t *sb, - int parse_only, - lsmash_stream_buffers_type type, - void *stream -); - -void vc1_cleanup_parser( vc1_info_t *info ); -int vc1_parse_sequence_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int probe ); -int vc1_parse_entry_point_header( vc1_info_t *info, uint8_t *ebdu, uint64_t ebdu_size, int probe ); -int vc1_parse_advanced_picture( lsmash_bits_t *bits, - vc1_sequence_header_t *sequence, vc1_picture_info_t *picture, - uint8_t *rbdu_buffer, uint8_t *ebdu, uint64_t ebdu_size ); -void vc1_update_au_property( vc1_access_unit_t *access_unit, vc1_picture_info_t *picture ); -int vc1_find_au_delimit_by_bdu_type( uint8_t bdu_type, uint8_t prev_bdu_type ); -int vc1_supplement_buffer( vc1_stream_buffer_t *buffer, vc1_access_unit_t *access_unit, uint32_t size ); - -static inline int vc1_check_next_start_code_prefix( uint8_t *buf_pos, uint8_t *buf_end ) -{ - return ((buf_pos + 2) < buf_end) && !buf_pos[0] && !buf_pos[1] && (buf_pos[2] == 0x01); -} - -static inline int vc1_check_next_start_code_suffix( uint8_t *p_bdu_type, uint8_t **p_start_code_suffix ) -{ - uint8_t bdu_type = **p_start_code_suffix; - if( (bdu_type >= 0x00 && bdu_type <= 0x09) || (bdu_type >= 0x20 && bdu_type <= 0xFF) ) - return -1; /* SMPTE reserved or forbidden value */ - *p_bdu_type = bdu_type; - ++ *p_start_code_suffix; - return 0; -} diff -Nru l-smash-1.9.1/write.c l-smash-2.3.0/write.c --- l-smash-1.9.1/write.c 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/write.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1559 +0,0 @@ -/***************************************************************************** - * write.c: - ***************************************************************************** - * Copyright (C) 2010-2014 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 - -#include "box.h" -#include "mp4a.h" -#include "mp4sys.h" -#include "write.h" -#include "description.h" - -int isom_write_box( lsmash_bs_t *bs, isom_box_t *box ); - -static int isom_write_children( lsmash_bs_t *bs, isom_box_t *box ) -{ - for( lsmash_entry_t *entry = box->extensions.head; entry; entry = entry->next ) - { - isom_box_t *child = (isom_box_t *)entry->data; - if( !child ) - continue; - int ret = isom_write_box( bs, child ); - if( ret < 0 ) - return ret; - } - return 0; -} - -static int isom_write_binary_coded_box( lsmash_bs_t *bs, isom_box_t *box ) -{ - lsmash_bs_put_bytes( bs, box->size, box->binary ); - return 0; -} - -static int isom_write_unknown_box( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_unknown_box_t *unknown_box = (isom_unknown_box_t *)box; - isom_bs_put_box_common( bs, unknown_box ); - if( unknown_box->unknown_field - && unknown_box->unknown_size ) - lsmash_bs_put_bytes( bs, unknown_box->unknown_size, unknown_box->unknown_field ); - return 0; -} - -static void isom_bs_put_qt_color_table( lsmash_bs_t *bs, isom_qt_color_table_t *color_table ) -{ - lsmash_bs_put_be32( bs, color_table->seed ); - lsmash_bs_put_be16( bs, color_table->flags ); - lsmash_bs_put_be16( bs, color_table->size ); - isom_qt_color_array_t *array = color_table->array; - if( array ) - for( uint16_t i = 0; i <= color_table->size; i++ ) - { - lsmash_bs_put_be16( bs, array[i].value ); - lsmash_bs_put_be16( bs, array[i].r ); - lsmash_bs_put_be16( bs, array[i].g ); - lsmash_bs_put_be16( bs, array[i].b ); - } -} - -static int isom_write_ctab( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_ctab_t *ctab = (isom_ctab_t *)box; - isom_bs_put_box_common( bs, ctab ); - isom_bs_put_qt_color_table( bs, &ctab->color_table ); - return 0; -} - -static int isom_write_tkhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_tkhd_t *tkhd = (isom_tkhd_t *)box; - 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, LSMASH_MIN( tkhd->creation_time, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, LSMASH_MIN( tkhd->modification_time, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, tkhd->track_ID ); - lsmash_bs_put_be32( bs, tkhd->reserved1 ); - lsmash_bs_put_be32( bs, LSMASH_MIN( tkhd->duration, UINT32_MAX ) ); - } - 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 0; -} - -static int isom_write_clef( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_clef_t *clef = (isom_clef_t *)box; - isom_bs_put_box_common( bs, clef ); - lsmash_bs_put_be32( bs, clef->width ); - lsmash_bs_put_be32( bs, clef->height ); - return 0; -} - -static int isom_write_prof( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_prof_t *prof = (isom_prof_t *)box; - isom_bs_put_box_common( bs, prof ); - lsmash_bs_put_be32( bs, prof->width ); - lsmash_bs_put_be32( bs, prof->height ); - return 0; -} - -static int isom_write_enof( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_enof_t *enof = (isom_enof_t *)box; - isom_bs_put_box_common( bs, enof ); - lsmash_bs_put_be32( bs, enof->width ); - lsmash_bs_put_be32( bs, enof->height ); - return 0; -} - -static int isom_write_tapt( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_elst( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_elst_t *elst = (isom_elst_t *)box; - assert( elst->list ); - if( elst->list->entry_count == 0 ) - return 0; - if( elst->file->fragment && !elst->file->bs->unseekable ) - elst->pos = elst->file->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, LSMASH_MIN( data->segment_duration, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, data->media_time < 0 ? (uint32_t)data->media_time : LSMASH_MIN( data->media_time, INT32_MAX ) ); - } - lsmash_bs_put_be32( bs, data->media_rate ); - } - return 0; -} - -static int isom_write_edts( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_tref( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_track_reference_type( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_tref_type_t *ref = (isom_tref_type_t *)box; - 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 0; -} - -static int isom_write_mdhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mdhd_t *mdhd = (isom_mdhd_t *)box; - 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, LSMASH_MIN( mdhd->creation_time, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, LSMASH_MIN( mdhd->modification_time, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, mdhd->timescale ); - lsmash_bs_put_be32( bs, LSMASH_MIN( mdhd->duration, UINT32_MAX ) ); - } - lsmash_bs_put_be16( bs, mdhd->language ); - lsmash_bs_put_be16( bs, mdhd->quality ); - return 0; -} - -static int isom_write_hdlr( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_hdlr_t *hdlr = (isom_hdlr_t *)box; - 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_length, hdlr->componentName ); - return 0; -} - -static int isom_write_vmhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_vmhd_t *vmhd = (isom_vmhd_t *)box; - 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 0; -} - -static int isom_write_smhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_smhd_t *smhd = (isom_smhd_t *)box; - isom_bs_put_box_common( bs, smhd ); - lsmash_bs_put_be16( bs, smhd->balance ); - lsmash_bs_put_be16( bs, smhd->reserved ); - return 0; -} - -static int isom_write_hmhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_hmhd_t *hmhd = (isom_hmhd_t *)box; - 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 0; -} - -static int isom_write_nmhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_nmhd_t *nmhd = (isom_nmhd_t *)box; - isom_bs_put_box_common( bs, nmhd ); - return 0; -} - -static int isom_write_gmin( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_gmin_t *gmin = (isom_gmin_t *)box; - 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 0; -} - -static int isom_write_text( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_text_t *text = (isom_text_t *)box; - isom_bs_put_box_common( bs, text ); - for( uint32_t i = 0; i < 9; i++ ) - lsmash_bs_put_be32( bs, text->matrix[i] ); - return 0; -} - -static int isom_write_gmhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_dref( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_dref_t *dref = (isom_dref_t *)box; - isom_bs_put_box_common( bs, dref ); - lsmash_bs_put_be32( bs, dref->list.entry_count ); - return 0; -} - -static int isom_write_url( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_dref_entry_t *url = (isom_dref_entry_t *)box; - isom_bs_put_box_common( bs, url ); - lsmash_bs_put_bytes( bs, url->location_length, url->location ); - return 0; -} - -static int isom_write_dinf( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_pasp( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_pasp_t *pasp = (isom_pasp_t *)box; - isom_bs_put_box_common( bs, pasp ); - lsmash_bs_put_be32( bs, pasp->hSpacing ); - lsmash_bs_put_be32( bs, pasp->vSpacing ); - return 0; -} - -static int isom_write_clap( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_clap_t *clap = (isom_clap_t *)box; - 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 0; -} - -static int isom_write_colr( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_colr_t *colr = (isom_colr_t *)box; - if( colr->color_parameter_type != ISOM_COLOR_PARAMETER_TYPE_NCLX - && colr->color_parameter_type != QT_COLOR_PARAMETER_TYPE_NCLC ) - 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 ); - if( colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX ) - lsmash_bs_put_byte( bs, (colr->full_range_flag << 7) | colr->reserved ); - return 0; -} - -static int isom_write_gama( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_gama_t *gama = (isom_gama_t *)box; - if( !gama->parent ) - return 0; - /* Note: 'gama' box is superseded by 'colr' box. - * Therefore, writers of QTFF should never write both 'colr' and 'gama' box into an Image Description. */ - if( isom_get_extension_box_format( &((isom_visual_entry_t *)gama->parent)->extensions, QT_BOX_TYPE_COLR ) ) - return 0; - isom_bs_put_box_common( bs, gama ); - lsmash_bs_put_be32( bs, gama->level ); - return 0; -} - -static int isom_write_fiel( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_fiel_t *fiel = (isom_fiel_t *)box; - isom_bs_put_box_common( bs, fiel ); - lsmash_bs_put_byte( bs, fiel->fields ); - lsmash_bs_put_byte( bs, fiel->detail ); - return 0; -} - -static int isom_write_cspc( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_cspc_t *cspc = (isom_cspc_t *)box; - isom_bs_put_box_common( bs, cspc ); - lsmash_bs_put_be32( bs, cspc->pixel_format ); - return 0; -} - -static int isom_write_sgbt( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_sgbt_t *sgbt = (isom_sgbt_t *)box; - isom_bs_put_box_common( bs, sgbt ); - lsmash_bs_put_byte( bs, sgbt->significantBits ); - return 0; -} - -static int isom_write_stsl( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stsl_t *stsl = (isom_stsl_t *)box; - 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 0; -} - -static int isom_write_esds( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_esds_t *esds = (isom_esds_t *)box; - isom_bs_put_box_common( bs, esds ); - return mp4sys_write_ES_Descriptor( bs, esds->ES ); -} - -static int isom_write_btrt( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_btrt_t *btrt = (isom_btrt_t *)box; - 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 0; -} - -static int isom_write_glbl( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_glbl_t *glbl = (isom_glbl_t *)box; - isom_bs_put_box_common( bs, glbl ); - if( glbl->header_data && glbl->header_size ) - lsmash_bs_put_bytes( bs, glbl->header_size, glbl->header_data ); - return 0; -} - -static int isom_write_frma( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_frma_t *frma = (isom_frma_t *)box; - isom_bs_put_box_common( bs, frma ); - lsmash_bs_put_be32( bs, frma->data_format ); - return 0; -} - -static int isom_write_enda( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_enda_t *enda = (isom_enda_t *)box; - isom_bs_put_box_common( bs, enda ); - lsmash_bs_put_be16( bs, enda->littleEndian ); - return 0; -} - -static int isom_write_mp4a( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mp4a_t *mp4a = (isom_mp4a_t *)box; - isom_bs_put_box_common( bs, mp4a ); - lsmash_bs_put_be32( bs, mp4a->unknown ); - return 0; -} - -static int isom_write_chan( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_chan_t *chan = (isom_chan_t *)box; - 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 0; -} - -static int isom_write_terminator( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_wave( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_visual_description( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_visual_entry_t *data = (isom_visual_entry_t *)box; - if( !data ) - return -1; - isom_bs_put_box_common( bs, data ); - lsmash_bs_put_bytes( bs, 6, data->reserved ); - 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, 32, data->compressorname ); - lsmash_bs_put_be16( bs, data->depth ); - lsmash_bs_put_be16( bs, data->color_table_ID ); - if( data->color_table_ID == 0 ) - isom_bs_put_qt_color_table( bs, &data->color_table ); - return 0; -} - -static int isom_write_audio_description( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_audio_entry_t *data = (isom_audio_entry_t *)box; - if( !data ) - return -1; - isom_bs_put_box_common( bs, data ); - lsmash_bs_put_bytes( bs, 6, data->reserved ); - 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 ); - } - return 0; -} - -#if 0 -static int isom_write_hint_description( 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, 6, data->reserved ); - lsmash_bs_put_be16( bs, data->data_reference_index ); - if( data->data && data->data_length ) - lsmash_bs_put_bytes( bs, data->data_length, data->data ); - return 0; -} - -static int isom_write_metadata_description( 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, 6, data->reserved ); - lsmash_bs_put_be16( bs, data->data_reference_index ); - return 0; -} -#endif - -static int isom_write_qt_text_description( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_qt_text_entry_t *data = (isom_qt_text_entry_t *)box; - if( !data ) - return -1; - isom_bs_put_box_common( bs, data ); - lsmash_bs_put_bytes( bs, 6, data->reserved ); - 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_length, data->font_name ); - return 0; -} - -static int isom_write_ftab( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_ftab_t *ftab = (isom_ftab_t *)box; - assert( ftab->list ); - 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_length, data->font_name ); - } - return 0; -} - -static int isom_write_tx3g_description( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_tx3g_entry_t *data = (isom_tx3g_entry_t *)box; - if( !data ) - return -1; - isom_bs_put_box_common( bs, data ); - lsmash_bs_put_bytes( bs, 6, data->reserved ); - 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] ); - return 0; -} - -static int isom_write_stsd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stsd_t *stsd = (isom_stsd_t *)box; - isom_bs_put_box_common( bs, stsd ); - lsmash_bs_put_be32( bs, stsd->list.entry_count ); - return 0; -} - -static int isom_write_stts( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stts_t *stts = (isom_stts_t *)box; - assert( stts->list ); - 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 0; -} - -static int isom_write_ctts( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_ctts_t *ctts = (isom_ctts_t *)box; - assert( ctts->list ); - 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 0; -} - -static int isom_write_cslg( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_cslg_t *cslg = (isom_cslg_t *)box; - 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 0; -} - -static int isom_write_stsz( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stsz_t *stsz = (isom_stsz_t *)box; - 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 0; -} - -static int isom_write_stss( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stss_t *stss = (isom_stss_t *)box; - assert( stss->list ); - 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 0; -} - -static int isom_write_stps( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stps_t *stps = (isom_stps_t *)box; - assert( stps->list ); - 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 0; -} - -static int isom_write_sdtp( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_sdtp_t *sdtp = (isom_sdtp_t *)box; - assert( sdtp->list ); - 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 0; -} - -static int isom_write_stsc( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stsc_t *stsc = (isom_stsc_t *)box; - assert( stsc->list ); - 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 0; -} - -static int isom_write_co64( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stco_t *co64 = (isom_stco_t *)box; - assert( co64->list ); - 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 0; -} - -static int isom_write_stco( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_stco_t *stco = (isom_stco_t *)box; - if( stco->large_presentation ) - return isom_write_co64( bs, box ); - assert( stco->list ); - 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 0; -} - -static int isom_write_sgpd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_sgpd_t *sgpd = (isom_sgpd_t *)box; - assert( sgpd->list ); - 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_t *)entry->data)->description_length ); - break; - } - } - return 0; -} - -static int isom_write_sbgp( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_sbgp_t *sbgp = (isom_sbgp_t *)box; - assert( sbgp->list ); - 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 0; -} - -static int isom_write_stbl( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_minf( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_mdia( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_chpl( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_chpl_t *chpl = (isom_chpl_t *)box; - assert( chpl->list ); - 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_length, data->chapter_name ); - } - return 0; -} - -static int isom_write_mean( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mean_t *mean = (isom_mean_t *)box; - isom_bs_put_box_common( bs, mean ); - if( mean->meaning_string && mean->meaning_string_length ) - lsmash_bs_put_bytes( bs, mean->meaning_string_length, mean->meaning_string ); - return 0; -} - -static int isom_write_name( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_name_t *name = (isom_name_t *)box; - isom_bs_put_box_common( bs, name ); - if( name->name && name->name_length ) - lsmash_bs_put_bytes( bs, name->name_length, name->name ); - return 0; -} - -static int isom_write_data( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_data_t *data = (isom_data_t *)box; - isom_bs_put_box_common( bs, data ); - lsmash_bs_put_be16( bs, data->reserved ); - lsmash_bs_put_byte( bs, data->type_set_identifier ); - lsmash_bs_put_byte( bs, data->type_code ); - lsmash_bs_put_be32( bs, data->the_locale ); - if( data->value && data->value_length ) - lsmash_bs_put_bytes( bs, data->value_length, data->value ); - return 0; -} - -static int isom_write_metaitem( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_ilst( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_meta( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_cprt( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_cprt_t *cprt = (isom_cprt_t *)box; - isom_bs_put_box_common( bs, cprt ); - lsmash_bs_put_be16( bs, cprt->language ); - lsmash_bs_put_bytes( bs, cprt->notice_length, cprt->notice ); - return 0; -} - -static int isom_write_udta( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_trak( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_iods( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_iods_t *iods = (isom_iods_t *)box; - isom_bs_put_box_common( bs, iods ); - return mp4sys_write_ObjectDescriptor( bs, iods->OD ); -} - -static int isom_write_mvhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mvhd_t *mvhd = (isom_mvhd_t *)box; - 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, LSMASH_MIN( mvhd->creation_time, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, LSMASH_MIN( mvhd->modification_time, UINT32_MAX ) ); - lsmash_bs_put_be32( bs, mvhd->timescale ); - lsmash_bs_put_be32( bs, LSMASH_MIN( mvhd->duration, UINT32_MAX ) ); - } - 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 0; -} - -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_box_t *box ) -{ - if( box->manager & LSMASH_PLACEHOLDER ) - { - /* Movie Extends Header Box is not written immediately. - * It's done after finishing all movie fragments. - * 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. */ - box->pos = box->file->bs->written; - lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE + 12 ); - lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE.fourcc ); - lsmash_bs_put_be32( bs, 0 ); - lsmash_bs_put_be64( bs, 0 ); - } - else - { - isom_mehd_t *mehd = (isom_mehd_t *)box; - 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 0; -} - -static int isom_write_trex( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_trex_t *trex = (isom_trex_t *)box; - 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 0; -} - -static int isom_write_mvex( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_mfhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mfhd_t *mfhd = (isom_mfhd_t *)box; - isom_bs_put_box_common( bs, mfhd ); - lsmash_bs_put_be32( bs, mfhd->sequence_number ); - return 0; -} - -static int isom_write_tfhd( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_tfhd_t *tfhd = (isom_tfhd_t *)box; - 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 0; -} - -static int isom_write_tfdt( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_tfdt_t *tfdt = (isom_tfdt_t *)box; - isom_bs_put_box_common( bs, tfdt ); - if( tfdt->version == 1 ) - lsmash_bs_put_be64( bs, tfdt->baseMediaDecodeTime ); - else - lsmash_bs_put_be32( bs, tfdt->baseMediaDecodeTime ); - return 0; -} - -static int isom_write_trun( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_trun_t *trun = (isom_trun_t *)box; - 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 0; -} - -static int isom_write_traf( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_moof( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_tfra( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_tfra_t *tfra = (isom_tfra_t *)box; - 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[ tfra->version == 1 ? 4 : 3 ]; - void (*bs_put_moof_offset) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->version == 1 ? 4 : 3 ]; - 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 0; -} - -static int isom_write_mfro( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mfro_t *mfro = (isom_mfro_t *)box; - isom_bs_put_box_common( bs, mfro ); - lsmash_bs_put_be32( bs, mfro->length ); - return 0; -} - -static int isom_write_mfra( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_mdat( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_mdat_t *mdat = (isom_mdat_t *)box; - lsmash_file_t *file = mdat->file; - /* If any fragment, write the Media Data Box all at once. */ - if( file->fragment ) - { - /* Write the size and type fields of the Media Data Box. */ - mdat->size = ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size; - if( mdat->size > UINT32_MAX ) - mdat->size += 8; /* large_size */ - isom_bs_put_box_common( bs, mdat ); - /* Write the samples in the current movie fragment. */ - for( lsmash_entry_t *entry = file->fragment->pool->head; entry; entry = entry->next ) - { - isom_sample_pool_t *pool = (isom_sample_pool_t *)entry->data; - if( !pool ) - return -1; - lsmash_bs_put_bytes( bs, pool->size, pool->data ); - } - mdat->media_size = file->fragment->pool_size; - return 0; - } - if( mdat->manager & LSMASH_PLACEHOLDER ) - { - /* Write the placeholder for large size. */ - if( !file->free && isom_add_free( file ) < 0 ) - return -1; - isom_free_t *skip = file->free; - skip->pos = bs->offset; - skip->size = ISOM_BASEBOX_COMMON_SIZE; - skip->manager |= LSMASH_PLACEHOLDER; - if( isom_write_box( bs, (isom_box_t *)skip ) < 0 ) - return -1; - /* Write an incomplete Media Data Box. */ - mdat->pos = bs->offset; - mdat->size = ISOM_BASEBOX_COMMON_SIZE; - mdat->manager |= LSMASH_INCOMPLETE_BOX; - mdat->manager &= ~LSMASH_PLACEHOLDER; - isom_bs_put_box_common( bs, mdat ); - return 0; - } - if( !bs->unseekable ) - { - /* Write the actual size. */ - uint64_t current_pos = bs->offset; - mdat->size = ISOM_BASEBOX_COMMON_SIZE + mdat->media_size; - if( mdat->size > UINT32_MAX ) - { - /* The placeholder is overwritten by the Media Data Box. */ - assert( file->free ); - mdat->pos = file->free->pos; - mdat->size += file->free->size; - isom_remove_box_by_itself( file->free ); - } - lsmash_bs_seek( bs, mdat->pos, SEEK_SET ); - isom_bs_put_box_common( bs, mdat ); - /* isom_write_box() also calls lsmash_bs_flush_buffer() but it must do nothing. */ - int ret = lsmash_bs_flush_buffer( bs ); - lsmash_bs_seek( bs, current_pos, SEEK_SET ); - return ret; - } - return -1; -} - -static int isom_write_ftyp( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_ftyp_t *ftyp = (isom_ftyp_t *)box; - if( ftyp->brand_count == 0 ) - return 0; - isom_bs_put_box_common( bs, ftyp ); - lsmash_bs_put_be32( bs, ftyp->major_brand ); - lsmash_bs_put_be32( bs, ftyp->minor_version ); - for( uint32_t i = 0; i < ftyp->brand_count; i++ ) - lsmash_bs_put_be32( bs, ftyp->compatible_brands[i] ); - return 0; -} - -static int isom_write_moov( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_bs_put_box_common( bs, box ); - return 0; -} - -static int isom_write_free( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_free_t *skip = (isom_free_t *)box; - isom_bs_put_box_common( bs, skip ); - if( skip->data && skip->length ) - lsmash_bs_put_bytes( bs, skip->length, skip->data ); - return 0; -} - -static int isom_write_sidx( lsmash_bs_t *bs, isom_box_t *box ) -{ - isom_sidx_t *sidx = (isom_sidx_t *)box; - isom_bs_put_box_common( bs, sidx ); - lsmash_bs_put_be32( bs, sidx->reference_ID ); - lsmash_bs_put_be32( bs, sidx->timescale ); - if( sidx->version == 0 ) - { - lsmash_bs_put_be32( bs, sidx->earliest_presentation_time ); - lsmash_bs_put_be32( bs, sidx->first_offset ); - } - else - { - lsmash_bs_put_be64( bs, sidx->earliest_presentation_time ); - lsmash_bs_put_be64( bs, sidx->first_offset ); - } - lsmash_bs_put_be16( bs, sidx->reserved ); - lsmash_bs_put_be16( bs, sidx->reference_count ); - for( lsmash_entry_t *entry = sidx->list->head; entry; entry = entry->next ) - { - isom_sidx_referenced_item_t *data = (isom_sidx_referenced_item_t *)entry->data; - if( !data ) - return -1; - uint32_t temp32; - temp32 = (data->reference_type << 31) - | data->reference_size; - lsmash_bs_put_be32( bs, temp32 ); - lsmash_bs_put_be32( bs, data->subsegment_duration ); - temp32 = (data->starts_with_SAP << 31) - | (data->SAP_type << 28) - | data->SAP_delta_time; - lsmash_bs_put_be32( bs, temp32 ); - } - return 0; -} - -int isom_write_box( lsmash_bs_t *bs, isom_box_t *box ) -{ - assert( bs ); - /* Don't write any incomplete or already written box to a file. */ - if( !box || !box->write - || (bs->stream && (box->manager & (LSMASH_INCOMPLETE_BOX | LSMASH_WRITTEN_BOX))) ) - return 0; - if( box->write( bs, box ) < 0 ) - return -1; - if( bs->stream ) - { - if( lsmash_bs_flush_buffer( bs ) < 0 ) - return -1; - /* Don't write any child box if this box is a placeholder or an incomplete box. */ - if( box->manager & (LSMASH_PLACEHOLDER | LSMASH_INCOMPLETE_BOX) ) - return 0; - else - box->manager |= LSMASH_WRITTEN_BOX; - } - return isom_write_children( bs, box ); -} - -void isom_set_box_writer( isom_box_t *box ) -{ - if( box->manager & LSMASH_BINARY_CODED_BOX ) - { - box->write = isom_write_binary_coded_box; - return; - } - else if( box->manager & LSMASH_UNKNOWN_BOX ) - { - box->write = isom_write_unknown_box; - return; - } - assert( box->parent ); - isom_box_t *parent = box->parent; - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STSD ) ) - { - /* Check whether CODEC is RAW Video/Audio encapsulated in QTFF. */ - if( parent->parent && parent->parent->parent ) - { - isom_minf_t *minf = (isom_minf_t *)parent->parent->parent; - if( minf->vmhd ) - box->write = isom_write_visual_description; - else if( minf->smhd ) - box->write = isom_write_audio_description; - if( box->write ) - return; - } - if( lsmash_check_box_type_identical( box->type, QT_CODEC_TYPE_TEXT_TEXT ) ) - box->write = isom_write_qt_text_description; - else if( lsmash_check_box_type_identical( box->type, ISOM_CODEC_TYPE_TX3G_TEXT ) ) - box->write = isom_write_tx3g_description; - if( box->write ) - return; - } - if( lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_WAVE ) ) - { - if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_FRMA ) ) box->write = isom_write_frma; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ENDA ) ) box->write = isom_write_enda; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_MP4A ) ) box->write = isom_write_mp4a; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_ESDS ) ) box->write = isom_write_esds; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_CHAN ) ) box->write = isom_write_chan; - else if( lsmash_check_box_type_identical( box->type, QT_BOX_TYPE_TERMINATOR ) ) box->write = isom_write_terminator; - else box->write = NULL; - return; - } - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TREF ) ) - { - box->write = isom_write_track_reference_type; - return; - } - static struct box_writer_table_tag - { - lsmash_box_type_t type; - isom_extension_writer_t writer_func; - } box_writer_table[128] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } }; - if( !box_writer_table[0].writer_func ) - { - /* Initialize the table. */ - int i = 0; -#define ADD_BOX_WRITER_TABLE_ELEMENT( type, reader_func ) \ - box_writer_table[i++] = (struct box_writer_table_tag){ type, reader_func } - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTYP, isom_write_ftyp ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STYP, isom_write_ftyp ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SIDX, isom_write_sidx ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOV, isom_write_moov ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVHD, isom_write_mvhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_IODS, isom_write_iods ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CTAB, isom_write_ctab ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, isom_write_esds ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAK, isom_write_trak ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TKHD, isom_write_tkhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_TAPT, isom_write_tapt ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CLEF, isom_write_clef ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_PROF, isom_write_prof ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_ENOF, isom_write_enof ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_EDTS, isom_write_edts ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_ELST, isom_write_elst ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREF, isom_write_tref ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDIA, isom_write_mdia ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDHD, isom_write_mdhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_HDLR, isom_write_hdlr ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MINF, isom_write_minf ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_VMHD, isom_write_vmhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SMHD, isom_write_smhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_HMHD, isom_write_hmhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_NMHD, isom_write_nmhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GMHD, isom_write_gmhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GMIN, isom_write_gmin ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_TEXT, isom_write_text ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_DINF, isom_write_dinf ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_DREF, isom_write_dref ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_URL, isom_write_url ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STBL, isom_write_stbl ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSD, isom_write_stsd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, isom_write_btrt ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_write_colr ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_write_colr ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_write_clap ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_write_pasp ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_write_glbl ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_write_gama ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_write_fiel ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_write_cspc ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_write_sgbt ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_write_stsl ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_write_wave ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_MP4A, isom_write_mp4a ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_write_chan ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_FTAB, isom_write_ftab ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STTS, isom_write_stts ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CTTS, isom_write_ctts ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CSLG, isom_write_cslg ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSS, isom_write_stss ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_STPS, isom_write_stps ); - 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_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 ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SBGP, isom_write_sbgp ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_UDTA, isom_write_udta ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CHPL, isom_write_chpl ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MVEX, isom_write_mvex ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MEHD, isom_write_mehd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TREX, isom_write_trex ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MOOF, isom_write_moof ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFHD, isom_write_mfhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRAF, isom_write_traf ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFHD, isom_write_tfhd ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFDT, isom_write_tfdt ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TRUN, isom_write_trun ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MDAT, isom_write_mdat ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_FREE, isom_write_free ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SKIP, isom_write_free ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_META, isom_write_meta ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_META, isom_write_meta ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_ILST, isom_write_ilst ); - ADD_BOX_WRITER_TABLE_ELEMENT( QT_BOX_TYPE_ILST, isom_write_ilst ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRA, isom_write_mfra ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_TFRA, isom_write_tfra ); - ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_MFRO, isom_write_mfro ); - ADD_BOX_WRITER_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL ); -#undef ADD_BOX_WRITER_TABLE_ELEMENT - } - for( int i = 0; box_writer_table[i].writer_func; i++ ) - if( lsmash_check_box_type_identical( box->type, box_writer_table[i].type ) ) - { - box->write = box_writer_table[i].writer_func; - return; - } - if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_ILST ) - || lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_ILST ) ) - { - box->write = isom_write_metaitem; - return; - } - if( parent->parent && lsmash_check_box_type_identical( parent->parent->type, ISOM_BOX_TYPE_ILST ) ) - { - if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_MEAN ) ) - box->write = isom_write_mean; - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_NAME ) ) - box->write = isom_write_name; - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_DATA ) ) - box->write = isom_write_data; - if( box->write ) - return; - } - else if( lsmash_check_box_type_identical( box->type, ISOM_BOX_TYPE_CPRT ) ) - { - /* Avoid confusing udta.cprt with ilst.cprt. */ - box->write = isom_write_cprt; - return; - } - box->write = isom_write_unknown_box; -} diff -Nru l-smash-1.9.1/write.h l-smash-2.3.0/write.h --- l-smash-1.9.1/write.h 2014-05-02 20:48:35.000000000 +0000 +++ l-smash-2.3.0/write.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/***************************************************************************** - * write.h: - ***************************************************************************** - * Copyright (C) 2011-2014 L-SMASH project - * - * Authors: Hiroki Taniura - * - * 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_WRITE_H -#define LSMASH_WRITE_H - -int isom_write_box( lsmash_bs_t *bs, isom_box_t *box ); -void isom_set_box_writer( isom_box_t *box ); - -#endif