Add FLAC__stream_decoder_find_total_samples (#758)
This commit is contained in:
parent
ecbac1ff3e
commit
17811b3af6
@ -995,6 +995,32 @@ FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDeco
|
||||
*/
|
||||
FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder);
|
||||
|
||||
/** Seek to the end of the file being decoded to find the total number
|
||||
* of samples. This will return a number of samples even if
|
||||
* FLAC__stream_decoder_get_total_samples() returns 0. It can also
|
||||
* be used to find the total number of samples of a chained stream,
|
||||
* as it returns the total number of samples in all chain links
|
||||
* combined. See FLAC__stream_decoder_set_decode_chained_stream()
|
||||
*
|
||||
* For this function to work, the stream must be seekable. Also, as
|
||||
* seeking can fail, this function returns 0 when it was unable to
|
||||
* find the total number of samples. Use
|
||||
* FLAC__stream_decoder_get_state() in that case to find out whether
|
||||
* the decoder is still valid.
|
||||
*
|
||||
* The state in which the decoder is left after calling this function
|
||||
* is undefined and might change in the future. Use
|
||||
* FLAC__stream_decoder_reset() to return the decoder to a defined
|
||||
* state.
|
||||
*
|
||||
* \param decoder A decoder instance to query.
|
||||
* \assert
|
||||
* \code decoder != NULL \endcode
|
||||
* \retval uint32_t
|
||||
* See above.
|
||||
*/
|
||||
FLAC_API FLAC__uint64 FLAC__stream_decoder_find_total_samples(FLAC__StreamDecoder *decoder);
|
||||
|
||||
/** Get the current number of channels in the stream being decoded.
|
||||
* Will only be valid after decoding has started and will contain the
|
||||
* value from the most recently decoded frame header.
|
||||
|
@ -195,9 +195,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
FPRINTF_DEBUG_ONLY(stderr,"finish_link\n");
|
||||
if(FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_LINK)
|
||||
FLAC__stream_decoder_finish_link(decoder);
|
||||
break;
|
||||
case 11:
|
||||
FPRINTF_DEBUG_ONLY(stderr,"skip_single_link\n");
|
||||
decoder_valid = FLAC__stream_decoder_skip_single_link(decoder);
|
||||
break;
|
||||
case 12:
|
||||
FPRINTF_DEBUG_ONLY(stderr,"find_total_samples\n");
|
||||
if(FLAC__stream_decoder_find_total_samples(decoder) == 0) {
|
||||
FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder);
|
||||
if(state == FLAC__STREAM_DECODER_OGG_ERROR ||
|
||||
state == FLAC__STREAM_DECODER_SEEK_ERROR ||
|
||||
state == FLAC__STREAM_DECODER_ABORTED ||
|
||||
state == FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR ||
|
||||
state == FLAC__STREAM_DECODER_UNINITIALIZED)
|
||||
decoder_valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@ typedef struct FLAC__StreamDecoderPrivate {
|
||||
FLAC__uint64 last_seen_framesync; /* if tell callback works, the location of the last seen frame sync code, to rewind to if needed */
|
||||
FLAC__uint64 target_sample;
|
||||
uint32_t unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */
|
||||
FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */
|
||||
FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine and find_total_samples to check when process_single() actually writes a frame */
|
||||
FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter);
|
||||
FLAC__bool error_has_been_sent; /* To check whether a missing frame has been signalled yet */
|
||||
} FLAC__StreamDecoderPrivate;
|
||||
@ -1338,6 +1338,129 @@ FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *deco
|
||||
}
|
||||
}
|
||||
|
||||
FLAC_API FLAC__uint64 FLAC__stream_decoder_find_total_samples(FLAC__StreamDecoder *decoder)
|
||||
{
|
||||
if(
|
||||
decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA &&
|
||||
decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA &&
|
||||
decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC &&
|
||||
decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME &&
|
||||
decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM
|
||||
)
|
||||
return 0;
|
||||
|
||||
if(
|
||||
decoder->private_->length_callback == NULL ||
|
||||
decoder->private_->seek_callback == NULL ||
|
||||
decoder->private_->tell_callback == NULL
|
||||
)
|
||||
return 0;
|
||||
|
||||
#if FLAC__HAS_OGG
|
||||
if(decoder->private_->is_ogg && FLAC__ogg_decoder_aspect_get_decode_chained_stream(&decoder->protected_->ogg_decoder_aspect)) {
|
||||
/* Keep moving forward until reaching end-of-stream */
|
||||
uint32_t i;
|
||||
FLAC__uint64 total_samples = 0;
|
||||
decoder->private_->is_indexing = true;
|
||||
while(1) {
|
||||
FLAC__OggDecoderAspectReadStatus status;
|
||||
if(decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM ||
|
||||
decoder->protected_->state == FLAC__STREAM_DECODER_OGG_ERROR ||
|
||||
decoder->protected_->state == FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR ||
|
||||
decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) {
|
||||
decoder->private_->is_indexing = false;
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
|
||||
return 0;
|
||||
}
|
||||
status = FLAC__ogg_decoder_aspect_skip_link(&decoder->protected_->ogg_decoder_aspect, read_callback_proxy_, decoder->private_->seek_callback, decoder->private_->tell_callback, decoder->private_->length_callback, decoder, decoder->private_->client_data);
|
||||
if(status == FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM)
|
||||
break;
|
||||
else if(status != FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK) {
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
decoder->private_->is_indexing = false;
|
||||
for(i = 0; i < decoder->protected_->ogg_decoder_aspect.number_of_links_indexed; i++) {
|
||||
total_samples += decoder->protected_->ogg_decoder_aspect.linkdetails[i].samples;
|
||||
}
|
||||
return total_samples;
|
||||
}
|
||||
else
|
||||
#endif /* FLAC__HAS_OGG */
|
||||
{ /* not decoding chained ogg */
|
||||
FLAC__uint64 length;
|
||||
FLAC__uint64 pos;
|
||||
uint32_t eof_distance = 1024; /* Some number, needs tuning */
|
||||
decoder->private_->is_seeking = true;
|
||||
decoder->private_->target_sample = UINT64_MAX;
|
||||
/* get the file length */
|
||||
if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) {
|
||||
decoder->private_->is_indexing = false;
|
||||
return 0;
|
||||
}
|
||||
pos = length;
|
||||
for( ; ; eof_distance *= 2) {
|
||||
if(eof_distance > (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
|
||||
/* Could not find a frame within a reasonable distance of EOF */
|
||||
return 0;
|
||||
}
|
||||
else if(pos == 0) {
|
||||
/* Did not find a frame while reading from the start of the file */
|
||||
return 0;
|
||||
}
|
||||
else if(eof_distance > length) {
|
||||
pos = 0;
|
||||
}
|
||||
else {
|
||||
pos = length - eof_distance;
|
||||
}
|
||||
|
||||
if(decoder->private_->seek_callback(decoder, pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) {
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
|
||||
return 0;
|
||||
}
|
||||
if(!FLAC__stream_decoder_flush(decoder)) {
|
||||
/* above call sets the state for us */
|
||||
return 0;
|
||||
}
|
||||
|
||||
decoder->private_->got_a_frame = false;
|
||||
if(!FLAC__stream_decoder_process_single(decoder) ||
|
||||
decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) {
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
|
||||
return 0;
|
||||
}
|
||||
if(decoder->private_->got_a_frame) {
|
||||
/* Found a frame, but we need the last frame and the frame before that, unless the last frame is
|
||||
* also the first frame */
|
||||
if(decoder->private_->frame.header.number.sample_number > 0) {
|
||||
/* For now, assume this is not the last frame, set blocksize, and continue.
|
||||
* If it turns out this is not the last frame, we'll start over anyway */
|
||||
decoder->private_->fixed_block_size = decoder->private_->last_frame.header.blocksize;
|
||||
if(!FLAC__stream_decoder_process_single(decoder) ||
|
||||
decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) {
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
|
||||
return 0;
|
||||
}
|
||||
if(decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM) {
|
||||
/* Found last frame, but need to find frame before that too */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!FLAC__stream_decoder_process_until_end_of_stream(decoder))
|
||||
return 0;
|
||||
FLAC__ASSERT(decoder->private_->is_seeking);
|
||||
FLAC__ASSERT(decoder->private_->last_frame_is_set);
|
||||
decoder->private_->is_seeking = false;
|
||||
return (FLAC__uint64)decoder->private_->last_frame.header.number.sample_number + (FLAC__uint64)decoder->private_->last_frame.header.blocksize;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Protected class methods
|
||||
@ -3395,9 +3518,8 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder
|
||||
|
||||
FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
|
||||
|
||||
#if FLAC__HAS_OGG
|
||||
decoder->private_->got_a_frame = true;
|
||||
#endif
|
||||
|
||||
if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */
|
||||
uint32_t delta = (uint32_t)(target_sample - this_frame_sample);
|
||||
/* kick out of seek mode */
|
||||
@ -3748,11 +3870,6 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
FLAC__stream_decoder_process_until_end_of_link(decoder);
|
||||
if(decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_LINK)
|
||||
FLAC__stream_decoder_finish_link(decoder);
|
||||
*/
|
||||
status = FLAC__ogg_decoder_aspect_skip_link(&decoder->protected_->ogg_decoder_aspect, read_callback_proxy_, decoder->private_->seek_callback, decoder->private_->tell_callback, decoder->private_->length_callback, decoder, decoder->private_->client_data);
|
||||
if(status == FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM)
|
||||
decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM;
|
||||
|
@ -65,6 +65,7 @@ static FLAC__StreamMetadata *expected_metadata_sequence_other_chain_[3];
|
||||
static uint32_t num_expected_;
|
||||
static uint32_t num_expected_other_chain_;
|
||||
static FLAC__off_t flacfilesize_;
|
||||
static uint32_t samples_;
|
||||
|
||||
static const char *flacfilename(FLAC__bool is_ogg, FLAC__bool is_chained_ogg)
|
||||
{
|
||||
@ -118,7 +119,7 @@ static FLAC__bool generate_file_(FLAC__bool is_ogg, FLAC__bool is_chained_ogg)
|
||||
expected_metadata_sequence_other_chain_[0] = &streaminfo_;
|
||||
expected_metadata_sequence_other_chain_[1] = &vorbiscomment_;
|
||||
expected_metadata_sequence_other_chain_[2] = &unknown_;
|
||||
if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg,true), &flacfilesize_, 512 * 1024, &streaminfo_, expected_metadata_sequence_other_chain_+1, 2))
|
||||
if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg,true), &flacfilesize_, samples_, &streaminfo_, expected_metadata_sequence_other_chain_+1, 2))
|
||||
return die_("creating the encoded file");
|
||||
file_utils__ogg_serial_number++;
|
||||
filesize=flacfilesize_;
|
||||
@ -135,7 +136,7 @@ static FLAC__bool generate_file_(FLAC__bool is_ogg, FLAC__bool is_chained_ogg)
|
||||
expected_metadata_sequence_[num_expected_++] = &unknown_;
|
||||
/* WATCHOUT: for Ogg FLAC the encoder should move the VORBIS_COMMENT block to the front, right after STREAMINFO */
|
||||
|
||||
if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg,false), &flacfilesize_, 512 * 1024, &streaminfo_, expected_metadata_sequence_, num_expected_))
|
||||
if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg,false), &flacfilesize_, samples_, &streaminfo_, expected_metadata_sequence_, num_expected_))
|
||||
return die_("creating the encoded file");
|
||||
|
||||
if(is_chained_ogg) {
|
||||
@ -482,6 +483,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool
|
||||
FLAC__StreamDecoderState state;
|
||||
StreamDecoderClientData decoder_client_data;
|
||||
FLAC__bool expect;
|
||||
FLAC__uint64 total_samples;
|
||||
|
||||
decoder_client_data.layer = layer;
|
||||
decoder_client_data.other_chain = false;
|
||||
@ -508,6 +510,72 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool
|
||||
}
|
||||
printf("OK\n");
|
||||
|
||||
if(layer < LAYER_FILENAME) {
|
||||
printf("opening %sFLAC file... ", is_ogg? "Ogg ":"");
|
||||
open_test_file(&decoder_client_data, is_ogg, is_chained_ogg, "rb");
|
||||
if(0 == decoder_client_data.file) {
|
||||
printf("ERROR (%s)\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
if(is_chained_ogg) {
|
||||
printf("testing FLAC__stream_decoder_set_decode_chained_stream()... ");
|
||||
if(!FLAC__stream_decoder_set_decode_chained_stream(decoder, true))
|
||||
return die_s_("returned false", decoder);
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
switch(layer) {
|
||||
case LAYER_STREAM:
|
||||
printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":"");
|
||||
init_status = is_ogg?
|
||||
FLAC__stream_decoder_init_ogg_stream(decoder, stream_decoder_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) :
|
||||
FLAC__stream_decoder_init_stream(decoder, stream_decoder_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data);
|
||||
break;
|
||||
case LAYER_SEEKABLE_STREAM:
|
||||
printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":"");
|
||||
init_status = is_ogg?
|
||||
FLAC__stream_decoder_init_ogg_stream(decoder, stream_decoder_read_callback_, stream_decoder_seek_callback_, stream_decoder_tell_callback_, stream_decoder_length_callback_, stream_decoder_eof_callback_, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) :
|
||||
FLAC__stream_decoder_init_stream(decoder, stream_decoder_read_callback_, stream_decoder_seek_callback_, stream_decoder_tell_callback_, stream_decoder_length_callback_, stream_decoder_eof_callback_, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data);
|
||||
break;
|
||||
case LAYER_FILE:
|
||||
printf("testing FLAC__stream_decoder_init_%sFILE()... ", is_ogg? "ogg_":"");
|
||||
init_status = is_ogg?
|
||||
FLAC__stream_decoder_init_ogg_FILE(decoder, decoder_client_data.file, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) :
|
||||
FLAC__stream_decoder_init_FILE(decoder, decoder_client_data.file, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data);
|
||||
break;
|
||||
case LAYER_FILENAME:
|
||||
printf("testing FLAC__stream_decoder_init_%sfile()... ", is_ogg? "ogg_":"");
|
||||
init_status = is_ogg?
|
||||
FLAC__stream_decoder_init_ogg_file(decoder, flacfilename(is_ogg,is_chained_ogg), stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) :
|
||||
FLAC__stream_decoder_init_file(decoder, flacfilename(is_ogg,is_chained_ogg), stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data);
|
||||
break;
|
||||
default:
|
||||
die_("internal error 009");
|
||||
return false;
|
||||
}
|
||||
if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
return die_s_(0, decoder);
|
||||
printf("OK\n");
|
||||
|
||||
printf("testing FLAC__stream_decoder_find_total_samples... ");
|
||||
total_samples = FLAC__stream_decoder_find_total_samples(decoder);
|
||||
printf("Number of samples returned is %" PRIu64 "... ",total_samples);
|
||||
if((layer == LAYER_STREAM && total_samples != 0) ||
|
||||
(layer != LAYER_STREAM && is_chained_ogg && total_samples != (samples_ * 2)) ||
|
||||
(layer != LAYER_STREAM && !is_chained_ogg && total_samples != samples_))
|
||||
return die_s_("returned wrong number of samples", decoder);
|
||||
printf("OK\n");
|
||||
|
||||
if(layer < LAYER_FILE) /* for LAYER_FILE, FLAC__stream_decoder_finish() closes the file */
|
||||
fclose(decoder_client_data.file);
|
||||
|
||||
printf("testing FLAC__stream_decoder_finish()... ");
|
||||
FLAC__stream_decoder_finish(decoder);
|
||||
printf("OK\n");
|
||||
|
||||
switch(layer) {
|
||||
case LAYER_STREAM:
|
||||
case LAYER_SEEKABLE_STREAM:
|
||||
@ -1327,6 +1395,7 @@ FLAC__bool test_decoders(void)
|
||||
{
|
||||
FLAC__bool is_ogg = false;
|
||||
FLAC__bool is_chained_ogg = false;
|
||||
samples_ = 1024 * 512;
|
||||
|
||||
while(1) {
|
||||
init_metadata_blocks_();
|
||||
|
Loading…
x
Reference in New Issue
Block a user