Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/demux/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 26 kB image not shown  

Quelle  flac.c   Sprache: C

 

/*****************************************************************************
 * flac.c : FLAC demux module for vlc
 *****************************************************************************
 * Copyright (C) 2001-2008 VLC authors and VideoLAN
 *
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
 *          Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include <vlc_meta.h>                 /* vlc_meta_* */
#include <vlc_input.h>                /* vlc_input_attachment, vlc_seekpoint */
#include <vlc_codec.h>                /* decoder_t */
#include <vlc_charset.h>              /* EnsureUTF8 */
#include <vlc_replay_gain.h>

#include <assert.h>
#include <limits.h>
#include "xiph_metadata.h"            /* vorbis comments */
#include "../packetizer/flac.h"

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

static int  Open  ( vlc_object_t * );
static void Close ( vlc_object_t * );

vlc_module_begin ()
    set_description( N_("FLAC demuxer") )
    set_capability( "demux", 155 )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_callbacks( Open, Close )
    add_shortcut( "flac" )
    add_file_extension("flac")
vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );

static int  ParseHeaders( demux_t *, es_format_t * );

typedef struct
{
    vlc_tick_t  i_time_offset;
    uint64_t i_byte_offset;
} flac_seekpoint_t;

typedef struct
{
    bool  b_start;
    int   i_next_block_flags;
    es_out_id_t *p_es;
    block_t *p_current_block;

    /* Packetizer */
    decoder_t *p_packetizer;

    vlc_meta_t *p_meta;

    vlc_tick_t i_pts;
    struct flac_stream_info stream_info;
    bool b_stream_info;

    vlc_tick_t i_length; /* Length from stream info */
    uint64_t i_data_pos;

    /* */
    int         i_seekpoint;
    flac_seekpoint_t **seekpoint;

    /* title/chapters seekpoints */
    int           i_title_seekpoints;
    seekpoint_t **pp_title_seekpoints;

    /* */
    int                i_attachments;
    input_attachment_t **attachments;
    int                i_cover_idx;
    int                i_cover_score;
} demux_sys_t;

#define FLAC_PACKET_SIZE 16384
#define FLAC_MAX_PREROLL      VLC_TICK_FROM_SEC(4)
#define FLAC_MAX_SLOW_PREROLL VLC_TICK_FROM_SEC(45)

/*****************************************************************************
 * Open: initializes ES structures
 *****************************************************************************/

static int Open( vlc_object_t * p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
    const uint8_t *p_peek;
    es_format_t fmt;

    /* Have a peep at the show. */
    if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC;

    if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' )
    {
        if( !p_demux->obj.force
         && !demux_IsContentType( p_demux, "audio/flac" ) )
            return VLC_EGENERIC;

        /* User forced */
        msg_Err( p_demux, "this doesn't look like a flac stream, "
                 "continuing anyway" );
    }

    p_sys = malloc( sizeof( demux_sys_t ) );
    if( unlikely(p_sys == NULL) )
        return VLC_ENOMEM;

    p_demux->pf_demux   = Demux;
    p_demux->pf_control = Control;
    p_demux->p_sys      = p_sys;
    p_sys->b_start = true;
    p_sys->i_next_block_flags = 0;
    p_sys->p_packetizer = NULL;
    p_sys->p_meta = NULL;
    p_sys->i_length = 0;
    p_sys->i_pts = VLC_TICK_INVALID;
    p_sys->b_stream_info = false;
    p_sys->p_es = NULL;
    p_sys->p_current_block = NULL;
    TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint );
    TAB_INIT( p_sys->i_attachments, p_sys->attachments);
    TAB_INIT( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints );
    p_sys->i_cover_idx = 0;
    p_sys->i_cover_score = 0;

    es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_FLAC );
    fmt.i_id = 0;

    /* We need to read and store the STREAMINFO metadata into fmt extra */
    if( ParseHeaders( p_demux, &fmt ) )
        goto error;

    /* Load the FLAC packetizer */
    p_sys->p_packetizer = demux_PacketizerNew( VLC_OBJECT(p_demux), &fmt, "flac" );
    if( !p_sys->p_packetizer )
        goto error;

    if( p_sys->i_cover_idx < p_sys->i_attachments )
    {
        char psz_url[128];
        if( !p_sys->p_meta )
            p_sys->p_meta = vlc_meta_New();
        snprintf( psz_url, sizeof(psz_url), "attachment://%s",
                  p_sys->attachments[p_sys->i_cover_idx]->psz_name );
        vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url );
    }

    p_sys->p_es = es_out_Add( p_demux->out, &fmt );
    if( !p_sys->p_es )
        goto error;

    return VLC_SUCCESS;
