aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
3 files changed, 177 insertions, 55 deletions
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