player_thread: added comments
This commit is contained in:
parent
13cd6b2834
commit
bc3702a4fd
@ -1,6 +1,6 @@
|
||||
/* the Music Player Daemon (MPD)
|
||||
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
/*
|
||||
* Copyright (C) 2003-2009 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
|
||||
@ -110,12 +110,17 @@ static void player_command_finished(void)
|
||||
notify_signal(&main_notify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the decoder and clears (and frees) its music pipe.
|
||||
*/
|
||||
static void
|
||||
player_dc_stop(struct player *player)
|
||||
{
|
||||
dc_stop(&pc.notify);
|
||||
|
||||
if (dc.pipe != NULL) {
|
||||
/* clear and free the decoder pipe */
|
||||
|
||||
music_pipe_clear(dc.pipe, player_buffer);
|
||||
|
||||
if (dc.pipe != player->pipe)
|
||||
@ -125,6 +130,11 @@ player_dc_stop(struct player *player)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After the decoder has been started asynchronously, wait for the
|
||||
* "START" command to finish. The decoder may not be initialized yet,
|
||||
* i.e. there is no audio_format information yet.
|
||||
*/
|
||||
static bool
|
||||
player_wait_for_decoder(struct player *player)
|
||||
{
|
||||
@ -148,6 +158,9 @@ player_wait_for_decoder(struct player *player)
|
||||
pc.next_song = NULL;
|
||||
pc.elapsed_time = 0;
|
||||
player->queued = false;
|
||||
|
||||
/* set the "starting" flag, which will be cleared by
|
||||
player_check_decoder_startup() */
|
||||
player->decoder_starting = true;
|
||||
|
||||
/* call syncPlaylistWithQueue() in the main thread */
|
||||
@ -156,6 +169,11 @@ player_wait_for_decoder(struct player *player)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The decoder has acknowledged the "START" command (see
|
||||
* player_wait_for_decoder()). This function checks if the decoder
|
||||
* initialization has completed yet.
|
||||
*/
|
||||
static bool
|
||||
player_check_decoder_startup(struct player *player)
|
||||
{
|
||||
@ -252,6 +270,9 @@ player_send_silence(struct player *player)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the handler for the #PLAYER_COMMAND_SEEK command.
|
||||
*/
|
||||
static bool player_seek_decoder(struct player *player)
|
||||
{
|
||||
double where;
|
||||
@ -260,14 +281,21 @@ static bool player_seek_decoder(struct player *player)
|
||||
assert(pc.next_song != NULL);
|
||||
|
||||
if (decoder_current_song() != pc.next_song) {
|
||||
/* the decoder is already decoding the "next" song -
|
||||
stop it and start the previous song again */
|
||||
|
||||
player_dc_stop(player);
|
||||
|
||||
/* clear music chunks which might still reside in the
|
||||
pipe */
|
||||
music_pipe_clear(player->pipe, player_buffer);
|
||||
dc.pipe = player->pipe;
|
||||
dc_start_async(pc.next_song);
|
||||
|
||||
/* re-start the decoder */
|
||||
dc_start_async(pc.next_song);
|
||||
ret = player_wait_for_decoder(player);
|
||||
if (!ret) {
|
||||
/* decoder failure */
|
||||
player_command_finished();
|
||||
return false;
|
||||
}
|
||||
@ -276,6 +304,8 @@ static bool player_seek_decoder(struct player *player)
|
||||
player->queued = false;
|
||||
}
|
||||
|
||||
/* send the SEEK command */
|
||||
|
||||
where = pc.seek_where;
|
||||
if (where > pc.total_time)
|
||||
where = pc.total_time - 0.1;
|
||||
@ -284,6 +314,7 @@ static bool player_seek_decoder(struct player *player)
|
||||
|
||||
ret = dc_seek(&pc.notify, where);
|
||||
if (!ret) {
|
||||
/* decoder failure */
|
||||
player_command_finished();
|
||||
return false;
|
||||
}
|
||||
@ -331,8 +362,11 @@ static void player_process_command(struct player *player)
|
||||
|
||||
pc.state = PLAYER_STATE_PLAY;
|
||||
} else if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
|
||||
/* unpaused, continue playing */
|
||||
pc.state = PLAYER_STATE_PLAY;
|
||||
} else {
|
||||
/* the audio device has failed - rollback to
|
||||
pause mode */
|
||||
assert(dc.next_song == NULL || dc.next_song->url != NULL);
|
||||
pc.errored_song = dc.next_song;
|
||||
pc.error = PLAYER_ERROR_AUDIO;
|
||||
@ -349,7 +383,7 @@ static void player_process_command(struct player *player)
|
||||
|
||||
case PLAYER_COMMAND_CANCEL:
|
||||
if (pc.next_song == NULL) {
|
||||
/* the cancel request arrived too later, we're
|
||||
/* the cancel request arrived too late, we're
|
||||
already playing the queued song... stop
|
||||
everything now */
|
||||
pc.command = PLAYER_COMMAND_STOP;
|
||||
@ -368,6 +402,11 @@ static void player_process_command(struct player *player)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a #music_chunk object (after applying software volume). If
|
||||
* it contains a (stream) tag, copy it to the current song, so MPD's
|
||||
* playlist reflects the new stream tag.
|
||||
*/
|
||||
static bool
|
||||
play_chunk(struct song *song, struct music_chunk *chunk,
|
||||
const struct audio_format *format, double sizeToTime)
|
||||
@ -399,6 +438,8 @@ play_chunk(struct song *song, struct music_chunk *chunk,
|
||||
}
|
||||
}
|
||||
|
||||
/* apply software volume */
|
||||
|
||||
success = pcm_volume(chunk->data, chunk->length,
|
||||
format, pc.software_volume);
|
||||
if (!success) {
|
||||
@ -409,6 +450,8 @@ play_chunk(struct song *song, struct music_chunk *chunk,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* send the chunk to the audio outputs */
|
||||
|
||||
if (!audio_output_all_play(chunk)) {
|
||||
pc.errored_song = dc.current_song;
|
||||
pc.error = PLAYER_ERROR_AUDIO;
|
||||
@ -433,8 +476,8 @@ play_next_chunk(struct player *player)
|
||||
bool success;
|
||||
|
||||
if (audio_output_all_check() >= 64) {
|
||||
/* the output pipe is still large
|
||||
enough, don't send another chunk */
|
||||
/* the output pipe is still large enough, don't send
|
||||
another chunk */
|
||||
|
||||
/* XXX synchronize in a better way */
|
||||
g_usleep(1000);
|
||||
@ -450,11 +493,10 @@ play_next_chunk(struct player *player)
|
||||
music_pipe_shift(dc.pipe);
|
||||
|
||||
if (!player->cross_fading) {
|
||||
/* beginning of the cross fade
|
||||
- adjust crossFadeChunks
|
||||
which might be bigger than
|
||||
the remaining number of
|
||||
chunks in the old song */
|
||||
/* beginning of the cross fade - adjust
|
||||
crossFadeChunks which might be bigger than
|
||||
the remaining number of chunks in the old
|
||||
song */
|
||||
player->cross_fade_chunks = cross_fade_position;
|
||||
player->cross_fading = true;
|
||||
}
|
||||
@ -469,16 +511,13 @@ play_next_chunk(struct player *player)
|
||||
player->cross_fade_chunks);
|
||||
music_buffer_return(player_buffer, other_chunk);
|
||||
} else {
|
||||
/* there are not enough
|
||||
decoded chunks yet */
|
||||
/* there are not enough decoded chunks yet */
|
||||
if (decoder_is_idle()) {
|
||||
/* the decoder isn't
|
||||
running, abort
|
||||
/* the decoder isn't running, abort
|
||||
cross fading */
|
||||
player->xfade = XFADE_DISABLED;
|
||||
} else {
|
||||
/* wait for the
|
||||
decoder */
|
||||
/* wait for the decoder */
|
||||
notify_signal(&dc.notify);
|
||||
notify_wait(&pc.notify);
|
||||
|
||||
@ -502,9 +541,8 @@ play_next_chunk(struct player *player)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* this formula should prevent that the
|
||||
decoder gets woken up with each chunk; it
|
||||
is more efficient to make it decode a
|
||||
/* this formula should prevent that the decoder gets woken up
|
||||
with each chunk; it is more efficient to make it decode a
|
||||
larger block at a time */
|
||||
if (!decoder_is_idle() &&
|
||||
music_pipe_size(dc.pipe) <= (pc.buffered_before_play +
|
||||
@ -535,6 +573,11 @@ player_song_border(struct player *player)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The main loop of the player thread, during playback. This is
|
||||
* basically a state machine, which multiplexes data between the
|
||||
* decoder thread and the output threads.
|
||||
*/
|
||||
static void do_play(void)
|
||||
{
|
||||
struct player player = {
|
||||
@ -576,6 +619,10 @@ static void do_play(void)
|
||||
}
|
||||
|
||||
if (player.buffering) {
|
||||
/* buffering at the start of the song - wait
|
||||
until the buffer is large enough, to
|
||||
prevent stuttering on slow machines */
|
||||
|
||||
if (music_pipe_size(player.pipe) < pc.buffered_before_play &&
|
||||
!decoder_is_idle()) {
|
||||
/* not enough decoded buffer space yet */
|
||||
@ -595,6 +642,7 @@ static void do_play(void)
|
||||
}
|
||||
|
||||
if (player.decoder_starting) {
|
||||
/* wait until the decoder is initialized completely */
|
||||
bool success;
|
||||
|
||||
success = player_check_decoder_startup(&player);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* the Music Player Daemon (MPD)
|
||||
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
|
||||
* This project's homepage is: http://www.musicpd.org
|
||||
/*
|
||||
* Copyright (C) 2003-2009 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
|
||||
@ -16,6 +16,23 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* \file
|
||||
*
|
||||
* The player thread controls the playback. It acts as a bridge
|
||||
* between the decoder thread and the output thread(s): it receives
|
||||
* #music_chunk objects from the decoder, optionally mixes them
|
||||
* (cross-fading), applies software volume, and sends them to the
|
||||
* audio outputs via audio_output_all_play().
|
||||
*
|
||||
* It is controlled by the main thread (the playlist code), see
|
||||
* player_control.h. The playlist enqueues new songs into the player
|
||||
* thread and sends it commands.
|
||||
*
|
||||
* The player thread itself does not do any I/O. It synchronizes with
|
||||
* other threads via #GMutex and #GCond objects, and passes
|
||||
* #music_chunk instances around in #music_pipe objects.
|
||||
*/
|
||||
|
||||
#ifndef MPD_PLAYER_THREAD_H
|
||||
#define MPD_PLAYER_THREAD_H
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user