X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/db2c19dcee351f69e2ce40902a8e92ecca4bac8c..9f28e855aad11d25c0a69a7e59825c07bdefcafe:/server/speaker-network.c diff --git a/server/speaker-network.c b/server/speaker-network.c index c81b7db..40abca5 100644 --- a/server/speaker-network.c +++ b/server/speaker-network.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "configuration.h" #include "syscalls.h" @@ -106,11 +107,6 @@ static void network_init(void) { socklen_t len; char *sockname, *ssockname; - /* Override sample format */ - config->sample_format.rate = 44100; - config->sample_format.channels = 2; - config->sample_format.bits = 16; - config->sample_format.byte_format = AO_FMT_BIG; res = get_address(&config->broadcast, &pref, &sockname); if(!res) exit(-1); if(config->broadcast_from.n) { @@ -122,14 +118,7 @@ static void network_init(void) { res->ai_socktype, res->ai_protocol)) < 0) fatal(errno, "error creating broadcast socket"); - if((res->ai_family == PF_INET - && IN_MULTICAST( - ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr) - )) - || (res->ai_family == PF_INET6 - && IN6_IS_ADDR_MULTICAST( - &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr - ))) { + if(multicast(res->ai_addr)) { /* Multicasting */ switch(res->ai_family) { case PF_INET: { @@ -155,7 +144,11 @@ static void network_init(void) { if(getifaddrs(&ifs) < 0) fatal(errno, "error calling getifaddrs"); while(ifs) { + /* (At least on Darwin) IFF_BROADCAST might be set but ifa_broadaddr + * still a null pointer. It turns out that there's a subsequent entry + * for he same interface which _does_ have ifa_broadaddr though... */ if((ifs->ifa_flags & IFF_BROADCAST) + && ifs->ifa_broadaddr && sockaddr_equal(ifs->ifa_broadaddr, res->ai_addr)) break; ifs = ifs->ifa_next; @@ -189,14 +182,13 @@ static void network_init(void) { fatal(errno, "error connecting broadcast socket to %s", sockname); /* Select an SSRC */ gcry_randomize(&rtp_id, sizeof rtp_id, GCRY_STRONG_RANDOM); - info("selected network backend, sending to %s", sockname); } /** @brief Play over the network */ static size_t network_play(size_t frames) { struct rtp_header header; struct iovec vec[2]; - size_t bytes = frames * device_bpf, written_frames; + size_t bytes = frames * bpf, written_frames; int written_bytes; /* We transmit using RTP (RFC3550) and attempt to conform to the internet * AVT profile (RFC3551). */ @@ -212,8 +204,8 @@ static size_t network_play(size_t frames) { /* Find the number of microseconds elapsed since rtp_time=0 */ delta = tvsub_us(now, rtp_time_0); assert(delta <= UINT64_MAX / 88200); - target_rtp_time = (delta * playing->format.rate - * playing->format.channels) / 1000000; + target_rtp_time = (delta * config->sample_format.rate + * config->sample_format.channels) / 1000000; /* Overflows at ~6 years uptime with 44100Hz stereo */ /* rtp_time is the number of samples we've played. NB that we play @@ -272,7 +264,7 @@ static size_t network_play(size_t frames) { if(bytes > NETWORK_BYTES - sizeof header) { bytes = NETWORK_BYTES - sizeof header; /* Always send a whole number of frames */ - bytes -= bytes % device_bpf; + bytes -= bytes % bpf; } /* "The RTP clock rate used for generating the RTP timestamp is independent * of the number of channels and the encoding; it equals the number of @@ -298,23 +290,25 @@ static size_t network_play(size_t frames) { } else audio_errors /= 2; written_bytes -= sizeof (struct rtp_header); - written_frames = written_bytes / device_bpf; + written_frames = written_bytes / bpf; /* Advance RTP's notion of the time */ - rtp_time += written_frames * playing->format.channels; + rtp_time += written_frames * config->sample_format.channels; return written_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) @@ -327,8 +321,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 */ @@ -341,7 +344,7 @@ static int network_ready(void) { const struct speaker_backend network_backend = { BACKEND_NETWORK, - FIXED_FORMAT, + 0, network_init, 0, /* activate */ network_play,