format_mp3.c 21.6 KB
Newer Older
1
/* -*- c-basic-offset: 4; -*- */
2 3 4 5 6 7 8 9 10 11
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
Philipp Schafft's avatar
Philipp Schafft committed
12
 * Copyright 2011-2012, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
13 14
 */

Michael Smith's avatar
Michael Smith committed
15
/* format_mp3.c
16 17 18 19
 **
 ** format plugin for mp3
 **
 */
Michael Smith's avatar
Michael Smith committed
20

21 22 23 24
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Michael Smith's avatar
Michael Smith committed
25 26 27
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Karl Heyes's avatar
Karl Heyes committed
28 29 30
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
Michael Smith's avatar
Michael Smith committed
31 32

#include "refbuf.h"
33 34
#include "source.h"
#include "client.h"
Michael Smith's avatar
Michael Smith committed
35 36 37

#include "stats.h"
#include "format.h"
38 39 40 41 42 43
#include "httpp/httpp.h"

#include "logging.h"

#include "format_mp3.h"

Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
44 45 46
#ifdef WIN32
#define strcasecmp stricmp
#define strncasecmp strnicmp
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
47
#define snprintf _snprintf
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
48 49
#endif

50 51
#define CATMODULE "format-mp3"

52 53 54
/* Note that this seems to be 8192 in shoutcast - perhaps we want to be the
 * same for compability with crappy clients?
 */
55
#define ICY_METADATA_INTERVAL 16000
Michael Smith's avatar
Michael Smith committed
56

57
static void format_mp3_free_plugin(format_plugin_t *self);
Karl Heyes's avatar
Karl Heyes committed
58 59 60 61
static refbuf_t *mp3_get_filter_meta (source_t *source);
static refbuf_t *mp3_get_no_meta (source_t *source);

static int  format_mp3_create_client_data (source_t *source, client_t *client);
62
static void free_mp3_client_data (client_t *client);
63
static int format_mp3_write_buf_to_client(client_t *client);
Karl Heyes's avatar
Karl Heyes committed
64
static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf);
65
static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset);
66
static void format_mp3_apply_settings(client_t *client, format_plugin_t *format, mount_proxy *mount);
Karl Heyes's avatar
Karl Heyes committed
67

68 69

typedef struct {
70 71 72 73 74
    unsigned int interval;
    int metadata_offset;
    unsigned int since_meta_block;
    int in_metadata;
    refbuf_t *associated;
75
} mp3_client_data;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
76

77
int format_mp3_get_plugin(source_t *source)
Michael Smith's avatar
Michael Smith committed
78
{
79
    const char *metadata;
80
    format_plugin_t *plugin;
Michael Smith's avatar
Michael Smith committed
81
    mp3_state *state = calloc(1, sizeof(mp3_state));
Karl Heyes's avatar
Karl Heyes committed
82
    refbuf_t *meta;
Michael Smith's avatar
Michael Smith committed
83

84
    plugin = (format_plugin_t *) calloc(1, sizeof(format_plugin_t));
Michael Smith's avatar
Michael Smith committed
85

86
    plugin->type = FORMAT_TYPE_GENERIC;
Karl Heyes's avatar
Karl Heyes committed
87
    plugin->get_buffer = mp3_get_no_meta;
88
    plugin->write_buf_to_client = format_mp3_write_buf_to_client;
Karl Heyes's avatar
Karl Heyes committed
89
    plugin->write_buf_to_file = write_mp3_to_file;
90
    plugin->create_client_data = format_mp3_create_client_data;
91
    plugin->free_plugin = format_mp3_free_plugin;
92
    plugin->set_tag = mp3_set_tag;
93
    plugin->apply_settings = format_mp3_apply_settings;
94

95
    plugin->contenttype = httpp_getvar(source->parser, "content-type");
96 97 98 99
    if (plugin->contenttype == NULL) {
        /* We default to MP3 audio for old clients without content types */
        plugin->contenttype = "audio/mpeg";
    }
Michael Smith's avatar
Michael Smith committed
100

101
    plugin->_state = state;
Michael Smith's avatar
Michael Smith committed
102

103 104
    /* initial metadata needs to be blank for sending to clients and for
       comparing with new metadata */
105 106
    meta = refbuf_new (17);
    memcpy (meta->data, "\001StreamTitle='';", 17);
Karl Heyes's avatar
Karl Heyes committed
107
    state->metadata = meta;
108
    state->interval = -1;
Michael Smith's avatar
Michael Smith committed
109

Karl Heyes's avatar
Karl Heyes committed
110 111 112 113
    metadata = httpp_getvar (source->parser, "icy-metaint");
    if (metadata)
    {
        state->inline_metadata_interval = atoi (metadata);
114 115 116 117
        if (state->inline_metadata_interval > 0)
        {
            state->offset = 0;
            plugin->get_buffer = mp3_get_filter_meta;
118
            state->interval = state->inline_metadata_interval;
119
        }
Karl Heyes's avatar
Karl Heyes committed
120 121
    }
    source->format = plugin;
122
    thread_mutex_create(&state->url_lock);
123

Karl Heyes's avatar
Karl Heyes committed
124
    return 0;
Michael Smith's avatar
Michael Smith committed
125 126
}

