瀏覽代碼

OggFlac & displayer fixes (see CHANGELOG) - release

philippe44 1 年之前
父節點
當前提交
55a8658f3a

+ 6 - 0
CHANGELOG

@@ -1,3 +1,9 @@
+2024-01-10
+ - add OggFlac to stream metadata
+ - fix OggFlac deadlock in flac callback when not enough data in streambuf
+ - fix no displayer due to threadshold too high (use 500ms instead)
+ - reset outputbuf when cspot starts
+ 
 2024-01-01
  - ogg stream are parsed to foward metadata to LMS
  - fix some ogg parsing on multi-stream containers

+ 2 - 0
components/squeezelite/decode_external.c

@@ -288,6 +288,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
 			output.frames_played = 0;
 			output.external = DECODE_RAOP;
 			output.state = OUTPUT_STOPPED;
+
 			if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
 			LOG_INFO("resizing buffer %u", outputbuf->size);
 			break;
@@ -377,6 +378,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
 		output.state = OUTPUT_STOPPED;
         sink_state = SINK_ABORT;
 		_buf_flush(outputbuf);
+        _buf_limit(outputbuf, 0);
 		if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
 		LOG_INFO("CSpot start track");
 		break;

+ 3 - 0
components/squeezelite/flac.c

@@ -121,6 +121,9 @@ static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder,
 	_buf_inc_readp(streambuf, bytes);
 	UNLOCK_S;
 
+    // give some time for stream to acquire data otherwise flac will hammer us
+    if (!end && !bytes) usleep(100 * 1000);
+
 	*want = bytes;
 
 	return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;

+ 5 - 2
components/squeezelite/output_bt.c

@@ -139,8 +139,11 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
 		u8_t *buf = silencebuf;
 		memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
 	}
-	
-	output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL  + gainR) / 2);
+
+    // don't update visu if we don't have enough data in buffer (500 ms)
+    if (silence || _buf_used(outputbuf) >  BYTES_PER_FRAME * output.current_sample_rate / 2) {
+    	output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL  + gainR) / 2);
+    }
 	
 	oframes += out_frames;
 

+ 2 - 2
components/squeezelite/output_i2s.c

@@ -485,8 +485,8 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
 		memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
 	}
 
-	// don't update visu if we don't have enough data in buffer
-	if (silence || output.external || _buf_used(outputbuf) > outputbuf->size >> 2 ) {
+	// don't update visu if we don't have enough data in buffer (500 ms)
+	if (silence || _buf_used(outputbuf) >  BYTES_PER_FRAME * output.current_sample_rate / 2) {
 		output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
 	}
 		

+ 3 - 1
components/squeezelite/slimproto.c

@@ -393,7 +393,9 @@ static void process_strm(u8_t *pkt, int len) {
 				stream_file(header, header_len, strm->threshold * 1024);
 				autostart -= 2;
 			} else {
-				stream_sock(ip, port, strm->format, header, header_len, strm->threshold * 1024, autostart >= 2);
+				stream_sock(ip, port, strm->flags & 0x20, 
+                            strm->format == 'o' || strm->format == 'u' || (strm->format == 'f' && strm->pcm_sample_size == 'o'),
+                            header, header_len, strm->threshold * 1024, autostart >= 2);
 			}
 			sendSTAT("STMc", 0);
 			sentSTMu = sentSTMo = sentSTMl = false;

+ 1 - 1
components/squeezelite/squeezelite.h

@@ -585,7 +585,7 @@ struct streamstate {
 void stream_init(log_level level, unsigned stream_buf_size);
 void stream_close(void);
 void stream_file(const char *header, size_t header_len, unsigned threshold);
-void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
+void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
 bool stream_disconnect(void);
 
 // decode.c

+ 22 - 14
components/squeezelite/stream.c

@@ -62,6 +62,7 @@ static sockfd fd;
 struct EXT_RAM_ATTR streamstate stream;
 
 static EXT_RAM_ATTR struct {
+    bool flac;
 	enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
 	size_t want, miss, match;
     u64_t granule;
@@ -183,6 +184,10 @@ static size_t memfind(const u8_t* haystack, size_t n, const char* needle, size_t
 	return i;
 }
 
+/* https://xiph.org/ogg/doc/framing.html 
+ * https://xiph.org/flac/ogg_mapping.html
+ * https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 */
+ 
 static void stream_ogg(size_t n) {
 	if (ogg.state == OGG_OFF) return;
 	u8_t* p = streambuf->writep;
@@ -212,9 +217,8 @@ static void stream_ogg(size_t n) {
 				ogg.data = (u8_t*) &ogg.header;
 				ogg.match = 0;
 			} else {
-                if (!ogg.match) {
-                    LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
-                }
+                if (!ogg.match) LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
+                LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
 				return;
 			}
 			break;
@@ -248,15 +252,19 @@ static void stream_ogg(size_t n) {
             if (ogg.header.granule != -1) ogg.granule = ogg.header.granule;            
 			break;
 		case OGG_PAGE: {
-			size_t offset = 0;
-
-			// try to find one of valid Ogg pattern (vorbis, opus)
-			for (char** tag = (char*[]) { "\x3vorbis", "OpusTags", NULL }; *tag; tag++, offset = 0) {
-				size_t pos = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &offset);
-				if (offset != strlen(*tag)) continue;
-				
+			char** tag = (char* []){ "\x3vorbis", "OpusTags", NULL };
+			size_t ofs = 0;
+
+			/* with OggFlac, we need the next page (packet) - VorbisComment is wrapped into a FLAC_METADATA
+			 * and except with vorbis, comment packet starts a new page but even in vorbis, it won't span
+			 * accross multiple pages */
+			if (ogg.flac) ofs = 4;
+			else if (!memcmp(ogg.data, "\x7f""FLAC", 5)) ogg.flac = true;
+			else for (size_t n = 0; *tag; tag++, ofs = 0) if ((ofs = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &n)) && n == strlen(*tag)) break;
+	
+			if (ofs) {			
 				// u32:len,char[]:vendorId, u32:N, N x (u32:len,char[]:comment)
-				char* p = (char*) ogg.data + pos;
+				char* p = (char*) ogg.data + ofs;
 				p += *p + 4;
 				u32_t count = *p;
 				p += 4;
@@ -283,7 +291,6 @@ static void stream_ogg(size_t n) {
 				stream.meta_send = true;
 				wake_controller();
 				LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
-				break;
 			}
 			free(ogg.data);
             ogg.data = NULL;
@@ -625,7 +632,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
 	UNLOCK;
 }
 
-void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
+void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
 	struct sockaddr_in addr;
 
 #if EMBEDDED
@@ -726,7 +733,8 @@ void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t he
 	stream.threshold = threshold;
     
     ogg.miss = ogg.match = 0;
-	ogg.state = (codec == 'o' || codec == 'p') ? OGG_SYNC : OGG_OFF;
+	ogg.state = use_ogg ? OGG_SYNC : OGG_OFF;
+    ogg.flac = false;
 
 	UNLOCK;
 }