Commit 9c44a7d1 authored by Karl Heyes's avatar Karl Heyes
Browse files

merged singleq branch 7177:7591

svn path=/icecast/trunk/icecast/; revision=7592
parent 490e6466
......@@ -799,11 +799,7 @@ static void command_metadata(client_t *client, source_t *source)
state = source->format->_state;
thread_mutex_lock(&(state->lock));
free(state->metadata);
state->metadata = strdup(value);
state->metadata_age++;
thread_mutex_unlock(&(state->lock));
mp3_set_tag (source->format, "title", value);
DEBUG2("Metadata on mountpoint %s changed to \"%s\"",
source->mount, value);
......
......@@ -339,7 +339,8 @@ static void _set_defaults(ice_config_t *configuration)
configuration->num_yp_directories = 0;
configuration->relay_username = NULL;
configuration->relay_password = NULL;
configuration->burst_on_connect = 1;
/* default to a typical prebuffer size used by clients */
configuration->burst_size = 65536;
}
static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
......@@ -463,7 +464,12 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node,
if (tmp) xmlFree(tmp);
} else if (strcmp(node->name, "burst-on-connect") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
configuration->burst_on_connect = atoi(tmp);
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));
......@@ -489,7 +495,9 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
else
configuration->mounts = mount;
/* default <mount> settings */
mount->max_listeners = -1;
mount->burst_size = -1;
mount->next = NULL;
do {
......@@ -574,6 +582,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));
}
......
......@@ -54,8 +54,10 @@ typedef struct _mount_proxy {
clients from the fallback? */
int no_mount; /* Do we permit direct requests of this mountpoint? (or only
indirect, through fallbacks) */
unsigned queue_size_limit;
unsigned source_timeout; /* source timeout in seconds */
int burst_size; /* amount to send to a new client if possible, -1 take
* from global setting */
unsigned int queue_size_limit;
unsigned int source_timeout; /* source timeout in seconds */
char *auth_type; /* Authentication type */
config_options_t *auth_options; /* Options for this type */
......@@ -84,8 +86,9 @@ typedef struct ice_config_tag
int client_limit;
int source_limit;
unsigned queue_size_limit;
unsigned int queue_size_limit;
int threadpool_size;
unsigned int burst_size;
int client_timeout;
int header_timeout;
int source_timeout;
......@@ -133,7 +136,6 @@ typedef struct ice_config_tag
char *yp_url[MAX_YP_DIRECTORIES];
int yp_url_timeout[MAX_YP_DIRECTORIES];
int num_yp_directories;
int burst_on_connect;
} ice_config_t;
typedef struct {
......
......@@ -42,17 +42,14 @@ client_t *client_create(connection_t *con, http_parser_t *parser)
client->con = con;
client->parser = parser;
client->queue = NULL;
client->refbuf = NULL;
client->pos = 0;
client->burst_sent = 0;
return client;
}
void client_destroy(client_t *client)
{
refbuf_t *refbuf;
if (client == NULL)
return;
/* write log entry if ip is set (some things don't set it, like outgoing
......@@ -64,9 +61,9 @@ void client_destroy(client_t *client)
connection_close(client->con);
httpp_destroy(client->parser);
while ((refbuf = refbuf_queue_remove(&client->queue)))
refbuf_release(refbuf);
/* 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);
......@@ -144,3 +141,14 @@ int client_send_bytes (client_t *client, const void *buf, unsigned len)
return ret;
}
void client_set_queue (client_t *client, refbuf_t *refbuf)
{
refbuf_t *to_release = client->refbuf;
client->refbuf = refbuf;
refbuf_addref (client->refbuf);
client->pos = 0;
if (to_release)
refbuf_release (to_release);
}
......@@ -32,8 +32,9 @@ typedef struct _client_tag
/* http response code for this client */
int respcode;
/* buffer queue */
refbuf_queue_t *queue;
/* where in the queue the client is */
refbuf_t *refbuf;
/* position in first buffer */
unsigned long pos;
......@@ -45,7 +46,6 @@ typedef struct _client_tag
/* function to call to release format specific resources */
void (*free_client_data)(struct _client_tag *client);
int burst_sent;
} client_t;
client_t *client_create(connection_t *con, http_parser_t *parser);
......@@ -56,5 +56,6 @@ void client_send_401(client_t *client);
void client_send_403(client_t *client);
void client_send_400(client_t *client, char *message);
int client_send_bytes (client_t *client, const void *buf, unsigned len);
void client_set_queue (client_t *client, refbuf_t *refbuf);
#endif /* __CLIENT_H__ */
......@@ -465,9 +465,8 @@ int connection_complete_source (source_t *source)
"for icecast 1.x relays. Assuming content is mp3.");
format_type = FORMAT_TYPE_MP3;
}
source->format = format_get_plugin (format_type, source->mount, source->parser);
if (source->format == NULL)
if (format_get_plugin (format_type, source) < 0)
{
global_unlock();
config_release_config();
......@@ -483,6 +482,7 @@ int connection_complete_source (source_t *source)
/* set global settings first */
source->queue_size_limit = config->queue_size_limit;
source->timeout = config->source_timeout;
source->burst_size = config->burst_size;
/* for relays, we don't yet have a client, however we do require one
* to retrieve the stream from. This is created here, quite late,
......@@ -932,8 +932,7 @@ static void _handle_get_request(connection_t *con,
global.clients++;
global_unlock();
client->format_data = source->format->create_client_data(
source->format, source, client);
source->format->create_client_data (source, client);
source->format->client_send_headers(source->format, source, client);
......
......@@ -75,32 +75,22 @@ char *format_get_mimetype(format_type_t type)
}
}
format_plugin_t *format_get_plugin(format_type_t type, char *mount,
http_parser_t *parser)
int format_get_plugin(format_type_t type, source_t *source)
{
format_plugin_t *plugin;
int ret = -1;
switch (type) {
case FORMAT_TYPE_VORBIS:
plugin = format_vorbis_get_plugin();
if (plugin) plugin->mount = mount;
ret = format_vorbis_get_plugin (source);
break;
case FORMAT_TYPE_MP3:
plugin = format_mp3_get_plugin(parser);
if (plugin) plugin->mount = mount;
ret = format_mp3_get_plugin (source);
break;
default:
plugin = NULL;
break;
}
return plugin;
}
int format_generic_write_buf_to_client(format_plugin_t *format,
client_t *client, unsigned char *buf, int len)
{
return client_send_bytes (client, buf, len);
return ret;
}
void format_send_general_headers(format_plugin_t *format,
......
......@@ -45,13 +45,10 @@ typedef struct _format_plugin_tag
*/
int has_predata;
int (*get_buffer)(struct _format_plugin_tag *self, char *data, unsigned long
len, refbuf_t **buffer);
refbuf_queue_t *(*get_predata)(struct _format_plugin_tag *self);
int (*write_buf_to_client)(struct _format_plugin_tag *format,
client_t *client, unsigned char *buf, int len);
void *(*create_client_data)(struct _format_plugin_tag *format,
struct source_tag *source, client_t *client);
refbuf_t *(*get_buffer)(struct source_tag *);
int (*write_buf_to_client)(struct _format_plugin_tag *format, client_t *client);
void (*write_buf_to_file)(struct source_tag *source, refbuf_t *refbuf);
int (*create_client_data)(struct source_tag *source, client_t *client);
void (*client_send_headers)(struct _format_plugin_tag *format,
struct source_tag *source, client_t *client);
void (*free_plugin)(struct _format_plugin_tag *self);
......@@ -62,11 +59,8 @@ typedef struct _format_plugin_tag
format_type_t format_get_type(char *contenttype);
char *format_get_mimetype(format_type_t type);
format_plugin_t *format_get_plugin(format_type_t type, char *mount,
http_parser_t *parser);
int format_get_plugin(format_type_t type, struct source_tag *source);
int format_generic_write_buf_to_client(format_plugin_t *format,
client_t *client, unsigned char *buf, int len);
void format_send_general_headers(format_plugin_t *format,
struct source_tag *source, client_t *client);
......
This diff is collapsed.
......@@ -19,18 +19,23 @@
#define __FORMAT_MP3_H__
typedef struct {
char *metadata;
int metadata_age;
mutex_t lock;
/* These are for inline metadata */
int inline_metadata_interval;
int offset;
int metadata_length;
char *metadata_buffer;
int metadata_offset;
unsigned interval;
char *url_artist;
char *url_title;
int update_metadata;
refbuf_t *metadata;
mutex_t url_lock;
unsigned build_metadata_len;
unsigned build_metadata_offset;
char build_metadata[4081];
} mp3_state;
format_plugin_t *format_mp3_get_plugin(http_parser_t *parser);
int format_mp3_get_plugin(struct source_tag *src);
void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value);
#endif /* __FORMAT_MP3_H__ */
......@@ -49,20 +49,31 @@ typedef struct _vstate_tag
ogg_page og;
unsigned long serialno;
int header;
refbuf_t *headbuf[MAX_HEADER_PAGES];
refbuf_t *file_headers;
refbuf_t *header_pages;
refbuf_t *header_pages_tail;
int packets;
} vstate_t;
struct client_vorbis
{
refbuf_t *headers;
refbuf_t *header_page;
unsigned int pos;
int processing_headers;
};
static void format_vorbis_free_plugin(format_plugin_t *self);
static int format_vorbis_get_buffer(format_plugin_t *self, char *data,
unsigned long len, refbuf_t **buffer);
static refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self);
static void *format_vorbis_create_client_data(format_plugin_t *self,
source_t *source, client_t *client);
static refbuf_t *format_vorbis_get_buffer (source_t *source);
static int format_vorbis_create_client_data (source_t *source, client_t *client);
static void format_vorbis_send_headers(format_plugin_t *self,
source_t *source, client_t *client);
static int write_buf_to_client (format_plugin_t *self, client_t *client);
static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
format_plugin_t *format_vorbis_get_plugin(void)
int format_vorbis_get_plugin(source_t *source)
{
format_plugin_t *plugin;
vstate_t *state;
......@@ -70,10 +81,9 @@ format_plugin_t *format_vorbis_get_plugin(void)
plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
plugin->type = FORMAT_TYPE_VORBIS;
plugin->has_predata = 1;
plugin->write_buf_to_file = write_ogg_to_file;
plugin->get_buffer = format_vorbis_get_buffer;
plugin->get_predata = format_vorbis_get_predata;
plugin->write_buf_to_client = format_generic_write_buf_to_client;
plugin->write_buf_to_client = write_buf_to_client;
plugin->create_client_data = format_vorbis_create_client_data;
plugin->client_send_headers = format_vorbis_send_headers;
plugin->free_plugin = format_vorbis_free_plugin;
......@@ -83,52 +93,67 @@ format_plugin_t *format_vorbis_get_plugin(void)
ogg_sync_init(&state->oy);
plugin->_state = (void *)state;
source->format = plugin;
return plugin;
return 0;
}
void format_vorbis_free_plugin(format_plugin_t *self)
{
int i;
vstate_t *state = (vstate_t *)self->_state;
refbuf_t *header = state->header_pages;
/* free memory associated with this plugin instance */
/* free state memory */
while (header)
{
refbuf_t *to_release = header;
header = header->next;
refbuf_release (to_release);
}
ogg_sync_clear(&state->oy);
ogg_stream_clear(&state->os);
vorbis_comment_clear(&state->vc);
vorbis_info_clear(&state->vi);
for (i = 0; i < MAX_HEADER_PAGES; i++) {
if (state->headbuf[i]) {
refbuf_release(state->headbuf[i]);
state->headbuf[i] = NULL;
}
}
free(state);
/* free the plugin instance */
free(self);
}
int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer)
static refbuf_t *format_vorbis_get_buffer (source_t *source)
{
char *buf;
int i, result;
int result;
ogg_packet op;
char *tag;
refbuf_t *refbuf, *source_refbuf;
refbuf_t *refbuf, *header;
char *data;
format_plugin_t *self = source->format;
int bytes;
vstate_t *state = (vstate_t *)self->_state;
source_t *source;
if (data) {
/* write the data to the buffer */
buf = ogg_sync_buffer(&state->oy, len);
memcpy(buf, data, len);
ogg_sync_wrote(&state->oy, len);
data = ogg_sync_buffer (&state->oy, 4096);
bytes = sock_read_bytes (source->con->sock, data, 4096);
if (bytes < 0)
{
if (sock_recoverable (sock_error()))
return NULL;
WARN0 ("source connection has died");
ogg_sync_wrote (&state->oy, 0);
source->running = 0;
return NULL;
}
if (bytes == 0)
{
INFO1 ("End of Stream %s", source->mount);
ogg_sync_wrote (&state->oy, 0);
source->running = 0;
return NULL;
}
ogg_sync_wrote (&state->oy, bytes);
refbuf = NULL;
if (ogg_sync_pageout(&state->oy, &state->og) == 1) {
......@@ -137,21 +162,25 @@ int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long le
memcpy(&refbuf->data[state->og.header_len], state->og.body, state->og.body_len);
if (state->serialno != ogg_page_serialno(&state->og)) {
DEBUG0("new stream");
/* this is a new logical bitstream */
state->header = 0;
state->packets = 0;
/* release old headers, stream state, vorbis data */
for (i = 0; i < MAX_HEADER_PAGES; i++) {
if (state->headbuf[i]) {
refbuf_release(state->headbuf[i]);
state->headbuf[i] = NULL;
}
}
/* Clear old stuff. Rarely but occasionally needed. */
header = state->header_pages;
while (header)
{
refbuf_t *to_release = header;
DEBUG0 ("clearing out header page");
header = header->next;
refbuf_release (to_release);
}
ogg_stream_clear(&state->os);
vorbis_comment_clear(&state->vc);
vorbis_info_clear(&state->vi);
state->header_pages = NULL;
state->header_pages_tail = NULL;
state->serialno = ogg_page_serialno(&state->og);
ogg_stream_init(&state->os, state->serialno);
......@@ -164,52 +193,40 @@ int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long le
* extras pages beyond the header. We need to collect the pages
* here anyway, but they may have to be discarded later.
*/
DEBUG1 ("header %d", state->header);
if (ogg_page_granulepos(&state->og) <= 0) {
state->header++;
} else {
/* we're done caching headers */
state->header = -1;
DEBUG0 ("doing stats");
/* put known comments in the stats */
tag = vorbis_comment_query(&state->vc, "TITLE", 0);
if (tag) stats_event(self->mount, "title", tag);
else stats_event(self->mount, "title", "unknown");
if (tag) stats_event(source->mount, "title", tag);
else stats_event(source->mount, "title", "unknown");
tag = vorbis_comment_query(&state->vc, "ARTIST", 0);
if (tag) stats_event(self->mount, "artist", tag);
else stats_event(self->mount, "artist", "unknown");
if (tag) stats_event(source->mount, "artist", tag);
else stats_event(source->mount, "artist", "unknown");
/* don't need these now */
ogg_stream_clear(&state->os);
vorbis_comment_clear(&state->vc);
vorbis_info_clear(&state->vi);
/* Drain the source queue on metadata update otherwise you
could have a mismatch between what is on the source queue
and what is in the state->headbuf */
avl_tree_rlock(global.source_tree);
source = source_find_mount_raw(self->mount);
avl_tree_unlock(global.source_tree);
thread_mutex_lock(&source->queue_mutex);
while ((source_refbuf = refbuf_queue_remove(&source->queue))) {
refbuf_release(source_refbuf);
}
thread_mutex_unlock(&source->queue_mutex);
yp_touch (self->mount);
yp_touch (source->mount);
}
}
/* cache header pages */
if (state->header > 0 && state->packets < 3) {
if(state->header > MAX_HEADER_PAGES) {
refbuf_release(refbuf);
ERROR1("Bad vorbis input: header is more than %d pages long", MAX_HEADER_PAGES);
/* build a list of headers pages for attaching */
if (state->header_pages_tail)
state->header_pages_tail->next = refbuf;
state->header_pages_tail = refbuf;
return -1;
}
refbuf_addref(refbuf);
state->headbuf[state->header - 1] = refbuf;
if (state->header_pages == NULL)
state->header_pages = refbuf;
if (state->packets >= 0 && state->packets < 3) {
ogg_stream_pagein(&state->os, &state->og);
......@@ -229,36 +246,40 @@ int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long le
}
}
}
/* we do not place ogg headers on the main queue */
return NULL;
}
/* increase ref counts on each header page going out */
header = state->header_pages;
while (header)
{
refbuf_addref (header);
header = header->next;
}
refbuf->associated = state->header_pages;
}
*buffer = refbuf;
return 0;
return refbuf;
}
refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self)
static void free_ogg_client_data (client_t *client)
{
refbuf_queue_t *queue;
int i;
vstate_t *state = (vstate_t *)self->_state;
queue = NULL;
for (i = 0; i < MAX_HEADER_PAGES; i++) {
if (state->headbuf[i]) {
refbuf_addref(state->headbuf[i]);
refbuf_queue_add(&queue, state->headbuf[i]);
} else {
break;
}
}
return queue;
free (client->format_data);
client->format_data = NULL;
}
static void *format_vorbis_create_client_data(format_plugin_t *self,
source_t *source, client_t *client)
static int format_vorbis_create_client_data (source_t *source, client_t *client)
{
return NULL;
struct client_vorbis *client_data = calloc (1, sizeof (struct client_vorbis));
int ret = -1;
if (client_data)
{
client->format_data = client_data;
client->free_client_data = free_ogg_client_data;
ret = 0;
}
return ret;
}
static void format_vorbis_send_headers(format_plugin_t *self,
......@@ -277,4 +298,122 @@ static void format_vorbis_send_headers(format_plugin_t *self,
format_send_general_headers(self, source, client);
}
static int send_ogg_headers (client_t *client, refbuf_t *headers)
{
struct client_vorbis *client_data = client->format_data;
refbuf_t *refbuf;
int written = 0;
if (client_data->processing_headers == 0)
{
client_data->header_page = headers;
client_data->pos = 0;
client_data->processing_headers = 1;
}
refbuf = client_data->header_page;
while (refbuf)
{
char *data = refbuf->data + client_data->pos;
unsigned int len = refbuf->len - client_data->pos;
int ret;
ret = client_send_bytes (client, data, len);
if (ret > 0)