/** @brief Buffer high watermark
*
* We'll only start playing when this many samples are available. */
-static unsigned readahead = 2 * 2 * 44100;
+static unsigned readahead = 44100; /* 0.5 seconds */
/** @brief Maximum buffer size
*
#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 }
#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"
);
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,
mem_init();
if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
- backend = &UAUDIO_DEFAULT;
- while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:re:", options, 0)) >= 0) {
+ backend = uaudio_apis[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");
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");
}
}
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:
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;
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");
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
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 */
}