From: ianmdlvl Date: Sun, 15 May 2005 18:45:47 +0000 (+0000) Subject: mcastsoundd wip X-Git-Tag: debian_version_4_0_99_0_16~2 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-utils.git;a=commitdiff_plain;h=7b71d538a8df1c6440641909947fa9bf89612fa4 mcastsoundd wip --- diff --git a/cprogs/.cvsignore b/cprogs/.cvsignore index 545d6c7..54b3c7d 100644 --- a/cprogs/.cvsignore +++ b/cprogs/.cvsignore @@ -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 index 0000000..499bae4 --- /dev/null +++ b/cprogs/mcastsoundd.c @@ -0,0 +1,248 @@ +/* + * http://www.ibiblio.org/pub/Linux/docs/HOWTO/Multicast-HOWTO + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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; +}