diff options
Diffstat (limited to 'src/audioOutputs')
-rw-r--r-- | src/audioOutputs/audioOutput_shout.c | 75 | ||||
-rw-r--r-- | src/audioOutputs/audioOutput_shout.h | 64 | ||||
-rw-r--r-- | src/audioOutputs/audioOutput_shout_ogg.c | 93 |
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 |