[xiph-commits] r8191 - in icecast/trunk/icecast: conf doc src web

oddsock at motherfish-iii.xiph.org oddsock at motherfish-iii.xiph.org
Thu Nov 11 07:47:34 PST 2004


Author: oddsock
Date: 2004-11-11 07:47:33 -0800 (Thu, 11 Nov 2004)
New Revision: 8191

Modified:
   icecast/trunk/icecast/conf/Makefile.am
   icecast/trunk/icecast/doc/icecast2_config_file.html
   icecast/trunk/icecast/src/admin.c
   icecast/trunk/icecast/src/cfgfile.c
   icecast/trunk/icecast/src/cfgfile.h
   icecast/trunk/icecast/src/connection.c
   icecast/trunk/icecast/src/format.c
   icecast/trunk/icecast/src/format.h
   icecast/trunk/icecast/src/slave.c
   icecast/trunk/icecast/src/source.c
   icecast/trunk/icecast/src/source.h
   icecast/trunk/icecast/src/util.c
   icecast/trunk/icecast/src/util.h
   icecast/trunk/icecast/src/yp.c
   icecast/trunk/icecast/web/status.xsl
Log:
* support for the Shoutcast DSP (yay!).
    You can now use the Shoutcast DSP as a source client.  The connection 
    protocol is a bit odd, and we had to handle it separately, and thus 
    we've added a new config option (<shoutcast-compat>) that is set at 
    the listener port level.
* support for NSV (and the nsvscsrc source client).
    After adding support for the connection protocol of the shoutcast DSP, 
    adding NSV was just a simple of a few special handling cases.
* removed all traces of the earlier attempt at the shoutcast DSP connection 
  protocol
* Due to the growing complexity of the config files, I've also created a 
  few alternate config files, namely one for a "shoutcast compat" setup
  as well as a "minimal" one for quick basic configurations.


Modified: icecast/trunk/icecast/conf/Makefile.am
===================================================================
--- icecast/trunk/icecast/conf/Makefile.am	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/conf/Makefile.am	2004-11-11 15:47:33 UTC (rev 8191)
@@ -2,11 +2,11 @@
 
 AUTOMAKE_OPTIONS = foreign
 
-EXTRA_DIST = icecast.xml.in
-DISTCLEANFILES = icecast.xml.dist
+EXTRA_DIST = icecast.xml.in icecast_minimal.xml.in icecast_shoutcast_compat.xml.in
+DISTCLEANFILES = icecast.xml.dist icecast_minimal.xml.dist icecast_shoutcast_compat.xml.dist
 
 docdir = $(datadir)/$(PACKAGE)/doc
-doc_DATA = icecast.xml.dist
+doc_DATA = icecast.xml.dist icecast_minimal.xml.dist icecast_shoutcast_compat.xml.dist
 
 install-data-hook:
 	$(mkinstalldirs) $(DESTDIR)$(sysconfdir)
@@ -20,6 +20,12 @@
 icecast.xml.dist: $(srcdir)/icecast.xml.in
 	$(edit) $(srcdir)/icecast.xml.in > icecast.xml.dist
 
+icecast_minimal.xml.dist: $(srcdir)/icecast_minimal.xml.in
+	$(edit) $(srcdir)/icecast_minimal.xml.in > icecast_minimal.xml.dist
+
+icecast_shoutcast_compat.xml.dist: $(srcdir)/icecast_shoutcast_compat.xml.in
+	$(edit) $(srcdir)/icecast_shoutcast_compat.xml.in > icecast_shoutcast_compat.xml.dist
+
 debug:
 	$(MAKE) all CFLAGS="@DEBUG@"
 

Modified: icecast/trunk/icecast/doc/icecast2_config_file.html
===================================================================
--- icecast/trunk/icecast/doc/icecast2_config_file.html	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/doc/icecast2_config_file.html	2004-11-11 15:47:33 UTC (rev 8191)
@@ -157,6 +157,11 @@
         &lt;port&gt;8000&lt;/port&gt;
         &lt;bind-address&gt;127.0.0.1&lt;/bind-address&gt;
     &lt;/listen-socket&gt;
