format.c 13.6 KB
Newer Older
1 2 3 4 5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 8 9 10 11 12
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */

13
/* -*- c-basic-offset: 4; -*- */
Jack Moffitt's avatar
Jack Moffitt committed
14
/* format.c
15 16 17 18
 **
 ** format plugin implementation
 **
 */
Jack Moffitt's avatar
Jack Moffitt committed
19

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

Jack Moffitt's avatar
Jack Moffitt committed
24 25
#include <stdlib.h>
#include <string.h>
Karl Heyes's avatar
Karl Heyes committed
26 27 28
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
29 30 31
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
Jack Moffitt's avatar
Jack Moffitt committed
32

33 34
#include <vorbis/codec.h>

Jack Moffitt's avatar
Jack Moffitt committed
35 36 37
#include "connection.h"
#include "refbuf.h"

Michael Smith's avatar
Michael Smith committed
38
#include "source.h"
Jack Moffitt's avatar
Jack Moffitt committed
39
#include "format.h"
40
#include "global.h"
Jack Moffitt's avatar
Jack Moffitt committed
41

42
#include "format_ogg.h"
Michael Smith's avatar
Michael Smith committed
43
#include "format_mp3.h"
44
#include "format_ebml.h"
Jack Moffitt's avatar
Jack Moffitt committed
45

46
#include "logging.h"
47
#include "stats.h"
48
#define CATMODULE "format"
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
49

50 51 52
static int format_prepare_headers (source_t *source, client_t *client);


53
format_type_t format_get_type (const char *contenttype)
Michael Smith's avatar
Michael Smith committed
54 55
{
    if(strcmp(contenttype, "application/x-ogg") == 0)
56
        return FORMAT_TYPE_OGG; /* Backwards compatibility */
57
    else if(strcmp(contenttype, "application/ogg") == 0)
58
        return FORMAT_TYPE_OGG; /* Now blessed by IANA */
Karl Heyes's avatar
Karl Heyes committed
59 60 61 62
    else if(strcmp(contenttype, "audio/ogg") == 0)
        return FORMAT_TYPE_OGG;
    else if(strcmp(contenttype, "video/ogg") == 0)
        return FORMAT_TYPE_OGG;
63 64 65 66 67 68 69 70 71 72
    else if(strcmp(contenttype, "audio/webm") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "video/webm") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "audio/x-matroska") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "video/x-matroska") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "video/x-matroska-3d") == 0)
        return FORMAT_TYPE_EBML;
73
    else
74
        /* We default to the Generic format handler, which
75
           can handle many more formats than just mp3.
76 77
           Let's warn that this is not well supported */
        ICECAST_LOG_WARN("Unsupported or legacy stream type: \"%s\". Falling back to generic minimal handler for best effort.", contenttype);
78
        return FORMAT_TYPE_GENERIC;
79 80
}

Karl Heyes's avatar
Karl Heyes committed
81
int format_get_plugin(format_type_t type, source_t *source)
Jack Moffitt's avatar
Jack Moffitt committed
82
{
Karl Heyes's avatar
Karl Heyes committed
83
    int ret = -1;
Jack Moffitt's avatar
Jack Moffitt committed
84

85
    switch (type) {
86 87
        case FORMAT_TYPE_OGG:
            ret = format_ogg_get_plugin(source);
88
        break;
89 90
        case FORMAT_TYPE_EBML:
            ret = format_ebml_get_plugin(source);
giles's avatar
giles committed
91
        break;
92 93
        case FORMAT_TYPE_GENERIC:
            ret = format_mp3_get_plugin(source);
94
        break;
95
        default:
96 97
        break;
    }
Jack Moffitt's avatar
Jack Moffitt committed
98

Karl Heyes's avatar
Karl Heyes committed
99
    return ret;
100 101
}

Karl Heyes's avatar
Karl Heyes committed
102 103

/* clients need to be start from somewhere in the queue so we will look for
104
 * a refbuf which has been previously marked as a sync point.
Karl Heyes's avatar
Karl Heyes committed
105
 */
106
static void find_client_start(source_t *source, client_t *client)
Karl Heyes's avatar
Karl Heyes committed
107 108 109
{
    refbuf_t *refbuf = source->burst_point;

110
    /* we only want to attempt a burst at connection time, not midstream
111 112 113 114
     * however streams like theora may not have the most recent page marked as
     * a starting point, so look for one from the burst point */
    if (client->intro_offset == -1 && source->stream_data_tail
            && source->stream_data_tail->sync_point)
Karl Heyes's avatar
Karl Heyes committed
115 116 117
        refbuf = source->stream_data_tail;
    else
    {
118
        size_t size = client->intro_offset;
Karl Heyes's avatar
Karl Heyes committed
119
        refbuf = source->burst_point;
120
        while (size > 0 && refbuf && refbuf->next)
Karl Heyes's avatar
Karl Heyes committed
121 122 123 124 125 126 127 128 129 130 131 132
        {
            size -= refbuf->len;
            refbuf = refbuf->next;
        }
    }

    while (refbuf)
    {
        if (refbuf->sync_point)
        {
            client_set_queue (client, refbuf);
            client->check_buffer = format_advance_queue;
133
            client->write_to_client = source->format->write_buf_to_client;
Karl Heyes's avatar
Karl Heyes committed
134 135 136 137 138 139 140 141
            client->intro_offset = -1;
            break;
        }
        refbuf = refbuf->next;
    }
}


