From 3b6299dc1ae812b6fe18633f11c7d3f6d1369557 Mon Sep 17 00:00:00 2001 From: Philippe G Date: Fri, 17 Dec 2021 10:54:25 -0800 Subject: [PATCH] AirPlay: no realloc, safe TCB cleanup, tools convergence --- components/display/core/gds_image.c | 2 +- components/display/display.c | 13 ++++-- components/raop/raop.c | 13 +++--- components/raop/rtp.c | 26 +++++++---- components/raop/rtp.h | 1 + components/raop/util.h | 13 +++++- components/squeezelite/buffer.c | 43 ++++++++++-------- components/squeezelite/decode_external.c | 18 +++++--- components/squeezelite/output.c | 1 - components/squeezelite/slimproto.c | 2 +- components/squeezelite/squeezelite.h | 3 +- components/tools/CMakeLists.txt | 4 +- components/tools/{utf8.c => tools.c} | 55 ++++++++++++++++++++---- components/tools/tools.h | 9 ++++ 14 files changed, 144 insertions(+), 59 deletions(-) rename components/tools/{utf8.c => tools.c} (78%) diff --git a/components/display/core/gds_image.c b/components/display/core/gds_image.c index 3d1fb7ca..cbb392e5 100644 --- a/components/display/core/gds_image.c +++ b/components/display/core/gds_image.c @@ -15,7 +15,7 @@ #include "gds_private.h" #include "gds_image.h" -const char TAG[] = "ImageDec"; +const static char TAG[] = "ImageDec"; #define SCRATCH_SIZE 3100 diff --git a/components/display/display.c b/components/display/display.c index 0c93b447..657dd356 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -193,7 +193,7 @@ static void displayer_task(void *args) { // handler elapsed track time if (displayer.timer && displayer.state == DISPLAYER_ACTIVE) { - char counter[16]; + char line[20], duration[12] = ""; TickType_t tick = xTaskGetTickCount(); uint32_t elapsed = (tick - displayer.tick) * portTICK_PERIOD_MS; @@ -202,9 +202,14 @@ static void displayer_task(void *args) { displayer.tick = tick; displayer.elapsed += elapsed / 1000; xSemaphoreGive(displayer.mutex); - if (displayer.elapsed < 3600) snprintf(counter, 16, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60); - else snprintf(counter, 16, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); - GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter); + if (displayer.duration > 0) { + if (displayer.duration < 3600) snprintf(duration, sizeof(duration), " / %u:%02u", displayer.duration / 60, displayer.duration % 60); + else snprintf(duration, sizeof(duration), " / %u:%02u:%02u", (displayer.duration / 3600) % 100, (displayer.duration % 3600) / 60, displayer.duration % 60); + } + if (displayer.elapsed < 3600) snprintf(line, sizeof(line), "%*u:%02u", sizeof(line) - 1 - strlen(duration) - 3, displayer.elapsed / 60, displayer.elapsed % 60); + else snprintf(line, sizeof(line), "%*u:%02u:%02u", sizeof(line) - 1 - strlen(duration) - 6, (displayer.elapsed / 3600) % 100, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); + strcat(line, duration); + GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, line); timer_sleep = 1000; } else timer_sleep = max(1000 - elapsed, 0); } else timer_sleep = DEFAULT_SLEEP; diff --git a/components/raop/raop.c b/components/raop/raop.c index 33f4f694..23032502 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -255,7 +255,7 @@ void raop_delete(struct raop_ctx_s *ctx) { vTaskDelay(100 / portTICK_PERIOD_MS); ulTaskNotifyTake(pdFALSE, portMAX_DELAY); vTaskDelete(ctx->thread); - heap_caps_free(ctx->xTaskBuffer); + SAFE_TCB_FREE(ctx->xTaskBuffer); // cleanup all session-created items cleanup_rtsp(ctx, true); @@ -525,15 +525,17 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) char *p; rtp_resp_t rtp = { 0 }; short unsigned tport = 0, cport = 0; + uint8_t *buffer = NULL; + size_t size = 0; - // we are about to stream, do something if needed - success = ctx->cmd_cb(RAOP_SETUP); + // we are about to stream, do something if needed and optionally give buffers to play with + success = ctx->cmd_cb(RAOP_SETUP, &buffer, &size); if ((p = strcasestr(buf, "timing_port")) != NULL) sscanf(p, "%*[^=]=%hu", &tport); if ((p = strcasestr(buf, "control_port")) != NULL) sscanf(p, "%*[^=]=%hu", &cport); rtp = rtp_init(ctx->peer, ctx->latency, ctx->rtsp.aeskey, ctx->rtsp.aesiv, - ctx->rtsp.fmtp, cport, tport, ctx->cmd_cb, ctx->data_cb); + ctx->rtsp.fmtp, cport, tport, buffer, size, ctx->cmd_cb, ctx->data_cb); ctx->rtp = rtp.ctx; @@ -672,9 +674,8 @@ void cleanup_rtsp(raop_ctx_t *ctx, bool abort) { ctx->active_remote.running = false; xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY); vTaskDelete(ctx->active_remote.thread); + SAFE_TCB_FREE(ctx->active_remote.xTaskBuffer); vSemaphoreDelete(ctx->active_remote.thread); - - heap_caps_free(ctx->active_remote.xTaskBuffer); #endif memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); LOG_INFO("[%p]: Remote search thread aborted", ctx); diff --git a/components/raop/rtp.c b/components/raop/rtp.c index e12cf95f..e332bd27 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -65,8 +65,8 @@ #define MS2TS(ms, rate) ((((u64_t) (ms)) * (rate)) / 1000) #define TS2MS(ts, rate) NTP2MS(TS2NTP(ts,rate)) - extern log_level raop_loglevel; - static log_level *loglevel = &raop_loglevel; +extern log_level raop_loglevel; +static log_level *loglevel = &raop_loglevel; //#define __RTP_STORE @@ -93,6 +93,7 @@ typedef struct audio_buffer_entry { // decoded audio packets u32_t rtptime, last_resend; s16_t *data; int len; + bool allocated; } abuf_t; typedef struct rtp_s { @@ -152,7 +153,7 @@ typedef struct rtp_s { #define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES) -static void buffer_alloc(abuf_t *audio_buffer, int size); +static void buffer_alloc(abuf_t *audio_buffer, int size, uint8_t *buf, size_t buf_size); static void buffer_release(abuf_t *audio_buffer); static void buffer_reset(abuf_t *audio_buffer); static void buffer_push_packet(rtp_t *ctx); @@ -208,6 +209,7 @@ static struct alac_codec_s* alac_init(int fmtp[32]) { /*---------------------------------------------------------------------------*/ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, char *fmtpstr, short unsigned pCtrlPort, short unsigned pTimingPort, + uint8_t *buffer, size_t size, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) { int i = 0; @@ -260,7 +262,7 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, ctx->alac_codec = alac_init(fmtp); rc &= ctx->alac_codec != NULL; - buffer_alloc(ctx->audio_buffer, ctx->frame_size*4); + buffer_alloc(ctx->audio_buffer, ctx->frame_size*4, buffer, size); // create rtp ports for (i = 0; i < 3; i++) { @@ -311,7 +313,7 @@ void rtp_end(rtp_t *ctx) #else ulTaskNotifyTake(pdFALSE, portMAX_DELAY); vTaskDelete(ctx->thread); - heap_caps_free(ctx->xTaskBuffer); + SAFE_TCB_FREE(ctx->xTaskBuffer); #endif } @@ -369,10 +371,18 @@ void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) { } /*---------------------------------------------------------------------------*/ -static void buffer_alloc(abuf_t *audio_buffer, int size) { +static void buffer_alloc(abuf_t *audio_buffer, int size, uint8_t *buf, size_t buf_size) { int i; for (i = 0; i < BUFFER_FRAMES; i++) { - audio_buffer[i].data = malloc(size); + if (buf && buf_size >= size) { + audio_buffer[i].data = (s16_t*) buf; + audio_buffer[i].allocated = false; + buf += size; + buf_size -= size; + } else { + audio_buffer[i].allocated = true; + audio_buffer[i].data = malloc(size); + } audio_buffer[i].ready = 0; } } @@ -381,7 +391,7 @@ static void buffer_alloc(abuf_t *audio_buffer, int size) { static void buffer_release(abuf_t *audio_buffer) { int i; for (i = 0; i < BUFFER_FRAMES; i++) { - free(audio_buffer[i].data); + if (audio_buffer[i].allocated) free(audio_buffer[i].data); } } diff --git a/components/raop/rtp.h b/components/raop/rtp.h index ebf734f5..a28e1d8a 100644 --- a/components/raop/rtp.h +++ b/components/raop/rtp.h @@ -12,6 +12,7 @@ typedef struct { rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, char *fmtpstr, short unsigned pCtrlPort, short unsigned pTimingPort, + uint8_t *buffer, size_t size, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); void rtp_end(struct rtp_s *ctx); bool rtp_flush(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime, bool exit_locked); diff --git a/components/raop/util.h b/components/raop/util.h index d788f4b2..2ea5d8f0 100644 --- a/components/raop/util.h +++ b/components/raop/util.h @@ -48,6 +48,16 @@ void winsock_init(void); void winsock_close(void); #else char *strlwr(char *str); + +// reason is that TCB might be cleanup in idle task +#define SAFE_TCB_FREE(T) \ + do { \ + int priority = uxTaskPriorityGet(NULL); \ + vTaskPrioritySet(NULL, tskIDLE_PRIORITY); \ + vTaskDelay(1); \ + vTaskPrioritySet(NULL, priority); \ + heap_caps_free(T); \ + } while (0) #endif char* strextract(char *s1, char *beg, char *end); in_addr_t get_localhost(char **name); @@ -67,5 +77,6 @@ char* kd_dump(key_data_t *kd); void kd_free(key_data_t *kd); int _fprintf(FILE *file, ...); - #endif + +#endif diff --git a/components/squeezelite/buffer.c b/components/squeezelite/buffer.c index d8bb48f4..a2e36743 100644 --- a/components/squeezelite/buffer.c +++ b/components/squeezelite/buffer.c @@ -73,13 +73,11 @@ void _buf_flush(struct buffer *buf) { // adjust buffer to multiple of mod bytes so reading in multiple always wraps on frame boundary void buf_adjust(struct buffer *buf, size_t mod) { - size_t size; mutex_lock(buf->mutex); - size = ((unsigned)(buf->base_size / mod)) * mod; - buf->readp = buf->buf; - buf->writep = buf->buf; - buf->wrap = buf->buf + size; - buf->size = size; + buf->base_size = ((size_t)(buf->size / mod)) * mod; + buf->readp = buf->writep = buf->buf; + buf->wrap = buf->buf + buf->base_size; + buf->size = buf->base_size; mutex_unlock(buf->mutex); } @@ -90,16 +88,23 @@ void _buf_resize(struct buffer *buf, size_t size) { buf->buf = malloc(size); if (!buf->buf) { size = buf->size; - buf->buf= malloc(size); - if (!buf->buf) { - size = 0; - } + buf->buf = malloc(size); + if (!buf->buf) size = 0; } - buf->readp = buf->buf; - buf->writep = buf->buf; + buf->writep = buf->readp = buf->buf; buf->wrap = buf->buf + size; - buf->size = size; - buf->base_size = size; + buf->true_size = buf->base_size = buf->size = size; +} + +size_t _buf_limit(struct buffer *buf, size_t limit) { + if (limit) { + buf->size = limit; + buf->readp = buf->writep = buf->buf; + } else { + buf->size = buf->base_size; + } + buf->wrap = buf->buf + buf->size; + return buf->base_size - buf->size; } void _buf_unwrap(struct buffer *buf, size_t cont) { @@ -130,7 +135,9 @@ void _buf_unwrap(struct buffer *buf, size_t cont) { if (len > by) { memmove(buf->buf, buf->buf + by, len - by); buf->writep -= by; - } else buf->writep += buf->size - by; + } else { + buf->writep += buf->size - by; + } return; } @@ -157,8 +164,7 @@ void buf_init(struct buffer *buf, size_t size) { buf->readp = buf->buf; buf->writep = buf->buf; buf->wrap = buf->buf + size; - buf->size = size; - buf->base_size = size; + buf->true_size = buf->base_size = buf->size = size; mutex_create_p(buf->mutex); } @@ -166,8 +172,7 @@ void buf_destroy(struct buffer *buf) { if (buf->buf) { free(buf->buf); buf->buf = NULL; - buf->size = 0; - buf->base_size = 0; + buf->size = buf->base_size = buf->true_size = 0; mutex_destroy(buf->mutex); } } diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index ae729f9e..1431177d 100644 --- a/components/squeezelite/decode_external.c +++ b/components/squeezelite/decode_external.c @@ -122,6 +122,7 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) switch(cmd) { case BT_SINK_AUDIO_STARTED: _buf_flush(outputbuf); + _buf_limit(outputbuf, 0); output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t); output.external = DECODE_BT; output.state = OUTPUT_STOPPED; @@ -251,15 +252,21 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) break; } - case RAOP_SETUP: - // we need a fair bit of space for RTP process - _buf_resize(outputbuf, RAOP_OUTPUT_SIZE); + case RAOP_SETUP: { + uint8_t **buffer = va_arg(args, uint8_t**); + size_t *size = va_arg(args, size_t*); + + // steal buffer tail from outputbuf but do not reallocate + *size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE); + *buffer = outputbuf->writep + RAOP_OUTPUT_SIZE; + 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; + } case RAOP_STREAM: LOG_INFO("Stream", NULL); raop_state = event; @@ -271,10 +278,9 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) break; case RAOP_STOP: case RAOP_FLUSH: - if (event == RAOP_FLUSH) { LOG_INFO("Flush", NULL); } - else { LOG_INFO("Stop", NULL); } + LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop"); + _buf_flush(outputbuf); raop_state = event; - _buf_flush(outputbuf); if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED; abort_sink = true; output.frames_played = 0; diff --git a/components/squeezelite/output.c b/components/squeezelite/output.c index cf420065..3cdb5fbc 100644 --- a/components/squeezelite/output.c +++ b/components/squeezelite/output.c @@ -354,7 +354,6 @@ void output_init_common(log_level level, const char *device, unsigned output_buf loglevel = level; output_buf_size = output_buf_size - (output_buf_size % BYTES_PER_FRAME); - output.init_size = output_buf_size; LOG_DEBUG("outputbuf size: %u", output_buf_size); buf_init(outputbuf, output_buf_size); diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index 87ac31fa..2fb65d27 100644 --- a/components/squeezelite/slimproto.c +++ b/components/squeezelite/slimproto.c @@ -391,7 +391,7 @@ static void process_strm(u8_t *pkt, int len) { #if EMBEDDED if (output.external) decode_restore(output.external); output.external = 0; - _buf_resize(outputbuf, output.init_size); + _buf_limit(outputbuf, 0); #endif output.threshold = strm->output_threshold; output.next_replay_gain = unpackN(&strm->replay_gain); diff --git a/components/squeezelite/squeezelite.h b/components/squeezelite/squeezelite.h index 21dfc6d8..081cb3d8 100644 --- a/components/squeezelite/squeezelite.h +++ b/components/squeezelite/squeezelite.h @@ -532,6 +532,7 @@ struct buffer { u8_t *wrap; size_t size; size_t base_size; + size_t true_size; mutex_type mutex; }; @@ -547,6 +548,7 @@ void _buf_flush(struct buffer *buf); void _buf_unwrap(struct buffer *buf, size_t cont); void buf_adjust(struct buffer *buf, size_t mod); void _buf_resize(struct buffer *buf, size_t size); +size_t _buf_limit(struct buffer *buf, size_t limit); void buf_init(struct buffer *buf, size_t size); void buf_destroy(struct buffer *buf); @@ -665,7 +667,6 @@ struct outputstate { u8_t channels; const char *device; int external; - u32_t init_size; #if ALSA unsigned buffer; unsigned period; diff --git a/components/tools/CMakeLists.txt b/components/tools/CMakeLists.txt index 07b3ba12..14dad4c6 100644 --- a/components/tools/CMakeLists.txt +++ b/components/tools/CMakeLists.txt @@ -1,5 +1,5 @@ -idf_component_register(SRCS operator.cpp utf8.c - REQUIRES esp_common pthread +idf_component_register(SRCS operator.cpp tools.c + REQUIRES esp_common pthread INCLUDE_DIRS . ) diff --git a/components/tools/utf8.c b/components/tools/tools.c similarity index 78% rename from components/tools/utf8.c rename to components/tools/tools.c index 210fa311..cd4021c6 100644 --- a/components/tools/utf8.c +++ b/components/tools/tools.c @@ -1,17 +1,31 @@ +/* + * (c) Philippe G. 20201, philippe_44@outlook.com + * see other copyrights below + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ +#include +#include +#include +#include +#include +#include "tools.h" +#include "esp_log.h" + +const static char TAG[] = "tools"; + +/**************************************************************************************** + * UTF-8 tools + */ + // Copyright (c) 2008-2009 Bjoern Hoehrmann // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. // Copyright (c) 2017 ZephRay // // utf8to1252 - almost equivalent to iconv -f utf-8 -t windows-1252, but better -#include -#include -#include -#include -#include "esp_log.h" - -#define TAG "aa" - #define UTF8_ACCEPT 0 #define UTF8_REJECT 1 @@ -90,4 +104,27 @@ void utf8_decode(char *src) { } *dst = '\0'; -} \ No newline at end of file +} + +/**************************************************************************************** + * URL tools + */ + +static inline char from_hex(char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +void url_decode(char *url) { + char *p, *src = strdup(url); + for (p = src; *src; url++) { + *url = *src++; + if (*url == '%') { + *url = from_hex(*src++) << 4; + *url |= from_hex(*src++); + } else if (*url == '+') { + *url = ' '; + } + } + *url = '\0'; + free(p); +} diff --git a/components/tools/tools.h b/components/tools/tools.h index 5a76150e..3a9b6f33 100644 --- a/components/tools/tools.h +++ b/components/tools/tools.h @@ -10,4 +10,13 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + void utf8_decode(char *src); +void url_decode(char *url); + +#ifdef __cplusplus +} +#endif