philippe44 пре 1 година
родитељ
комит
4b1f8a8d4b

+ 5 - 1
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

+ 31 - 1
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

BIN
components/codecs/lib/libFLAC.a


+ 3 - 0
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);		

+ 1 - 1
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)

+ 14 - 9
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;
 }

+ 1 - 1
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)