format_vorbis.c 8.02 KB
Newer Older
Jack Moffitt's avatar
Jack Moffitt committed
1 2 3 4 5 6
/* format_vorbis.c
**
** format plugin for vorbis
**
*/

7 8 9 10
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
11 12 13 14 15 16 17 18
#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
19 20
#include "source.h"
#include "client.h"
Jack Moffitt's avatar
Jack Moffitt committed
21

Michael Smith's avatar
Michael Smith committed
22
#include "stats.h"
Jack Moffitt's avatar
Jack Moffitt committed
23 24
#include "format.h"

25 26 27
#define CATMODULE "format-vorbis"
#include "logging.h"

Michael Smith's avatar
Michael Smith committed
28 29
#define MAX_HEADER_PAGES 10

Jack Moffitt's avatar
Jack Moffitt committed
30 31
typedef struct _vstate_tag
{
32 33 34 35 36 37 38 39 40 41
    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
42 43
} vstate_t;

44 45 46 47
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
48 49 50 51
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
52 53 54

format_plugin_t *format_vorbis_get_plugin(void)
{
55 56
    format_plugin_t *plugin;
    vstate_t *state;
Jack Moffitt's avatar
Jack Moffitt committed
57

58
    plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
59

60 61 62 63
    plugin->type = FORMAT_TYPE_VORBIS;
    plugin->has_predata = 1;
    plugin->get_buffer = format_vorbis_get_buffer;
    plugin->get_predata = format_vorbis_get_predata;
64 65
    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
66
    plugin->client_send_headers = format_vorbis_send_headers;
67
    plugin->free_plugin = format_vorbis_free_plugin;
68
    plugin->format_description = "Ogg Vorbis";
Jack Moffitt's avatar
Jack Moffitt committed
69

70 71
    state = (vstate_t *)calloc(1, sizeof(vstate_t));
    ogg_sync_init(&state->oy);
Jack Moffitt's avatar
Jack Moffitt committed
72

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

75
    return plugin;
Jack Moffitt's avatar
Jack Moffitt committed
76 77
}

78 79
void format_vorbis_free_plugin(format_plugin_t *self)
{
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
    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);
102 103
}

Michael Smith's avatar
Michael Smith committed
104
int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer)
Jack Moffitt's avatar
Jack Moffitt committed
105
{
106 107 108 109
    char *buf;
    int i, result;
    ogg_packet op;
    char *tag;
Michael Smith's avatar
Michael Smith committed
110
    refbuf_t *refbuf;
111
    vstate_t *state = (vstate_t *)self->_state;
112 113 114 115
#ifdef USE_YP
    source_t *source;
    time_t current_time;
#endif
Jack Moffitt's avatar
Jack Moffitt committed
116

117 118 119
    if (data) {
        /* write the data to the buffer */
        buf = ogg_sync_buffer(&state->oy, len);
Michael Smith's avatar
Michael Smith committed
120
        memcpy(buf, data, len);
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
        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;
                }
            }
142
            /* Clear old stuff. Rarely but occasionally needed. */
143 144 145
            ogg_stream_clear(&state->os);
            vorbis_comment_clear(&state->vc);
            vorbis_info_clear(&state->vi);
Jack Moffitt's avatar
Jack Moffitt committed
146

147 148 149 150 151
            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
152

153
        if (state->header >= 0) {
Michael Smith's avatar
Michael Smith committed
154 155 156 157
            /* 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.
             */
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
            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);
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
#ifdef USE_YP
                /* If we get an update on the mountpoint, force a
                   yp touch */
                avl_tree_rlock(global.source_tree);
                source = source_find_mount(self->mount);
                avl_tree_unlock(global.source_tree);

                if (source) {
                    /* If we get an update on the mountpoint, force a
                       yp touch */
                    current_time = time(NULL);
                    for (i=0; i<source->num_yp_directories; i++) {
                        source->ypdata[i]->yp_last_touch = current_time -
                            source->ypdata[i]->yp_touch_interval + 2;
                    }
                }
#endif

194 195 196 197 198
            }
        }

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

Michael Smith's avatar
Michael Smith committed
203 204
                return -1;
            }
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
            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
228

Michael Smith's avatar
Michael Smith committed
229
    *buffer = refbuf;
230
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
231 232 233 234
}

refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self)
{
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    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
250 251
}

Michael Smith's avatar
Michael Smith committed
252 253 254
static void *format_vorbis_create_client_data(format_plugin_t *self,
        source_t *source, client_t *client) 
{
255 256
    return NULL;
}
Jack Moffitt's avatar
Jack Moffitt committed
257

Michael Smith's avatar
Michael Smith committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
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
274