{ "max", required_argument, 0, 'x' },
{ "buffer", required_argument, 0, 'b' },
{ "rcvbuf", required_argument, 0, 'R' },
+ { "multicast", required_argument, 0, 'M' },
{ 0, 0, 0, 0 }
};
" --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"
" --help, -h Display usage message\n"
" --version, -V Display version number\n"
);
char *sockname;
int rcvbuf, target_rcvbuf = 131072;
socklen_t len;
+ char *multicast_group = 0;
+ struct ip_mreq mreq;
+ struct ipv6_mreq mreq6;
static const struct addrinfo prefs = {
AI_PASSIVE,
mem_init();
if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
- while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:", options, 0)) >= 0) {
+ while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:", options, 0)) >= 0) {
switch(n) {
case 'h': help();
case 'V': version();
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;
default: fatal(0, "invalid option");
}
}
fatal(errno, "error creating socket");
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) {
+ case PF_INET:
+ mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->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;
+ memset(&mreq6.ipv6mr_interface, 0, sizeof mreq6.ipv6mr_interface);
+ if(setsockopt(rtpfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq6, sizeof mreq6) < 0)
+ fatal(errno, "error calling setsockopt IPV6_JOIN_GROUP");
+ break;
+ default:
+ fatal(0, "unsupported address family %d", res->ai_family);
+ }
+ }
len = sizeof rcvbuf;
if(getsockopt(rtpfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len) < 0)
fatal(errno, "error calling getsockopt SO_RCVBUF");
{ C(listen), &type_stringlist, validate_addrport },
{ C(lock), &type_boolean, validate_any },
{ C(mixer), &type_string, validate_ischr },
+ { C(multicast_ttl), &type_integer, validate_non_negative },
{ C(namepart), &type_namepart, validate_any },
{ C2(nice, nice_rescan), &type_integer, validate_non_negative },
{ C(nice_rescan), &type_integer, validate_non_negative },
c->sample_format.byte_format = AO_FMT_NATIVE;
c->queue_pad = 10;
c->speaker_backend = -1;
+ c->multicast_ttl = 1;
return c;
}
res->ai_socktype,
res->ai_protocol)) < 0)
fatal(errno, "error creating broadcast socket");
- if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
- fatal(errno, "error setting SO_BROADCAST on 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
+ ))) {
+ /* Multicasting */
+ switch(res->ai_family) {
+ case PF_INET: {
+ const int mttl = config->multicast_ttl;
+ if(setsockopt(bfd, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, sizeof mttl) < 0)
+ fatal(errno, "error setting IP_MULTICAST_TTL on multicast socket");
+ break;
+ }
+ case PF_INET6: {
+ const int mttl = config->multicast_ttl;
+ if(setsockopt(bfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &mttl, sizeof mttl) < 0)
+ fatal(errno, "error setting IPV6_MULTICAST_HOPS on multicast socket");
+ break;
+ }
+ default:
+ fatal(0, "unsupported address family %d", res->ai_family);
+ }
+ } else {
+ /* Presumably just broadcasting */
+ if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
+ fatal(errno, "error setting SO_BROADCAST on broadcast socket");
+ }
len = sizeof sndbuf;
if(getsockopt(bfd, SOL_SOCKET, SO_SNDBUF,
&sndbuf, &len) < 0)