[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