shout: use the new encoder API

Removed shout's encoder plugin API in favor of the new generic encoder
plugin API.
This commit is contained in:
Max Kellermann 2009-02-22 17:18:28 +01:00
parent f7c685f1ab
commit f6e5c00726
6 changed files with 103 additions and 566 deletions

View File

@ -557,8 +557,7 @@ if test x$enable_shout_ogg = xyes; then
enable_shout_ogg=no
fi
if test x$enable_shout_ogg = xyes; then
PKG_CHECK_MODULES(VORBISENC, [vorbisenc],
AC_DEFINE(HAVE_SHOUT_OGG, 1, [Define to enable ogg streaming support]),
PKG_CHECK_MODULES(VORBISENC, [vorbisenc],,
enable_shout_ogg=no)
fi
fi
@ -573,9 +572,6 @@ if test x$enable_shout_mp3 = xyes; then
AC_MSG_WARN([disabling mp3 shout streaming support because lame is not enabled])
enable_shout_mp3=no
fi
if test x$enable_shout_mp3 = xyes; then
AC_DEFINE(HAVE_SHOUT_MP3, 1, [Define to enable mp3 streaming support])
fi
fi
if test x$enable_shout_ogg = xyes || test x$enable_shout_mp3 = xyes; then
@ -586,8 +582,6 @@ else
fi
AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes)
AM_CONDITIONAL(HAVE_SHOUT_OGG, test x$enable_shout_ogg = xyes)
AM_CONDITIONAL(HAVE_SHOUT_MP3, test x$enable_shout_mp3 = xyes)
AM_CONDITIONAL(ENABLE_ENCODER, test x$enable_shout = xyes)
AM_CONDITIONAL(ENABLE_VORBIS_ENCODER, test x$enable_shout_ogg = xyes)

View File

@ -419,14 +419,6 @@ if HAVE_SHOUT
mpd_SOURCES += output/shout_plugin.c
endif
if HAVE_SHOUT_MP3
mpd_SOURCES += output/shout_mp3.c
endif
if HAVE_SHOUT_OGG
mpd_SOURCES += output/shout_ogg.c
endif
# sparse is a semantic parser
# URL: git://www.kernel.org/pub/scm/devel/sparse/sparse.git
SPARSE = sparse

View File

