format.c 14.8 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"

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

81
int format_get_plugin(format_type_t type, source_t *source)
Jack Moffitt's avatar
Jack Moffitt committed
82
{
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

99
    return ret;
100 101
}

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.
105
 */
106
static void find_client_start(source_t *source, client_t *client)
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)
115 116 117
        refbuf = source->stream_data_tail;
    else
    {
118
        size_t size = client->intro_offset;
119
        refbuf = source->burst_point;
120
        while (size > 0 && refbuf && refbuf->next)
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;
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)
143 144
{
    refbuf_t *refbuf = client->refbuf;
145
    size_t bytes;
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;
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;
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;
}


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
260 261
 * the existing buffer.
 */
262
int format_advance_queue(source_t *source, client_t *client)
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!
 */
Philipp Schafft's avatar
Philipp Schafft committed
290 291 292 293 294 295 296 297
static inline ssize_t __print_var(char *str, size_t remaining, const char *format, const char *first, const http_var_t *var)
{
    size_t i;
    ssize_t done = 0;
    int ret;

    for (i = 0; i < var->values; i++) {
        ret = snprintf(str + done, remaining - done, format, first, var->value[i]);
298
        if (ret <= 0 || (size_t)ret >= (remaining - done))
Philipp Schafft's avatar
Philipp Schafft committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
            return -1;

        done += ret;
    }

    return done;
}

static inline const char *__find_bitrate(const http_var_t *var)
{
    size_t i;
    const char *ret;

    for (i = 0; i < var->values; i++) {
        ret = strstr(var->value[i], "bitrate=");
        if (ret)
            return ret;
    }

    return NULL;
}

321
static int format_prepare_headers (source_t *source, client_t *client)
322
{
323
    size_t remaining;
324
    char *ptr;
325
    int bytes;
326 327 328 329 330 331 332
    int bitrate_filtered = 0;
    avl_node *node;

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

333
    bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
334
    if (bytes <= 0) {
335
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
336
        client->respcode = 500;
337
        return -1;
338
    } 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 */
339 340 341 342 343
        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;
344
            bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
345
            if (bytes <= 0 || (size_t)bytes >= remaining) {
346
                ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
347
                client->respcode = 500;
348 349 350 351
                return -1;
            }
        } else {
            ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
352
            client->respcode = 500;
353 354 355
            return -1;
        }
    }
356

357 358 359 360 361
    if (bytes <= 0 || (size_t)bytes >= remaining) {
        ICECAST_LOG_ERROR("Can not allocate headers for client %p", client);
        client->respcode = 500;
        return -1;
    }
362 363
    remaining -= bytes;
    ptr += bytes;
364

365 366 367
    /* iterate through source http headers and send to client */
    avl_tree_rlock(source->parser->vars);
    node = avl_get_first(source->parser->vars);
368 369
    while (node)
    {
370
        int next = 1;
371
        http_var_t *var = (http_var_t *) node->key;
372 373 374
        bytes = 0;
        if (!strcasecmp(var->name, "ice-audio-info"))
        {
375
            /* convert ice-audio-info to icy-br */
Philipp Schafft's avatar
Philipp Schafft committed
376
            const char *brfield = NULL;
377 378
            unsigned int bitrate;

379
            if (bitrate_filtered == 0)
Philipp Schafft's avatar
Philipp Schafft committed
380
                brfield = __find_bitrate(var);
381
            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
382
            {
383 384 385
                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
                next = 0;
                bitrate_filtered = 1;
386
            }
387 388
            else
                /* show ice-audio_info header as well because of relays */
Philipp Schafft's avatar
Philipp Schafft committed
389
                bytes = __print_var(ptr, remaining, "%s: %s\r\n", var->name, var);
390 391 392 393 394 395
        }
        else
        {
            if (strcasecmp(var->name, "ice-password") &&
                strcasecmp(var->name, "icy-metaint"))
            {
396 397 398 399
                if (!strcasecmp(var->name, "ice-name"))
                {
                    ice_config_t *config;
                    mount_proxy *mountinfo;
400

401 402
                    config = config_get_config();
                    mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);
403

404 405
                    if (mountinfo && mountinfo->stream_name)
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", mountinfo->stream_name);
406
                    else
Philipp Schafft's avatar
Philipp Schafft committed
407
                        bytes = __print_var(ptr, remaining, "icy-%s:%s\r\n", "name", var);
408 409

                    config_release_config();
410
                }
411
                else if (!strncasecmp("ice-", var->name, 4))
412
                {
413
                    if (!strcasecmp("ice-public", var->name))
Philipp Schafft's avatar
Philipp Schafft committed
414
                        bytes = __print_var(ptr, remaining, "icy-%s:%s\r\n", "pub", var);
415
                    else
416
                        if (!strcasecmp ("ice-bitrate", var->name))
Philipp Schafft's avatar
Philipp Schafft committed
417
                            bytes = __print_var(ptr, remaining, "icy-%s:%s\r\n", "br", var);
418
                        else
Philipp Schafft's avatar
Philipp Schafft committed
419
                            bytes = __print_var(ptr, remaining, "icy%s:%s\r\n", var->name + 3, var);
420
                }
421 422 423
                else
                    if (!strncasecmp("icy-", var->name, 4))
                    {
Philipp Schafft's avatar
Philipp Schafft committed
424
                        bytes = __print_var(ptr, remaining, "icy%s:%s\r\n", var->name + 3, var);
425
                    }
426
            }
427
        }
428

429 430 431 432 433 434 435
        if (bytes < 0 || (size_t)bytes >= remaining) {
            avl_tree_unlock(source->parser->vars);
            ICECAST_LOG_ERROR("Can not allocate headers for client %p", client);
            client->respcode = 500;
            return -1;
        }

436 437 438 439
        remaining -= bytes;
        ptr += bytes;
        if (next)
            node = avl_get_next(node);
440 441
    }
    avl_tree_unlock(source->parser->vars);
442

443
    bytes = snprintf(ptr, remaining, "\r\n");
444 445 446 447 448
    if (bytes <= 0 || (size_t)bytes >= remaining) {
        ICECAST_LOG_ERROR("Can not allocate headers for client %p", client);
        client->respcode = 500;
        return -1;
    }
449 450 451 452 453
    remaining -= bytes;
    ptr += bytes;

    client->refbuf->len -= remaining;
    if (source->format->create_client_data)
454 455 456 457
        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;
458
            return -1;
459
        }
460
    return 0;
461 462
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
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);
}