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

Thomas Guillem thomas at gllm.fr
Thu Mar 12 17:44:46 CET 2020


On Thu, Mar 12, 2020, at 17:39, Remi Denis-Courmont wrote:
> 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.

It need an extra check to not bother, that is why I did it that way.
Some new specs (ADM, cf. axml chunk) are adding chunks after the DATA one. That is the main reason of this commit.



> 
> 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
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list