@ -1,193 +0,0 @@
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "shout_plugin.h"
#include <lame/lame.h>
#include <assert.h>
#include <stdlib.h>
struct lame_data {
lame_global_flags *gfp;
};
static int shout_mp3_encoder_init(struct shout_data *sd)
{
struct lame_data *ld = g_new(struct lame_data, 1);
sd->encoder_data = ld;
return 0;
}
static int shout_mp3_encoder_clear_encoder(struct shout_data *sd)
{
struct lame_data *ld = (struct lame_data *)sd->encoder_data;
struct shout_buffer *buf = &sd->buf;
int ret;
ret = lame_encode_flush(ld->gfp, buf->data, sizeof(buf->data));
if (ret < 0)
g_warning("error flushing lame buffers\n");
lame_close(ld->gfp);
ld->gfp = NULL;
return (ret > 0);
}
static void shout_mp3_encoder_finish(struct shout_data *sd)
{
struct lame_data *ld = (struct lame_data *)sd->encoder_data;
assert(ld->gfp == NULL);
g_free(ld);
}
static int shout_mp3_encoder_init_encoder(struct shout_data *sd)
{
struct lame_data *ld = (struct lame_data *)sd->encoder_data;
if (NULL == (ld->gfp = lame_init())) {
g_warning("error initializing lame encoder for shout\n");
return -1;
}
if (sd->quality >= -1.0) {
if (0 != lame_set_VBR(ld->gfp, vbr_rh)) {
g_warning("error setting lame VBR mode\n");
return -1;
}
if (0 != lame_set_VBR_q(ld->gfp, sd->quality)) {
g_warning("error setting lame VBR quality\n");
return -1;
}
} else {
if (0 != lame_set_brate(ld->gfp, sd->bitrate)) {
g_warning("error setting lame bitrate\n");
return -1;
}
}
if (0 != lame_set_num_channels(ld->gfp,
sd->audio_format.channels)) {
g_warning("error setting lame num channels\n");
return -1;
}
if (0 != lame_set_in_samplerate(ld->gfp,
sd->audio_format.sample_rate)) {
g_warning("error setting lame sample rate\n");
return -1;
}
if (0 > lame_init_params(ld->gfp))
g_error("error initializing lame params\n");
return 0;
}
static int shout_mp3_encoder_send_metadata(struct shout_data *sd,
char * song, size_t size)
{
char artist[size];
char title[size];
int i;
const struct tag *tag = sd->tag;
strncpy(artist, "", size);
strncpy(title, "", size);
for (i = 0; i < tag->numOfItems; i++) {
switch (tag->items[i]->type) {
case TAG_ITEM_ARTIST:
strncpy(artist, tag->items[i]->value, size);
break;
case TAG_ITEM_TITLE:
strncpy(title, tag->items[i]->value, size);
break;
default:
break;
}
}
snprintf(song, size, "%s - %s", title, artist);
return 1;
}
static int
shout_mp3_encoder_encode(struct shout_data *sd, const void *chunk, size_t len)
{
const int16_t *src = (const int16_t*)chunk;
unsigned int i;
float *left, *right;
struct shout_buffer *buf = &(sd->buf);
unsigned int samples;
struct lame_data *ld = (struct lame_data *)sd->encoder_data;
int bytes_out;
samples = len / audio_format_sample_size(&sd->audio_format);
left = g_malloc(sizeof(left[0]) * samples);
if (sd->audio_format.channels > 1)
right = g_malloc(sizeof(left[0]) * samples);
else
right = left;
/* this is for only 16-bit audio */
for (i = 0; i < samples; i++) {
left[i] = src[0];
if (right != left)
right[i] = src[1];
src += sd->audio_format.channels;
}
bytes_out = lame_encode_buffer_float(ld->gfp, left, right,
samples, buf->data,
sizeof(buf->data));
g_free(left);
if (right != left)
g_free(right);
if (0 > bytes_out) {
g_warning("error encoding lame buffer for shout\n");
lame_close(ld->gfp);
ld->gfp = NULL;
return -1;
} else
buf->len = bytes_out; /* signed to unsigned conversion */
return 0;
}
const struct shout_encoder_plugin shout_mp3_encoder = {
"mp3",
SHOUT_FORMAT_MP3,
shout_mp3_encoder_clear_encoder,
shout_mp3_encoder_encode,
shout_mp3_encoder_finish,
shout_mp3_encoder_init,
shout_mp3_encoder_init_encoder,
shout_mp3_encoder_send_metadata,
};

View File

