aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac39
-rw-r--r--doc/mpd.conf.510
-rw-r--r--doc/mpdconf.example1
-rw-r--r--src/audioOutputs/audioOutput_shout.c75
-rw-r--r--src/audioOutputs/audioOutput_shout.h64
-rw-r--r--src/audioOutputs/audioOutput_shout_ogg.c93
6 files changed, 209 insertions, 73 deletions
diff --git a/configure.ac b/configure.ac
index c3db4cbc1..ffe161307 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,7 +73,7 @@ if test -z "$prefix" || test "x$prefix" = xNONE; then
fi
AC_ARG_ENABLE(ao,[ --enable-ao enable support for libao (default: disable)],[enable_ao=$enableval],[enable_ao=no])
-AC_ARG_ENABLE(shout,[ --disable-shout disable support for streaming through shout (default: enable)],[enable_shout=$enableval],[enable_shout=yes])
+AC_ARG_ENABLE(shout_ogg,[ --disable-shout_ogg disable support for ogg streaming through shout (default: enable)],[enable_shout_ogg=$enableval],[enable_shout_ogg=yes])
AC_ARG_ENABLE(iconv,[ --disable-iconv disable iconv support (default: enable)],[enable_iconv=$enableval],[enable_iconv=yes])
AC_ARG_ENABLE(ipv6,[ --disable-ipv6 disable IPv6 support (default: enable)],[enable_ipv6=$enableval],[enable_ipv6=yes])
AC_ARG_ENABLE(tcp,[ --disable-tcp disable support for clients connecting via TCP (default: enable)],[enable_tcp=$enableval],[enable_tcp=yes])
@@ -185,14 +185,25 @@ case $host in
enable_osx=yes ;;
esac
-if test x$enable_shout = xyes; then
+if test x$enable_shout_ogg = xyes || x$enable_shout_mp3 = xyes; then
+ XIPH_PATH_SHOUT([enable_shout=yes;AC_DEFINE(HAVE_SHOUT, 1, [Define to enable libshout support]) MPD_LIBS="$MPD_LIBS $SHOUT_LIBS" MPD_CFLAGS="$MPD_CFLAGS $SHOUT_CFLAGS"], [AC_MSG_WARN(libshout not found -- disabling shout support);enable_shout=no])
+fi
+
+if test x$enable_shout_ogg = xyes; then
if test x$enable_oggvorbis = xno; then
- AC_MSG_WARN([disabling shout streaming support because vorbis is not enabled])
- enable_shout=no
+ AC_MSG_WARN([disabling ogg shout streaming support because vorbis is not enabled])
+ enable_shout_ogg=no
fi
if test x$use_tremor = xyes; then
- AC_MSG_WARN([disabling shout streaming support because tremor does not support vorbis encoding])
- enable_shout=no
+ AC_MSG_WARN([disabling ogg shout streaming support because tremor does not support vorbis encoding])
+ enable_shout_ogg=no
+ fi
+ if test x$enable_shout = xno; then
+ AC_MSG_WARN([disabling ogg shout streaming support because libshout is not found])
+ enable_shout_ogg=no
+ fi
+ if test x$enable_shout_ogg = xyes; then
+ AC_DEFINE(HAVE_SHOUT_OGG, 1, [Define to enable ogg streaming support])
fi
fi
@@ -200,12 +211,8 @@ if test x$enable_ao = xyes; then
XIPH_PATH_AO([AC_DEFINE(HAVE_AO, 1, [Define to play with ao]) MPD_LIBS="$MPD_LIBS $AO_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AO_CFLAGS"], enable_ao=no)
fi
-if test x$enable_shout = xyes; then
- XIPH_PATH_SHOUT([AC_DEFINE(HAVE_SHOUT, 1, [Define to enable libshout support]) MPD_LIBS="$MPD_LIBS $SHOUT_LIBS" MPD_CFLAGS="$MPD_CFLAGS $SHOUT_CFLAGS"], enable_shout=no)
-fi
-
if test x$enable_oss = xyes; then
- AC_CHECK_HEADER(sys/soundcard.h,[enable_oss=yes;AC_DEFINE(HAVE_OSS,1,[Define to enable OSS])],[AC_MSG_WARN(Soundcard headers not found -- disabling OSS support);enable_oss=no])
+ AC_CHECK_HEADER(sys/soundcard.h,[enable_oss=yes;AC_DEFINE(HAVE_OSS,1,[Define to enable OSS])],[AC_MSG_WARN(Soundcard headers not found -- disabling OSS support);enable_oss=no])
fi
PKG_PROG_PKG_CONFIG
@@ -535,7 +542,7 @@ elif test x$enable_oggvorbis = xyes; then
MPD_LIBS="$MPD_LIBS $OGG_LIBS $VORBIS_LIBS $VORBISFILE_LIBS"
MPD_CFLAGS="$MPD_CFLAGS $OGG_CFLAGS $VORBIS_CFLAGS"
- if test x$enable_shout = xyes; then
+ if test x$enable_shout_ogg = xyes; then
MPD_LIBS="$MPD_LIBS $VORBISENC_LIBS"
MPD_CFLAGS="$MPD_CFLAGS $VORBISFILE_CFLAGS $VORBISENC_CFLAGS"
fi
@@ -740,17 +747,17 @@ else
echo " Media MVP support .............disabled"
fi
-if test x$enable_shout = xyes; then
- echo " Shout streaming support .......enabled"
+if test x$enable_shout_ogg = xyes; then
+ echo " Shout ogg streaming support ...enabled"
else
- echo " Shout streaming support .......disabled"
+ echo " Shout ogg streaming support ...disabled"
fi
echo ""
if test x$enable_ao = xno &&
test x$enable_oss = xno &&
- test x$enable_shout = xno &&
+ test x$enable_shout_ogg = xno &&
test x$enable_alsa = xno &&
test x$enable_osx = xno &&
test x$enable_pulse = xno &&
diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5
index 371c0d774..5a77d4944 100644
--- a/doc/mpd.conf.5
+++ b/doc/mpd.conf.5
@@ -365,9 +365,11 @@ This specifies the icecast mountpoint to use.
This specifies the password to use when logging in to the icecast server.
.TP
.B quality <quality>
-This specifies the ogg encoding quality to use. The value must be between 0
+This specifies the encoding quality to use. The value must be between 0
and 10. Fractional values, such as 2.5, are permitted. Either the quality or
-the bitrate parameter must be specified, but not both.
+the bitrate parameter must be specified, but not both. For Ogg, a
+higher quality number produces higher quality output. For MP3, it's
+just the opposite, with lower numbers producing higher quality output.
.TP
.B bitrate <kbps>
This specifies the bitrate to use for encoding. Either the quality or the
@@ -378,6 +380,10 @@ This specifies the sample rate, bits per sample, and number of channels to use
for encoding.
.SH OPTIONAL SHOUT OUTPUT PARAMETERS
.TP
+.B encoding <encoding>
+This specifies which output encoding to use. Should be either "ogg"
+or "mp3". The default is "ogg".
+.TP
.B user <username>
This specifies the username to use when logging in to the icecast server. The
default is "source".
diff --git a/doc/mpdconf.example b/doc/mpdconf.example
index be04adb91..995bba3aa 100644
--- a/doc/mpdconf.example
+++ b/doc/mpdconf.example
@@ -112,6 +112,7 @@ error_file "~/.mpd/mpd.error"
#
#audio_output {
# type "shout"
+# encoding "ogg"
# name "My Shout Stream"
# host "localhost"
# port "8000"
diff --git a/src/audioOutputs/audioOutput_shout.c b/src/audioOutputs/audioOutput_shout.c
index 5bd2b575c..1e60e3546 100644
--- a/src/audioOutputs/audioOutput_shout.c
+++ b/src/audioOutputs/audioOutput_shout.c
@@ -20,6 +20,7 @@
#ifdef HAVE_SHOUT
+#include "../list.h"
#include "../utils.h"
#define CONN_ATTEMPT_INTERVAL 60
@@ -28,6 +29,33 @@
#define SHOUT_BUF_SIZE 8192
static int shout_init_count;
+static List *shout_encoder_plugin_list;
+
+mpd_unused
+static void finish_shout_encoder_plugins(void)
+{
+ freeList(shout_encoder_plugin_list);
+}
+
+static void init_shout_encoder_plugins(void)
+{
+ shout_encoder_plugin_list = makeList(NULL, 0);
+}
+
+static void load_shout_encoder_plugin(shout_encoder_plugin * plugin)
+{
+ if (!plugin->name)
+ return;
+ insertInList(shout_encoder_plugin_list, plugin->name, plugin);
+}
+
+mpd_unused
+static void unload_shout_encoder_plugin(shout_encoder_plugin * plugin)
+{
+ if (!plugin->name)
+ return;
+ deleteFromList(shout_encoder_plugin_list, plugin->name);
+}
static void clear_shout_buffer(struct shout_data * sd)
{
@@ -82,6 +110,12 @@ static void free_shout_data(struct shout_data *sd)
} \
}
+static void load_shout_plugins(void)
+{
+ init_shout_encoder_plugins();
+ load_shout_encoder_plugin(&shout_ogg_encoder);
+}
+
static int my_shout_init_driver(struct audio_output *audio_output,
ConfigParam * param)
{
@@ -91,10 +125,14 @@ static int my_shout_init_driver(struct audio_output *audio_output,
char *host;
char *mount;
char *passwd;
+ const char *encoding;
const char *user;
char *name;
BlockParam *block_param;
int public;
+ void *data = NULL;
+
+ load_shout_plugins();
sd = new_shout_data();
@@ -173,6 +211,25 @@ static int my_shout_init_driver(struct audio_output *audio_output,
check_block_param("format");
sd->audio_format = audio_output->reqAudioFormat;
+ block_param = getBlockParam(param, "encoding");
+ if (block_param) {
+ if (0 == strncasecmp(block_param->value, "mp3", 3))
+ encoding = block_param->value;
+ else if (0 == strncasecmp(block_param->value, "ogg", 3))
+ encoding = block_param->value;
+ else
+ FATAL("shout encoding \"%s\" is not \"ogg\" or "
+ "\"mp3\", line %i\n", block_param->value,
+ block_param->line);
+ } else {
+ encoding = "ogg";
+ }
+ if (!findInList(shout_encoder_plugin_list, encoding, &data)) {
+ FATAL("couldn't find shout encoder plugin for \"%s\" "
+ "at line %i\n", encoding, block_param->line);
+ }
+ sd->encoder = (shout_encoder_plugin *) data;
+
if (shout_set_host(sd->shout_conn, host) != SHOUTERR_SUCCESS ||
shout_set_port(sd->shout_conn, port) != SHOUTERR_SUCCESS ||
shout_set_password(sd->shout_conn, passwd) != SHOUTERR_SUCCESS ||
@@ -181,7 +238,7 @@ static int my_shout_init_driver(struct audio_output *audio_output,
shout_set_user(sd->shout_conn, user) != SHOUTERR_SUCCESS ||
shout_set_public(sd->shout_conn, public) != SHOUTERR_SUCCESS ||
shout_set_nonblocking(sd->shout_conn, 1) != SHOUTERR_SUCCESS ||
- shout_set_format(sd->shout_conn, SHOUT_FORMAT_VORBIS)
+ shout_set_format(sd->shout_conn, sd->encoder->shout_format)
!= SHOUTERR_SUCCESS ||
shout_set_protocol(sd->shout_conn, SHOUT_PROTOCOL_HTTP)
!= SHOUTERR_SUCCESS ||
@@ -237,7 +294,7 @@ static int my_shout_init_driver(struct audio_output *audio_output,
audio_output->data = sd;
- return 0;
+ return sd->encoder->init_func(sd);
}
static int handle_shout_error(struct shout_data *sd, int err)
@@ -278,10 +335,10 @@ static int write_page(struct shout_data *sd)
return 0;
}
-static void close_shout_conn(struct shout_data *sd)
+static void close_shout_conn(struct shout_data * sd)
{
if (sd->opened) {
- if (shout_ogg_encoder_clear_encoder(sd))
+ if (sd->encoder->clear_encoder_func(sd))
write_page(sd);
}
@@ -300,6 +357,7 @@ static void my_shout_finish_driver(struct audio_output *audio_output)
close_shout_conn(sd);
+ sd->encoder->finish_func(sd);
free_shout_data(sd);
shout_init_count--;
@@ -395,7 +453,7 @@ static int open_shout_conn(struct audio_output *audio_output)
if (status != 0)
return status;
- if (init_encoder(sd) < 0) {
+ if (sd->encoder->init_encoder_func(sd) < 0) {
shout_close(sd->shout_conn);
return -1;
}
@@ -436,7 +494,7 @@ static void send_metadata(struct shout_data * sd)
if (!sd->opened || !sd->tag)
return;
- if (shout_ogg_encoder_send_metadata(sd, song, size)) {
+ if (sd->encoder->send_metadata_func(sd, song, size)) {
shout_metadata_add(sd->shout_meta, "song", song);
if (SHOUTERR_SUCCESS != shout_set_metadata(sd->shout_conn,
sd->shout_meta)) {
@@ -473,7 +531,10 @@ static int my_shout_play(struct audio_output *audio_output,
}
}
- shout_ogg_encoder_encode(sd, chunk, size);
+ if (sd->encoder->encode_func(sd, chunk, size)) {
+ my_shout_close_device(audio_output);
+ return -1;
+ }
if (write_page(sd) < 0) {
my_shout_close_device(audio_output);
diff --git a/src/audioOutputs/audioOutput_shout.h b/src/audioOutputs/audioOutput_shout.h
index eee2ccad2..c21ddff40 100644
--- a/src/audioOutputs/audioOutput_shout.h
+++ b/src/audioOutputs/audioOutput_shout.h
@@ -27,7 +27,38 @@
#include "../timer.h"
#include <shout/shout.h>
-#include <vorbis/vorbisenc.h>
+
+#define DISABLED_SHOUT_ENCODER_PLUGIN(plugin) shout_encoder_plugin plugin;
+
+typedef struct shout_data shout_data;
+
+typedef int (*shout_encoder_clear_encoder_func) (shout_data * sd);
+typedef int (*shout_encoder_encode_func) (shout_data * sd,
+ const char * chunk,
+ size_t len);
+typedef void (*shout_encoder_finish_func) (shout_data * sd);
+typedef int (*shout_encoder_init_func) (shout_data * sd);
+typedef int (*shout_encoder_init_encoder_func) (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. */
+typedef int (*shout_encoder_send_metadata_func) (shout_data * sd,
+ char * song,
+ size_t size);
+
+typedef struct _shout_encoder_plugin {
+ const char *name;
+ unsigned int shout_format;
+
+ shout_encoder_clear_encoder_func clear_encoder_func;
+ shout_encoder_encode_func encode_func;
+ shout_encoder_finish_func finish_func;
+ shout_encoder_init_func init_func;
+ shout_encoder_init_encoder_func init_encoder_func;
+ shout_encoder_send_metadata_func send_metadata_func;
+} shout_encoder_plugin;
typedef struct _shout_buffer {
unsigned char *data;
@@ -35,26 +66,13 @@ typedef struct _shout_buffer {
size_t max_len;
} shout_buffer;
-typedef 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;
-} ogg_vorbis_data;
-
struct shout_data {
shout_t *shout_conn;
shout_metadata_t *shout_meta;
int shout_error;
- ogg_vorbis_data od;
+ shout_encoder_plugin *encoder;
+ void *encoder_data;
float quality;
int bitrate;
@@ -76,19 +94,7 @@ struct shout_data {
shout_buffer buf;
};
-void copy_tag_to_vorbis_comment(struct shout_data *sd);
-
-int send_ogg_vorbis_header(struct shout_data *sd);
-
-int shout_ogg_encoder_clear_encoder(struct shout_data *sd);
-
-int init_encoder(struct shout_data *sd);
-
-int shout_ogg_encoder_send_metadata(struct shout_data * sd,
- char *song, size_t size);
-
-void shout_ogg_encoder_encode(struct shout_data *sd,
- const char *chunk, size_t len);
+extern shout_encoder_plugin shout_ogg_encoder;
#endif
diff --git a/src/audioOutputs/audioOutput_shout_ogg.c b/src/audioOutputs/audioOutput_shout_ogg.c
index d0d1b6880..5e0adf4d0 100644
--- a/src/audioOutputs/audioOutput_shout_ogg.c
+++ b/src/audioOutputs/audioOutput_shout_ogg.c
@@ -18,9 +18,24 @@
#include "audioOutput_shout.h"
-#ifdef HAVE_SHOUT
+#ifdef HAVE_SHOUT_OGG
#include "../utils.h"
+#include <vorbis/vorbisenc.h>
+
+typedef 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;
+} ogg_vorbis_data;
static void add_tag(ogg_vorbis_data *od, const char *name, char *value)
{
@@ -31,9 +46,9 @@ static void add_tag(ogg_vorbis_data *od, const char *name, char *value)
}
}
-void copy_tag_to_vorbis_comment(struct shout_data *sd)
+static void copy_tag_to_vorbis_comment(struct shout_data *sd)
{
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
if (sd->tag) {
int i;
@@ -49,6 +64,7 @@ void copy_tag_to_vorbis_comment(struct shout_data *sd)
case TAG_ITEM_TITLE:
add_tag(od, "TITLE", sd->tag->items[i]->value);
break;
+
default:
break;
}
@@ -83,7 +99,7 @@ static int copy_ogg_buffer_to_shout_buffer(ogg_page *og,
static int flush_ogg_buffer(struct shout_data *sd)
{
shout_buffer *buf = &sd->buf;
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
int ret = 0;
if (ogg_stream_flush(&od->os, &od->og))
@@ -92,9 +108,9 @@ static int flush_ogg_buffer(struct shout_data *sd)
return ret;
}
-int send_ogg_vorbis_header(struct shout_data *sd)
+static int send_ogg_vorbis_header(struct shout_data *sd)
{
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
vorbis_analysis_headerout(&od->vd, &od->vc,
&od->header_main,
@@ -121,9 +137,9 @@ static void finish_encoder(ogg_vorbis_data *od)
}
}
-int shout_ogg_encoder_clear_encoder(struct shout_data *sd)
+static int shout_ogg_encoder_clear_encoder(struct shout_data *sd)
{
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
int ret;
finish_encoder(od);
@@ -139,9 +155,30 @@ int shout_ogg_encoder_clear_encoder(struct shout_data *sd)
return ret;
}
+static void shout_ogg_encoder_finish(struct shout_data *sd)
+{
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
+
+ if (od) {
+ free(od);
+ sd->encoder_data = NULL;
+ }
+}
+
+static int shout_ogg_encoder_init(struct shout_data *sd)
+{
+ ogg_vorbis_data *od;
+
+ if (NULL == (od = xmalloc(sizeof(ogg_vorbis_data))))
+ FATAL("error initializing ogg vorbis encoder data\n");
+ sd->encoder_data = od;
+
+ return 0;
+}
+
static int reinit_encoder(struct shout_data *sd)
{
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
vorbis_info_init(&od->vi);
@@ -150,7 +187,7 @@ static int reinit_encoder(struct shout_data *sd)
sd->audio_format.channels,
sd->audio_format.sampleRate,
sd->quality * 0.1)) {
- ERROR("problem setting up vorbis encoder for shout\n");
+ ERROR("error initializing vorbis vbr\n");
vorbis_info_clear(&od->vi);
return -1;
}
@@ -159,7 +196,7 @@ static int reinit_encoder(struct shout_data *sd)
sd->audio_format.channels,
sd->audio_format.sampleRate, -1.0,
sd->bitrate * 1000, -1.0)) {
- ERROR("problem setting up vorbis encoder for shout\n");
+ ERROR("error initializing vorbis encoder\n");
vorbis_info_clear(&od->vi);
return -1;
}
@@ -173,7 +210,7 @@ static int reinit_encoder(struct shout_data *sd)
return 0;
}
-int init_encoder(struct shout_data *sd)
+static int shout_ogg_encoder_init_encoder(struct shout_data *sd)
{
if (reinit_encoder(sd))
return -1;
@@ -186,11 +223,11 @@ int init_encoder(struct shout_data *sd)
return 0;
}
-int shout_ogg_encoder_send_metadata(struct shout_data * sd,
- mpd_unused char * song,
- mpd_unused size_t size)
+static int shout_ogg_encoder_send_metadata(struct shout_data *sd,
+ mpd_unused char * song,
+ mpd_unused size_t size)
{
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
shout_ogg_encoder_clear_encoder(sd);
if (reinit_encoder(sd))
@@ -212,8 +249,8 @@ int shout_ogg_encoder_send_metadata(struct shout_data * sd,
return 0;
}
-void shout_ogg_encoder_encode(struct shout_data *sd,
- const char *chunk, size_t size)
+static int shout_ogg_encoder_encode(struct shout_data *sd,
+ const char *chunk, size_t size)
{
shout_buffer *buf = &sd->buf;
unsigned int i;
@@ -221,7 +258,7 @@ void shout_ogg_encoder_encode(struct shout_data *sd,
float **vorbbuf;
unsigned int samples;
int bytes = sd->audio_format.bits / 8;
- ogg_vorbis_data *od = &sd->od;
+ ogg_vorbis_data *od = (ogg_vorbis_data *)sd->encoder_data;
samples = size / (bytes * sd->audio_format.channels);
vorbbuf = vorbis_analysis_buffer(&od->vd, samples);
@@ -248,6 +285,24 @@ void shout_ogg_encoder_encode(struct shout_data *sd,
if (ogg_stream_pageout(&od->os, &od->og))
copy_ogg_buffer_to_shout_buffer(&od->og, buf);
+
+ return 0;
}
+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,
+};
+
+#else
+
+DISABLED_SHOUT_ENCODER_PLUGIN(shout_ogg_encoder);
+
#endif