Commit 5c0576ca authored by Max Kellermann's avatar Max Kellermann

Merge branch 'v0.16.x'

Conflicts: configure.ac src/player_control.c src/player_thread.c src/playlist_song.c
parents 4e909f94 039b3544
......@@ -873,6 +873,15 @@ FILTER_SRC = \
#
# systemd unit
#
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
mpd.service
endif
#
# Sparse code analysis
#
# sparse is a semantic parser
......
......@@ -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)
......
......@@ -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.'
[Unit]
Description=Music Player Daemon
After=sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon
[Install]
WantedBy=multi-user.target
......@@ -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;
}
......
......@@ -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);
......
......@@ -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
......
......@@ -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;
......
......@@ -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.
*/
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment