From ec926539a3a7a09b310e74a4fc84902e0971b29d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 26 Feb 2009 22:04:59 +0100 Subject: output_plugin: report errors with GError Use GLib's GError library for reporting output device failures. Note that some init() methods don't clean up properly after a failure, but that's ok for now, because the MPD core will abort anyway. --- src/output/alsa_plugin.c | 55 +++++++++------ src/output/ao_plugin.c | 52 +++++++++----- src/output/fifo_plugin.c | 73 ++++++++++++------- src/output/jack_plugin.c | 61 ++++++++++------ src/output/mvp_plugin.c | 67 ++++++++++++------ src/output/null_plugin.c | 9 ++- src/output/oss_plugin.c | 75 +++++++++++--------- src/output/osx_plugin.c | 41 +++++++---- src/output/pulse_plugin.c | 26 ++++--- src/output/shout_plugin.c | 173 +++++++++++++++++++++++++--------------------- src/output_init.c | 11 ++- src/output_plugin.h | 34 ++++++--- src/output_thread.c | 21 +++++- 13 files changed, 443 insertions(+), 255 deletions(-) diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c index 1623dcac8..874e4ac93 100644 --- a/src/output/alsa_plugin.c +++ b/src/output/alsa_plugin.c @@ -74,6 +74,15 @@ struct alsa_data { struct mixer *mixer; }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +alsa_output_quark(void) +{ + return g_quark_from_static_string("alsa_output"); +} + static const char * alsa_device(const struct alsa_data *ad) { @@ -130,7 +139,8 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param) static void * alsa_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, + G_GNUC_UNUSED GError **error) { /* no need for pthread_once thread-safety when reading config */ static int free_global_registered; @@ -198,7 +208,8 @@ get_bitformat(const struct audio_format *af) */ static bool alsa_setup(struct alsa_data *ad, struct audio_format *audio_format, - snd_pcm_format_t bitformat) + snd_pcm_format_t bitformat, + GError **error) { snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; @@ -256,17 +267,20 @@ configure_hw: } if (err < 0) { - g_warning("ALSA device \"%s\" does not support %u bit audio: %s\n", - alsa_device(ad), audio_format->bits, snd_strerror(-err)); + g_set_error(error, alsa_output_quark(), err, + "ALSA device \"%s\" does not support %u bit audio: %s", + alsa_device(ad), audio_format->bits, + snd_strerror(-err)); return false; } err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams, &channels); if (err < 0) { - g_warning("ALSA device \"%s\" does not support %i channels: %s\n", - alsa_device(ad), (int)audio_format->channels, - snd_strerror(-err)); + g_set_error(error, alsa_output_quark(), err, + "ALSA device \"%s\" does not support %i channels: %s", + alsa_device(ad), (int)audio_format->channels, + snd_strerror(-err)); return false; } audio_format->channels = (int8_t)channels; @@ -274,8 +288,9 @@ configure_hw: err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams, &sample_rate, NULL); if (err < 0 || sample_rate == 0) { - g_warning("ALSA device \"%s\" does not support %u Hz audio\n", - alsa_device(ad), audio_format->sample_rate); + g_set_error(error, alsa_output_quark(), err, + "ALSA device \"%s\" does not support %u Hz audio", + alsa_device(ad), audio_format->sample_rate); return false; } audio_format->sample_rate = sample_rate; @@ -348,14 +363,14 @@ configure_hw: return true; error: - g_warning("Error opening ALSA device \"%s\" (%s): %s\n", - alsa_device(ad), cmd, snd_strerror(-err)); - + g_set_error(error, alsa_output_quark(), err, + "Error opening ALSA device \"%s\" (%s): %s", + alsa_device(ad), cmd, snd_strerror(-err)); return false; } static bool -alsa_open(void *data, struct audio_format *audio_format) +alsa_open(void *data, struct audio_format *audio_format, GError **error) { struct alsa_data *ad = data; snd_pcm_format_t bitformat; @@ -378,12 +393,13 @@ alsa_open(void *data, struct audio_format *audio_format) if (err < 0) { ad->pcm = NULL; - g_warning("Error opening ALSA device \"%s\": %s\n", - alsa_device(ad), snd_strerror(-err)); + g_set_error(error, alsa_output_quark(), err, + "Failed to open ALSA device \"%s\": %s", + alsa_device(ad), snd_strerror(err)); return false; } - success = alsa_setup(ad, audio_format, bitformat); + success = alsa_setup(ad, audio_format, bitformat, error); if (!success) { snd_pcm_close(ad->pcm); ad->pcm = NULL; @@ -463,7 +479,7 @@ alsa_close(void *data) } static size_t -alsa_play(void *data, const void *chunk, size_t size) +alsa_play(void *data, const void *chunk, size_t size, GError **error) { struct alsa_data *ad = data; int ret; @@ -477,9 +493,8 @@ alsa_play(void *data, const void *chunk, size_t size) if (ret < 0 && ret != -EAGAIN && ret != -EINTR && alsa_recover(ad, ret) < 0) { - g_warning("closing ALSA device \"%s\" due to write " - "error: %s\n", - alsa_device(ad), snd_strerror(-errno)); + g_set_error(error, alsa_output_quark(), errno, + "%s", snd_strerror(-errno)); return 0; } } diff --git a/src/output/ao_plugin.c b/src/output/ao_plugin.c index 565384b7b..09d345fd9 100644 --- a/src/output/ao_plugin.c +++ b/src/output/ao_plugin.c @@ -33,8 +33,14 @@ struct ao_data { ao_device *device; } AoData; +static inline GQuark +ao_output_quark(void) +{ + return g_quark_from_static_string("ao_output"); +} + static void -ao_output_error(const char *msg) +ao_output_error(GError **error_r) { const char *error; @@ -63,12 +69,14 @@ ao_output_error(const char *msg) error = strerror(errno); } - g_warning("%s: %s\n", msg, error); + g_set_error(error_r, ao_output_quark(), errno, + "%s", error); } static void * ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, + GError **error) { struct ao_data *ad = g_new(struct ao_data, 1); ao_info *ai; @@ -84,15 +92,22 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, ao_output_ref++; value = config_get_block_string(param, "driver", "default"); - if (0 == strcmp(value, "default")) { + if (0 == strcmp(value, "default")) ad->driver = ao_default_driver_id(); - } else if ((ad->driver = ao_driver_id(value)) < 0) - g_error("\"%s\" is not a valid ao driver at line %i\n", - value, param->line); + else + ad->driver = ao_driver_id(value); + + if (ad->driver < 0) { + g_set_error(error, ao_output_quark(), 0, + "\"%s\" is not a valid ao driver", + value); + return NULL; + } if ((ai = ao_driver_info(ad->driver)) == NULL) { - g_error("problems getting driver info for device defined at line %i\n" - "you may not have permission to the audio device\n", param->line); + g_set_error(error, ao_output_quark(), 0, + "problems getting driver info"); + return NULL; } g_debug("using ao driver \"%s\" for \"%s\"\n", ai->short_name, @@ -105,9 +120,12 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, for (unsigned i = 0; options[i] != NULL; ++i) { gchar **key_value = g_strsplit(options[i], "=", 2); - if (key_value[0] == NULL || key_value[1] == NULL) - g_error("problems parsing options \"%s\"\n", - options[i]); + if (key_value[0] == NULL || key_value[1] == NULL) { + g_set_error(error, ao_output_quark(), 0, + "problems parsing options \"%s\"", + options[i]); + return NULL; + } ao_append_option(&ad->options, key_value[0], key_value[1]); @@ -144,7 +162,8 @@ ao_output_close(void *data) } static bool -ao_output_open(void *data, struct audio_format *audio_format) +ao_output_open(void *data, struct audio_format *audio_format, + GError **error) { ao_sample_format format; struct ao_data *ad = (struct ao_data *)data; @@ -163,7 +182,7 @@ ao_output_open(void *data, struct audio_format *audio_format) ad->device = ao_open_live(ad->driver, &format, ad->options); if (ad->device == NULL) { - ao_output_error("Failed to open libao"); + ao_output_error(error); return false; } @@ -188,7 +207,8 @@ static int ao_play_deconst(ao_device *device, const void *output_samples, } static size_t -ao_output_play(void *data, const void *chunk, size_t size) +ao_output_play(void *data, const void *chunk, size_t size, + GError **error) { struct ao_data *ad = (struct ao_data *)data; @@ -196,7 +216,7 @@ ao_output_play(void *data, const void *chunk, size_t size) size = ad->write_size; if (ao_play_deconst(ad->device, chunk, size) == 0) { - ao_output_error("Closing libao device due to play error"); + ao_output_error(error); return 0; } diff --git a/src/output/fifo_plugin.c b/src/output/fifo_plugin.c index 47f0346a7..449faa24c 100644 --- a/src/output/fifo_plugin.c +++ b/src/output/fifo_plugin.c @@ -42,6 +42,15 @@ struct fifo_data { Timer *timer; }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +fifo_output_quark(void) +{ + return g_quark_from_static_string("fifo_output"); +} + static struct fifo_data *fifo_data_new(void) { struct fifo_data *ret; @@ -95,11 +104,12 @@ fifo_close(struct fifo_data *fd) } static bool -fifo_make(struct fifo_data *fd) +fifo_make(struct fifo_data *fd, GError **error) { if (mkfifo(fd->path, 0666) < 0) { - g_warning("Couldn't create FIFO \"%s\": %s", - fd->path, strerror(errno)); + g_set_error(error, fifo_output_quark(), errno, + "Couldn't create FIFO \"%s\": %s", + fd->path, strerror(errno)); return false; } @@ -109,24 +119,26 @@ fifo_make(struct fifo_data *fd) } static bool -fifo_check(struct fifo_data *fd) +fifo_check(struct fifo_data *fd, GError **error) { struct stat st; if (stat(fd->path, &st) < 0) { if (errno == ENOENT) { /* Path doesn't exist */ - return fifo_make(fd); + return fifo_make(fd, error); } - g_warning("Failed to stat FIFO \"%s\": %s", - fd->path, strerror(errno)); + g_set_error(error, fifo_output_quark(), errno, + "Failed to stat FIFO \"%s\": %s", + fd->path, strerror(errno)); return false; } if (!S_ISFIFO(st.st_mode)) { - g_warning("\"%s\" already exists, but is not a FIFO", - fd->path); + g_set_error(error, fifo_output_quark(), 0, + "\"%s\" already exists, but is not a FIFO", + fd->path); return false; } @@ -134,23 +146,25 @@ fifo_check(struct fifo_data *fd) } static bool -fifo_open(struct fifo_data *fd) +fifo_open(struct fifo_data *fd, GError **error) { - if (!fifo_check(fd)) + if (!fifo_check(fd, error)) return false; fd->input = open(fd->path, O_RDONLY|O_NONBLOCK); if (fd->input < 0) { - g_warning("Could not open FIFO \"%s\" for reading: %s", - fd->path, strerror(errno)); + g_set_error(error, fifo_output_quark(), errno, + "Could not open FIFO \"%s\" for reading: %s", + fd->path, strerror(errno)); fifo_close(fd); return false; } fd->output = open(fd->path, O_WRONLY|O_NONBLOCK); if (fd->output < 0) { - g_warning("Could not open FIFO \"%s\" for writing: %s", - fd->path, strerror(errno)); + g_set_error(error, fifo_output_quark(), errno, + "Could not open FIFO \"%s\" for writing: %s", + fd->path, strerror(errno)); fifo_close(fd); return false; } @@ -160,27 +174,31 @@ fifo_open(struct fifo_data *fd) static void * fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, + GError **error) { struct fifo_data *fd; char *value, *path; value = config_dup_block_string(param, "path", NULL); - if (value == NULL) - g_error("No \"path\" parameter specified for fifo output " - "defined at line %i", param->line); + if (value == NULL) { + g_set_error(error, fifo_output_quark(), errno, + "No \"path\" parameter specified"); + return NULL; + } path = parsePath(value); g_free(value); if (!path) { - g_error("Could not parse \"path\" parameter for fifo output " - "at line %i", param->line); + g_set_error(error, fifo_output_quark(), errno, + "Could not parse \"path\" parameter"); + return NULL; } fd = fifo_data_new(); fd->path = path; - if (!fifo_open(fd)) { + if (!fifo_open(fd, error)) { fifo_data_free(fd); return NULL; } @@ -198,7 +216,8 @@ fifo_output_finish(void *data) } static bool -fifo_output_open(void *data, struct audio_format *audio_format) +fifo_output_open(void *data, struct audio_format *audio_format, + G_GNUC_UNUSED GError **error) { struct fifo_data *fd = (struct fifo_data *)data; @@ -234,7 +253,8 @@ fifo_output_cancel(void *data) } static size_t -fifo_output_play(void *data, const void *chunk, size_t size) +fifo_output_play(void *data, const void *chunk, size_t size, + GError **error) { struct fifo_data *fd = (struct fifo_data *)data; ssize_t bytes; @@ -261,8 +281,9 @@ fifo_output_play(void *data, const void *chunk, size_t size) continue; } - g_warning("Closing FIFO output \"%s\" due to write error: %s", - fd->path, strerror(errno)); + g_set_error(error, fifo_output_quark(), errno, + "Failed to write to FIFO %s: %s", + fd->path, g_strerror(errno)); return 0; } } diff --git a/src/output/jack_plugin.c b/src/output/jack_plugin.c index a04d37f00..6b3dd9d03 100644 --- a/src/output/jack_plugin.c +++ b/src/output/jack_plugin.c @@ -58,6 +58,15 @@ struct jack_data { bool shutdown; }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +jack_output_quark(void) +{ + return g_quark_from_static_string("jack_output"); +} + static void mpd_jack_client_free(struct jack_data *jd) { @@ -158,7 +167,7 @@ mpd_jack_info(const char *msg) static void * mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, GError **error) { struct jack_data *jd; const char *value; @@ -172,9 +181,12 @@ mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format, if (value != NULL) { char **ports = g_strsplit(value, ",", 0); - if (ports[0] == NULL || ports[1] == NULL || ports[2] != NULL) - g_error("two port names expected in line %d", - param->line); + if (ports[0] == NULL || ports[1] == NULL || ports[2] != NULL) { + g_set_error(error, jack_output_quark(), 0, + "two port names expected in line %d", + param->line); + return NULL; + } jd->output_ports[0] = ports[0]; jd->output_ports[1] = ports[1]; @@ -204,7 +216,7 @@ mpd_jack_test_default_device(void) } static bool -mpd_jack_connect(struct jack_data *jd) +mpd_jack_connect(struct jack_data *jd, GError **error) { const char *output_ports[2], **jports; @@ -215,7 +227,8 @@ mpd_jack_connect(struct jack_data *jd) jd->shutdown = false; if ((jd->client = jack_client_new(jd->name)) == NULL) { - g_warning("jack server not running?"); + g_set_error(error, jack_output_quark(), 0, + "Failed to connect to JACK server"); return false; } @@ -227,14 +240,16 @@ mpd_jack_connect(struct jack_data *jd) JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (jd->ports[i] == NULL) { - g_warning("Cannot register %s output port.", - port_names[i]); + g_set_error(error, jack_output_quark(), 0, + "Cannot register output port \"%s\"", + port_names[i]); return false; } } if ( jack_activate(jd->client) ) { - g_warning("cannot activate client"); + g_set_error(error, jack_output_quark(), 0, + "cannot activate client"); return false; } @@ -244,7 +259,8 @@ mpd_jack_connect(struct jack_data *jd) jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); if (jports == NULL) { - g_warning("no ports found"); + g_set_error(error, jack_output_quark(), 0, + "no ports found"); return false; } @@ -267,8 +283,9 @@ mpd_jack_connect(struct jack_data *jd) ret = jack_connect(jd->client, jack_port_name(jd->ports[i]), output_ports[i]); if (ret != 0) { - g_warning("%s is not a valid Jack Client / Port", - output_ports[i]); + g_set_error(error, jack_output_quark(), 0, + "Not a valid JACK port: %s", + output_ports[i]); if (jports != NULL) free(jports); @@ -284,13 +301,13 @@ mpd_jack_connect(struct jack_data *jd) } static bool -mpd_jack_open(void *data, struct audio_format *audio_format) +mpd_jack_open(void *data, struct audio_format *audio_format, GError **error) { struct jack_data *jd = data; assert(jd != NULL); - if (!mpd_jack_connect(jd)) { + if (!mpd_jack_connect(jd, error)) { mpd_jack_client_free(jd); return false; } @@ -381,21 +398,23 @@ mpd_jack_write_samples(struct jack_data *jd, const void *src, } static size_t -mpd_jack_play(void *data, const void *chunk, size_t size) +mpd_jack_play(void *data, const void *chunk, size_t size, GError **error) { struct jack_data *jd = data; const size_t frame_size = audio_format_frame_size(&jd->audio_format); size_t space = 0, space1; - if (jd->shutdown) { - g_warning("Refusing to play, because there is no client thread."); - return 0; - } - assert(size % frame_size == 0); size /= frame_size; - while (!jd->shutdown) { + while (true) { + if (jd->shutdown) { + g_set_error(error, jack_output_quark(), 0, + "Refusing to play, because " + "there is no client thread"); + return 0; + } + space = jack_ringbuffer_write_space(jd->ringbuffer[0]); space1 = jack_ringbuffer_write_space(jd->ringbuffer[1]); if (space > space1) diff --git a/src/output/mvp_plugin.c b/src/output/mvp_plugin.c index 65df5a2aa..b9004029b 100644 --- a/src/output/mvp_plugin.c +++ b/src/output/mvp_plugin.c @@ -83,6 +83,15 @@ static const unsigned mvp_sample_rates[][3] = { {15, 96000, 48000} }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +mvp_output_quark(void) +{ + return g_quark_from_static_string("mvp_output"); +} + /** * Translate a sample rate to a MVP sample rate. * @@ -118,7 +127,8 @@ mvp_output_test_default_device(void) static void * mvp_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - G_GNUC_UNUSED const struct config_param *param) + G_GNUC_UNUSED const struct config_param *param, + G_GNUC_UNUSED GError **error) { struct mvp_data *md = g_new(struct mvp_data, 1); md->fd = -1; @@ -134,7 +144,8 @@ mvp_output_finish(void *data) } static bool -mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format) +mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format, + GError **error) { unsigned mix[5]; @@ -181,23 +192,27 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format) */ mix[2] = mvp_find_sample_rate(audio_format->sample_rate); if (mix[2] == (unsigned)-1) { - g_warning("Can not find suitable output frequency for %u\n", - audio_format->sample_rate); + g_set_error(error, mvp_output_quark(), 0, + "Can not find suitable output frequency for %u", + audio_format->sample_rate); return false; } if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) { - g_warning("Can not set audio format\n"); + g_set_error(error, mvp_output_quark(), errno, + "Can not set audio format"); return false; } if (ioctl(md->fd, MVP_SET_AUD_SYNC, 2) != 0) { - g_warning("Can not set audio sync\n"); + g_set_error(error, mvp_output_quark(), errno, + "Can not set audio sync"); return false; } if (ioctl(md->fd, MVP_SET_AUD_PLAY, 0) < 0) { - g_warning("Can not set audio play mode\n"); + g_set_error(error, mvp_output_quark(), errno, + "Can not set audio play mode"); return false; } @@ -205,7 +220,7 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format) } static bool -mvp_output_open(void *data, struct audio_format *audio_format) +mvp_output_open(void *data, struct audio_format *audio_format, GError **error) { struct mvp_data *md = data; long long int stc = 0; @@ -213,33 +228,38 @@ mvp_output_open(void *data, struct audio_format *audio_format) bool success; if ((md->fd = open("/dev/adec_pcm", O_RDWR | O_NONBLOCK)) < 0) { - g_warning("Error opening /dev/adec_pcm: %s\n", - strerror(errno)); + g_set_error(error, mvp_output_quark(), errno, + "Error opening /dev/adec_pcm: %s", + strerror(errno)); return false; } if (ioctl(md->fd, MVP_SET_AUD_SRC, 1) < 0) { - g_warning("Error setting audio source: %s\n", - strerror(errno)); + g_set_error(error, mvp_output_quark(), errno, + "Error setting audio source: %s", + strerror(errno)); return false; } if (ioctl(md->fd, MVP_SET_AUD_STREAMTYPE, 0) < 0) { - g_warning("Error setting audio streamtype: %s\n", - strerror(errno)); + g_set_error(error, mvp_output_quark(), errno, + "Error setting audio streamtype: %s", + strerror(errno)); return false; } if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) { - g_warning("Error setting audio format: %s\n", - strerror(errno)); + g_set_error(error, mvp_output_quark(), errno, + "Error setting audio format: %s", + strerror(errno)); return false; } ioctl(md->fd, MVP_SET_AUD_STC, &stc); if (ioctl(md->fd, MVP_SET_AUD_BYPASS, 1) < 0) { - g_warning("Error setting audio streamtype: %s\n", - strerror(errno)); + g_set_error(error, mvp_output_quark(), errno, + "Error setting audio streamtype: %s", + strerror(errno)); return false; } - success = mvp_set_pcm_params(md, audio_format); + success = mvp_set_pcm_params(md, audio_format, error); if (!success) return false; @@ -266,7 +286,7 @@ static void mvp_output_cancel(void *data) } static size_t -mvp_output_play(void *data, const void *chunk, size_t size) +mvp_output_play(void *data, const void *chunk, size_t size, GError **error) { struct mvp_data *md = data; ssize_t ret; @@ -275,7 +295,7 @@ mvp_output_play(void *data, const void *chunk, size_t size) if (md->fd < 0) { bool success; - success = mvp_output_open(md, &md->audio_format); + success = mvp_output_open(md, &md->audio_format, error); if (!success) return 0; } @@ -288,8 +308,9 @@ mvp_output_play(void *data, const void *chunk, size_t size) if (ret < 0) { if (errno == EINTR) continue; - g_warning("closing mvp PCM device due to write error: " - "%s\n", strerror(errno)); + + g_set_error(error, mvp_output_quark(), errno, + "Failed to write: %s", strerror(errno)); return 0; } } diff --git a/src/output/null_plugin.c b/src/output/null_plugin.c index a10f5e45f..03a4b7fac 100644 --- a/src/output/null_plugin.c +++ b/src/output/null_plugin.c @@ -31,7 +31,8 @@ struct null_data { static void * null_init(G_GNUC_UNUSED const struct audio_format *audio_format, - G_GNUC_UNUSED const struct config_param *param) + G_GNUC_UNUSED const struct config_param *param, + G_GNUC_UNUSED GError **error) { struct null_data *nd = g_new(struct null_data, 1); @@ -52,7 +53,8 @@ null_finish(void *data) } static bool -null_open(void *data, struct audio_format *audio_format) +null_open(void *data, struct audio_format *audio_format, + G_GNUC_UNUSED GError **error) { struct null_data *nd = data; @@ -74,7 +76,8 @@ null_close(void *data) } static size_t -null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size) +null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size, + G_GNUC_UNUSED GError **error) { struct null_data *nd = data; Timer *timer = nd->timer; diff --git a/src/output/oss_plugin.c b/src/output/oss_plugin.c index b070b9a9d..d4ecf88f7 100644 --- a/src/output/oss_plugin.c +++ b/src/output/oss_plugin.c @@ -72,6 +72,15 @@ enum oss_param { OSS_BITS = 2, }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +oss_output_quark(void) +{ + return g_quark_from_static_string("oss_output"); +} + static enum oss_param oss_param_from_ioctl(unsigned param) { @@ -353,7 +362,7 @@ oss_output_test_default_device(void) } static void * -oss_open_default(const struct config_param *param) +oss_open_default(GError **error) { int i; int err[G_N_ELEMENTS(default_devices)]; @@ -364,17 +373,11 @@ oss_open_default(const struct config_param *param) if (ret[i] == OSS_STAT_NO_ERROR) { struct oss_data *od = oss_data_new(); od->device = default_devices[i]; - od->mixer = mixer_new(&oss_mixer, param); + od->mixer = mixer_new(&oss_mixer, NULL); return od; } } - if (param) - g_warning("error trying to open specified OSS device" - " at line %i\n", param->line); - else - g_warning("error trying to open default OSS device\n"); - for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) { const char *dev = default_devices[i]; switch(ret[i]) { @@ -395,13 +398,16 @@ oss_open_default(const struct config_param *param) dev, strerror(err[i])); } } - exit(EXIT_FAILURE); - return NULL; /* some compilers can be dumb... */ + + g_set_error(error, oss_output_quark(), 0, + "error trying to open default OSS device"); + return NULL; } static void * oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, + GError **error) { const char *device = config_get_block_string(param, "device", NULL); if (device != NULL) { @@ -411,7 +417,7 @@ oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, return od; } - return oss_open_default(param); + return oss_open_default(error); } static void @@ -473,24 +479,26 @@ oss_close(struct oss_data *od) * Sets up the OSS device which was opened before. */ static bool -oss_setup(struct oss_data *od) +oss_setup(struct oss_data *od, GError **error) { int tmp; tmp = od->audio_format.channels; if (oss_set_param(od, SNDCTL_DSP_CHANNELS, &tmp)) { - g_warning("OSS device \"%s\" does not support %u channels: %s\n", - od->device, od->audio_format.channels, - strerror(errno)); + g_set_error(error, oss_output_quark(), errno, + "OSS device \"%s\" does not support %u channels: %s", + od->device, od->audio_format.channels, + strerror(errno)); return false; } od->audio_format.channels = tmp; tmp = od->audio_format.sample_rate; if (oss_set_param(od, SNDCTL_DSP_SPEED, &tmp)) { - g_warning("OSS device \"%s\" does not support %u Hz audio: %s\n", - od->device, od->audio_format.sample_rate, - strerror(errno)); + g_set_error(error, oss_output_quark(), errno, + "OSS device \"%s\" does not support %u Hz audio: %s", + od->device, od->audio_format.sample_rate, + strerror(errno)); return false; } od->audio_format.sample_rate = tmp; @@ -511,8 +519,9 @@ oss_setup(struct oss_data *od) } if (oss_set_param(od, SNDCTL_DSP_SAMPLESIZE, &tmp)) { - g_warning("OSS device \"%s\" does not support %u bit audio: %s\n", - od->device, tmp, strerror(errno)); + g_set_error(error, oss_output_quark(), errno, + "OSS device \"%s\" does not support %u bit audio: %s", + od->device, tmp, strerror(errno)); return false; } @@ -520,17 +529,18 @@ oss_setup(struct oss_data *od) } static bool -oss_open(struct oss_data *od) +oss_open(struct oss_data *od, GError **error) { bool success; if ((od->fd = open(od->device, O_WRONLY)) < 0) { - g_warning("Error opening OSS device \"%s\": %s\n", od->device, - strerror(errno)); + g_set_error(error, oss_output_quark(), errno, + "Error opening OSS device \"%s\": %s", + od->device, strerror(errno)); return false; } - success = oss_setup(od); + success = oss_setup(od, error); if (!success) { oss_close(od); return false; @@ -540,14 +550,14 @@ oss_open(struct oss_data *od) } static bool -oss_output_open(void *data, struct audio_format *audio_format) +oss_output_open(void *data, struct audio_format *audio_format, GError **error) { bool ret; struct oss_data *od = data; od->audio_format = *audio_format; - ret = oss_open(od); + ret = oss_open(od, error); if (!ret) return false; @@ -584,14 +594,14 @@ oss_output_cancel(void *data) } static size_t -oss_output_play(void *data, const void *chunk, size_t size) +oss_output_play(void *data, const void *chunk, size_t size, GError **error) { struct oss_data *od = data; ssize_t ret; /* reopen the device since it was closed by dropBufferedAudio */ - if (od->fd < 0 && !oss_open(od)) - return false; + if (od->fd < 0 && !oss_open(od, error)) + return 0; while (true) { ret = write(od->fd, chunk, size); @@ -599,8 +609,9 @@ oss_output_play(void *data, const void *chunk, size_t size) return (size_t)ret; if (ret < 0 && errno != EINTR) { - g_warning("closing oss device \"%s\" due to write error: " - "%s\n", od->device, strerror(errno)); + g_set_error(error, oss_output_quark(), errno, + "Write error on %s: %s", + od->device, strerror(errno)); return 0; } } diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c index fc3025ca1..7aa7659a8 100644 --- a/src/output/osx_plugin.c +++ b/src/output/osx_plugin.c @@ -34,6 +34,15 @@ struct osx_output { size_t len; }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +osx_output_quark(void) +{ + return g_quark_from_static_string("osx_output"); +} + static bool osx_output_test_default_device(void) { @@ -44,7 +53,8 @@ osx_output_test_default_device(void) static void * osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - G_GNUC_UNUSED const struct config_param *param) + G_GNUC_UNUSED const struct config_param *param, + G_GNUC_UNUSED GError **error) { struct osx_output *oo = g_new(struct osx_output, 1); @@ -143,7 +153,7 @@ osx_render(void *vdata, } static bool -osx_output_open(void *data, struct audio_format *audio_format) +osx_output_open(void *data, struct audio_format *audio_format, GError **error) { struct osx_output *od = data; ComponentDescription desc; @@ -164,22 +174,25 @@ osx_output_open(void *data, struct audio_format *audio_format) comp = FindNextComponent(NULL, &desc); if (comp == 0) { - g_warning("Error finding OS X component\n"); + g_set_error(error, osx_output_quark(), 0, + "Error finding OS X component"); return false; } status = OpenAComponent(comp, &od->au); if (status != noErr) { - g_warning("Unable to open OS X component: %s", - GetMacOSStatusCommentString(status)); + g_set_error(error, osx_output_quark(), 0, + "Unable to open OS X component: %s", + GetMacOSStatusCommentString(status)); return false; } status = AudioUnitInitialize(od->au); if (status != noErr) { CloseComponent(od->au); - g_warning("Unable to initialize OS X audio unit: %s", - GetMacOSStatusCommentString(status)); + g_set_error(error, osx_output_quark(), 0, + "Unable to initialize OS X audio unit: %s", + GetMacOSStatusCommentString(status)); return false; } @@ -193,7 +206,8 @@ osx_output_open(void *data, struct audio_format *audio_format) if (result != noErr) { AudioUnitUninitialize(od->au); CloseComponent(od->au); - g_warning("unable to set callback for OS X audio unit\n"); + g_set_error(error, osx_output_quark(), 0, + "unable to set callback for OS X audio unit"); return false; } @@ -218,7 +232,8 @@ osx_output_open(void *data, struct audio_format *audio_format) if (result != noErr) { AudioUnitUninitialize(od->au); CloseComponent(od->au); - g_warning("Unable to set format on OS X device\n"); + g_set_error(error, osx_output_quark(), 0, + "Unable to set format on OS X device"); return false; } @@ -232,8 +247,9 @@ osx_output_open(void *data, struct audio_format *audio_format) status = AudioOutputUnitStart(od->au); if (status != 0) { - g_warning("unable to start audio output: %s", - GetMacOSStatusCommentString(status)); + g_set_error(error, osx_output_quark(), 0, + "unable to start audio output: %s", + GetMacOSStatusCommentString(status)); return false; } @@ -241,7 +257,8 @@ osx_output_open(void *data, struct audio_format *audio_format) } static size_t -osx_output_play(void *data, const void *chunk, size_t size) +osx_output_play(void *data, const void *chunk, size_t size, + G_GNUC_UNUSED GError **error) { struct osx_output *od = data; size_t start, nbytes; diff --git a/src/output/pulse_plugin.c b/src/output/pulse_plugin.c index b8fe5ad88..dcdd427db 100644 --- a/src/output/pulse_plugin.c +++ b/src/output/pulse_plugin.c @@ -32,6 +32,15 @@ struct pulse_data { char *sink; }; +/** + * The quark used for GError.domain. + */ +static inline GQuark +pulse_output_quark(void) +{ + return g_quark_from_static_string("pulse_output"); +} + static struct pulse_data *pulse_new_data(void) { struct pulse_data *ret; @@ -53,7 +62,7 @@ static void pulse_free_data(struct pulse_data *pd) static void * pulse_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, G_GNUC_UNUSED GError **error) { struct pulse_data *pd; @@ -98,7 +107,7 @@ static bool pulse_test_default_device(void) } static bool -pulse_open(void *data, struct audio_format *audio_format) +pulse_open(void *data, struct audio_format *audio_format, GError **error_r) { struct pulse_data *pd = data; pa_sample_spec ss; @@ -117,9 +126,9 @@ pulse_open(void *data, struct audio_format *audio_format) &ss, NULL, NULL, &error); if (!pd->s) { - g_warning("Cannot connect to server in PulseAudio output " - "\"%s\": %s\n", - pd->name, pa_strerror(error)); + g_set_error(error_r, pulse_output_quark(), error, + "Cannot connect to PulseAudio server: %s", + pa_strerror(error)); return false; } @@ -151,15 +160,14 @@ static void pulse_close(void *data) } static size_t -pulse_play(void *data, const void *chunk, size_t size) +pulse_play(void *data, const void *chunk, size_t size, GError **error_r) { struct pulse_data *pd = data; int error; if (pa_simple_write(pd->s, chunk, size, &error) < 0) { - g_warning("PulseAudio output \"%s\" disconnecting due to " - "write error: %s\n", - pd->name, pa_strerror(error)); + g_set_error(error_r, pulse_output_quark(), error, + "%s", pa_strerror(error)); return 0; } diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c index 2a8f2f189..2ad7e7b56 100644 --- a/src/output/shout_plugin.c +++ b/src/output/shout_plugin.c @@ -53,6 +53,15 @@ struct shout_data { static int shout_init_count; +/** + * The quark used for GError.domain. + */ +static inline GQuark +shout_output_quark(void) +{ + return g_quark_from_static_string("shout_output"); +} + static const struct encoder_plugin * shout_encoder_plugin_get(const char *name) { @@ -97,7 +106,8 @@ static void free_shout_data(struct shout_data *sd) static void * my_shout_init_driver(const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, + GError **error) { struct shout_data *sd; char *test; @@ -107,7 +117,6 @@ my_shout_init_driver(const struct audio_format *audio_format, char *passwd; const char *encoding; const struct encoder_plugin *encoder_plugin; - GError *error = NULL; unsigned shout_format; unsigned protocol; const char *user; @@ -131,8 +140,9 @@ my_shout_init_driver(const struct audio_format *audio_format, port = config_get_block_unsigned(param, "port", 0); if (port == 0) { - g_error("shout port \"%s\" is not a positive integer, line %i\n", - block_param->value, block_param->line); + g_set_error(error, shout_output_quark(), 0, + "shout port must be configured"); + return NULL; } check_block_param("password"); @@ -150,27 +160,33 @@ my_shout_init_driver(const struct audio_format *audio_format, sd->quality = strtod(value, &test); if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) { - g_error("shout quality \"%s\" is not a number in the " - "range -1 to 10, line %i", - value, param->line); + g_set_error(error, shout_output_quark(), 0, + "shout quality \"%s\" is not a number in the " + "range -1 to 10, line %i", + value, param->line); + return NULL; } if (config_get_block_string(param, "bitrate", NULL) != NULL) { - g_error("quality and bitrate are " - "both defined for shout output (line %i)", - param->line); + g_set_error(error, shout_output_quark(), 0, + "quality and bitrate are " + "both defined"); + return NULL; } } else { value = config_get_block_string(param, "bitrate", NULL); - if (value == NULL) - g_error("neither bitrate nor quality defined for shout " - "output at line %i", param->line); + if (value == NULL) { + g_set_error(error, shout_output_quark(), 0, + "neither bitrate nor quality defined"); + return NULL; + } sd->bitrate = strtol(value, &test, 10); if (*test != '\0' || sd->bitrate <= 0) { - g_error("bitrate at line %i should be a positive integer " - "\n", param->line); + g_set_error(error, shout_output_quark(), 0, + "bitrate must be a positive integer"); + return NULL; } } @@ -178,13 +194,16 @@ my_shout_init_driver(const struct audio_format *audio_format, encoding = config_get_block_string(param, "encoding", "ogg"); encoder_plugin = shout_encoder_plugin_get(encoding); - if (encoder_plugin == NULL) - g_error("couldn't find shout encoder plugin \"%s\"\n", - encoding); + if (encoder_plugin == NULL) { + g_set_error(error, shout_output_quark(), 0, + "couldn't find shout encoder plugin \"%s\"", + encoding); + return NULL; + } - sd->encoder = encoder_init(encoder_plugin, param, &error); + sd->encoder = encoder_init(encoder_plugin, param, error); if (sd->encoder == NULL) - g_error("%s", error->message); + return NULL; if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0) shout_format = SHOUT_FORMAT_MP3; @@ -194,20 +213,24 @@ my_shout_init_driver(const struct audio_format *audio_format, value = config_get_block_string(param, "protocol", NULL); if (value != NULL) { if (0 == strcmp(value, "shoutcast") && - 0 != strcmp(encoding, "mp3")) - g_error("you cannot stream \"%s\" to shoutcast, use mp3\n", - encoding); - else if (0 == strcmp(value, "shoutcast")) + 0 != strcmp(encoding, "mp3")) { + g_set_error(error, shout_output_quark(), 0, + "you cannot stream \"%s\" to shoutcast, use mp3", + encoding); + return NULL; + } else if (0 == strcmp(value, "shoutcast")) protocol = SHOUT_PROTOCOL_ICY; else if (0 == strcmp(value, "icecast1")) protocol = SHOUT_PROTOCOL_XAUDIOCAST; else if (0 == strcmp(value, "icecast2")) protocol = SHOUT_PROTOCOL_HTTP; - else - g_error("shout protocol \"%s\" is not \"shoutcast\" or " - "\"icecast1\"or " - "\"icecast2\", line %i\n", - value, param->line); + else { + g_set_error(error, shout_output_quark(), 0, + "shout protocol \"%s\" is not \"shoutcast\" or " + "\"icecast1\"or \"icecast2\"", + value); + return NULL; + } } else { protocol = SHOUT_PROTOCOL_HTTP; } @@ -223,8 +246,9 @@ my_shout_init_driver(const struct audio_format *audio_format, != SHOUTERR_SUCCESS || shout_set_protocol(sd->shout_conn, protocol) != SHOUTERR_SUCCESS || shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) { - g_error("error configuring shout defined at line %i: %s\n", - param->line, shout_get_error(sd->shout_conn)); + g_set_error(error, shout_output_quark(), 0, + "%s", shout_get_error(sd->shout_conn)); + return NULL; } /* optional paramters */ @@ -233,14 +257,16 @@ my_shout_init_driver(const struct audio_format *audio_format, value = config_get_block_string(param, "genre", NULL); if (value != NULL && shout_set_genre(sd->shout_conn, value)) { - g_error("error configuring shout defined at line %i: %s\n", - param->line, shout_get_error(sd->shout_conn)); + g_set_error(error, shout_output_quark(), 0, + "%s", shout_get_error(sd->shout_conn)); + return NULL; } value = config_get_block_string(param, "description", NULL); if (value != NULL && shout_set_description(sd->shout_conn, value)) { - g_error("error configuring shout defined at line %i: %s\n", - param->line, shout_get_error(sd->shout_conn)); + g_set_error(error, shout_output_quark(), 0, + "%s", shout_get_error(sd->shout_conn)); + return NULL; } { @@ -269,7 +295,7 @@ my_shout_init_driver(const struct audio_format *audio_format, } static bool -handle_shout_error(struct shout_data *sd, int err) +handle_shout_error(struct shout_data *sd, int err, GError **error) { switch (err) { case SHOUTERR_SUCCESS: @@ -277,17 +303,19 @@ handle_shout_error(struct shout_data *sd, int err) case SHOUTERR_UNCONNECTED: case SHOUTERR_SOCKET: - g_warning("Lost shout connection to %s:%i: %s\n", - shout_get_host(sd->shout_conn), - shout_get_port(sd->shout_conn), - shout_get_error(sd->shout_conn)); + g_set_error(error, shout_output_quark(), err, + "Lost shout connection to %s:%i: %s", + shout_get_host(sd->shout_conn), + shout_get_port(sd->shout_conn), + shout_get_error(sd->shout_conn)); return false; default: - g_warning("shout: connection to %s:%i error: %s\n", - shout_get_host(sd->shout_conn), - shout_get_port(sd->shout_conn), - shout_get_error(sd->shout_conn)); + g_set_error(error, shout_output_quark(), err, + "connection to %s:%i error: %s", + shout_get_host(sd->shout_conn), + shout_get_port(sd->shout_conn), + shout_get_error(sd->shout_conn)); return false; } @@ -295,7 +323,7 @@ handle_shout_error(struct shout_data *sd, int err) } static bool -write_page(struct shout_data *sd) +write_page(struct shout_data *sd, GError **error) { int err; @@ -308,7 +336,7 @@ write_page(struct shout_data *sd) shout_sync(sd->shout_conn); err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len); - if (!handle_shout_error(sd, err)) + if (!handle_shout_error(sd, err, error)) return false; return true; @@ -320,7 +348,7 @@ static void close_shout_conn(struct shout_data * sd) if (sd->encoder != NULL) { if (encoder_flush(sd->encoder, NULL)) - write_page(sd); + write_page(sd, NULL); encoder_close(sd->encoder); } @@ -362,7 +390,7 @@ static void my_shout_close_device(void *data) } static bool -shout_connect(struct shout_data *sd) +shout_connect(struct shout_data *sd, GError **error) { int state; @@ -373,58 +401,47 @@ shout_connect(struct shout_data *sd) return true; default: - g_warning("problem opening connection to shout server %s:%i: %s\n", - shout_get_host(sd->shout_conn), - shout_get_port(sd->shout_conn), - shout_get_error(sd->shout_conn)); + g_set_error(error, shout_output_quark(), 0, + "problem opening connection to shout server %s:%i: %s", + shout_get_host(sd->shout_conn), + shout_get_port(sd->shout_conn), + shout_get_error(sd->shout_conn)); return false; } } static bool -my_shout_open_device(void *data, struct audio_format *audio_format) +my_shout_open_device(void *data, struct audio_format *audio_format, + GError **error) { struct shout_data *sd = (struct shout_data *)data; bool ret; - GError *error = NULL; - ret = shout_connect(sd); + ret = shout_connect(sd, error); if (!ret) return false; sd->buf.len = 0; - ret = encoder_open(sd->encoder, audio_format, &error); + ret = encoder_open(sd->encoder, audio_format, error) && + write_page(sd, error); if (!ret) { shout_close(sd->shout_conn); - g_warning("%s", error->message); - g_error_free(error); return false; } - write_page(sd); - return true; } static size_t -my_shout_play(void *data, const void *chunk, size_t size) +my_shout_play(void *data, const void *chunk, size_t size, GError **error) { struct shout_data *sd = (struct shout_data *)data; - bool ret; - GError *error = NULL; - - ret = encoder_write(sd->encoder, chunk, size, &error); - if (!ret) { - g_warning("%s", error->message); - g_error_free(error); - return false; - } - if (!write_page(sd)) - return 0; - - return size; + return encoder_write(sd->encoder, chunk, size, error) && + write_page(sd, error) + ? size + : 0; } static bool @@ -432,7 +449,7 @@ my_shout_pause(void *data) { static const char silence[1020]; - return my_shout_play(data, silence, sizeof(silence)); + return my_shout_play(data, silence, sizeof(silence), NULL); } static void @@ -479,7 +496,9 @@ static void my_shout_set_tag(void *data, return; } - write_page(sd); + ret = write_page(sd, NULL); + if (!ret) + return; ret = encoder_tag(sd->encoder, tag, &error); if (!ret) { @@ -499,7 +518,7 @@ static void my_shout_set_tag(void *data, } } - write_page(sd); + write_page(sd, NULL); } const struct audio_output_plugin shoutPlugin = { diff --git a/src/output_init.c b/src/output_init.c index ab26e76ef..004b73e84 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -48,6 +48,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param) char *format = NULL; struct block_param *bp = NULL; const struct audio_output_plugin *plugin = NULL; + GError *error = NULL; if (param) { const char *type = NULL; @@ -97,7 +98,6 @@ audio_output_init(struct audio_output *ao, const struct config_param *param) pcm_convert_init(&ao->convert_state); if (format) { - GError *error = NULL; bool ret; ret = audio_format_parse(&ao->config_audio_format, format, @@ -114,9 +114,14 @@ audio_output_init(struct audio_output *ao, const struct config_param *param) ao->data = ao_plugin_init(plugin, format ? &ao->config_audio_format : NULL, - param); - if (ao->data == NULL) + param, &error); + if (ao->data == NULL) { + g_warning("Failed to initialize \"%s\" [%s]: %s", + ao->name, ao->plugin->name, + error->message); + g_error_free(error); return false; + } return true; } diff --git a/src/output_plugin.h b/src/output_plugin.h index 2def73132..1c04ea921 100644 --- a/src/output_plugin.h +++ b/src/output_plugin.h @@ -19,6 +19,8 @@ #ifndef MPD_OUTPUT_PLUGIN_H #define MPD_OUTPUT_PLUGIN_H +#include + #include #include @@ -45,16 +47,18 @@ struct audio_output_plugin { * Configure and initialize the device, but do not open it * yet. * - * @param ao an opaque pointer to the audio_output structure * @param audio_format the configured audio format, or NULL if * none is configured * @param param the configuration section, or NULL if there is * no configuration + * @param error location to store the error occuring, or NULL + * to ignore errors * @return NULL on error, or an opaque pointer to the plugin's * data */ void *(*init)(const struct audio_format *audio_format, - const struct config_param *param); + const struct config_param *param, + GError **error); /** * Free resources allocated by this device. @@ -72,10 +76,14 @@ struct audio_output_plugin { /** * Really open the device. + * * @param audio_format the audio format in which data is going * to be delivered; may be modified by the plugin + * @param error location to store the error occuring, or NULL + * to ignore errors */ - bool (*open)(void *data, struct audio_format *audio_format); + bool (*open)(void *data, struct audio_format *audio_format, + GError **error); /** * Close the device. @@ -91,9 +99,12 @@ struct audio_output_plugin { /** * Play a chunk of audio data. * + * @param error location to store the error occuring, or NULL + * to ignore errors * @return the number of bytes played, or 0 on error */ - size_t (*play)(void *data, const void *chunk, size_t size); + size_t (*play)(void *data, const void *chunk, size_t size, + GError **error); /** * Try to cancel data which may still be in the device's @@ -126,9 +137,10 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin) static inline void * ao_plugin_init(const struct audio_output_plugin *plugin, const struct audio_format *audio_format, - const struct config_param *param) + const struct config_param *param, + GError **error) { - return plugin->init(audio_format, param); + return plugin->init(audio_format, param, error); } static inline void @@ -147,9 +159,10 @@ ao_plugin_get_mixer(const struct audio_output_plugin *plugin, void *data) static inline bool ao_plugin_open(const struct audio_output_plugin *plugin, - void *data, struct audio_format *audio_format) + void *data, struct audio_format *audio_format, + GError **error) { - return plugin->open(data, audio_format); + return plugin->open(data, audio_format, error); } static inline void @@ -168,9 +181,10 @@ ao_plugin_send_tag(const struct audio_output_plugin *plugin, static inline size_t ao_plugin_play(const struct audio_output_plugin *plugin, - void *data, const void *chunk, size_t size) + void *data, const void *chunk, size_t size, + GError **error) { - return plugin->play(data, chunk, size); + return plugin->play(data, chunk, size, error); } static inline void diff --git a/src/output_thread.c b/src/output_thread.c index 91fd8d406..b5a4c6117 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -56,6 +56,7 @@ static void ao_play(struct audio_output *ao) { const char *data = ao->args.play.data; size_t size = ao->args.play.size; + GError *error = NULL; assert(size > 0); assert(size % audio_format_frame_size(&ao->in_audio_format) == 0); @@ -76,9 +77,14 @@ static void ao_play(struct audio_output *ao) while (size > 0) { size_t nbytes; - nbytes = ao_plugin_play(ao->plugin, ao->data, data, size); + nbytes = ao_plugin_play(ao->plugin, ao->data, data, size, + &error); if (nbytes == 0) { /* play()==0 means failure */ + g_warning("\"%s\" [%s] failed to play: %s", + ao->name, ao->plugin->name, error->message); + g_error_free(error); + ao_plugin_cancel(ao->plugin, ao->data); ao_close(ao); break; @@ -114,6 +120,7 @@ static gpointer audio_output_task(gpointer arg) { struct audio_output *ao = arg; bool ret; + GError *error; while (1) { switch (ao->command) { @@ -123,15 +130,23 @@ static gpointer audio_output_task(gpointer arg) case AO_COMMAND_OPEN: assert(!ao->open); + error = NULL; ret = ao_plugin_open(ao->plugin, ao->data, - &ao->out_audio_format); + &ao->out_audio_format, + &error); assert(!ao->open); if (ret) { pcm_convert_init(&ao->convert_state); ao->open = true; - } else + } else { + g_warning("Failed to open \"%s\" [%s]: %s", + ao->name, ao->plugin->name, + error->message); + g_error_free(error); + ao->reopen_after = time(NULL) + REOPEN_AFTER; + } ao_command_finished(ao); break; -- cgit v1.2.3