aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am9
-rw-r--r--NEWS7
-rw-r--r--configure.ac8
-rw-r--r--mpd.service.in9
-rw-r--r--src/decoder_api.c61
-rw-r--r--src/decoder_control.c3
-rw-r--r--src/decoder_control.h20
-rw-r--r--src/decoder_thread.c2
-rw-r--r--src/output_control.c3
-rw-r--r--src/player_control.c3
-rw-r--r--src/player_thread.c97
-rw-r--r--src/playlist_song.c5
12 files changed, 173 insertions, 54 deletions
diff --git a/Makefile.am b/Makefile.am
index 54e03c27..ccf74b9f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -873,6 +873,15 @@ FILTER_SRC = \
#
+# systemd unit
+#
+
+if HAVE_SYSTEMD
+systemdsystemunit_DATA = \
+ mpd.service
+endif
+
+#
# Sparse code analysis
#
# sparse is a semantic parser
diff --git a/NEWS b/NEWS
index a38f66e8..933b9795 100644
--- a/NEWS
+++ b/NEWS
@@ -37,7 +37,14 @@ ver 0.16.5 (2010/??/??)
- ffmpeg: higher precision timestamps
- ffmpeg: don't require key frame for seeking
- fix CUE track seeking
+* player:
+ - make seeking to CUE track more reliable
+ - the "seek" command works when MPD is stopped
+ - restore song position from state file (bug fix)
+ - fix crash that sometimes occurred when audio device fails on startup
+ - fix absolute path support in playlists
* WIN32: close sockets properly
+* install systemd service file if systemd is available
ver 0.16.4 (2011/09/01)
diff --git a/configure.ac b/configure.ac
index 18e7e471..98062eea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,13 @@ AM_CONDITIONAL(HAVE_CXX, test x$HAVE_CXX = xyes)
AC_PROG_INSTALL
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
+AC_ARG_WITH([systemdsystemunitdir],
+ AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
+ [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
+if test "x$with_systemdsystemunitdir" != xno; then
+ AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
+fi
+AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
dnl ---------------------------------------------------------------------------
dnl Declare Variables
@@ -1617,5 +1624,6 @@ dnl Generate files
dnl ---------------------------------------------------------------------------
AC_OUTPUT(Makefile)
AC_OUTPUT(doc/doxygen.conf)
+AC_OUTPUT(mpd.service)
echo 'MPD is ready for compilation, type "make" to begin.'
diff --git a/mpd.service.in b/mpd.service.in
new file mode 100644
index 00000000..9e9282fa
--- /dev/null
+++ b/mpd.service.in
@@ -0,0 +1,9 @@
+[Unit]
+Description=Music Player Daemon
+After=sound.target
+
+[Service]
+ExecStart=@prefix@/bin/mpd --no-daemon
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/decoder_api.c b/src/decoder_api.c
index a34e19b1..311f1f31 100644
--- a/src/decoder_api.c
+++ b/src/decoder_api.c
@@ -77,30 +77,54 @@ decoder_initialized(struct decoder *decoder,
}
/**
- * Returns the current decoder command. May return a "virtual"
- * synthesized command, e.g. to seek to the beginning of the CUE
- * track.
+ * Checks if we need an "initial seek". If so, then the initial seek
+ * is prepared, and the function returns true.
*/
G_GNUC_PURE
-static enum decoder_command
-decoder_get_virtual_command(struct decoder *decoder)
+static bool
+decoder_prepare_initial_seek(struct decoder *decoder)
{
const struct decoder_control *dc = decoder->dc;
assert(dc->pipe != NULL);
if (decoder->initial_seek_running)
- return DECODE_COMMAND_SEEK;
+ /* initial seek has already begun - override any other
+ command */
+ return true;
if (decoder->initial_seek_pending) {
if (dc->command == DECODE_COMMAND_NONE) {
+ /* begin initial seek */
+
decoder->initial_seek_pending = false;
decoder->initial_seek_running = true;
- return DECODE_COMMAND_SEEK;
+ return true;
}
+ /* skip initial seek when there's another command
+ (e.g. STOP) */
+
decoder->initial_seek_pending = false;
}
+ return false;
+}
+
+/**
+ * Returns the current decoder command. May return a "virtual"
+ * synthesized command, e.g. to seek to the beginning of the CUE
+ * track.
+ */
+G_GNUC_PURE
+static enum decoder_command
+decoder_get_virtual_command(struct decoder *decoder)
+{
+ const struct decoder_control *dc = decoder->dc;
+ assert(dc->pipe != NULL);
+
+ if (decoder_prepare_initial_seek(decoder))
+ return DECODE_COMMAND_SEEK;
+
return dc->command;
}
@@ -130,7 +154,7 @@ decoder_command_finished(struct decoder *decoder)
assert(music_pipe_empty(dc->pipe));
decoder->initial_seek_running = false;
- decoder->timestamp = dc->song->start_ms / 1000.;
+ decoder->timestamp = dc->start_ms / 1000.;
decoder_unlock(dc);
return;
}
@@ -162,7 +186,7 @@ double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
assert(dc->pipe != NULL);
if (decoder->initial_seek_running)
- return dc->song->start_ms / 1000.;
+ return dc->start_ms / 1000.;
assert(dc->command == DECODE_COMMAND_SEEK);
@@ -177,10 +201,12 @@ void decoder_seek_error(struct decoder * decoder)
assert(dc->pipe != NULL);
- if (decoder->initial_seek_running)
+ if (decoder->initial_seek_running) {
/* d'oh, we can't seek to the sub-song start position,
what now? - no idea, ignoring the problem for now. */
+ decoder->initial_seek_running = false;
return;
+ }
assert(dc->command == DECODE_COMMAND_SEEK);
@@ -424,8 +450,8 @@ decoder_data(struct decoder *decoder,
decoder->timestamp += (double)nbytes /
audio_format_time_to_size(&dc->out_audio_format);
- if (dc->song->end_ms > 0 &&
- decoder->timestamp >= dc->song->end_ms / 1000.0)
+ if (dc->end_ms > 0 &&
+ decoder->timestamp >= dc->end_ms / 1000.0)
/* the end of this range has been reached:
stop decoding */
return DECODE_COMMAND_STOP;
@@ -455,6 +481,14 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
update_stream_tag(decoder, is);
+ /* check if we're seeking */
+
+ if (decoder_prepare_initial_seek(decoder))
+ /* during initial seek, no music chunk must be created
+ until seeking is finished; skip the rest of the
+ function here */
+ return DECODE_COMMAND_SEEK;
+
/* send tag to music pipe */
if (decoder->stream_tag != NULL) {
@@ -468,9 +502,6 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
/* send only the decoder tag */
cmd = do_send_tag(decoder, tag);
- if (cmd == DECODE_COMMAND_NONE)
- cmd = decoder_get_virtual_command(decoder);
-
return cmd;
}
diff --git a/src/decoder_control.c b/src/decoder_control.c
index 685db6c2..70f34b33 100644
--- a/src/decoder_control.c
+++ b/src/decoder_control.c
@@ -96,6 +96,7 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
void
dc_start(struct decoder_control *dc, struct song *song,
+ unsigned start_ms, unsigned end_ms,
struct music_buffer *buffer, struct music_pipe *pipe)
{
assert(song != NULL);
@@ -104,6 +105,8 @@ dc_start(struct decoder_control *dc, struct song *song,
assert(music_pipe_empty(pipe));
dc->song = song;
+ dc->start_ms = start_ms;
+ dc->end_ms = end_ms;
dc->buffer = buffer;
dc->pipe = pipe;
dc_command(dc, DECODE_COMMAND_START);
diff --git a/src/decoder_control.h b/src/decoder_control.h
index e1a718a5..566b153e 100644
--- a/src/decoder_control.h
+++ b/src/decoder_control.h
@@ -85,6 +85,23 @@ struct decoder_control {
*/
const struct song *song;
+ /**
+ * The initial seek position (in milliseconds), e.g. to the
+ * start of a sub-track described by a CUE file.
+ *
+ * This attribute is set by dc_start().
+ */
+ unsigned start_ms;
+
+ /**
+ * The decoder will stop when it reaches this position (in
+ * milliseconds). 0 means don't stop before the end of the
+ * file.
+ *
+ * This attribute is set by dc_start().
+ */
+ unsigned end_ms;
+
float total_time;
/** the #music_chunk allocator */
@@ -229,11 +246,14 @@ decoder_current_song(const struct decoder_control *dc)
*
* @param the decoder
* @param song the song to be decoded
+ * @param start_ms see #decoder_control
+ * @param end_ms see #decoder_control
* @param pipe the pipe which receives the decoded chunks (owned by
* the caller)
*/
void
dc_start(struct decoder_control *dc, struct song *song,
+ unsigned start_ms, unsigned end_ms,
struct music_buffer *buffer, struct music_pipe *pipe);
void
diff --git a/src/decoder_thread.c b/src/decoder_thread.c
index dbead655..c2bb655e 100644
--- a/src/decoder_thread.c
+++ b/src/decoder_thread.c
@@ -380,7 +380,7 @@ decoder_run_song(struct decoder_control *dc,
{
struct decoder decoder = {
.dc = dc,
- .initial_seek_pending = song->start_ms > 0,
+ .initial_seek_pending = dc->start_ms > 0,
.initial_seek_running = false,
};
int ret;
diff --git a/src/output_control.c b/src/output_control.c
index 69553145..7b95be49 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -126,9 +126,6 @@ audio_output_disable(struct audio_output *ao)
ao_lock_command(ao, AO_COMMAND_DISABLE);
}
-static void
-audio_output_close_locked(struct audio_output *ao);
-
/**
* Object must be locked (and unlocked) by the caller.
*/
diff --git a/src/player_control.c b/src/player_control.c
index 51420a43..d8d54dfd 100644
--- a/src/player_control.c
+++ b/src/player_control.c
@@ -319,9 +319,6 @@ pc_seek(struct player_control *pc, struct song *song, float seek_time)
{
assert(song != NULL);
- if (pc->state == PLAYER_STATE_STOP)
- return false;
-
player_lock(pc);
pc->next_song = song;
pc->seek_where = seek_time;
diff --git a/src/player_thread.c b/src/player_thread.c
index 7696c22c..c0243fa0 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -76,6 +76,14 @@ struct player {
bool queued;
/**
+ * Was any audio output opened successfully? It might have
+ * failed meanwhile, but was not explicitly closed by the
+ * player thread. When this flag is unset, some output
+ * methods must not be called.
+ */
+ bool output_open;
+
+ /**
* the song currently being played
*/
struct song *song;
@@ -150,7 +158,13 @@ player_dc_start(struct player *player, struct music_pipe *pipe)
assert(player->queued || pc->command == PLAYER_COMMAND_SEEK);
assert(pc->next_song != NULL);
- dc_start(dc, pc->next_song, player_buffer, pipe);
+ unsigned start_ms = pc->next_song->start_ms;
+ if (pc->command == PLAYER_COMMAND_SEEK)
+ start_ms += (unsigned)(pc->seek_where * 1000);
+
+ dc_start(dc, pc->next_song,
+ start_ms, pc->next_song->end_ms,
+ player_buffer, pipe);
}
/**
@@ -277,6 +291,46 @@ real_song_duration(const struct song *song, double decoder_duration)
}
/**
+ * Wrapper for audio_output_all_open(). Upon failure, it pauses the
+ * player.
+ *
+ * @return true on success
+ */
+static bool
+player_open_output(struct player *player)
+{
+ struct player_control *pc = player->pc;
+
+ assert(audio_format_defined(&player->play_audio_format));
+ assert(pc->state == PLAYER_STATE_PLAY ||
+ pc->state == PLAYER_STATE_PAUSE);
+
+ if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
+ player->output_open = true;
+ player->paused = false;
+
+ player_lock(pc);
+ pc->state = PLAYER_STATE_PLAY;
+ player_unlock(pc);
+
+ return true;
+ } else {
+ player->output_open = false;
+
+ /* pause: the user may resume playback as soon as an
+ audio output becomes available */
+ player->paused = true;
+
+ player_lock(pc);
+ pc->error = PLAYER_ERROR_AUDIO;
+ pc->state = PLAYER_STATE_PAUSE;
+ player_unlock(pc);
+
+ return false;
+ }
+}
+
+/**
* The decoder has acknowledged the "START" command (see
* player_wait_for_decoder()). This function checks if the decoder
* initialization has completed yet.
@@ -308,7 +362,7 @@ player_check_decoder_startup(struct player *player)
decoder_unlock(dc);
- if (audio_format_defined(&player->play_audio_format) &&
+ if (player->output_open &&
!audio_output_all_wait(pc, 1))
/* the output devices havn't finished playing
all chunks yet - wait for that */
@@ -322,23 +376,12 @@ player_check_decoder_startup(struct player *player)
player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false;
- if (!player->paused &&
- !audio_output_all_open(&dc->out_audio_format,
- player_buffer)) {
+ if (!player->paused && !player_open_output(player)) {
char *uri = song_get_uri(dc->song);
g_warning("problems opening audio device "
"while playing \"%s\"", uri);
g_free(uri);
- player_lock(pc);
- pc->error = PLAYER_ERROR_AUDIO;
-
- /* pause: the user may resume playback as soon
- as an audio output becomes available */
- pc->state = PLAYER_STATE_PAUSE;
- player_unlock(pc);
-
- player->paused = true;
return true;
}
@@ -363,6 +406,7 @@ player_check_decoder_startup(struct player *player)
static bool
player_send_silence(struct player *player)
{
+ assert(player->output_open);
assert(audio_format_defined(&player->play_audio_format));
struct music_chunk *chunk = music_buffer_allocate(player_buffer);
@@ -520,17 +564,8 @@ static void player_process_command(struct player *player)
player_lock(pc);
pc->state = PLAYER_STATE_PLAY;
- } else if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
- /* unpaused, continue playing */
- player_lock(pc);
-
- pc->state = PLAYER_STATE_PLAY;
} else {
- /* the audio device has failed - rollback to
- pause mode */
- pc->error = PLAYER_ERROR_AUDIO;
-
- player->paused = true;
+ player_open_output(player);
player_lock(pc);
}
@@ -567,8 +602,7 @@ static void player_process_command(struct player *player)
break;
case PLAYER_COMMAND_REFRESH:
- if (audio_format_defined(&player->play_audio_format) &&
- !player->paused) {
+ if (player->output_open && !player->paused) {
player_unlock(pc);
audio_output_all_check();
player_lock(pc);
@@ -823,6 +857,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
.decoder_starting = false,
.paused = false,
.queued = true,
+ .output_open = false,
.song = NULL,
.xfade = XFADE_UNKNOWN,
.cross_fading = false,
@@ -847,6 +882,10 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
player_lock(pc);
pc->state = PLAYER_STATE_PLAY;
+
+ if (pc->command == PLAYER_COMMAND_SEEK)
+ player.elapsed_time = pc->seek_where;
+
player_command_finished_locked(pc);
while (true) {
@@ -871,7 +910,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
/* not enough decoded buffer space yet */
if (!player.paused &&
- audio_format_defined(&player.play_audio_format) &&
+ player.output_open &&
audio_output_all_check() < 4 &&
!player_send_silence(&player))
break;
@@ -976,7 +1015,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
audio_output_all_drain();
break;
}
- } else {
+ } else if (player.output_open) {
/* the decoder is too busy and hasn't provided
new PCM data in time: send silence (if the
output pipe is empty) */
@@ -1025,6 +1064,7 @@ player_task(gpointer arg)
while (1) {
switch (pc->command) {
+ case PLAYER_COMMAND_SEEK:
case PLAYER_COMMAND_QUEUE:
assert(pc->next_song != NULL);
@@ -1038,7 +1078,6 @@ player_task(gpointer arg)
/* fall through */
- case PLAYER_COMMAND_SEEK:
case PLAYER_COMMAND_PAUSE:
pc->next_song = NULL;
player_command_finished_locked(pc);
diff --git a/src/playlist_song.c b/src/playlist_song.c
index 8c966d54..afdc0cf9 100644
--- a/src/playlist_song.c
+++ b/src/playlist_song.c
@@ -115,9 +115,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri,
if (g_path_is_absolute(uri)) {
/* XXX fs_charset vs utf8? */
- char *prefix = base_uri != NULL
- ? map_uri_fs(base_uri)
- : map_directory_fs(db_get_root());
+ char *prefix = map_directory_fs(db_get_root());
if (prefix != NULL && g_str_has_prefix(uri, prefix) &&
uri[strlen(prefix)] == '/')
@@ -130,6 +128,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri,
return NULL;
}
+ base_uri = NULL;
g_free(prefix);
}