mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2026-01-27 04:40:48 +03:00
better handling of AIrPlay network issues - release
This commit is contained in:
@@ -276,6 +276,7 @@ bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|||||||
|
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
bool 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;
|
struct sockaddr_in addr;
|
||||||
@@ -325,7 +326,7 @@ bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no command to send to remote or no remote found yet
|
// no command to send to remote or no remote found yet
|
||||||
@@ -348,16 +349,19 @@ bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|||||||
|
|
||||||
asprintf(&method, "GET /ctrl-int/1/%s HTTP/1.0", command);
|
asprintf(&method, "GET /ctrl-int/1/%s HTTP/1.0", command);
|
||||||
kd_add(headers, "Active-Remote", ctx->active_remote.id);
|
kd_add(headers, "Active-Remote", ctx->active_remote.id);
|
||||||
kd_add(headers, "Connection", "close");
|
kd_add(headers, "Connection", "close");
|
||||||
|
|
||||||
buf = http_send(sock, method, headers);
|
buf = http_send(sock, method, headers);
|
||||||
len = recv(sock, resp, 512, 0);
|
len = recv(sock, resp, 512, 0);
|
||||||
if (len > 0) resp[len-1] = '\0';
|
if (len > 0) resp[len-1] = '\0';
|
||||||
|
LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
|
||||||
|
|
||||||
|
NFREE(method);
|
||||||
NFREE(buf);
|
NFREE(buf);
|
||||||
kd_free(headers);
|
kd_free(headers);
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
kd_free(headers);
|
LOG_INFO("[%p]: can't connect to remote for %s", ctx, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(command);
|
free(command);
|
||||||
|
|||||||
@@ -119,7 +119,12 @@ static bool cmd_handler(raop_event_t event, ...) {
|
|||||||
break;
|
break;
|
||||||
case RAOP_FLUSH:
|
case RAOP_FLUSH:
|
||||||
displayer_control(DISPLAYER_TIMER_PAUSE);
|
displayer_control(DISPLAYER_TIMER_PAUSE);
|
||||||
break;
|
break;
|
||||||
|
case RAOP_STALLED:
|
||||||
|
raop_abort(raop);
|
||||||
|
actrls_unset();
|
||||||
|
displayer_control(DISPLAYER_SHUTDOWN);
|
||||||
|
break;
|
||||||
case RAOP_STOP:
|
case RAOP_STOP:
|
||||||
actrls_unset();
|
actrls_unset();
|
||||||
displayer_control(DISPLAYER_SUSPEND);
|
displayer_control(DISPLAYER_SUSPEND);
|
||||||
@@ -191,8 +196,7 @@ void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) {
|
|||||||
*/
|
*/
|
||||||
void raop_disconnect(void) {
|
void raop_disconnect(void) {
|
||||||
LOG_INFO("forced disconnection");
|
LOG_INFO("forced disconnection");
|
||||||
displayer_control(DISPLAYER_SHUTDOWN);
|
|
||||||
// in case we can't communicate with AirPlay controller, abort session
|
// in case we can't communicate with AirPlay controller, abort session
|
||||||
if (!raop_cmd(raop, RAOP_STOP, NULL)) raop_abort(raop);
|
if (!raop_cmd(raop, RAOP_STOP, NULL)) cmd_handler(RAOP_STALLED);
|
||||||
actrls_unset();
|
else displayer_control(DISPLAYER_SHUTDOWN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#define RAOP_SAMPLE_RATE 44100
|
#define RAOP_SAMPLE_RATE 44100
|
||||||
|
|
||||||
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_METADATA, RAOP_ARTWORK, RAOP_PROGRESS, RAOP_PAUSE, RAOP_STOP,
|
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_METADATA, RAOP_ARTWORK, RAOP_PROGRESS, RAOP_PAUSE, RAOP_STOP, RAOP_STALLED,
|
||||||
RAOP_VOLUME, RAOP_TIMING, RAOP_PREV, RAOP_NEXT, RAOP_REW, RAOP_FWD,
|
RAOP_VOLUME, RAOP_TIMING, RAOP_PREV, RAOP_NEXT, RAOP_REW, RAOP_FWD,
|
||||||
RAOP_VOLUME_UP, RAOP_VOLUME_DOWN, RAOP_RESUME, RAOP_TOGGLE } raop_event_t ;
|
RAOP_VOLUME_UP, RAOP_VOLUME_DOWN, RAOP_RESUME, RAOP_TOGGLE } raop_event_t ;
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ static log_level *loglevel = &raop_loglevel;
|
|||||||
#define RTP_SYNC (0x01)
|
#define RTP_SYNC (0x01)
|
||||||
#define NTP_SYNC (0x02)
|
#define NTP_SYNC (0x02)
|
||||||
|
|
||||||
#define RESEND_TO 200
|
#define RESEND_TO 250
|
||||||
|
|
||||||
enum { DATA = 0, CONTROL, TIMING };
|
enum { DATA = 0, CONTROL, TIMING };
|
||||||
|
|
||||||
@@ -149,6 +149,7 @@ typedef struct rtp_s {
|
|||||||
struct alac_codec_s *alac_codec;
|
struct alac_codec_s *alac_codec;
|
||||||
int flush_seqno;
|
int flush_seqno;
|
||||||
bool playing;
|
bool playing;
|
||||||
|
int stalled;
|
||||||
raop_data_cb_t data_cb;
|
raop_data_cb_t data_cb;
|
||||||
raop_cmd_cb_t cmd_cb;
|
raop_cmd_cb_t cmd_cb;
|
||||||
} rtp_t;
|
} rtp_t;
|
||||||
@@ -466,26 +467,27 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
|
|||||||
abuf = ctx->audio_buffer + BUFIDX(seqno);
|
abuf = ctx->audio_buffer + BUFIDX(seqno);
|
||||||
ctx->ab_write = seqno;
|
ctx->ab_write = seqno;
|
||||||
LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read);
|
LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read);
|
||||||
|
|
||||||
} else if (seq_order(ctx->ab_write, seqno)) {
|
} else if (seq_order(ctx->ab_write, seqno)) {
|
||||||
seq_t i;
|
|
||||||
u32_t now;
|
|
||||||
|
|
||||||
// newer than expected
|
// newer than expected
|
||||||
if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_write - 1)) {
|
if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_write - 1)) {
|
||||||
// only get rtp latency-1 frames back (last one is seqno)
|
// this is a shitstorm, reset buffer
|
||||||
LOG_WARN("[%p] too many missing frames %hu seq: %hu, (W:%hu R:%hu)", ctx, seqno - ctx->ab_write - 1, seqno, ctx->ab_write, ctx->ab_read);
|
LOG_WARN("[%p] too many missing frames %hu seq: %hu, (W:%hu R:%hu)", ctx, seqno - ctx->ab_write - 1, seqno, ctx->ab_write, ctx->ab_read);
|
||||||
ctx->ab_write = seqno - ctx->latency / ctx->frame_size;
|
ctx->ab_read = seqno;
|
||||||
}
|
} else {
|
||||||
|
// request re-send missed frames and evaluate resent date as a whole *after*
|
||||||
|
rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1);
|
||||||
|
|
||||||
|
// resend date is after all requests have been sent
|
||||||
|
u32_t now = gettime_ms();
|
||||||
|
|
||||||
|
// set expected timing of missed frames for buffer_push_packet and set last_resend date
|
||||||
|
for (seq_t i = ctx->ab_write + 1; seq_order(i, seqno); i++) {
|
||||||
|
ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size;
|
||||||
|
ctx->audio_buffer[BUFIDX(i)].last_resend = now;
|
||||||
|
}
|
||||||
|
LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
|
||||||
|
}
|
||||||
|
|
||||||
// need to request re-send and adjust timing of gaps
|
|
||||||
rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1);
|
|
||||||
for (now = gettime_ms(), i = ctx->ab_write + 1; seq_order(i, seqno); i++) {
|
|
||||||
ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size;
|
|
||||||
ctx->audio_buffer[BUFIDX(i)].last_resend = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
|
|
||||||
abuf = ctx->audio_buffer + BUFIDX(seqno);
|
abuf = ctx->audio_buffer + BUFIDX(seqno);
|
||||||
ctx->ab_write = seqno;
|
ctx->ab_write = seqno;
|
||||||
} else if (seq_order(ctx->ab_read, seqno + 1)) {
|
} else if (seq_order(ctx->ab_read, seqno + 1)) {
|
||||||
@@ -524,10 +526,9 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
|
|||||||
static void buffer_push_packet(rtp_t *ctx) {
|
static void buffer_push_packet(rtp_t *ctx) {
|
||||||
abuf_t *curframe = NULL;
|
abuf_t *curframe = NULL;
|
||||||
u32_t now, playtime, hold = max((ctx->latency * 1000) / (8 * RAOP_SAMPLE_RATE), 100);
|
u32_t now, playtime, hold = max((ctx->latency * 1000) / (8 * RAOP_SAMPLE_RATE), 100);
|
||||||
int i;
|
|
||||||
|
|
||||||
// not ready to play yet
|
// not ready to play yet
|
||||||
if (!ctx->playing || ctx->synchro.status != (RTP_SYNC | NTP_SYNC)) return;
|
if (!ctx->playing || ctx->synchro.status != (RTP_SYNC | NTP_SYNC)) return;
|
||||||
|
|
||||||
// there is always at least one frame in the buffer
|
// there is always at least one frame in the buffer
|
||||||
do {
|
do {
|
||||||
@@ -573,10 +574,11 @@ static void buffer_push_packet(rtp_t *ctx) {
|
|||||||
LOG_SDEBUG("playtime %u %d [W:%hu R:%hu] %d", playtime, playtime - now, ctx->ab_write, ctx->ab_read, curframe->ready);
|
LOG_SDEBUG("playtime %u %d [W:%hu R:%hu] %d", playtime, playtime - now, ctx->ab_write, ctx->ab_read, curframe->ready);
|
||||||
|
|
||||||
// each missing packet will be requested up to (latency_frames / 16) times
|
// each missing packet will be requested up to (latency_frames / 16) times
|
||||||
for (i = 0; seq_order(ctx->ab_read + i, ctx->ab_write); i += 16) {
|
for (int i = 0; seq_order(ctx->ab_read + i, ctx->ab_write); i += 16) {
|
||||||
abuf_t *frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
|
abuf_t *frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
|
||||||
if (!frame->ready && now - frame->last_resend > RESEND_TO) {
|
if (!frame->ready && now - frame->last_resend > RESEND_TO) {
|
||||||
rtp_request_resend(ctx, ctx->ab_read + i, ctx->ab_read + i);
|
// stop if one fails
|
||||||
|
if (!rtp_request_resend(ctx, ctx->ab_read + i, ctx->ab_read + i)) break;
|
||||||
frame->last_resend = now;
|
frame->last_resend = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,7 +615,10 @@ static void rtp_thread_func(void *arg) {
|
|||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
for (i = 0; i < 3; i++) { FD_SET(ctx->rtp_sockets[i].sock, &fds); }
|
for (i = 0; i < 3; i++) { FD_SET(ctx->rtp_sockets[i].sock, &fds); }
|
||||||
|
|
||||||
|
if (select(sock + 1, &fds, NULL, NULL, &timeout) <= 0) {
|
||||||
|
if (ctx->stalled++ == 30*10) ctx->cmd_cb(RAOP_STALLED);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
@@ -631,6 +636,7 @@ static void rtp_thread_func(void *arg) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(plen <= MAX_PACKET);
|
||||||
ctx->stalled = 0;
|
ctx->stalled = 0;
|
||||||
|
|
||||||
type = packet[1] & ~0x80;
|
type = packet[1] & ~0x80;
|
||||||
@@ -823,6 +829,7 @@ static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) {
|
|||||||
ctx->rtp_host.sin_port = htons(ctx->rtp_sockets[CONTROL].rport);
|
ctx->rtp_host.sin_port = htons(ctx->rtp_sockets[CONTROL].rport);
|
||||||
|
|
||||||
if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), MSG_DONTWAIT, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) {
|
if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), MSG_DONTWAIT, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) {
|
||||||
|
LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
|
|||||||
raop_sync.enabled = !strcasestr(output.device, "BT");
|
raop_sync.enabled = !strcasestr(output.device, "BT");
|
||||||
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
|
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
|
||||||
break;
|
break;
|
||||||
|
case RAOP_STALLED:
|
||||||
case RAOP_STOP:
|
case RAOP_STOP:
|
||||||
output.external = 0;
|
output.external = 0;
|
||||||
__attribute__ ((fallthrough));
|
__attribute__ ((fallthrough));
|
||||||
@@ -492,7 +493,6 @@ void decode_restore(int external) {
|
|||||||
#if CONFIG_AIRPLAY_SINK
|
#if CONFIG_AIRPLAY_SINK
|
||||||
case DECODE_RAOP:
|
case DECODE_RAOP:
|
||||||
raop_disconnect();
|
raop_disconnect();
|
||||||
raop_state = RAOP_STOP;
|
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if CONFIG_CSPOT_SINK
|
#if CONFIG_CSPOT_SINK
|
||||||
|
|||||||
Reference in New Issue
Block a user