Beispiel #1
0
static int prefile_send (client_t *client)
{
    int loop = 8, bytes, written = 0;
    worker_t *worker = client->worker;

    while (loop)
    {
        refbuf_t *refbuf = client->refbuf;
        fh_node *fh = client->shared_data;
        loop--;
        if (fserve_running == 0 || client->connection.error)
            return -1;
        if (refbuf == NULL || client->pos == refbuf->len)
        {
            if (fh->finfo.fallback)
                return fserve_move_listener (client);

            if (refbuf == NULL || refbuf->next == NULL)
            {
                if (file_in_use (fh->f)) // is there a file to read from
                {
                    if (fh->finfo.limit)
                        client->ops = &throttled_file_content_ops;
                    else
                        client->ops = &file_content_ops;
                    refbuf_release (client->refbuf);
                    client->refbuf = NULL;
                    client->pos = 0;
                    return client->ops->process (client);
                }
                if (client->respcode)
                    return -1;
                return client_send_404 (client, NULL);
            }
            else
            {
                refbuf_t *to_go = client->refbuf;
                refbuf = client->refbuf = to_go->next;
                to_go->next = NULL;
                refbuf_release (to_go);
            }
            client->pos = 0;
        }
        if (refbuf->flags & WRITE_BLOCK_GENERIC)
            bytes = format_generic_write_to_client (client);
        else 
            bytes = client->check_buffer (client);
        if (bytes < 0)
        {
            client->schedule_ms = worker->time_ms + (written ? 150 : 300);
            return 0;
        }
        written += bytes;
        global_add_bitrates (global.out_bitrate, bytes, worker->time_ms);
        if (written > 30000)
            break;
    }
    return 0;
}
Beispiel #2
0
static void format_mp3_free_plugin(format_plugin_t *self)
{
    /* free the plugin instance */
    mp3_state *state = self->_state;

    thread_mutex_destroy (&state->url_lock);
    free (state->url_artist);
    free (state->url_title);
    free (self->charset);
    refbuf_release (state->metadata);
    refbuf_release (state->read_data);
    free(state);
    free(self);
}
Beispiel #3
0
static void _add_stats_to_stats_client (client_t *client, const char *fmt, va_list ap)
{
    event_listener_t *listener = client->shared_data;
    refbuf_t *r = listener->recent_block;

    if (r && (r->flags & STATS_BLOCK_CONNECTION) == 0)
    {
        /* lets see if we can append to an existing block */
        if (r->len < 1390)
        {
            int written = _append_to_bufferv (r, 1400, fmt, ap);
            if (written > 0)
            {
                listener->content_len += written;
                return;
            }
        }
    }
    r = refbuf_new (1400);
    r->len = 0;
    if (_append_to_bufferv (r, 1400, fmt, ap) < 0)
    {
        WARN1 ("stat details are too large \"%s\"", fmt);
        refbuf_release (r);
        return;
    }
    _add_node_to_stats_client (client, r);
    client->schedule_ms = 0;
}
Beispiel #4
0
void format_vorbis_free_plugin(format_plugin_t *self)
{
    int i;
    vstate_t *state = (vstate_t *)self->_state;

    /* free memory associated with this plugin instance */

    /* free state memory */
    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);
}
Beispiel #5
0
void source_client_callback (client_t *client, void *arg)
{
    const char *agent;
    source_t *source = arg;
    refbuf_t *old_data = client->refbuf;

    if (client->con->error)
    {
        global_lock();
        global.sources--;
        global_unlock();
        source_clear_source (source);
        source_free_source (source);
        return;
    }
    client->refbuf = old_data->associated;
    old_data->associated = NULL;
    refbuf_release (old_data);
    stats_event (source->mount, "source_ip", source->client->con->ip);
    agent = httpp_getvar (source->client->parser, "user-agent");
    if (agent)
        stats_event (source->mount, "user_agent", agent);

    thread_create ("Source Thread", source_client_thread,
            source, THREAD_DETACHED);
}
Beispiel #6
0
/* release the memory used for the codec and header pages from the module */
static void free_ogg_codecs (ogg_state_t *ogg_info)
{
    ogg_codec_t *codec;

    if (ogg_info == NULL)
        return;

    format_ogg_free_headers (ogg_info);

    /* now free the codecs */
    codec = ogg_info->codecs;
    ICECAST_LOG_DEBUG("freeing codecs");
    while (codec)
    {
        ogg_codec_t *next = codec->next;
        if (codec->possible_start)
            refbuf_release (codec->possible_start);
        codec->codec_free (ogg_info, codec);
        codec = next;
    }
    ogg_info->codecs = NULL;
    ogg_info->current = NULL;
    ogg_info->bos_completed = 0;
    ogg_info->codec_count = 0;
}
Beispiel #7
0
int format_file_read (client_t *client, format_plugin_t *plugin, FILE *fp)
{
    refbuf_t *refbuf = client->refbuf;
    size_t bytes = -1;
    int unprocessed = 0;

    do
    {
        if (refbuf == NULL)
        {
            if (fp == NULL)
                return -2;
            refbuf = client->refbuf = refbuf_new (4096);
            client->pos = refbuf->len;
            client->intro_offset = 0;
            client->queue_pos = 0;
        }
        if (client->pos < refbuf->len)
            break;
        if (fp == NULL || client->flags & CLIENT_HAS_INTRO_CONTENT)
        {
            if (refbuf->next)
            {
                //DEBUG1 ("next intro block is %d", refbuf->next->len);
                client->refbuf = refbuf->next;
                refbuf->next = NULL;
                refbuf_release (refbuf);
                client->pos = 0;
                return 0;
            }
            //DEBUG0 ("No more intro data ");
            client_set_queue (client, NULL);
            client->flags &= ~CLIENT_HAS_INTRO_CONTENT;
            client->intro_offset = client->connection.sent_bytes;
            refbuf = NULL;
            continue;
        }

        if (fseek (fp, client->intro_offset, SEEK_SET) < 0 ||
                (bytes = fread (refbuf->data, 1, 4096, fp)) <= 0)
        {
            return bytes < 0 ? -2 : -1;
        }
        refbuf->len = bytes;
        client->pos = 0;
        if (plugin->align_buffer)
        {
            /* here the buffer may require truncating to keep the buffers aligned on
             * certain boundaries */
            unprocessed = plugin->align_buffer (client, plugin);
            if (unprocessed < 0 || unprocessed >= bytes)
                unprocessed = 0;
        }
        client->intro_offset += (bytes - unprocessed);
    } while (1);
    return 0;
}
void format_ogg_free_headers (ogg_state_t *ogg_info)
{
    /* release the header pages first */
    DEBUG0 ("releasing header pages");
    refbuf_release (ogg_info->header_pages);
    ogg_info->header_pages = NULL;
    ogg_info->header_pages_tail = NULL;
    ogg_info->bos_end = &ogg_info->header_pages;
}
Beispiel #9
0
static void ebml_free_client_data (client_t *client)
{

    ebml_client_data_t *ebml_client_data = client->format_data;

    refbuf_release (ebml_client_data->header);
    free (client->format_data);
    client->format_data = NULL;
}
Beispiel #10
0
/* This does the actual reading, making sure the read data is packaged in
 * blocks of 1400 bytes (near the common MTU size). This is because many
 * incoming streams come in small packets which could waste a lot of 
 * bandwidth with many listeners due to headers and such like.
 */
