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 = importer->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. */ importer->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 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 ) { 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 ); } 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 && 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 ); } /* 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( 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 ); } 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, 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 ); } /* 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, 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 ); } /* 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 ); /* 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 ); } /* 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 wave_importer_probe( importer_t *importer ) { wave_importer_t *wave_imp = create_wave_importer( importer ); if( !wave_imp ) return LSMASH_ERR_MEMORY_ALLOC; int err = 0; uint32_t filesize; lsmash_bs_t *bs = importer->bs; if( lsmash_bs_get_be32( bs ) != LSMASH_4CC( 'R', 'I', 'F', 'F' ) || ((filesize = lsmash_bs_get_le32( bs ) + 8) < WAVE_MIN_FILESIZE && filesize > 8) || lsmash_bs_get_be32( bs ) != LSMASH_4CC( 'W', 'A', 'V', 'E' ) ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } int fmt_chunk_present = 0; int data_chunk_present = 0; while( !bs->eob && !(fmt_chunk_present && data_chunk_present) ) { uint32_t ckID = lsmash_bs_get_be32( bs ); uint32_t ckSize = lsmash_bs_get_le32( bs ); lsmash_bs_reset_counter( bs ); switch( ckID ) { case LSMASH_4CC( 'f', 'm', 't', ' ' ) : if( ckSize < 16 ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } if( (err = wave_parse_fmt_chunk( wave_imp, bs )) < 0 ) goto fail; fmt_chunk_present = 1; break; case LSMASH_4CC( 'd', 'a', 't', 'a' ) : if( !fmt_chunk_present ) { /* The 'fmt ' chunk must be present before the 'data' chunk. */ err = LSMASH_ERR_INVALID_DATA; goto fail; } wave_imp->chunk.data_offset = lsmash_bs_get_stream_pos( bs ); wave_imp->chunk.length = ckSize; wave_imp->chunk.number = 1; wave_imp->chunk.file = importer->file; wave_imp->number_of_samples = ckSize / wave_imp->fmt.wfx.nBlockAlign; data_chunk_present = 1; break; default : break; } if( !data_chunk_present ) { /* Skip the rest of this chunk. * Note that ckData is word-aligned even if ckSize is an odd number. */ uint32_t skip_size = ckSize; if( skip_size & 1 ) skip_size++; if( skip_size > lsmash_bs_count( bs ) ) { skip_size -= lsmash_bs_count( bs ); lsmash_bs_read_seek( bs, skip_size, SEEK_CUR ); } } } if( !(fmt_chunk_present && data_chunk_present) ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } /* Make fake movie. * Treat WAVE file format as if it's QuickTime file format. */ uint32_t track_ID; lsmash_movie_parameters_t movie_param = { 0 }; lsmash_track_parameters_t track_param = { 0 }; lsmash_media_parameters_t media_param = { 0 }; importer->file->qt_compatible = 1; if( (err = lsmash_importer_make_fake_movie( importer )) < 0 || (err = lsmash_importer_make_fake_track( importer, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, &track_ID )) < 0 || (err = lsmash_get_movie_parameters( importer->root, &movie_param )) < 0 || (err = lsmash_get_track_parameters( importer->root, track_ID, &track_param )) < 0 || (err = lsmash_get_media_parameters( importer->root, track_ID, &media_param )) < 0 ) goto fail; movie_param.timescale = wave_imp->fmt.wfx.nSamplesPerSec; media_param.timescale = wave_imp->fmt.wfx.nSamplesPerSec; if( (err = lsmash_set_movie_parameters( importer->root, &movie_param )) < 0 || (err = lsmash_set_track_parameters( importer->root, track_ID, &track_param )) < 0 || (err = lsmash_set_media_parameters( importer->root, track_ID, &media_param )) < 0 ) goto fail; lsmash_audio_summary_t *summary = wave_create_summary( &wave_imp->fmt ); if( !summary || lsmash_add_sample_entry( importer->root, track_ID, summary ) != 1 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); err = LSMASH_ERR_NAMELESS; goto fail; } if( (err = lsmash_add_entry( importer->summaries, summary )) < 0 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); goto fail; } importer->info = wave_imp; importer->status = IMPORTER_OK; return 0; fail: lsmash_importer_break_fake_movie( importer ); remove_wave_importer( wave_imp ); importer->file->qt_compatible = 0; importer->info = NULL; return err; }
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. */ importer->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( importer->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 bs->error ? LSMASH_ERR_NAMELESS : 0; }