format_vorbis.c 8.39 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).
 */

Jack Moffitt's avatar
Jack Moffitt committed
13 14 15 16 17 18
/* format_vorbis.c
**
** format plugin for vorbis
**
*/

19 20 21 22
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
23 24 25 26 27 28 29 30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <ogg/ogg.h>
#include <vorbis/codec.h>

#include "refbuf.h"
Michael Smith's avatar
Michael Smith committed
31 32
#include "source.h"
#include "client.h"
Jack Moffitt's avatar
Jack Moffitt committed
33

Michael Smith's avatar
Michael Smith committed
34
#include "stats.h"
Jack Moffitt's avatar
Jack Moffitt committed
35 36
#include "format.h"

37 38 39
#define CATMODULE "format-vorbis"
#include "logging.h"

Michael Smith's avatar
Michael Smith committed
40 41
#define MAX_HEADER_PAGES 10

Jack Moffitt's avatar
Jack Moffitt committed
42 43
typedef struct _vstate_tag
{
44 45 46 47 48 49 50 51 52 53
    ogg_sync_state oy;
    ogg_stream_state os;
    vorbis_info vi;
    vorbis_comment vc;

    ogg_page og;
    unsigned long serialno;
    int header;
    refbuf_t *headbuf[MAX_HEADER_PAGES];
    int packets;
Jack Moffitt's avatar
Jack Moffitt committed
54 55
} vstate_t;

56 57 58 59
static void format_vorbis_free_plugin(format_plugin_t *self);
static int format_vorbis_get_buffer(format_plugin_t *self, char *data, 
        unsigned long len, refbuf_t **buffer);
static refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self);
Michael Smith's avatar
Michael Smith committed
60 61 62 63
static void *format_vorbis_create_client_data(format_plugin_t *self,
        source_t *source, client_t *client);
static void format_vorbis_send_headers(format_plugin_t *self,
        source_t *source, client_t *client);
Jack Moffitt's avatar
Jack Moffitt committed
64 65 66

format_plugin_t *format_vorbis_get_plugin(void)
{
67 68
    format_plugin_t *plugin;
    vstate_t *state;
Jack Moffitt's avatar
Jack Moffitt committed
69

70
    plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
71

72 73 74 75
    plugin->type = FORMAT_TYPE_VORBIS;
    plugin->has_predata = 1;
    plugin->get_buffer = format_vorbis_get_buffer;
    plugin->get_predata = format_vorbis_get_predata;
76 77
    plugin->write_buf_to_client = format_generic_write_buf_to_client;
    plugin->create_client_data = format_vorbis_create_client_data;
Michael Smith's avatar
Michael Smith committed
78
    plugin->client_send_headers = format_vorbis_send_headers;
79
    plugin->free_plugin = format_vorbis_free_plugin;
80
    plugin->format_description = "Ogg Vorbis";
Jack Moffitt's avatar
Jack Moffitt committed
81

82 83
    state = (vstate_t *)calloc(1, sizeof(vstate_t));
    ogg_sync_init(&state->oy);
Jack Moffitt's avatar
Jack Moffitt committed
84

85
    plugin->_state = (void *)state;
Jack Moffitt's avatar
Jack Moffitt committed
86

87
    return plugin;
Jack Moffitt's avatar
Jack Moffitt committed
88 89
}

90 91
void format_vorbis_free_plugin(format_plugin_t *self)
{
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
    int i;
    vstate_t *state = (vstate_t *)self->_state;

    /* free memory associated with this plugin instance */

    /* free state memory */
    ogg_sync_clear(&state->oy);
    ogg_stream_clear(&state->os);
    vorbis_comment_clear(&state->vc);
    vorbis_info_clear(&state->vi);
    
    for (i = 0; i < MAX_HEADER_PAGES; i++) {
        if (state->headbuf[i]) {
            refbuf_release(state->headbuf[i]);
            state->headbuf[i] = NULL;
        }
    }

    free(state);

    /* free the plugin instance */
    free(self);
114 115
}

