Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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 );
    }
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;
}