error:
    Close( p_this );
    return VLC_EGENERIC;
}

/*****************************************************************************
 * Close: frees unused data
 *****************************************************************************/

static void Close( vlc_object_t * p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;

    if( p_sys->p_current_block )
        block_Release( p_sys->p_current_block );

    for( int i = 0; i < p_sys->i_seekpoint; i++ )
        free(p_sys->seekpoint[i]);
    TAB_CLEAN( p_sys->i_seekpoint, p_sys->seekpoint );

    for( int i = 0; i < p_sys->i_attachments; i++ )
        vlc_input_attachment_Release( p_sys->attachments[i] );
    TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);

    for( int i = 0; i < p_sys->i_title_seekpoints; i++ )
        vlc_seekpoint_Delete( p_sys->pp_title_seekpoints[i] );
    TAB_CLEAN( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints );

    /* Delete the decoder */
    if( p_sys->p_packetizer )
        demux_PacketizerDestroy( p_sys->p_packetizer );

    if( p_sys->p_meta )
        vlc_meta_Delete( p_sys->p_meta );
    free( p_sys );
}

static block_t *GetPacketizedBlock( decoder_t *p_packetizer,
                                    const struct flac_stream_info *streaminfo,
                                    block_t **pp_current_block )
{
    block_t *p_block = p_packetizer->pf_packetize( p_packetizer, pp_current_block );
    if( p_block )
    {
        if( p_block->i_buffer >= FLAC_HEADER_SIZE_MIN && p_block->i_buffer < INT_MAX )
        {
            struct flac_header_info headerinfo = { .i_pts = VLC_TICK_INVALID };
            int i_ret = FLAC_ParseSyncInfo( p_block->p_buffer, p_block->i_buffer,
                                            streaminfo, NULL, &headerinfo );
            assert( i_ret != 0 ); /* Same as packetizer */
            /* Use Frame PTS, not the interpolated one */
            p_block->i_dts = p_block->i_pts = headerinfo.i_pts;
        }
    }
    return p_block;
}

static void FlushPacketizer( decoder_t *p_packetizer )
{
    if( p_packetizer->pf_flush )
        p_packetizer->pf_flush( p_packetizer );
    else
    {
        block_t *p_block_out;
        while( (p_block_out = p_packetizer->pf_packetize( p_packetizer, NULL )) )
            block_Release( p_block_out );
    }
}

static void Reset( demux_sys_t *p_sys )
{
    p_sys->i_pts = VLC_TICK_INVALID;

    FlushPacketizer( p_sys->p_packetizer );
    if( p_sys->p_current_block )
    {
        block_Release( p_sys->p_current_block );
        p_sys->p_current_block = NULL;
    }
}

