diff --git a/CHANGELOG b/CHANGELOG index e6df73e3..55cb938a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +2024-01-27 + - complete libflac fix and add chaining enablement + - fixed stream Ogg demux issue with unknown granule + 2024-01-19 - fixed libflac with OggFlac - AirPlay missed frame logging @@ -31,7 +35,7 @@ - force gpio_pad_select_gpio in dac_controlset in case somebody uses UART gpio's (or other pre-programmed) 2023-11-08 - - execute dac_controlset even whne there is no i2s (for gpio) + - execute dac_controlset even when there is no i2s (for gpio) 2023-11-07 - led-vu gain + misc fixes diff --git a/components/codecs/inc/FLAC/stream_decoder.h b/components/codecs/inc/FLAC/stream_decoder.h index f4e16488..da0db792 100644 --- a/components/codecs/inc/FLAC/stream_decoder.h +++ b/components/codecs/inc/FLAC/stream_decoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2022 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -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,17 @@ 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. + */ +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/components/codecs/lib/libFLAC.a b/components/codecs/lib/libFLAC.a index e535aa93..2ea8603c 100644 Binary files a/components/codecs/lib/libFLAC.a and b/components/codecs/lib/libFLAC.a differ diff --git a/components/squeezelite/flac.c b/components/squeezelite/flac.c index 2afa2e19..01a58297 100644 --- a/components/squeezelite/flac.c +++ b/components/squeezelite/flac.c @@ -70,6 +70,7 @@ struct flac { ); FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_set_ogg_chaining)(FLAC__StreamDecoder* decoder, FLAC__bool allow); #endif }; @@ -256,6 +257,7 @@ static void flac_open(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t en if ( f->container == 'o' ) { LOG_INFO("ogg/flac container - using init_ogg_stream"); + FLAC(f, stream_decoder_set_ogg_chaining, f->decoder, true); FLAC(f, stream_decoder_init_ogg_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); } else { FLAC(f, stream_decoder_init_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); @@ -298,6 +300,7 @@ static bool load_flac() { f->FLAC__stream_decoder_init_ogg_stream = dlsym(handle, "FLAC__stream_decoder_init_ogg_stream"); f->FLAC__stream_decoder_process_single = dlsym(handle, "FLAC__stream_decoder_process_single"); f->FLAC__stream_decoder_get_state = dlsym(handle, "FLAC__stream_decoder_get_state"); + f->FLAC__stream_decoder_set_ogg_chaining = dlsym(handle, "FLAC__stream_decoder_set_ogg_chaining"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); diff --git a/components/squeezelite/opus.c b/components/squeezelite/opus.c index 0b15d7f3..27c8a352 100644 --- a/components/squeezelite/opus.c +++ b/components/squeezelite/opus.c @@ -194,7 +194,7 @@ static int read_opus_header(void) { // nothing has been found and we have no more bytes, come back later if (status <= 0) break; - // always set stream serialno if we have a new one + // always set stream serialno if we have a new one (no multiplexed streams) if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page)); // bring new page in if we want it (otherwise we're just skipping) diff --git a/components/squeezelite/stream.c b/components/squeezelite/stream.c index 759a7487..7f203081 100644 --- a/components/squeezelite/stream.c +++ b/components/squeezelite/stream.c @@ -63,9 +63,9 @@ struct EXT_RAM_ATTR streamstate stream; static EXT_RAM_ATTR struct { bool flac; + u64_t serial; enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state; size_t want, miss, match; - u64_t granule; u8_t* data, segments[255]; #pragma pack(push, 1) struct { @@ -237,19 +237,22 @@ static void stream_ogg(size_t n) { // calculate size of page using lacing values for (size_t i = 0; i < ogg.want; i++) ogg.miss += ogg.data[i]; ogg.want = ogg.miss; + + // acquire serial number when we are looking for headers and hit a bos + if (ogg.serial == ULLONG_MAX && (ogg.header.type & 0x02)) ogg.serial = ogg.header.serial; - if (ogg.header.granule == 0 || (ogg.header.granule == -1 && ogg.granule == 0)) { - // granule 0 means a new stream, so let's look into it - ogg.state = OGG_PAGE; - ogg.data = malloc(ogg.want); - } else { + // we have overshot and missed header, reset serial number to restart search (O and -1 are le/be) + if (ogg.header.serial == ogg.serial && ogg.header.granule && ogg.header.granule != -1) ogg.serial = ULLONG_MAX; + + // not our serial (the above protected us from granule > 0) + if (ogg.header.serial != ogg.serial) { // otherwise, jump over data ogg.state = OGG_SYNC; ogg.data = NULL; + } else { + ogg.state = OGG_PAGE; + ogg.data = malloc(ogg.want); } - - // memorize granule for next page - if (ogg.header.granule != -1) ogg.granule = ogg.header.granule; break; case OGG_PAGE: { char** tag = (char* []){ "\x3vorbis", "OpusTags", NULL }; @@ -289,6 +292,7 @@ static void stream_ogg(size_t n) { } ogg.flac = false; + ogg.serial = ULLONG_MAX; stream.meta_send = true; wake_controller(); LOG_INFO("Ogg metadata length: %u", stream.header_len - 3); @@ -736,6 +740,7 @@ void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *h ogg.miss = ogg.match = 0; ogg.state = use_ogg ? OGG_SYNC : OGG_OFF; ogg.flac = false; + ogg.serial = ULLONG_MAX; UNLOCK; } diff --git a/components/squeezelite/vorbis.c b/components/squeezelite/vorbis.c index a330a303..613b95d2 100644 --- a/components/squeezelite/vorbis.c +++ b/components/squeezelite/vorbis.c @@ -201,7 +201,7 @@ static int read_vorbis_header(void) { // nothing has been found and we have no more bytes, come back later if (status <= 0) break; - // always set stream serialno if we have a new one + // always set stream serialno if we have a new one (no multiplexed streams) if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page)); // bring new page in if we want it (otherwise we're just skipping)