chiark / gitweb /
synchronize with disorder.dev
authorRichard Kettlewell <rjk@greenend.org.uk>
Fri, 23 Nov 2007 14:22:30 +0000 (14:22 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Fri, 23 Nov 2007 14:22:30 +0000 (14:22 +0000)
clients/Makefile.am
clients/playrtp.c
debian/changelog
doc/disorder-playrtp.1.in
lib/addr.c
lib/addr.h
server/server.c
server/speaker-network.c

index 880f86ae5368438f15688c3a694652c9882a558e..72a77396891e15671e5307bac4657f7e58dc0388 100644 (file)
@@ -64,7 +64,7 @@ check-help: all
        ./disorder-playrtp --help > /dev/null
 
 # check that the command completions are up to date
-check-completions:
+check-completions: disorder
        ./disorder --help-commands \
                | awk '/^  [a-z]/ { print $$1 }' \
                | sort > ,commands
index 908c619905fd7ed7c7e5035d0c6b8181e40d532a..9757be6566d6835da080646181a0799e6a72a369 100644 (file)
@@ -199,7 +199,6 @@ static const struct option options[] = {
   { "max", required_argument, 0, 'x' },
   { "buffer", required_argument, 0, 'b' },
   { "rcvbuf", required_argument, 0, 'R' },
-  { "multicast", required_argument, 0, 'M' },
 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
   { "oss", no_argument, 0, 'o' },
 #endif
@@ -491,7 +490,6 @@ static void help(void) {
           "  --buffer, -b FRAMES     Buffer high water mark\n"
           "  --max, -x FRAMES        Buffer maximum size\n"
           "  --rcvbuf, -R BYTES      Socket receive buffer size\n"
-          "  --multicast, -M GROUP   Join multicast group\n"
           "  --config, -C PATH       Set configuration file\n"
 #if HAVE_ALSA_ASOUNDLIB_H
           "  --alsa, -a              Use ALSA to play audio\n"
@@ -523,11 +521,17 @@ int main(int argc, char **argv) {
   char *sockname;
   int rcvbuf, target_rcvbuf = 131072;
   socklen_t len;
-  char *multicast_group = 0;
   struct ip_mreq mreq;
   struct ipv6_mreq mreq6;
   disorder_client *c;
   char *address, *port;
+  int is_multicast;
+  union any_sockaddr {
+    struct sockaddr sa;
+    struct sockaddr_in in;
+    struct sockaddr_in6 in6;
+  };
+  union any_sockaddr mgroup;
 
   static const struct addrinfo prefs = {
     AI_PASSIVE,
@@ -553,7 +557,6 @@ int main(int argc, char **argv) {
     case 'x': maxbuffer = 2 * atol(optarg); break;
     case 'L': logfp = fopen(optarg, "w"); break;
     case 'R': target_rcvbuf = atoi(optarg); break;
-    case 'M': multicast_group = optarg; break;
 #if HAVE_ALSA_ASOUNDLIB_H
     case 'a': backend = playrtp_alsa; break;
 #endif
@@ -575,44 +578,70 @@ int main(int argc, char **argv) {
   argv += optind;
   switch(argc) {
   case 0:
-  case 1:
+    /* 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 = 1;
-    sl.s = &port;
-    /* set multicast_group if address is a multicast address */
+    sl.n = 2;
+    sl.s = xcalloc(2, sizeof *sl.s);
+    sl.s[0] = address;
+    sl.s[1] = port;
     break;
+  case 1:
   case 2:
+    /* Use command-line ADDRESS+PORT or just PORT */
     sl.n = argc;
     sl.s = argv;
     break;
   default:
-    fatal(0, "usage: disorder-playrtp [OPTIONS] [ADDRESS [PORT]]");
+    fatal(0, "usage: disorder-playrtp [OPTIONS] [[ADDRESS] PORT]");
   }
-  /* Listen for inbound audio data */
+  /* Look up address and port */
   if(!(res = get_address(&sl, &prefs, &sockname)))
     exit(1);
-  info("listening on %s", sockname);
+  /* Create the socket */
   if((rtpfd = socket(res->ai_family,
                      res->ai_socktype,
                      res->ai_protocol)) < 0)
     fatal(errno, "error creating socket");
+  /* Stash the multicast group address */
+  if((is_multicast = multicast(res->ai_addr))) {
+    memcpy(&mgroup, res->ai_addr, res->ai_addrlen);
+    switch(res->ai_addr->sa_family) {
+    case AF_INET:
+      mgroup.in.sin_port = 0;
+      break;
+    case AF_INET6:
+      mgroup.in6.sin6_port = 0;
+      break;
+    }
+  }
+  /* 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(multicast_group) {
-    if((n = getaddrinfo(multicast_group, 0, &prefs, &res)))
-      fatal(0, "getaddrinfo %s: %s", multicast_group, gai_strerror(n));
-    switch(res->ai_family) {
+  if(is_multicast) {
+    switch(mgroup.sa.sa_family) {
     case PF_INET:
-      mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
+      mreq.imr_multiaddr = mgroup.in.sin_addr;
       mreq.imr_interface.s_addr = 0;      /* use primary interface */
       if(setsockopt(rtpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                     &mreq, sizeof mreq) < 0)
         fatal(errno, "error calling setsockopt IP_ADD_MEMBERSHIP");
       break;
     case PF_INET6:
-      mreq6.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+      mreq6.ipv6mr_multiaddr = mgroup.in6.sin6_addr;
       memset(&mreq6.ipv6mr_interface, 0, sizeof mreq6.ipv6mr_interface);
       if(setsockopt(rtpfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
                     &mreq6, sizeof mreq6) < 0)
@@ -621,7 +650,10 @@ int main(int argc, char **argv) {
     default:
       fatal(0, "unsupported address family %d", res->ai_family);
     }
-  }
+    info("listening on %s multicast group %s",
+         format_sockaddr(res->ai_addr), format_sockaddr(&mgroup.sa));
+  } else
+    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");
index b017518252a1f5416948b2a6f7c0a3cef8cd31af..8257af27581a5b4a4549cce6f3c7a494a81028c8 100644 (file)
@@ -1,3 +1,11 @@
+disorder (1.5.99+dev8) unstable; urgency=low
+
+  * disorder-playrtp automatically picks up multicast
+  * Disobedience no longer messes with random play settings on
+    start/recovery
+
+ -- Richard Kettlewell <rjk@greenend.org.uk>  Thu, 22 Nov 2007 17:17:25 +0000
+
 disorder (1.5.99+dev7) unstable; urgency=low
 
   * New Disobedience now it's a bit quicker
index cc2f1ac06562255258bb9b9e67bdd60ca5cc4c4e..f4bcfd6f7cc9f8ec3b01d7f49f06dce6ef85e9f4 100644 (file)
@@ -23,26 +23,27 @@ disorder-playrtp \- play DisOrder network broadcasts
 .B disorder-playrtp
 .RI [ OPTIONS ]
 .RB [ -- ]
-.I ADDRESS
-.I PORT
+.RI [[ GROUP ]
+.IR PORT ]
 .SH DESCRIPTION
 \fBdisorder-playrtp\fR plays a network broadcast sent from the specified
 address.
 .PP
-Normally the \fIADDRESS\fR would be 0.0.0.0.  The \fIPORT\fR will depend on the
-server configuration.  You may need to specify the \fB--multicast\fR option
-(see below) and if you have more than one soundcard perhaps the \fB--device\fR
-option.
+If neither a group nor port are specified then the local DisOrder
+configuration is consulted to find the server and the server is asked where the
+RTP stream is.
+.PP
+If just a port is specified then the RTP stream is assumed to be unicast or
+broadcast to that port.
+.PP
+If a group and a port are specified then the RTP stream is assumed to be
+multicast to that group and port.
 .SH OPTIONS
 .TP
 .B --device \fIDEVICE\fR, \fB-D \fIDEVICE\fR
 Specifies the audio device to use.  The exact meaning of this is
 platform-dependent; on Linux it is the ALSA device name.
 .TP
-.B --multicast \fIGROUP\fR, \fB-M \fIGROUP\fR
-Specifies a multicast group to join.  This is necessary if the network
-broadcast is being multicasted rather than broadcast.
-.TP
 .B --help\fR, \fB-h
 Display a usage message.
 .TP
index 579fd0f3a40d1a833380d91ed67fede593a2687a..0b5b7837b9e9c3b4f13e9f375938281c0ef5c663 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004 Richard Kettlewell
+ * Copyright (C) 2004, 2007 Richard Kettlewell
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
 
 #include "log.h"
 #include "printf.h"
 #include "configuration.h"
 #include "addr.h"
+#include "mem.h"
 
 /** @brief Convert a pair of strings to an address
  * @param a Pointer to string list
@@ -120,7 +123,75 @@ int addrinfocmp(const struct addrinfo *a,
     return memcmp(a->ai_addr, b->ai_addr, a->ai_addrlen); /* kludge */
   }
 }
-  
+
+static inline int multicast4(const struct sockaddr_in *sin) {
+  return IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
+}
+
+static inline int multicast6(const struct sockaddr_in6 *sin6) {
+  return IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
+}
+
+/** @brief Return true if @p sa represents a multicast address */
+int multicast(const struct sockaddr *sa) {
+  switch(sa->sa_family) {
+  case AF_INET:
+    return multicast4((const struct sockaddr_in *)sa);
+  case AF_INET6:
+    return multicast6((const struct sockaddr_in6 *)sa);
+  default:
+    return 0;
+  }
+}
+
+static inline char *format_sockaddr4(const struct sockaddr_in *sin) {
+  char buffer[1024], *r;
+
+  if(sin->sin_port)
+    byte_xasprintf(&r, "%s port %u",
+                  inet_ntop(sin->sin_family, &sin->sin_addr,
+                            buffer, sizeof buffer),
+                  ntohs(sin->sin_port));
+  else
+    byte_xasprintf(&r, "%s",
+                  inet_ntop(sin->sin_family, &sin->sin_addr,
+                            buffer, sizeof buffer));
+  return r;
+}
+
+static inline char *format_sockaddr6(const struct sockaddr_in6 *sin6) {
+  char buffer[1024], *r;
+
+  if(sin6->sin6_port)
+    byte_xasprintf(&r, "%s port %u",
+                  inet_ntop(sin6->sin6_family, &sin6->sin6_addr,
+                            buffer, sizeof buffer),
+                  ntohs(sin6->sin6_port));
+  else
+    byte_xasprintf(&r, "%s",
+                  inet_ntop(sin6->sin6_family, &sin6->sin6_addr,
+                            buffer, sizeof buffer));
+  return r;
+}
+
+static inline char *format_sockaddrun(const struct sockaddr_un *sun) {
+  return xstrdup(sun->sun_path);
+}
+    
+/** @brief Construct a text description a sockaddr */
+char *format_sockaddr(const struct sockaddr *sa) {
+  switch(sa->sa_family) {
+  case AF_INET:
+    return format_sockaddr4((const struct sockaddr_in *)sa);
+  case AF_INET6:
+    return format_sockaddr6((const struct sockaddr_in6 *)sa);
+  case AF_UNIX:
+    return format_sockaddrun((const struct sockaddr_un *)sa);
+  default:
+    return 0;
+  }
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 9ce2720288545384b5fd3f7fbe3f86dc5165efcd..fb1dd7f53f63edd90360b8361e744ee220a20f18 100644 (file)
@@ -28,6 +28,9 @@ struct addrinfo *get_address(const struct stringlist *a,
 int addrinfocmp(const struct addrinfo *a,
                const struct addrinfo *b);
 
+int multicast(const struct sockaddr *sa);
+char *format_sockaddr(const struct sockaddr *sa);
+
 #endif /* ADDR_H */
 
 /*
index 64502c0330d7588e4afcd27b242abefcfd9c23c7..c8b4a622d0238e6d5d255d088209d86e28b1677d 100644 (file)
@@ -806,19 +806,6 @@ static int c_log(struct conn *c,
   return 0;
 }
 
-static void post_move_cleanup(void) {
-  struct queue_entry *q;
-
-  /* If we have caused any random tracks to not be at the end then we make them
-   * no longer be random. */
-  for(q = qhead.next; q != &qhead; q = q->next)
-    if(q->state == playing_random && q->next != &qhead)
-      q->state = playing_unplayed;
-  /* That might mean we need to add a new random track. */
-  add_random_track();
-  queue_write();
-}
-
 static int c_move(struct conn *c,
                  char **vec,
                  int attribute((unused)) nvec) {
@@ -837,7 +824,6 @@ static int c_move(struct conn *c,
     return 1;
   }
   n = queue_move(q, atoi(vec[1]), c->who);
-  post_move_cleanup();
   sink_printf(ev_writer_sink(c->w), "252 %d\n", n);
   /* If we've moved to the head of the queue then prepare the track. */
   if(q == qhead.next)
@@ -874,7 +860,6 @@ static int c_moveafter(struct conn *c,
       return 1;
     }
   queue_moveafter(q, nvec, qs, c->who);
-  post_move_cleanup();
   sink_printf(ev_writer_sink(c->w), "250 Moved tracks\n");
   /* If we've moved to the head of the queue then prepare the track. */
   if(q == qhead.next)
index c8edbfe2d28d2c85f70e3cb7717870b9cceb28e0..40abca59f14aeeba85f4eee8ad9dfa32a112a379 100644 (file)
@@ -118,14 +118,7 @@ static void network_init(void) {
                    res->ai_socktype,
                    res->ai_protocol)) < 0)
     fatal(errno, "error creating broadcast socket");
-  if((res->ai_family == PF_INET
-      && IN_MULTICAST(
-           ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr)
-         ))
-     || (res->ai_family == PF_INET6
-         && IN6_IS_ADDR_MULTICAST(
-               &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr
-            ))) {
+  if(multicast(res->ai_addr)) {
     /* Multicasting */
     switch(res->ai_family) {
     case PF_INET: {