mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 20:47:08 +03:00
OggFlac & displayer fixes (see CHANGELOG) - release
This commit is contained in:
@@ -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
|
2024-01-01
|
||||||
- ogg stream are parsed to foward metadata to LMS
|
- ogg stream are parsed to foward metadata to LMS
|
||||||
- fix some ogg parsing on multi-stream containers
|
- fix some ogg parsing on multi-stream containers
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
|
|||||||
output.frames_played = 0;
|
output.frames_played = 0;
|
||||||
output.external = DECODE_RAOP;
|
output.external = DECODE_RAOP;
|
||||||
output.state = OUTPUT_STOPPED;
|
output.state = OUTPUT_STOPPED;
|
||||||
|
|
||||||
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
|
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
|
||||||
LOG_INFO("resizing buffer %u", outputbuf->size);
|
LOG_INFO("resizing buffer %u", outputbuf->size);
|
||||||
break;
|
break;
|
||||||
@@ -377,6 +378,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
|
|||||||
output.state = OUTPUT_STOPPED;
|
output.state = OUTPUT_STOPPED;
|
||||||
sink_state = SINK_ABORT;
|
sink_state = SINK_ABORT;
|
||||||
_buf_flush(outputbuf);
|
_buf_flush(outputbuf);
|
||||||
|
_buf_limit(outputbuf, 0);
|
||||||
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
|
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
|
||||||
LOG_INFO("CSpot start track");
|
LOG_INFO("CSpot start track");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder,
|
|||||||
_buf_inc_readp(streambuf, bytes);
|
_buf_inc_readp(streambuf, bytes);
|
||||||
UNLOCK_S;
|
UNLOCK_S;
|
||||||
|
|
||||||
|
// give some time for stream to acquire data otherwise flac will hammer us
|
||||||
|
if (!end && !bytes) usleep(100 * 1000);
|
||||||
|
|
||||||
*want = bytes;
|
*want = bytes;
|
||||||
|
|
||||||
return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||||
|
|||||||
@@ -139,8 +139,11 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
|
|||||||
u8_t *buf = silencebuf;
|
u8_t *buf = silencebuf;
|
||||||
memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
|
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;
|
oframes += out_frames;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
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
|
// don't update visu if we don't have enough data in buffer (500 ms)
|
||||||
if (silence || output.external || _buf_used(outputbuf) > outputbuf->size >> 2 ) {
|
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);
|
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -393,7 +393,9 @@ static void process_strm(u8_t *pkt, int len) {
|
|||||||
stream_file(header, header_len, strm->threshold * 1024);
|
stream_file(header, header_len, strm->threshold * 1024);
|
||||||
autostart -= 2;
|
autostart -= 2;
|
||||||
} else {
|
} 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);
|
sendSTAT("STMc", 0);
|
||||||
sentSTMu = sentSTMo = sentSTMl = false;
|
sentSTMu = sentSTMo = sentSTMl = false;
|
||||||
|
|||||||
@@ -585,7 +585,7 @@ struct streamstate {
|
|||||||
void stream_init(log_level level, unsigned stream_buf_size);
|
void stream_init(log_level level, unsigned stream_buf_size);
|
||||||
void stream_close(void);
|
void stream_close(void);
|
||||||
void stream_file(const char *header, size_t header_len, unsigned threshold);
|
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);
|
bool stream_disconnect(void);
|
||||||
|
|
||||||
// decode.c
|
// decode.c
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ static sockfd fd;
|
|||||||
struct EXT_RAM_ATTR streamstate stream;
|
struct EXT_RAM_ATTR streamstate stream;
|
||||||
|
|
||||||
static EXT_RAM_ATTR struct {
|
static EXT_RAM_ATTR struct {
|
||||||
|
bool flac;
|
||||||
enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
|
enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
|
||||||
size_t want, miss, match;
|
size_t want, miss, match;
|
||||||
u64_t granule;
|
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;
|
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) {
|
static void stream_ogg(size_t n) {
|
||||||
if (ogg.state == OGG_OFF) return;
|
if (ogg.state == OGG_OFF) return;
|
||||||
u8_t* p = streambuf->writep;
|
u8_t* p = streambuf->writep;
|
||||||
@@ -212,9 +217,8 @@ static void stream_ogg(size_t n) {
|
|||||||
ogg.data = (u8_t*) &ogg.header;
|
ogg.data = (u8_t*) &ogg.header;
|
||||||
ogg.match = 0;
|
ogg.match = 0;
|
||||||
} else {
|
} else {
|
||||||
if (!ogg.match) {
|
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);
|
LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -248,15 +252,19 @@ static void stream_ogg(size_t n) {
|
|||||||
if (ogg.header.granule != -1) ogg.granule = ogg.header.granule;
|
if (ogg.header.granule != -1) ogg.granule = ogg.header.granule;
|
||||||
break;
|
break;
|
||||||
case OGG_PAGE: {
|
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)
|
/* with OggFlac, we need the next page (packet) - VorbisComment is wrapped into a FLAC_METADATA
|
||||||
for (char** tag = (char*[]) { "\x3vorbis", "OpusTags", NULL }; *tag; tag++, offset = 0) {
|
* and except with vorbis, comment packet starts a new page but even in vorbis, it won't span
|
||||||
size_t pos = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &offset);
|
* accross multiple pages */
|
||||||
if (offset != strlen(*tag)) continue;
|
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)
|
// 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;
|
p += *p + 4;
|
||||||
u32_t count = *p;
|
u32_t count = *p;
|
||||||
p += 4;
|
p += 4;
|
||||||
@@ -283,7 +291,6 @@ static void stream_ogg(size_t n) {
|
|||||||
stream.meta_send = true;
|
stream.meta_send = true;
|
||||||
wake_controller();
|
wake_controller();
|
||||||
LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
|
LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
free(ogg.data);
|
free(ogg.data);
|
||||||
ogg.data = NULL;
|
ogg.data = NULL;
|
||||||
@@ -625,7 +632,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
|
|||||||
UNLOCK;
|
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;
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
#if EMBEDDED
|
#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;
|
stream.threshold = threshold;
|
||||||
|
|
||||||
ogg.miss = ogg.match = 0;
|
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;
|
UNLOCK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user