/* -*- c-basic-offset: 8; -*- */
/* ogg.c: Generic ogg data handler
* $Id: ogg.c 10686 2006-01-03 18:47:54Z brendan $
*
* Copyright (C) 2002-2004 the Icecast team <team@icecast.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include <ogg/ogg.h>
#include <shout/shout.h>
#include "shout_private.h"
#include "shout_ogg.h"
/* -- local datatypes -- */
typedef struct {
ogg_sync_state oy;
ogg_codec_t *codecs;
char bos;
} ogg_data_t;
/* -- static prototypes -- */
static int send_ogg(shout_t *self, const unsigned char *data, size_t len);
static void close_ogg(shout_t *self);
static int open_codec(ogg_codec_t *codec, ogg_page *page);
static void free_codec(ogg_codec_t *codec);
static void free_codecs(ogg_data_t *ogg_data);
static int send_page(shout_t *self, ogg_page *page);
typedef int (*codec_open_t)(ogg_codec_t *codec, ogg_page *page);
static codec_open_t codecs[] = {
_shout_open_vorbis,
#ifdef HAVE_THEORA
_shout_open_theora,
#endif
#ifdef HAVE_SPEEX
_shout_open_speex,
#endif
NULL
};
int shout_open_ogg(shout_t *self)
{
ogg_data_t *ogg_data;
if (!(ogg_data = (ogg_data_t *)calloc(1, sizeof(ogg_data_t))))
return self->error = SHOUTERR_MALLOC;
self->format_data = ogg_data;
ogg_sync_init(&ogg_data->oy);
ogg_data->bos = 1;
self->send = send_ogg;
self->close = close_ogg;
return SHOUTERR_SUCCESS;
}
static int send_ogg(shout_t *self, const unsigned char *data, size_t len)
{
ogg_data_t *ogg_data = (ogg_data_t *)self->format_data;
ogg_codec_t *codec;
char *buffer;
ogg_page page;
buffer = ogg_sync_buffer(&ogg_data->oy, len);
memcpy(buffer, data, len);
ogg_sync_wrote(&ogg_data->oy, len);
while (ogg_sync_pageout(&ogg_data->oy, &page) == 1) {
if (ogg_page_bos (&page)) {
if (! ogg_data->bos) {
free_codecs(ogg_data);
ogg_data->bos = 1;
}
codec = calloc(1, sizeof(ogg_codec_t));
if (! codec)
return self->error = SHOUTERR_MALLOC;
if ((self->error = open_codec(codec, &page)) != SHOUTERR_SUCCESS)
return self->error;
codec->headers = 1;
codec->senttime = self->senttime;
codec->next = ogg_data->codecs;
ogg_data->codecs = codec;
} else {
ogg_data->bos = 0;
codec = ogg_data->codecs;
while (codec) {
if (ogg_page_serialno(&page) == codec->os.serialno) {
if (codec->read_page) {
ogg_stream_pagein(&codec->os, &page);
codec->read_page(codec, &page);
if (self->senttime < codec->senttime)
self->senttime = codec->senttime;
}
break;
}
codec = codec->next;
}
}
if ((self->error = send_page(self, &page)) != SHOUTERR_SUCCESS)
return self->error;
}
return self->error = SHOUTERR_SUCCESS;
}
static void close_ogg(shout_t *self)
{
ogg_data_t *ogg_data = (ogg_data_t *)self->format_data;
free_codecs(ogg_data);
ogg_sync_clear(&ogg_data->oy);
free(ogg_data);
}
static int open_codec(ogg_codec_t *codec, ogg_page *page)
{
codec_open_t this_codec;
int i = 0;
while ((this_codec = codecs[i])) {
ogg_stream_init(&codec->os, ogg_page_serialno(page));
ogg_stream_pagein(&codec->os, page);
if (this_codec(codec, page) == SHOUTERR_SUCCESS)
return SHOUTERR_SUCCESS;
ogg_stream_clear(&codec->os);
i++;
}
/* if no handler is found, we currently just fall back to untimed send_raw */
return SHOUTERR_SUCCESS;
}
static void free_codecs(ogg_data_t *ogg_data)
{
ogg_codec_t *codec, *next;
if (ogg_data == NULL)
return;
codec = ogg_data->codecs;
while (codec) {
next = codec->next;
free_codec(codec);
codec = next;
}
ogg_data->codecs = NULL;
}
static void free_codec(ogg_codec_t *codec)
{
if (codec->free_data)
codec->free_data(codec->codec_data);
ogg_stream_clear(&codec->os);
free(codec);
}
static int send_page(shout_t *self, ogg_page *page)
{
int ret;
ret = shout_send_raw(self, page->header, page->header_len);
if (ret != page->header_len)
return self->error = SHOUTERR_SOCKET;
ret = shout_send_raw(self, page->body, page->body_len);
if (ret != page->body_len)
return self->error = SHOUTERR_SOCKET;
return SHOUTERR_SUCCESS;
}