X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/cca034e58b00be2f53f9145957346bcd3e451c45..7675ceab119f7f6fa69e8be37172993a208bd7b1:/clients/playrtp.c diff --git a/clients/playrtp.c b/clients/playrtp.c index 89e458e..539dc59 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -243,6 +243,7 @@ static void *control_thread(void attribute((unused)) *arg) { char *line; socklen_t salen; FILE *fp; + int vl, vr; assert(control_socket); unlink(control_socket); @@ -276,9 +277,21 @@ static void *control_thread(void attribute((unused)) *arg) { if(!strcmp(line, "stop")) { disorder_info("stopped via %s", control_socket); exit(0); /* terminate immediately */ - } - if(!strcmp(line, "query")) + } else if(!strcmp(line, "query")) fprintf(fp, "running"); + else if(!strcmp(line, "getvol")) { + if(backend->get_volume) backend->get_volume(&vl, &vr); + else vl = vr = 0; + fprintf(fp, "%d %d\n", vl, vr); + } else if(!strncmp(line, "setvol ", 7)) { + if(!backend->set_volume) + vl = vr = 0; + else if(sscanf(line + 7, "%d %d", &vl, &vr) == 2) + backend->set_volume(&vl, &vr); + else + backend->get_volume(&vl, &vr); + fprintf(fp, "%d %d\n", vl, vr); + } xfree(line); } if(fclose(fp) < 0) @@ -487,7 +500,7 @@ struct packet *playrtp_next_packet(void) { } /* display usage message and terminate */ -static void help(void) { +static void attribute((noreturn)) help(void) { xprintf("Usage:\n" " disorder-playrtp [OPTIONS] [[ADDRESS] PORT]\n" "Options:\n" @@ -647,10 +660,12 @@ static int compare_flags(const struct ifaddrs *a, unsigned aup = aflags & IFF_UP, bup = bflags & IFF_UP; if(aup != bup) return aup > bup ? 1 : -1; +#if IFF_DYNAMIC /* Static addresses are better than dynamic */ unsigned adynamic = aflags & IFF_DYNAMIC, bdynamic = bflags & IFF_DYNAMIC; if(adynamic != bdynamic) return adynamic < bdynamic ? 1 : -1; +#endif unsigned aloopback = aflags & IFF_LOOPBACK, bloopback = bflags & IFF_LOOPBACK; /* Static addresses are better than dynamic */ if(aloopback != bloopback) @@ -689,7 +704,7 @@ int main(int argc, char **argv) { int monitor = 0; static const int one = 1; - static const struct addrinfo prefs = { + struct addrinfo prefs = { .ai_flags = AI_PASSIVE, .ai_family = PF_INET, .ai_socktype = SOCK_DGRAM, @@ -739,6 +754,7 @@ int main(int argc, char **argv) { } } if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration"); + /* Choose a sensible default audio backend */ if(!backend) { backend = uaudio_default(uaudio_apis, UAUDIO_API_CLIENT); if(!backend) @@ -766,8 +782,7 @@ int main(int argc, char **argv) { sl.s[0] = address; sl.s[1] = port; break; - case 1: - case 2: + case 1: case 2: case 3: /* Use command-line ADDRESS+PORT or just PORT */ sl.n = argc; sl.s = argv; @@ -780,28 +795,84 @@ int main(int argc, char **argv) { struct sockaddr *addr; socklen_t addr_len; if(!strcmp(sl.s[0], "-")) { - /* Pick address family to match known-working connectivity to the server */ - int family = disorder_client_af(c); - /* Get a list of interfaces */ - struct ifaddrs *ifa, *bestifa = NULL; - if(getifaddrs(&ifa) < 0) - disorder_fatal(errno, "error calling getifaddrs"); - /* Try to pick a good one */ - for(; ifa; ifa = ifa->ifa_next) { - if(bestifa == NULL - || compare_interfaces(ifa, bestifa, family) > 0) - bestifa = ifa; + /* Syntax: - [[ADDRESS] PORT]. Here, the PORT may be `-' to get the local + * kernel to choose. The ADDRESS may be omitted or `-' to pick something + * suitable. */ + const char *node, *svc; + struct sockaddr *sa = 0; + switch (sl.n) { +#define NULLDASH(s) (strcmp((s), "-") ? (s) : 0) + case 1: node = 0; svc = 0; break; + case 2: node = 0; svc = NULLDASH(sl.s[1]); break; + case 3: node = NULLDASH(sl.s[1]); svc = NULLDASH(sl.s[2]); break; + default: disorder_fatal(0, "too many listening-address compoennts"); +#undef NULLDASH + } + /* We'll need a connection to request the incoming stream, so open one if + * we don't have one already */ + if(!c) { + if(!(c = disorder_new(1))) exit(EXIT_FAILURE); + if(disorder_connect(c)) exit(EXIT_FAILURE); + } + /* If no address was given, pick something sensible based on the known- + * working connectivity to the server */ + if(!node) { + int family = disorder_client_af(c); + /* Get a list of interfaces */ + struct ifaddrs *ifa, *bestifa = NULL; + if(getifaddrs(&ifa) < 0) + disorder_fatal(errno, "error calling getifaddrs"); + /* Try to pick a good one */ + for(; ifa; ifa = ifa->ifa_next) { + if(!ifa->ifa_addr) continue; + if(bestifa == NULL + || compare_interfaces(ifa, bestifa, family) > 0) + bestifa = ifa; + } + if(!bestifa) + disorder_fatal(0, "failed to select a network interface"); + sa = bestifa->ifa_addr; + switch(sa->sa_family) { + case AF_INET: ((struct sockaddr_in *)sa)->sin_port = 0; break; + case AF_INET6: ((struct sockaddr_in6 *)sa)->sin6_port = 0; break; + default: assert(!"unexpected address family"); + } + prefs.ai_family = sa->sa_family; + } + /* If we have an address or port to resolve then do that now */ + if (node || svc) { + struct addrinfo *ai; + char errbuf[1024]; + int rc; + if((rc = getaddrinfo(node, svc, &prefs, &ai))) + disorder_fatal(0, "failed to resolve address `%s' and service `%s': %s", + node ? node : "-", svc ? svc : "-", + format_error(ec_getaddrinfo, rc, + errbuf, sizeof(errbuf))); + if(!sa) + sa = ai->ai_addr; + else { + assert(sa->sa_family == ai->ai_addr->sa_family); + switch(sa->sa_family) { + case AF_INET: + ((struct sockaddr_in *)sa)->sin_port = + ((struct sockaddr_in *)ai->ai_addr)->sin_port; + break; + case AF_INET6: + ((struct sockaddr_in6 *)sa)->sin6_port = + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port; + break; + default: + assert(!"unexpected address family"); + } + } } - if(!bestifa) - disorder_fatal(0, "failed to select a network interface"); - family = bestifa->ifa_addr->sa_family; - if((rtpfd = socket(family, - SOCK_DGRAM, - IPPROTO_UDP)) < 0) - disorder_fatal(errno, "error creating socket (family %d)", family); + if((rtpfd = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) + disorder_fatal(errno, "error creating socket (family %d)", + sa->sa_family); /* Bind the address */ - if(bind(rtpfd, bestifa->ifa_addr, - family == AF_INET + if(bind(rtpfd, sa, + sa->sa_family == AF_INET ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)) < 0) disorder_fatal(errno, "error binding socket"); static struct sockaddr_storage bound_address; @@ -819,8 +890,10 @@ int main(int argc, char **argv) { /* Ask for audio data */ if(disorder_rtp_request(c, addrname, portname)) exit(EXIT_FAILURE); /* Report what we did */ - disorder_info("listening on %s", format_sockaddr(addr)); + disorder_info("listening on %s (stream requested)", + format_sockaddr(addr)); } else { + if(sl.n > 2) disorder_fatal(0, "too many address components"); /* Look up address and port */ if(!(res = get_address(&sl, &prefs, &sockname))) exit(1); @@ -951,7 +1024,9 @@ int main(int argc, char **argv) { uaudio_set_format(44100/*Hz*/, 2/*channels*/, 16/*bits/channel*/, 1/*signed*/); uaudio_set("application", "disorder-playrtp"); + backend->configure(); backend->start(playrtp_callback, NULL); + if(backend->open_mixer) backend->open_mixer(); /* We receive and convert audio data in a background thread */ if((err = pthread_create(<id, 0, listen_thread, 0))) disorder_fatal(err, "pthread_create listen_thread");