@ -1,293 +0,0 @@
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "shout_plugin.h"
#include <vorbis/vorbisenc.h>
#include <stdlib.h>
struct ogg_vorbis_data {
ogg_stream_state os;
ogg_page og;
ogg_packet op;
ogg_packet header_main;
ogg_packet header_comments;
ogg_packet header_codebooks;
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_info vi;
vorbis_comment vc;
};
static void add_tag(struct ogg_vorbis_data *od, const char *name, char *value)
{
if (value) {
union {
const char *in;
char *out;
} u = { .in = name };
vorbis_comment_add_tag(&od->vc, u.out, value);
}
}
static void copy_tag_to_vorbis_comment(struct shout_data *sd)
{
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
if (sd->tag) {
int i;
for (i = 0; i < sd->tag->numOfItems; i++) {
switch (sd->tag->items[i]->type) {
case TAG_ITEM_ARTIST:
add_tag(od, "ARTIST", sd->tag->items[i]->value);
break;
case TAG_ITEM_ALBUM:
add_tag(od, "ALBUM", sd->tag->items[i]->value);
break;
case TAG_ITEM_TITLE:
add_tag(od, "TITLE", sd->tag->items[i]->value);
break;
default:
break;
}
}
}
}
static int copy_ogg_buffer_to_shout_buffer(ogg_page *og,
struct shout_buffer *buf)
{
if ((size_t)og->header_len + (size_t)og->body_len > sizeof(buf->data)) {
g_warning("%s: not enough buffer space!\n", __func__);
return -1;
}
memcpy(buf->data, og->header, og->header_len);
memcpy(buf->data + og->header_len, og->body, og->body_len);
buf->len = og->header_len + og->body_len;
return 0;
}
static int flush_ogg_buffer(struct shout_data *sd)
{
struct shout_buffer *buf = &sd->buf;
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
int ret = 0;
if (ogg_stream_flush(&od->os, &od->og))
ret = copy_ogg_buffer_to_shout_buffer(&od->og, buf);
return ret;
}
static int send_ogg_vorbis_header(struct shout_data *sd)
{
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
vorbis_analysis_headerout(&od->vd, &od->vc,
&od->header_main,
&od->header_comments,
&od->header_codebooks);
ogg_stream_packetin(&od->os, &od->header_main);
ogg_stream_packetin(&od->os, &od->header_comments);
ogg_stream_packetin(&od->os, &od->header_codebooks);
return flush_ogg_buffer(sd);
}
static void finish_encoder(struct ogg_vorbis_data *od)
{
vorbis_analysis_wrote(&od->vd, 0);
while (vorbis_analysis_blockout(&od->vd, &od->vb) == 1) {
vorbis_analysis(&od->vb, NULL);
vorbis_bitrate_addblock(&od->vb);
while (vorbis_bitrate_flushpacket(&od->vd, &od->op)) {
ogg_stream_packetin(&od->os, &od->op);
}
}
}
static int shout_ogg_encoder_clear_encoder(struct shout_data *sd)
{
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
int ret;
finish_encoder(od);
if ((ret = ogg_stream_pageout(&od->os, &od->og)))
copy_ogg_buffer_to_shout_buffer(&od->og, &sd->buf);
vorbis_comment_clear(&od->vc);
ogg_stream_clear(&od->os);
vorbis_block_clear(&od->vb);
vorbis_dsp_clear(&od->vd);
vorbis_info_clear(&od->vi);
return ret;
}
static void shout_ogg_encoder_finish(struct shout_data *sd)
{
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
if (od) {
free(od);
sd->encoder_data = NULL;
}
}
static int shout_ogg_encoder_init(struct shout_data *sd)
{
struct ogg_vorbis_data *od = g_new(struct ogg_vorbis_data, 1);
sd->encoder_data = od;
return 0;
}
static int reinit_encoder(struct shout_data *sd)
{
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
vorbis_info_init(&od->vi);
if (sd->quality >= -1.0) {
if (0 != vorbis_encode_init_vbr(&od->vi,
sd->audio_format.channels,
sd->audio_format.sample_rate,
sd->quality * 0.1)) {
g_warning("error initializing vorbis vbr\n");
vorbis_info_clear(&od->vi);
return -1;
}
} else {
if (0 != vorbis_encode_init(&od->vi,
sd->audio_format.channels,
sd->audio_format.sample_rate, -1.0,
sd->bitrate * 1000, -1.0)) {
g_warning("error initializing vorbis encoder\n");
vorbis_info_clear(&od->vi);
return -1;
}
}
vorbis_analysis_init(&od->vd, &od->vi);
vorbis_block_init(&od->vd, &od->vb);
ogg_stream_init(&od->os, rand());
vorbis_comment_init(&od->vc);
return 0;
}
static int shout_ogg_encoder_init_encoder(struct shout_data *sd)
{
if (reinit_encoder(sd))
return -1;
if (send_ogg_vorbis_header(sd)) {
g_warning("error sending ogg vorbis header for shout\n");
return -1;
}
return 0;
}
static int shout_ogg_encoder_send_metadata(struct shout_data *sd,
G_GNUC_UNUSED char * song,
G_GNUC_UNUSED size_t size)
{
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
shout_ogg_encoder_clear_encoder(sd);
if (reinit_encoder(sd))
return 0;
copy_tag_to_vorbis_comment(sd);
vorbis_analysis_headerout(&od->vd, &od->vc,
&od->header_main,
&od->header_comments,
&od->header_codebooks);
ogg_stream_packetin(&od->os, &od->header_main);
ogg_stream_packetin(&od->os, &od->header_comments);
ogg_stream_packetin(&od->os, &od->header_codebooks);
flush_ogg_buffer(sd);
return 0;
}
static void
pcm16_to_ogg_buffer(float **dest, const int16_t *src,
unsigned num_samples, unsigned num_channels)
{
for (unsigned i = 0; i < num_samples; i++)
for (unsigned j = 0; j < num_channels; j++)
dest[j][i] = *src++ / 32768.0;
}
static int
shout_ogg_encoder_encode(struct shout_data *sd,
const void *chunk, size_t size)
{
struct shout_buffer *buf = &sd->buf;
unsigned int samples;
struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
samples = size / audio_format_frame_size(&sd->audio_format);
/* this is for only 16-bit audio */
pcm16_to_ogg_buffer(vorbis_analysis_buffer(&od->vd, samples),
(const int16_t *)chunk,
samples, sd->audio_format.channels);
vorbis_analysis_wrote(&od->vd, samples);
while (1 == vorbis_analysis_blockout(&od->vd, &od->vb)) {
vorbis_analysis(&od->vb, NULL);
vorbis_bitrate_addblock(&od->vb);
while (vorbis_bitrate_flushpacket(&od->vd, &od->op)) {
ogg_stream_packetin(&od->os, &od->op);
}
}
if (ogg_stream_pageout(&od->os, &od->og))
copy_ogg_buffer_to_shout_buffer(&od->og, buf);
return 0;
}
const struct shout_encoder_plugin shout_ogg_encoder = {
"ogg",
SHOUT_FORMAT_VORBIS,
shout_ogg_encoder_clear_encoder,
shout_ogg_encoder_encode,
shout_ogg_encoder_finish,
shout_ogg_encoder_init,
shout_ogg_encoder_init_encoder,
shout_ogg_encoder_send_metadata,
};

