diff --git a/include/FLAC++/decoder.h b/include/FLAC++/decoder.h index 6f0bda99a0..5227079694 100644 --- a/include/FLAC++/decoder.h +++ b/include/FLAC++/decoder.h @@ -123,6 +123,7 @@ namespace FLAC { //@} virtual bool set_ogg_serial_number(long value); ///< See FLAC__stream_decoder_set_ogg_serial_number() + virtual bool set_ogg_chaining(bool value); ///< See FLAC__stream_decoder_set_ogg_chaining() virtual bool set_md5_checking(bool value); ///< See FLAC__stream_decoder_set_md5_checking() virtual bool set_metadata_respond(::FLAC__MetadataType type); ///< See FLAC__stream_decoder_set_metadata_respond() virtual bool set_metadata_respond_application(const FLAC__byte id[4]); ///< See FLAC__stream_decoder_set_metadata_respond_application() @@ -133,6 +134,7 @@ namespace FLAC { /* get_state() is not virtual since we want subclasses to be able to return their own state */ State get_state() const; ///< See FLAC__stream_decoder_get_state() + virtual bool get_ogg_chaining() const; ///< See FLAC__stream_decoder_get_ogg_chaining() virtual bool get_md5_checking() const; ///< See FLAC__stream_decoder_get_md5_checking() virtual FLAC__uint64 get_total_samples() const; ///< See FLAC__stream_decoder_get_total_samples() virtual uint32_t get_channels() const; ///< See FLAC__stream_decoder_get_channels() diff --git a/include/FLAC/stream_decoder.h b/include/FLAC/stream_decoder.h index 2272bcac9b..90bf9ff5f0 100644 --- a/include/FLAC/stream_decoder.h +++ b/include/FLAC/stream_decoder.h @@ -782,6 +782,25 @@ FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); */ FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); +/** Set the "allow Ogg chaining" flag. If set, the Ogg decoder will + * prepare to receive a new stream once the last Ogg page arrives for + * the stream encapsulating the FLAC audio data. This can be used to + * support chained Ogg FLAC streams; a new \c STREAMINFO signals the + * beginning of a new stream. + * + * \note + * This function has no effect with native FLAC decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param allow Whether to allow chained streams. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_chaining(FLAC__StreamDecoder* decoder, FLAC__bool value); + /** Set the "MD5 signature checking" flag. If \c true, the decoder will * compute the MD5 signature of the unencoded audio data while decoding * and compare it to the signature from the STREAMINFO block, if it @@ -906,6 +925,18 @@ FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__Str */ FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); +/** Get the "allow Ogg chaining" flag as described in + * \code FLAC__stream_decoder_set_ogg_chaining \endcode. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +#define FLAC__OGG_CHAINING +FLAC_API FLAC__bool FLAC__stream_decoder_get_ogg_chaining(const FLAC__StreamDecoder* decoder); + /** Get the "MD5 signature checking" flag. * This is the value of the setting, not whether or not the decoder is * currently checking the MD5 (remember, it can be turned off automatically diff --git a/oss-fuzz/decoder.cc b/oss-fuzz/decoder.cc index b7ddf21828..25a9d25678 100644 --- a/oss-fuzz/decoder.cc +++ b/oss-fuzz/decoder.cc @@ -205,6 +205,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if ( ds.Get() ) { use_ogg = false; } + + decoder.set_ogg_chaining(true); + if ( ds.Get() ) { #ifdef FUZZER_DEBUG printf("set_ogg_serial_number\n"); diff --git a/src/flac/decode.c b/src/flac/decode.c index e832d70731..ce215d036b 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -37,6 +37,8 @@ typedef struct { FLAC__bool is_ogg; FLAC__bool use_first_serial_number; long serial_number; + FLAC__bool chaining; + uint32_t stream_counter; #endif FileFormat format; @@ -105,7 +107,7 @@ static FLAC__bool is_big_endian_host_; /* * local routines */ -static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool use_first_serial_number, long serial_number, FileFormat format, FileSubFormat subformat, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, FLAC__bool relaxed_foreign_metadata_handling, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename); +static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool chaining, FLAC__bool use_first_serial_number, long serial_number, FileFormat format, FileSubFormat subformat, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, FLAC__bool relaxed_foreign_metadata_handling, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename); static void DecoderSession_destroy(DecoderSession *d, FLAC__bool error_occurred); static FLAC__bool DecoderSession_init_decoder(DecoderSession *d, const char *infilename); static FLAC__bool DecoderSession_process(DecoderSession *d); @@ -156,12 +158,14 @@ int flac__decode_file(const char *infilename, const char *outfilename, FLAC__boo &decoder_session, #if FLAC__HAS_OGG options.is_ogg, + options.chaining, options.use_first_serial_number, options.serial_number, #else /*is_ogg=*/false, /*use_first_serial_number=*/false, /*serial_number=*/0, + /*chaining=*/false, #endif options.format, options.force_subformat, @@ -192,16 +196,20 @@ int flac__decode_file(const char *infilename, const char *outfilename, FLAC__boo return DecoderSession_finish_ok(&decoder_session); } -FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool use_first_serial_number, long serial_number, FileFormat format, FileSubFormat subformat, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, FLAC__bool relaxed_foreign_metadata_handling, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename) +FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool chaining, FLAC__bool use_first_serial_number, long serial_number, FileFormat format, FileSubFormat subformat, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, FLAC__bool relaxed_foreign_metadata_handling, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename) { #if FLAC__HAS_OGG d->is_ogg = is_ogg; d->use_first_serial_number = use_first_serial_number; d->serial_number = serial_number; + d->chaining = chaining; + d->stream_counter = 0; #else + (void)is_ogg; (void)use_first_serial_number; (void)serial_number; + (void)chaining; #endif d->format = format; @@ -365,6 +373,7 @@ FLAC__bool DecoderSession_init_decoder(DecoderSession *decoder_session, const ch if(decoder_session->is_ogg) { if(!decoder_session->use_first_serial_number) FLAC__stream_decoder_set_ogg_serial_number(decoder_session->decoder, decoder_session->serial_number); + FLAC__stream_decoder_set_ogg_chaining(decoder_session->decoder, decoder_session->chaining); init_status = FLAC__stream_decoder_init_ogg_file(decoder_session->decoder, strcmp(infilename, "-")? infilename : 0, write_callback, metadata_callback, error_callback, /*client_data=*/decoder_session); } else @@ -553,6 +562,12 @@ int DecoderSession_finish_ok(DecoderSession *d) flac__utils_printf(stderr, 1, "WARNING, cannot check total number of samples since it was unset in the STREAMINFO\n"); ok = !d->treat_warnings_as_errors; } +#if FLAC__HAS_OGG + if (d->chaining) { + stats_print_name(2, d->inbasefilename); + flac__utils_printf(stderr, 2, "%d stream(s) found\n", d->stream_counter); + } +#endif stats_print_name(2, d->inbasefilename); flac__utils_printf(stderr, 2, "%s \n", d->test_only? "ok ":d->analysis_mode?"done ":"done"); } @@ -1456,6 +1471,16 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet (void)decoder; +#if FLAC__HAS_OGG + if (decoder_session->frame_counter && decoder_session->is_ogg && decoder_session->chaining) { + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + decoder_session->stream_counter++; + decoder_session->total_samples += metadata->data.stream_info.total_samples; + } + return; + } +#endif + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { FLAC__uint64 skip, until; @@ -1472,6 +1497,7 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet decoder_session->bps = metadata->data.stream_info.bits_per_sample; decoder_session->channels = metadata->data.stream_info.channels; decoder_session->sample_rate = metadata->data.stream_info.sample_rate; + decoder_session->stream_counter = 1; if(!flac__utils_canonicalize_skip_until_specification(decoder_session->skip_specification, decoder_session->sample_rate)) { flac__utils_printf(stderr, 1, "%s: ERROR, value of --skip is too large\n", decoder_session->inbasefilename); diff --git a/src/flac/decode.h b/src/flac/decode.h index 24f5723dad..12a7976f30 100644 --- a/src/flac/decode.h +++ b/src/flac/decode.h @@ -46,6 +46,7 @@ typedef struct { FLAC__bool is_ogg; FLAC__bool use_first_serial_number; long serial_number; + FLAC__bool chaining; #endif utils__SkipUntilSpecification skip_specification; utils__SkipUntilSpecification until_specification; diff --git a/src/flac/main.c b/src/flac/main.c index 846cf892bb..5f36058319 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -155,6 +155,7 @@ static struct share__option long_options_[] = { { "padding" , share__required_argument, 0, 'P' }, #if FLAC__HAS_OGG { "ogg" , share__no_argument, 0, 0 }, + { "chaining" , share__no_argument, 0, 0 }, { "serial-number" , share__required_argument, 0, 0 }, #endif { "blocksize" , share__required_argument, 0, 'b' }, @@ -240,6 +241,7 @@ static struct { FLAC__bool analyze; FLAC__bool use_ogg; FLAC__bool has_serial_number; /* true iff --serial-number was used */ + FLAC__bool chaining; long serial_number; /* this is the Ogg serial number and is unused for native FLAC */ FLAC__bool force_to_stdout; FLAC__bool force_raw_format; @@ -565,6 +567,7 @@ FLAC__bool init_options(void) option_values.test_only = false; option_values.analyze = false; option_values.use_ogg = false; + option_values.chaining = false; option_values.has_serial_number = false; option_values.serial_number = 0; option_values.force_to_stdout = false; @@ -805,6 +808,9 @@ int parse_option(int short_option, const char *long_option, const char *option_a else if(0 == strcmp(long_option, "ogg")) { option_values.use_ogg = true; } + else if (0 == strcmp(long_option, "chaining")) { + option_values.chaining = true; + } else if(0 == strcmp(long_option, "serial-number")) { option_values.has_serial_number = true; option_values.serial_number = atol(option_argument); @@ -1302,6 +1308,7 @@ void show_help(void) printf(" --until={#|[+|-]mm:ss.ss} Stop at the given sample for each input file\n"); #if FLAC__HAS_OGG printf(" --ogg Use Ogg as transport layer\n"); + printf(" --chaining Allow Ogg stream chaining decoding\n"); printf(" --serial-number Serial number to use for the FLAC stream\n"); #endif printf("analysis options:\n"); @@ -2313,6 +2320,7 @@ int decode_file(const char *infilename) decode_options.force_subformat = output_subformat; #if FLAC__HAS_OGG decode_options.is_ogg = treat_as_ogg; + decode_options.chaining = option_values.chaining; decode_options.use_first_serial_number = !option_values.has_serial_number; decode_options.serial_number = option_values.serial_number; #endif diff --git a/src/libFLAC++/stream_decoder.cpp b/src/libFLAC++/stream_decoder.cpp index 6e351d94b2..d6222814cd 100644 --- a/src/libFLAC++/stream_decoder.cpp +++ b/src/libFLAC++/stream_decoder.cpp @@ -74,6 +74,12 @@ namespace FLAC { return static_cast(::FLAC__stream_decoder_set_ogg_serial_number(decoder_, value)); } + bool Stream::set_ogg_chaining(bool value) + { + FLAC__ASSERT(is_valid()); + return static_cast(::FLAC__stream_decoder_set_ogg_chaining(decoder_, value)); + } + bool Stream::set_md5_checking(bool value) { FLAC__ASSERT(is_valid()); @@ -122,6 +128,12 @@ namespace FLAC { return State(::FLAC__stream_decoder_get_state(decoder_)); } + bool Stream::get_ogg_chaining() const + { + FLAC__ASSERT(is_valid()); + return static_cast(::FLAC__stream_decoder_get_ogg_chaining(decoder_)); + } + bool Stream::get_md5_checking() const { FLAC__ASSERT(is_valid()); diff --git a/src/libFLAC/include/private/ogg_decoder_aspect.h b/src/libFLAC/include/private/ogg_decoder_aspect.h index c9236410ef..a35d9a08bf 100644 --- a/src/libFLAC/include/private/ogg_decoder_aspect.h +++ b/src/libFLAC/include/private/ogg_decoder_aspect.h @@ -49,6 +49,8 @@ typedef struct FLAC__OggDecoderAspect { uint32_t version_major, version_minor; FLAC__bool need_serial_number; FLAC__bool end_of_stream; + FLAC__bool page_eos; + FLAC__bool chaining; FLAC__bool have_working_page; /* only if true will the following vars be valid */ ogg_page working_page; FLAC__bool have_working_packet; /* only if true will the following vars be valid */ @@ -60,7 +62,10 @@ void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect); FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect); 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_reset(FLAC__OggDecoderAspect* aspect); +bool FLAC__ogg_decoder_aspect_page_eos(FLAC__OggDecoderAspect* aspect, bool reset); +void FLAC__ogg_decoder_aspect_set_chaining(FLAC__OggDecoderAspect* aspect, FLAC__bool value); +FLAC__bool FLAC__ogg_decoder_aspect_get_chaining(FLAC__OggDecoderAspect* aspect); 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 58a2934601..2cd0627849 100644 --- a/src/libFLAC/ogg_decoder_aspect.c +++ b/src/libFLAC/ogg_decoder_aspect.c @@ -63,6 +63,7 @@ FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect) aspect->end_of_stream = false; aspect->have_working_page = false; + aspect->page_eos = false; return true; } @@ -82,6 +83,7 @@ void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect) { aspect->use_first_serial_number = true; + aspect->chaining = false; } void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect) @@ -90,6 +92,7 @@ void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect) (void)ogg_sync_reset(&aspect->sync_state); aspect->end_of_stream = false; aspect->have_working_page = false; + aspect->page_eos = false; } void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) @@ -100,6 +103,23 @@ void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) aspect->need_serial_number = true; } +bool FLAC__ogg_decoder_aspect_page_eos(FLAC__OggDecoderAspect* aspect, bool reset) +{ + bool page_eos = aspect->page_eos; + if (reset) aspect->page_eos = false; + return page_eos; +} + +void FLAC__ogg_decoder_aspect_set_chaining(FLAC__OggDecoderAspect* aspect, FLAC__bool value) +{ + aspect->chaining = value; +} + +FLAC__bool FLAC__ogg_decoder_aspect_get_chaining(FLAC__OggDecoderAspect* aspect) +{ + return aspect->chaining; +} + 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; @@ -191,19 +211,29 @@ FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper( } } } + else if (aspect->page_eos) { + /* we've now consumed all packets of the last page */ + aspect->need_serial_number = true; + return *bytes ? FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK : + FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; + } else { /* try and get another page */ const int ret = ogg_sync_pageout(&aspect->sync_state, &aspect->working_page); if (ret > 0) { /* got a page, grab the serial number if necessary */ if(aspect->need_serial_number) { - aspect->stream_state.serialno = aspect->serial_number = ogg_page_serialno(&aspect->working_page); + aspect->serial_number = ogg_page_serialno(&aspect->working_page); + ogg_stream_reset_serialno(&aspect->stream_state, aspect->serial_number); aspect->need_serial_number = false; } if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) { aspect->have_working_page = true; aspect->have_working_packet = false; } + if(aspect->chaining && ogg_page_eos(&aspect->working_page) != 0) { + aspect->page_eos = true; + } /* else do nothing, could be a page from another stream */ } else if (ret == 0) { diff --git a/src/libFLAC/stream_decoder.c b/src/libFLAC/stream_decoder.c index 18d8dd3b21..0cff132d22 100644 --- a/src/libFLAC/stream_decoder.c +++ b/src/libFLAC/stream_decoder.c @@ -112,6 +112,9 @@ static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecod static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); +#if FLAC__HAS_OGG +static void reset_decoder(FLAC__StreamDecoder* decoder); +#endif /*********************************************************************** * @@ -681,6 +684,21 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecod #endif } +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_chaining(FLAC__StreamDecoder* decoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + if (decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + FLAC__ogg_decoder_aspect_set_chaining(&decoder->protected_->ogg_decoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value) { FLAC__ASSERT(0 != decoder); @@ -819,6 +837,18 @@ FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__ return FLAC__StreamDecoderStateString[decoder->protected_->state]; } +FLAC_API FLAC__bool FLAC__stream_decoder_get_ogg_chaining(const FLAC__StreamDecoder* decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); +#if FLAC__HAS_OGG + return FLAC__ogg_decoder_aspect_get_chaining(&decoder->protected_->ogg_decoder_aspect); +#else + (void)decoder; + return false; +#endif +} + FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); @@ -1007,8 +1037,16 @@ FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *dec else return true; case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: - if(!frame_sync_(decoder)) - return true; /* above function sets the status for us */ + if(!frame_sync_(decoder)) { +#if FLAC__HAS_OGG + if (decoder->private_->is_ogg && + decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM && + FLAC__ogg_decoder_aspect_page_eos(&decoder->protected_->ogg_decoder_aspect, true)) + reset_decoder(decoder); + else +#endif + return true; /* above function sets the status for us */ + } break; case FLAC__STREAM_DECODER_READ_FRAME: if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/true)) @@ -1068,8 +1106,16 @@ FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__Strea return false; /* above function sets the status for us */ break; case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: - if(!frame_sync_(decoder)) - return true; /* above function sets the status for us */ + if(!frame_sync_(decoder)) { +#if FLAC__HAS_OGG + if (decoder->private_->is_ogg && + decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM && + FLAC__ogg_decoder_aspect_page_eos(&decoder->protected_->ogg_decoder_aspect, true)) + reset_decoder(decoder); + else +#endif + return true; /* above function sets the status for us */ + } break; case FLAC__STREAM_DECODER_READ_FRAME: if(!read_frame_(decoder, &dummy, /*do_full_decode=*/true)) @@ -1096,8 +1142,16 @@ FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder * case FLAC__STREAM_DECODER_READ_METADATA: return false; /* above function sets the status for us */ case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: - if(!frame_sync_(decoder)) - return true; /* above function sets the status for us */ + if(!frame_sync_(decoder)) { +#if FLAC__HAS_OGG + if (decoder->private_->is_ogg && + decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM && + FLAC__ogg_decoder_aspect_page_eos(&decoder->protected_->ogg_decoder_aspect, true)) + reset_decoder(decoder); + else +#endif + return true; /* above function sets the status for us */ + } break; case FLAC__STREAM_DECODER_READ_FRAME: if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/false)) @@ -3729,3 +3783,14 @@ FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_d return feof(decoder->private_->file)? true : false; } + +#if FLAC__HAS_OGG +void reset_decoder(FLAC__StreamDecoder* decoder) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + decoder->private_->has_stream_info = false; + decoder->private_->first_frame_offset = 0; + decoder->private_->unparseable_frame_count = 0; + decoder->private_->last_seen_framesync = 0; + decoder->private_->last_frame_is_set = false; +} +#endif