format.c 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      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 15 16 17 18 19
/* format.c
**
** format plugin implementation
**
*/

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 35

#include "connection.h"
#include "refbuf.h"

Michael Smith's avatar
Michael Smith committed
36
#include "source.h"
Jack Moffitt's avatar
Jack Moffitt committed
37
#include "format.h"
38
#include "global.h"
Karl Heyes's avatar
Karl Heyes committed
39
#include "httpp/httpp.h"
Jack Moffitt's avatar
Jack Moffitt committed
40

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

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

#ifdef WIN32
#define strcasecmp stricmp
#define strncasecmp strnicmp
52
#define snprintf _snprintf
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
53
#endif
54

55 56 57
static int format_prepare_headers (source_t *source, client_t *client);


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

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

88
    switch (type) {
89 90
    case FORMAT_TYPE_OGG:
        ret = format_ogg_get_plugin (source);
91
        break;
92 93 94
	case FORMAT_TYPE_EBML:
		ret = format_ebml_get_plugin (source);
		break;
95
    case FORMAT_TYPE_GENERIC:
96 97
        ret = format_mp3_get_plugin (source);
        break;
98 99 100
    default:
        break;
    }
Karl Heyes's avatar
Karl Heyes committed
101 102 103
    if (ret < 0)
        stats_event (source->mount, "content-type", 
                source->format->contenttype);
Jack Moffitt's avatar
Jack Moffitt committed
104

Karl Heyes's avatar
Karl Heyes committed
105
    return ret;
106 107
}

Karl Heyes's avatar
Karl Heyes committed
108 109 110 111 112 113 114 115

/* clients need to be start from somewhere in the queue so we will look for
 * a refbuf which has been previously marked as a sync point. 
 */
static void find_client_start (source_t *source, client_t *client)
{
    refbuf_t *refbuf = source->burst_point;

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

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


static int get_file_data (FILE *intro, client_t *client)
{
    refbuf_t *refbuf = client->refbuf;
151
    size_t bytes;
Karl Heyes's avatar
Karl Heyes committed
152 153 154 155 156 157 158

    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;

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


210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
/* call this to verify that the HTTP data has been sent and if so setup
 * callbacks to the appropriate format functions
 */
int format_check_http_buffer (source_t *source, client_t *client)
{
    refbuf_t *refbuf = client->refbuf;

    if (refbuf == NULL)
        return -1;

    if (client->respcode == 0)
    {
        DEBUG0("processing pending client headers");

        if (format_prepare_headers (source, client) < 0)
        {
            ERROR0 ("internal problem, dropping client");
            client->con->error = 1;
            return -1;
        }
Karl Heyes's avatar
Karl Heyes committed
230
        client->respcode = 200;
Karl Heyes's avatar
Karl Heyes committed
231
        stats_event_inc (NULL, "listeners");
232 233
        stats_event_inc (NULL, "listener_connections");
        stats_event_inc (source->mount, "listener_connections");
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    }

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


int format_generic_write_to_client (client_t *client)
{
    refbuf_t *refbuf = client->refbuf;
    int ret;
    const char *buf = refbuf->data + client->pos;
    unsigned int len = refbuf->len - client->pos;

    ret = client_send_bytes (client, buf, len);

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

    return ret;
}


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 283 284 285 286 287
/* This is the commonly used for source streams, here we just progress to
 * the next buffer in the queue if there is no more left to be written from 
 * the existing buffer.
 */
int format_advance_queue (source_t *source, client_t *client)
{
    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;
}


288
static int format_prepare_headers (source_t *source, client_t *client)
Michael Smith's avatar
Michael Smith committed
289
{
290 291
    unsigned remaining;
    char *ptr;
Michael Smith's avatar
Michael Smith committed
292
    int bytes;
293 294
    int bitrate_filtered = 0;
    avl_node *node;
295
    ice_config_t *config;
296 297 298 299 300 301 302 303 304 305

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

    bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
            "Content-Type: %s\r\n", source->format->contenttype);

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

307 308 309
    /* 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
310 311
    while (node)
    {
312 313 314 315 316
        int next = 1;
        http_var_t *var = (http_var_t *)node->key;
        bytes = 0;
        if (!strcasecmp(var->name, "ice-audio-info"))
        {
Karl Heyes's avatar
Karl Heyes committed
317
            /* convert ice-audio-info to icy-br */
318
            char *brfield = NULL;
Karl Heyes's avatar
Karl Heyes committed
319 320
            unsigned int bitrate;

321 322 323 324 325 326 327
            if (bitrate_filtered == 0)
                brfield = strstr(var->value, "bitrate=");
            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
            {           
                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
                next = 0;
                bitrate_filtered = 1;
Karl Heyes's avatar
Karl Heyes committed
328
            }
329 330 331
            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
332 333 334 335 336 337 338 339
        }
        else
        {
            if (strcasecmp(var->name, "ice-password") &&
                strcasecmp(var->name, "icy-metaint"))
            {
                if (!strncasecmp("ice-", var->name, 4))
                {
340 341
                    if (!strcasecmp("ice-public", var->name))
                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
Karl Heyes's avatar
Karl Heyes committed
342
                    else
343 344
                        if (!strcasecmp ("ice-bitrate", var->name))
                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
345
                        else
346 347
                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
                                    var->name + 3, var->value);
Karl Heyes's avatar
Karl Heyes committed
348
                }
349 350 351 352 353 354
                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
355
            }
356
        }
357 358 359 360 361

        remaining -= bytes;
        ptr += bytes;
        if (next)
            node = avl_get_next(node);
362 363
    }
    avl_tree_unlock(source->parser->vars);
364

365 366 367
    config = config_get_config();
    bytes = snprintf (ptr, remaining, "Server: %s\r\n", config->server_id);
    config_release_config();
368 369 370
    remaining -= bytes;
    ptr += bytes;

371 372 373 374 375
    /* prevent proxy servers from caching */
    bytes = snprintf (ptr, remaining, "Cache-Control: no-cache\r\n");
    remaining -= bytes;
    ptr += bytes;

376 377 378 379 380 381 382 383 384
    bytes = snprintf (ptr, remaining, "\r\n");
    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
385 386
}

387