diff --git a/doc/icecast2_config_file.html b/doc/icecast2_config_file.html
index f98854aa..b90e5924 100644
--- a/doc/icecast2_config_file.html
+++ b/doc/icecast2_config_file.html
@@ -345,6 +345,7 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al
<password>hackmemore</password>
<max-listeners>1</max-listeners>
<dump-file>/tmp/dump-example1.ogg</dump-file>
+ <intro>/intro.ogg</intro>
<fallback-mount>/example2.ogg</fallback-mount>
<fallback-override>1</fallback-override>
<public>1</public>
@@ -396,6 +397,15 @@ An optional value which will set the maximum number of listeners that can be att
An optional value which will set the filename which will be a dump of the stream coming through on this mountpoint.
+intro
+
+
An optional value which will specify the file those contents will be sent to new listeners
+ when they connect but before the normal stream is sent. Make sure the format of the file
+ specified matches the streaming format. The specified file is appended to webroot before
+ being opened.
+
+
+
fallback-mount
This optional value specifies a mountpoint that clients are automatically moved to if the source
diff --git a/src/cfgfile.c b/src/cfgfile.c
index dbc2251b..a8b16fb4 100644
--- a/src/cfgfile.c
+++ b/src/cfgfile.c
@@ -190,6 +190,7 @@ void config_clear(ice_config_t *c)
xmlFree(mount->username);
xmlFree(mount->password);
xmlFree(mount->dumpfile);
+ xmlFree(mount->intro_filename);
xmlFree(mount->fallback_mount);
xmlFree(mount->stream_name);
xmlFree(mount->stream_description);
@@ -558,6 +559,10 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
mount->dumpfile = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
}
+ else if (strcmp(node->name, "intro") == 0) {
+ mount->intro_filename = (char *)xmlNodeListGetString(
+ doc, node->xmlChildrenNode, 1);
+ }
else if (strcmp(node->name, "fallback-mount") == 0) {
mount->fallback_mount = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
diff --git a/src/cfgfile.h b/src/cfgfile.h
index d1077321..b9a41065 100644
--- a/src/cfgfile.h
+++ b/src/cfgfile.h
@@ -46,6 +46,7 @@ typedef struct _mount_proxy {
char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
to not dump. */
+ char *intro_filename; /* Send contents of file to client before the stream */
int max_listeners; /* Max listeners for this mountpoint only. -1 to not
limit here (i.e. only use the global limit) */
char *fallback_mount; /* Fallback mountname */
diff --git a/src/client.c b/src/client.c
index 543cec98..d4e28c74 100644
--- a/src/client.c
+++ b/src/client.c
@@ -30,6 +30,7 @@
#include "cfgfile.h"
#include "connection.h"
#include "refbuf.h"
+#include "format.h"
#include "stats.h"
#include "client.h"
@@ -62,6 +63,7 @@ client_t *client_create(connection_t *con, http_parser_t *parser)
client->parser = parser;
client->refbuf = NULL;
client->pos = 0;
+ client->check_buffer = format_advance_queue;
return client;
}
diff --git a/src/client.h b/src/client.h
index d864806e..16ae5fda 100644
--- a/src/client.h
+++ b/src/client.h
@@ -32,6 +32,9 @@ typedef struct _client_tag
/* http response code for this client */
int respcode;
+ /* is client getting intro data */
+ long intro_offset;
+
/* where in the queue the client is */
refbuf_t *refbuf;
@@ -46,6 +49,10 @@ typedef struct _client_tag
/* function to call to release format specific resources */
void (*free_client_data)(struct _client_tag *client);
+
+ /* function to check if refbuf needs updating */
+ int (*check_buffer)(struct source_tag *source, struct _client_tag *client);
+
} client_t;
client_t *client_create(connection_t *con, http_parser_t *parser);
diff --git a/src/connection.c b/src/connection.c
index 4c25f6a6..6700f28b 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -952,6 +952,8 @@ static void _handle_get_request (client_t *client, char *passed_uri)
sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
sock_set_nodelay(client->con->sock);
+
+ client->check_buffer = format_check_file_buffer;
avl_tree_wlock(source->pending_tree);
avl_insert(source->pending_tree, (void *)client);
diff --git a/src/format.c b/src/format.c
index 79d793b6..1e801555 100644
--- a/src/format.c
+++ b/src/format.c
@@ -81,6 +81,130 @@ int format_get_plugin(format_type_t type, source_t *source)
return ret;
}
+
+/* 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.
+ */
+static void find_client_start (source_t *source, client_t *client)
+{
+ refbuf_t *refbuf = source->burst_point;
+
+ /* we only want to attempt a burst at connection time, not midstream */
+ if (client->intro_offset == -1)
+ refbuf = source->stream_data_tail;
+ else
+ {
+ long size = 0;
+ refbuf = source->burst_point;
+ size = source->burst_size - client->intro_offset;
+ while (size > 0 && refbuf->next)
+ {
+ size -= refbuf->len;
+ refbuf = refbuf->next;
+ }
+ }
+
+ while (refbuf)
+ {
+ if (refbuf->sync_point)
+ {
+ client_set_queue (client, refbuf);
+ client->check_buffer = format_advance_queue;
+ client->intro_offset = -1;
+ break;
+ }
+ refbuf = refbuf->next;
+ }
+}
+
+
+static int get_file_data (FILE *intro, client_t *client)
+{
+ refbuf_t *refbuf = client->refbuf;
+ int bytes;
+
+ if (intro == NULL || fseek (intro, client->intro_offset, SEEK_SET) < 0)
+ return 0;
+ bytes = fread (refbuf->data, 1, 4096, intro);
+ if (bytes == 0)
+ return 0;
+
+ refbuf->len = bytes;
+ return 1;
+}
+
+
+/* call to check the buffer contents for file reading. move the client
+ * to right place in the queue at end of file else repeat file if queue
+ * is not ready yet.
+ */
+int format_check_file_buffer (source_t *source, client_t *client)
+{
+ refbuf_t *refbuf = client->refbuf;
+
+ if (refbuf == NULL)
+ {
+ if (source->intro_file && client->intro_offset == 0)
+ {
+ refbuf = refbuf_new (4096);
+ client->refbuf = refbuf;
+ client->pos = refbuf->len;
+ }
+ else
+ {
+ find_client_start (source, client);
+ return -1;
+ }
+ }
+ if (client->pos == refbuf->len)
+ {
+ if (get_file_data (source->intro_file, client))
+ {
+ client->pos = 0;
+ client->intro_offset += refbuf->len;
+ }
+ else
+ {
+ if (source->stream_data_tail)
+ {
+ /* better find the right place in queue for this client */
+ client->intro_offset = -1;
+ client_set_queue (client, NULL);
+ find_client_start (source, client);
+ }
+ else
+ client->intro_offset = 0; /* replay intro file */
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* 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 existing buffer.
+ */
+int format_advance_queue (source_t *source, client_t *client)
+{
+ refbuf_t *refbuf = client->refbuf;
+
+ if (refbuf == NULL)
+ return -1;
+
+ if (refbuf->next == NULL && client->pos == refbuf->len)
+ return -1;
+
+ /* move to the next buffer if we have finished with the current one */
+ if (refbuf->next && client->pos == refbuf->len)
+ {
+ client_set_queue (client, refbuf->next);
+ refbuf = client->refbuf;
+ }
+ return 0;
+}
+
+
void format_send_general_headers(format_plugin_t *format,
source_t *source, client_t *client)
{
diff --git a/src/format.h b/src/format.h
index c5eb70fb..27b3ad45 100644
--- a/src/format.h
+++ b/src/format.h
@@ -61,14 +61,11 @@ format_type_t format_get_type(char *contenttype);
char *format_get_mimetype(format_type_t type);
int format_get_plugin(format_type_t type, struct source_tag *source);
+int format_advance_queue (struct source_tag *source, client_t *client);
+int format_check_file_buffer (struct source_tag *source, client_t *client);
+
void format_send_general_headers(format_plugin_t *format,
struct source_tag *source, client_t *client);
#endif /* __FORMAT_H__ */
-
-
-
-
-
-
diff --git a/src/format_mp3.c b/src/format_mp3.c
index b145560c..17c4d8ea 100644
--- a/src/format_mp3.c
+++ b/src/format_mp3.c
@@ -326,21 +326,8 @@ static int format_mp3_write_buf_to_client (format_plugin_t *self, client_t *clie
int ret, written = 0;
mp3_client_data *client_mp3 = client->format_data;
refbuf_t *refbuf = client->refbuf;
- char *buf;
- unsigned int len;
-
- if (refbuf->next == NULL && client->pos == refbuf->len)
- return 0;
-
- /* move to the next buffer if we have finished with the current one */
- if (refbuf->next && client->pos == refbuf->len)
- {
- client_set_queue (client, refbuf->next);
- refbuf = client->refbuf;
- }
-
- buf = refbuf->data + client->pos;
- len = refbuf->len - client->pos;
+ char *buf = refbuf->data + client->pos;
+ unsigned int len = refbuf->len - client->pos;
do
{
diff --git a/src/format_ogg.c b/src/format_ogg.c
index 2ae5949c..0531fa61 100644
--- a/src/format_ogg.c
+++ b/src/format_ogg.c
@@ -504,21 +504,11 @@ static int send_ogg_headers (client_t *client, refbuf_t *headers)
static int write_buf_to_client (format_plugin_t *self, client_t *client)
{
refbuf_t *refbuf = client->refbuf;
- char *buf;
- unsigned len;
+ char *buf = refbuf->data + client->pos;
+ unsigned len = refbuf->len - client->pos;
struct ogg_client *client_data = client->format_data;
int ret, written = 0;
- if (refbuf->next == NULL && client->pos == refbuf->len)
- return 0;
-
- if (refbuf->next && client->pos == refbuf->len)
- {
- client_set_queue (client, refbuf->next);
- refbuf = client->refbuf;
- }
- buf = refbuf->data + client->pos;
- len = refbuf->len - client->pos;
do
{
if (client_data->headers != refbuf->associated)
diff --git a/src/source.c b/src/source.c
index 3a4df4e2..1b574c88 100644
--- a/src/source.c
+++ b/src/source.c
@@ -48,6 +48,7 @@
#include "source.h"
#include "format.h"
#include "auth.h"
+#include "os.h"
#undef CATMODULE
#define CATMODULE "source"
@@ -250,6 +251,12 @@ void source_clear_source (source_t *source)
free(source->dumpfilename);
source->dumpfilename = NULL;
+
+ if (source->intro_file)
+ {
+ fclose (source->intro_file);
+ source->intro_file = NULL;
+ }
}
@@ -344,7 +351,8 @@ void source_move_clients (source_t *source, source_t *dest)
avl_delete (source->pending_tree, client, NULL);
/* switch client to different queue */
- client_set_queue (client, dest->stream_data_tail);
+ client_set_queue (client, NULL);
+ client->check_buffer = format_check_file_buffer;
avl_insert (dest->pending_tree, (void *)client);
}
@@ -359,7 +367,8 @@ void source_move_clients (source_t *source, source_t *dest)
avl_delete (source->client_tree, client, NULL);
/* switch client to different queue */
- client_set_queue (client, dest->stream_data_tail);
+ client_set_queue (client, NULL);
+ client->check_buffer = format_check_file_buffer;
avl_insert (dest->pending_tree, (void *)client);
}
source->listeners = 0;
@@ -374,25 +383,6 @@ void source_move_clients (source_t *source, source_t *dest)
}
-/* clients need to be start from somewhere in the queue
- * so we will look for a refbuf which has been previous
- * marked as a sync point */
-static void find_client_start (source_t *source, client_t *client)
-{
- refbuf_t *refbuf = source->burst_point;
-
- while (refbuf)
- {
- if (refbuf->sync_point)
- {
- client_set_queue (client, refbuf);
- break;
- }
- refbuf = refbuf->next;
- }
-}
-
-
/* get some data from the source. The stream data is placed in a refbuf
* and sent back, however NULL is also valid as in the case of a short
* timeout and there's no data pending.
@@ -466,14 +456,6 @@ static void send_to_listener (source_t *source, client_t *client, int deletion_e
int loop = 10; /* max number of iterations in one go */
int total_written = 0;
- /* new users need somewhere to start from */
- if (client->refbuf == NULL)
- {
- find_client_start (source, client);
- if (client->refbuf == NULL)
- return;
- }
-
while (1)
{
/* jump out if client connection has died */
@@ -490,6 +472,9 @@ static void send_to_listener (source_t *source, client_t *client, int deletion_e
loop--;
+ if (client->check_buffer (source, client) < 0)
+ break;
+
bytes = source->format->write_buf_to_client (source->format, client);
if (bytes <= 0)
break; /* can't write any more */
@@ -500,7 +485,7 @@ static void send_to_listener (source_t *source, client_t *client, int deletion_e
/* the refbuf referenced at head (last in queue) may be marked for deletion
* if so, check to see if this client is still referring to it */
- if (deletion_expected && client->refbuf == source->stream_data)
+ if (deletion_expected && client->refbuf && client->refbuf == source->stream_data)
{
DEBUG0("Client has fallen too far behind, removing");
client->con->error = 1;
@@ -1012,6 +997,24 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
else
source->dumpfilename = NULL;
+ if (mountinfo && mountinfo->intro_filename && source->intro_file == NULL)
+ {
+ ice_config_t *config = config_get_config_unlocked ();
+ unsigned int len = strlen (config->webroot_dir) +
+ strlen (mountinfo->intro_filename) + 2;
+ char *path = malloc (len);
+ if (path)
+ {
+ snprintf (path, len, "%s" PATH_SEPARATOR "%s", config->webroot_dir,
+ mountinfo->intro_filename);
+
+ source->intro_file = fopen (path, "rb");
+ if (source->intro_file == NULL)
+ WARN2 ("Cannot open intro file \"%s\": %s", path, strerror(errno));
+ free (path);
+ }
+ }
+
if (mountinfo && mountinfo->queue_size_limit)
source->queue_size_limit = mountinfo->queue_size_limit;
@@ -1040,6 +1043,8 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy
if (source->fallback_mount)
DEBUG1 ("fallback %s", source->fallback_mount);
+ if (mountinfo && mountinfo->intro_filename)
+ DEBUG1 ("intro file is %s", mountinfo->intro_filename);
if (source->dumpfilename)
DEBUG1 ("Dumping stream to %s", source->dumpfilename);
if (source->hidden)
diff --git a/src/source.h b/src/source.h
index b8d50cd5..b1f7ad9f 100644
--- a/src/source.h
+++ b/src/source.h
@@ -46,6 +46,8 @@ typedef struct source_tag
rwlock_t *shutdown_rwlock;
util_dict *audio_info;
+ FILE *intro_file;
+
char *dumpfilename; /* Name of a file to dump incoming stream to */
FILE *dumpfile;