static int RefineSeek( demux_t *p_demux, vlc_tick_t i_time, double i_bytemicrorate,
                       uint64_t i_lowpos, uint64_t i_highpos )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    bool b_found = false;
    block_t *p_block_out;
    block_t *p_block_in;

    unsigned i_frame_size = FLAC_FRAME_SIZE_MIN;

    bool b_canfastseek = false;
    vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_canfastseek );

    uint64_t i_start_pos = vlc_stream_Tell( p_demux->s );

    while( !b_found )
    {
        FlushPacketizer( p_sys->p_packetizer );

        p_block_out = NULL;
        p_block_in = NULL;

        while( !p_block_out )
        {
            if( !p_block_in )
            {
                if( !(p_block_in = vlc_stream_Block( p_demux->s, i_frame_size )) )
                    break;
            }

            p_block_out = GetPacketizedBlock( p_sys->p_packetizer,
                                              p_sys->b_stream_info ? &p_sys->stream_info : NULL,
                                             &p_block_in );
        }

        if( !p_block_out )
        {
            if( p_block_in )
                block_Release( p_block_in );
            break;
        }

        if( p_block_out->i_buffer > i_frame_size )
            i_frame_size = p_block_out->i_buffer;

        /* If we are further than wanted block */
        if( p_block_out->i_dts >= i_time )
        {
            vlc_tick_t i_diff = p_block_out->i_dts - i_time;
            /* Not in acceptable approximation range */
            if( i_diff > VLC_TICK_FROM_MS(100) && i_diff / i_bytemicrorate > i_frame_size )
            {
                i_highpos = i_start_pos;
                i_start_pos -= ( i_diff / i_bytemicrorate );
                i_start_pos = __MAX(i_start_pos, i_lowpos + i_frame_size);
            }
            else b_found = true;
        }
        else
        {
            vlc_tick_t i_diff = i_time - p_block_out->i_dts;
            /* Not in acceptable NEXT_TIME demux range */
            if( i_diff >= ((b_canfastseek) ? FLAC_MAX_PREROLL : FLAC_MAX_SLOW_PREROLL) &&
                i_diff / i_bytemicrorate > i_frame_size )
            {
                i_lowpos = i_start_pos;
                i_start_pos += ( i_diff / i_bytemicrorate );
                i_start_pos = __MIN(i_start_pos, i_highpos - i_frame_size);
            }
            else b_found = true;
        }

        if( p_block_out )
            block_Release( p_block_out );
        if( p_block_in )
            block_Release( p_block_in );

        if( !b_found )
        {
            if( i_highpos < i_lowpos || i_highpos - i_lowpos <= i_frame_size )
                break;

            if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) )
                break;
        }
    }

    return b_found ? VLC_SUCCESS : VLC_EGENERIC;
}

/*****************************************************************************
 * Demux: reads and demuxes data packets
 *****************************************************************************
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
 *****************************************************************************/

static int Demux( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    block_t *p_block_out;

    if( p_sys->p_current_block == NULL )
        p_sys->p_current_block = vlc_stream_Block( p_demux->s, FLAC_PACKET_SIZE );

    bool b_eof = (p_sys->p_current_block == NULL);

    if ( !b_eof )
    {
        p_sys->p_current_block->i_flags = p_sys->i_next_block_flags;
        p_sys->i_next_block_flags = 0;
        p_sys->p_current_block->i_pts =
        p_sys->p_current_block->i_dts = p_sys->b_start ? VLC_TICK_0 : VLC_TICK_INVALID;
    }

    while( (p_block_out = GetPacketizedBlock( p_sys->p_packetizer,
                            p_sys->b_stream_info ? &p_sys->stream_info : NULL,
                            p_sys->p_current_block ? &p_sys->p_current_block : NULL ) ) )
    {
        /* Only clear on output when packet is accepted as sync #17111 */
        p_sys->b_start = false;
        while( p_block_out )
        {
            block_t *p_next = p_block_out->p_next;

            p_block_out->p_next = NULL;

            /* set PCR */
            if( unlikely(p_sys->i_pts == VLC_TICK_INVALID) )
                es_out_SetPCR( p_demux->out, __MAX(p_block_out->i_dts - 1, VLC_TICK_0) );

            if(p_block_out->i_dts != VLC_TICK_INVALID)
                p_sys->i_pts = p_block_out->i_dts;

            es_out_Send( p_demux->out, p_sys->p_es, p_block_out );

            es_out_SetPCR( p_demux->out, p_sys->i_pts );

            p_block_out = p_next;
        }
        break;
    }

    return b_eof ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
}

/*****************************************************************************
 * Control:
 *****************************************************************************/

static vlc_tick_t ControlGetLength( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    const uint64_t i_size = stream_Size(p_demux->s) - p_sys->i_data_pos;
    vlc_tick_t i_length = p_sys->i_length;
    int i;

    /* Try to fix length using seekpoint and current size for truncated file */
    for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
    {
        flac_seekpoint_t *s = p_sys->seekpoint[i];
        if( s->i_byte_offset <= i_size )
        {
            if( i+1 < p_sys->i_seekpoint )
            {
                /* Broken file */
                flac_seekpoint_t *n = p_sys->seekpoint[i+1];
                assert( n->i_byte_offset != s->i_byte_offset); /* Should be ensured by ParseSeekTable */
                i_length = s->i_time_offset + (n->i_time_offset-s->i_time_offset) * (i_size-s->i_byte_offset) / (n->i_byte_offset-s->i_byte_offset);
            }
            break;
        }
    }
    return i_length;
}