127

128
static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset)
Michael Smith's avatar
Michael Smith committed
129
{
Karl Heyes's avatar
Karl Heyes committed
130
    mp3_state *source_mp3 = plugin->_state;
131
    char *value = NULL;
Karl Heyes's avatar
Karl Heyes committed
132 133 134

    /* protect against multiple updaters */
    thread_mutex_lock (&source_mp3->url_lock);
135

136 137 138 139 140 141
    if (tag==NULL)
    {
        source_mp3->update_metadata = 1;
        thread_mutex_unlock (&source_mp3->url_lock);
        return;
    }
142

143 144 145 146 147 148
    if (in_value)
    {
        value = util_conv_string (in_value, charset, plugin->charset);
        if (value == NULL)
            value = strdup (in_value);
    }
149

Karl Heyes's avatar
Karl Heyes committed
150
    if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
151
    {
152 153
        free (source_mp3->url_title);
        source_mp3->url_title = value;
Karl Heyes's avatar
Karl Heyes committed
154 155 156
    }
    else if (strcmp (tag, "artist") == 0)
    {
157 158
        free (source_mp3->url_artist);
        source_mp3->url_artist = value;
159 160 161 162 163
    }
    else if (strcmp (tag, "url") == 0)
    {
        free (source_mp3->url);
        source_mp3->url = value;
Karl Heyes's avatar
Karl Heyes committed
164
    }
165 166
    else
        free (value);
Karl Heyes's avatar
Karl Heyes committed
167 168
    thread_mutex_unlock (&source_mp3->url_lock);
}
169 170


Karl Heyes's avatar
Karl Heyes committed
171 172 173 174 175 176
static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigned int meta_len)
{
    if (metadata)
    {
        char *end, *p;
        int len;
177

Karl Heyes's avatar
Karl Heyes committed
178
        do
179
        {
Karl Heyes's avatar
Karl Heyes committed
180 181 182
            metadata++;
            if (strncmp (metadata, "StreamTitle='", 13))
                break;
Karl Heyes's avatar
Karl Heyes committed
183
            if ((end = strstr (metadata+13, "\';")) == NULL)
Karl Heyes's avatar
Karl Heyes committed
184 185 186 187 188 189
                break;
            len = (end - metadata) - 13;
            p = calloc (1, len+1);
            if (p)
            {
                memcpy (p, metadata+13, len);
190
                logging_playlist (source->mount, p, source->listeners);
191
                stats_event_conv (source->mount, "title", p, source->format->charset);
Karl Heyes's avatar
Karl Heyes committed
192 193 194 195 196 197
                yp_touch (source->mount);
                free (p);
            }
        } while (0);
    }
}
198

Michael Smith's avatar
Michael Smith committed
199

200 201 202 203
static void format_mp3_apply_settings (client_t *client, format_plugin_t *format, mount_proxy *mount)
{
    mp3_state *source_mp3 = format->_state;

204 205 206 207 208 209
    source_mp3->interval = -1;
    free (format->charset);
    format->charset = NULL;

    if (mount)
    {
210
        if (mount->mp3_meta_interval >= 0)
211 212 213 214
            source_mp3->interval = mount->mp3_meta_interval;
        if (mount->charset)
            format->charset = strdup (mount->charset);
    }
215
    if (source_mp3->interval < 0)
216
    {
217
        const char *metadata = httpp_getvar (client->parser, "icy-metaint");
218
        source_mp3->interval = ICY_METADATA_INTERVAL;
219 220 221 222 223 224 225
        if (metadata)
        {
            int interval = atoi (metadata);
            if (interval > 0)
                source_mp3->interval = interval;
        }
    }
226 227 228 229

    if (format->charset == NULL)
        format->charset = strdup ("ISO8859-1");

230 231
    ICECAST_LOG_DEBUG("sending metadata interval %d", source_mp3->interval);
    ICECAST_LOG_DEBUG("charset %s", format->charset);
232 233 234
}