+    &lt;listen-socket&gt;
+        &lt;port&gt;8001&lt;/port&gt;
+        &lt;bind-address&gt;127.0.0.1&lt;/bind-address&gt;
+        &lt;shoutcast-compat&gt;1&lt;/shoutcast-compat&gt;
+    &lt;/listen-socket&gt;
 
     &lt;fileserve&gt;1&lt;/fileserve&gt;
 </pre>
@@ -170,6 +175,10 @@
 <div class="indentedbox">
 And option IP address that can be used to bind to a specific network card.  If not supplied, then &lt;hostname&gt; will be used.
 </div>
+<h4>shoutcast-compat</h4>
+<div class="indentedbox">
+This optional flag will indicate that this port will operate in 'shoutcast-compatibility' mode.  Due to major differences in the source client connection protocol, if you wish to use any of the shoutcast DJ tools, you will need to configure at least one socket as shoutcast-compatible.  Note that when in this mode, only source clients (and specifically shoutcast source clients) will be able to attach to this port.  All listeners may connect to any of the ports defined without this flag.  Also, for proper Shoutcast DSP compatibility, you must define a listen socket with a port one less than the one defined as 'shoutcast-compat'.  This means if you define 8001 as shoutcast-compat, then you will need to define a listen port of 8000 and it must not also be defined as shoutcast-compat.  See the example config file is the distribution for more info.
+</div>
 <h4>fileserve</h4>
 <div class="indentedbox">
 This flag turns on the icecast2 fileserver from which static files can be served.  All files are served relative to the path specified in the &lt;paths&gt;&lt;webroot&gt; configuration setting.

Modified: icecast/trunk/icecast/src/admin.c
===================================================================
--- icecast/trunk/icecast/src/admin.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/admin.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -52,6 +52,7 @@
 #define COMMAND_RAW_SHOW_LISTENERS  3
 #define COMMAND_RAW_MOVE_CLIENTS    4
 #define COMMAND_RAW_MANAGEAUTH      5
+#define COMMAND_SHOUTCAST_METADATA_UPDATE     6
 
 #define COMMAND_TRANSFORMED_FALLBACK        50
 #define COMMAND_TRANSFORMED_SHOW_LISTENERS  53
@@ -78,6 +79,7 @@
 
 #define FALLBACK_RAW_REQUEST "fallbacks"
 #define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
+#define SHOUTCAST_METADATA_REQUEST "admin.cgi"
 #define METADATA_REQUEST "metadata"
 #define LISTCLIENTS_RAW_REQUEST "listclients"
 #define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
@@ -112,6 +114,8 @@
         return COMMAND_TRANSFORMED_FALLBACK;
     else if(!strcmp(command, METADATA_REQUEST))
         return COMMAND_METADATA_UPDATE;
+    else if(!strcmp(command, SHOUTCAST_METADATA_REQUEST))
+        return COMMAND_SHOUTCAST_METADATA_UPDATE;
     else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
         return COMMAND_RAW_SHOW_LISTENERS;
     else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
@@ -158,6 +162,7 @@
 
 static void command_fallback(client_t *client, source_t *source, int response);
 static void command_metadata(client_t *client, source_t *source);
