diff --git a/include/FLAC/stream_decoder.h b/include/FLAC/stream_decoder.h index eadb6cf359..ffc7b9f841 100644 --- a/include/FLAC/stream_decoder.h +++ b/include/FLAC/stream_decoder.h @@ -806,10 +806,8 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecod * by using FLAC__STREAM_DECODER_READ_STATUS_END_OF_LINK * appropriately. * - * Note that seeking is not yet supported when this flag is set to - * true. Also, when this flag is set to true, the serial number set - * with FLAC__stream_decoder_set_ogg_serial_number is ignored when - * decoding chained ogg streams. + * Note that when this flag is set to true, the serial number set + * with FLAC__stream_decoder_set_ogg_serial_number is ignored. * * \note * This function has no effect with native FLAC decoding. @@ -856,7 +854,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *d * \code decoder != NULL \endcode * \a type is valid * \retval FLAC__bool - * \c false if the decoder is already initialized, else \c true. + * \c false if type is invalid, else \c true. */ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); @@ -871,7 +869,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecode * \code decoder != NULL \endcode * \code id != NULL \endcode * \retval FLAC__bool - * \c false if the decoder is already initialized, else \c true. + * \c false when memory allocation fails, else \c true. */ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); @@ -883,7 +881,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__ * \assert * \code decoder != NULL \endcode * \retval FLAC__bool - * \c false if the decoder is already initialized, else \c true. + * \c always \c true. */ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); @@ -897,7 +895,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDe * \code decoder != NULL \endcode * \a type is valid * \retval FLAC__bool - * \c false if the decoder is already initialized, else \c true. + * \c false if type is invalid, else \c true. */ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); @@ -912,7 +910,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder * \code decoder != NULL \endcode * \code id != NULL \endcode * \retval FLAC__bool - * \c false if the decoder is already initialized, else \c true. + * \c false if memory allocation fails, else \c true. */ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); @@ -924,7 +922,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__S * \assert * \code decoder != NULL \endcode * \retval FLAC__bool - * \c false if the decoder is already initialized, else \c true. + * \c always \c true. */ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); @@ -1661,6 +1659,14 @@ FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder * * with FLAC__stream_decoder_flush() or reset with * FLAC__stream_decoder_reset() before decoding can continue. * + * When seeking in a chained stream with decoding of such streams + * enabled with FLAC__stream_decoder_set_decode_chained_stream(), + * this function seeks in the whole stream, over all links. When + * a seek to another link is performed, the decoder will also + * return metadata blocks of that link. If this is not desired, + * use FLAC__stream_decoder_set_metadata_ignore_all() before + * seeking. + * * \param decoder A decoder instance. * \param sample The target sample number to seek to. * \assert diff --git a/oss-fuzz/seek.cc b/oss-fuzz/seek.cc index 6f4fac6b8f..b2a6744f5b 100644 --- a/oss-fuzz/seek.cc +++ b/oss-fuzz/seek.cc @@ -42,7 +42,7 @@ int write_abort_check_counter = -1; #define FPRINTF_DEBUG_ONLY(...) #endif -#define CONFIG_LENGTH 2 +#define CONFIG_LENGTH 3 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) { @@ -70,8 +70,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) uint8_t command_length; FLAC__bool init_bools[16], ogg; - if(size > 2 && data[1] < 128) /* Use MSB as on/off */ - alloc_check_threshold = data[1]; + if(size < 1) + return 1; + + if(data[0] < 128) /* Use MSB as on/off */ + alloc_check_threshold = data[0]; else alloc_check_threshold = INT32_MAX; alloc_check_counter = 0; @@ -83,17 +86,17 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 1; } - /* Use first byte for configuration, leave at least one byte of input */ + /* Use first CONFIG_LENGTH bytes for configuration, leave at least one byte of input */ if(size < 1 + CONFIG_LENGTH){ FLAC__stream_decoder_delete(decoder); return 0; } - /* First 4 bits for configuration bools, next 4 for length of command section */ - for(int i = 0; i < 4; i++) - init_bools[i] = data[i/8] & (1 << (i % 8)); + /* bit 8 to 19 bits for configuration bools, bit 20 to 23 for length of command section */ + for(int i = 0; i < 12; i++) + init_bools[i] = data[1+i/8] & (1 << (i % 8)); - command_length = data[0] >> 4; + command_length = data[CONFIG_LENGTH-1] >> 4; /* Leave at least one byte as input */ if(command_length >= size - 1 - CONFIG_LENGTH) @@ -106,13 +109,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) fclose(file_to_decode); } - ogg = init_bools[0]; + ogg = init_bools[0]; FLAC__stream_decoder_set_md5_checking(decoder,init_bools[1]); if(init_bools[2]) FLAC__stream_decoder_set_metadata_respond_all(decoder); if(init_bools[3]) FLAC__stream_decoder_set_metadata_ignore_all(decoder); + if(init_bools[4]) + FLAC__stream_decoder_set_decode_chained_stream(decoder, true); /* initialize decoder */ if(decoder_valid) { @@ -182,6 +187,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) /* Set abort on write callback */ write_abort_check_counter = (command[0] >> 4) + 1; break; + case 9: + FPRINTF_DEBUG_ONLY(stderr,"end_of_link\n"); + decoder_valid = FLAC__stream_decoder_process_until_end_of_link(decoder); + break; + case 10: + 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); + } } diff --git a/src/libFLAC/include/private/ogg_decoder_aspect.h b/src/libFLAC/include/private/ogg_decoder_aspect.h index 16a7f9b0bb..e225595ec6 100644 --- a/src/libFLAC/include/private/ogg_decoder_aspect.h +++ b/src/libFLAC/include/private/ogg_decoder_aspect.h @@ -37,6 +37,22 @@ #include "FLAC/ordinals.h" #include "FLAC/stream_decoder.h" /* for FLAC__StreamDecoderReadStatus */ +#include "share/compat.h" + +typedef struct FLAC__OggDecoderAspect_LinkDetails { + long serial_number; + FLAC__off_t size; + uint64_t samples; +} FLAC__OggDecoderAspect_LinkDetails; + +typedef struct FLAC__OggDecoderAspect_TargetLink { + long serial_number; + FLAC__off_t start_byte; + FLAC__off_t end_byte; + uint64_t samples_in_preceding_links; + uint64_t samples_this_link; + uint32_t linknumber; +} FLAC__OggDecoderAspect_TargetLink; typedef struct FLAC__OggDecoderAspect { /* these are storage for values that can be set through the API */ @@ -55,6 +71,12 @@ typedef struct FLAC__OggDecoderAspect { ogg_page working_page; FLAC__bool have_working_packet; /* only if true will the following vars be valid */ ogg_packet working_packet; /* as we work through the packet we will move working_packet.packet forward and working_packet.bytes down */ + FLAC__OggDecoderAspect_LinkDetails *linkdetails; + FLAC__OggDecoderAspect_TargetLink target_link; /* to pass data to the seek routine */ + uint32_t number_of_links_detected; + uint32_t number_of_links_indexed; + uint32_t current_linknumber; + FLAC__bool is_seeking; } FLAC__OggDecoderAspect; void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value); @@ -64,8 +86,10 @@ void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect); void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect); void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect* aspect); void FLAC__ogg_decoder_aspect_next_link(FLAC__OggDecoderAspect* aspect); +FLAC__OggDecoderAspect_TargetLink * FLAC__ogg_decoder_aspect_get_target_link(FLAC__OggDecoderAspect* aspect, FLAC__uint64 target_sample); void FLAC__ogg_decoder_aspect_set_decode_chained_stream(FLAC__OggDecoderAspect* aspect, FLAC__bool value); FLAC__bool FLAC__ogg_decoder_aspect_get_decode_chained_stream(FLAC__OggDecoderAspect* aspect); +void FLAC__ogg_decoder_aspect_set_seek_parameters(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspect_TargetLink *target_link); typedef enum { FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK = 0, diff --git a/src/libFLAC/ogg_decoder_aspect.c b/src/libFLAC/ogg_decoder_aspect.c index 3c68f3e907..2befc324d3 100644 --- a/src/libFLAC/ogg_decoder_aspect.c +++ b/src/libFLAC/ogg_decoder_aspect.c @@ -36,11 +36,11 @@ #include /* for memcpy() */ #include "FLAC/assert.h" +#include "share/alloc.h" /* for free() */ #include "private/ogg_decoder_aspect.h" #include "private/ogg_mapping.h" #include "private/macros.h" - /*********************************************************************** * * Public class methods @@ -65,6 +65,14 @@ FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect) aspect->have_working_page = false; aspect->end_of_link = false; + aspect->current_linknumber = 0; + aspect->number_of_links_indexed = 0; + aspect->number_of_links_detected = 0; + + if(NULL == (aspect->linkdetails = safe_realloc_mul_2op_(NULL,4,sizeof(FLAC__OggDecoderAspect_LinkDetails)))) + return false; + memset(aspect->linkdetails, 0, 4 * sizeof(FLAC__OggDecoderAspect_LinkDetails)); + return true; } @@ -72,6 +80,7 @@ void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect) { (void)ogg_sync_clear(&aspect->sync_state); (void)ogg_stream_clear(&aspect->stream_state); + free(aspect->linkdetails); } void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value) @@ -98,6 +107,7 @@ void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect) void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) { FLAC__ogg_decoder_aspect_flush(aspect); + aspect->current_linknumber = 0; if(aspect->use_first_serial_number || aspect->decode_chained_stream) aspect->need_serial_number = true; @@ -106,6 +116,7 @@ void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) void FLAC__ogg_decoder_aspect_next_link(FLAC__OggDecoderAspect* aspect) { aspect->end_of_link = false; + aspect->current_linknumber++; } void FLAC__ogg_decoder_aspect_set_decode_chained_stream(FLAC__OggDecoderAspect* aspect, FLAC__bool value) @@ -118,6 +129,48 @@ FLAC__bool FLAC__ogg_decoder_aspect_get_decode_chained_stream(FLAC__OggDecoderAs return aspect->decode_chained_stream; } +FLAC__OggDecoderAspect_TargetLink * FLAC__ogg_decoder_aspect_get_target_link(FLAC__OggDecoderAspect* aspect, FLAC__uint64 target_sample) +{ + /* This returns the link containing the seek target if known. In + * effect, this function always returns NULL if no links have been + * indexed */ + + uint32_t current_link = 0; + uint32_t total_samples = 0; + uint32_t position = 0; + + while(current_link < aspect->number_of_links_indexed) { + total_samples += aspect->linkdetails[current_link].samples; + if(target_sample < total_samples) { + aspect->target_link.serial_number = aspect->linkdetails[current_link].serial_number; + aspect->target_link.start_byte = position; + aspect->target_link.samples_in_preceding_links = total_samples - aspect->linkdetails[current_link].samples; + aspect->target_link.end_byte = position + aspect->linkdetails[current_link].size; + aspect->target_link.samples_this_link = aspect->linkdetails[current_link].samples; + aspect->target_link.linknumber = current_link; + return &aspect->target_link; + } + position += aspect->linkdetails[current_link].size; + current_link++; + } + return NULL; +} + +void FLAC__ogg_decoder_aspect_set_seek_parameters(FLAC__OggDecoderAspect *aspect, FLAC__OggDecoderAspect_TargetLink *target_link) +{ + if(target_link == 0) { + aspect->is_seeking = false; + } + else { + aspect->need_serial_number = false; + aspect->current_linknumber = target_link->linknumber; + aspect->serial_number = target_link->serial_number; + ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number); + aspect->is_seeking = true; + } +} + + FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data) { static const size_t OGG_BYTES_CHUNK = 8192; @@ -173,7 +226,13 @@ FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper( aspect->end_of_stream = true; else { aspect->end_of_link = true; - aspect->need_serial_number = true; + if(aspect->current_linknumber >= aspect->number_of_links_indexed) { + aspect->linkdetails[aspect->current_linknumber].samples = aspect->working_packet.granulepos; + aspect->linkdetails[aspect->current_linknumber].serial_number = aspect->serial_number; + aspect->number_of_links_indexed++; + } + if(!aspect->is_seeking) + aspect->need_serial_number = true; aspect->have_working_page = false; /* e-o-s packet ends page */ } } @@ -234,10 +293,31 @@ FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper( aspect->serial_number = ogg_page_serialno(&aspect->working_page); ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number); aspect->need_serial_number = false; + + /* current_linknumber lags a bit: it is only increased after processing + * of the whole links is done, while this code does advance processing */ + if(aspect->current_linknumber >= aspect->number_of_links_detected) { + FLAC__OggDecoderAspect_LinkDetails * tmpptr = NULL; + aspect->number_of_links_detected = aspect->current_linknumber + 1; + + /* reallocate in chunks of 4 */ + if((aspect->current_linknumber + 1) % 4 == 0) { + if(NULL == (tmpptr = safe_realloc_nofree_mul_2op_(aspect->linkdetails,5+aspect->current_linknumber,sizeof(FLAC__OggDecoderAspect_LinkDetails)))) { + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; + } + aspect->linkdetails = tmpptr; + } + memset(&aspect->linkdetails[aspect->current_linknumber], 0, sizeof(FLAC__OggDecoderAspect_LinkDetails)); + } + + } if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) { aspect->have_working_page = true; aspect->have_working_packet = false; + if(aspect->current_linknumber >= aspect->number_of_links_indexed) { + aspect->linkdetails[aspect->current_linknumber].size += aspect->working_page.header_len + aspect->working_page.body_len; + } } /* else do nothing, could be a page from another stream */ } diff --git a/src/libFLAC/stream_decoder.c b/src/libFLAC/stream_decoder.c index 955510b34f..4754cc2005 100644 --- a/src/libFLAC/stream_decoder.c +++ b/src/libFLAC/stream_decoder.c @@ -157,6 +157,7 @@ typedef struct FLAC__StreamDecoderPrivate { FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */ FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */ FLAC__bool is_seeking; + FLAC__bool is_indexing; /* to be able to seek in chained streams */ FLAC__MD5Context md5context; FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ /* (the rest of these are only used for seeking) */ @@ -372,8 +373,10 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_( #if FLAC__HAS_OGG decoder->private_->is_ogg = is_ogg; - if(is_ogg && !FLAC__ogg_decoder_aspect_init(&decoder->protected_->ogg_decoder_aspect)) + if(is_ogg && !FLAC__ogg_decoder_aspect_init(&decoder->protected_->ogg_decoder_aspect)) { + FLAC__ogg_decoder_aspect_finish(&decoder->protected_->ogg_decoder_aspect); return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; + } #endif FLAC__cpu_info(&decoder->private_->cpuinfo); @@ -408,6 +411,7 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_( decoder->private_->do_md5_checking = decoder->protected_->md5_checking; decoder->private_->is_seeking = false; + decoder->private_->is_indexing = false; decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */ if(!FLAC__stream_decoder_reset(decoder)) { @@ -660,6 +664,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) md5_failed = true; } decoder->private_->is_seeking = false; + decoder->private_->is_indexing = false; set_defaults_(decoder); @@ -719,8 +724,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecode /* double protection */ if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE) return false; - if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return false; decoder->private_->metadata_filter[type] = true; if(type == FLAC__METADATA_TYPE_APPLICATION) decoder->private_->metadata_filter_ids_count = 0; @@ -733,8 +736,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__ FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); FLAC__ASSERT(0 != id); - if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return false; if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) return true; @@ -761,8 +762,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDe FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); - if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return false; for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++) decoder->private_->metadata_filter[i] = true; decoder->private_->metadata_filter_ids_count = 0; @@ -778,8 +777,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder /* double protection */ if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE) return false; - if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return false; decoder->private_->metadata_filter[type] = false; if(type == FLAC__METADATA_TYPE_APPLICATION) decoder->private_->metadata_filter_ids_count = 0; @@ -792,8 +789,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__S FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); FLAC__ASSERT(0 != id); - if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return false; if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) return true; @@ -819,8 +814,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDec FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); - if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return false; memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); decoder->private_->metadata_filter_ids_count = 0; return true; @@ -1250,7 +1243,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *deco FLAC__ASSERT(decoder->private_->length_callback); FLAC__ASSERT(decoder->private_->eof_callback); - if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) + if(!FLAC__stream_decoder_get_decode_chained_stream(decoder) && FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) return false; decoder->private_->is_seeking = true; @@ -3181,16 +3174,19 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) #endif decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data) ; - if(status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_LINK) { - decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_LINK; - return false; - } - else if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { + if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; return false; } else if(*bytes == 0) { - if( + if(status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_LINK) { + if(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_LINK; + else + decoder->protected_->state = FLAC__STREAM_DECODER_OGG_ERROR; + return false; + } + else if( status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM || ( #if FLAC__HAS_OGG @@ -3339,7 +3335,7 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder { decoder->private_->last_frame = *frame; /* save the frame */ decoder->private_->last_frame_is_set = true; - if(decoder->private_->is_seeking) { + if(decoder->private_->is_seeking && !decoder->private_->is_indexing) { FLAC__uint64 this_frame_sample = frame->header.number.sample_number; FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; FLAC__uint64 target_sample = decoder->private_->target_sample; @@ -3377,7 +3373,7 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } } - else { + else if(!decoder->private_->is_indexing) { /* * If we never got STREAMINFO, turn off MD5 checking to save * cycles since we don't have a sum to compare to anyway @@ -3390,6 +3386,9 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder } return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); } + else { /* decoder->private_->is_indexing == true */ + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } } void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) @@ -3651,10 +3650,12 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) { FLAC__uint64 left_pos = 0, right_pos = stream_length; - FLAC__uint64 left_sample = 0, right_sample = FLAC__stream_decoder_get_total_samples(decoder); + FLAC__uint64 left_sample = 0, right_sample = 0; FLAC__uint64 this_frame_sample = (FLAC__uint64)0 - 1; FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ FLAC__bool did_a_seek; + FLAC__OggDecoderAspect_TargetLink *target_link; + uint32_t current_linknumber = decoder->protected_->ogg_decoder_aspect.current_linknumber; uint32_t iteration = 0; /* In the first iterations, we will calculate the target byte position @@ -3669,12 +3670,61 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint */ static const FLAC__uint64 LINEAR_SEARCH_WITHIN_SAMPLES = FLAC__MAX_BLOCK_SIZE * 2; - /* If the total number of samples is unknown, use a large value, and - * force binary search immediately. - */ - if(right_sample == 0) { - right_sample = (FLAC__uint64)(-1); - BINARY_SEARCH_AFTER_ITERATION = 0; + if(FLAC__ogg_decoder_aspect_get_decode_chained_stream(&decoder->protected_->ogg_decoder_aspect)) { + /* Check whether target sample is contained by indexed links. + * If it is not keep moving forward until found */ + decoder->private_->is_indexing = true; + while(NULL == (target_link = FLAC__ogg_decoder_aspect_get_target_link(&decoder->protected_->ogg_decoder_aspect, target_sample))) { + 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) { + /* Target sample is not contained in stream or other error */ + decoder->private_->is_indexing = false; + 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); + } + decoder->private_->is_indexing = false; + /* Target link found, send metadata if necessary */ + FLAC__ogg_decoder_aspect_set_seek_parameters(&decoder->protected_->ogg_decoder_aspect, target_link); + if(target_link->linknumber != current_linknumber) { + if(decoder->private_->seek_callback((FLAC__StreamDecoder*)decoder, (FLAC__uint64)target_link->start_byte, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + decoder->private_->is_seeking = false; + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + decoder->private_->is_seeking = true; + } + /* set boundaries */ + left_pos = target_link->start_byte; + left_sample = 0; + right_pos = target_link->end_byte; + right_sample = target_link->samples_this_link; + target_sample -= target_link->samples_in_preceding_links; + } + else { + right_sample = FLAC__stream_decoder_get_total_samples(decoder); + + /* If the total number of samples is unknown, use a large value, and + * force binary search immediately. + */ + if(right_sample == 0) { + right_sample = (FLAC__uint64)(-1); + BINARY_SEARCH_AFTER_ITERATION = 0; + } } decoder->private_->target_sample = target_sample; @@ -3691,13 +3741,13 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } else { #ifndef FLAC__INTEGER_ONLY_LIBRARY - pos = (FLAC__uint64)((double)(target_sample - left_sample) / (double)(right_sample - left_sample) * (double)(right_pos - left_pos)); + pos = (FLAC__uint64)((double)(target_sample - left_sample) / (double)(right_sample - left_sample) * (double)(right_pos - left_pos)) + left_pos; #else /* a little less accurate: */ if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) - pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)); + pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)) + left_pos; else /* @@@ WATCHOUT, ~2TB limit */ - pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16)); + pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16)) + left_pos; #endif /* @@@ TODO: might want to limit pos to some distance * before EOF, to make sure we land before the last frame, @@ -3781,7 +3831,8 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } } } - + /* Tell ogg decoder aspect that seeking is done */ + FLAC__ogg_decoder_aspect_set_seek_parameters(&decoder->protected_->ogg_decoder_aspect, 0); return true; } #endif diff --git a/src/test_libFLAC++/decoders.cpp b/src/test_libFLAC++/decoders.cpp index 3e663fbcc6..187c9e599f 100644 --- a/src/test_libFLAC++/decoders.cpp +++ b/src/test_libFLAC++/decoders.cpp @@ -665,12 +665,17 @@ static bool test_stream_decoder(Layer layer, bool is_ogg, bool is_chained_ogg) dynamic_cast(decoder)->error_occurred_ = false; if(is_chained_ogg) { - dynamic_cast(decoder)->mute_test_ = true; printf("skip first chain link with process_until_end_of_link()... "); if(!decoder->process_until_end_of_link()) return die_s_("returned false", decoder); printf("OK\n"); - dynamic_cast(decoder)->mute_test_ = false; + + printf("checking whether metadata was returned by previous call... "); + if(dynamic_cast(decoder)->current_metadata_number_ != 1) { + return die_s_("no metadata blocks returned", decoder); + } + printf("OK\n"); + dynamic_cast(decoder)->current_metadata_number_ = 0; printf("testing get_state()... "); FLAC::Decoder::Stream::State state = decoder->get_state(); @@ -722,12 +727,42 @@ static bool test_stream_decoder(Layer layer, bool is_ogg, bool is_chained_ogg) dynamic_cast(decoder)->ignore_errors_ = false; } + if(is_chained_ogg) { + dynamic_cast(decoder)->current_metadata_number_ = 0; + } + expect = (layer != LAYER_STREAM); printf("testing seek_absolute()... "); if(decoder->seek_absolute(0) != expect) return die_s_(expect? "returned false" : "returned true", decoder); printf("OK\n"); + if(is_chained_ogg) { + if(layer != LAYER_STREAM) { + printf("checking whether metadata was returned by previous call... "); + if(dynamic_cast(decoder)->current_metadata_number_ != 1) { + return die_s_("no metadata blocks returned", decoder); + } + printf("OK\n"); + dynamic_cast(decoder)->current_metadata_number_ = 0; + } + + printf("testing seek_absolute(), seeking to second link... "); + if(decoder->seek_absolute(512 * 1024) != expect) + return die_s_(expect? "returned false" : "returned true", decoder); + printf("OK\n"); + + if(layer != LAYER_STREAM) { + printf("checking whether metadata was returned by previous call... "); + if(dynamic_cast(decoder)->current_metadata_number_ != 1) { + return die_s_("no metadata blocks returned", decoder); + } + printf("OK\n"); + dynamic_cast(decoder)->current_metadata_number_ = 0; + } + } + + printf("testing process_until_end_of_stream()... "); if(!decoder->process_until_end_of_stream()) return die_s_("returned false", decoder); diff --git a/src/test_libFLAC/decoders.c b/src/test_libFLAC/decoders.c index 6fd2c7c106..857ef13804 100644 --- a/src/test_libFLAC/decoders.c +++ b/src/test_libFLAC/decoders.c @@ -55,12 +55,14 @@ typedef struct { uint32_t current_metadata_number; FLAC__bool ignore_errors; FLAC__bool error_occurred; - FLAC__bool mute_test; + FLAC__bool other_chain; } StreamDecoderClientData; static FLAC__StreamMetadata streaminfo_, padding_, seektable_, application1_, application2_, vorbiscomment_, cuesheet_, picture_, unknown_; static FLAC__StreamMetadata *expected_metadata_sequence_[9]; +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 const char *flacfilename(FLAC__bool is_ogg, FLAC__bool is_chained_ogg) @@ -111,10 +113,13 @@ static FLAC__bool generate_file_(FLAC__bool is_ogg, FLAC__bool is_chained_ogg) if(is_chained_ogg) { /* Create a different file as the first link */ - expected_metadata_sequence_[0] = &picture_; - if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg,true), &flacfilesize_, 512 * 1024, &streaminfo_, expected_metadata_sequence_, 1)) + 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)) return die_("creating the encoded file"); file_utils__ogg_serial_number++; + } num_expected_ = 0; @@ -295,27 +300,26 @@ static void stream_decoder_metadata_callback_(const FLAC__StreamDecoder *decoder if(dcd->error_occurred) return; - if(!dcd->mute_test) { - if (metadata->type == FLAC__METADATA_TYPE_APPLICATION) { - printf ("%u ('%c%c%c%c')... ", dcd->current_metadata_number, metadata->data.application.id [0], metadata->data.application.id [1], metadata->data.application.id [2], metadata->data.application.id [3]); - } - else { - printf("%u... ", dcd->current_metadata_number); - } - fflush(stdout); + if (metadata->type == FLAC__METADATA_TYPE_APPLICATION) { + printf ("%u ('%c%c%c%c')... ", dcd->current_metadata_number, metadata->data.application.id [0], metadata->data.application.id [1], metadata->data.application.id [2], metadata->data.application.id [3]); + } + else { + printf("%u... ", dcd->current_metadata_number); + } + fflush(stdout); - if(dcd->current_metadata_number >= num_expected_) { - (void)die_("got more metadata blocks than expected"); + if((!dcd->other_chain && dcd->current_metadata_number >= num_expected_) || + (dcd->other_chain && dcd->current_metadata_number >= num_expected_other_chain_)) { + (void)die_("got more metadata blocks than expected"); + dcd->error_occurred = true; + } + else { + if(!mutils__compare_block(dcd->other_chain ? expected_metadata_sequence_other_chain_[dcd->current_metadata_number] : expected_metadata_sequence_[dcd->current_metadata_number], metadata)) { + (void)die_("metadata block mismatch"); dcd->error_occurred = true; } - else { - if(!mutils__compare_block(expected_metadata_sequence_[dcd->current_metadata_number], metadata)) { - (void)die_("metadata block mismatch"); - dcd->error_occurred = true; - } - } - dcd->current_metadata_number++; } + dcd->current_metadata_number++; } static void stream_decoder_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) @@ -402,18 +406,25 @@ static FLAC__bool stream_decoder_test_respond_(FLAC__StreamDecoder *decoder, Str } if(is_chained_ogg) { - dcd->mute_test = true; + dcd->other_chain = true; printf("skip first chain link with FLAC__stream_decoder_process_until_end_of_link()... "); if(!FLAC__stream_decoder_process_until_end_of_link(decoder)) return die_s_("returned false", decoder); printf("OK\n"); - dcd->mute_test = false; + dcd->other_chain = false; + printf("checking whether metadata was returned by previous call... "); + if(dcd->current_metadata_number != num_expected_other_chain_) { + return die_s_("too few or too much metadata blocks returned", decoder); + } + printf("OK\n"); printf("progress to next chain link with FLAC__stream_decoder_finish_link()... "); if(!FLAC__stream_decoder_finish_link(decoder)) return die_s_("returned false", decoder); printf("OK\n"); + dcd->current_metadata_number = 0; + } printf("testing FLAC__stream_decoder_process_until_end_of_stream()... "); @@ -421,6 +432,36 @@ static FLAC__bool stream_decoder_test_respond_(FLAC__StreamDecoder *decoder, Str return die_s_("returned false", decoder); printf("OK\n"); + if(is_chained_ogg && dcd->layer != LAYER_STREAM) { + dcd->current_metadata_number = 0; + + dcd->other_chain = true; + printf("seek to first chain... "); + if(!FLAC__stream_decoder_seek_absolute(decoder, 0)) + return die_s_("returned false", decoder); + printf("OK\n"); + dcd->other_chain = false; + + printf("checking whether metadata was returned by previous call... "); + if(dcd->current_metadata_number != num_expected_other_chain_) { + return die_s_("too few or too much metadata blocks returned", decoder); + } + printf("OK\n"); + + dcd->current_metadata_number = 0; + + printf("testing FLAC__stream_decoder_seek_absolute(), seeking to second link... "); + if(!FLAC__stream_decoder_seek_absolute(decoder, 512 * 1024)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("checking whether metadata was returned by previous call... "); + if(dcd->current_metadata_number != num_expected_) { + return die_s_("too few or too much metadata blocks returned", decoder); + } + printf("OK\n"); + } + printf("testing FLAC__stream_decoder_finish()... "); if(!FLAC__stream_decoder_finish(decoder)) return die_s_("returned false", decoder); @@ -438,7 +479,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool FLAC__bool expect; decoder_client_data.layer = layer; - decoder_client_data.mute_test = false; + decoder_client_data.other_chain = false; printf("\n+++ libFLAC unit test: FLAC__StreamDecoder (layer: %s, format: %s)\n\n", LayerString[layer], is_chained_ogg? "chained Ogg FLAC" : is_ogg? "Ogg FLAC" : "FLAC"); @@ -572,12 +613,18 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool decoder_client_data.error_occurred = false; if(is_chained_ogg) { - decoder_client_data.mute_test = true; printf("skip first chain link with FLAC__stream_decoder_process_until_end_of_link()... "); if(!FLAC__stream_decoder_process_until_end_of_link(decoder)) return die_s_("returned false", decoder); printf("OK\n"); - decoder_client_data.mute_test = false; + + printf("checking whether metadata was returned by previous call... "); + if(decoder_client_data.current_metadata_number != 1) { + return die_s_("no metadata returned", decoder); + } + printf("OK\n"); + + decoder_client_data.current_metadata_number = 0; printf("testing FLAC__stream_decoder_get_state()... "); state = FLAC__stream_decoder_get_state(decoder); @@ -630,12 +677,40 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool decoder_client_data.ignore_errors = false; } + if(is_chained_ogg) + decoder_client_data.current_metadata_number = 0; + expect = (layer != LAYER_STREAM); printf("testing FLAC__stream_decoder_seek_absolute()... "); if(FLAC__stream_decoder_seek_absolute(decoder, 0) != expect) return die_s_(expect? "returned false" : "returned true", decoder); printf("OK\n"); + if(is_chained_ogg) { + if(layer != LAYER_STREAM) { + printf("checking whether seeking to different link returns metadata... "); + if(decoder_client_data.current_metadata_number != 1) { + return die_s_("no metadata returned", decoder); + } + printf("OK\n"); + decoder_client_data.current_metadata_number = 0; + } + + printf("testing FLAC__stream_decoder_seek_absolute(), seeking to second link... "); + if(FLAC__stream_decoder_seek_absolute(decoder, 512 * 1024) != expect) + return die_s_(expect? "returned false" : "returned true", decoder); + printf("OK\n"); + + if(layer != LAYER_STREAM) { + printf("checking whether seeking to different link returns metadata... "); + if(decoder_client_data.current_metadata_number != 1) { + return die_s_("no metadata returned", decoder); + } + decoder_client_data.current_metadata_number = 0; + printf("OK\n"); + } + } + printf("testing FLAC__stream_decoder_process_until_end_of_stream()... "); if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) return die_s_("returned false", decoder); @@ -714,12 +789,17 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool decoder_client_data.current_metadata_number = 0; if(is_chained_ogg) { - decoder_client_data.mute_test = true; printf("skip first chain link with FLAC__stream_decoder_process_until_end_of_link()... "); if(!FLAC__stream_decoder_process_until_end_of_link(decoder)) return die_s_("returned false", decoder); printf("OK\n"); - decoder_client_data.mute_test = false; + + printf("checking whether seeking to different link returns metadata... "); + if(decoder_client_data.current_metadata_number != 1) { + return die_s_("no metadata returned", decoder); + } + printf("OK\n"); + decoder_client_data.current_metadata_number = 0; printf("testing FLAC__stream_decoder_get_state()... "); state = FLAC__stream_decoder_get_state(decoder); @@ -752,6 +832,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping. Also removes the seektable */ expected_metadata_sequence_[num_expected_++] = &streaminfo_; expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; @@ -761,6 +842,10 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool expected_metadata_sequence_[num_expected_++] = &cuesheet_; expected_metadata_sequence_[num_expected_++] = &picture_; expected_metadata_sequence_[num_expected_++] = &unknown_; + + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &streaminfo_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &vorbiscomment_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &unknown_; } else { expected_metadata_sequence_[num_expected_++] = &streaminfo_; @@ -787,6 +872,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg, is_chained_ogg)) return false; @@ -806,6 +892,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; expected_metadata_sequence_[num_expected_++] = &streaminfo_; expected_metadata_sequence_[num_expected_++] = &padding_; if(!is_ogg) /* encoder removes seektable for ogg */ @@ -816,6 +903,9 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool expected_metadata_sequence_[num_expected_++] = &picture_; expected_metadata_sequence_[num_expected_++] = &unknown_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &streaminfo_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &unknown_; + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg, is_chained_ogg)) return false; @@ -834,6 +924,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping. Also removes the seektable */ expected_metadata_sequence_[num_expected_++] = &streaminfo_; expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; @@ -841,6 +932,10 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool expected_metadata_sequence_[num_expected_++] = &cuesheet_; expected_metadata_sequence_[num_expected_++] = &picture_; expected_metadata_sequence_[num_expected_++] = &unknown_; + + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &streaminfo_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &vorbiscomment_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &unknown_; } else { expected_metadata_sequence_[num_expected_++] = &streaminfo_; @@ -870,6 +965,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 3; if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping. Also removes the seektable */ expected_metadata_sequence_[num_expected_++] = &streaminfo_; expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; @@ -913,6 +1009,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 3; if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping. Also removes the seektable */ expected_metadata_sequence_[num_expected_++] = &streaminfo_; expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; @@ -949,7 +1046,9 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &vorbiscomment_; if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg, is_chained_ogg)) return false; @@ -969,6 +1068,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; expected_metadata_sequence_[num_expected_++] = &application1_; expected_metadata_sequence_[num_expected_++] = &application2_; @@ -990,6 +1090,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; expected_metadata_sequence_[num_expected_++] = &application1_; if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg, is_chained_ogg)) @@ -1015,6 +1116,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; expected_metadata_sequence_[num_expected_++] = &application1_; expected_metadata_sequence_[num_expected_++] = &application2_; @@ -1041,6 +1143,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping. Also removes the seektable */ expected_metadata_sequence_[num_expected_++] = &streaminfo_; expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; @@ -1049,6 +1152,10 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool expected_metadata_sequence_[num_expected_++] = &cuesheet_; expected_metadata_sequence_[num_expected_++] = &picture_; expected_metadata_sequence_[num_expected_++] = &unknown_; + + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &streaminfo_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &vorbiscomment_; + expected_metadata_sequence_other_chain_[num_expected_other_chain_++] = &unknown_; } else { expected_metadata_sequence_[num_expected_++] = &streaminfo_; @@ -1084,6 +1191,7 @@ static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg, FLAC__bool printf("OK\n"); num_expected_ = 0; + num_expected_other_chain_ = 0; expected_metadata_sequence_[num_expected_++] = &application2_; if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg, is_chained_ogg)) diff --git a/src/test_seeking/main.c b/src/test_seeking/main.c index 731888250f..6469d5af18 100644 --- a/src/test_seeking/main.c +++ b/src/test_seeking/main.c @@ -45,6 +45,7 @@ typedef struct { FLAC__bool quiet; FLAC__bool ignore_errors; FLAC__bool error_occurred; + uint32_t last_seek_target; } DecoderClientData; static FLAC__bool stop_signal_ = false; @@ -210,13 +211,15 @@ static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder /* check against PCM data if we have it */ if (dcd->pcm) { - uint32_t c, i, j; + uint32_t c, i = 0, j; for (c = 0; c < frame->header.channels; c++) - for (i = (uint32_t)frame->header.number.sample_number, j = 0; j < frame->header.blocksize; i++, j++) + for (i = (uint32_t)dcd->last_seek_target, j = 0; j < frame->header.blocksize; i++, j++) if (buffer[c][j] != dcd->pcm[c][i]) { printf("ERROR: sample mismatch at sample#%u(%u), channel=%u, expected %d, got %d\n", i, j, c, buffer[c][j], dcd->pcm[c][i]); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } + /* Save last decoded sample for next call to this callback */ + dcd->last_seek_target = i; } return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; @@ -287,6 +290,7 @@ static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, FLAC__of return die_("FLAC__stream_decoder_new() FAILED, returned NULL\n"); if(is_ogg) { + FLAC__stream_decoder_set_decode_chained_stream(decoder, true); if(FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder); } @@ -301,6 +305,7 @@ static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, FLAC__of if(!is_ogg) { /* not necessary to do this for Ogg because of its seeking method */ /* process until end of stream to make sure we can still seek in that state */ decoder_client_data.quiet = true; + decoder_client_data.last_seek_target = 0; if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder); decoder_client_data.quiet = false; @@ -310,12 +315,13 @@ static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, FLAC__of return die_s_("expected FLAC__STREAM_DECODER_END_OF_STREAM", decoder); } - printf("file's total_samples is %" PRIu64 "\n", decoder_client_data.total_samples); n = (long int)decoder_client_data.total_samples; - if(n == 0 && total_samples >= 0) + if(total_samples > n) n = (long int)total_samples; + printf("file's total_samples is %" PRIu64 "\n", n); + /* if we don't have a total samples count, just guess based on the file size */ /* @@@ for is_ogg we should get it from last page's granulepos */ if(n == 0) { @@ -344,6 +350,7 @@ static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, FLAC__of pos = (FLAC__uint64)(local_rand_() % n); } + decoder_client_data.last_seek_target = pos; printf("#%u:seek(%" PRIu64 ")... ", i, pos); fflush(stdout); if(!FLAC__stream_decoder_seek_absolute(decoder, pos)) { diff --git a/test/test_seeking.sh b/test/test_seeking.sh index cb44393827..07ec302b50 100755 --- a/test/test_seeking.sh +++ b/test/test_seeking.sh @@ -136,6 +136,20 @@ if [ $has_ogg = "yes" ] ; then die "ERROR: during test_seeking" fi + echo "generating chained Ogg FLAC files for seeking:" + # need to generate a second set with a different serial number + tail -c 750000 noise.raw > noise-secondhalf.raw + run_flac --verify --force --silent --force-raw-format --endian=big --sign=signed --sample-rate=44100 --bps=16 --channels=2 --blocksize=576 --output-name=small2.oga --ogg noise-secondhalf.raw || die "ERROR generating Ogg FLAC file" + + cat small.oga small2.oga > chained.oga + cat noise.raw noise-secondhalf.raw > chained.raw + + echo "testing chained.oga:" + chained_samples=$(($small_samples+187500)) + if run_test_seeking chained.oga $small_seek_count $chained_samples chained.raw ; then : ; else + die "ERROR: during test_seeking" + fi + fi rm -f tiny.flac tiny.oga small.flac small.oga tiny-s.flac small-s.flac