chiark / gitweb /
mcastsoundd wip
authorianmdlvl <ianmdlvl>
Sun, 15 May 2005 18:45:47 +0000 (18:45 +0000)
committerianmdlvl <ianmdlvl>
Sun, 15 May 2005 18:45:47 +0000 (18:45 +0000)
cprogs/.cvsignore
cprogs/mcastsoundd.c [new file with mode: 0644]

index 545d6c7..54b3c7d 100644 (file)
@@ -4,3 +4,4 @@ trivsoundd
 really
 with-lock-ex
 xacpi-simple
+mcastsoundd
diff --git a/cprogs/mcastsoundd.c b/cprogs/mcastsoundd.c
new file mode 100644 (file)
index 0000000..499bae4
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * http://www.ibiblio.org/pub/Linux/docs/HOWTO/Multicast-HOWTO
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <endian.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+typedef unsigned char Byte;
+
+static const char *ov_mcast= "239.193.27.221";
+static int ov_port_ctrl= 4101;
+static int ov_port_data= -1;
+
+static int mcast_fd;
+static struct sockaddr_in mcast_sa;
+
+static void sysfail(const char *m) { perror(m); exit(16); }
+
+static Byte packet[1024];
+static int packet_len;
+
+static uint64_t htonll(uint64_t v) {
+#if LITTLE_ENDIAN
+  return (v >> 32) | (v << 32);
+#endif
+#if BIG_ENDIAN
+  return v;
+#endif
+}
+
+#define OP_CTRL_PLAY 1
+#define OP_CTRL_STOP 2
+#define OP_CTRL_DATA 3
+
+#define MAR_CTRL_PLAY                          \
+  FI8(operation)                               \
+  FI8(reserved)                                        \
+  FI8(generation)                              \
+  FI8(counter)                                 \
+  FI64(totallen)                               \
+  FI64(startts)                                        \
+  FI32(starttns)                               \
+  FI32(txrate)                                 \
+  FR(trackfn,char,256)
+  
+#define MAR_CTRL_STOP                          \
+  FI8(operation)                               \
+  FI8(reserved)                                        \
+  FR0
+
+#define MAR_DATA                               \
+  FI8(operation)                               \
+  FI8(reserved)                                        \
+  FI8(generation)                              \
+  FI8(counter)                                 \
+  FI64(offset)                                 \
+  FR(data,Byte,1024)
+     
+#define FI8(f)      F(f, uint8_t,  v)
+#define FI32(f)     F(f, uint32_t, htonl(v))
+#define FI64(f)     F(f, uint64_t, htonll(v))
+
+#define MARS                                   \
+  MAR(CTRL_PLAY)                               \
+  MAR(CTRL_STOP)                               \
+  MAR(DATA)
+
+#define F(f,t,c) t f;
+#define FR(f,t,l) t f[(l)]; int f##_l;
+#define FR0 /* */
+#define MAR(m) typedef struct Mar_##m { MAR_##m } Mar_##m;
+MARS
+#undef F
+#undef FR
+#undef FR0
+#undef MAR
+
+#define F(f,t,c) { t v= d->f; *(t*)p= c; p += sizeof(t); };
+#define FR(f,t,l) assert(d->f##_l<=l); memcpy(p,d->f,d->f##_l); p+=d->f##_l;
+#define FR0 /* */
+#define MAR(m)                                 \
+  static void mar_##m(const Mar_##m *d) {      \
+    Byte *p= packet;                           \
+    MAR_##m                                    \
+    packet_len= p - packet;                    \
+    assert(packet_len < sizeof(packet));       \
+  }
+MARS
+#undef F
+#undef FR
+#undef FR0
+#undef MAR
+
+#define F(f,t,c) {                             \
+    t v;                                       \
+    if (lr < sizeof(t)) return -1;             \
+    v= *(const t*)p;                           \
+    p += sizeof(t);  lr -= sizeof(t);          \
+    d->f= c;                                   \
+  };
+#define FR(f,t,l) {                            \
+    if (lr > l) return -1;                     \
+    memcpy(d->f, p, lr);                       \
+    d->f##_l= lr;                              \
+  };
+#define FR0                                    \
+    if (lr) return -1;
+#define MAR(m)                                 \
+  static int unmar_##m(Mar_##m *d) {           \
+    const Byte *p= packet;                     \
+    int lr= packet_len;                                \
+    MAR_##m                                    \
+    return 0;                                  \
+  }
+MARS
+#undef F
+#undef FR
+#undef FR0
+#undef MAR
+
+static void nonblock(int fd) {
+  int r;
+  r= fcntl(fd,F_GETFL);  if (r<0) sysfail("nonblock fcntl F_GETFL");
+  r |= O_NONBLOCK;
+  r= fcntl(fd,F_SETFL,r);  if (r<0) sysfail("nonblock fcntl F_GETFL");
+}
+
+static void blocksignals(int how) {
+  sigset_t set;
+  int r;
+
+  sigemptyset(&set);
+  sigaddset(&set,SIGCHLD);
+  r= sigprocmask(how,&set,0);
+  if (r) sysfail("sigprocmask");
+}
+
+static void recvd_play(void) {
+  Mar_CTRL_PLAY pkt;
+  int r;
+
+  r= unmar_CTRL_PLAY(&pkt);
+  if (r) { fprintf(stderr,"bad PLAY packet\n"); return; }
+}
+
+static void recvd_stop(void) {
+  Mar_CTRL_STOP pkt;
+  int r;
+
+  r= unmar_CTRL_STOP(&pkt);
+  if (r) { fprintf(stderr,"bad STOP packet\n"); return; }
+}
+  
+static void player(void) {
+  struct sockaddr_in peer_sa, old_peer_sa;
+  socklen_t peer_salen;
+  struct ip_mreq mreq;
+  int r;
+  
+  memset(&old_peer_sa, 0, sizeof(old_peer_sa));
+
+  mcast_fd= socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (mcast_fd<0) sysfail("mcast_fd socket");
+
+  r= bind(mcast_fd, (struct sockaddr*)&mcast_sa, sizeof(mcast_sa));
+  if (r) sysfail("mcast_fd bind");
+
+  mcast_sa.sin_port= htons(ov_port_ctrl);
+  
+  mreq.imr_multiaddr= mcast_sa.sin_addr;
+  mreq.imr_interface.s_addr= INADDR_ANY;
+  r= setsockopt(mcast_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+  if (r) sysfail("mcast_fd add memb");
+
+  for (;;) {
+    peer_salen= sizeof(peer_sa);
+    memset(&peer_sa, 0, sizeof(peer_sa));
+    
+    blocksignals(SIG_UNBLOCK);
+    packet_len= recvfrom(mcast_fd, packet, sizeof(packet),
+                        MSG_TRUNC, (struct sockaddr*)&peer_sa, &peer_salen);
+    blocksignals(SIG_BLOCK);
+
+    if (packet_len<0) {
+      if (errno==EINTR) continue;
+      perror("mcast_fd recvfrom");
+      continue;
+    }
+    if (peer_salen != sizeof(peer_sa)) {
+      fprintf(stderr,"mcast_fd recvfrom salen %ld not %ld\n",
+             (unsigned long)peer_salen, (unsigned long)sizeof(peer_sa));
+      continue;
+    }
+    if (packet_len > sizeof(packet)) {
+      fprintf(stderr,"mcast_fd recvfrom packet len %ld longer than max %ld\n",
+             (unsigned long)packet_len, (unsigned long)sizeof(packet));
+      continue;
+    }
+    if (memcmp(&old_peer_sa, &peer_sa, sizeof(old_peer_sa))) {
+      char *p= inet_ntoa(peer_sa.sin_addr);
+      fprintf(stderr,"receiving from %s:%d\n",p,ntohs(peer_sa.sin_port));
+      memcpy(&old_peer_sa, &peer_sa, sizeof(old_peer_sa));
+    }
+    if (packet_len==0) {
+      fprintf(stderr,"empty packet!\n");
+      continue;
+    }
+    switch (packet[0]) {
+    case OP_CTRL_PLAY:
+      recvd_play();
+      break;
+    case OP_CTRL_STOP:
+      recvd_stop();
+      break;
+    default:
+      fprintf(stderr,"unknown opcode %d\n",packet[0]);
+    }
+  }
+}
+
+int main(int argc, const char *const *argv) {
+  int r;
+
+  if (ov_port_data < 0) ov_port_data= ov_port_ctrl+1;
+
+  memset(&mcast_sa,0,sizeof(mcast_sa));
+  mcast_sa.sin_family= AF_INET;
+  r= inet_aton(ov_mcast, &mcast_sa.sin_addr);  assert(r);
+
+  player();
+  nonblock(0);
+  mar_CTRL_PLAY(0);
+  mar_CTRL_STOP(0);
+  mar_DATA(0);
+  unmar_DATA(0);
+  return 0;
+}