format_vorbis.c 2.5 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 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
/* 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"

#include "format.h"

typedef struct _vstate_tag
{
	ogg_sync_state oy;
	ogg_page og;
	unsigned long serialno;
	int header;
	refbuf_t *headbuf[10];
} vstate_t;

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));
	plugin->type = FORMAT_TYPE_VORBIS;
	plugin->has_predata = 1;
	plugin->get_buffer = format_vorbis_get_buffer;
	plugin->get_predata = format_vorbis_get_predata;

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

	plugin->_state = (void *)state;

	return plugin;
}

refbuf_t *format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len)
{
	char *buffer;
	refbuf_t *refbuf;
	int i;
	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;
			for (i = 0; i < 10; i++) {
				if (state->headbuf[i]) {
					refbuf_release(state->headbuf[i]);
					state->headbuf[i] = NULL;
				}
			}
					
			state->serialno = ogg_page_serialno(&state->og);
		}

		if (state->header >= 0) {
			if (ogg_page_granulepos(&state->og) == 0) {
				state->header++;
			} else {
				state->header = 0;
			}
		}

		/* cache first three pages */
		if (state->header) {
			refbuf_addref(refbuf);
			state->headbuf[state->header - 1] = refbuf;
		}
	}

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