Karl Heyes's avatar
Karl Heyes committed
235 236 237
/* called from the source thread when the metadata has been updated.
 * The artist title are checked and made ready for clients to send
 */
238
static void mp3_set_title(source_t *source)
Karl Heyes's avatar
Karl Heyes committed
239
{
240 241 242
    const char streamtitle[] = "StreamTitle='";
    const char streamurl[] = "StreamUrl='";
    size_t size;
Karl Heyes's avatar
Karl Heyes committed
243 244
    unsigned char len_byte;
    refbuf_t *p;
245
    unsigned int len = sizeof(streamtitle) + 2; /* the StreamTitle, quotes, ; and null */
Karl Heyes's avatar
Karl Heyes committed
246 247 248 249 250 251 252 253 254 255 256 257
    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;
258 259 260 261 262 263 264 265
    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;
Karl Heyes's avatar
Karl Heyes committed
266 267 268 269
#define MAX_META_LEN 255*16
    if (len > MAX_META_LEN)
    {
        thread_mutex_unlock (&source_mp3->url_lock);
270
        ICECAST_LOG_WARN("Metadata too long at %d chars", len);
Karl Heyes's avatar
Karl Heyes committed
271 272 273 274
        return;
    }
    /* work out the metadata len byte */
    len_byte = (len-1) / 16 + 1;
Michael Smith's avatar
Michael Smith committed
275

Karl Heyes's avatar
Karl Heyes committed
276 277
    /* now we know how much space to allocate, +1 for the len byte */
    size = len_byte * 16 + 1;
Michael Smith's avatar
Michael Smith committed
278

Karl Heyes's avatar
Karl Heyes committed
279 280 281 282
    p = refbuf_new (size);
    if (p)
    {
        mp3_state *source_mp3 = source->format->_state;
283
        int r;
Michael Smith's avatar
Michael Smith committed
284

Karl Heyes's avatar
Karl Heyes committed
285 286
        memset (p->data, '\0', size);
        if (source_mp3->url_artist && source_mp3->url_title)
287
            r = snprintf (p->data, size, "%c%s%s - %s';", len_byte, streamtitle,
Karl Heyes's avatar
Karl Heyes committed
288 289
                    source_mp3->url_artist, source_mp3->url_title);
        else
290
            r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle,
Karl Heyes's avatar
Karl Heyes committed
291
                    source_mp3->url_title);
292 293 294 295 296
        if (r > 0)
        {
            if (source_mp3->inline_url)
            {
                char *end = strstr (source_mp3->inline_url, "';");
297
                ssize_t urllen = size;
298
                if (end) urllen = end - source_mp3->inline_url + 2;
299
                if ((ssize_t)(size-r) > urllen)
300 301 302 303 304
                    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);
        }
305
        ICECAST_LOG_DEBUG("shoutcast metadata block setup with %s", p->data+1);
Karl Heyes's avatar
Karl Heyes committed
306
        filter_shoutcast_metadata (source, p->data, size);
Michael Smith's avatar
Michael Smith committed
307

Karl Heyes's avatar
Karl Heyes committed
308 309 310 311
        refbuf_release (source_mp3->metadata);
        source_mp3->metadata = p;
    }
    thread_mutex_unlock (&source_mp3->url_lock);
Michael Smith's avatar
Michael Smith committed
312 313
}

Karl Heyes's avatar
Karl Heyes committed
314 315 316 317 318

/* send the appropriate metadata, and return the number of bytes written
 * which is 0 or greater.  Check the client in_metadata value afterwards
 * to see if all metadata has been sent
 */
