chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/disorder
[disorder] / clients / playrtp.c
index 561ef997054d276bcbf4b7e018973ec8f6af907e..2f9841571f87e6f89069f3325e2391531b1c7c53 100644 (file)
@@ -74,6 +74,7 @@
 #include "configuration.h"
 #include "addr.h"
 #include "syscalls.h"
+#include "printf.h"
 #include "rtp.h"
 #include "defs.h"
 #include "vector.h"
@@ -99,7 +100,7 @@ static FILE *logfp;
 /** @brief Output device */
 
 /** @brief Buffer low watermark in samples */
-unsigned minbuffer = 4 * (2 * 44100) / 10;  /* 0.4 seconds */
+unsigned minbuffer;
 
 /** @brief Maximum buffer size in samples
  *
@@ -216,6 +217,7 @@ static const struct option options[] = {
   { "pause-mode", required_argument, 0, 'P' },
   { "socket", required_argument, 0, 's' },
   { "config", required_argument, 0, 'C' },
+  { "user-config", required_argument, 0, 'u' },
   { "monitor", no_argument, 0, 'M' },
   { 0, 0, 0, 0 }
 };
@@ -508,7 +510,8 @@ static void attribute((noreturn)) help(void) {
           "  --min, -m FRAMES        Buffer low water mark\n"
           "  --max, -x FRAMES        Buffer maximum size\n"
           "  --rcvbuf, -R BYTES      Socket receive buffer size\n"
-          "  --config, -C PATH       Set configuration file\n"
+          "  --config, -C PATH       Set system configuration file\n"
+          "  --user-config, -u PATH  Set user configuration file\n"
           "  --api, -A API           Select audio API.  Possibilities:\n"
           "                            ");
   int first = 1;
@@ -634,59 +637,12 @@ static size_t playrtp_callback(void *buffer,
   return samples;
 }
 
-static int compare_family(const struct ifaddrs *a,
-                          const struct ifaddrs *b,
-                          int family) {
-  int afamily = a->ifa_addr->sa_family;
-  int bfamily = b->ifa_addr->sa_family;
-  if(afamily != bfamily) {
-    /* Preferred family wins */
-    if(afamily == family) return 1;
-    if(bfamily == family) return -1;
-    /* Either there's no preference or it doesn't help.  Prefer IPv4 */
-    if(afamily == AF_INET) return 1;
-    if(bfamily == AF_INET) return -1;
-    /* Failing that prefer IPv6 */
-    if(afamily == AF_INET6) return 1;
-    if(bfamily == AF_INET6) return -1;
-  }
-  return 0;
-}
-
-static int compare_flags(const struct ifaddrs *a,
-                         const struct ifaddrs *b) {
-  unsigned aflags = a->ifa_flags, bflags = b->ifa_flags;
-  /* Up interfaces are better than down ones */
-  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)
-    return aloopback < bloopback ? 1 : -1;
-  return 0;
-}
-
-static int compare_interfaces(const struct ifaddrs *a,
-                              const struct ifaddrs *b,
-                              int family) {
-  int c;
-  if((c = compare_family(a, b, family))) return c;
-  return compare_flags(a, b);
-}
-
 int main(int argc, char **argv) {
   int n, err;
   struct addrinfo *res;
   struct stringlist sl;
   char *sockname;
-  int rcvbuf, target_rcvbuf = 0;
+  int rcvbuf, target_rcvbuf = -1;
   socklen_t len;
   struct ip_mreq mreq;
   struct ipv6_mreq mreq6;
@@ -716,7 +672,7 @@ int main(int argc, char **argv) {
   logdate = 1;
   mem_init();
   if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
-  while((n = getopt_long(argc, argv, "hVdD:m:x:L:R:aocC:re:P:MA:", options, 0)) >= 0) {
+  while((n = getopt_long(argc, argv, "hVdD:m:x:L:R:aocC:u:re:P:MA:", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version("disorder-playrtp");
@@ -745,6 +701,7 @@ int main(int argc, char **argv) {
 #endif
     case 'A': backend = uaudio_find(optarg); break;
     case 'C': configfile = optarg; break;
+    case 'u': userconfigfile = optarg; break;
     case 's': control_socket = optarg; break;
     case 'r': dumpfile = optarg; break;
     case 'e': backend = &uaudio_command; uaudio_set("command", optarg); break;
@@ -767,20 +724,45 @@ int main(int argc, char **argv) {
      * CoreAudio/AudioHardware.h). */
     disorder_fatal(0, "cannot play RTP through RTP");
   }
-  if(!maxbuffer)
-    maxbuffer = 2 * minbuffer;
+  /* Set buffering parameters if not overridden */
+  if(!minbuffer) {
+    minbuffer = config->rtp_minbuffer;
+    if(!minbuffer) minbuffer = (2*44100)*4/10;
+  }
+  if(!maxbuffer) {
+    maxbuffer = config->rtp_maxbuffer;
+    if(!maxbuffer) maxbuffer = 2 * minbuffer;
+  }
+  if(target_rcvbuf < 0) target_rcvbuf = config->rtp_rcvbuf;
   argc -= optind;
   argv += optind;
   switch(argc) {
   case 0:
-    /* Get configuration from server */
-    if(!(c = disorder_new(1))) exit(EXIT_FAILURE);
-    if(disorder_connect(c)) exit(EXIT_FAILURE);
-    if(disorder_rtp_address(c, &address, &port)) exit(EXIT_FAILURE);
-    sl.n = 2;
-    sl.s = xcalloc(2, sizeof *sl.s);
-    sl.s[0] = address;
-    sl.s[1] = port;
+    sl.s = xcalloc(3, sizeof *sl.s);
+    if(config->rtp_always_request) {
+      sl.s[0] = sl.s[1] = (/*unconst*/ char *)"-";
+      sl.n = 2;
+    } else {
+      /* Get configuration from server */
+      if(!(c = disorder_new(1))) exit(EXIT_FAILURE);
+      if(disorder_connect(c)) exit(EXIT_FAILURE);
+      if(disorder_rtp_address(c, &address, &port)) exit(EXIT_FAILURE);
+      sl.s[0] = address;
+      sl.s[1] = port;
+      sl.n = 2;
+    }
+    /* If we're requesting a new stream then apply the local network address
+     * overrides.
+     */
+    if(!strcmp(sl.s[0], "-")) {
+      if(config->rtp_request_address.port)
+        byte_xasprintf(&sl.s[1], "%d", config->rtp_request_address.port);
+      if(config->rtp_request_address.address) {
+        sl.s[2] = sl.s[1];
+        sl.s[1] = config->rtp_request_address.address;
+        sl.n = 3;
+      }
+    }
     break;
   case 1: case 2: case 3:
     /* Use command-line ADDRESS+PORT or just PORT */
@@ -814,29 +796,22 @@ int main(int argc, char **argv) {
       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 no address was given, we need to pick one.  But we already have a
+     * connection to the server, so we can probably use the address from that.
+     */
+    struct sockaddr_storage ss;
     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");
+      addr_len = sizeof ss;
+      if(disorder_client_sockname(c, (struct sockaddr *)&ss, &addr_len))
+        exit(EXIT_FAILURE);
+      if(ss.ss_family != AF_INET && ss.ss_family != AF_INET6) {
+        /* We're using a Unix-domain socket, so use a loopback address.  I'm
+         * cowardly using IPv4 here. */
+        struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+        sin->sin_family = AF_INET;
+        sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
       }
+      sa = (struct sockaddr *)&ss;
       prefs.ai_family = sa->sa_family;
     }
     /* If we have an address or port to resolve then do that now */
@@ -890,7 +865,8 @@ 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 */