[Icecast-dev] [PATCH 01/31] Hack to support IPhone streaming

Niv Sardi
Fri, 30 Jul 2010 14:54:23 -0000

Iphone (CoreMedia) requires that we insert a Range header,
create a fake one on the fly !

Signed-off-by: Niv Sardi <nsardi at smartjog.com>
---
 src/format.c     |   26 ++++++++++++++++++------
 src/format_mp3.c |   56 +++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/src/format.c b/src/format.c
index 415391c..369dfbd 100644
--- a/src/format.c
+++ b/src/format.c
@@ -3,7 +3,7 @@
  * This program is distributed under the GNU General Public License, version 2.
  * A copy of this license is included with this source.
  *
- * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
  *                      Michael Smith <msmith at xiph.org>,
  *                      oddsock <oddsock at xiph.org>,
  *                      Karl Heyes <karl at xiph.org>
@@ -85,7 +85,7 @@ int format_get_plugin(format_type_t type, source_t *source)
         break;
     }
     if (ret < 0)
-        stats_event (source->mount, "content-type",
+        stats_event (source->mount, "content-type",
                 source->format->contenttype);

     return ret;
@@ -93,7 +93,7 @@ int format_get_plugin(format_type_t type, source_t *source)


 /* clients need to be start from somewhere in the queue so we will look for
- * a refbuf which has been previously marked as a sync point.
+ * a refbuf which has been previously marked as a sync point.
  */
 static void find_client_start (source_t *source, client_t *client)
 {
@@ -248,7 +248,7 @@ int format_generic_write_to_client (client_t *client)


 /* This is the commonly used for source streams, here we just progress to
- * the next buffer in the queue if there is no more left to be written from
+ * the next buffer in the queue if there is no more left to be written from
  * the existing buffer.
  */
 int format_advance_queue (source_t *source, client_t *client)
@@ -280,12 +280,24 @@ static int format_prepare_headers (source_t *source,
client_t *client)
     avl_node *node;
     ice_config_t *config;

+    /* Partial hack, check for range and user agent in this function */
+    const char *useragent;
+    const char *range;
+    useragent = httpp_getvar (client->parser, "user-agent");
+    range     = httpp_getvar (client->parser, "range");
+
     remaining = client->refbuf->len;
     ptr = client->refbuf->data;
     client->respcode = 200;

-    bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
-            "Content-Type: %s\r\n",
source->format->contenttype);
+
+    if (range && useragent && strstr(useragent,
"CoreMedia")) {
+        bytes = snprintf (ptr, remaining, "HTTP/1.1 206 Partial
Content\r\n"
+                          "Content-Type: %s\r\n",
source->format->contenttype);
+    } else {
+	    bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
+                          "Content-Type: %s\r\n",
source->format->contenttype);
+    }

     remaining -= bytes;
     ptr += bytes;
@@ -307,7 +319,7 @@ static int format_prepare_headers (source_t *source,
client_t *client)
             if (bitrate_filtered == 0)
                 brfield = strstr(var->value, "bitrate=");
             if (brfield && sscanf (brfield, "bitrate=%u",
&bitrate))
-            {
+            {
                 bytes = snprintf (ptr, remaining, "icy-br:%u\r\n",
bitrate);
                 next = 0;
                 bitrate_filtered = 1;
diff --git a/src/format_mp3.c b/src/format_mp3.c
index 7f08e15..2d62314 100644
--- a/src/format_mp3.c
+++ b/src/format_mp3.c
@@ -4,7 +4,7 @@
  * This program is distributed under the GNU General Public License, version 2.
  * A copy of this license is included with this source.
  *
- * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
  *                      Michael Smith <msmith at xiph.org>,
  *                      oddsock <oddsock at xiph.org>,
  *                      Karl Heyes <karl at xiph.org>
@@ -461,7 +461,7 @@ static void format_mp3_free_plugin(format_plugin_t *self)

 /* This does the actual reading, making sure the read data is packaged in
  * blocks of 1400 bytes (near the common MTU size). This is because many
- * incoming streams come in small packets which could waste a lot of
+ * incoming streams come in small packets which could waste a lot of
  * bandwidth with many listeners due to headers and such like.
  */
 static int complete_read (source_t *source)
@@ -476,7 +476,7 @@ static int complete_read (source_t *source)

     if (source_mp3->read_data == NULL)
     {
-        source_mp3->read_data = refbuf_new (REFBUF_SIZE);
+        source_mp3->read_data = refbuf_new (REFBUF_SIZE);
         source_mp3->read_count = 0;
     }
     buf = source_mp3->read_data->data + source_mp3->read_count;
@@ -603,7 +603,7 @@ static refbuf_t *mp3_get_filter_meta (source_t *source)
             source_mp3->build_metadata_offset += bytes;
             break;
         }
-        /* copy all bytes except the last one, that way we
+        /* copy all bytes except the last one, that way we
          * know a null byte terminates the message */
         memcpy (source_mp3->build_metadata +
source_mp3->build_metadata_offset,
                 src, metadata_remaining-1);
@@ -654,6 +654,42 @@ static refbuf_t *mp3_get_filter_meta (source_t *source)
     return refbuf;
 }

+inline int format_mp3_insert_coremedia_header(client_t *client, char *ptr, int
remaining) {
+    int range[2];
+    char *rangestr;
+    int read = 0;
+    int bytes;
+    rangestr = httpp_getvar(client->parser, "range");
+
+    if (rangestr != NULL) {
+	if (sscanf(rangestr, "bytes=%d-%d", &range[0], &range[1]) !=
2)
+	    return 0;
+	if (range[0] < 0)
+	    return 0;
+
+	char currenttime[50];
+	time_t now;
+	int strflen;
+	struct tm result;
+	time(&now);
+	strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT",
+			   gmtime_r(&now, &result));
+	client->respcode = 206;
+	bytes = snprintf(ptr, remaining, "Date: %s\r\n", currenttime);
+	if (bytes > 0){
+	    remaining -= bytes;
+	    read += bytes;
+	}
+	bytes = snprintf(ptr + read, remaining, "Content-Range: bytes
%d-%d/221183499\r\n",
+			 range[0], range[1]);
+	if (bytes > 0) {
+	    remaining -= bytes;
+	    read += bytes;
+	}
+    }
+
+    return read;
+}

 static int format_mp3_create_client_data(source_t *source, client_t *client)
 {
@@ -681,7 +717,17 @@ static int format_mp3_create_client_data(source_t *source,
client_t *client)
         /* avoid browser caching, reported via forum */
         bytes = snprintf (ptr, remaining, "Expires: Mon, 26 Jul 1997
05:00:00 GMT\r\n");
         remaining -= bytes;
-        ptr += bytes;
+        ptr += bytes;
+    }
+
+    /* hack for iPhone OS or Simulator with CoreMedia. checks user agent then
adds iPhone-specific headers */
+    if (useragent && strstr(useragent, "CoreMedia"))
+    {
+	bytes = format_mp3_insert_coremedia_header(client, ptr, remaining);
+	if (bytes) {
+	    ptr += bytes;
+	    remaining -= bytes;
+	}
     }

     client->format_data = client_mp3;
--
1.7.1