static vlc_tick_t ControlGetTime( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    return p_sys->i_pts;
}

static int ControlSetTime( demux_t *p_demux, vlc_tick_t i_time )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    bool b_seekable;
    int i;

    /* */
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
    if( !b_seekable )
        return VLC_EGENERIC;

    const vlc_tick_t i_length = ControlGetLength( p_demux );
    if( i_length <= 0 )
        return VLC_EGENERIC;

    const uint64_t i_stream_size = stream_Size( p_demux->s );
    if( i_stream_size <= p_sys->i_data_pos )
        return VLC_EGENERIC;

    const double i_bytemicrorate = (double) i_length / (i_stream_size - p_sys->i_data_pos);
    if( i_bytemicrorate == 0 )
        return VLC_EGENERIC;

    uint64_t i_lower = p_sys->i_data_pos;
    uint64_t i_upper = i_stream_size;
    uint64_t i_start_pos;

    assert( p_sys->i_seekpoint > 0 );   /* ReadMeta ensure at least (0,0) */
    if( p_sys->i_seekpoint > 1 )
    {
        /* lookup base offset */
        for( i = p_sys->i_seekpoint-1; i >= 0; i-- )
        {
            if( p_sys->seekpoint[i]->i_time_offset <= i_time )
                break;
        }

        i_lower = p_sys->seekpoint[0]->i_byte_offset + p_sys->i_data_pos;
        if( i+1 < p_sys->i_seekpoint )
            i_upper = p_sys->seekpoint[i+1]->i_byte_offset + p_sys->i_data_pos;

        i_start_pos = i_lower;
    }
    else
    {
        i_start_pos = i_time / i_bytemicrorate;
    }

    if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) )
        return VLC_EGENERIC;

    int i_ret = RefineSeek( p_demux, i_time, i_bytemicrorate, i_lower, i_upper );
    if( i_ret == VLC_SUCCESS )
    {
        p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
        Reset( p_sys );
        es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_time );
    }

    return i_ret;
}

static int Control( demux_t *p_demux, int i_query, va_list args )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if( i_query == DEMUX_GET_META )
    {
        vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * );
        if( p_sys->p_meta )
            vlc_meta_Merge( p_meta, p_sys->p_meta );
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_HAS_UNSUPPORTED_META )
    {
        bool *pb_bool = va_arg( args, bool* );
        *pb_bool = true;
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_GET_LENGTH )
    {
        *va_arg( args, vlc_tick_t * ) = ControlGetLength( p_demux );
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_SET_TIME )
    {
        return ControlSetTime( p_demux, va_arg( args, vlc_tick_t ) );
    }
    else if( i_query == DEMUX_SET_POSITION )
    {
        const double f = va_arg( args, double );
        vlc_tick_t i_length = ControlGetLength( p_demux );
        int i_ret;
        if( i_length > 0 )
        {
            i_ret = ControlSetTime( p_demux, i_length * f );
            if( i_ret == VLC_SUCCESS )
                return i_ret;
        }
        /* just byte pos seek */
        i_ret = vlc_stream_Seek( p_demux->s, (int64_t) (f * stream_Size( p_demux->s )) );
        if( i_ret == VLC_SUCCESS )
        {
            p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
            Reset( p_sys );
        }
        return i_ret;
    }
    else if( i_query == DEMUX_GET_TIME )
    {
        *va_arg( args, vlc_tick_t * ) = ControlGetTime( p_demux );
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_GET_POSITION )
    {
        const vlc_tick_t i_length = ControlGetLength(p_demux);
        if( i_length > 0 )
        {
            vlc_tick_t current = ControlGetTime(p_demux);
            if( current <= i_length )
            {
                *(va_arg( args, double * )) = (double)current / (double)i_length;
                return VLC_SUCCESS;
            }
        }
        /* Else fallback on byte position */
    }
    else if( i_query == DEMUX_GET_ATTACHMENTS )
    {
        input_attachment_t ***ppp_attach =
            va_arg( args, input_attachment_t *** );
        int *pi_int = va_arg( args, int * );

        if( p_sys->i_attachments <= 0 )
            return VLC_EGENERIC;

        *ppp_attach = vlc_alloc( p_sys->i_attachments, sizeof(input_attachment_t*) );
        if( !*ppp_attach )
            return VLC_EGENERIC;
        *pi_int = p_sys->i_attachments;
        for( int i = 0; i < p_sys->i_attachments; i++ )
            (*ppp_attach)[i] = vlc_input_attachment_Hold( p_sys->attachments[i] );
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_GET_TITLE_INFO )
    {
        input_title_t ***ppp_title = va_arg( args, input_title_t *** );
        int *pi_int = va_arg( args, int * );
        int *pi_title_offset = va_arg( args, int * );
        int *pi_seekpoint_offset = va_arg( args, int * );

        if( !p_sys->i_title_seekpoints )
            return VLC_EGENERIC;

        *pi_int = 1;
        *ppp_title = malloc( sizeof(input_title_t*) );
        if(!*ppp_title)
            return VLC_EGENERIC;

        input_title_t *p_title = (*ppp_title)[0] = vlc_input_title_New();
        if(!p_title)
        {
            free(*ppp_title);
            return VLC_EGENERIC;
        }

        p_title->seekpoint = vlc_alloc( p_sys->i_title_seekpoints, sizeof(seekpoint_t*) );
        if(!p_title->seekpoint)
        {
            vlc_input_title_Delete(p_title);
            free(*ppp_title);
            return VLC_EGENERIC;
        }

        p_title->i_seekpoint = p_sys->i_title_seekpoints;
        for( int i = 0; i < p_title->i_seekpoint; i++ )
            p_title->seekpoint[i] = vlc_seekpoint_Duplicate( p_sys->pp_title_seekpoints[i] );

        *pi_title_offset = 0;
        *pi_seekpoint_offset = 0;

        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_SET_TITLE )
    {
        const int i_title = va_arg( args, int );
        if( i_title != 0 )
            return VLC_EGENERIC;
        return VLC_SUCCESS;
    }
    else if( i_query == DEMUX_SET_SEEKPOINT )
    {
        const int i_seekpoint = va_arg( args, int );
        if( !p_sys->i_title_seekpoints || i_seekpoint >= p_sys->i_title_seekpoints )
            return VLC_EGENERIC;
        return ControlSetTime( p_demux, p_sys->pp_title_seekpoints[i_seekpoint]->i_time_offset );
    }

    return demux_vaControlHelper( p_demux->s, p_sys->i_data_pos, -1,
                                   8*0, 1, i_query, args );
}

