diff --git a/Makefile.am b/Makefile.am index dc3b04658..e43f4e799 100644 --- a/Makefile.am +++ b/Makefile.am @@ -247,6 +247,7 @@ src_mpd_SOURCES = \ src/conf.c \ src/crossfade.c \ src/cue/cue_parser.c src/cue/cue_parser.h \ + src/decoder_error.h \ src/decoder_thread.c \ src/decoder_control.c \ src/decoder_api.c \ diff --git a/src/decoder_control.c b/src/decoder_control.c index 70f34b331..caa3705f5 100644 --- a/src/decoder_control.c +++ b/src/decoder_control.c @@ -52,6 +52,8 @@ dc_new(GCond *client_cond) void dc_free(struct decoder_control *dc) { + dc_clear_error(dc); + g_cond_free(dc->cond); g_mutex_free(dc->mutex); g_free(dc->mixramp_start); @@ -79,6 +81,7 @@ static void dc_command(struct decoder_control *dc, enum decoder_command cmd) { decoder_lock(dc); + dc_clear_error(dc); dc_command_locked(dc, cmd); decoder_unlock(dc); } diff --git a/src/decoder_control.h b/src/decoder_control.h index 566b153ee..164875ae0 100644 --- a/src/decoder_control.h +++ b/src/decoder_control.h @@ -67,6 +67,14 @@ struct decoder_control { enum decoder_state state; enum decoder_command command; + /** + * The error that occurred in the decoder thread. This + * attribute is only valid if #state is #DECODE_STATE_ERROR. + * The object must be freed when this object transitions to + * any other state (usually #DECODE_STATE_START). + */ + GError *error; + bool quit; bool seek_error; bool seekable; @@ -188,6 +196,49 @@ decoder_has_failed(const struct decoder_control *dc) return dc->state == DECODE_STATE_ERROR; } +/** + * Checks whether an error has occurred, and if so, returns a newly + * allocated copy of the #GError object. + * + * Caller must lock the object. + */ +static inline GError * +dc_get_error(const struct decoder_control *dc) +{ + assert(dc != NULL); + assert(dc->command == DECODE_COMMAND_NONE); + + return dc->state == DECODE_STATE_ERROR + ? g_error_copy(dc->error) + : NULL; +} + +/** + * Like dc_get_error(), but locks and unlocks the object. + */ +static inline GError * +dc_lock_get_error(struct decoder_control *dc) +{ + decoder_lock(dc); + GError *error = dc_get_error(dc); + decoder_unlock(dc); + return error; +} + +/** + * Clear the error condition and free the #GError object (if any). + * + * Caller must lock the object. + */ +static inline void +dc_clear_error(struct decoder_control *dc) +{ + if (dc->state == DECODE_STATE_ERROR) { + g_error_free(dc->error); + dc->state = DECODE_STATE_STOP; + } +} + static inline bool decoder_lock_is_idle(struct decoder_control *dc) { diff --git a/src/decoder_error.h b/src/decoder_error.h new file mode 100644 index 000000000..a12a31937 --- /dev/null +++ b/src/decoder_error.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_DECODER_ERROR_H +#define MPD_DECODER_ERROR_H + +#include + +/** + * Quark for GError.domain. + */ +G_GNUC_CONST +static inline GQuark +decoder_quark(void) +{ + return g_quark_from_static_string("decoder"); +} + +#endif diff --git a/src/decoder_thread.c b/src/decoder_thread.c index 1440fc272..11d96b35c 100644 --- a/src/decoder_thread.c +++ b/src/decoder_thread.c @@ -19,6 +19,7 @@ #include "config.h" #include "decoder_thread.h" +#include "decoder_error.h" #include "decoder_control.h" #include "decoder_internal.h" #include "decoder_list.h" @@ -428,12 +429,27 @@ decoder_run_song(struct decoder_control *dc, decoder_lock(dc); - dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR; + if (ret) + dc->state = DECODE_STATE_STOP; + else { + dc->state = DECODE_STATE_ERROR; + + const char *error_uri = song->uri; + char *allocated = uri_remove_auth(error_uri); + if (allocated != NULL) + error_uri = allocated; + + dc->error = g_error_new(decoder_quark(), 0, + "Failed to decode %s", error_uri); + g_free(allocated); + } } static void decoder_run(struct decoder_control *dc) { + dc_clear_error(dc); + const struct song *song = dc->song; char *uri;