--- /dev/null
+/*
+ * 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;
+}