X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/ba70caca3f9debf14c9f551ff4dcaddd1eb07d3d..a202463f02ef9fe101e14bd5ca264de34d50a405:/lib/uaudio-rtp.c diff --git a/lib/uaudio-rtp.c b/lib/uaudio-rtp.c index b800b8b..e1768a8 100644 --- a/lib/uaudio-rtp.c +++ b/lib/uaudio-rtp.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -57,6 +59,9 @@ static int rtp_fd; /** @brief RTP SSRC */ static uint32_t rtp_id; +/** @brief Base for timestamp */ +static uint32_t rtp_base; + /** @brief RTP sequence number */ static uint16_t rtp_sequence; @@ -66,11 +71,8 @@ static uint16_t rtp_sequence; */ static int rtp_errors; -/** @brief Delay threshold in microseconds - * - * rtp_play() never attempts to introduce a delay shorter than this. - */ -static int64_t rtp_delay_threshold; +/** @brief Set while paused */ +static volatile int rtp_paused; static const char *const rtp_options[] = { "rtp-destination", @@ -79,20 +81,75 @@ static const char *const rtp_options[] = { "rtp-source-port", "multicast-ttl", "multicast-loop", - "delay-threshold", NULL }; -static size_t rtp_play(void *buffer, size_t nsamples) { +static void rtp_get_netconfig(const char *af, + const char *addr, + const char *port, + struct netaddress *na) { + char *vec[3]; + + vec[0] = uaudio_get(af, NULL); + vec[1] = uaudio_get(addr, NULL); + vec[2] = uaudio_get(port, NULL); + if(!*vec) + na->af = -1; + else + if(netaddress_parse(na, 3, vec)) + fatal(0, "invalid RTP address"); +} + +static void rtp_set_netconfig(const char *af, + const char *addr, + const char *port, + const struct netaddress *na) { + uaudio_set(af, NULL); + uaudio_set(addr, NULL); + uaudio_set(port, NULL); + if(na->af != -1) { + int nvec; + char **vec; + + netaddress_format(na, &nvec, &vec); + if(nvec > 0) { + uaudio_set(af, vec[0]); + xfree(vec[0]); + } + if(nvec > 1) { + uaudio_set(addr, vec[1]); + xfree(vec[1]); + } + if(nvec > 2) { + uaudio_set(port, vec[2]); + xfree(vec[2]); + } + xfree(vec); + } +} + +static size_t rtp_play(void *buffer, size_t nsamples, unsigned flags) { struct rtp_header header; struct iovec vec[2]; - + +#if 0 + if(flags & (UAUDIO_PAUSE|UAUDIO_RESUME)) + fprintf(stderr, "rtp_play %zu samples%s%s%s%s\n", nsamples, + flags & UAUDIO_PAUSE ? " UAUDIO_PAUSE" : "", + flags & UAUDIO_RESUME ? " UAUDIO_RESUME" : "", + flags & UAUDIO_PLAYING ? " UAUDIO_PLAYING" : "", + flags & UAUDIO_PAUSED ? " UAUDIO_PAUSED" : ""); +#endif + /* We do as much work as possible before checking what time it is */ /* Fill out header */ header.vpxcc = 2 << 6; /* V=2, P=0, X=0, CC=0 */ header.seq = htons(rtp_sequence++); header.ssrc = rtp_id; - header.mpt = (uaudio_schedule_reactivated ? 0x80 : 0x00) | rtp_payload; + header.mpt = rtp_payload; + /* If we've come out of a pause, set the marker bit */ + if(flags & UAUDIO_RESUME) + header.mpt |= 0x80; #if !WORDS_BIGENDIAN /* Convert samples to network byte order */ uint16_t *u = buffer, *const limit = u + nsamples; @@ -105,8 +162,13 @@ static size_t rtp_play(void *buffer, size_t nsamples) { vec[0].iov_len = sizeof header; vec[1].iov_base = buffer; vec[1].iov_len = nsamples * uaudio_sample_size; - uaudio_schedule_synchronize(); - header.timestamp = htonl((uint32_t)uaudio_schedule_timestamp); + const uint32_t timestamp = uaudio_schedule_sync(); + header.timestamp = htonl(rtp_base + (uint32_t)timestamp); + /* If we're paused don't actually end a packet, we just pretend */ + if(flags & UAUDIO_PAUSED) { + uaudio_schedule_sent(nsamples); + return nsamples; + } int written_bytes; do { written_bytes = writev(rtp_fd, vec, 2); @@ -119,60 +181,38 @@ static size_t rtp_play(void *buffer, size_t nsamples) { return 0; } else rtp_errors /= 2; /* gradual decay */ - written_bytes -= sizeof (struct rtp_header); - const size_t written_samples = written_bytes / uaudio_sample_size; - uaudio_schedule_update(written_samples); - return written_samples; + /* TODO what can we sensibly do about short writes here? Really that's just + * an error and we ought to be using smaller packets. */ + uaudio_schedule_sent(nsamples); + return nsamples; } static void rtp_open(void) { struct addrinfo *res, *sres; - static const struct addrinfo pref = { - .ai_flags = 0, - .ai_family = PF_INET, - .ai_socktype = SOCK_DGRAM, - .ai_protocol = IPPROTO_UDP, - }; - static const struct addrinfo prefbind = { - .ai_flags = AI_PASSIVE, - .ai_family = PF_INET, - .ai_socktype = SOCK_DGRAM, - .ai_protocol = IPPROTO_UDP, - }; static const int one = 1; int sndbuf, target_sndbuf = 131072; socklen_t len; - char *sockname, *ssockname; - struct stringlist dst, src; + struct netaddress dst[1], src[1]; /* Get configuration */ - dst.n = 2; - dst.s = xcalloc(2, sizeof *dst.s); - dst.s[0] = uaudio_get("rtp-destination", NULL); - dst.s[1] = uaudio_get("rtp-destination-port", NULL); - src.n = 2; - src.s = xcalloc(2, sizeof *dst.s); - src.s[0] = uaudio_get("rtp-source", NULL); - src.s[1] = uaudio_get("rtp-source-port", NULL); - if(!dst.s[0]) - fatal(0, "'rtp-destination' not set"); - if(!dst.s[1]) - fatal(0, "'rtp-destination-port' not set"); - if(src.s[0]) { - if(!src.s[1]) - fatal(0, "'rtp-source-port' not set"); - src.n = 2; - } else - src.n = 0; - rtp_delay_threshold = atoi(uaudio_get("rtp-delay-threshold", "1000")); + rtp_get_netconfig("rtp-destination-af", + "rtp-destination", + "rtp-destination-port", + dst); + rtp_get_netconfig("rtp-source-af", + "rtp-source", + "rtp-source-port", + src); /* ...microseconds */ /* Resolve addresses */ - res = get_address(&dst, &pref, &sockname); - if(!res) exit(-1); - if(src.n) { - sres = get_address(&src, &prefbind, &ssockname); - if(!sres) exit(-1); + res = netaddress_resolve(dst, 0, IPPROTO_UDP); + if(!res) + exit(-1); + if(src->af != -1) { + sres = netaddress_resolve(src, 1, IPPROTO_UDP); + if(!sres) + exit(-1); } else sres = 0; /* Create the socket */ @@ -207,7 +247,7 @@ static void rtp_open(void) { fatal(0, "unsupported address family %d", res->ai_family); } info("multicasting on %s TTL=%d loop=%s", - sockname, ttl, loop ? "yes" : "no"); + format_sockaddr(res->ai_addr), ttl, loop ? "yes" : "no"); } else { struct ifaddrs *ifs; @@ -226,9 +266,10 @@ static void rtp_open(void) { if(ifs) { if(setsockopt(rtp_fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0) fatal(errno, "error setting SO_BROADCAST on broadcast socket"); - info("broadcasting on %s (%s)", sockname, ifs->ifa_name); + info("broadcasting on %s (%s)", + format_sockaddr(res->ai_addr), ifs->ifa_name); } else - info("unicasting on %s", sockname); + info("unicasting on %s", format_sockaddr(res->ai_addr)); } /* Enlarge the socket buffer */ len = sizeof sndbuf; @@ -248,9 +289,11 @@ static void rtp_open(void) { /* We might well want to set additional broadcast- or multicast-related * options here */ if(sres && bind(rtp_fd, sres->ai_addr, sres->ai_addrlen) < 0) - fatal(errno, "error binding broadcast socket to %s", ssockname); + fatal(errno, "error binding broadcast socket to %s", + format_sockaddr(sres->ai_addr)); if(connect(rtp_fd, res->ai_addr, res->ai_addrlen) < 0) - fatal(errno, "error connecting broadcast socket to %s", sockname); + fatal(errno, "error connecting broadcast socket to %s", + format_sockaddr(res->ai_addr)); } static void rtp_start(uaudio_callback *callback, @@ -271,6 +314,7 @@ static void rtp_start(uaudio_callback *callback, * packet contents are highly public so there's no point asking for very * strong randomness. */ gcry_create_nonce(&rtp_id, sizeof rtp_id); + gcry_create_nonce(&rtp_base, sizeof rtp_base); gcry_create_nonce(&rtp_sequence, sizeof rtp_sequence); rtp_open(); uaudio_schedule_init(); @@ -279,7 +323,8 @@ static void rtp_start(uaudio_callback *callback, rtp_play, 256 / uaudio_sample_size, (NETWORK_BYTES - sizeof(struct rtp_header)) - / uaudio_sample_size); + / uaudio_sample_size, + 0); } static void rtp_stop(void) { @@ -288,32 +333,18 @@ static void rtp_stop(void) { rtp_fd = -1; } -static void rtp_activate(void) { - uaudio_schedule_reactivated = 1; - uaudio_thread_activate(); -} - -static void rtp_deactivate(void) { - uaudio_thread_deactivate(); -} - static void rtp_configure(void) { char buffer[64]; - uaudio_set("rtp-destination", config->broadcast.s[0]); - uaudio_set("rtp-destination-port", config->broadcast.s[1]); - if(config->broadcast_from.n) { - uaudio_set("rtp-source", config->broadcast_from.s[0]); - uaudio_set("rtp-source-port", config->broadcast_from.s[0]); - } else { - uaudio_set("rtp-source", NULL); - uaudio_set("rtp-source-port", NULL); - } + rtp_set_netconfig("rtp-destination-af", + "rtp-destination", + "rtp-destination-port", &config->broadcast); + rtp_set_netconfig("rtp-source-af", + "rtp-source", + "rtp-source-port", &config->broadcast_from); snprintf(buffer, sizeof buffer, "%ld", config->multicast_ttl); uaudio_set("multicast-ttl", buffer); uaudio_set("multicast-loop", config->multicast_loop ? "yes" : "no"); - snprintf(buffer, sizeof buffer, "%ld", config->rtp_delay_threshold); - uaudio_set("delay-threshold", buffer); } const struct uaudio uaudio_rtp = { @@ -321,8 +352,8 @@ const struct uaudio uaudio_rtp = { .options = rtp_options, .start = rtp_start, .stop = rtp_stop, - .activate = rtp_activate, - .deactivate = rtp_deactivate, + .activate = uaudio_thread_activate, + .deactivate = uaudio_thread_deactivate, .configure = rtp_configure, };