diff --git a/components/display/display.c b/components/display/display.c index 8ccfa419..03713e5b 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -301,16 +301,19 @@ void displayer_control(enum displayer_cmd_e cmd, ...) { displayer.string[0] = '\0'; displayer.elapsed = displayer.duration = 0; displayer.offset = displayer.boundary = 0; + display_bus(&displayer, DISPLAY_BUS_TAKE); vTaskResume(displayer.task); break; } case DISPLAYER_SUSPEND: // task will display the line 2 from beginning and suspend displayer.state = DISPLAYER_IDLE; + display_bus(&displayer, DISPLAY_BUS_GIVE); break; case DISPLAYER_SHUTDOWN: // let the task self-suspend (we might be doing i2c_write) displayer.state = DISPLAYER_DOWN; + display_bus(&displayer, DISPLAY_BUS_GIVE); break; case DISPLAYER_TIMER_RUN: if (!displayer.timer) { diff --git a/components/display/display.h b/components/display/display.h index 31f0f5e7..da2bb4f4 100644 --- a/components/display/display.h +++ b/components/display/display.h @@ -28,6 +28,9 @@ So it can conflict with other display direct writes that have been made during sleep. Note that if DISPLAY_SHUTDOWN has been called meanwhile, it (almost) never happens + The display_bus() shall be subscribed by other displayers so that at least + when this one (the main) wants to take control over display, it can signal + that to others */ #define DISPLAY_CLEAR 0x01 @@ -67,7 +70,10 @@ extern struct display_s { void (*draw_box)( int x1, int y1, int x2, int y2, bool fill); } *display; +enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE }; +bool (*display_bus)(void *from, enum display_bus_cmd_e cmd); + void displayer_scroll(char *string, int speed); void displayer_control(enum displayer_cmd_e cmd, ...); void displayer_metadata(char *artist, char *album, char *title); -void displayer_timer(enum displayer_time_e mode, int elapsed, int duration); \ No newline at end of file +void displayer_timer(enum displayer_time_e mode, int elapsed, int duration); diff --git a/components/display/driver_SSD13x6.c b/components/display/driver_SSD13x6.c index baec7144..f2fd8cc5 100644 --- a/components/display/driver_SSD13x6.c +++ b/components/display/driver_SSD13x6.c @@ -167,8 +167,8 @@ static void clear(bool full, ...) { va_end(args); } + SSD13x6_display.dirty = true; if (commit) update(); - else SSD13x6_display.dirty = true; } /**************************************************************************************** @@ -247,9 +247,9 @@ static bool line(int num, int x, int attribute, char *text) { ESP_LOGD(TAG, "displaying %s line %u (x:%d, attr:%u)", text, num+1, x, attribute); // update whole display if requested + SSD13x6_display.dirty = true; if (attribute & DISPLAY_UPDATE) update(); - else SSD13x6_display.dirty = true; - + return width + x < Display.Width; } @@ -340,8 +340,9 @@ static void text(enum display_font_e font, enum display_pos_e pos, int attribute ESP_LOGD(TAG, "SSDD13x6 displaying %s at %u with attribute %u", text, Anchor, attribute); SSD13x6_FontDrawAnchoredString( &Display, Anchor, text, SSD_COLOR_WHITE ); + + SSD13x6_display.dirty = true; if (attribute & DISPLAY_UPDATE) update(); - else SSD13x6_display.dirty = true; va_end(args); } diff --git a/components/raop/raop.c b/components/raop/raop.c index db6c24cb..f5d7b966 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -55,7 +55,7 @@ typedef struct raop_ctx_s { short unsigned port; // RTSP port for AirPlay int sock; // socket of the above struct in_addr peer; // IP of the iDevice (airplay sender) - bool running; + bool running, abort; #ifdef WIN32 pthread_t thread, search_thread; #else @@ -83,6 +83,7 @@ typedef struct raop_ctx_s { TaskHandle_t thread, joiner; StaticTask_t *xTaskBuffer; StackType_t xStack[SEARCH_STACK_SIZE] __attribute__ ((aligned (4)));; + SemaphoreHandle_t destroy_mutex; #endif } active_remote; void *owner; @@ -93,6 +94,7 @@ extern log_level raop_loglevel; static log_level *loglevel = &raop_loglevel; static void* rtsp_thread(void *arg); +static void abort_rtsp(raop_ctx_t *ctx); static bool handle_rtsp(raop_ctx_t *ctx, int sock); static char* rsa_apply(unsigned char *input, int inlen, int *outlen, int mode); @@ -198,6 +200,12 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name, return ctx; } +/*----------------------------------------------------------------------------*/ +void raop_abort(struct raop_ctx_s *ctx) { + LOG_INFO("[%p]: aborting RTSP session at next select() wakeup", ctx); + ctx->abort = true; +} + /*----------------------------------------------------------------------------*/ void raop_delete(struct raop_ctx_s *ctx) { #ifdef WIN32 @@ -270,7 +278,7 @@ if (!ctx) return; } /*----------------------------------------------------------------------------*/ -void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) { +bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) { struct sockaddr_in addr; int sock; char *command = NULL; @@ -323,7 +331,7 @@ void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) { // no command to send to remote or no remote found yet if (!command || !ctx->active_remote.port) { NFREE(command); - return; + return false; } sock = socket(AF_INET, SOCK_STREAM, 0); @@ -354,6 +362,8 @@ void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) { free(command); closesocket(sock); + + return true; } /*----------------------------------------------------------------------------*/ @@ -373,6 +383,7 @@ static void *rtsp_thread(void *arg) { sock = accept(ctx->sock, (struct sockaddr*) &peer, &addrlen); ctx->peer.s_addr = peer.sin_addr.s_addr; + ctx->abort = false; if (sock != -1 && ctx->running) { LOG_INFO("got RTSP connection %u", sock); @@ -383,12 +394,13 @@ static void *rtsp_thread(void *arg) { FD_SET(sock, &rfds); n = select(sock + 1, &rfds, NULL, NULL, &timeout); - - if (!n) continue; + + if (!n && !ctx->abort) continue; if (n > 0) res = handle_rtsp(ctx, sock); - if (n < 0 || !res) { + if (n < 0 || !res || ctx->abort) { + abort_rtsp(ctx); closesocket(sock); LOG_INFO("RTSP close %u", sock); sock = -1; @@ -460,27 +472,6 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) NFREE(ctx->rtsp.aeskey); NFREE(ctx->rtsp.aesiv); NFREE(ctx->rtsp.fmtp); - - // LMS might has taken over the player, leaving us with a running RTP session (should not happen) - if (ctx->rtp) { - LOG_WARN("[%p]: closing unfinished RTP session", ctx); - rtp_end(ctx->rtp); - } - - // same, should not happen unless we have missed a teardown ... - if (ctx->active_remote.running) { - ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); - ctx->active_remote.running = false; - - vTaskResume(ctx->active_remote.thread); - ulTaskNotifyTake(pdFALSE, portMAX_DELAY); - vTaskDelete(ctx->active_remote.thread); - - heap_caps_free(ctx->active_remote.xTaskBuffer); - memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); - - LOG_WARN("[%p]: closing unfinished mDNS search", ctx); - } if ((p = strcasestr(body, "rsaaeskey")) != NULL) { unsigned char *aeskey; @@ -522,6 +513,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) pthread_create(&ctx->search_thread, NULL, &search_remote, ctx); #else ctx->active_remote.running = true; + ctx->active_remote.destroy_mutex = xSemaphoreCreateMutex(); ctx->active_remote.xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); ctx->active_remote.thread = xTaskCreateStatic( (TaskFunction_t) search_remote, "search_remote", SEARCH_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->active_remote.xStack, ctx->active_remote.xTaskBuffer); #endif @@ -600,11 +592,10 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); ctx->active_remote.running = false; - // task might not need to be resumed anyway - vTaskResume(ctx->active_remote.thread); - ulTaskNotifyTake(pdFALSE, portMAX_DELAY); + xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY); vTaskDelete(ctx->active_remote.thread); - + vSemaphoreDelete(ctx->active_remote.thread); + heap_caps_free(ctx->active_remote.xTaskBuffer); LOG_INFO("[%p]: mDNS search task terminated", ctx); @@ -681,6 +672,35 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) return true; } +/*----------------------------------------------------------------------------*/ +void abort_rtsp(raop_ctx_t *ctx) { + // first stop RTP process + if (ctx->rtp) { + rtp_end(ctx->rtp); + ctx->rtp = NULL; + LOG_INFO("[%p]: RTP thread aborted", ctx); + } + + if (ctx->active_remote.running) { + // need to make sure no search is on-going and reclaim task memory + ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); + ctx->active_remote.running = false; + + xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY); + vTaskDelete(ctx->active_remote.thread); + vSemaphoreDelete(ctx->active_remote.thread); + + heap_caps_free(ctx->active_remote.xTaskBuffer); + memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); + + LOG_INFO("[%p]: Remote search thread aborted", ctx); + } + + NFREE(ctx->rtsp.aeskey); + NFREE(ctx->rtsp.aesiv); + NFREE(ctx->rtsp.fmtp); +} + /*----------------------------------------------------------------------------*/ #ifdef WIN32 bool search_remote_cb(mDNSservice_t *slist, void *cookie, bool *stop) { @@ -746,20 +766,10 @@ static void* search_remote(void *args) { mdns_query_results_free(results); } - /* - for some reason which is beyond me, if that tasks gives the semaphore - before the RTSP tasks waits for it, then freeRTOS crashes in queue - management caused by LWIP stack once the RTSP socket is closed. I have - no clue why, but so we'll suspend the tasks as soon as we're done with - search and wait for the resume then give the semaphore - */ - // PS: I know this is not fully race-condition free - if (ctx->active_remote.running) vTaskSuspend(NULL); - xTaskNotifyGive(ctx->active_remote.joiner); - - // now our context will be deleted + // can't use xNotifyGive as it seems LWIP is using it as well + xSemaphoreGive(ctx->active_remote.destroy_mutex); vTaskSuspend(NULL); - + return NULL; } #endif diff --git a/components/raop/raop.h b/components/raop/raop.h index 5c6d0acf..5009be09 100644 --- a/components/raop/raop.h +++ b/components/raop/raop.h @@ -27,6 +27,7 @@ struct raop_ctx_s* raop_create(struct in_addr host, char *name, unsigned char mac[6], int latency, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); void raop_delete(struct raop_ctx_s *ctx); -void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param); +void raop_abort(struct raop_ctx_s *ctx); +bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param); #endif diff --git a/components/raop/raop_sink.c b/components/raop/raop_sink.c index 0627a794..1424e94f 100644 --- a/components/raop/raop_sink.c +++ b/components/raop/raop_sink.c @@ -178,6 +178,7 @@ void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) { void raop_disconnect(void) { LOG_INFO("forced disconnection"); displayer_control(DISPLAYER_SHUTDOWN); - raop_cmd(raop, RAOP_STOP, NULL); + // in case we can't communicate with AirPlay controller, abort session + if (!raop_cmd(raop, RAOP_STOP, NULL)) raop_abort(raop); actrls_unset(); } diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index 3cf0c698..23c0fc08 100644 --- a/components/squeezelite/decode_external.c +++ b/components/squeezelite/decode_external.c @@ -46,7 +46,7 @@ static bool enable_airplay; #define SYNC_NB 5 static raop_event_t raop_state; -static bool raop_expect_stop = false; + static struct { bool enabled, start; s32_t error[SYNC_NB]; @@ -123,9 +123,8 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) LOG_INFO("BT sink started"); break; case BT_SINK_AUDIO_STOPPED: - // do we still need that? if (output.external == DECODE_BT) { - output.state = OUTPUT_OFF; + if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED; LOG_INFO("BT sink stopped"); } break; @@ -136,6 +135,7 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) case BT_SINK_STOP: _buf_flush(outputbuf); output.state = OUTPUT_STOPPED; + output.stop_time = gettime_ms(); LOG_INFO("BT sink stopped"); break; case BT_SINK_PAUSE: @@ -253,18 +253,14 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE; break; case RAOP_STOP: - LOG_INFO("Stop", NULL); - output.state = OUTPUT_OFF; - output.frames_played = 0; - raop_state = event; - break; case RAOP_FLUSH: - LOG_INFO("Flush", NULL); - raop_expect_stop = true; + if (event == RAOP_FLUSH) { LOG_INFO("Flush", NULL); } + else { LOG_INFO("Stop", NULL); } raop_state = event; _buf_flush(outputbuf); - output.state = OUTPUT_STOPPED; + if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED; output.frames_played = 0; + output.stop_time = gettime_ms(); break; case RAOP_PLAY: { LOG_INFO("Play", NULL); diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index c1ca5620..bd805b28 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -84,7 +84,10 @@ extern struct outputstate output; static struct { TaskHandle_t task; SemaphoreHandle_t mutex; -} displayer; + int width, height; + bool dirty; + bool owned; +} displayer = { .dirty = true, .owned = true }; #define LONG_WAKE (10*1000) #define SB_HEIGHT 32 @@ -147,26 +150,28 @@ static u8_t SETD_width; #define LINELEN 40 static log_level loglevel = lINFO; + static bool (*slimp_handler_chain)(u8_t *data, int len); static void (*slimp_loop_chain)(void); static void (*notify_chain)(in_addr_t ip, u16_t hport, u16_t cport); -static int display_width, display_height; -static bool display_dirty = true; +static bool (*display_bus_chain)(void *from, enum display_bus_cmd_e cmd); #define max(a,b) (((a) > (b)) ? (a) : (b)) static void server(in_addr_t ip, u16_t hport, u16_t cport); static void send_server(void); static bool handler(u8_t *data, int len); +static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd); static void vfdc_handler( u8_t *_data, int bytes_read); static void grfe_handler( u8_t *data, int len); static void grfb_handler(u8_t *data, int len); static void grfs_handler(u8_t *data, int len); static void grfg_handler(u8_t *data, int len); -static void visu_handler( u8_t *data, int len); +static void visu_handler(u8_t *data, int len); static void displayer_task(void* arg); + /* scrolling undocumented information grfs B: screen number @@ -208,8 +213,8 @@ bool sb_display_init(void) { } // need to force height to 32 maximum - display_width = display->width; - display_height = min(display->height, SB_HEIGHT); + displayer.width = display->width; + displayer.height = min(display->height, SB_HEIGHT); SETD_width = display->width; // create visu configuration @@ -223,10 +228,10 @@ bool sb_display_init(void) { displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer); // size scroller - scroller.scroll.max = (display_width * display_height / 8) * 10; + scroller.scroll.max = (displayer.width * displayer.height / 8) * 10; scroller.scroll.frame = malloc(scroller.scroll.max); - scroller.back.frame = malloc(display_width * display_height / 8); - scroller.frame = malloc(display_width * display_height / 8); + scroller.back.frame = malloc(displayer.width * displayer.height / 8); + scroller.frame = malloc(displayer.width * displayer.height / 8); // chain handlers slimp_handler_chain = slimp_handler; @@ -238,9 +243,39 @@ bool sb_display_init(void) { notify_chain = server_notify; server_notify = server; + display_bus_chain = display_bus; + display_bus = display_bus_handler; + return true; } +/**************************************************************************************** + * Receive display bus commands + */ +static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd) { + // don't answer to own requests + if (from == &displayer) return false ; + + LOG_INFO("Display bus command %d", cmd); + + xSemaphoreTake(displayer.mutex, portMAX_DELAY); + + switch (cmd) { + case DISPLAY_BUS_TAKE: + displayer.owned = false; + break; + case DISPLAY_BUS_GIVE: + displayer.owned = true; + break; + } + + xSemaphoreGive(displayer.mutex); + + if (display_bus_chain) return (*display_bus_chain)(from, cmd); + else return true; +} + + /**************************************************************************************** * Send message to server (ANIC at that time) */ @@ -289,9 +324,9 @@ static void send_server(void) { static void server(in_addr_t ip, u16_t hport, u16_t cport) { char msg[32]; sprintf(msg, "%s:%hu", inet_ntoa(ip), hport); - display->text(DISPLAY_FONT_DEFAULT, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, msg); + if (displayer.owned) display->text(DISPLAY_FONT_DEFAULT, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, msg); SETD_width = display->width; - display_dirty = true; + displayer.dirty = true; if (notify_chain) (*notify_chain)(ip, hport, cport); } @@ -301,24 +336,21 @@ static void server(in_addr_t ip, u16_t hport, u16_t cport) { static bool handler(u8_t *data, int len){ bool res = true; - // don't do anything if we dont own the display (no lock needed) - if (!output.external || output.state < OUTPUT_STOPPED) { - if (!strncmp((char*) data, "vfdc", 4)) { - vfdc_handler(data, len); - } else if (!strncmp((char*) data, "grfe", 4)) { - grfe_handler(data, len); - } else if (!strncmp((char*) data, "grfb", 4)) { - grfb_handler(data, len); - } else if (!strncmp((char*) data, "grfs", 4)) { - grfs_handler(data, len); - } else if (!strncmp((char*) data, "grfg", 4)) { - grfg_handler(data, len); - } else if (!strncmp((char*) data, "visu", 4)) { - visu_handler(data, len); - } else { - res = false; - } - } + if (!strncmp((char*) data, "vfdc", 4)) { + vfdc_handler(data, len); + } else if (!strncmp((char*) data, "grfe", 4)) { + grfe_handler(data, len); + } else if (!strncmp((char*) data, "grfb", 4)) { + grfb_handler(data, len); + } else if (!strncmp((char*) data, "grfs", 4)) { + grfs_handler(data, len); + } else if (!strncmp((char*) data, "grfg", 4)) { + grfg_handler(data, len); + } else if (!strncmp((char*) data, "visu", 4)) { + visu_handler(data, len); + } else { + res = false; + } // chain protocol handlers (bitwise or is fine) if (*slimp_handler_chain) res |= (*slimp_handler_chain)(data, len); @@ -448,20 +480,23 @@ static void grfe_handler( u8_t *data, int len) { scroller.active = false; - // if we are displaying visu on a small screen, do not do screen update + // we are not in control or we are displaying visu on a small screen, do not do screen update if (visu.mode && !visu.col && visu.row < SB_HEIGHT) { xSemaphoreGive(displayer.mutex); return; } - // did we have something that might have write on the bottom of a SB_HEIGHT+ display - if (display_dirty) { - display->clear(true); - display_dirty = false; - } + if (displayer.owned) { + // did we have something that might have write on the bottom of a SB_HEIGHT+ display + if (displayer.dirty) { + display->clear(true); + displayer.dirty = false; + } - display->draw_cbr(data + sizeof(struct grfe_packet), display_width, display_height); - display->update(); + // draw new frame + display->draw_cbr(data + sizeof(struct grfe_packet), displayer.width, displayer.height); + display->update(); + } xSemaphoreGive(displayer.mutex); @@ -518,7 +553,7 @@ static void grfs_handler(u8_t *data, int len) { scroller.first = true; // background excludes space taken by visu (if any) - scroller.back.width = display_width - ((visu.mode && visu.row < SB_HEIGHT) ? visu.width : 0); + scroller.back.width = displayer.width - ((visu.mode && visu.row < SB_HEIGHT) ? visu.width : 0); // set scroller steps & beginning if (pkt->direction == 1) { @@ -557,10 +592,14 @@ static void grfg_handler(u8_t *data, int len) { memcpy(scroller.back.frame, data + sizeof(struct grfg_packet), len - sizeof(struct grfg_packet)); // update display asynchronously (frames are oganized by columns) - memcpy(scroller.frame, scroller.back.frame, scroller.back.width * display_height / 8); - for (int i = 0; i < scroller.width * display_height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * display_height / 8 + i]; - display->draw_cbr(scroller.frame, scroller.back.width, display_height); - display->update(); + memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8); + for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i]; + + // can only write if we really own display + if (displayer.owned) { + display->draw_cbr(scroller.frame, scroller.back.width, displayer.height); + display->update(); + } // now we can active scrolling, but only if we are not on a small screen if (!visu.mode || visu.col || visu.row >= SB_HEIGHT) scroller.active = true; @@ -578,6 +617,7 @@ static void grfg_handler(u8_t *data, int len) { * Update visualization bars */ static void visu_update(void) { + // no need to protect against no woning the display as we are playing if (pthread_mutex_trylock(&visu_export.mutex)) return; // not enough samples @@ -731,6 +771,7 @@ static void visu_handler( u8_t *data, int len) { // give up if not enough space if (visu.bar_width < 0) { visu.mode = VISU_BLANK; + LOG_WARN("Not enough room for displaying visu"); } else { // de-activate scroller if we are taking main screen if (visu.row < SB_HEIGHT) scroller.active = false; @@ -781,10 +822,10 @@ static void displayer_task(void *args) { // do we have more to scroll (scroll.width is the last column from which we havea full zone) if (scroller.by > 0 ? (scroller.scrolled <= scroller.scroll.width) : (scroller.scrolled >= 0)) { - memcpy(scroller.frame, scroller.back.frame, scroller.back.width * display_height / 8); - for (int i = 0; i < scroller.width * display_height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * display_height / 8 + i]; + memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8); + for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i]; scroller.scrolled += scroller.by; - display->draw_cbr(scroller.frame, scroller.width, display_height); + if (displayer.owned) display->draw_cbr(scroller.frame, scroller.width, displayer.height); // short sleep & don't need background update scroller.wake = scroller.speed; @@ -813,7 +854,8 @@ static void displayer_task(void *args) { visu.wake = 100; } - display->update(); + // need to make sure we own display + if (displayer.owned) display->update(); // release semaphore and sleep what's needed xSemaphoreGive(displayer.mutex); diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index 73d59d66..af7f7bde 100644 --- a/components/squeezelite/slimproto.c +++ b/components/squeezelite/slimproto.c @@ -690,46 +690,47 @@ static void slimproto_run() { UNLOCK_D; LOCK_O; - status.output_full = _buf_used(outputbuf); - status.output_size = outputbuf->size; - status.frames_played = output.frames_played_dmp; - status.current_sample_rate = output.current_sample_rate; - status.updated = output.updated; - status.device_frames = output.device_frames; - - if (output.track_started) { - _sendSTMs = true; - output.track_started = false; - status.stream_start = output.track_start_time; - } + if (!output.external) { + status.output_full = _buf_used(outputbuf); + status.output_size = outputbuf->size; + status.frames_played = output.frames_played_dmp; + status.current_sample_rate = output.current_sample_rate; + status.updated = output.updated; + status.device_frames = output.device_frames; + + if (output.track_started) { + _sendSTMs = true; + output.track_started = false; + status.stream_start = output.track_start_time; + } #if PORTAUDIO - if (output.pa_reopen) { - _pa_open(); - output.pa_reopen = false; - } + if (output.pa_reopen) { + _pa_open(); + output.pa_reopen = false; + } #endif - if (_start_output && (output.state == OUTPUT_STOPPED || output.state == OUTPUT_OFF)) { - output.state = OUTPUT_BUFFER; - } - if (!output.external && output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT && - _decode_state == DECODE_STOPPED) { + if (_start_output && (output.state == OUTPUT_STOPPED || output.state == OUTPUT_OFF)) { + output.state = OUTPUT_BUFFER; + } + if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT && + _decode_state == DECODE_STOPPED) { - _sendSTMu = true; - sentSTMu = true; - LOG_DEBUG("output underrun"); - output.state = OUTPUT_STOPPED; - output.stop_time = now; - } - if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) { - - _sendSTMo = true; - sentSTMo = true; - } + _sendSTMu = true; + sentSTMu = true; + LOG_DEBUG("output underrun"); + output.state = OUTPUT_STOPPED; + output.stop_time = now; + } + if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) { + _sendSTMo = true; + sentSTMo = true; + } + } if (output.state == OUTPUT_STOPPED && output.idle_to && (now - output.stop_time > output.idle_to)) { output.state = OUTPUT_OFF; LOG_DEBUG("output timeout"); - } - if (!output.external && output.state == OUTPUT_RUNNING && now - status.last > 1000) { + } + if (output.state == OUTPUT_RUNNING && now - status.last > 1000) { _sendSTMt = true; status.last = now; }