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;