better handling of AIrPlay network issues - release

This commit is contained in:
philippe44
2023-09-11 18:36:08 -07:00
parent 12ae3d08b6
commit 38fee20680
5 changed files with 47 additions and 32 deletions

View File

@@ -276,6 +276,7 @@ bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
free(ctx);
}
/*----------------------------------------------------------------------------*/
bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
struct sockaddr_in addr;
@@ -325,7 +326,7 @@ bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
break;
}
default:
break;
break;
}
// 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);
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);
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);
kd_free(headers);
success = true;
} else {
kd_free(headers);
LOG_INFO("[%p]: can't connect to remote for %s", ctx, command);
}
free(command);

View File

@@ -119,7 +119,12 @@ static bool cmd_handler(raop_event_t event, ...) {
break;
case RAOP_FLUSH:
displayer_control(DISPLAYER_TIMER_PAUSE);
break;
break;
case RAOP_STALLED:
raop_abort(raop);
actrls_unset();
displayer_control(DISPLAYER_SHUTDOWN);
break;
case RAOP_STOP:
actrls_unset();
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) {
LOG_INFO("forced disconnection");
displayer_control(DISPLAYER_SHUTDOWN);
// in case we can't communicate with AirPlay controller, abort session
if (!raop_cmd(raop, RAOP_STOP, NULL)) raop_abort(raop);
actrls_unset();
if (!raop_cmd(raop, RAOP_STOP, NULL)) cmd_handler(RAOP_STALLED);
else displayer_control(DISPLAYER_SHUTDOWN);
}

View File

@@ -14,7 +14,7 @@
#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_UP, RAOP_VOLUME_DOWN, RAOP_RESUME, RAOP_TOGGLE } raop_event_t ;

View File

@@ -82,7 +82,7 @@ static log_level *loglevel = &raop_loglevel;
#define RTP_SYNC (0x01)
#define NTP_SYNC (0x02)
#define RESEND_TO 200
#define RESEND_TO 250
enum { DATA = 0, CONTROL, TIMING };
@@ -149,6 +149,7 @@ typedef struct rtp_s {
struct alac_codec_s *alac_codec;
int flush_seqno;
bool playing;
int stalled;
raop_data_cb_t data_cb;
raop_cmd_cb_t cmd_cb;
} 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);
ctx->ab_write = seqno;
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)) {
seq_t i;
u32_t now;
// newer than expected
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)
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;
}
// 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);
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);
ctx->ab_write = seqno;
} 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) {
abuf_t *curframe = NULL;
u32_t now, playtime, hold = max((ctx->latency * 1000) / (8 * RAOP_SAMPLE_RATE), 100);
int i;
// 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
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);
// 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);
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;
}
}
@@ -613,7 +615,10 @@ static void rtp_thread_func(void *arg) {
FD_ZERO(&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++)
@@ -631,6 +636,7 @@ static void rtp_thread_func(void *arg) {
continue;
}
assert(plen <= MAX_PACKET);
ctx->stalled = 0;
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);
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;
}

View File

@@ -289,6 +289,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
raop_sync.enabled = !strcasestr(output.device, "BT");
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
break;
case RAOP_STALLED:
case RAOP_STOP:
output.external = 0;
__attribute__ ((fallthrough));
@@ -492,7 +493,6 @@ void decode_restore(int external) {
#if CONFIG_AIRPLAY_SINK
case DECODE_RAOP:
raop_disconnect();
raop_state = RAOP_STOP;
break;
#endif
#if CONFIG_CSPOT_SINK