diff --git a/conf/icecast.xml.in b/conf/icecast.xml.in index 0314f71e..af89ea8c 100644 --- a/conf/icecast.xml.in +++ b/conf/icecast.xml.in @@ -7,7 +7,11 @@ 30 15 10 - + + 1 + 65535 @@ -34,6 +38,9 @@ --> + localhost @@ -103,7 +110,8 @@ 1 1 - --> diff --git a/doc/icecast2_admin.html b/doc/icecast2_admin.html index 1e610e5e..0cb0c223 100755 --- a/doc/icecast2_admin.html +++ b/doc/icecast2_admin.html @@ -114,6 +114,8 @@ http://192.168.1.10:8000/admin/listmounts http://192.168.1.10:8000/admin/stats.xsl

From this URL all of the other admin functions can be exercised.

+

Modification of existing XSLT transforms in /admin is allowed, but new files cannot be created here. Creation of new XSLT transforms as well as modification of existing transforms is allowed in /web. These work using the document returned by /admin/stats.xml. To see the XML document that is applied to each admin XSLT, just change the .xsl to .xml in your request (i.e. /admin/listclients.xml). You can then code your XSLT transform accordingly. +
diff --git a/doc/icecast2_config_file.html b/doc/icecast2_config_file.html index 8d2c8aff..5899d77a 100755 --- a/doc/icecast2_config_file.html +++ b/doc/icecast2_config_file.html @@ -24,14 +24,16 @@ This section will describe each section of the config file and is grouped into t

Limits

     <limits>
-        <clients>100<clients>
-        <sources>2<sources>
-        <threadpool>5<threadpool>
-        <queue-size>102400<queue-size>
-        <client-timeout>30<client-timeout>
-        <header-timeout>15<header-timeout>
-        <source-timeout>10<source-timeout>
-    <limits>
+        <clients>100</clients>
+        <sources>2</sources>
+        <threadpool>5</threadpool>
+        <queue-size>102400</queue-size>
+        <client-timeout>30</client-timeout>
+        <header-timeout>15</header-timeout>
+        <source-timeout>10</source-timeout>
+        <burst-on-connect>1</burst-on-connect>
+        <burst-size>65536</burst-size>
+    </limits>
 

This section contains server level settings that, in general, do not need to be changed. Only modify this section if you are know what you are doing.

@@ -63,6 +65,17 @@ The maximum time (in seconds) to wait for a request to come in once the client h
If a connected source does not send any data within this timeout period (in seconds), then the source connection will be removed from the server.
+

burst-on-connect

+
+With this enabled, a connecting client will be sent a burst of audio data from the stream. This will have the effect of reducing the startup time for the stream from the perspective of the listener. This is due to the fact that most media players have local buffers that must be filled before the stream begins to play. This may introduce a small latency in the stream (difference in time between when the source plays a clip and the listener hears a clip). If this latency is important to you, then you can disable this feature. The latency is bitrate-dependent, but as an example, for a 128kbps stream, the latency between the source and the player is ~ 1.5 secs WITHOUT burst on connect, and WITH burst on connect the latency is 3 secs. +
+

burst-size

+
+The burst size is the amount of data (in bytes) to burst to a client at connection time. Like +burst-on-connect, this is to quickly fill the pre-buffer used by media players. The default +is 64kbytes which is a typical size used by most clients so changing it is not usually required. +This setting applies to all mountpoints. +