View File

@ -17,6 +17,8 @@
*/
#include "shout_plugin.h"
#include "encoder_plugin.h"
#include "encoder_list.h"
#include "config.h"
#include <assert.h>
@ -37,16 +39,15 @@ static const struct shout_encoder_plugin *const shout_encoder_plugins[] = {
NULL
};
static const struct shout_encoder_plugin *
static const struct encoder_plugin *
shout_encoder_plugin_get(const char *name)
{
unsigned i;
if (strcmp(name, "ogg") == 0)
name = "vorbis";
else if (strcmp(name, "mp3") == 0)
name = "lame";
for (i = 0; shout_encoder_plugins[i] != NULL; ++i)
if (strcmp(shout_encoder_plugins[i]->name, name) == 0)
return shout_encoder_plugins[i];
return NULL;
return encoder_plugin_get(name);
}
static struct shout_data *new_shout_data(void)
@ -91,6 +92,9 @@ static void *my_shout_init_driver(struct audio_output *audio_output,
char *mount;
char *passwd;
const char *encoding;
const struct encoder_plugin *encoder_plugin;
GError *error = NULL;
unsigned shout_format;
unsigned protocol;
const char *user;
char *name;
@ -162,15 +166,21 @@ static void *my_shout_init_driver(struct audio_output *audio_output,
check_block_param("format");
assert(audio_format != NULL);
sd->audio_format = *audio_format;
encoding = config_get_block_string(param, "encoding", "ogg");
sd->encoder = shout_encoder_plugin_get(encoding);
if (sd->encoder == NULL)
encoder_plugin = shout_encoder_plugin_get(encoding);
if (encoder_plugin == NULL)
g_error("couldn't find shout encoder plugin \"%s\"\n",
encoding);
sd->encoder = encoder_init(encoder_plugin, param, &error);
if (sd->encoder == NULL)
g_error("%s", error->message);
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
shout_format = SHOUT_FORMAT_MP3;
else
shout_format = SHOUT_FORMAT_OGG;
value = config_get_block_string(param, "protocol", NULL);
if (value != NULL) {
if (0 == strcmp(value, "shoutcast") &&
@ -199,7 +209,7 @@ static void *my_shout_init_driver(struct audio_output *audio_output,
shout_set_name(sd->shout_conn, name) != SHOUTERR_SUCCESS ||
shout_set_user(sd->shout_conn, user) != SHOUTERR_SUCCESS ||
shout_set_public(sd->shout_conn, public) != SHOUTERR_SUCCESS ||
shout_set_format(sd->shout_conn, sd->encoder->shout_format)
shout_set_format(sd->shout_conn, shout_format)
!= SHOUTERR_SUCCESS ||
shout_set_protocol(sd->shout_conn, protocol) != SHOUTERR_SUCCESS ||
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
@ -227,10 +237,10 @@ static void *my_shout_init_driver(struct audio_output *audio_output,
char temp[11];
memset(temp, 0, sizeof(temp));
snprintf(temp, sizeof(temp), "%u", sd->audio_format.channels);
snprintf(temp, sizeof(temp), "%u", audio_format->channels);
shout_set_audio_info(sd->shout_conn, SHOUT_AI_CHANNELS, temp);
snprintf(temp, sizeof(temp), "%u", sd->audio_format.sample_rate);
snprintf(temp, sizeof(temp), "%u", audio_format->sample_rate);
shout_set_audio_info(sd->shout_conn, SHOUT_AI_SAMPLERATE, temp);
@ -245,10 +255,6 @@ static void *my_shout_init_driver(struct audio_output *audio_output,
}
}
if (sd->encoder->init_func(sd) != 0)
g_error("shout: encoder plugin '%s' failed to initialize\n",
sd->encoder->name);
return sd;
}
@ -283,6 +289,10 @@ write_page(struct shout_data *sd)
{
int err;
assert(sd->encoder != NULL);
sd->buf.len = encoder_read(sd->encoder,
sd->buf.data, sizeof(sd->buf.data));
if (sd->buf.len == 0)
return true;
@ -298,8 +308,12 @@ static void close_shout_conn(struct shout_data * sd)
{
sd->buf.len = 0;
if (sd->encoder->clear_encoder_func(sd))
write_page(sd);
if (sd->encoder != NULL) {
if (encoder_flush(sd->encoder, NULL))
write_page(sd);
encoder_close(sd->encoder);
}
if (shout_get_connected(sd->shout_conn) != SHOUTERR_UNCONNECTED &&
shout_close(sd->shout_conn) != SHOUTERR_SUCCESS) {
@ -312,7 +326,8 @@ static void my_shout_finish_driver(void *data)
{
struct shout_data *sd = (struct shout_data *)data;
sd->encoder->finish_func(sd);
encoder_finish(sd->encoder);
free_shout_data(sd);
shout_init_count--;
@ -357,11 +372,11 @@ shout_connect(struct shout_data *sd)
}
static bool
my_shout_open_device(void *data,
G_GNUC_UNUSED struct audio_format *audio_format)
my_shout_open_device(void *data, struct audio_format *audio_format)
{
struct shout_data *sd = (struct shout_data *)data;
bool ret;
GError *error = NULL;
ret = shout_connect(sd);
if (!ret)
@ -369,8 +384,11 @@ my_shout_open_device(void *data,
sd->buf.len = 0;
if (sd->encoder->init_encoder_func(sd) < 0) {
ret = encoder_open(sd->encoder, audio_format, &error);
if (!ret) {
shout_close(sd->shout_conn);
g_warning("%s", error->message);
g_error_free(error);
return false;
}
@ -383,11 +401,15 @@ static bool
my_shout_play(void *data, const char *chunk, size_t size)
{
struct shout_data *sd = (struct shout_data *)data;
bool ret;
GError *error = NULL;
sd->buf.len = 0;
if (sd->encoder->encode_func(sd, chunk, size))
ret = encoder_write(sd->encoder, chunk, size, &error);
if (!ret) {
g_warning("%s", error->message);
g_error_free(error);
return false;
}
return write_page(sd);
}
@ -400,17 +422,63 @@ my_shout_pause(void *data)
return my_shout_play(data, silence, sizeof(silence));
}
static void
shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size)
{
char artist[size];
char title[size];
int i;
artist[0] = 0;
title[0] = 0;
for (i = 0; i < tag->numOfItems; i++) {
switch (tag->items[i]->type) {
case TAG_ITEM_ARTIST:
strncpy(artist, tag->items[i]->value, size);
break;
case TAG_ITEM_TITLE:
strncpy(title, tag->items[i]->value, size);
break;
default:
break;
}
}
snprintf(dest, size, "%s - %s", title, artist);
}
static void my_shout_set_tag(void *data,
const struct tag *tag)
{
struct shout_data *sd = (struct shout_data *)data;
char song[1024];
bool ret;
GError *error = NULL;
if (sd->encoder->plugin->tag != NULL) {
/* encoder plugin supports stream tags */
ret = encoder_flush(sd->encoder, &error);
if (!ret) {
g_warning("%s", error->message);
g_error_free(error);
return;
}
write_page(sd);
ret = encoder_tag(sd->encoder, tag, &error);
if (!ret) {
g_warning("%s", error->message);
g_error_free(error);
}
} else {
/* no stream tag support: fall back to icy-metadata */
char song[1024];
shout_tag_to_metadata(tag, song, sizeof(song));
sd->buf.len = 0;
sd->tag = tag;
ret = sd->encoder->send_metadata_func(sd, song, sizeof(song));
if (ret) {
shout_metadata_add(sd->shout_meta, "song", song);
if (SHOUTERR_SUCCESS != shout_set_metadata(sd->shout_conn,
sd->shout_meta)) {

View File

@ -28,28 +28,6 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "shout"
struct shout_data;
struct shout_encoder_plugin {
const char *name;
unsigned int shout_format;
int (*clear_encoder_func)(struct shout_data *sd);
int (*encode_func)(struct shout_data *sd,
const void *chunk, size_t len);
void (*finish_func)(struct shout_data *sd);
int (*init_func)(struct shout_data *sd);
int (*init_encoder_func) (struct shout_data *sd);
/* Called when there is a new MpdTag to encode into the
stream. If this function returns non-zero, then the
resulting song will be passed to the shout server as
metadata. This allows the Ogg encoder to send metadata via
Vorbis comments in the stream, while an MP3 encoder can use
the Shout Server's metadata API. */
int (*send_metadata_func)(struct shout_data *sd,
char *song, size_t size);
};
struct shout_buffer {
unsigned char data[32768];
size_t len;
@ -61,23 +39,14 @@ struct shout_data {
shout_t *shout_conn;
shout_metadata_t *shout_meta;
const struct shout_encoder_plugin *encoder;
void *encoder_data;
struct encoder *encoder;
float quality;
int bitrate;
const struct tag *tag;
int timeout;
/* the configured audio format */
struct audio_format audio_format;
struct shout_buffer buf;
};
extern const struct shout_encoder_plugin shout_mp3_encoder;
extern const struct shout_encoder_plugin shout_ogg_encoder;
#endif