+static void command_shoutcast_metadata(client_t *client, source_t *source);
 static void command_show_listeners(client_t *client, source_t *source,
         int response);
 static void command_move_clients(client_t *client, source_t *source,
@@ -269,13 +274,20 @@
     int command;
     int noauth = 0;
 
-    if(strncmp("/admin/", uri, 7)) {
+    DEBUG1("Admin request (%s)", uri);
+    if (!((strcmp(uri, "/admin.cgi") == 0) ||
+         (strncmp("/admin/", uri, 7) == 0))) {
         ERROR0("Internal error: admin request isn't");
         client_send_401(client);
         return;
     }
 
-    command_string = uri + 7;
+    if (strcmp(uri, "/admin.cgi") == 0) {
+        command_string = uri + 1;
+    }
+    else {
+        command_string = uri + 7;
+    }
 
     DEBUG1("Got command (%s)", command_string);
     command = admin_get_command(command_string);
@@ -289,6 +301,31 @@
 
     mount = httpp_get_query_param(client->parser, "mount");
 
+    if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
+        source_t *source;
+
+        mount = "/";
+        noauth = 1;
+        avl_tree_rlock(global.source_tree);
+        source = source_find_mount_raw(mount);
+        if (source == NULL) {
+            WARN2("Admin command %s on non-existent source %s", 
+                    command_string, mount);
+            avl_tree_unlock(global.source_tree);
+            client_send_400(client, "Mount / does not exist");
+            return;
+        }
+        else {
+            if (source->shoutcast_compat == 0) {
+                ERROR0("Illegal call to change metadata, source not shoutcast compatible");
+                avl_tree_unlock (global.source_tree);
+                client_send_400 (client, "Illegal metadata call");
+                return;
+            }
+        }
+        avl_tree_unlock(global.source_tree);
+    }
+
     if(mount != NULL) {
         source_t *source;
 
@@ -319,13 +356,15 @@
         }
         else
         {
-            if (source->running == 0)
-            {
-                INFO2("Received admin command %s on unavailable mount \"%s\"",
-                        command_string, mount);
-                avl_tree_unlock (global.source_tree);
-                client_send_400 (client, "Source is not available");
-                return;
+            if (!source->shoutcast_compat) {
+                if (source->running == 0)
+                {
+                    INFO2("Received admin command %s on unavailable mount \"%s\"",
+                            command_string, mount);
+                    avl_tree_unlock (global.source_tree);
+                    client_send_400 (client, "Source is not available");
+                    return;
+                }
             }
             INFO2("Received admin command %s on mount \"%s\"", 
                     command_string, mount);
@@ -403,6 +442,9 @@
         case COMMAND_METADATA_UPDATE:
             command_metadata(client, source);
             break;
+        case COMMAND_SHOUTCAST_METADATA_UPDATE:
+            command_shoutcast_metadata(client, source);
+            break;
         case COMMAND_RAW_SHOW_LISTENERS:
             command_show_listeners(client, source, RAW);
             break;
@@ -784,7 +826,8 @@
     COMMAND_REQUIRE(client, "mode", action);
     COMMAND_REQUIRE(client, "song", value);
 
-    if (source->format->type != FORMAT_TYPE_MP3)
+    if ((source->format->type != FORMAT_TYPE_MP3) &&
+        (source->format->type != FORMAT_TYPE_NSV))
     {
         client_send_400 (client, "Not mp3, cannot update metadata");
         return;
@@ -811,6 +854,64 @@
     html_success(client, "Metadata update successful");
 }
 
+static void command_shoutcast_metadata(client_t *client, source_t *source)
+{
+    char *action;
+    char *value;
+    char *source_pass;
+    char *config_source_pass;
+    ice_config_t *config;
+    mp3_state *state;
+
+    DEBUG0("Got shoutcast metadata update request");
+
+    COMMAND_REQUIRE(client, "mode", action);
+    COMMAND_REQUIRE(client, "song", value);
+    COMMAND_REQUIRE(client, "pass", source_pass);
+
+    config = config_get_config();
+    config_source_pass = strdup(config->source_password);
+    config_release_config();
+
+    if ((source->format->type != FORMAT_TYPE_MP3) &&
+        (source->format->type != FORMAT_TYPE_NSV))
+    {
+        client_send_400 (client, "Not mp3 or NSV, cannot update metadata");
+        return;
+    }
+
+    if (strcmp (action, "updinfo") != 0)
+    {
+        client_send_400 (client, "No such action");
+        return;
+    }
+
+    if (strcmp(source_pass, config_source_pass) != 0)
+    {
+        ERROR0("Invalid source password specified, metadata not updated");
+        client_send_400 (client, "Invalid source password");
+        return;
+    }
+
+    if (config_source_pass) {
+        free(config_source_pass);
+    }
+
+    state = source->format->_state;
+
+    mp3_set_tag (source->format, "title", value);
+
+    DEBUG2("Metadata on mountpoint %s changed to \"%s\"", 
+        source->mount, value);
+    stats_event(source->mount, "title", value);
+
+    /* If we get an update on the mountpoint, force a
+       yp touch */
+    yp_touch (source->mount);
+
+    html_success(client, "Metadata update successful");
+}
+
 static void command_stats(client_t *client, int response) {
     xmlDocPtr doc;
 

Modified: icecast/trunk/icecast/src/cfgfile.c
===================================================================
--- icecast/trunk/icecast/src/cfgfile.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/cfgfile.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -321,6 +321,7 @@
     configuration->port = 0;
     configuration->listeners[0].port = 0;
     configuration->listeners[0].bind_address = NULL;
+    configuration->listeners[0].shoutcast_compat = 0;
     configuration->master_server = NULL;
     configuration->master_server_port = 0;
     configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
@@ -667,6 +668,11 @@
             listener->port = atoi(tmp);
             if(tmp) xmlFree(tmp);
         }
+        else if (strcmp(node->name, "shoutcast-compat") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            listener->shoutcast_compat = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
         else if (strcmp(node->name, "bind-address") == 0) {
             listener->bind_address = (char *)xmlNodeListGetString(doc, 
                     node->xmlChildrenNode, 1);

Modified: icecast/trunk/icecast/src/cfgfile.h
===================================================================
--- icecast/trunk/icecast/src/cfgfile.h	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/cfgfile.h	2004-11-11 15:47:33 UTC (rev 8191)
@@ -75,6 +75,7 @@
 typedef struct {
     int port;
     char *bind_address;
+    int shoutcast_compat;
 } listener_t;
 
 typedef struct ice_config_tag

Modified: icecast/trunk/icecast/src/connection.c
===================================================================
--- icecast/trunk/icecast/src/connection.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/connection.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -65,6 +65,23 @@
 
 #define CATMODULE "connection"
 
+/* Two different major types of source authentication.
+   Shoutcast style is used only by the Shoutcast DSP
+   and is a crazy version of HTTP.  It looks like :
+     Source Client -> Connects to port + 1
+     Source Client -> sends encoder password (plaintext)\r\n
+     Icecast -> reads encoder password, if ok, sends OK2\r\n, else disconnects
+     Source Client -> reads OK2\r\n, then sends http-type request headers
+                      that contain the stream details (icy-name, etc..)
+     Icecast -> reads headers, stores them
+     Source Client -> starts sending MP3 data
+     Source Client -> periodically updates metadata via admin.cgi call
+
+   Icecast auth style uses HTTP and Basic Authorization.
+*/
+#define SHOUTCAST_SOURCE_AUTH 1
+#define ICECAST_SOURCE_AUTH 0
+
 typedef struct con_queue_tag {
     connection_t *con;
     struct con_queue_tag *next;
@@ -664,7 +681,7 @@
 
 
 static void _handle_source_request(connection_t *con, 
-        http_parser_t *parser, char *uri)
+        http_parser_t *parser, char *uri, int auth_style)
 {
     client_t *client;
     source_t *source;
@@ -680,18 +697,23 @@
         return;
     }
 
-    if (!connection_check_source_pass(parser, uri)) {
-        /* We commonly get this if the source client is using the wrong
-         * protocol: attempt to diagnose this and return an error
-         */
-        /* TODO: Do what the above comment says */
-        INFO1("Source (%s) attempted to login with invalid or missing password", uri);
-        client_send_401(client);
-        return;
+    if (auth_style == ICECAST_SOURCE_AUTH) {
+        if (!connection_check_source_pass(parser, uri)) {
+            /* We commonly get this if the source client is using the wrong
+             * protocol: attempt to diagnose this and return an error
+             */
+            /* TODO: Do what the above comment says */
+            INFO1("Source (%s) attempted to login with invalid or missing password", uri);
+            client_send_401(client);
+            return;
+        }
     }
     source = source_reserve (uri);
     if (source)
     {
+        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
+            source->shoutcast_compat = 1;
+        }
         source->client = client;
         source->parser = parser;
         source->con = con;
@@ -796,7 +818,8 @@
     stats_event_inc(NULL, "client_connections");
 
     /* Dispatch all admin requests */
-    if (strncmp(uri, "/admin/", 7) == 0) {
+    if ((strcmp(uri, "/admin.cgi") == 0) ||
+        (strncmp(uri, "/admin/", 7) == 0)) {
         admin_handle_request(client, uri);
         if (uri != passed_uri) free (uri);
         return;
@@ -965,6 +988,74 @@
     if (uri != passed_uri) free (uri);
 }
 
+void _handle_shoutcast_compatible(connection_t *con, char *source_password) {
+    char shoutcast_password[256];
+    char shoutcast_source[256];
+    char *http_compliant;
+    int http_compliant_len = 0;
+    char header[4096];
+    http_parser_t *parser;
+
+    memset(shoutcast_password, 0, sizeof (shoutcast_password));
+    /* Step one of shoutcast auth protocol, read encoder password (1 line) */
+    if (util_read_header(con->sock, shoutcast_password, 
+            sizeof (shoutcast_password), 
+            READ_LINE) == 0) {
+        /* either we didn't get a complete line, or we timed out */
+        connection_close(con);
+        return;
+    }
+    /* Get rid of trailing \n */
+    shoutcast_password[strlen(shoutcast_password)-1] = '\000';
+    if (strcmp(shoutcast_password, source_password)) {
+        ERROR0("Invalid source password");
+        connection_close(con);
+        return;
+    }
+    /* Step two of shoutcast auth protocol, send OK2.  For those
+       interested, OK2 means it supports metadata updates via admin.cgi,
+       and the string "OK" can also be sent, but will indicate to the
+       shoutcast source client to not send metadata updates.
+       I believe icecast 1.x used to send OK. */
+    sock_write(con->sock, "%s\r\n", "OK2");
+
+    memset(header, 0, sizeof (header));
+    /* Step three of shoutcast auth protocol, read HTTP-style
+       request headers and process them.*/
+    if (util_read_header(con->sock, header, sizeof (header), 
+                         READ_ENTIRE_HEADER) == 0) {
+        /* either we didn't get a complete header, or we timed out */
+        connection_close(con);
+        return;
+    }
+    /* Here we create a valid HTTP request based of the information
+       that was passed in via the non-HTTP style protocol above. This
+       means we can use some of our existing code to handle this case */
+    memset(shoutcast_source, 0, sizeof (shoutcast_source));
+    strcpy(shoutcast_source, "SOURCE / HTTP/1.0\r\n");
+    http_compliant_len = strlen(shoutcast_source) + 
+                         strlen(header) + 1;
+    http_compliant = (char *)calloc(1, http_compliant_len);
+    sprintf(http_compliant, "%s%s", shoutcast_source, 
+        header);
+    parser = httpp_create_parser();
+    httpp_initialize(parser, NULL);
+    if (httpp_parse(parser, http_compliant, 
+        strlen(http_compliant))) {
+        _handle_source_request(con, parser, "/", SHOUTCAST_SOURCE_AUTH);
+        free(http_compliant);
+        return;
+    }
+    else {
+        ERROR0("Invalid source request");
+        connection_close(con);
+        free(http_compliant);
+        httpp_destroy(parser);
+        return;
+    }
+    return;
+}
+
 static void *_handle_connection(void *arg)
 {
     char header[4096];
@@ -972,6 +1063,10 @@
     http_parser_t *parser;
     char *rawuri, *uri;
     client_t *client;
+    int i = 0;
+    int continue_flag = 0;
+    ice_config_t *config;
+    char *source_password;
 
     while (global.running == ICE_RUNNING) {
 
@@ -996,9 +1091,30 @@
 
             sock_set_blocking(con->sock, SOCK_BLOCK);
 
+            continue_flag = 0;
+            /* Check for special shoutcast compatability processing */
+            for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
+                if(global.serversock[i] == con->serversock) {
+                    config = config_get_config();
+                    if (config->listeners[i].shoutcast_compat) {
+                        source_password = strdup(config->source_password);
+                        config_release_config();
+                        _handle_shoutcast_compatible(con, source_password);
+                        free(source_password);
+                        continue_flag = 1;
+                        break;
+                    }
+                    config_release_config();
+                }
+            }
+            if(continue_flag) {
+                continue;
+            }
+
             /* fill header with the http header */
             memset(header, 0, sizeof (header));
-            if (util_read_header(con->sock, header, sizeof (header)) == 0) {
+            if (util_read_header(con->sock, header, sizeof (header), 
+                                 READ_ENTIRE_HEADER) == 0) {
                 /* either we didn't get a complete header, or we timed out */
                 connection_close(con);
                 continue;
@@ -1027,7 +1143,7 @@
                 }
 
                 if (parser->req_type == httpp_req_source) {
-                    _handle_source_request(con, parser, uri);
+                    _handle_source_request(con, parser, uri, ICECAST_SOURCE_AUTH);
                 }
                 else if (parser->req_type == httpp_req_stats) {
                     _handle_stats_request(con, parser, uri);
@@ -1044,22 +1160,6 @@
                 free(uri);
                 continue;
             } 
-            else if(httpp_parse_icy(parser, header, strlen(header))) {
-                /* TODO: Map incoming icy connections to /icy_0, etc. */
-                char mount[20];
-                unsigned i = 0;
-
-                strcpy(mount, "/");
-
-                avl_tree_rlock(global.source_tree);
-                while (source_find_mount (mount) != NULL) {
-                    snprintf (mount, sizeof (mount), "/icy_%u", i++);
-                }
-                avl_tree_unlock(global.source_tree);
-
-                _handle_source_request(con, parser, mount);
-                continue;
-            }
             else {
                 ERROR0("HTTP request parsing failed");
                 connection_close(con);

Modified: icecast/trunk/icecast/src/format.c
===================================================================
--- icecast/trunk/icecast/src/format.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/format.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -40,6 +40,7 @@
 #include "format_mp3.h"
 
 #include "logging.h"
+#include "stats.h"
 #define CATMODULE "format"
 
 #ifdef WIN32
@@ -57,6 +58,8 @@
         return FORMAT_TYPE_MP3; 
     else if(strcmp(contenttype, "audio/x-mpeg") == 0)
         return FORMAT_TYPE_MP3; /* Relay-compatibility for some servers */
+    else if(strcmp(contenttype, "video/nsv") == 0)
+        return FORMAT_TYPE_NSV; 
     else
         return FORMAT_ERROR;
 }
@@ -70,6 +73,9 @@
         case FORMAT_TYPE_MP3:
             return "audio/mpeg";
             break;
+        case FORMAT_TYPE_NSV:
+            return "video/nsv";
+            break;
         default:
             return NULL;
     }
@@ -86,9 +92,16 @@
     case FORMAT_TYPE_MP3:
         ret = format_mp3_get_plugin (source);
         break;
+    case FORMAT_TYPE_NSV:
+        ret = format_mp3_get_plugin (source);
+        source->format->format_description = "NSV Video";
+        source->format->type = FORMAT_TYPE_NSV;
+        break;
     default:
         break;
     }
+    stats_event (source->mount, "content-type", 
+                 format_get_mimetype(source->format->type));
 
     return ret;
 }

Modified: icecast/trunk/icecast/src/format.h
===================================================================
--- icecast/trunk/icecast/src/format.h	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/format.h	2004-11-11 15:47:33 UTC (rev 8191)
@@ -28,6 +28,7 @@
 {
     FORMAT_TYPE_VORBIS,
     FORMAT_TYPE_MP3,
+    FORMAT_TYPE_NSV,
     FORMAT_ERROR /* No format, source not processable */
 } format_type_t;
 

Modified: icecast/trunk/icecast/src/slave.c
===================================================================
--- icecast/trunk/icecast/src/slave.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/slave.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -169,7 +169,7 @@
                 "\r\n",
                 relay->mount, relay->mp3metadata?"Icy-MetaData: 1\r\n":"");
         memset (header, 0, sizeof(header));
-        if (util_read_header (con->sock, header, 4096) == 0)
+        if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0)
         {
             WARN0("Header read failed");
             break;

Modified: icecast/trunk/icecast/src/source.c
===================================================================
--- icecast/trunk/icecast/src/source.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/source.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -239,6 +239,7 @@
     source->queue_size_limit = 0;
     source->listeners = 0;
     source->no_mount = 0;
+    source->shoutcast_compat = 0;
     source->max_listeners = -1;
     source->yp_public = 0;
     util_dict_free (source->audio_info);
@@ -484,15 +485,20 @@
     char *listenurl, *str;
     int listen_url_size;
     char *s;
+    char *extra = "";
 
+    if (source->format->type == FORMAT_TYPE_NSV) {
+        extra = "?file=stream.nsv";
+    }
+
     /* 6 for max size of port */
     listen_url_size = strlen("http://") + strlen(config->hostname) +
-        strlen(":") + 6 + strlen(source->mount) + 1;
+        strlen(":") + 6 + strlen(source->mount) + strlen(extra) + 1;
 
     listenurl = malloc (listen_url_size);
     memset (listenurl, '\000', listen_url_size);
-    snprintf (listenurl, listen_url_size, "http://%s:%d%s",
-            config->hostname, config->port, source->mount);
+    snprintf (listenurl, listen_url_size, "http://%s:%d%s%s",
+            config->hostname, config->port, source->mount, extra);
     config_release_config();
 
     do

Modified: icecast/trunk/icecast/src/source.h
===================================================================
--- icecast/trunk/icecast/src/source.h	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/source.h	2004-11-11 15:47:33 UTC (rev 8191)
@@ -54,6 +54,7 @@
     struct auth_tag *authenticator;
     int fallback_override;
     int no_mount;
+    int shoutcast_compat;
 
     /* per source burst handling for connecting clients */
     unsigned int burst_size;    /* trigger level for burst on connect */

Modified: icecast/trunk/icecast/src/util.c
===================================================================
--- icecast/trunk/icecast/src/util.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/util.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -84,7 +84,7 @@
 #endif
 }
 
-int util_read_header(int sock, char *buff, unsigned long len)
+int util_read_header(int sock, char *buff, unsigned long len, int entire)
 {
     int read_bytes, ret;
     unsigned long pos;
@@ -107,10 +107,19 @@
 
             if ((read_bytes = recv(sock, &c, 1, 0))) {
                 if (c != '\r') buff[pos++] = c;
-                if ((pos > 1) && (buff[pos - 1] == '\n' && buff[pos - 2] == '\n')) {
-                    ret = 1;
-                    break;
+                if (entire) {
+                    if ((pos > 1) && (buff[pos - 1] == '\n' && 
+                                      buff[pos - 2] == '\n')) {
+                        ret = 1;
+                        break;
+                    }
                 }
+                else {
+                    if ((pos > 1) && (buff[pos - 1] == '\n')) {
+                        ret = 1;
+                        break;
+                    }
+                }
             }
         } else {
             break;

Modified: icecast/trunk/icecast/src/util.h
===================================================================
--- icecast/trunk/icecast/src/util.h	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/util.h	2004-11-11 15:47:33 UTC (rev 8191)
@@ -16,8 +16,11 @@
 #define XSLT_CONTENT 1
 #define HTML_CONTENT 2
 
+#define READ_ENTIRE_HEADER 1
+#define READ_LINE 0
+
 int util_timed_wait_for_fd(int fd, int timeout);
-int util_read_header(int sock, char *buff, unsigned long len);
+int util_read_header(int sock, char *buff, unsigned long len, int entire);
 int util_check_valid_extension(char *uri);
 char *util_get_extension(char *path);
 char *util_get_path_from_uri(char *uri);

Modified: icecast/trunk/icecast/src/yp.c
===================================================================
--- icecast/trunk/icecast/src/yp.c	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/src/yp.c	2004-11-11 15:47:33 UTC (rev 8191)
@@ -479,12 +479,22 @@
         if (url == NULL)
             break;
         config = config_get_config();
-        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, source->mount);
+        if (source->format->type == FORMAT_TYPE_NSV) {
+            ret = snprintf (url, len, "http://%s:%d%s?stream.nsv", config->hostname, config->port, source->mount);
+        }
+        else {
+            ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, source->mount);
+        }
         if (ret >= (signed)len)
         {
             s = realloc (url, ++ret);
             if (s) url = s;
-            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, source->mount);
+            if (source->format->type == FORMAT_TYPE_NSV) {
+                snprintf (url, ret, "http://%s:%d%s?file=stream.nsv", config->hostname, config->port, source->mount);
+            }
+            else {
+                snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, source->mount);
+            }
         }
         config_release_config();
         yp->listen_url = util_url_escape (url);

Modified: icecast/trunk/icecast/web/status.xsl
===================================================================
--- icecast/trunk/icecast/web/status.xsl	2004-11-10 20:24:43 UTC (rev 8190)
+++ icecast/trunk/icecast/web/status.xsl	2004-11-11 15:47:33 UTC (rev 8191)
@@ -54,7 +54,14 @@
 <a href="auth.xsl">Click to Listen</a>
 </xsl:when>
 <xsl:otherwise>
-<a href="{@mount}.m3u">Click to Listen</a>
+	<xsl:choose>
+	<xsl:when test="content-type='video/nsv'">
+	<a href="{@mount}%3Ffile%3Dstream.nsv.m3u">Click to Listen</a>
+	</xsl:when>
+	<xsl:otherwise>
+	<a href="{@mount}.m3u">Click to Listen</a>
+	</xsl:otherwise>
+	</xsl:choose>
 </xsl:otherwise>
 </xsl:choose>
 </td></tr>



More information about the commits mailing list