[icecast-dev] [RFC] RTP support

Samuel Thibault Samuel.Thibault at ens-lyon.fr
Sun Nov 23 08:45:17 PST 2003



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 at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the Icecast-dev mailing list