X-Git-Url: https://git.distorted.org.uk/~mdw/disorder/blobdiff_plain/85db49a59d75229b893047fceab4667c4fdef851..287ad384814351a195696f91e83388c12b265f90:/clients/playrtp.c diff --git a/clients/playrtp.c b/clients/playrtp.c index 9647437..338cbbf 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -217,6 +217,7 @@ static const struct option options[] = { #endif { "dump", required_argument, 0, 'r' }, { "command", required_argument, 0, 'e' }, + { "pause-mode", required_argument, 0, 'P' }, { "socket", required_argument, 0, 's' }, { "config", required_argument, 0, 'C' }, { 0, 0, 0, 0 } @@ -492,7 +493,9 @@ static void help(void) { #if HAVE_COREAUDIO_AUDIOHARDWARE_H " --core-audio, -c Use Core Audio to play audio\n" #endif - " --command, -e COMMAND Pipe audio to command\n" + " --command, -e COMMAND Pipe audio to command.\n" + " --pause-mode, -P silence For -e: pauses send silence (default)\n" + " --pause-mode, -P suspend For -e: pauses suspend writes\n" " --help, -h Display usage message\n" " --version, -V Display version number\n" ); @@ -580,6 +583,7 @@ int main(int argc, char **argv) { union any_sockaddr mgroup; const char *dumpfile = 0; pthread_t ltid; + static const int one = 1; static const struct addrinfo prefs = { .ai_flags = AI_PASSIVE, @@ -591,7 +595,7 @@ int main(int argc, char **argv) { mem_init(); if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale"); backend = uaudio_apis[0]; - while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:re:", options, 0)) >= 0) { + while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:re:P:", options, 0)) >= 0) { switch(n) { case 'h': help(); case 'V': version("disorder-playrtp"); @@ -615,6 +619,7 @@ int main(int argc, char **argv) { case 's': control_socket = optarg; break; case 'r': dumpfile = optarg; break; case 'e': backend = &uaudio_command; uaudio_set("command", optarg); break; + case 'P': uaudio_set("pause-mode", optarg); break; default: fatal(0, "invalid option"); } } @@ -651,8 +656,14 @@ int main(int argc, char **argv) { res->ai_socktype, res->ai_protocol)) < 0) fatal(errno, "error creating socket"); - /* Stash the multicast group address */ - if((is_multicast = multicast(res->ai_addr))) { + /* Allow multiple listeners */ + xsetsockopt(rtpfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); + is_multicast = multicast(res->ai_addr); + /* The multicast and unicast/broadcast cases are different enough that they + * are totally split. Trying to find commonality between them causes more + * trouble that it's worth. */ + if(is_multicast) { + /* Stash the multicast group address */ memcpy(&mgroup, res->ai_addr, res->ai_addrlen); switch(res->ai_addr->sa_family) { case AF_INET: @@ -661,24 +672,13 @@ int main(int argc, char **argv) { case AF_INET6: mgroup.in6.sin6_port = 0; break; + default: + fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family); } - } - /* Bind to 0/port */ - switch(res->ai_addr->sa_family) { - case AF_INET: - memset(&((struct sockaddr_in *)res->ai_addr)->sin_addr, 0, - sizeof (struct in_addr)); - break; - case AF_INET6: - memset(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 0, - sizeof (struct in6_addr)); - break; - default: - fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family); - } - if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0) - fatal(errno, "error binding socket to %s", sockname); - if(is_multicast) { + /* Bind to to the multicast group address */ + if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0) + fatal(errno, "error binding socket to %s", format_sockaddr(res->ai_addr)); + /* Add multicast group membership */ switch(mgroup.sa.sa_family) { case PF_INET: mreq.imr_multiaddr = mgroup.in.sin_addr; @@ -697,10 +697,35 @@ int main(int argc, char **argv) { default: fatal(0, "unsupported address family %d", res->ai_family); } + /* Report what we did */ info("listening on %s multicast group %s", format_sockaddr(res->ai_addr), format_sockaddr(&mgroup.sa)); - } else + } else { + /* Bind to 0/port */ + switch(res->ai_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *in = (struct sockaddr_in *)res->ai_addr; + + memset(&in->sin_addr, 0, sizeof (struct in_addr)); + if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0) + fatal(errno, "error binding socket to 0.0.0.0 port %d", + ntohs(in->sin_port)); + break; + } + case AF_INET6: { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)res->ai_addr; + + memset(&in6->sin6_addr, 0, sizeof (struct in6_addr)); + break; + } + default: + fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family); + } + if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0) + fatal(errno, "error binding socket to %s", format_sockaddr(res->ai_addr)); + /* Report what we did */ info("listening on %s", format_sockaddr(res->ai_addr)); + } len = sizeof rcvbuf; if(getsockopt(rtpfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len) < 0) fatal(errno, "error calling getsockopt SO_RCVBUF"); @@ -763,7 +788,9 @@ int main(int argc, char **argv) { info("Playing..."); next_timestamp = pheap_first(&packets)->timestamp; active = 1; + pthread_mutex_unlock(&lock); backend->activate(); + pthread_mutex_lock(&lock); /* Wait until the buffer empties out */ while(nsamples >= minbuffer || (nsamples > 0 @@ -771,7 +798,9 @@ int main(int argc, char **argv) { pthread_cond_wait(&cond, &lock); } /* Stop playing for a bit until the buffer re-fills */ + pthread_mutex_unlock(&lock); backend->deactivate(); + pthread_mutex_lock(&lock); active = 0; /* Go back round */ }