format_vorbis.c 6.32 KB
Newer Older
Jack Moffitt's avatar
Jack Moffitt committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* format_vorbis.c
**
** format plugin for vorbis
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#include "refbuf.h"
15 16
#include "source.h"
#include "client.h"
Jack Moffitt's avatar
Jack Moffitt committed
17

Michael Smith's avatar
Michael Smith committed
18
#include "stats.h"
Jack Moffitt's avatar
Jack Moffitt committed
19 20
#include "format.h"

21 22 23 24
#define CATMODULE "format-vorbis"
#include "log.h"
#include "logging.h"

Michael Smith's avatar
Michael Smith committed
25 26
#define MAX_HEADER_PAGES 10

Jack Moffitt's avatar
Jack Moffitt committed
27 28 29
typedef struct _vstate_tag
{
	ogg_sync_state oy;
Jack Moffitt's avatar
Jack Moffitt committed
30 31 32 33
	ogg_stream_state os;
	vorbis_info vi;
	vorbis_comment vc;

Jack Moffitt's avatar
Jack Moffitt committed
34 35 36
	ogg_page og;
	unsigned long serialno;
	int header;
Michael Smith's avatar
Michael Smith committed
37
	refbuf_t *headbuf[MAX_HEADER_PAGES];
Jack Moffitt's avatar
Jack Moffitt committed
38
	int packets;
Jack Moffitt's avatar
Jack Moffitt committed
39 40
} vstate_t;

41 42 43 44
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);
45 46 47 48
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
49 50 51 52 53 54 55

format_plugin_t *format_vorbis_get_plugin(void)
{
	format_plugin_t *plugin;
	vstate_t *state;

	plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
56

Jack Moffitt's avatar
Jack Moffitt committed
57 58 59 60
	plugin->type = FORMAT_TYPE_VORBIS;
	plugin->has_predata = 1;
	plugin->get_buffer = format_vorbis_get_buffer;
	plugin->get_predata = format_vorbis_get_predata;
61 62
    plugin->write_buf_to_client = format_generic_write_buf_to_client;
    plugin->create_client_data = format_vorbis_create_client_data;
63
    plugin->client_send_headers = format_vorbis_send_headers;
64
	plugin->free_plugin = format_vorbis_free_plugin;
65
    plugin->format_description = "Ogg Vorbis";
Jack Moffitt's avatar
Jack Moffitt committed
66 67 68 69 70 71 72 73 74

	state = (vstate_t *)calloc(1, sizeof(vstate_t));
	ogg_sync_init(&state->oy);

	plugin->_state = (void *)state;

	return plugin;
}