142
static int get_file_data(FILE *intro, client_t *client)
Karl Heyes's avatar
Karl Heyes committed
143 144
{
    refbuf_t *refbuf = client->refbuf;
145
    size_t bytes;
Karl Heyes's avatar
Karl Heyes committed
146 147 148 149 150 151 152

    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;

153
    refbuf->len = (unsigned int)bytes;
Karl Heyes's avatar
Karl Heyes committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167
    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)
    {
168
        /* client refers to no data, must be from a move */
169
        if (source->client)
170 171 172 173 174
        {
            find_client_start (source, client);
            return -1;
        }
        /* source -> file fallback, need a refbuf for data */
175
        refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
176 177 178
        client->refbuf = refbuf;
        client->pos = refbuf->len;
        client->intro_offset = 0;
Karl Heyes's avatar
Karl Heyes committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
    }
    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_set_queue (client, NULL);
                find_client_start (source, client);
            }
            else
                client->intro_offset = 0;  /* replay intro file */
            return -1;
        }
    }
    return 0;
}


204 205 206
/* call this to verify that the HTTP data has been sent and if so setup
 * callbacks to the appropriate format functions
 */
207
int format_check_http_buffer(source_t *source, client_t *client)
208 209 210 211 212 213 214 215
{
    refbuf_t *refbuf = client->refbuf;

    if (refbuf == NULL)
        return -1;

    if (client->respcode == 0)
    {
216
        ICECAST_LOG_DEBUG("processing pending client headers");
217 218 219

        if (format_prepare_headers (source, client) < 0)
        {
220
            ICECAST_LOG_ERROR("internal problem, dropping client");
221 222 223
            client->con->error = 1;
            return -1;
        }
Karl Heyes's avatar
Karl Heyes committed
224
        client->respcode = 200;
225 226 227
        stats_event_inc(NULL, "listeners");
        stats_event_inc(NULL, "listener_connections");
        stats_event_inc(source->mount, "listener_connections");
228 229 230 231 232 233 234 235 236 237 238 239 240 241
    }

    if (client->pos == refbuf->len)
    {
        client->write_to_client = source->format->write_buf_to_client;
        client->check_buffer = format_check_file_buffer;
        client->intro_offset = 0;
        client->pos = refbuf->len = 4096;
        return -1;
    }
    return 0;
}


242
int format_generic_write_to_client(client_t *client)
243 244 245 246 247 248
{
    refbuf_t *refbuf = client->refbuf;
    int ret;
    const char *buf = refbuf->data + client->pos;
    unsigned int len = refbuf->len - client->pos;

249
    ret = client_send_bytes(client, buf, len);
250 251 252 253 254 255 256 257

    if (ret > 0)
        client->pos += ret;

    return ret;
}


Karl Heyes's avatar
Karl Heyes committed
258
/* This is the commonly used for source streams, here we just progress to
259
 * the next buffer in the queue if there is no more left to be written from
Karl Heyes's avatar
Karl Heyes committed
260 261
 * the existing buffer.
 */
262
int format_advance_queue(source_t *source, client_t *client)
Karl Heyes's avatar
Karl Heyes committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
{
    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;
}


282 283 284 285 286 287 288 289
/* Prepare headers
 * If any error occurs in this function, return -1
 * Do not send a error to the client using client_send_error
 * here but instead set client->respcode to 500.
 * Else client_send_error will destroy and free the client and all
 * calling functions will use a already freed client struct and
 * cause a segfault!
 */
