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