format_vorbis.c 4.66 KB
Newer Older
Jack Moffitt's avatar
Jack Moffitt committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* 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"

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

typedef struct _vstate_tag
{
	ogg_sync_state oy;
Jack Moffitt's avatar
Jack Moffitt committed
22 23 24 25
	ogg_stream_state os;
	vorbis_info vi;
	vorbis_comment vc;

Jack Moffitt's avatar
Jack Moffitt committed
26 27 28 29
	ogg_page og;
	unsigned long serialno;
	int header;
	refbuf_t *headbuf[10];
Jack Moffitt's avatar
Jack Moffitt committed
30
	int packets;
Jack Moffitt's avatar
Jack Moffitt committed
31 32
} vstate_t;

33
void format_vorbis_free_plugin(format_plugin_t *self);
Jack Moffitt's avatar
Jack Moffitt committed
34 35 36 37 38 39 40 41 42
refbuf_t *format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len);
refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self);

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

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

Jack Moffitt's avatar
Jack Moffitt committed
44 45 46 47
	plugin->type = FORMAT_TYPE_VORBIS;
	plugin->has_predata = 1;
	plugin->get_buffer = format_vorbis_get_buffer;
	plugin->get_predata = format_vorbis_get_predata;
48
	plugin->free_plugin = format_vorbis_free_plugin;
Jack Moffitt's avatar
Jack Moffitt committed
49 50 51 52 53 54 55 56 57

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

	plugin->_state = (void *)state;

	return plugin;
}

58 59 60 61 62 63 64 65 66
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
67 68 69
	ogg_stream_clear(&state->os);
	vorbis_comment_clear(&state->vc);
	vorbis_info_clear(&state->vi);
70 71 72 73 74 75 76 77 78 79 80 81 82 83
	
	for (i = 0; i < 10; i++) {
		if (state->headbuf[i]) {
			refbuf_release(state->headbuf[i]);
			state->headbuf[i] = NULL;
		}
	}

	free(state);

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

Jack Moffitt's avatar
Jack Moffitt committed
84 85 86 87
refbuf_t *format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len)
{
	char *buffer;
	refbuf_t *refbuf;
Jack Moffitt's avatar
Jack Moffitt committed
88 89 90
	int i, result;
	ogg_packet op;
	char *tag;
Jack Moffitt's avatar
Jack Moffitt committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	vstate_t *state = (vstate_t *)self->_state;

	if (data) {
		/* write the data to the buffer */
		buffer = ogg_sync_buffer(&state->oy, len);
	        memcpy(buffer, data, len);
		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
109 110 111
			state->packets = 0;

			/* release old headers, stream state, vorbis data */
Jack Moffitt's avatar
Jack Moffitt committed
112 113 114 115 116 117
			for (i = 0; i < 10; i++) {
				if (state->headbuf[i]) {
					refbuf_release(state->headbuf[i]);
					state->headbuf[i] = NULL;
				}
			}
Jack Moffitt's avatar
Jack Moffitt committed
118

Jack Moffitt's avatar
Jack Moffitt committed
119
			state->serialno = ogg_page_serialno(&state->og);
Jack Moffitt's avatar
Jack Moffitt committed
120 121 122
			ogg_stream_init(&state->os, state->serialno);
			vorbis_info_init(&state->vi);
			vorbis_comment_init(&state->vc);
Jack Moffitt's avatar
Jack Moffitt committed
123 124 125
		}

		if (state->header >= 0) {
Michael Smith's avatar
Michael Smith committed
126 127 128 129 130
            /* 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
131 132
				state->header++;
			} else {
Jack Moffitt's avatar
Jack Moffitt committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
				/* 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_args(self->mount, "title", tag);
				else stats_event_args(self->mount, "title", "unknown");
				tag = vorbis_comment_query(&state->vc, "ARTIST", 0);
				if (tag) stats_event_args(self->mount, "artist", tag);
				else stats_event_args(self->mount, "artist", "unknown");

				/* 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
148 149 150
			}
		}

Jack Moffitt's avatar
Jack Moffitt committed
151 152
		/* cache header pages */
		if (state->header > 0) {
Jack Moffitt's avatar
Jack Moffitt committed
153 154
			refbuf_addref(refbuf);
			state->headbuf[state->header - 1] = refbuf;
Jack Moffitt's avatar
Jack Moffitt committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

			if (state->packets >= 0 && state->packets < 2) {
				ogg_stream_pagein(&state->os, &state->og);
				while (state->packets < 2) {
					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
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
		}
	}

	return refbuf;
}

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;
	for (i = 0; i < 10; i++) {
		if (state->headbuf[i]) {
			refbuf_addref(state->headbuf[i]);
			refbuf_queue_add(&queue, state->headbuf[i]);
		} else {
			break;
		}
	}

	return queue;
}