format.c 13.3 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;
    }
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
static int format_prepare_headers (source_t *source, client_t *client)
Michael Smith's avatar
Michael Smith committed
286
{
287
288
    unsigned remaining;
    char *ptr;
Michael Smith's avatar
Michael Smith committed
289
    int bytes;
290
291
292
293
294
295
296
    int bitrate_filtered = 0;
    avl_node *node;

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

297
    bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
298
299
    if (bytes == -1) {
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
300
        client_send_error(client, 500, 0, "Header generation failed.");
301
302
303
304
305
306
307
        return -1;
    } else if ((bytes + 1024) >= remaining) { /* we don't know yet how much to follow but want at least 1kB free space */
        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;
308
            bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
309
310
            if (bytes == -1 ) {
                ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
311
                client_send_error(client, 500, 0, "Header generation failed.");
312
313
314
315
                return -1;
            }
        } else {
            ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
316
            client_send_error(client, 500, 0, "Buffer reallocation failed.");
317
318
319
            return -1;
        }
    }
320
321
322

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

324
325
326
    /* 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
327
328
    while (node)
    {
329
        int next = 1;
330
        http_var_t *var = (http_var_t *) node->key;
331
332
333
        bytes = 0;
        if (!strcasecmp(var->name, "ice-audio-info"))
        {
Karl Heyes's avatar
Karl Heyes committed
334
            /* convert ice-audio-info to icy-br */
335
            char *brfield = NULL;
Karl Heyes's avatar
Karl Heyes committed
336
337
            unsigned int bitrate;

338
339
340
            if (bitrate_filtered == 0)
                brfield = strstr(var->value, "bitrate=");
            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
341
            {
342
343
344
                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
                next = 0;
                bitrate_filtered = 1;
Karl Heyes's avatar
Karl Heyes committed
345
            }
346
347
348
            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
349
350
351
352
353
354
        }
        else
        {
            if (strcasecmp(var->name, "ice-password") &&
                strcasecmp(var->name, "icy-metaint"))
            {
355
356
357
358
                if (!strcasecmp(var->name, "ice-name"))
                {
                    ice_config_t *config;
                    mount_proxy *mountinfo;
359

360
361
                    config = config_get_config();
                    mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);
362

363
364
                    if (mountinfo && mountinfo->stream_name)
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", mountinfo->stream_name);
365
                    else
366
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", var->value);
367
368

                    config_release_config();
369
                }
370
                else if (!strncasecmp("ice-", var->name, 4))
Karl Heyes's avatar
Karl Heyes committed
371
                {
372
373
                    if (!strcasecmp("ice-public", var->name))
                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
Karl Heyes's avatar
Karl Heyes committed
374
                    else
375
376
                        if (!strcasecmp ("ice-bitrate", var->name))
                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
377
                        else
378
379
                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
                                    var->name + 3, var->value);
Karl Heyes's avatar
Karl Heyes committed
380
                }
381
382
383
384
385
386
                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
387
            }
388
        }
389
390
391
392
393

        remaining -= bytes;
        ptr += bytes;
        if (next)
            node = avl_get_next(node);
394
395
    }
    avl_tree_unlock(source->parser->vars);
396

397
    bytes = snprintf(ptr, remaining, "\r\n");
398
399
400
401
402
403
404
405
    remaining -= bytes;
    ptr += bytes;

    client->refbuf->len -= remaining;
    if (source->format->create_client_data)
        if (source->format->create_client_data (source, client) < 0)
            return -1;
    return 0;
Michael Smith's avatar
Michael Smith committed
406
407
}

408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
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);
}