290
static int format_prepare_headers (source_t *source, client_t *client)
Michael Smith's avatar
Michael Smith committed
291
{
292
    size_t remaining;
293
    char *ptr;
Michael Smith's avatar
Michael Smith committed
294
    int bytes;
295 296 297 298 299 300 301
    int bitrate_filtered = 0;
    avl_node *node;

    remaining = client->refbuf->len;
    ptr = client->refbuf->data;
    client->respcode = 200;

302
    bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
303
    if (bytes < 0) {
304
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
305
        client->respcode = 500;
306
        return -1;
307
    } else if (((size_t)bytes + (size_t)1024U) >= remaining) { /* we don't know yet how much to follow but want at least 1kB free space */
308 309 310 311 312
        void *new_ptr = realloc(ptr, bytes + 1024);
        if (new_ptr) {
            ICECAST_LOG_DEBUG("Client buffer reallocation succeeded.");
            client->refbuf->data = ptr = new_ptr;
            client->refbuf->len = remaining = bytes + 1024;
313
            bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
314 315
            if (bytes == -1 ) {
                ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
316
                client->respcode = 500;
317 318 319 320
                return -1;
            }
        } else {
            ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
321
            client->respcode = 500;
322 323 324
            return -1;
        }
    }
325 326 327

    remaining -= bytes;
    ptr += bytes;
Michael Smith's avatar
Michael Smith committed
328

329 330 331
    /* iterate through source http headers and send to client */
    avl_tree_rlock(source->parser->vars);
    node = avl_get_first(source->parser->vars);
Karl Heyes's avatar
Karl Heyes committed
332 333
    while (node)
    {
334
        int next = 1;
335
        http_var_t *var = (http_var_t *) node->key;
336 337 338
        bytes = 0;
        if (!strcasecmp(var->name, "ice-audio-info"))
        {
Karl Heyes's avatar
Karl Heyes committed
339
            /* convert ice-audio-info to icy-br */
340
            char *brfield = NULL;
Karl Heyes's avatar
Karl Heyes committed
341 342
            unsigned int bitrate;

343 344 345
            if (bitrate_filtered == 0)
                brfield = strstr(var->value, "bitrate=");
            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
346
            {
347 348 349
                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
                next = 0;
                bitrate_filtered = 1;
Karl Heyes's avatar
Karl Heyes committed
350
            }
351 352 353
            else
                /* show ice-audio_info header as well because of relays */
                bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value);
Karl Heyes's avatar
Karl Heyes committed
354 355 356 357 358 359
        }
        else
        {
            if (strcasecmp(var->name, "ice-password") &&
                strcasecmp(var->name, "icy-metaint"))
            {
360 361 362 363
                if (!strcasecmp(var->name, "ice-name"))
                {
                    ice_config_t *config;
                    mount_proxy *mountinfo;
364

365 366
                    config = config_get_config();
                    mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);
367

368 369
                    if (mountinfo && mountinfo->stream_name)
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", mountinfo->stream_name);
370
                    else
371
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", var->value);
372 373

                    config_release_config();
374
                }
375
                else if (!strncasecmp("ice-", var->name, 4))
Karl Heyes's avatar
Karl Heyes committed
376
                {
377 378
                    if (!strcasecmp("ice-public", var->name))
                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
Karl Heyes's avatar
Karl Heyes committed
379
                    else
380 381
                        if (!strcasecmp ("ice-bitrate", var->name))
                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
382
                        else
383 384
                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
                                    var->name + 3, var->value);
Karl Heyes's avatar
Karl Heyes committed
385
                }
386 387 388 389 390 391
                else
                    if (!strncasecmp("icy-", var->name, 4))
                    {
                        bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
                                var->name + 3, var->value);
                    }
Karl Heyes's avatar
Karl Heyes committed
392
            }
393
        }
394 395 396 397 398

        remaining -= bytes;
        ptr += bytes;
        if (next)
            node = avl_get_next(node);
399 400
    }
    avl_tree_unlock(source->parser->vars);
401

402
    bytes = snprintf(ptr, remaining, "\r\n");
403 404 405 406 407
    remaining -= bytes;
    ptr += bytes;

    client->refbuf->len -= remaining;
    if (source->format->create_client_data)
408 409 410 411
        if (source->format->create_client_data (source, client) < 0) {
            ICECAST_LOG_ERROR("Client format header generation failed. "
                "(Likely not enough or wrong source data) Dropping client.");
            client->respcode = 500;
412
            return -1;
413
        }
414
    return 0;
Michael Smith's avatar
Michael Smith committed
415 416
}

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
void format_set_vorbiscomment(format_plugin_t *plugin, const char *tag, const char *value) {
    if (vorbis_comment_query_count(&plugin->vc, tag) != 0) {
        /* delete key */
        /* as libvorbis hides away all the memory functions we need to copy
         * the structure comment by comment. sorry about that...
         */
        vorbis_comment vc;
        int i; /* why does vorbis_comment use int, not size_t? */
        size_t keylen = strlen(tag);

        vorbis_comment_init(&vc);
        /* copy tags */
        for (i = 0; i < plugin->vc.comments; i++) {
            if (strncasecmp(plugin->vc.user_comments[i], tag, keylen) == 0 && plugin->vc.user_comments[i][keylen] == '=')
                continue;
            vorbis_comment_add(&vc, plugin->vc.user_comments[i]);
        }
        /* move vendor */
        vc.vendor = plugin->vc.vendor;
        plugin->vc.vendor = NULL;
        vorbis_comment_clear(&plugin->vc);
        plugin->vc = vc;
    }
    vorbis_comment_add_tag(&plugin->vc, tag, value);
}