Hi, A radio is being developped at school, and we wanted to avoid eating too much bandwidth, so we had a look at RTP. I first implemented it in icecast1 and it worked well (although we had to apply a patch to make RTP work with xmms). We are indeed saving a lot of bandwidth. I then adapted it to icecast2, here is the patch. The biggest trouble for now is the configurability. I added 3 global parameters: rtp_port, rtp_ttl and rtp_ip, but they actually should be given by the source (if it wants to have RTP cast). Some details like the SSRC and mtu should be parameters as well. RTCP is not handled either. This implementation is only for mp3 streams, because the vorbis RTP payload format is still not fully standardized. Once it is, a vorbis stream implementation may be possible. Yet we may start an experimental implementation right now. Regards, Samuel Thibault --- icecast2-1.9+2.0alphasnap2+20030802.orig/src/cfgfile.h +++ icecast2-1.9+2.0alphasnap2+20030802/src/cfgfile.h @@ -82,6 +82,9 @@ char *hostname; int port; + int rtp_port; + int rtp_ttl; + int rtp_ip; listener_t listeners[MAX_LISTEN_SOCKETS]; --- icecast2-1.9+2.0alphasnap2+20030802.orig/src/cfgfile.c +++ icecast2-1.9+2.0alphasnap2+20030802/src/cfgfile.c @@ -282,6 +282,9 @@ configuration->dir_list = NULL; configuration->hostname = CONFIG_DEFAULT_HOSTNAME; configuration->port = 0; + configuration->rtp_port = 8000; + configuration->rtp_ttl = 1; + configuration->rtp_ip = 0; configuration->listeners[0].port = 0; configuration->listeners[0].bind_address = NULL; configuration->master_server = NULL; @@ -348,6 +351,18 @@ configuration->port = atoi(tmp); configuration->listeners[0].port = atoi(tmp); if (tmp) xmlFree(tmp); + } else if (strcmp(node->name, "rtp-port") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + configuration->rtp_port = atoi(tmp); + if (tmp) xmlFree(tmp); + } else if (strcmp(node->name, "rtp-ttl") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + configuration->rtp_ttl = atoi(tmp); + if (tmp) xmlFree(tmp); + } else if (strcmp(node->name, "rtp-ip") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + configuration->rtp_ip = (239<<24)|atoi(tmp); + if (tmp) xmlFree(tmp); } else if (strcmp(node->name, "bind-address") == 0) { if (configuration->listeners[0].bind_address) xmlFree(configuration->listeners[0].bind_address); --- icecast2-1.9+2.0alphasnap2+20030802.orig/src/source.c +++ icecast2-1.9+2.0alphasnap2+20030802/src/source.c @@ -19,6 +19,10 @@ #include <windows.h> #endif +#include <netinet/udp.h> +#include <netinet/ip.h> +#include <sys/time.h> + #include "thread/thread.h" #include "avl/avl.h" #include "httpp/httpp.h" @@ -175,9 +179,50 @@ return NULL; } +/* ST: added RTP support. + * + * TODO: have everything as an option + */ + +/** + * RTP header structure + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |V=2|P|X| CC |M| PT | sequence number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | timestamp | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | synchronization source (SSRC) identifier | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | contributing source (CSRC) identifiers | + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +#define RTP_PKT_SIZE 1500 + +/** RTP header size */ +#define RTP_HDR_SIZE 12 +/** RTP MPEG header size */ +#define RTP_MPEG_HDR_SIZE 4 +/** RTP CSRC list entry size */ +#define RTP_CSRC_SIZE 4 + +#define RTP_MPEG_CLK 90000 void *source_main(void *arg) { + int rtp_socket; + struct sockaddr_in rtp_sin; + unsigned char on=1; + char rtp_buf[RTP_PKT_SIZE]; + int rtp_done,rtp_amount; + struct timeval rtp_tv; + unsigned long rtp_timestamp=random(); /* random timestamp */ + source_t *source = (source_t *)arg; source_t *fallback_source; char buffer[4096]; @@ -224,6 +269,30 @@ } #endif + /* RTP diffusion initialization, if asked for */ + if (config->rtp_ip && (rtp_socket = socket(PF_INET,SOCK_DGRAM,0))>=0) { + rtp_sin.sin_addr.s_addr = htonl(config->rtp_ip); + rtp_sin.sin_family = AF_INET; + rtp_sin.sin_port = htons(config->rtp_port); + + if (connect(rtp_socket, (struct sockaddr *) &rtp_sin, sizeof(rtp_sin)) < 0 + || setsockopt(rtp_socket, IPPROTO_IP, IP_MULTICAST_TTL, + &config->rtp_ttl, sizeof(config->rtp_ttl)) < 0 + || setsockopt(rtp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, + &on, sizeof(on)) < 0) { + WARN1("WARNING: RTP failed: %s", strerror(errno)); + close(rtp_socket); + rtp_socket = -1; + } else { + bzero(&rtp_buf,RTP_HDR_SIZE+RTP_MPEG_HDR_SIZE); + rtp_buf[0]=2<<6; /* V */ + rtp_buf[1]=14; /* MPEG 1 or 2 */ + *(unsigned short *) (rtp_buf+2)=random(); + *(unsigned long *) (rtp_buf+8)=(unsigned long)source; /* use source as SSRC, TODO: set as option */ + INFO0("rtp socket ok"); + } + } else WARN1("rtp socket failed: %s", strerror(errno)); + config_release_config(); /* grab a read lock, to make sure we get a chance to cleanup */ @@ -412,6 +481,23 @@ } } + if (rtp_socket>=0) { + rtp_amount=1420-sizeof(struct iphdr)-sizeof(struct udphdr)-RTP_HDR_SIZE-RTP_MPEG_HDR_SIZE; + gettimeofday(&rtp_tv,NULL); + *(unsigned long *)(rtp_buf+4)=htonl(rtp_timestamp+ + (((unsigned long)rtp_tv.tv_sec)*RTP_MPEG_CLK)+ + (((unsigned long)rtp_tv.tv_usec)*(RTP_MPEG_CLK/10000))/100); + for(rtp_done=0;rtp_done<refbuf->len;rtp_done+=rtp_amount) { + if (rtp_amount>refbuf->len-rtp_done) + rtp_amount=refbuf->len-rtp_done; + *(unsigned short*)(rtp_buf+RTP_HDR_SIZE+2)=htons(rtp_done); + memcpy(rtp_buf+RTP_HDR_SIZE+RTP_MPEG_HDR_SIZE, refbuf->data+rtp_done, rtp_amount); + write(rtp_socket, rtp_buf, RTP_HDR_SIZE+RTP_MPEG_HDR_SIZE+rtp_amount); + *(unsigned short*)(rtp_buf+2)+ htons(ntohs(*(unsigned short*)(rtp_buf+2))+1); + } + } + /* acquire read lock on client_tree */ avl_tree_rlock(source->client_tree); --- >8 ---- List archives: http://www.xiph.org/archives/ icecast project homepage: http://www.icecast.org/ To unsubscribe from this list, send a message to 'icecast-dev-request@xiph.org' containing only the word 'unsubscribe' in the body. No subject is needed. Unsubscribe messages sent to the list will be ignored/filtered.