aboutsummaryrefslogtreecommitdiffstats
path: root/src/PlayerThread.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/PlayerThread.cxx191
1 files changed, 91 insertions, 100 deletions
diff --git a/src/PlayerThread.cxx b/src/PlayerThread.cxx
index 356559e37..c5308e612 100644
--- a/src/PlayerThread.cxx
+++ b/src/PlayerThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,21 +19,21 @@
#include "config.h"
#include "PlayerThread.hxx"
-#include "DecoderThread.hxx"
-#include "DecoderControl.hxx"
+#include "PlayerListener.hxx"
+#include "decoder/DecoderThread.hxx"
+#include "decoder/DecoderControl.hxx"
#include "MusicPipe.hxx"
#include "MusicBuffer.hxx"
#include "MusicChunk.hxx"
-#include "Song.hxx"
-#include "Main.hxx"
+#include "DetachedSong.hxx"
#include "system/FatalError.hxx"
#include "CrossFade.hxx"
#include "PlayerControl.hxx"
-#include "OutputAll.hxx"
+#include "output/MultipleOutputs.hxx"
#include "tag/Tag.hxx"
#include "Idle.hxx"
-#include "GlobalEvents.hxx"
#include "util/Domain.hxx"
+#include "thread/Name.hxx"
#include "Log.hxx"
#include <string.h>
@@ -93,7 +93,7 @@ class Player {
/**
* the song currently being played
*/
- Song *song;
+ DetachedSong *song;
/**
* is cross fading enabled?
@@ -125,11 +125,11 @@ class Player {
/**
* The time stamp of the chunk most recently sent to the
* output thread. This attribute is only used if
- * audio_output_all_get_elapsed_time() didn't return a usable
+ * MultipleOutputs::GetElapsedTime() didn't return a usable
* value; the output thread can estimate the elapsed time more
* precisely.
*/
- float elapsed_time;
+ SongTime elapsed_time;
public:
Player(PlayerControl &_pc, DecoderControl &_dc,
@@ -146,7 +146,7 @@ public:
cross_fading(false),
cross_fade_chunks(0),
cross_fade_tag(nullptr),
- elapsed_time(0.0) {}
+ elapsed_time(SongTime::zero()) {}
private:
void ClearAndDeletePipe() {
@@ -228,8 +228,8 @@ private:
bool WaitForDecoder();
/**
- * Wrapper for audio_output_all_open(). Upon failure, it pauses the
- * player.
+ * Wrapper for MultipleOutputs::Open(). Upon failure, it
+ * pauses the player.
*
* @return true on success
*/
@@ -291,12 +291,12 @@ Player::StartDecoder(MusicPipe &_pipe)
assert(queued || pc.command == PlayerCommand::SEEK);
assert(pc.next_song != nullptr);
- unsigned start_ms = pc.next_song->start_ms;
+ SongTime start_time = pc.next_song->GetStartTime();
if (pc.command == PlayerCommand::SEEK)
- start_ms += (unsigned)(pc.seek_where * 1000);
+ start_time += pc.seek_time;
- dc.Start(pc.next_song->DupDetached(),
- start_ms, pc.next_song->end_ms,
+ dc.Start(new DetachedSong(*pc.next_song),
+ start_time, pc.next_song->GetEndTime(),
buffer, _pipe);
}
@@ -330,7 +330,7 @@ Player::WaitForDecoder()
if (error.IsDefined()) {
pc.SetError(PlayerError::DECODER, std::move(error));
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
pc.Unlock();
@@ -340,11 +340,9 @@ Player::WaitForDecoder()
pc.ClearTaggedSong();
- if (song != nullptr)
- song->Free();
-
+ delete song;
song = pc.next_song;
- elapsed_time = 0.0;
+ elapsed_time = SongTime::zero();
/* set the "starting" flag, which will be cleared by
player_check_decoder_startup() */
@@ -361,7 +359,7 @@ Player::WaitForDecoder()
pc.Unlock();
/* call syncPlaylistWithQueue() in the main thread */
- GlobalEvents::Emit(GlobalEvents::PLAYLIST);
+ pc.listener.OnPlayerSync();
return true;
}
@@ -370,20 +368,21 @@ Player::WaitForDecoder()
* Returns the real duration of the song, comprising the duration
* indicated by the decoder plugin.
*/
-static double
-real_song_duration(const Song *song, double decoder_duration)
+static SignedSongTime
+real_song_duration(const DetachedSong &song, SignedSongTime decoder_duration)
{
- assert(song != nullptr);
-
- if (decoder_duration <= 0.0)
+ if (decoder_duration.IsNegative())
/* the decoder plugin didn't provide information; fall
back to Song::GetDuration() */
- return song->GetDuration();
+ return song.GetDuration();
+
+ const SongTime start_time = song.GetStartTime();
+ const SongTime end_time = song.GetEndTime();
- if (song->end_ms > 0 && song->end_ms / 1000.0 < decoder_duration)
- return (song->end_ms - song->start_ms) / 1000.0;
+ if (end_time.IsPositive() && end_time < SongTime(decoder_duration))
+ return SignedSongTime(end_time - start_time);
- return decoder_duration - song->start_ms / 1000.0;
+ return SignedSongTime(SongTime(decoder_duration) - start_time);
}
bool
@@ -394,7 +393,7 @@ Player::OpenOutput()
pc.state == PlayerState::PAUSE);
Error error;
- if (audio_output_all_open(play_audio_format, buffer, error)) {
+ if (pc.outputs.Open(play_audio_format, buffer, error)) {
output_open = true;
paused = false;
@@ -445,13 +444,13 @@ Player::CheckDecoderStartup()
pc.Unlock();
if (output_open &&
- !audio_output_all_wait(pc, 1))
+ !pc.outputs.Wait(pc, 1))
/* the output devices havn't finished playing
all chunks yet - wait for that */
return true;
pc.Lock();
- pc.total_time = real_song_duration(dc.song, dc.total_time);
+ pc.total_time = real_song_duration(*dc.song, dc.total_time);
pc.audio_format = dc.in_audio_format;
pc.Unlock();
@@ -461,10 +460,10 @@ Player::CheckDecoderStartup()
decoder_starting = false;
if (!paused && !OpenOutput()) {
- const auto uri = dc.song->GetURI();
FormatError(player_domain,
"problems opening audio device "
- "while playing \"%s\"", uri.c_str());
+ "while playing \"%s\"",
+ dc.song->GetURI());
return true;
}
@@ -485,7 +484,7 @@ Player::SendSilence()
assert(output_open);
assert(play_audio_format.IsDefined());
- struct music_chunk *chunk = buffer.Allocate();
+ MusicChunk *chunk = buffer.Allocate();
if (chunk == nullptr) {
LogError(player_domain, "Failed to allocate silence buffer");
return false;
@@ -500,12 +499,12 @@ Player::SendSilence()
partial frames */
unsigned num_frames = sizeof(chunk->data) / frame_size;
- chunk->times = -1.0; /* undefined time stamp */
+ chunk->time = SignedSongTime::Negative(); /* undefined time stamp */
chunk->length = num_frames * frame_size;
memset(chunk->data, 0, chunk->length);
Error error;
- if (!audio_output_all_play(chunk, error)) {
+ if (!pc.outputs.Play(chunk, error)) {
LogError(error);
buffer.Return(chunk);
return false;
@@ -519,7 +518,7 @@ Player::SeekDecoder()
{
assert(pc.next_song != nullptr);
- const unsigned start_ms = pc.next_song->start_ms;
+ const SongTime start_time = pc.next_song->GetStartTime();
if (!dc.LockIsCurrentSong(*pc.next_song)) {
/* the decoder is already decoding the "next" song -
@@ -545,7 +544,7 @@ Player::SeekDecoder()
ClearAndReplacePipe(dc.pipe);
}
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
queued = false;
}
@@ -562,13 +561,14 @@ Player::SeekDecoder()
/* send the SEEK command */
- double where = pc.seek_where;
- if (where > pc.total_time)
- where = pc.total_time - 0.1;
- if (where < 0.0)
- where = 0.0;
+ SongTime where = pc.seek_time;
+ if (!pc.total_time.IsNegative()) {
+ const SongTime total_time(pc.total_time);
+ if (where > total_time)
+ where = total_time;
+ }
- if (!dc.Seek(where + start_ms / 1000.0)) {
+ if (!dc.Seek(where + start_time)) {
/* decoder failure */
player_command_finished(pc);
return false;
@@ -583,7 +583,7 @@ Player::SeekDecoder()
/* re-fill the buffer after seeking */
buffering = true;
- audio_output_all_cancel();
+ pc.outputs.Cancel();
return true;
}
@@ -600,7 +600,7 @@ Player::ProcessCommand()
case PlayerCommand::UPDATE_AUDIO:
pc.Unlock();
- audio_output_all_enable_disable();
+ pc.outputs.EnableDisable();
pc.Lock();
pc.CommandFinished();
break;
@@ -619,7 +619,7 @@ Player::ProcessCommand()
paused = !paused;
if (paused) {
- audio_output_all_pause();
+ pc.outputs.Pause();
pc.Lock();
pc.state = PlayerState::PAUSE;
@@ -661,7 +661,7 @@ Player::ProcessCommand()
pc.Lock();
}
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
queued = false;
pc.CommandFinished();
@@ -670,13 +670,13 @@ Player::ProcessCommand()
case PlayerCommand::REFRESH:
if (output_open && !paused) {
pc.Unlock();
- audio_output_all_check();
+ pc.outputs.Check();
pc.Lock();
}
- pc.elapsed_time = audio_output_all_get_elapsed_time();
- if (pc.elapsed_time < 0.0)
- pc.elapsed_time = elapsed_time;
+ pc.elapsed_time = !pc.outputs.GetElapsedTime().IsNegative()
+ ? SongTime(pc.outputs.GetElapsedTime())
+ : elapsed_time;
pc.CommandFinished();
break;
@@ -684,23 +684,20 @@ Player::ProcessCommand()
}
static void
-update_song_tag(PlayerControl &pc, Song *song, const Tag &new_tag)
+update_song_tag(PlayerControl &pc, DetachedSong &song, const Tag &new_tag)
{
- if (song->IsFile())
+ if (song.IsFile())
/* don't update tags of local files, only remote
streams may change tags dynamically */
return;
- Tag *old_tag = song->tag;
- song->tag = new Tag(new_tag);
-
- delete old_tag;
+ song.SetTag(new_tag);
- pc.LockSetTaggedSong(*song);
+ pc.LockSetTaggedSong(song);
/* the main thread will update the playlist version when he
receives this event */
- GlobalEvents::Emit(GlobalEvents::TAG);
+ pc.listener.OnPlayerTagModified();
/* notify all clients that the tag of the current song has
changed */
@@ -708,7 +705,7 @@ update_song_tag(PlayerControl &pc, Song *song, const Tag &new_tag)
}
/**
- * Plays a #music_chunk object (after applying software volume). If
+ * Plays a #MusicChunk 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.
*
@@ -716,7 +713,7 @@ update_song_tag(PlayerControl &pc, Song *song, const Tag &new_tag)
*/
static bool
play_chunk(PlayerControl &pc,
- Song *song, struct music_chunk *chunk,
+ DetachedSong &song, MusicChunk *chunk,
MusicBuffer &buffer,
const AudioFormat format,
Error &error)
@@ -737,7 +734,7 @@ play_chunk(PlayerControl &pc,
/* send the chunk to the audio outputs */
- if (!audio_output_all_play(chunk, error))
+ if (!pc.outputs.Play(chunk, error))
return false;
pc.total_play_time += (double)chunk->length /
@@ -748,17 +745,17 @@ play_chunk(PlayerControl &pc,
inline bool
Player::PlayNextChunk()
{
- if (!audio_output_all_wait(pc, 64))
+ if (!pc.outputs.Wait(pc, 64))
/* the output pipe is still large enough, don't send
another chunk */
return true;
unsigned cross_fade_position;
- struct music_chunk *chunk = nullptr;
+ MusicChunk *chunk = nullptr;
if (xfade_state == CrossFadeState::ENABLED && IsDecoderAtNextSong() &&
(cross_fade_position = pipe->GetSize()) <= cross_fade_chunks) {
/* perform cross fade */
- music_chunk *other_chunk = dc.pipe->Shift();
+ MusicChunk *other_chunk = dc.pipe->Shift();
if (!cross_fading) {
/* beginning of the cross fade - adjust
@@ -790,7 +787,7 @@ Player::PlayNextChunk()
}
if (other_chunk->IsEmpty()) {
- /* the "other" chunk was a music_chunk
+ /* the "other" chunk was a MusicChunk
which had only a tag, but no music
data - we cannot cross-fade that;
but since this happens only at the
@@ -839,7 +836,7 @@ Player::PlayNextChunk()
/* play the current chunk */
Error error;
- if (!play_chunk(pc, song, chunk, buffer, play_audio_format, error)) {
+ if (!play_chunk(pc, *song, chunk, buffer, play_audio_format, error)) {
LogError(error);
buffer.Return(chunk);
@@ -883,14 +880,11 @@ Player::SongBorder()
{
xfade_state = CrossFadeState::UNKNOWN;
- {
- const auto uri = song->GetURI();
- FormatDefault(player_domain, "played \"%s\"", uri.c_str());
- }
+ FormatDefault(player_domain, "played \"%s\"", song->GetURI());
ReplacePipe(dc.pipe);
- audio_output_all_song_border();
+ pc.outputs.SongBorder();
if (!WaitForDecoder())
return false;
@@ -930,7 +924,7 @@ Player::Run()
pc.state = PlayerState::PLAY;
if (pc.command == PlayerCommand::SEEK)
- elapsed_time = pc.seek_where;
+ elapsed_time = pc.seek_time;
pc.CommandFinished();
@@ -940,7 +934,7 @@ Player::Run()
pc.command == PlayerCommand::EXIT ||
pc.command == PlayerCommand::CLOSE_AUDIO) {
pc.Unlock();
- audio_output_all_cancel();
+ pc.outputs.Cancel();
break;
}
@@ -956,7 +950,7 @@ Player::Run()
/* not enough decoded buffer space yet */
if (!paused && output_open &&
- audio_output_all_check() < 4 &&
+ pc.outputs.Check() < 4 &&
!SendSilence())
break;
@@ -1036,7 +1030,7 @@ Player::Run()
to the audio output */
PlayNextChunk();
- } else if (audio_output_all_check() > 0) {
+ } else if (pc.outputs.Check() > 0) {
/* not enough data from decoder, but the
output thread is still busy, so it's
okay */
@@ -1061,7 +1055,7 @@ Player::Run()
if (pipe->IsEmpty()) {
/* wait for the hardware to finish
playback */
- audio_output_all_drain();
+ pc.outputs.Drain();
break;
}
} else if (output_open) {
@@ -1082,9 +1076,8 @@ Player::Run()
delete cross_fade_tag;
if (song != nullptr) {
- const auto uri = song->GetURI();
- FormatDefault(player_domain, "played \"%s\"", uri.c_str());
- song->Free();
+ FormatDefault(player_domain, "played \"%s\"", song->GetURI());
+ delete song;
}
pc.Lock();
@@ -1093,7 +1086,7 @@ Player::Run()
if (queued) {
assert(pc.next_song != nullptr);
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
}
@@ -1115,6 +1108,8 @@ player_task(void *arg)
{
PlayerControl &pc = *(PlayerControl *)arg;
+ SetThreadName("player");
+
DecoderControl dc(pc.mutex, pc.cond);
decoder_thread_start(dc);
@@ -1130,22 +1125,20 @@ player_task(void *arg)
pc.Unlock();
do_play(pc, dc, buffer);
- GlobalEvents::Emit(GlobalEvents::PLAYLIST);
+ pc.listener.OnPlayerSync();
pc.Lock();
break;
case PlayerCommand::STOP:
pc.Unlock();
- audio_output_all_cancel();
+ pc.outputs.Cancel();
pc.Lock();
/* fall through */
case PlayerCommand::PAUSE:
- if (pc.next_song != nullptr) {
- pc.next_song->Free();
- pc.next_song = nullptr;
- }
+ delete pc.next_song;
+ pc.next_song = nullptr;
pc.CommandFinished();
break;
@@ -1153,7 +1146,7 @@ player_task(void *arg)
case PlayerCommand::CLOSE_AUDIO:
pc.Unlock();
- audio_output_all_release();
+ pc.outputs.Release();
pc.Lock();
pc.CommandFinished();
@@ -1164,7 +1157,7 @@ player_task(void *arg)
case PlayerCommand::UPDATE_AUDIO:
pc.Unlock();
- audio_output_all_enable_disable();
+ pc.outputs.EnableDisable();
pc.Lock();
pc.CommandFinished();
break;
@@ -1174,16 +1167,14 @@ player_task(void *arg)
dc.Quit();
- audio_output_all_close();
+ pc.outputs.Close();
player_command_finished(pc);
return;
case PlayerCommand::CANCEL:
- if (pc.next_song != nullptr) {
- pc.next_song->Free();
- pc.next_song = nullptr;
- }
+ delete pc.next_song;
+ pc.next_song = nullptr;
pc.CommandFinished();
break;
@@ -1201,7 +1192,7 @@ player_task(void *arg)
}
void
-player_create(PlayerControl &pc)
+StartPlayerThread(PlayerControl &pc)
{
assert(!pc.thread.IsDefined());