Michael Smith's avatar
Michael Smith committed
116
int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer)
Jack Moffitt's avatar
Jack Moffitt committed
117
{
118 119 120 121
    char *buf;
    int i, result;
    ogg_packet op;
    char *tag;
122
    refbuf_t *refbuf, *source_refbuf;
123
    vstate_t *state = (vstate_t *)self->_state;
124
    source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
125

126 127 128
    if (data) {
        /* write the data to the buffer */
        buf = ogg_sync_buffer(&state->oy, len);
Michael Smith's avatar
Michael Smith committed
129
        memcpy(buf, data, len);
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
        ogg_sync_wrote(&state->oy, len);
    }

    refbuf = NULL;
    if (ogg_sync_pageout(&state->oy, &state->og) == 1) {
        refbuf = refbuf_new(state->og.header_len + state->og.body_len);
        memcpy(refbuf->data, state->og.header, state->og.header_len);
        memcpy(&refbuf->data[state->og.header_len], state->og.body, state->og.body_len);

        if (state->serialno != ogg_page_serialno(&state->og)) {
            /* this is a new logical bitstream */
            state->header = 0;
            state->packets = 0;

            /* release old headers, stream state, vorbis data */
            for (i = 0; i < MAX_HEADER_PAGES; i++) {
                if (state->headbuf[i]) {
                    refbuf_release(state->headbuf[i]);
                    state->headbuf[i] = NULL;
                }
            }
151
            /* Clear old stuff. Rarely but occasionally needed. */
152 153 154
            ogg_stream_clear(&state->os);
            vorbis_comment_clear(&state->vc);
            vorbis_info_clear(&state->vi);
Jack Moffitt's avatar
Jack Moffitt committed
155

156 157 158 159 160
            state->serialno = ogg_page_serialno(&state->og);
            ogg_stream_init(&state->os, state->serialno);
            vorbis_info_init(&state->vi);
            vorbis_comment_init(&state->vc);
        }
Jack Moffitt's avatar
Jack Moffitt committed
161

162
        if (state->header >= 0) {
Michael Smith's avatar
Michael Smith committed
163 164 165 166
            /* FIXME: In some streams (non-vorbis ogg streams), this could get
             * extras pages beyond the header. We need to collect the pages
             * here anyway, but they may have to be discarded later.
             */
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
            if (ogg_page_granulepos(&state->og) <= 0) {
                state->header++;
            } else {
                /* we're done caching headers */
                state->header = -1;

                /* put known comments in the stats */
                tag = vorbis_comment_query(&state->vc, "TITLE", 0);
                if (tag) stats_event(self->mount, "title", tag);
                else stats_event(self->mount, "title", "unknown");
                tag = vorbis_comment_query(&state->vc, "ARTIST", 0);
                if (tag) stats_event(self->mount, "artist", tag);
                else stats_event(self->mount, "artist", "unknown");

                /* don't need these now */
                ogg_stream_clear(&state->os);
                vorbis_comment_clear(&state->vc);
                vorbis_info_clear(&state->vi);
185 186 187 188

                /* Drain the source queue on metadata update otherwise you
                   could have a mismatch between what is on the source queue
                   and what is in the state->headbuf */
189
                avl_tree_rlock(global.source_tree);
Michael Smith's avatar
Michael Smith committed
190
                source = source_find_mount_raw(self->mount);
191 192
                avl_tree_unlock(global.source_tree);

193 194 195 196 197 198
                thread_mutex_lock(&source->queue_mutex);
                while ((source_refbuf = refbuf_queue_remove(&source->queue))) {
                    refbuf_release(source_refbuf);
                }
                thread_mutex_unlock(&source->queue_mutex);

199
                yp_touch (self->mount);
200 201 202 203 204
            }
        }

        /* cache header pages */
        if (state->header > 0 && state->packets < 3) {
Michael Smith's avatar
Michael Smith committed
205 206
            if(state->header > MAX_HEADER_PAGES) {
                refbuf_release(refbuf);
207 208
                ERROR1("Bad vorbis input: header is more than %d pages long", MAX_HEADER_PAGES);

Michael Smith's avatar
Michael Smith committed
209 210
                return -1;
            }
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
            refbuf_addref(refbuf);
            state->headbuf[state->header - 1] = refbuf;

            if (state->packets >= 0 && state->packets < 3) {
                ogg_stream_pagein(&state->os, &state->og);
                while (state->packets < 3) {
                    result = ogg_stream_packetout(&state->os, &op);
                    if (result == 0) break; /* need more data */
                    if (result < 0) {
                        state->packets = -1;
                        break;
                    }

                    state->packets++;

                    if (vorbis_synthesis_headerin(&state->vi, &state->vc, &op) < 0) {
                        state->packets = -1;
                        break;
                    }
                }
            }
        }
    }
Jack Moffitt's avatar
Jack Moffitt committed
234

Michael Smith's avatar
Michael Smith committed
235
    *buffer = refbuf;
236
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
237 238 239 240
}

refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self)
{
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    refbuf_queue_t *queue;
    int i;
    vstate_t *state = (vstate_t *)self->_state;

    queue = NULL;
    for (i = 0; i < MAX_HEADER_PAGES; i++) {
        if (state->headbuf[i]) {
            refbuf_addref(state->headbuf[i]);
            refbuf_queue_add(&queue, state->headbuf[i]);
        } else {
            break;
        }
    }

    return queue;
Jack Moffitt's avatar
Jack Moffitt committed
256 257
}

Michael Smith's avatar
Michael Smith committed
258 259 260
static void *format_vorbis_create_client_data(format_plugin_t *self,
        source_t *source, client_t *client) 
{
261 262
    return NULL;
}
Jack Moffitt's avatar
Jack Moffitt committed
263

Michael Smith's avatar
Michael Smith committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
static void format_vorbis_send_headers(format_plugin_t *self,
        source_t *source, client_t *client)
{
    int bytes;
    
    client->respcode = 200;
    bytes = sock_write(client->con->sock, 
            "HTTP/1.0 200 OK\r\n" 
            "Content-Type: %s\r\n", 
            format_get_mimetype(source->format->type));

    if(bytes > 0) client->con->sent_bytes += bytes;

    format_send_general_headers(self, source, client);
}

Jack Moffitt's avatar
Jack Moffitt committed
280