enum
{
    META_STREAMINFO = 0,
    META_SEEKTABLE = 3,
    META_COMMENT = 4,
    META_PICTURE = 6,
};

static inline int Get24bBE( const uint8_t *p )
{
    return (p[0] << 16)|(p[1] << 8)|(p[2]);
}

static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, size_t i_data,
                            unsigned i_sample_rate );
static void ParseComment( demux_t *, const uint8_t *p_data, size_t i_data );
static void ParsePicture( demux_t *, const uint8_t *p_data, size_t i_data );

static int  ParseHeaders( demux_t *p_demux, es_format_t *p_fmt )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    ssize_t i_peek;
    const uint8_t *p_peek;
    bool b_last;

    /* Be sure we have seekpoint 0 */
    flac_seekpoint_t *s = xmalloc( sizeof (*s) );
    s->i_time_offset = 0;
    s->i_byte_offset = 0;
    TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );

    uint8_t header[4];
    if( vlc_stream_Read( p_demux->s, header, 4) < 4)
        return VLC_EGENERIC;

    if (memcmp(header, "fLaC", 4))
        return VLC_EGENERIC;

    b_last = 0;
    while( !b_last )
    {
        int i_len;
        int i_type;

        i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4 );
        if( i_peek < 4 )
            break;
        b_last = p_peek[0]&0x80;
        i_type = p_peek[0]&0x7f;
        i_len  = Get24bBE( &p_peek[1] );

        if( i_type == META_STREAMINFO && p_fmt->p_extra == NULL )
        {
            if( i_len != FLAC_STREAMINFO_SIZE ) {
                msg_Err( p_demux, "invalid size %d for a STREAMINFO metadata block", i_len );
                return VLC_EGENERIC;
            }

            p_fmt->p_extra = malloc( FLAC_STREAMINFO_SIZE );
            if( p_fmt->p_extra == NULL )
                return VLC_EGENERIC;

            if( vlc_stream_Read( p_demux->s, NULL, 4 ) != 4 )
            {
                FREENULL( p_fmt->p_extra );
                return VLC_EGENERIC;
            }
            if( vlc_stream_Read( p_demux->s, p_fmt->p_extra,
                                 FLAC_STREAMINFO_SIZE ) != FLAC_STREAMINFO_SIZE )
            {
                msg_Err( p_demux, "failed to read STREAMINFO metadata block" );
                FREENULL( p_fmt->p_extra );
                return VLC_EGENERIC;
            }
            p_fmt->i_extra = FLAC_STREAMINFO_SIZE;

            /* */
            p_sys->b_stream_info = true;
            FLAC_ParseStreamInfo( (uint8_t *) p_fmt->p_extra, &p_sys->stream_info );

            p_fmt->audio.i_rate = p_sys->stream_info.sample_rate;
            p_fmt->audio.i_channels = p_sys->stream_info.channels;
            p_fmt->audio.i_bitspersample = p_sys->stream_info.bits_per_sample;
            if( p_sys->stream_info.sample_rate > 0 )
                p_sys->i_length = vlc_tick_from_samples(p_sys->stream_info.total_samples,
                                  p_sys->stream_info.sample_rate);

            continue;
        }
        else if( i_type == META_SEEKTABLE )
        {
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
            if( i_peek == 4+i_len )
                ParseSeekTable( p_demux, p_peek, i_peek, p_fmt->audio.i_rate );
        }
        else if( i_type == META_COMMENT )
        {
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
            if( i_peek == 4+i_len )
                ParseComment( p_demux, p_peek, i_peek );

            vlc_replay_gain_CopyFromMeta( &p_fmt->audio_replay_gain, p_sys->p_meta );
        }
        else if( i_type == META_PICTURE )
        {
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
            if( i_peek == 4+i_len )
                ParsePicture( p_demux, p_peek, i_peek );
        }

        if( vlc_stream_Read( p_demux->s, NULL, 4+i_len ) != (4+i_len) )
            break;
    }

    /* */
    p_sys->i_data_pos = vlc_stream_Tell( p_demux->s );

    if ( p_fmt->p_extra == NULL )
        return VLC_EGENERIC;

    return VLC_SUCCESS;
}