static int complete_read (source_t *source)
{
    int bytes;
    format_plugin_t *format = source->format;
    mp3_state *source_mp3 = format->_state;
    char *buf;
    refbuf_t *refbuf;

#define REFBUF_SIZE 1400

    if (source_mp3->read_data == NULL)
    {
        source_mp3->read_data = refbuf_new (REFBUF_SIZE); 
        source_mp3->read_count = 0;
    }
    buf = source_mp3->read_data->data + source_mp3->read_count;

    bytes = client_read_bytes (source->client, buf, REFBUF_SIZE-source_mp3->read_count);
    if (bytes < 0)
    {
        if (source->client->con->error)
        {
            refbuf_release (source_mp3->read_data);
            source_mp3->read_data = NULL;
        }
        return 0;
    }
    source_mp3->read_count += bytes;
    refbuf = source_mp3->read_data;
    refbuf->len = source_mp3->read_count;
    format->read_bytes += bytes;

    if (source_mp3->read_count < REFBUF_SIZE)
    {
        if (source_mp3->read_count == 0)
        {
            refbuf_release (source_mp3->read_data);
            source_mp3->read_data = NULL;
        }
        return 0;
    }
    return 1;
}
Beispiel #11
0
static void ebml_free_plugin (format_plugin_t *plugin, client_t *client)
{

    ebml_source_state_t *ebml_source_state = plugin->_state;

    refbuf_release (ebml_source_state->header);
    ebml_destroy (ebml_source_state->ebml);
    free (ebml_source_state);

}
Beispiel #12
0
int format_file_read (client_t *client, format_plugin_t *plugin, icefile_handle f)
{
    refbuf_t *refbuf = client->refbuf;
    ssize_t bytes = -1;
    int unprocessed = 0;

    do
    {
        if (refbuf == NULL)
        {
            if (file_in_use (f) == 0)
                return -2;
            refbuf = client->refbuf = refbuf_new (8192);
            client->flags |= CLIENT_HAS_INTRO_CONTENT;
            client->pos = refbuf->len;
            client->queue_pos = 0;
        }
        if (client->pos < refbuf->len)
            break;

        if (refbuf->next)
        {
            //DEBUG1 ("next intro block is %d", refbuf->next->len);
            client->refbuf = refbuf->next;
            refbuf->next = NULL;
            refbuf_release (refbuf);
            client->pos = 0;
            return 0;
        }

        if (file_in_use (f) == 0) return -2;

        bytes = pread (f, refbuf->data, 8192, client->intro_offset);
        if (bytes <= 0)
            return bytes < 0 ? -2 : -1;

        refbuf->len = bytes;
        client->pos = 0;
        if (plugin && plugin->align_buffer)
        {
            /* here the buffer may require truncating to keep the buffers aligned on
             * certain boundaries */
            unprocessed = plugin->align_buffer (client, plugin);
            if (unprocessed == bytes)
                return -1;
            if (unprocessed < 0 || unprocessed > bytes)
            {
                unprocessed = 0;
                client->connection.error = 1;
            }
        }
        client->intro_offset += (bytes - unprocessed);
    } while (1);
    return refbuf->len - client->pos;
}
Beispiel #13
0
static void ebml_free_plugin(format_plugin_t *plugin)
{

    ebml_source_state_t *ebml_source_state = plugin->_state;

    refbuf_release(ebml_source_state->header);
    ebml_destroy(ebml_source_state->ebml);
    free(ebml_source_state);
    vorbis_comment_clear(&plugin->vc);
    free(plugin);
}
Beispiel #14
0
void client_set_queue (client_t *client, refbuf_t *refbuf)
{
    refbuf_t *to_release = client->refbuf;

    client->refbuf = refbuf;
    if (refbuf)
        refbuf_addref (client->refbuf);
    client->pos = 0;
    if (to_release)
        refbuf_release (to_release);
}
Beispiel #15
0
static int command_list_log (client_t *client, int response)
{
    refbuf_t *content;
    const char *logname = httpp_get_query_param (client->parser, "log");
    int log = -1;
    ice_config_t *config;

    if (logname == NULL)
        return client_send_400 (client, "No log specified");

    config = config_get_config ();
    if (strcmp (logname, "errorlog") == 0)
        log = config->error_log.logid;
    else if (strcmp (logname, "accesslog") == 0)
        log = config->access_log.logid;
    else if (strcmp (logname, "playlistlog") == 0)
        log = config->playlist_log.logid;

    if (log < 0)
    {
        config_release_config();
        WARN1 ("request to show unknown log \"%s\"", logname);
        return client_send_400 (client, "unknown");
    }
    content = refbuf_new (0);
    log_contents (log, &content->data, &content->len);
    config_release_config();

    if (response == XSLT)
    {
        xmlNodePtr xmlnode;
        xmlDocPtr doc;

        doc = xmlNewDoc(XMLSTR("1.0"));
        xmlnode = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
        xmlDocSetRootElement(doc, xmlnode);
        xmlNewTextChild (xmlnode, NULL, XMLSTR("log"), XMLSTR(content->data));
        refbuf_release (content);

        return admin_send_response (doc, client, XSLT, "showlog.xsl");
    }
    else
    {
        refbuf_t *http = refbuf_new (100);
        int len = snprintf (http->data, 100, "%s",
                "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
        http->len = len;
        http->next = content; 
        client->respcode = 200;
        client_set_queue (client, http);
        return fserve_setup_client (client);
    }
}
Beispiel #16
0
void client_destroy(client_t *client)
{
    if (client == NULL)
        return;

    if (client->worker)
    {
        WARN0 ("client still on worker thread");
        return;
    }
    /* release the buffer now, as the buffer could be on the source queue
     * and may of disappeared after auth completes */
    if (client->refbuf)
    {
        refbuf_release (client->refbuf);
        client->refbuf = NULL;
    }

    if (client->flags & CLIENT_AUTHENTICATED)
        DEBUG1 ("client still in auth \"%s\"", httpp_getvar (client->parser, HTTPP_VAR_URI));

    /* write log entry if ip is set (some things don't set it, like outgoing 
     * slave requests
     */
    if (client->respcode > 0 && client->parser)
        logging_access(client);

    if (client->flags & CLIENT_IP_BAN_LIFT)
    {
        INFO1 ("lifting IP ban on client at %s", client->connection.ip);
        connection_release_banned_ip (client->connection.ip);
        client->flags &= ~CLIENT_IP_BAN_LIFT;
    }

    connection_close (&client->connection);
    if (client->parser)
        httpp_destroy (client->parser);

    global_lock ();
    global.clients--;
    stats_event_args (NULL, "clients", "%d", global.clients);
    config_clear_listener (client->server_conn);
    global_unlock ();

    /* we need to free client specific format data (if any) */
    if (client->free_client_data)
        client->free_client_data (client);

    free(client->username);
    free(client->password);

    free(client);
}
Beispiel #17
0
static void clear_stats_queue (client_t *client)
{
    refbuf_t *refbuf = client->refbuf;
    while (refbuf)
    {
        refbuf_t *to_go = refbuf;
        refbuf = to_go->next;
        if (to_go->_count != 1) DEBUG1 ("odd count for stats %d", to_go->_count);
        to_go->next = NULL;
        refbuf_release (to_go);
    }
    client->refbuf = NULL;
}
Beispiel #18
0
static void refbuf_release_associated (refbuf_t *ref)
{
    if (ref == NULL)
        return;
    while (ref)
    {
        refbuf_t *to_go = ref;
        ref = to_go->next;
        if (to_go->_count == 1)
            to_go->next = NULL;
        refbuf_release (to_go);
    }
}
/* called from the source thread when the metadata has been updated.
 * The artist title are checked and made ready for clients to send
 */
static void mp3_set_title (source_t *source)
{
    const char meta[] = "StreamTitle='";
    int size;
    unsigned char len_byte;
    refbuf_t *p;
    unsigned int len = sizeof(meta) + 2; /* the StreamTitle, quotes, ; and null */
    mp3_state *source_mp3 = source->format->_state;

    /* make sure the url data does not disappear from under us */
    thread_mutex_lock (&source_mp3->url_lock);

    /* work out message length */
    if (source_mp3->url_artist)
        len += strlen (source_mp3->url_artist);
    if (source_mp3->url_title)
        len += strlen (source_mp3->url_title);
    if (source_mp3->url_artist && source_mp3->url_title)
        len += 3;
#define MAX_META_LEN 255*16
    if (len > MAX_META_LEN)
    {
        thread_mutex_unlock (&source_mp3->url_lock);
        WARN1 ("Metadata too long at %d chars", len);
        return;
    }
    /* work out the metadata len byte */
    len_byte = (len-1) / 16 + 1;

    /* now we know how much space to allocate, +1 for the len byte */
    size = len_byte * 16 + 1;

    p = refbuf_new (size);
    if (p)
    {
        mp3_state *source_mp3 = source->format->_state;

        memset (p->data, '\0', size);
        if (source_mp3->url_artist && source_mp3->url_title)
            snprintf (p->data, size, "%c%s%s - %s';", len_byte, meta,
                    source_mp3->url_artist, source_mp3->url_title);
        else
            snprintf (p->data, size, "%c%s%s';", len_byte, meta,
                    source_mp3->url_title);
        filter_shoutcast_metadata (source, p->data, size);

        refbuf_release (source_mp3->metadata);
        source_mp3->metadata = p;
    }
    thread_mutex_unlock (&source_mp3->url_lock);
}
Beispiel #20
0
void refbuf_release(refbuf_t *self)
{
    self->_count--;
    if (self->_count == 0) {
        while (self->associated)
        {
            refbuf_t *ref = self->associated;
            self->associated = ref->next;
            refbuf_release (ref);
        }
        free(self->data);
        free(self);
        return;
    }
}
void format_ogg_free_headers (ogg_state_t *ogg_info)
{
    refbuf_t *header;

    /* release the header pages first */
    DEBUG0 ("releasing header pages");
    header = ogg_info->header_pages;
    while (header)
    {
        refbuf_t *to_release = header;
        header = header->next;
        refbuf_release (to_release);
    }
    ogg_info->header_pages = NULL;
    ogg_info->header_pages_tail = NULL;
    ogg_info->bos_end = &ogg_info->header_pages;
}
Beispiel #22
0
static void _connection_node_destroy (connection_queue_t *node) {
    INFO("destroying node");

    if (node->client) {
        client_destroy(node->client); /* destroys con, parser, refbuf */
    } else {
        if (node->parser) {
            httpp_destroy(node->parser);
        }
        if (node->refbuf) {
            refbuf_release(node->refbuf);
        }
        if (node->con) {
            connection_close(node->con);
        }
    }
    free(node);
}
Beispiel #23
0
static void ebml_apply_client (format_plugin_t *plugin, client_t *client)
{
    ebml_source_state_t *ebml_source_state = plugin->_state;
    const char *s;

    refbuf_release (ebml_source_state->header);
    ebml_source_state->header = NULL;
    ebml_destroy (ebml_source_state->ebml);
    ebml_source_state->ebml = NULL;
    free (plugin->contenttype);

    if (client == NULL)
        return;

    s = plugin->parser ? httpp_getvar (plugin->parser, "content-type") : NULL;
    plugin->contenttype = strdup (s ? s : "video/x-matroska");
    ebml_source_state->ebml = ebml_create();
}
Beispiel #24
0
static size_t handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
{
    auth_client *auth_user = stream;
    unsigned bytes = size * nmemb;
    client_t *client = auth_user->client;
    refbuf_t *r = client->refbuf;

    if (client && client->respcode == 0 && r &&
         client->flags & CLIENT_HAS_INTRO_CONTENT)
    {
        refbuf_t *n;
        struct build_intro_contents *x = (void*)r->data;

        n = refbuf_new (bytes);
        memcpy (n->data, ptr, bytes);
        if (x->type)
        {
            int unprocessed = mpeg_complete_frames (&x->sync, n, 0);
            if (unprocessed < 0)
            {
                mpeg_data_insert (&x->sync, n); /* maybe short read, less than frame */
                return (int)(bytes);
            }
            if (unprocessed > 0)
            {
                refbuf_t *next = refbuf_new (unprocessed);
                memcpy (next->data, n->data + n->len, unprocessed);
                next->len = unprocessed;
                mpeg_data_insert (&x->sync, next);
            }
        }
        if (n->len == 0)
            refbuf_release (n);
        else
        {
            *x->tailp = n;
            x->tailp = &n->next;
            x->intro_len += n->len;
        }
    }
    return (int)(bytes);
}
Beispiel #25
0
void client_destroy(client_t *client)
{
    if (client == NULL)
        return;

    /* release the buffer now, as the buffer could be on the source queue
     * and may of disappeared after auth completes */
    if (client->refbuf)
    {
        refbuf_release (client->refbuf);
        client->refbuf = NULL;
    }

    if (auth_release_listener (client))
        return;

    /* write log entry if ip is set (some things don't set it, like outgoing 
     * slave requests
     */
    if (client->respcode && client->parser)
        logging_access(client);

    if (client->con)
        connection_close(client->con);
    if (client->parser)
        httpp_destroy(client->parser);

    global_lock ();
    global.clients--;
    stats_event_args (NULL, "clients", "%d", global.clients);
    global_unlock ();

    /* we need to free client specific format data (if any) */
    if (client->free_client_data)
        client->free_client_data (client);

    free(client->username);
    free(client->password);

    free(client);
}
Beispiel #26
0
void client_set_queue (client_t *client, refbuf_t *refbuf)
{
    refbuf_t *next, *to_release = client->refbuf;

    while (to_release)
    {
        if (to_release->flags & REFBUF_SHARED)
        {
            ERROR1 ("content has a shared flag status for %s", client->connection.ip);
            break;
        }
        next = to_release->next;
        to_release->next = NULL;
        refbuf_release (to_release);
        to_release = next;
    }

    client->refbuf = refbuf;
    if (refbuf)
        refbuf_addref (client->refbuf);

    client->pos = 0;
}
Beispiel #27
0
void client_destroy(client_t *client)
{
    if (client == NULL)
        return;

    if (release_client (client))
        return;

    /* write log entry if ip is set (some things don't set it, like outgoing 
     * slave requests
     */
    if (client->respcode && client->parser)
        logging_access(client);
    
    if (client->con)
        connection_close(client->con);
    if (client->parser)
        httpp_destroy(client->parser);

    global_lock ();
    global.clients--;
    stats_event_args (NULL, "clients", "%d", global.clients);
    global_unlock ();

    /* 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);

    free(client->username);
    free(client->password);

    free(client);
}
Beispiel #28
0
void client_destroy(client_t *client)
{
    if (client == NULL)
        return;

    if (client->worker)
    {
        WARN0 ("client still on worker thread");
        return;
    }
    /* release the buffer now, as the buffer could be on the source queue
     * and may of disappeared after auth completes */
    if (client->refbuf)
    {
        refbuf_release (client->refbuf);
        client->refbuf = NULL;
    }

    if (client->flags & CLIENT_AUTHENTICATED)
        DEBUG1 ("client still in auth \"%s\"", httpp_getvar (client->parser, HTTPP_VAR_URI));

    /* write log entry if ip is set (some things don't set it, like outgoing 
     * slave requests
     */
    if (client->respcode > 0 && client->parser)
        logging_access(client);

    if (client->flags & CLIENT_IP_BAN_LIFT)
    {
        INFO1 ("lifting IP ban on client at %s", client->connection.ip);
        connection_release_banned_ip (client->connection.ip);
        client->flags &= ~CLIENT_IP_BAN_LIFT;
    }

    if (client->parser)
        httpp_destroy (client->parser);

    /* we need to free client specific format data (if any) */
    if (client->free_client_data)
        client->free_client_data (client);

    free(client->username);
    free(client->password);
    client->username = NULL;
    client->password = NULL;
    client->parser = NULL;
    client->respcode = 0;
    client->free_client_data = NULL;

    global_lock ();
    if (global.running != ICE_RUNNING || client->connection.error ||
            (client->flags & CLIENT_KEEPALIVE) == 0 || client_connected (client) == 0)
    {
        global.clients--;
        stats_event_args (NULL, "clients", "%d", global.clients);
        config_clear_listener (client->server_conn);
        global_unlock ();
        connection_close (&client->connection);

        free(client);
        return;
    }
    global_unlock ();
    DEBUG0 ("keepalive detected, placing back onto worker");
    client->counter = client->schedule_ms = timing_get_time();
    client->connection.con_time = client->schedule_ms/1000;
    client->connection.discon.time = client->connection.con_time + 7;
    client->ops = &http_request_ops;
    client->flags = CLIENT_ACTIVE;
    client->shared_data = NULL;
    client->refbuf = NULL;
    client->pos = 0;
    client->intro_offset = client->connection.sent_bytes = 0;
    client_add_worker (client);
}
Beispiel #29
0
/* read mp3 data with inlined metadata from the source. Filter out the
 * metadata so that the mp3 data itself is store on the queue and the
 * metadata is is associated with it
 */
static refbuf_t *mp3_get_filter_meta (source_t *source)
{
    refbuf_t *refbuf;
    format_plugin_t *plugin = source->format;
    mp3_state *source_mp3 = plugin->_state;
    unsigned char *src;
    unsigned int bytes, mp3_block;

    if (complete_read (source) == 0)
        return NULL;

    refbuf = source_mp3->read_data;
    source_mp3->read_data = NULL;
    src = (unsigned char *)refbuf->data;

    if (source_mp3->update_metadata)
    {
        mp3_set_title (source);
        source_mp3->update_metadata = 0;
    }
    /* fill the buffer with the read data */
    bytes = source_mp3->read_count;
    refbuf->len = 0;
    while (bytes > 0)
    {
        unsigned int metadata_remaining;

        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;

        /* is there only enough to account for mp3 data */
        if (bytes <= mp3_block)
        {
            refbuf->len += bytes;
            source_mp3->offset += bytes;
            break;
        }
        /* we have enough data to get to the metadata
         * block, but only transfer upto it */
        if (mp3_block)
        {
            src += mp3_block;
            bytes -= mp3_block;
            refbuf->len += mp3_block;
            source_mp3->offset += mp3_block;
            continue;
        }

        /* process the inline metadata, len == 0 indicates not seen any yet */
        if (source_mp3->build_metadata_len == 0)
        {
            memset (source_mp3->build_metadata, 0,
                    sizeof (source_mp3->build_metadata));
            source_mp3->build_metadata_offset = 0;
            source_mp3->build_metadata_len = 1 + (*src * 16);
        }

        /* do we have all of the metatdata block */
        metadata_remaining = source_mp3->build_metadata_len -
            source_mp3->build_metadata_offset;
        if (bytes < metadata_remaining)
        {
            memcpy (source_mp3->build_metadata +
                    source_mp3->build_metadata_offset, src, bytes);
            source_mp3->build_metadata_offset += bytes;
            break;
        }
        /* copy all bytes except the last one, that way we 
         * know a null byte terminates the message */
        memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset,
                src, metadata_remaining-1);

        /* overwrite metadata in the buffer */
        bytes -= metadata_remaining;
        memmove (src, src+metadata_remaining, bytes);

        /* assign metadata if it's greater than 1 byte, and the text has changed */
        if (source_mp3->build_metadata_len > 1 &&
                strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
        {
            refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
            memcpy (meta->data, source_mp3->build_metadata,
                    source_mp3->build_metadata_len);

	    ICECAST_LOG_DEBUG("shoutcast metadata %.*s", 4080, meta->data+1);
            if (strncmp (meta->data+1, "StreamTitle=", 12) == 0)
            {
                filter_shoutcast_metadata (source, source_mp3->build_metadata,
                        source_mp3->build_metadata_len);
                refbuf_release (source_mp3->metadata);
                source_mp3->metadata = meta;
                source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='");
            }
            else
            {
                ICECAST_LOG_ERROR("Incorrect metadata format, ending stream");
                source->running = 0;
                refbuf_release (refbuf);
                refbuf_release (meta);
                return NULL;
            }
        }
        source_mp3->offset = 0;
        source_mp3->build_metadata_len = 0;
    }
    /* the data we have just read may of just been metadata */
    if (refbuf->len == 0)
    {
        refbuf_release (refbuf);
        return NULL;
    }
    refbuf->associated = source_mp3->metadata;
    refbuf_addref (source_mp3->metadata);
    refbuf->sync_point = 1;

    return refbuf;
}
Beispiel #30
0
/* called from the source thread when the metadata has been updated.
 * The artist title are checked and made ready for clients to send
 */
static void mp3_set_title (source_t *source)
{
    const char streamtitle[] = "StreamTitle='";
    const char streamurl[] = "StreamUrl='";
    size_t size;
    unsigned char len_byte;
    refbuf_t *p;
    unsigned int len = sizeof(streamtitle) + 2; /* the StreamTitle, quotes, ; and null */
    mp3_state *source_mp3 = source->format->_state;

    /* make sure the url data does not disappear from under us */
    thread_mutex_lock (&source_mp3->url_lock);

    /* work out message length */
    if (source_mp3->url_artist)
        len += strlen (source_mp3->url_artist);
    if (source_mp3->url_title)
        len += strlen (source_mp3->url_title);
    if (source_mp3->url_artist && source_mp3->url_title)
        len += 3;
    if (source_mp3->inline_url)
    {
        char *end = strstr (source_mp3->inline_url, "';");
        if (end)
            len += end - source_mp3->inline_url+2;
    }
    else if (source_mp3->url)
        len += strlen (source_mp3->url) + strlen (streamurl) + 2;
#define MAX_META_LEN 255*16
    if (len > MAX_META_LEN)
    {
        thread_mutex_unlock (&source_mp3->url_lock);
        ICECAST_LOG_WARN("Metadata too long at %d chars", len);
        return;
    }
    /* work out the metadata len byte */
    len_byte = (len-1) / 16 + 1;

    /* now we know how much space to allocate, +1 for the len byte */
    size = len_byte * 16 + 1;

    p = refbuf_new (size);
    if (p)
    {
        mp3_state *source_mp3 = source->format->_state;
        int r;

        memset (p->data, '\0', size);
        if (source_mp3->url_artist && source_mp3->url_title)
            r = snprintf (p->data, size, "%c%s%s - %s';", len_byte, streamtitle,
                    source_mp3->url_artist, source_mp3->url_title);
        else
            r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle,
                    source_mp3->url_title);
        if (r > 0)
        {
            if (source_mp3->inline_url)
            {
                char *end = strstr (source_mp3->inline_url, "';");
                ssize_t urllen = size;
                if (end) urllen = end - source_mp3->inline_url + 2;
                if ((ssize_t)(size-r) > urllen)
                    snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->inline_url+11);
            }
            else if (source_mp3->url)
                snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->url);
        }
        ICECAST_LOG_DEBUG("shoutcast metadata block setup with %s", p->data+1);
        filter_shoutcast_metadata (source, p->data, size);

        refbuf_release (source_mp3->metadata);
        source_mp3->metadata = p;
    }
    thread_mutex_unlock (&source_mp3->url_lock);
}