format.c 14.9 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
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
11
 * Copyright 2012-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12
13
 */

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

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

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

34
35
#include <vorbis/codec.h>

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

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

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

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

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


54
format_type_t format_get_type (const char *contenttype)
Michael Smith's avatar
Michael Smith committed
55
56
{
    if(strcmp(contenttype, "application/x-ogg") == 0)
57
        return FORMAT_TYPE_OGG; /* Backwards compatibility */
58
    else if(strcmp(contenttype, "application/ogg") == 0)
59
        return FORMAT_TYPE_OGG; /* Now blessed by IANA */
Karl Heyes's avatar
Karl Heyes committed
60
61
62
63
    else if(strcmp(contenttype, "audio/ogg") == 0)
        return FORMAT_TYPE_OGG;
    else if(strcmp(contenttype, "video/ogg") == 0)
        return FORMAT_TYPE_OGG;
64
65
66
67
68
69
70
71
72
73
    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;
74
    else
75
        /* We default to the Generic format handler, which
76
           can handle many more formats than just mp3.
77
78
           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);
79
        return FORMAT_TYPE_GENERIC;
80
81
}

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

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

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

Karl Heyes's avatar
Karl Heyes committed
103
104

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

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

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


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

    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;

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


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

    if (refbuf == NULL)
        return -1;

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

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

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


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

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

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

    return ret;
}


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


283
284
285
286
287
288
289
290
/* 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
291
292
293
294
295
296
297
298
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]);
299
        if (ret <= 0 || (size_t)ret >= (remaining - done))
Philipp Schafft's avatar
Philipp Schafft committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
            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;
}

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

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

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

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

366
367
368
    /* 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
369
370
    while (node)
    {
371
        int next = 1;
372
        http_var_t *var = (http_var_t *) node->key;
373
374
375
        bytes = 0;
        if (!strcasecmp(var->name, "ice-audio-info"))
        {
Karl Heyes's avatar
Karl Heyes committed
376
            /* convert ice-audio-info to icy-br */
Philipp Schafft's avatar
Philipp Schafft committed
377
            const char *brfield = NULL;
Karl Heyes's avatar
Karl Heyes committed
378
379
            unsigned int bitrate;

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

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

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

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

430
431
432
433
434
435
436
        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;
        }

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

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

    client->refbuf->len -= remaining;
    if (source->format->create_client_data)
455
456
457
458
        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;
459
            return -1;
460
        }
461
    return 0;
Michael Smith's avatar
Michael Smith committed
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
488
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);
}