format.c 13.7 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"
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;
    }
Karl Heyes's avatar
Karl Heyes committed
98
    if (ret < 0)
99
        stats_event (source->mount, "content-type",
Karl Heyes's avatar
Karl Heyes committed
100
                source->format->contenttype);
Jack Moffitt's avatar
Jack Moffitt committed
101

Karl Heyes's avatar
Karl Heyes committed
102
    return ret;
103 104
}

Karl Heyes's avatar
Karl Heyes committed
105 106

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

113
    /* we only want to attempt a burst at connection time, not midstream
114 115 116 117
     * 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
118 119 120
        refbuf = source->stream_data_tail;
    else
    {
121
        size_t size = client->intro_offset;
Karl Heyes's avatar
Karl Heyes committed
122
        refbuf = source->burst_point;
123
        while (size > 0 && refbuf && refbuf->next)
Karl Heyes's avatar
Karl Heyes committed
124 125 126 127 128 129 130 131 132 133 134 135
        {
            size -= refbuf->len;
            refbuf = refbuf->next;
        }
    }

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


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

    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;

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


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

    if (refbuf == NULL)
        return -1;

    if (client->respcode == 0)
    {
219
        ICECAST_LOG_DEBUG("processing pending client headers");
220 221 222

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

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


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

252
    ret = client_send_bytes(client, buf, len);
253 254 255 256 257 258 259 260

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

    return ret;
}


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


285 286 287 288 289 290 291 292
/* 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!
 */
293
static int format_prepare_headers (source_t *source, client_t *client)
294
{
295
    size_t remaining;
296
    char *ptr;
297
    int bytes;
298 299 300 301 302 303 304
    int bitrate_filtered = 0;
    avl_node *node;

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

305
    bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
306
    if (bytes < 0) {
307
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
308
        client->respcode = 500;
309
        return -1;
310
    } 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 */
311 312 313 314 315
        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;
316
            bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
317 318
            if (bytes == -1 ) {
                ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
319
                client->respcode = 500;
320 321 322 323
                return -1;
            }
        } else {
            ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
324
            client->respcode = 500;
325 326 327
            return -1;
        }
    }
328 329 330

    remaining -= bytes;
    ptr += bytes;
331

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

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

368 369
                    config = config_get_config();
                    mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);
370

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

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

        remaining -= bytes;
        ptr += bytes;
        if (next)
            node = avl_get_next(node);
402 403
    }
    avl_tree_unlock(source->parser->vars);
404

405
    bytes = snprintf(ptr, remaining, "\r\n");
406 407 408 409 410
    remaining -= bytes;
    ptr += bytes;

    client->refbuf->len -= remaining;
    if (source->format->create_client_data)
411 412 413 414
        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;
415
            return -1;
416
        }
417
    return 0;
418 419
}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
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);
}