From f6e5c00726cf3322813242b804f3bf2369cfa337 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sun, 22 Feb 2009 17:18:28 +0100 Subject: shout: use the new encoder API Removed shout's encoder plugin API in favor of the new generic encoder plugin API. --- src/output/shout_mp3.c | 193 ------------------------------ src/output/shout_ogg.c | 293 ---------------------------------------------- src/output/shout_plugin.c | 134 +++++++++++++++------ src/output/shout_plugin.h | 33 +----- 4 files changed, 102 insertions(+), 551 deletions(-) delete mode 100644 src/output/shout_mp3.c delete mode 100644 src/output/shout_ogg.c (limited to 'src/output') diff --git a/src/output/shout_mp3.c b/src/output/shout_mp3.c deleted file mode 100644 index a4ed37a14..000000000 --- a/src/output/shout_mp3.c +++ /dev/null @@ -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 - -#include -#include - -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, -}; diff --git a/src/output/shout_ogg.c b/src/output/shout_ogg.c deleted file mode 100644 index 8b769e8c0..000000000 --- a/src/output/shout_ogg.c +++ /dev/null @@ -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 -#include - -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, -}; diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c index f03d80231..536353178 100644 --- a/src/output/shout_plugin.c +++ b/src/output/shout_plugin.c @@ -17,6 +17,8 @@ */ #include "shout_plugin.h" +#include "encoder_plugin.h" +#include "encoder_list.h" #include "config.h" #include @@ -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)) { diff --git a/src/output/shout_plugin.h b/src/output/shout_plugin.h index eb40907d3..4bc6dac90 100644 --- a/src/output/shout_plugin.h +++ b/src/output/shout_plugin.h @@ -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 -- cgit v1.2.3