From e84fb5f0c934ff2b8054a582c2ce09bcb05cba40 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Wed, 10 Oct 2007 20:26:43 +0100 Subject: [PATCH] speaker beforepoll() now gets to modify the timeout. This allows speaker-network to transmit at a constant rate instead of bursting once a second. Organization: Straylight/Edgeware From: Richard Kettlewell --- server/speaker-alsa.c | 2 +- server/speaker-command.c | 2 +- server/speaker-coreaudio.c | 2 +- server/speaker-network.c | 19 +++++++++++++++---- server/speaker-oss.c | 2 +- server/speaker.c | 2 +- server/speaker.h | 14 ++++++++++---- 7 files changed, 30 insertions(+), 13 deletions(-) diff --git a/server/speaker-alsa.c b/server/speaker-alsa.c index 731c4ff..8ed01a8 100644 --- a/server/speaker-alsa.c +++ b/server/speaker-alsa.c @@ -219,7 +219,7 @@ static size_t alsa_play(size_t frames) { static int alsa_slots, alsa_nslots = -1; /** @brief Fill in poll fd array for ALSA */ -static void alsa_beforepoll(void) { +static void alsa_beforepoll(int attribute((unused)) *timeoutp) { /* We send sample data to ALSA as fast as it can accept it, relying on * the fact that it has a relatively small buffer to minimize pause * latency. */ diff --git a/server/speaker-command.c b/server/speaker-command.c index 088d0cd..17efb99 100644 --- a/server/speaker-command.c +++ b/server/speaker-command.c @@ -96,7 +96,7 @@ static size_t command_play(size_t frames) { } /** @brief Update poll array for writing to subprocess */ -static void command_beforepoll(void) { +static void command_beforepoll(int attribute((unused)) *timeoutp) { /* We send sample data to the subprocess as fast as it can accept it. * This isn't ideal as pause latency can be very high as a result. */ if(cmdfd >= 0) diff --git a/server/speaker-coreaudio.c b/server/speaker-coreaudio.c index 75a69dd..4f46891 100644 --- a/server/speaker-coreaudio.c +++ b/server/speaker-coreaudio.c @@ -202,7 +202,7 @@ static size_t coreaudio_play(size_t frames) { } /** @brief Fill in poll fd array for Core Audio */ -static void coreaudio_beforepoll(void) { +static void coreaudio_beforepoll(int attribute((unused)) *timeoutp) { pfd_slot = addfd(pfd[1], POLLOUT); } diff --git a/server/speaker-network.c b/server/speaker-network.c index 89412f5..c8edbfe 100644 --- a/server/speaker-network.c +++ b/server/speaker-network.c @@ -306,14 +306,16 @@ static size_t network_play(size_t frames) { static int bfd_slot; /** @brief Set up poll array for network play */ -static void network_beforepoll(void) { +static void network_beforepoll(int *timeoutp) { struct timeval now; uint64_t target_us; uint64_t target_rtp_time; + const int64_t samples_per_second = config->sample_format.rate + * config->sample_format.channels; const int64_t samples_ahead = ((uint64_t)RTP_AHEAD_MS - * config->sample_format.rate - * config->sample_format.channels + * samples_per_second / 1000); + int64_t lead, ahead_ms; /* If we're starting then initialize the base time */ if(!rtp_time) @@ -326,8 +328,17 @@ static void network_beforepoll(void) { target_rtp_time = (target_us * config->sample_format.rate * config->sample_format.channels) / 1000000; - if((int64_t)(rtp_time - target_rtp_time) < samples_ahead) + lead = rtp_time - target_rtp_time; + if(lead < samples_ahead) + /* We've not reached the desired lead, write as fast as we can */ bfd_slot = addfd(bfd, POLLOUT); + else { + /* We've reached the desired lead, we can afford to wait a bit even if the + * IP stack thinks it can accept more. */ + ahead_ms = 1000 * (lead - samples_ahead) / samples_per_second; + if(ahead_ms < *timeoutp) + *timeoutp = ahead_ms; + } } /** @brief Process poll() results for network play */ diff --git a/server/speaker-oss.c b/server/speaker-oss.c index 9f409d5..9682cde 100644 --- a/server/speaker-oss.c +++ b/server/speaker-oss.c @@ -142,7 +142,7 @@ static size_t oss_play(size_t frames) { static int oss_slot; /** @brief Fill in poll fd array for OSS */ -static void oss_beforepoll(void) { +static void oss_beforepoll(int attribute((unused)) *timeoutp) { oss_slot = addfd(ossfd, POLLOUT|POLLERR); } diff --git a/server/speaker.c b/server/speaker.c index 41a12e5..19f88b2 100644 --- a/server/speaker.c +++ b/server/speaker.c @@ -432,7 +432,7 @@ static void mainloop(void) { * instead, but the post-poll code will cope even if it's * device_closed. */ if(device_state == device_open) - backend->beforepoll(); + backend->beforepoll(&timeout); } /* If any other tracks don't have a full buffer, try to read sample data * from them. We do this last of all, so that if we run out of slots, diff --git a/server/speaker.h b/server/speaker.h index ae4eac3..4e897e4 100644 --- a/server/speaker.h +++ b/server/speaker.h @@ -167,12 +167,18 @@ struct speaker_backend { void (*deactivate)(void); /** @brief Called before poll() + * @param timeoutp Pointer to timeout * - * Called before the call to poll(). Should call addfd() to update - * the FD array and stash the slot number somewhere safe. This will - * only be called if @ref device_state = @ref device_open. + * Called before the call to poll(). + * + * If desirable, should call addfd() to update the FD array and stash the + * slot number somewhere safe. This will only be called if @ref device_state + * is @ref device_open. + * + * @p timeoutp points to the poll timeout value in milliseconds. It may be + * reduced, but never increased. */ - void (*beforepoll)(void); + void (*beforepoll)(int *timeoutp); /** @brief Called after poll() * @return 1 if output device ready for play, 0 otherwise -- [mdw]