static void ParseSeekTable( demux_t *p_demux, const uint8_t *p_data, size_t i_data,
                            unsigned i_sample_rate )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    flac_seekpoint_t *s;
    size_t i;

    if( i_sample_rate == 0 )
        return;

    /* */
    for( i = 0; i < (i_data-4)/18; i++ )
    {
        const int64_t i_sample = GetQWBE( &p_data[4+18*i+0] );
        int j;

        if( i_sample < 0 || i_sample >= INT64_MAX ||
            GetQWBE( &p_data[4+18*i+8] ) < FLAC_STREAMINFO_SIZE )
            break;

        s = xmalloc( sizeof (*s) );
        s->i_time_offset = vlc_tick_from_samples(i_sample, i_sample_rate);
        s->i_byte_offset = GetQWBE( &p_data[4+18*i+8] );

        /* Check for duplicate entry */
        for( j = 0; j < p_sys->i_seekpoint; j++ )
        {
            if( p_sys->seekpoint[j]->i_time_offset == s->i_time_offset ||
                p_sys->seekpoint[j]->i_byte_offset == s->i_byte_offset )
            {
                free( s );
                s = NULL;
                break;
            }
        }
        if( s )
        {
            TAB_APPEND( p_sys->i_seekpoint, p_sys->seekpoint, s );
        }
    }
    /* TODO sort it by size and remove wrong seek entry (time not increasing) */
}

static void ParseComment( demux_t *p_demux, const uint8_t *p_data, size_t i_data )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if( i_data < 4 )
        return;

    vorbis_ParseComment( NULL, &p_sys->p_meta, &p_data[4], i_data - 4,
        &p_sys->i_attachments, &p_sys->attachments,
        &p_sys->i_cover_score, &p_sys->i_cover_idx,
        &p_sys->i_title_seekpoints, &p_sys->pp_title_seekpoints );
}

static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, size_t i_data )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    i_data -= 4; p_data += 4;

    input_attachment_t *p_attachment = ParseFlacPicture( p_data, i_data,
        p_sys->i_attachments, &p_sys->i_cover_score, &p_sys->i_cover_idx );
    if( p_attachment == NULL )
        return;

    TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment );
}

Messung
CHZG
829710093

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.