X-Git-Url: https://git.distorted.org.uk/~mdw/disorder/blobdiff_plain/7aa087a7e0f7829c8c6778fa420e0b5df0d0ca77..451169fc66436badbfa3c5b053ec2ee1a71bf59b:/server/speaker.c diff --git a/server/speaker.c b/server/speaker.c index c5fc668..5d20394 100644 --- a/server/speaker.c +++ b/server/speaker.c @@ -104,10 +104,10 @@ * * Don't make this too big or arithmetic will start to overflow. */ -#define NETWORK_BYTES 1024 +#define NETWORK_BYTES (1024+sizeof(struct rtp_header)) -/** @brief Maximum RTP playahead (seconds) */ -#define RTP_AHEAD 2 +/** @brief Maximum RTP playahead (ms) */ +#define RTP_AHEAD_MS 1000 /** @brief Maximum number of FDs to poll for */ #define NFDS 256 @@ -726,7 +726,7 @@ static void play(size_t frames) { * AVT profile (RFC3551). */ if(idled) { - /* There's been a gap. Fix up the RTP time accordingly. */ + /* There may have been a gap. Fix up the RTP time accordingly. */ struct timeval now; uint64_t delta; uint64_t target_rtp_time; @@ -739,13 +739,48 @@ static void play(size_t frames) { target_rtp_time = (delta * playing->format.rate * playing->format.channels) / 1000000; /* Overflows at ~6 years uptime with 44100Hz stereo */ - if(target_rtp_time > rtp_time) + + /* rtp_time is the number of samples we've played. NB that we play + * RTP_AHEAD_MS ahead of ourselves, so it may legitimately be ahead of + * the value we deduce from time comparison. + * + * Suppose we have 1s track started at t=0, and another track begins to + * play at t=2s. Suppose RTP_AHEAD_MS=1000 and 44100Hz stereo. In that + * case we'll send 1s of audio as fast as we can, giving rtp_time=88200. + * rtp_time stops at this point. + * + * At t=2s we'll have calculated target_rtp_time=176400. In this case we + * set rtp_time=176400 and the player can correctly conclude that it + * should leave 1s between the tracks. + * + * Suppose instead that the second track arrives at t=0.5s, and that + * we've managed to transmit the whole of the first track already. We'll + * have target_rtp_time=44100. + * + * The desired behaviour is to play the second track back to back with + * first. In this case therefore we do not modify rtp_time. + * + * Is it ever right to reduce rtp_time? No; for that would imply + * transmitting packets with overlapping timestamp ranges, which does not + * make sense. + */ + if(target_rtp_time > rtp_time) { + /* More time has elapsed than we've transmitted samples. That implies + * we've been 'sending' silence. */ info("advancing rtp_time by %"PRIu64" samples", target_rtp_time - rtp_time); - else if(target_rtp_time < rtp_time) - info("reversing rtp_time by %"PRIu64" samples", - rtp_time - target_rtp_time); - rtp_time = target_rtp_time; + rtp_time = target_rtp_time; + } else if(target_rtp_time < rtp_time) { + const int64_t samples_ahead = ((uint64_t)RTP_AHEAD_MS + * config->sample_format.rate + * config->sample_format.channels + / 1000); + + if(target_rtp_time + samples_ahead < rtp_time) { + info("reversing rtp_time by %"PRIu64" samples", + rtp_time - target_rtp_time); + } + } } header.vpxcc = 2 << 6; /* V=2, P=0, X=0, CC=0 */ header.seq = htons(rtp_seq++); @@ -869,6 +904,8 @@ int main(int argc, char **argv) { 0 }; static const int one = 1; + int sndbuf, target_sndbuf = 131072; + socklen_t len; char *sockname, *ssockname; #if API_ALSA int alsa_nslots = -1, err; @@ -923,7 +960,17 @@ int main(int argc, char **argv) { res->ai_protocol)) < 0) fatal(errno, "error creating broadcast socket"); if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0) - fatal(errno, "error settting SO_BROADCAST on broadcast socket"); + fatal(errno, "error setting SO_BROADCAST on broadcast socket"); + len = sizeof sndbuf; + if(getsockopt(bfd, SOL_SOCKET, SO_SNDBUF, + &sndbuf, &len) < 0) + fatal(errno, "error getting SO_SNDBUF"); + if(setsockopt(bfd, SOL_SOCKET, SO_SNDBUF, + &target_sndbuf, sizeof target_sndbuf) < 0) + error(errno, "error setting SO_SNDBUF to %d", target_sndbuf); + else + info("changed socket send buffer size from %d to %d", + sndbuf, target_sndbuf); /* We might well want to set additional broadcast- or multicast-related * options here */ if(sres && bind(bfd, sres->ai_addr, sres->ai_addrlen) < 0) @@ -971,10 +1018,13 @@ int main(int argc, char **argv) { struct timeval now; uint64_t target_us; uint64_t target_rtp_time; - const uint64_t ahead = RTP_AHEAD * 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 + / 1000); +#if 0 static unsigned logit; +#endif /* If we're starting then initialize the base time */ if(!rtp_time) @@ -988,14 +1038,15 @@ int main(int argc, char **argv) { * config->sample_format.channels) / 1000000; +#if 0 /* TODO remove logging guff */ if(!(logit++ & 1023)) info("rtp_time %llu target %llu difference %lld [%lld]", rtp_time, target_rtp_time, rtp_time - target_rtp_time, - ahead); - if(rtp_time < target_rtp_time - || rtp_time - target_rtp_time < ahead) + samples_ahead); +#endif + if((int64_t)(rtp_time - target_rtp_time) < samples_ahead) bfd_slot = addfd(bfd, POLLOUT); break; }