319
static int send_stream_metadata (client_t *client, refbuf_t *associated)
320
{
321
    int ret = 0;
322
    char *metadata;
Karl Heyes's avatar
Karl Heyes committed
323 324 325 326 327 328
    int meta_len;
    mp3_client_data *client_mp3 = client->format_data;

    /* If there is a change in metadata then send it else
     * send a single zero value byte in its place
     */
329
    if (associated && associated != client_mp3->associated)
Karl Heyes's avatar
Karl Heyes committed
330
    {
331 332
        metadata = associated->data + client_mp3->metadata_offset;
        meta_len = associated->len - client_mp3->metadata_offset;
Karl Heyes's avatar
Karl Heyes committed
333 334 335
    }
    else
    {
336 337 338 339 340 341 342 343 344 345 346
        if (associated)
        {
            metadata = "\0";
            meta_len = 1;
        }
        else
        {
            char *meta = "\001StreamTitle='';";
            metadata = meta + client_mp3->metadata_offset;
            meta_len = 17 - client_mp3->metadata_offset;
        }
Karl Heyes's avatar
Karl Heyes committed
347
    }
348
    ret = client_send_bytes(client, metadata, meta_len);
Karl Heyes's avatar
Karl Heyes committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

    if (ret == meta_len)
    {
        client_mp3->associated = associated;
        client_mp3->metadata_offset = 0;
        client_mp3->in_metadata = 0;
        client_mp3->since_meta_block = 0;
        return ret;
    }
    if (ret > 0)
        client_mp3->metadata_offset += ret;
    else
        ret = 0;
    client_mp3->in_metadata = 1;

    return ret;
}


/* Handler for writing mp3 data to a client, taking into account whether
 * client has requested shoutcast style metadata updates
 */
371
static int format_mp3_write_buf_to_client(client_t *client)
Karl Heyes's avatar
Karl Heyes committed
372 373 374 375
{
    int ret, written = 0;
    mp3_client_data *client_mp3 = client->format_data;
    refbuf_t *refbuf = client->refbuf;
Karl Heyes's avatar
Karl Heyes committed
376 377
    char *buf = refbuf->data + client->pos;
    unsigned int len = refbuf->len - client->pos;
Michael Smith's avatar
Michael Smith committed
378

Karl Heyes's avatar
Karl Heyes committed
379 380 381 382 383 384
    do
    {
        /* send any unwritten metadata to the client */
        if (client_mp3->in_metadata)
        {
            refbuf_t *associated = refbuf->associated;
385
            ret = send_stream_metadata(client, associated);
Michael Smith's avatar
Michael Smith committed
386

Karl Heyes's avatar
Karl Heyes committed
387 388 389
            if (client_mp3->in_metadata)
                break;
            written += ret;
Michael Smith's avatar
Michael Smith committed
390
        }
Karl Heyes's avatar
Karl Heyes committed
391
        /* see if we need to send the current metadata to the client */
392
        if (client_mp3->interval)
Karl Heyes's avatar
Karl Heyes committed
393
        {
394
            unsigned int remaining = client_mp3->interval -
Karl Heyes's avatar
Karl Heyes committed
395 396
                client_mp3->since_meta_block;

397
            /* leading up to sending the metadata block */
Karl Heyes's avatar
Karl Heyes committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
            if (remaining <= len)
            {
                /* send any mp3 before the metadata block */
                if (remaining)
                {
                    ret = client_send_bytes (client, buf, remaining);

                    if (ret > 0)
                    {
                        client_mp3->since_meta_block += ret;
                        client->pos += ret;
                    }
                    if (ret < (int)remaining)
                        break;
                    written += ret;
                }
414
                ret = send_stream_metadata (client, refbuf->associated);
Karl Heyes's avatar
Karl Heyes committed
415 416 417 418 419
                if (client_mp3->in_metadata)
                    break;
                written += ret;
                buf += remaining;
                len -= remaining;
420 421 422
                /* limit how much mp3 we send if using small intervals */
                if (len > client_mp3->interval)
                    len = client_mp3->interval;
Karl Heyes's avatar
Karl Heyes committed
423
            }
424
        }
Karl Heyes's avatar
Karl Heyes committed
425 426 427 428
        /* write any mp3, maybe after the metadata block */
        if (len)
        {
            ret = client_send_bytes (client, buf, len);
429

Karl Heyes's avatar
Karl Heyes committed
430 431 432 433 434 435 436 437 438 439 440
            if (ret > 0)
            {
                client_mp3->since_meta_block += ret;
                client->pos += ret;
            }
            if (ret < (int)len)
                break;
            written += ret;
        }
        ret = 0;
    } while (0);
441

Karl Heyes's avatar
Karl Heyes committed
442 443 444
    if (ret > 0)
        written += ret;
    return written;
445 446 447
}

