Update copyright notices.
[mpd-mk.git] / src / output / shout_plugin.c
bloba8b409be259be2c0af6217491803dee2d3af5d27
1 /*
2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "config.h"
21 #include "output_api.h"
22 #include "encoder_plugin.h"
23 #include "encoder_list.h"
25 #include <shout/shout.h>
26 #include <glib.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <stdio.h>
32 #undef G_LOG_DOMAIN
33 #define G_LOG_DOMAIN "shout"
35 #define DEFAULT_CONN_TIMEOUT 2
37 struct shout_buffer {
38 unsigned char data[32768];
39 size_t len;
42 struct shout_data {
43 shout_t *shout_conn;
44 shout_metadata_t *shout_meta;
46 struct encoder *encoder;
48 float quality;
49 int bitrate;
51 int timeout;
53 struct shout_buffer buf;
56 static int shout_init_count;
58 /**
59 * The quark used for GError.domain.
61 static inline GQuark
62 shout_output_quark(void)
64 return g_quark_from_static_string("shout_output");
67 static const struct encoder_plugin *
68 shout_encoder_plugin_get(const char *name)
70 if (strcmp(name, "ogg") == 0)
71 name = "vorbis";
72 else if (strcmp(name, "mp3") == 0)
73 name = "lame";
75 return encoder_plugin_get(name);
78 static struct shout_data *new_shout_data(void)
80 struct shout_data *ret = g_new(struct shout_data, 1);
82 ret->shout_conn = shout_new();
83 ret->shout_meta = shout_metadata_new();
84 ret->bitrate = -1;
85 ret->quality = -2.0;
86 ret->timeout = DEFAULT_CONN_TIMEOUT;
88 return ret;
91 static void free_shout_data(struct shout_data *sd)
93 if (sd->shout_meta)
94 shout_metadata_free(sd->shout_meta);
95 if (sd->shout_conn)
96 shout_free(sd->shout_conn);
98 g_free(sd);
101 #define check_block_param(name) { \
102 block_param = config_get_block_param(param, name); \
103 if (!block_param) { \
104 g_error("no \"%s\" defined for shout device defined at line " \
105 "%i\n", name, param->line); \
109 static void *
110 my_shout_init_driver(const struct audio_format *audio_format,
111 const struct config_param *param,
112 GError **error)
114 struct shout_data *sd;
115 char *test;
116 unsigned port;
117 char *host;
118 char *mount;
119 char *passwd;
120 const char *encoding;
121 const struct encoder_plugin *encoder_plugin;
122 unsigned shout_format;
123 unsigned protocol;
124 const char *user;
125 char *name;
126 const char *value;
127 struct block_param *block_param;
128 int public;
130 if (audio_format == NULL ||
131 !audio_format_fully_defined(audio_format)) {
132 g_set_error(error, shout_output_quark(), 0,
133 "Need full audio format specification");
134 return NULL;
137 sd = new_shout_data();
139 if (shout_init_count == 0)
140 shout_init();
142 shout_init_count++;
144 check_block_param("host");
145 host = block_param->value;
147 check_block_param("mount");
148 mount = block_param->value;
150 port = config_get_block_unsigned(param, "port", 0);
151 if (port == 0) {
152 g_set_error(error, shout_output_quark(), 0,
153 "shout port must be configured");
154 return NULL;
157 check_block_param("password");
158 passwd = block_param->value;
160 check_block_param("name");
161 name = block_param->value;
163 public = config_get_block_bool(param, "public", false);
165 user = config_get_block_string(param, "user", "source");
167 value = config_get_block_string(param, "quality", NULL);
168 if (value != NULL) {
169 sd->quality = strtod(value, &test);
171 if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) {
172 g_set_error(error, shout_output_quark(), 0,
173 "shout quality \"%s\" is not a number in the "
174 "range -1 to 10, line %i",
175 value, param->line);
176 return NULL;
179 if (config_get_block_string(param, "bitrate", NULL) != NULL) {
180 g_set_error(error, shout_output_quark(), 0,
181 "quality and bitrate are "
182 "both defined");
183 return NULL;
185 } else {
186 value = config_get_block_string(param, "bitrate", NULL);
187 if (value == NULL) {
188 g_set_error(error, shout_output_quark(), 0,
189 "neither bitrate nor quality defined");
190 return NULL;
193 sd->bitrate = strtol(value, &test, 10);
195 if (*test != '\0' || sd->bitrate <= 0) {
196 g_set_error(error, shout_output_quark(), 0,
197 "bitrate must be a positive integer");
198 return NULL;
202 encoding = config_get_block_string(param, "encoding", "ogg");
203 encoder_plugin = shout_encoder_plugin_get(encoding);
204 if (encoder_plugin == NULL) {
205 g_set_error(error, shout_output_quark(), 0,
206 "couldn't find shout encoder plugin \"%s\"",
207 encoding);
208 return NULL;
211 sd->encoder = encoder_init(encoder_plugin, param, error);
212 if (sd->encoder == NULL)
213 return NULL;
215 if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
216 shout_format = SHOUT_FORMAT_MP3;
217 else
218 shout_format = SHOUT_FORMAT_OGG;
220 value = config_get_block_string(param, "protocol", NULL);
221 if (value != NULL) {
222 if (0 == strcmp(value, "shoutcast") &&
223 0 != strcmp(encoding, "mp3")) {
224 g_set_error(error, shout_output_quark(), 0,
225 "you cannot stream \"%s\" to shoutcast, use mp3",
226 encoding);
227 return NULL;
228 } else if (0 == strcmp(value, "shoutcast"))
229 protocol = SHOUT_PROTOCOL_ICY;
230 else if (0 == strcmp(value, "icecast1"))
231 protocol = SHOUT_PROTOCOL_XAUDIOCAST;
232 else if (0 == strcmp(value, "icecast2"))
233 protocol = SHOUT_PROTOCOL_HTTP;
234 else {
235 g_set_error(error, shout_output_quark(), 0,
236 "shout protocol \"%s\" is not \"shoutcast\" or "
237 "\"icecast1\"or \"icecast2\"",
238 value);
239 return NULL;
241 } else {
242 protocol = SHOUT_PROTOCOL_HTTP;
245 if (shout_set_host(sd->shout_conn, host) != SHOUTERR_SUCCESS ||
246 shout_set_port(sd->shout_conn, port) != SHOUTERR_SUCCESS ||
247 shout_set_password(sd->shout_conn, passwd) != SHOUTERR_SUCCESS ||
248 shout_set_mount(sd->shout_conn, mount) != SHOUTERR_SUCCESS ||
249 shout_set_name(sd->shout_conn, name) != SHOUTERR_SUCCESS ||
250 shout_set_user(sd->shout_conn, user) != SHOUTERR_SUCCESS ||
251 shout_set_public(sd->shout_conn, public) != SHOUTERR_SUCCESS ||
252 shout_set_format(sd->shout_conn, shout_format)
253 != SHOUTERR_SUCCESS ||
254 shout_set_protocol(sd->shout_conn, protocol) != SHOUTERR_SUCCESS ||
255 shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
256 g_set_error(error, shout_output_quark(), 0,
257 "%s", shout_get_error(sd->shout_conn));
258 return NULL;
261 /* optional paramters */
262 sd->timeout = config_get_block_unsigned(param, "timeout",
263 DEFAULT_CONN_TIMEOUT);
265 value = config_get_block_string(param, "genre", NULL);
266 if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
267 g_set_error(error, shout_output_quark(), 0,
268 "%s", shout_get_error(sd->shout_conn));
269 return NULL;
272 value = config_get_block_string(param, "description", NULL);
273 if (value != NULL && shout_set_description(sd->shout_conn, value)) {
274 g_set_error(error, shout_output_quark(), 0,
275 "%s", shout_get_error(sd->shout_conn));
276 return NULL;
280 char temp[11];
281 memset(temp, 0, sizeof(temp));
283 snprintf(temp, sizeof(temp), "%u", audio_format->channels);
284 shout_set_audio_info(sd->shout_conn, SHOUT_AI_CHANNELS, temp);
286 snprintf(temp, sizeof(temp), "%u", audio_format->sample_rate);
288 shout_set_audio_info(sd->shout_conn, SHOUT_AI_SAMPLERATE, temp);
290 if (sd->quality >= -1.0) {
291 snprintf(temp, sizeof(temp), "%2.2f", sd->quality);
292 shout_set_audio_info(sd->shout_conn, SHOUT_AI_QUALITY,
293 temp);
294 } else {
295 snprintf(temp, sizeof(temp), "%d", sd->bitrate);
296 shout_set_audio_info(sd->shout_conn, SHOUT_AI_BITRATE,
297 temp);
301 return sd;
304 static bool
305 handle_shout_error(struct shout_data *sd, int err, GError **error)
307 switch (err) {
308 case SHOUTERR_SUCCESS:
309 break;
311 case SHOUTERR_UNCONNECTED:
312 case SHOUTERR_SOCKET:
313 g_set_error(error, shout_output_quark(), err,
314 "Lost shout connection to %s:%i: %s",
315 shout_get_host(sd->shout_conn),
316 shout_get_port(sd->shout_conn),
317 shout_get_error(sd->shout_conn));
318 return false;
320 default:
321 g_set_error(error, shout_output_quark(), err,
322 "connection to %s:%i error: %s",
323 shout_get_host(sd->shout_conn),
324 shout_get_port(sd->shout_conn),
325 shout_get_error(sd->shout_conn));
326 return false;
329 return true;
332 static bool
333 write_page(struct shout_data *sd, GError **error)
335 int err;
337 assert(sd->encoder != NULL);
339 sd->buf.len = encoder_read(sd->encoder,
340 sd->buf.data, sizeof(sd->buf.data));
341 if (sd->buf.len == 0)
342 return true;
344 shout_sync(sd->shout_conn);
345 err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
346 if (!handle_shout_error(sd, err, error))
347 return false;
349 return true;
352 static void close_shout_conn(struct shout_data * sd)
354 sd->buf.len = 0;
356 if (sd->encoder != NULL) {
357 if (encoder_flush(sd->encoder, NULL))
358 write_page(sd, NULL);
360 encoder_close(sd->encoder);
363 if (shout_get_connected(sd->shout_conn) != SHOUTERR_UNCONNECTED &&
364 shout_close(sd->shout_conn) != SHOUTERR_SUCCESS) {
365 g_warning("problem closing connection to shout server: %s\n",
366 shout_get_error(sd->shout_conn));
370 static void my_shout_finish_driver(void *data)
372 struct shout_data *sd = (struct shout_data *)data;
374 encoder_finish(sd->encoder);
376 free_shout_data(sd);
378 shout_init_count--;
380 if (shout_init_count == 0)
381 shout_shutdown();
384 static void my_shout_drop_buffered_audio(void *data)
386 G_GNUC_UNUSED
387 struct shout_data *sd = (struct shout_data *)data;
389 /* needs to be implemented for shout */
392 static void my_shout_close_device(void *data)
394 struct shout_data *sd = (struct shout_data *)data;
396 close_shout_conn(sd);
399 static bool
400 shout_connect(struct shout_data *sd, GError **error)
402 int state;
404 state = shout_open(sd->shout_conn);
405 switch (state) {
406 case SHOUTERR_SUCCESS:
407 case SHOUTERR_CONNECTED:
408 return true;
410 default:
411 g_set_error(error, shout_output_quark(), 0,
412 "problem opening connection to shout server %s:%i: %s",
413 shout_get_host(sd->shout_conn),
414 shout_get_port(sd->shout_conn),
415 shout_get_error(sd->shout_conn));
416 return false;
420 static bool
421 my_shout_open_device(void *data, struct audio_format *audio_format,
422 GError **error)
424 struct shout_data *sd = (struct shout_data *)data;
425 bool ret;
427 ret = shout_connect(sd, error);
428 if (!ret)
429 return false;
431 sd->buf.len = 0;
433 ret = encoder_open(sd->encoder, audio_format, error) &&
434 write_page(sd, error);
435 if (!ret) {
436 shout_close(sd->shout_conn);
437 return false;
440 return true;
443 static size_t
444 my_shout_play(void *data, const void *chunk, size_t size, GError **error)
446 struct shout_data *sd = (struct shout_data *)data;
448 return encoder_write(sd->encoder, chunk, size, error) &&
449 write_page(sd, error)
450 ? size
451 : 0;
454 static bool
455 my_shout_pause(void *data)
457 struct shout_data *sd = (struct shout_data *)data;
458 static const char silence[1020];
460 if (shout_delay(sd->shout_conn) > 500) {
461 /* cap the latency for unpause */
462 g_usleep(500000);
463 return true;
466 return my_shout_play(data, silence, sizeof(silence), NULL);
469 static void
470 shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size)
472 char artist[size];
473 char title[size];
475 artist[0] = 0;
476 title[0] = 0;
478 for (unsigned i = 0; i < tag->num_items; i++) {
479 switch (tag->items[i]->type) {
480 case TAG_ARTIST:
481 strncpy(artist, tag->items[i]->value, size);
482 break;
483 case TAG_TITLE:
484 strncpy(title, tag->items[i]->value, size);
485 break;
487 default:
488 break;
492 snprintf(dest, size, "%s - %s", title, artist);
495 static void my_shout_set_tag(void *data,
496 const struct tag *tag)
498 struct shout_data *sd = (struct shout_data *)data;
499 bool ret;
500 GError *error = NULL;
502 if (sd->encoder->plugin->tag != NULL) {
503 /* encoder plugin supports stream tags */
505 ret = encoder_flush(sd->encoder, &error);
506 if (!ret) {
507 g_warning("%s", error->message);
508 g_error_free(error);
509 return;
512 ret = write_page(sd, NULL);
513 if (!ret)
514 return;
516 ret = encoder_tag(sd->encoder, tag, &error);
517 if (!ret) {
518 g_warning("%s", error->message);
519 g_error_free(error);
521 } else {
522 /* no stream tag support: fall back to icy-metadata */
523 char song[1024];
525 shout_tag_to_metadata(tag, song, sizeof(song));
527 shout_metadata_add(sd->shout_meta, "song", song);
528 if (SHOUTERR_SUCCESS != shout_set_metadata(sd->shout_conn,
529 sd->shout_meta)) {
530 g_warning("error setting shout metadata\n");
534 write_page(sd, NULL);
537 const struct audio_output_plugin shoutPlugin = {
538 .name = "shout",
539 .init = my_shout_init_driver,
540 .finish = my_shout_finish_driver,
541 .open = my_shout_open_device,
542 .play = my_shout_play,
543 .pause = my_shout_pause,
544 .cancel = my_shout_drop_buffered_audio,
545 .close = my_shout_close_device,
546 .send_tag = my_shout_set_tag,