75 76 77 78 79 80 81 82 83
void format_vorbis_free_plugin(format_plugin_t *self)
{
	int i;
	vstate_t *state = (vstate_t *)self->_state;

	/* free memory associated with this plugin instance */

	/* free state memory */
	ogg_sync_clear(&state->oy);
Jack Moffitt's avatar
Jack Moffitt committed
84 85 86
	ogg_stream_clear(&state->os);
	vorbis_comment_clear(&state->vc);
	vorbis_info_clear(&state->vi);
87
	
Michael Smith's avatar
Michael Smith committed
88
	for (i = 0; i < MAX_HEADER_PAGES; i++) {
89 90 91 92 93 94 95 96 97 98 99 100
		if (state->headbuf[i]) {
			refbuf_release(state->headbuf[i]);
			state->headbuf[i] = NULL;
		}
	}

	free(state);

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

Michael Smith's avatar
Michael Smith committed
101
int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer)
Jack Moffitt's avatar
Jack Moffitt committed
102
{
Michael Smith's avatar
Michael Smith committed
103
	char *buf;
Jack Moffitt's avatar
Jack Moffitt committed
104 105 106
	int i, result;
	ogg_packet op;
	char *tag;
Michael Smith's avatar
Michael Smith committed
107
    refbuf_t *refbuf;
Jack Moffitt's avatar
Jack Moffitt committed
108 109 110 111
	vstate_t *state = (vstate_t *)self->_state;

	if (data) {
		/* write the data to the buffer */
Michael Smith's avatar
Michael Smith committed
112 113
		buf = ogg_sync_buffer(&state->oy, len);
        memcpy(buf, data, len);
Jack Moffitt's avatar
Jack Moffitt committed
114 115 116 117 118 119 120 121 122 123 124 125
		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;
Jack Moffitt's avatar
Jack Moffitt committed
126 127 128
			state->packets = 0;

			/* release old headers, stream state, vorbis data */
Michael Smith's avatar
Michael Smith committed
129
			for (i = 0; i < MAX_HEADER_PAGES; i++) {
Jack Moffitt's avatar
Jack Moffitt committed
130 131 132 133 134
				if (state->headbuf[i]) {
					refbuf_release(state->headbuf[i]);
					state->headbuf[i] = NULL;
				}
			}
135 136 137 138
            /* Clear old stuff. Rarely but occasionally needed. */
			ogg_stream_clear(&state->os);
			vorbis_comment_clear(&state->vc);
			vorbis_info_clear(&state->vi);
Jack Moffitt's avatar
Jack Moffitt committed
139

Jack Moffitt's avatar
Jack Moffitt committed
140
			state->serialno = ogg_page_serialno(&state->og);
Jack Moffitt's avatar
Jack Moffitt committed
141 142 143
			ogg_stream_init(&state->os, state->serialno);
			vorbis_info_init(&state->vi);
			vorbis_comment_init(&state->vc);
Jack Moffitt's avatar
Jack Moffitt committed
144 145 146
		}

		if (state->header >= 0) {
Michael Smith's avatar
Michael Smith committed
147 148 149 150 151
            /* 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.
             */
			if (ogg_page_granulepos(&state->og) <= 0) {
Jack Moffitt's avatar
Jack Moffitt committed
152 153
				state->header++;
			} else {
Jack Moffitt's avatar
Jack Moffitt committed
154 155 156 157 158
				/* we're done caching headers */
				state->header = -1;

				/* put known comments in the stats */
				tag = vorbis_comment_query(&state->vc, "TITLE", 0);
159 160
				if (tag) stats_event(self->mount, "title", tag);
				else stats_event(self->mount, "title", "unknown");
Jack Moffitt's avatar
Jack Moffitt committed
161
				tag = vorbis_comment_query(&state->vc, "ARTIST", 0);
162 163
				if (tag) stats_event(self->mount, "artist", tag);
				else stats_event(self->mount, "artist", "unknown");
Jack Moffitt's avatar
Jack Moffitt committed
164 165 166 167 168

				/* don't need these now */
				ogg_stream_clear(&state->os);
				vorbis_comment_clear(&state->vc);
				vorbis_info_clear(&state->vi);
Jack Moffitt's avatar
Jack Moffitt committed
169 170 171
			}
		}

Jack Moffitt's avatar
Jack Moffitt committed
172
		/* cache header pages */
173
		if (state->header > 0 && state->packets < 3) {
Michael Smith's avatar
Michael Smith committed
174 175
            if(state->header > MAX_HEADER_PAGES) {
                refbuf_release(refbuf);
176 177
                ERROR1("Bad vorbis input: header is more than %d pages long", MAX_HEADER_PAGES);

Michael Smith's avatar
Michael Smith committed
178 179
                return -1;
            }
Jack Moffitt's avatar
Jack Moffitt committed
180 181
			refbuf_addref(refbuf);
			state->headbuf[state->header - 1] = refbuf;
Jack Moffitt's avatar
Jack Moffitt committed
182

183
			if (state->packets >= 0 && state->packets < 3) {
Jack Moffitt's avatar
Jack Moffitt committed
184
				ogg_stream_pagein(&state->os, &state->og);
185
				while (state->packets < 3) {
Jack Moffitt's avatar
Jack Moffitt committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
					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
201 202 203
		}
	}

Michael Smith's avatar
Michael Smith committed
204 205
    *buffer = refbuf;
	return 0;
Jack Moffitt's avatar
Jack Moffitt committed
206 207 208 209 210 211 212 213 214
}

refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self)
{
	refbuf_queue_t *queue;
	int i;
	vstate_t *state = (vstate_t *)self->_state;

	queue = NULL;
Michael Smith's avatar
Michael Smith committed
215
	for (i = 0; i < MAX_HEADER_PAGES; i++) {
Jack Moffitt's avatar
Jack Moffitt committed
216 217 218 219 220 221 222 223 224 225 226
		if (state->headbuf[i]) {
			refbuf_addref(state->headbuf[i]);
			refbuf_queue_add(&queue, state->headbuf[i]);
		} else {
			break;
		}
	}

	return queue;
}

227 228 229
static void *format_vorbis_create_client_data(format_plugin_t *self,
        source_t *source, client_t *client) 
{
230 231
    return NULL;
}
Jack Moffitt's avatar
Jack Moffitt committed
232

233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
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
249