[vlc-devel] [PATCH 09/11] demux: wav: more robust chunk parsing

Remi Denis-Courmont remi at remlab.net
Thu Mar 12 17:39:45 CET 2020


Le 2020-03-12 16:05, Thomas Guillem a écrit :
> From the spec:
> "There are no restrictions upon the order of the chunks within a WAVE 
> file,
> with the exception that the Format chunk must precede the Data chunk."
> 
> The Wav demuxer can now parse chunks in any order. I don't check that 
> the fmt
> chunk is before the data one in order to be more resilient to broken 
> samples.

Why bother? No player will handle this... WAV was already used in Win16 
area and there's no way that such a broken authoring tool would have 
been deployed.

And this probably interferes with nonseekable playback.

> This commit will allow to add new chunks more easily, specially chunks 
> that can
> be after the 'data' one. I'm thinking about the ADM support, that need 
> 2
> chunks, 'chna' and 'axml' that is after the 'data' chunk.
> ---
>  modules/demux/wav.c | 154 +++++++++++++++++++++++++++++++-------------
>  1 file changed, 108 insertions(+), 46 deletions(-)
> 
> diff --git a/modules/demux/wav.c b/modules/demux/wav.c
> index 1349baa2555..12c39688b39 100644
> --- a/modules/demux/wav.c
> +++ b/modules/demux/wav.c
> @@ -59,6 +59,31 @@ typedef struct
>      uint8_t pi_chan_table[AOUT_CHAN_MAX];
>  } demux_sys_t;
> 
> +enum wav_chunk_id {
> +    wav_chunk_id_data,
> +    wav_chunk_id_ds64,
> +    wav_chunk_id_fmt,
> +};
> +
> +static const struct wav_chunk_id_key
> +{
> +    enum wav_chunk_id id;
> +    char key[5];
> +} wav_chunk_id_key_list[] =  {
> +    /* Alphabetical order */
> +    { wav_chunk_id_data, "data" },
> +    { wav_chunk_id_ds64, "ds64" },
> +    { wav_chunk_id_fmt,  "fmt " },
> +};
> +static const size_t wav_chunk_id_key_count = 
> ARRAY_SIZE(wav_chunk_id_key_list);
> +
> +static int
> +wav_chunk_CompareCb(const void *a, const void *b)
> +{
> +    const struct wav_chunk_id_key *id = b;
> +    return memcmp(a, id->key, 4);
> +}
> +
>  static int Demux( demux_t *p_demux )
>  {
>      demux_sys_t *p_sys = p_demux->p_sys;
> @@ -127,37 +152,42 @@ static int ChunkSkip( demux_t *p_demux, size_t 
> i_size )
>      return VLC_SUCCESS;
>  }
> 
> -static int ChunkFind( demux_t *p_demux, const char *fcc, uint32_t 
> *pi_size )
> +static int ChunkGetNext( demux_t *p_demux, enum wav_chunk_id *p_id,
> +                         uint32_t *pi_size )
>  {
> -    const uint8_t *p_peek;
> +#ifndef NDEBUG
> +    /* assert that keys are in alphabetical order */
> +    for( size_t i = 0; i < wav_chunk_id_key_count - 1; ++i )
> +        assert( strcmp( wav_chunk_id_key_list[i].key,
> +                        wav_chunk_id_key_list[i + 1].key ) < 0 );
> +#endif
> 
>      for( ;; )
>      {
> -        uint32_t i_size;
> -
> +        const uint8_t *p_peek;
>          if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
> -        {
> -            msg_Err( p_demux, "cannot peek" );
>              return VLC_EGENERIC;
> -        }
> 
> -        i_size = GetDWLE( p_peek + 4 );
> +        const struct wav_chunk_id_key *id =
> +            bsearch( p_peek, wav_chunk_id_key_list, 
> wav_chunk_id_key_count,
> +                     sizeof(*wav_chunk_id_key_list), 
> wav_chunk_CompareCb );
> +        uint32_t i_size = GetDWLE( p_peek + 4 );
> 
> -        msg_Dbg( p_demux, "chunk: fcc=`%4.4s` size=%"PRIu32, p_peek, 
> i_size );
> -
> -        if( !memcmp( p_peek, fcc, 4 ) )
> +        if( id == NULL )
>          {
> -            if( pi_size )
> -            {
> -                *pi_size = i_size;
> -            }
> -            if( vlc_stream_Read( p_demux->s, NULL, 8 ) != 8 )
> -                return VLC_EGENERIC;
> -            return VLC_SUCCESS;
> +            msg_Warn( p_demux, "unknown chunk '%4.4s' of size: %u",
> +                      p_peek, i_size );
> +            ChunkSkip( p_demux, i_size + 8 );
> +            continue;
>          }
> 
> -        if( ChunkSkip( p_demux, i_size + 8 ) != VLC_SUCCESS )
> +        if( vlc_stream_Read( p_demux->s, NULL, 8 ) != 8 )
>              return VLC_EGENERIC;
> +
> +        *p_id = id->id;
> +        *pi_size = i_size;
> +
> +        return VLC_SUCCESS;
>      }
>  }
> 
> @@ -550,7 +580,7 @@ static int Open( vlc_object_t * p_this )
> 
>      es_format_Init( &p_sys->fmt, AUDIO_ES, 0 );
>      p_sys->p_es           = NULL;
> -    p_sys->i_data_size    = 0;
> +    p_sys->i_data_pos = p_sys->i_data_size = 0;
>      p_sys->i_chans_to_reorder = 0;
>      p_sys->i_channel_mask = 0;
> 
> @@ -558,42 +588,75 @@ static int Open( vlc_object_t * p_this )
>      if( vlc_stream_Read( p_demux->s, NULL, 12 ) != 12 )
>          goto error;
> 
> -    if( b_is_rf64 )
> +    bool eof = false;
> +    enum wav_chunk_id id;
> +    while( !eof && ( ChunkGetNext( p_demux, &id, &i_size ) ) == 
> VLC_SUCCESS )
>      {
> -        /* search datasize64 chunk */
> -        if( ChunkFind( p_demux, "ds64", &i_size ) )
> +        if( i_size == 0 )
>          {
> -            msg_Err( p_demux, "cannot find 'ds64' chunk" );
> +            msg_Err( p_demux, "invalid chunk with a size 0");
>              goto error;
>          }
> -        if( ChunkParseDS64( p_demux, i_size ) != VLC_SUCCESS )
> -            goto error;
> -    }
> 
> -    /* search fmt chunk */
> -    if( ChunkFind( p_demux, "fmt ", &i_size ) )
> -    {
> -        msg_Err( p_demux, "cannot find 'fmt ' chunk" );
> -        goto error;
> +        switch( id )
> +        {
> +            case wav_chunk_id_data:
> +            {
> +                int64_t i_stream_size = stream_Size( p_demux->s );
> +                p_sys->i_data_pos = vlc_stream_Tell( p_demux->s );
> +
> +                if( !b_is_rf64 || i_size < UINT32_MAX )
> +                {
> +                    if( i_stream_size > 0
> +                     && (uint64_t) i_stream_size >= i_size +
> p_sys->i_data_pos )
> +                        p_sys->i_data_size = i_size;
> +                }
> +                if( likely( p_sys->i_data_pos + i_size == (uint64_t)
> i_stream_size ) )
> +                {
> +                    /* Bypass the final ChunkGetNext() to avoid a 
> read+seek
> +                     * since this chunk is the last one */
> +                    eof = true;
> +                }
> +                else
> +                {
> +                    /* Unlikely case where there is a chunk after 
> 'data' */
> +                    ChunkSkip( p_demux, i_size );
> +                }
> +                break;
> +            }
> +            case wav_chunk_id_ds64:
> +                if( b_is_rf64 )
> +                {
> +                    if( ChunkParseDS64( p_demux, i_size ) != 
> VLC_SUCCESS )
> +                        goto error;
> +                }
> +                else
> +                {
> +                    msg_Err( p_demux, "'ds64' chunk found but format
> not RF64" );
> +                    goto error;
> +                }
> +                break;
> +            case wav_chunk_id_fmt:
> +                if( ChunkParseFmt( p_demux, i_size ) != VLC_SUCCESS )
> +                    goto error;
> +                break;
> +        }
>      }
> -    if( ChunkParseFmt( p_demux, i_size ) != VLC_SUCCESS )
> -        goto error;
> 
> -    if( ChunkFind( p_demux, "data", &i_size ) )
> +    if( p_sys->i_data_pos == 0 || p_sys->i_data_size == 0
> +     || p_sys->i_frame_samples <= 0 )
>      {
> -        msg_Err( p_demux, "cannot find 'data' chunk" );
> +        msg_Err( p_demux, "'%s' chunk not found",
> +                 p_sys->i_data_pos == 0 ? "data" :
> +                 p_sys->i_frame_samples <= 0 ? "fmt " :
> +                 b_is_rf64 ? "ds64" : "data" );
>          goto error;
>      }
> 
> -    p_sys->i_data_pos = vlc_stream_Tell( p_demux->s );
> -
> -    if( !b_is_rf64 || i_size < UINT32_MAX )
> -    {
> -        int64_t i_stream_size = stream_Size( p_demux->s );
> -        if( i_stream_size > 0
> -         && (uint64_t) i_stream_size >= i_size + p_sys->i_data_pos )
> -            p_sys->i_data_size = i_size;
> -    }
> +    /* Seek back to data position if needed */
> +    if( unlikely( vlc_stream_Tell( p_demux->s ) != p_sys->i_data_pos )
> +     && vlc_stream_Seek( p_demux->s, p_sys->i_data_pos ) != 
> VLC_SUCCESS )
> +        goto error;
> 
>      if( p_sys->fmt.i_bitrate <= 0 )
>      {
> @@ -612,7 +675,6 @@ static int Open( vlc_object_t * p_this )
>      return VLC_SUCCESS;
> 
>  error:
> -    msg_Err( p_demux, "An error occurred during wav demuxing" );
>      es_format_Clean( &p_sys->fmt );
>      free( p_sys );
>      return VLC_EGENERIC;

-- 
Rémi Denis-Courmont


More information about the vlc-devel mailing list