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; }
/* 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; }
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; }
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) && !isom_movie_create( file ) ) goto fail; } if( !root->file ) root->file = file; return file; fail: isom_remove_box_by_itself( file ); return NULL; }
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; importer->status = IMPORTER_OK; int err; while( importer->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; vc1_importer_check_eof( importer, &info->access_unit ); /* 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; }