static void format_mp3_free_plugin(format_plugin_t *self)
Michael Smith's avatar
Michael Smith committed
448
{
449
    /* free the plugin instance */
Michael Smith's avatar
Michael Smith committed
450
    mp3_state *state = self->_state;
Michael Smith's avatar
Michael Smith committed
451

452 453 454 455 456 457
    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);
Michael Smith's avatar
Michael Smith committed
458
    free(state);
459
    free(self);
Michael Smith's avatar
Michael Smith committed
460 461
}

Karl Heyes's avatar
Karl Heyes committed
462

463 464 465 466 467
/* 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.
 */
468
static int complete_read(source_t *source)
Michael Smith's avatar
Michael Smith committed
469
{
Karl Heyes's avatar
Karl Heyes committed
470
    int bytes;
471
    format_plugin_t *format = source->format;
472 473 474
    mp3_state *source_mp3 = format->_state;
    char *buf;
    refbuf_t *refbuf;
475

476 477 478 479 480 481 482 483
#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;
484

485
    bytes = client_read_bytes (source->client, buf, REFBUF_SIZE-source_mp3->read_count);
486
    if (bytes < 0)
Karl Heyes's avatar
Karl Heyes committed
487
    {
488 489 490 491 492 493
        if (source->client->con->error)
        {
            refbuf_release (source_mp3->read_data);
            source_mp3->read_data = NULL;
        }
        return 0;
Karl Heyes's avatar
Karl Heyes committed
494
    }
495 496 497
    source_mp3->read_count += bytes;
    refbuf = source_mp3->read_data;
    refbuf->len = source_mp3->read_count;
498
    format->read_bytes += bytes;
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524

    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;
}


/* read an mp3 stream which does not have shoutcast style metadata */
static refbuf_t *mp3_get_no_meta (source_t *source)
{
    refbuf_t *refbuf;
    mp3_state *source_mp3 = source->format->_state;

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

    refbuf = source_mp3->read_data;
    source_mp3->read_data = NULL;

Karl Heyes's avatar
Karl Heyes committed
525 526 527 528 529
    if (source_mp3->update_metadata)
    {
        mp3_set_title (source);
        source_mp3->update_metadata = 0;
    }
530 531 532 533
    refbuf->associated = source_mp3->metadata;
    refbuf_addref (source_mp3->metadata);
    refbuf->sync_point = 1;
    return refbuf;
Karl Heyes's avatar
Karl Heyes committed
534
}
535 536


Karl Heyes's avatar
Karl Heyes committed
537 538 539 540
/* 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
 */
