From 55a8658f3ab424eed641a80f67aa88cb7ecaab96 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Wed, 10 Jan 2024 18:58:47 -0800 Subject: [PATCH] OggFlac & displayer fixes (see CHANGELOG) - release --- CHANGELOG | 6 +++++ components/squeezelite/decode_external.c | 2 ++ components/squeezelite/flac.c | 3 +++ components/squeezelite/output_bt.c | 7 +++-- components/squeezelite/output_i2s.c | 4 +-- components/squeezelite/slimproto.c | 4 ++- components/squeezelite/squeezelite.h | 2 +- components/squeezelite/stream.c | 34 +++++++++++++++--------- 8 files changed, 43 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6c5d7abc..7dff3e67 100644 --- a/CHANGELOG +++ b/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 diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index ba7269e7..09bc0f4a 100644 --- a/components/squeezelite/decode_external.c +++ b/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; diff --git a/components/squeezelite/flac.c b/components/squeezelite/flac.c index ecb54ef6..2afa2e19 100644 --- a/components/squeezelite/flac.c +++ b/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; diff --git a/components/squeezelite/output_bt.c b/components/squeezelite/output_bt.c index c062c0ab..17dc5366 100644 --- a/components/squeezelite/output_bt.c +++ b/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; diff --git a/components/squeezelite/output_i2s.c b/components/squeezelite/output_i2s.c index 2f9d2e89..1803ddbc 100644 --- a/components/squeezelite/output_i2s.c +++ b/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); } diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index de915d63..412e3d62 100644 --- a/components/squeezelite/slimproto.c +++ b/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; diff --git a/components/squeezelite/squeezelite.h b/components/squeezelite/squeezelite.h index 5d2a4669..aaff5777 100644 --- a/components/squeezelite/squeezelite.h +++ b/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 diff --git a/components/squeezelite/stream.c b/components/squeezelite/stream.c index fb6ae0d1..0dd5d008 100644 --- a/components/squeezelite/stream.c +++ b/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; + char** tag = (char* []){ "\x3vorbis", "OpusTags", NULL }; + size_t ofs = 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; - + /* 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; }