@@ -70,11 +83,11 @@ If a connected source does not send any data within this timeout period (in seco

Authentication

     <authentication>
-        <source-password>hackme<source-password>
-        <relay-password>hackme<relay-password>
-        <admin-user>admin<admin-user>
-        <admin-password>hackme<admin-password>
-    <authentication>
+        <source-password>hackme</source-password>
+        <relay-password>hackme</relay-password>
+        <admin-user>admin</admin-user>
+        <admin-password>hackme</admin-password>
+    </authentication>
 

This section contains all the users and passwords used for administration purposes or to connect sources and relays.

@@ -98,9 +111,9 @@ The username/password used for all administration functions. This includes retr

YP Directory Settings

     <directory>
-        <yp-url-timeout>15<yp-url-timeout>
-        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi<yp-url>
-    <directory>
+        <yp-url-timeout>15</yp-url-timeout>
+        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi</yp-url>
+    </directory>
 

This section contains all the settings for listing a stream on any of the Icecast2 YP Directory servers. Multiple occurances of this section can be specified in order to be listed on multiple directory servers.

@@ -121,16 +134,16 @@ The URL which icecast2 uses to communicate with the Directory server. The value <hostname>localhost<hostname> <-- You can use these two if you only want a single listener --> - <-- <port>8000<port> --> - <-- <bind-address>127.0.0.1<bind-address> --> + <-- <port>8000</port> --> + <-- <bind-address>127.0.0.1</bind-address> --> - <-- You may have multiple <listener> elements --> - <listen-socket> - <port>8000<port> - <bind-address>127.0.0.1<bind-address> + <-- You may have multiple <listen-socket> elements --> <listen-socket> + <port>8000</port> + <bind-address>127.0.0.1</bind-address> + </listen-socket> - <fileserve>1<fileserve> + <fileserve>1</fileserve>

This section contains miscellaneous server settings. Note that multiple listen-socket sections may be configured in order to have icecast2 listen on multiple network interfaces. If a bind-address is not specified for a particular listen-socket, then the hostname parameter will be used to specify the address that will be bound.

@@ -152,18 +165,18 @@ This flag turns on the icecast2 fileserver from which static files can be served

Relay Settings

-    <master-server>127.0.0.1<master-server>
-    <master-server-port>8001<master-server-port>
-    <master-update-interval>120<master-update-interval>
-    <master-password>hackme<master-password>
+    <master-server>127.0.0.1</master-server>
+    <master-server-port>8001</master-server-port>
+    <master-update-interval>120</master-update-interval>
+    <master-password>hackme</master-password>
 
     <relay>
-        <server>127.0.0.1<server>
-        <port>8001<port>
-        <mount>example.ogg<mount>
-        <local-mount>different.ogg<local-mount>
-        <relay-shoutcast-metadata>0<relay-shoutcast-metadata>
-    <relay>
+        <server>127.0.0.1</server>
+        <port>8001</port>
+        <mount>example.ogg</mount>
+        <local-mount>different.ogg</local-mount>
+        <relay-shoutcast-metadata>0</relay-shoutcast-metadata>
+    </relay>
 

This section contains the server's relay settings. There are two types of relays: a "Master server relay" or a "Specific Mountpoint relay." A Master server relay is only supported between icecast2 servers and is used to relays all mountpoints on a remote icecast2 server. @@ -226,11 +239,15 @@ The following diagram shows the basics of doing a Specific Mountpoint relay. No Specific Mountpoint Relays can be configured to relay from an Icecast 2 server, as well as Icecast 1.x and Shoutcast. A server is configured as a Specific Mountpoint Server relay by specifying a <relay> XML chunk in the config file for each mountpoint to be relayed. The server that is being relayed does not need any special configuration. - <server>127.0.0.1<server> - <port>8001<port> - <mount>example.ogg<mount> - <local-mount>different.ogg<local-mount> - <relay-shoutcast-metadata>0<relay-shoutcast-metadata> +

+    <relay>
+        <server>127.0.0.1</server>
+        <port>8001</port>
+        <mount>example.ogg</mount>
+        <local-mount>different.ogg</local-mount>
+        <relay-shoutcast-metadata>0</relay-shoutcast-metadata>
+    </relay>
+

server

@@ -260,17 +277,20 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al

Mount Specific Settings

     <mount>
-        <mount-name>/example-complex.ogg<mount-name>
-        <username>othersource<username>
-        <password>hackmemore<password>
-        <max-listeners>1<max-listeners>
-        <dump-file>/tmp/dump-example1.ogg<dump-file>
-        <fallback-mount>example2.ogg<fallback-mount>
+        <mount-name>/example-complex.ogg</mount-name>
+        <username>othersource</username>
+        <password>hackmemore</password>
+        <max-listeners>1</max-listeners>
+        <dump-file>/tmp/dump-example1.ogg</dump-file>
+        <fallback-mount>example2.ogg</fallback-mount>
+        <fallback-override>1</fallback-override>
+        <burst-size>65536</burst-size>
         <authentication type="htpasswd">
                 <option name="filename" value="myauth"/>
+                <option name="allow_duplicate_users" value="0"/>
         </authentication>
 
-    <mount>
+    </mount>
 

This section contains settings which apply only to a specific mountpoint. Within this section you can reserve a specific mountpoint and set a source username/password for that mountpoint (not yet implemented) as well as specify individual settings which will apply only to the supplied mountpoint.

@@ -298,9 +318,19 @@ An optional value which will set the filename which will be a dump of the stream
This specifies a mountpoint that is used in the case of a source disconnect. If listeners are connected to the mount specified by the <mount-name> config value, then if the source is disconnected; all currently connected clients will be moved to the fallback-mount.
+

fallback-override

+
+When enabled, this allows a connecting source client or relay on this mountpoint to move +listening clients back from the fallback mount. +
+

burst-size

+
+This optional setting allows for providing a burst size which overrides the default burst size +as defined in limits. The value is in bytes. +

authentication

-This specifies that the named mount point will require listener authentication. Currently, we only support a file-based authentication scheme (type=htpasswd). Users and encrypted password are placed in this file (separated by a :) and all requests for this mountpoint will require that a user and password be supplied for authentication purposes. These values are passed in via normal HTTP Basic Authentication means (i.e. http://user:password@stream:port/mountpoint.ogg). Users and Passwords are maintained via the web admin interface. A mountpoint configured with an authenticator will display a red key next to the mount point name on the admin screens. +This specifies that the named mount point will require listener authentication. Currently, we only support a file-based authentication scheme (type=htpasswd). Users and encrypted password are placed in this file (separated by a :) and all requests for this mountpoint will require that a user and password be supplied for authentication purposes. These values are passed in via normal HTTP Basic Authentication means (i.e. http://user:password@stream:port/mountpoint.ogg). Users and Passwords are maintained via the web admin interface. A mountpoint configured with an authenticator will display a red key next to the mount point name on the admin screens. You can read more about listener authentication here.


@@ -309,13 +339,13 @@ This specifies that the named mount point will require listener authentication.

Path Settings

     <paths>
-        <basedir>./<basedir>
-        <logdir>./logs<logdir>
-        <pidfile>./icecast.pid<pidfile>
-        <webroot>./web<webroot>
-        <adminroot>./admin<adminroot>
+        <basedir>./</basedir>
+        <logdir>./logs</logdir>
+        <pidfile>./icecast.pid</pidfile>
+        <webroot>./web</webroot>
+        <adminroot>./admin</adminroot>
         <alias source="/foo" dest="/bar"/>
-    <paths>
+    </paths>
 

This section contains paths which are used for various things within icecast. All paths should not end in a '/'.

diff --git a/src/cfgfile.c b/src/cfgfile.c index 9ff714f9..6934c75c 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -328,7 +328,6 @@ static void _set_defaults(ice_config_t *configuration) configuration->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT; configuration->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT; configuration->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT; - configuration->burst_size_limit = CONFIG_DEFAULT_BURST_SIZE; configuration->threadpool_size = CONFIG_DEFAULT_THREADPOOL_SIZE; configuration->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT; configuration->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT; @@ -364,6 +363,8 @@ static void _set_defaults(ice_config_t *configuration) configuration->slaves_count = 0; configuration->relay_username = NULL; configuration->relay_password = NULL; + /* default to a typical prebuffer size used by clients */ + configuration->burst_size = CONFIG_DEFAULT_BURST_SIZE; } static void _parse_root(xmlDocPtr doc, xmlNodePtr node, @@ -478,10 +479,6 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); configuration->source_limit = atoi(tmp); if (tmp) xmlFree(tmp); - } else if (strcmp(node->name, "burst-size") == 0) { - tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - configuration->burst_size_limit = atoi(tmp); - if (tmp) xmlFree(tmp); } else if (strcmp(node->name, "queue-size") == 0) { tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); configuration->queue_size_limit = atoi(tmp); @@ -502,6 +499,15 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); configuration->source_timeout = atoi(tmp); if (tmp) xmlFree(tmp); + } else if (strcmp(node->name, "burst-on-connect") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + if (atoi(tmp) == 0) + configuration->burst_size = 0; + if (tmp) xmlFree(tmp); + } else if (strcmp(node->name, "burst-size") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + configuration->burst_size = atoi(tmp); + if (tmp) xmlFree(tmp); } } while ((node = node->next)); } @@ -526,7 +532,9 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, else configuration->mounts = mount; + /* default settings */ mount->max_listeners = -1; + mount->burst_size = -1; mount->next = NULL; do { @@ -622,14 +630,6 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, mount->queue_size_limit = atoi (tmp); if(tmp) xmlFree(tmp); } - else if (strcmp(node->name, "burst-size") == 0) { - tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - if (tmp) - { - mount->burst_size = atoi (tmp); - xmlFree(tmp); - } - } else if (strcmp(node->name, "source-timeout") == 0) { tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (tmp) @@ -637,6 +637,10 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, mount->source_timeout = atoi (tmp); xmlFree(tmp); } + } else if (strcmp(node->name, "burst-size") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + mount->burst_size = atoi(tmp); + if (tmp) xmlFree(tmp); } } while ((node = node->next)); } diff --git a/src/cfgfile.h b/src/cfgfile.h index dd6585b8..7ad99751 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -57,10 +57,11 @@ typedef struct _mount_proxy { clients from the fallback? */ int no_mount; /* Do we permit direct requests of this mountpoint? (or only indirect, through fallbacks) */ + int burst_size; /* amount to send to a new client if possible, -1 take + * from global setting */ + unsigned int queue_size_limit; int no_yp; /* Do we prevent YP on this mount */ - unsigned queue_size_limit; - unsigned source_timeout; /* source timeout in seconds */ - unsigned burst_size; + unsigned int source_timeout; /* source timeout in seconds */ char *auth_type; /* Authentication type */ config_options_t *auth_options; /* Options for this type */ @@ -92,9 +93,9 @@ typedef struct ice_config_tag int client_limit; int source_limit; - unsigned queue_size_limit; - unsigned burst_size_limit; + unsigned int queue_size_limit; int threadpool_size; + unsigned int burst_size; int client_timeout; int header_timeout; int source_timeout; diff --git a/src/client.c b/src/client.c index 923b63cc..7c9d220c 100644 --- a/src/client.c +++ b/src/client.c @@ -46,6 +46,7 @@ client_t *client_create(connection_t *con, http_parser_t *parser) client->con = con; client->parser = parser; + client->refbuf = NULL; client->pos = 0; return client; @@ -75,6 +76,7 @@ void client_destroy(client_t *client) /* drop ref counts if need be */ if (client->refbuf) refbuf_release (client->refbuf); + /* we need to free client specific format data (if any) */ if (client->free_client_data) client->free_client_data (client); diff --git a/src/connection.c b/src/connection.c index a038a620..d45f68b8 100644 --- a/src/connection.c +++ b/src/connection.c @@ -594,7 +594,6 @@ int connection_check_relay_pass(http_parser_t *parser) return ret; } - int connection_check_source_pass(http_parser_t *parser, char *mount) { ice_config_t *config = config_get_config(); diff --git a/src/slave.c b/src/slave.c index 1aaa04bf..f9d46fe4 100644 --- a/src/slave.c +++ b/src/slave.c @@ -116,6 +116,7 @@ void slave_rescan (void) rescan_relays = 1; } + void slave_initialize(void) { if (slave_running) @@ -374,7 +375,7 @@ update_relay_set (relay_server **current, relay_server *updated) /* update the relay_list with entries from new_relay_list. Any new relays * are added to the list, and any not listed in the provided new_relay_list - * are separated an returned in a separate list + * are separated and returned in a separate list */ static relay_server * update_relays (relay_server **relay_list, relay_server *new_relay_list) @@ -399,9 +400,9 @@ static void relay_check_streams (relay_server *to_start, relay_server *to_free) { if (to_free->running && to_free->source) { - DEBUG1 ("source shutdown request on \"%s\"", to_free->localmount); - to_free->source->running = 0; - thread_join (to_free->thread); + DEBUG1 ("source shutdown request on \"%s\"", to_free->localmount); + to_free->source->running = 0; + thread_join (to_free->thread); } to_free = relay_free (to_free); } diff --git a/src/source.c b/src/source.c index 271b059f..3319911f 100644 --- a/src/source.c +++ b/src/source.c @@ -59,7 +59,8 @@ mutex_t move_clients_mutex; /* avl tree helper */ static int _compare_clients(void *compare_arg, void *a, void *b); -static void _parse_audio_info (source_t *source, const char *str); +static void _parse_audio_info (source_t *source, const char *s); +static void source_shutdown (source_t *source); #ifdef _WIN32 #define source_run_script(x,y) WARN0("on [dis]connect scripts disabled"); #else @@ -226,6 +227,7 @@ void source_clear_source (source_t *source) source_free_client (source, client); } source->pending_clients_tail = &source->pending_clients; + source->first_normal_client = NULL; /* flush out the stream data, we don't want any left over */ while (source->stream_data) @@ -248,7 +250,8 @@ void source_clear_source (source_t *source) auth_clear (source->authenticator); source->burst_point = NULL; - source->first_normal_client = NULL; + source->burst_size = 0; + source->burst_offset = 0; source->queue_size = 0; source->queue_size_limit = 0; source->listeners = 0; @@ -617,7 +620,7 @@ static void get_next_buffer (source_t *source) { source->stream_data = refbuf; source->burst_point = refbuf; - source->burst_size = 0; + source->burst_offset = 0; } if (source->stream_data_tail) source->stream_data_tail->next = refbuf; @@ -626,13 +629,13 @@ static void get_next_buffer (source_t *source) refbuf_addref (refbuf); /* move the starting point for new listeners */ - source->burst_size += refbuf->len; - if (source->burst_size > source->burst_size_limit) + source->burst_offset += refbuf->len; + if (source->burst_offset > source->burst_size) { if (source->burst_point->next) { refbuf_release (source->burst_point); - source->burst_size -= source->burst_point->len; + source->burst_offset -= source->burst_point->len; source->burst_point = source->burst_point->next; } } @@ -731,8 +734,8 @@ static void source_init (source_t *source) sock_set_blocking (source->con->sock, SOCK_NONBLOCK); DEBUG0("Source creation complete"); - source->running = 1; source->last_read = time (NULL); + source->running = 1; thread_mutex_unlock (&source->lock); if (source->on_connect) @@ -763,53 +766,6 @@ static void source_init (source_t *source) } -static void source_shutdown (source_t *source) -{ - INFO1("Source \"%s\" exiting", source->mount); - source->running = 0; - - yp_remove (source->mount); - - if (source->on_disconnect) - source_run_script (source->on_disconnect, source->mount); - - if (source->fallback_mount) - { - source_t *fallback_source; - - avl_tree_rlock(global.source_tree); - fallback_source = source_find_mount (source->fallback_mount); - - if (fallback_source != NULL) - { - /* be careful wrt to deadlocking */ - thread_mutex_unlock (&source->lock); - source_move_clients (source, fallback_source); - thread_mutex_lock (&source->lock); - } - - avl_tree_unlock (global.source_tree); - } - - /* delete this sources stats */ - stats_event_dec (NULL, "sources"); - stats_event (source->mount, "listeners", NULL); - - /* we don't remove the source from the tree here, it may be a relay and - therefore reserved */ - source_clear_source (source); - - thread_mutex_unlock (&source->lock); - - global_lock(); - global.sources--; - global_unlock(); - - /* release our hold on the lock so the main thread can continue cleaning up */ - thread_rwlock_unlock(source->shutdown_rwlock); -} - - /* Check whether this client is currently on this mount, the client may be * on either the active or pending lists. * return 1 if ok to add or 0 to prevent @@ -1102,6 +1058,56 @@ void source_main(source_t *source) } +static void source_shutdown (source_t *source) +{ + INFO1("Source \"%s\" exiting", source->mount); + source->running = 0; + + yp_remove (source->mount); + + if (source->on_disconnect) + source_run_script (source->on_disconnect, source->mount); + + /* we have de-activated the source now, so no more clients will be + * added, now move the listeners we have to the fallback (if any) + */ + if (source->fallback_mount) + { + source_t *fallback_source; + + avl_tree_rlock(global.source_tree); + fallback_source = source_find_mount (source->fallback_mount); + + if (fallback_source != NULL) + { + /* be careful wrt to deadlocking */ + thread_mutex_unlock (&source->lock); + source_move_clients (source, fallback_source); + thread_mutex_lock (&source->lock); + } + + avl_tree_unlock (global.source_tree); + } + + /* delete this sources stats */ + stats_event_dec (NULL, "sources"); + stats_event (source->mount, "listeners", NULL); + + /* we don't remove the source from the tree here, it may be a relay and + therefore reserved */ + source_clear_source (source); + + thread_mutex_unlock (&source->lock); + + global_lock(); + global.sources--; + global_unlock(); + + /* release our hold on the lock so the main thread can continue cleaning up */ + thread_rwlock_unlock(source->shutdown_rwlock); +} + + static int _compare_clients(void *compare_arg, void *a, void *b) { client_t *clienta = (client_t *)a; @@ -1196,8 +1202,8 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo) if (mountinfo->source_timeout) source->timeout = mountinfo->source_timeout; - if (mountinfo->burst_size) - source->burst_size_limit = mountinfo->burst_size; + if (mountinfo->burst_size >= 0) + source->burst_size = (unsigned int)mountinfo->burst_size; if (mountinfo->fallback_when_full) source->fallback_when_full = mountinfo->fallback_when_full; @@ -1220,7 +1226,7 @@ void source_update_settings (ice_config_t *config, source_t *source) /* set global settings first */ source->queue_size_limit = config->queue_size_limit; source->timeout = config->source_timeout; - source->burst_size_limit = config->burst_size_limit; + source->burst_size = config->burst_size; source->dumpfilename = NULL; auth_clear (source->authenticator); @@ -1250,7 +1256,7 @@ void source_update_settings (ice_config_t *config, source_t *source) DEBUG1 ("max listeners to %d", source->max_listeners); DEBUG1 ("queue size to %u", source->queue_size_limit); - DEBUG1 ("burst size to %u", source->burst_size_limit); + DEBUG1 ("burst size to %u", source->burst_size); DEBUG1 ("source timeout to %u", source->timeout); DEBUG1 ("fallback_when_full to %u", source->fallback_when_full); source->recheck_settings = 0; diff --git a/src/source.h b/src/source.h index 12f255b1..9bb20208 100644 --- a/src/source.h +++ b/src/source.h @@ -63,8 +63,8 @@ typedef struct source_tag int no_mount; /* per source burst handling for connecting clients */ - unsigned int burst_size; - unsigned int burst_size_limit; + unsigned int burst_size; /* trigger level for burst on connect */ + unsigned int burst_offset; refbuf_t *burst_point; unsigned int queue_size;