diff options
Diffstat (limited to 'src/PlayerThread.cxx')
-rw-r--r-- | src/PlayerThread.cxx | 191 |
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()); |