541
static refbuf_t *mp3_get_filter_meta(source_t *source)
Karl Heyes's avatar
Karl Heyes committed
542 543 544 545 546 547
{
    refbuf_t *refbuf;
    format_plugin_t *plugin = source->format;
    mp3_state *source_mp3 = plugin->_state;
    unsigned char *src;
    unsigned int bytes, mp3_block;
548

549 550 551 552 553
    if (complete_read (source) == 0)
        return NULL;

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

Karl Heyes's avatar
Karl Heyes committed
556 557 558 559 560 561
    if (source_mp3->update_metadata)
    {
        mp3_set_title (source);
        source_mp3->update_metadata = 0;
    }
    /* fill the buffer with the read data */
562
    bytes = source_mp3->read_count;
Karl Heyes's avatar
Karl Heyes committed
563 564 565 566
    refbuf->len = 0;
    while (bytes > 0)
    {
        unsigned int metadata_remaining;
567

Karl Heyes's avatar
Karl Heyes committed
568
        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;
569

Karl Heyes's avatar
Karl Heyes committed
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
        /* 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;
        }
587

Karl Heyes's avatar
Karl Heyes committed
588 589 590 591 592 593 594 595
        /* 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);
        }
596

Karl Heyes's avatar
Karl Heyes committed
597 598 599 600 601 602 603 604 605 606 607 608 609 610
        /* 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);
611

Karl Heyes's avatar
Karl Heyes committed
612 613 614
        /* overwrite metadata in the buffer */
        bytes -= metadata_remaining;
        memmove (src, src+metadata_remaining, bytes);
615

616 617 618
        /* 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)
Karl Heyes's avatar
Karl Heyes committed
619 620 621 622 623
        {
            refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
            memcpy (meta->data, source_mp3->build_metadata,
                    source_mp3->build_metadata_len);

624
	    ICECAST_LOG_DEBUG("shoutcast metadata %.*s", 4080, meta->data+1);
Karl Heyes's avatar
Karl Heyes committed
625 626 627 628 629 630
            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;
631
                source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='");
Karl Heyes's avatar
Karl Heyes committed
632 633 634
            }
            else
            {
635
                ICECAST_LOG_ERROR("Incorrect metadata format, ending stream");
Karl Heyes's avatar
Karl Heyes committed
636 637
                source->running = 0;
                refbuf_release (refbuf);
638
                refbuf_release (meta);
Karl Heyes's avatar
Karl Heyes committed
639
                return NULL;
640 641
            }
        }
Karl Heyes's avatar
Karl Heyes committed
642 643
        source_mp3->offset = 0;
        source_mp3->build_metadata_len = 0;
644
    }
Karl Heyes's avatar
Karl Heyes committed
645 646 647 648 649
    /* the data we have just read may of just been metadata */
    if (refbuf->len == 0)
    {
        refbuf_release (refbuf);
        return NULL;
650
    }
Karl Heyes's avatar
Karl Heyes committed
651 652
    refbuf->associated = source_mp3->metadata;
    refbuf_addref (source_mp3->metadata);
653
    refbuf->sync_point = 1;
Michael Smith's avatar
Michael Smith committed
654

Karl Heyes's avatar
Karl Heyes committed
655
    return refbuf;
Michael Smith's avatar
Michael Smith committed
656 657
}

Karl Heyes's avatar
Karl Heyes committed
658 659

static int format_mp3_create_client_data(source_t *source, client_t *client)
660
{
661
    mp3_client_data *client_mp3 = calloc(1, sizeof(mp3_client_data));
662
    mp3_state *source_mp3 = source->format->_state;
663
    const char *metadata;
664 665 666 667
    /* the +-2 is for overwriting the last set of \r\n */
    unsigned remaining = 4096 - client->refbuf->len + 2;
    char *ptr = client->refbuf->data + client->refbuf->len - 2;
    int bytes;
668
    const char *useragent;
669

670
    if (client_mp3 == NULL)
Karl Heyes's avatar
Karl Heyes committed
671
        return -1;
672

673 674 675 676 677
    /* hack for flash player, it wants a length.  It has also been reported that the useragent
     * appears as MSIE if run in internet explorer */
    useragent = httpp_getvar (client->parser, "user-agent");
    if (httpp_getvar(client->parser, "x-flash-version") ||
            (useragent && strstr(useragent, "MSIE")))
678
    {
679
        bytes = snprintf (ptr, remaining, "Content-Length: 221183499\r\n");
680 681 682 683
        remaining -= bytes;
        ptr += bytes;
    }

684
    client->format_data = client_mp3;
Karl Heyes's avatar
Karl Heyes committed
685
    client->free_client_data = free_mp3_client_data;
686
    metadata = httpp_getvar(client->parser, "icy-metadata");
687 688
    if (metadata && atoi(metadata))
    {
689
        if (source_mp3->interval >= 0)
690 691 692
            client_mp3->interval = source_mp3->interval;
        else
            client_mp3->interval = ICY_METADATA_INTERVAL;
693
        if (client_mp3->interval)
694
        {
695 696 697 698 699 700 701
            bytes = snprintf (ptr, remaining, "icy-metaint:%u\r\n",
                    client_mp3->interval);
            if (bytes > 0)
            {
                remaining -= bytes;
                ptr += bytes;
            }
702
        }
703
    }
704 705 706
    bytes = snprintf (ptr, remaining, "\r\n");
    remaining -= bytes;
    ptr += bytes;
707

708
    client->refbuf->len = 4096 - remaining;
709

Karl Heyes's avatar
Karl Heyes committed
710
    return 0;
711
}
Michael Smith's avatar
Michael Smith committed
712

713 714 715 716 717 718 719 720

static void free_mp3_client_data (client_t *client)
{
    free (client->format_data);
    client->format_data = NULL;
}


Karl Heyes's avatar
Karl Heyes committed
721 722 723 724 725 726
static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf)
{
    if (refbuf->len == 0)
        return;
    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len)
    {
727
        ICECAST_LOG_WARN("Write to dump file failed, disabling");
Karl Heyes's avatar
Karl Heyes committed
728 729 730 731 732
        fclose (source->dumpfile);
        source->dumpfile = NULL;
    }
}