From e8c459b4f7c8596857a925643181c28589d99cad Mon Sep 17 00:00:00 2001 From: ckdo Date: Tue, 16 Feb 2021 20:19:30 +0200 Subject: [PATCH] raop: set initial volume before sending RTP packets --- src/modules/raop/raop-client.c | 15 ++++++++++ src/modules/raop/raop-client.h | 2 ++ src/modules/raop/raop-sink.c | 52 ++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c index 18f8626dc..90094c70f 100644 --- a/src/modules/raop/raop-client.c +++ b/src/modules/raop/raop-client.c @@ -130,6 +130,8 @@ struct pa_raop_client { pa_raop_client_state_cb_t state_callback; void *state_userdata; + + pa_volume_t initial_volume; }; /* Audio TCP packet header [16x8] (cf. rfc4571): @@ -1200,6 +1202,13 @@ connect_finish: case STATE_SET_PARAMETER: { pa_log_debug("RAOP: SET_PARAMETER"); + if (c->initial_volume != 0){ + c->initial_volume = 0; + // We just have set initial volume, so raise PA_RAOP_VOLUME_INIT to chain + if (c->state_callback) + c->state_callback((int) PA_RAOP_VOLUME_INIT, c->state_userdata); + } + break; } @@ -1251,6 +1260,7 @@ connect_finish: /* Polling sockets will be closed by sink */ c->udp_cfd = c->udp_tfd = c->udp_tport = -1; c->tcp_sfd = -1; + c->initial_volume = 0; pa_log_error("RTSP control channel closed (disconnected)"); @@ -1508,6 +1518,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot c->udp_cfd = -1; c->udp_tfd = -1; c->udp_tport = -1; + c->initial_volume = 0; c->secret = NULL; if (c->encryption != PA_RAOP_ENCRYPTION_NONE) @@ -1691,6 +1702,10 @@ int pa_raop_client_stream(pa_raop_client *c) { return rv; } +void pa_raop_client_set_initial_volume(pa_raop_client *c, pa_volume_t initial_volume) { + c->initial_volume = initial_volume; +} + int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) { char *param; int rv = 0; diff --git a/src/modules/raop/raop-client.h b/src/modules/raop/raop-client.h index aab22d98d..3a98d61a1 100644 --- a/src/modules/raop/raop-client.h +++ b/src/modules/raop/raop-client.h @@ -53,6 +53,7 @@ typedef enum pa_raop_state { PA_RAOP_AUTHENTICATED, PA_RAOP_CONNECTED, PA_RAOP_RECORDING, + PA_RAOP_VOLUME_INIT, PA_RAOP_DISCONNECTED } pa_raop_state_t; @@ -83,6 +84,7 @@ ssize_t pa_raop_client_send_audio_packet(pa_raop_client *c, pa_memchunk *block, typedef void (*pa_raop_client_state_cb_t)(pa_raop_state_t state, void *userdata); void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata); void pa_raop_client_set_tport(pa_raop_client *c, int udp_tport); +void pa_raop_client_set_initial_volume(pa_raop_client *c, pa_volume_t initial_volume); void pa_raop_client_send_progress (pa_raop_client *c); #endif diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c index 4f62b29ad..ed487bb2e 100644 --- a/src/modules/raop/raop-sink.c +++ b/src/modules/raop/raop-sink.c @@ -122,6 +122,8 @@ static void userdata_free(struct userdata *u); static void sink_set_volume_cb(pa_sink *s); +static pa_volume_t pa_raop_sink_get_hw_volume(pa_sink *s); + static void raop_state_cb(pa_raop_state_t state, void *userdata) { struct userdata *u = userdata; @@ -213,14 +215,18 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } case PA_RAOP_CONNECTED: { + pa_volume_t initial_volume; + pa_assert(!u->rtpoll_item); u->oob = pa_raop_client_register_pollfd(u->raop, u->rtpoll, &u->rtpoll_item); + initial_volume = pa_raop_sink_get_hw_volume(u->sink); + pa_raop_client_set_initial_volume(u->raop, initial_volume); return 0; } - case PA_RAOP_RECORDING: { + case PA_RAOP_VOLUME_INIT: { pa_usec_t now; now = pa_rtclock_now(); @@ -234,13 +240,18 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_rtpoll_set_timer_disabled(u->rtpoll); pa_raop_client_flush(u->raop); } else { - /* Set the initial volume */ - sink_set_volume_cb(u->sink); - pa_sink_process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, data, offset, chunk); + pa_raop_client_send_progress(u->raop); } return 0; } + case PA_RAOP_RECORDING: { + /* Set the initial volume */ + sink_set_volume_cb(u->sink); + pa_sink_process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, data, offset, chunk); + + return 0; + } case PA_RAOP_INVALID_STATE: case PA_RAOP_DISCONNECTED: { @@ -369,6 +380,25 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, return 0; } +static pa_volume_t pa_raop_sink_get_hw_volume(pa_sink *s){ + struct userdata *u = s->userdata; + pa_volume_t v, v_orig; + + pa_assert(u); + + /* Calculate the max volume of all channels. + * We'll use this as our (single) volume on the APEX device and emulate + * any variation in channel volumes in software. */ + v = pa_cvolume_max(&s->real_volume); + + v_orig = v; + v = pa_raop_client_adjust_volume(u->raop, v_orig); + + pa_log_debug("Volume adjusted: orig=%u adjusted=%u", v_orig, v); + + return v; +} + static void sink_set_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; pa_cvolume hw; @@ -494,8 +524,20 @@ static void thread_func(void *userdata) { goto fail; } if (pollfd->revents & pollfd->events) { + struct sockaddr_in srcaddr; + socklen_t addrlen; + pollfd->revents = 0; - read = pa_read(pollfd->fd, packet, sizeof(packet), NULL); + // read = pa_read(pollfd->fd, packet, sizeof(packet), NULL); + // Newest Airplay devices does not provide response to SETUP request if we do not respond + // to timing request packets immediatly after the setup request + // To do this we use the source port of incoming packets + // TBD: Code rework (move this in raop client?) + Ipv6 Support + addrlen = sizeof(struct sockaddr_in); + read = recvfrom(pollfd->fd, packet, sizeof(packet), 0, (struct sockaddr *)&srcaddr, &addrlen); + + pa_raop_client_set_tport(u->raop, htons(srcaddr.sin_port)); + pa_log_debug("Source: %d", htons(srcaddr.sin_port)); pa_raop_client_handle_oob_packet(u->raop, pollfd->fd, packet, read); if (pa_raop_client_is_timing_fd(u->raop, pollfd->fd)) { last_timing = pa_rtclock_now();