output/Thread: move MusicPipe code to class SharedPipeConsumer
This commit is contained in:
parent
99659e4cf9
commit
2775d747ac
|
@ -1333,6 +1333,7 @@ OUTPUT_API_SRC = \
|
|||
src/output/Wrapper.hxx \
|
||||
src/output/Registry.cxx src/output/Registry.hxx \
|
||||
src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \
|
||||
src/output/SharedPipeConsumer.cxx src/output/SharedPipeConsumer.hxx \
|
||||
src/output/OutputThread.cxx \
|
||||
src/output/Domain.cxx src/output/Domain.hxx \
|
||||
src/output/OutputControl.cxx \
|
||||
|
|
|
@ -19,10 +19,6 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "Internal.hxx"
|
||||
#include "MusicPipe.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
bool
|
||||
AudioOutput::IsChunkConsumed(const MusicChunk &chunk) const
|
||||
|
@ -30,16 +26,5 @@ AudioOutput::IsChunkConsumed(const MusicChunk &chunk) const
|
|||
if (!open)
|
||||
return true;
|
||||
|
||||
if (current_chunk == nullptr)
|
||||
return false;
|
||||
|
||||
assert(&chunk == current_chunk ||
|
||||
pipe->Contains(current_chunk));
|
||||
|
||||
if (&chunk != current_chunk) {
|
||||
assert(chunk.next != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return current_chunk_finished && chunk.next == nullptr;
|
||||
return pipe.IsConsumed(chunk);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef MPD_OUTPUT_INTERNAL_HXX
|
||||
#define MPD_OUTPUT_INTERNAL_HXX
|
||||
|
||||
#include "SharedPipeConsumer.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "pcm/PcmBuffer.hxx"
|
||||
#include "pcm/PcmDither.hxx"
|
||||
|
@ -247,13 +248,7 @@ struct AudioOutput {
|
|||
Command command = Command::NONE;
|
||||
|
||||
/**
|
||||
* The music pipe which provides music chunks to be played.
|
||||
*/
|
||||
const MusicPipe *pipe;
|
||||
|
||||
/**
|
||||
* This mutex protects #open, #fail_timer, #current_chunk and
|
||||
* #current_chunk_finished.
|
||||
* This mutex protects #open, #fail_timer, #pipe.
|
||||
*/
|
||||
Mutex mutex;
|
||||
|
||||
|
@ -270,17 +265,9 @@ struct AudioOutput {
|
|||
PlayerControl *player_control;
|
||||
|
||||
/**
|
||||
* The #MusicChunk which is currently being played. All
|
||||
* chunks before this one may be returned to the
|
||||
* #music_buffer, because they are not going to be used by
|
||||
* this output anymore.
|
||||
* A reference to the #MusicPipe and the current position.
|
||||
*/
|
||||
const MusicChunk *current_chunk;
|
||||
|
||||
/**
|
||||
* Has the output finished playing #current_chunk?
|
||||
*/
|
||||
bool current_chunk_finished;
|
||||
SharedPipeConsumer pipe;
|
||||
|
||||
/**
|
||||
* Throws #std::runtime_error on error.
|
||||
|
@ -445,9 +432,6 @@ private:
|
|||
*/
|
||||
bool WaitForDelay();
|
||||
|
||||
gcc_pure
|
||||
const MusicChunk *GetNextChunk() const;
|
||||
|
||||
bool PlayChunk(const MusicChunk *chunk);
|
||||
|
||||
/**
|
||||
|
|
|
@ -262,7 +262,7 @@ MultipleOutputs::IsChunkConsumed(const MusicChunk *chunk) const
|
|||
}
|
||||
|
||||
inline void
|
||||
MultipleOutputs::ClearTailChunk(gcc_unused const MusicChunk *chunk,
|
||||
MultipleOutputs::ClearTailChunk(const MusicChunk *chunk,
|
||||
bool *locked)
|
||||
{
|
||||
assert(chunk->next == nullptr);
|
||||
|
@ -281,9 +281,7 @@ MultipleOutputs::ClearTailChunk(gcc_unused const MusicChunk *chunk,
|
|||
continue;
|
||||
}
|
||||
|
||||
assert(ao->current_chunk == chunk);
|
||||
assert(ao->current_chunk_finished);
|
||||
ao->current_chunk = nullptr;
|
||||
ao->pipe.ClearTail(*chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,11 +113,10 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
|
|||
fail_timer.Reset();
|
||||
|
||||
if (open && audio_format == in_audio_format) {
|
||||
assert(pipe == &mp || (always_on && pause));
|
||||
assert(&pipe.GetPipe() == &mp || (always_on && pause));
|
||||
|
||||
if (pause) {
|
||||
current_chunk = nullptr;
|
||||
pipe = ∓
|
||||
pipe.Init(mp);
|
||||
|
||||
/* unpause with the CANCEL command; this is a
|
||||
hack, but suits well for forcing the thread
|
||||
|
@ -133,9 +132,8 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
|
|||
}
|
||||
|
||||
in_audio_format = audio_format;
|
||||
current_chunk = nullptr;
|
||||
|
||||
pipe = ∓
|
||||
pipe.Init(mp);
|
||||
|
||||
if (!thread.IsDefined())
|
||||
StartThread();
|
||||
|
|
|
@ -134,8 +134,6 @@ AudioOutput::Open()
|
|||
struct audio_format_string af_string;
|
||||
|
||||
assert(!open);
|
||||
assert(pipe != nullptr);
|
||||
assert(current_chunk == nullptr);
|
||||
assert(in_audio_format.IsValid());
|
||||
|
||||
fail_timer.Reset();
|
||||
|
@ -239,9 +237,8 @@ AudioOutput::Close(bool drain)
|
|||
{
|
||||
assert(open);
|
||||
|
||||
pipe = nullptr;
|
||||
pipe.Deinit();
|
||||
|
||||
current_chunk = nullptr;
|
||||
open = false;
|
||||
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
@ -292,9 +289,9 @@ AudioOutput::Reopen()
|
|||
{
|
||||
if (!config_audio_format.IsFullyDefined()) {
|
||||
if (open) {
|
||||
const MusicPipe *mp = pipe;
|
||||
const MusicPipe &old_pipe = pipe.GetPipe();
|
||||
Close(true);
|
||||
pipe = mp;
|
||||
pipe.Init(old_pipe);
|
||||
}
|
||||
|
||||
/* no audio format is configured: copy in->out, let
|
||||
|
@ -499,50 +496,28 @@ AudioOutput::PlayChunk(const MusicChunk *chunk)
|
|||
return true;
|
||||
}
|
||||
|
||||
inline const MusicChunk *
|
||||
AudioOutput::GetNextChunk() const
|
||||
{
|
||||
return current_chunk != nullptr
|
||||
/* continue the previous play() call */
|
||||
? current_chunk->next
|
||||
/* get the first chunk from the pipe */
|
||||
: pipe->Peek();
|
||||
}
|
||||
|
||||
inline bool
|
||||
AudioOutput::Play()
|
||||
{
|
||||
assert(pipe != nullptr);
|
||||
|
||||
const MusicChunk *chunk = GetNextChunk();
|
||||
const MusicChunk *chunk = pipe.Get();
|
||||
if (chunk == nullptr)
|
||||
/* no chunk available */
|
||||
return false;
|
||||
|
||||
current_chunk_finished = false;
|
||||
|
||||
assert(!in_playback_loop);
|
||||
in_playback_loop = true;
|
||||
|
||||
while (chunk != nullptr && command == Command::NONE) {
|
||||
assert(!current_chunk_finished);
|
||||
|
||||
current_chunk = chunk;
|
||||
|
||||
if (!PlayChunk(chunk)) {
|
||||
assert(current_chunk == nullptr);
|
||||
if (!PlayChunk(chunk))
|
||||
break;
|
||||
}
|
||||
|
||||
assert(current_chunk == chunk);
|
||||
chunk = chunk->next;
|
||||
pipe.Consume(*chunk);
|
||||
chunk = pipe.Get();
|
||||
}
|
||||
|
||||
assert(in_playback_loop);
|
||||
in_playback_loop = false;
|
||||
|
||||
current_chunk_finished = true;
|
||||
|
||||
const ScopeUnlock unlock(mutex);
|
||||
player_control->LockSignal();
|
||||
|
||||
|
@ -626,7 +601,6 @@ AudioOutput::Task()
|
|||
|
||||
case Command::CLOSE:
|
||||
assert(open);
|
||||
assert(pipe != nullptr);
|
||||
|
||||
Close(false);
|
||||
CommandFinished();
|
||||
|
@ -651,8 +625,8 @@ AudioOutput::Task()
|
|||
|
||||
case Command::DRAIN:
|
||||
if (open) {
|
||||
assert(current_chunk == nullptr);
|
||||
assert(pipe->Peek() == nullptr);
|
||||
assert(pipe.IsInitial());
|
||||
assert(pipe.GetPipe().Peek() == nullptr);
|
||||
|
||||
const ScopeUnlock unlock(mutex);
|
||||
ao_plugin_drain(this);
|
||||
|
@ -662,7 +636,7 @@ AudioOutput::Task()
|
|||
continue;
|
||||
|
||||
case Command::CANCEL:
|
||||
current_chunk = nullptr;
|
||||
pipe.Cancel();
|
||||
|
||||
if (open) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
@ -673,7 +647,7 @@ AudioOutput::Task()
|
|||
continue;
|
||||
|
||||
case Command::KILL:
|
||||
current_chunk = nullptr;
|
||||
pipe.Cancel();
|
||||
CommandFinished();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2003-2016 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "SharedPipeConsumer.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "MusicPipe.hxx"
|
||||
|
||||
const MusicChunk *
|
||||
SharedPipeConsumer::Get()
|
||||
{
|
||||
if (chunk != nullptr) {
|
||||
if (!consumed)
|
||||
return chunk;
|
||||
|
||||
if (chunk->next == nullptr)
|
||||
return nullptr;
|
||||
|
||||
consumed = false;
|
||||
return chunk = chunk->next;
|
||||
} else {
|
||||
/* get the first chunk from the pipe */
|
||||
consumed = false;
|
||||
return chunk = pipe->Peek();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SharedPipeConsumer::IsConsumed(const MusicChunk &_chunk) const
|
||||
{
|
||||
if (chunk == nullptr)
|
||||
return false;
|
||||
|
||||
assert(&_chunk == chunk || pipe->Contains(chunk));
|
||||
|
||||
if (&_chunk != chunk) {
|
||||
assert(_chunk.next != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return consumed && _chunk.next == nullptr;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2003-2016 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef SHARED_PIPE_CONSUMER_HXX
|
||||
#define SHARED_PIPE_CONSUMER_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct MusicChunk;
|
||||
class MusicPipe;
|
||||
|
||||
/**
|
||||
* A utility class which helps with consuming data from a #MusicPipe.
|
||||
*/
|
||||
class SharedPipeConsumer {
|
||||
/**
|
||||
* The music pipe which provides music chunks to be played.
|
||||
*/
|
||||
const MusicPipe *pipe = nullptr;
|
||||
|
||||
/**
|
||||
* The #MusicChunk which is currently being played. All
|
||||
* chunks before this one may be returned to the #MusicBuffer,
|
||||
* because they are not going to be used by this output
|
||||
* anymore.
|
||||
*/
|
||||
const MusicChunk *chunk;
|
||||
|
||||
/**
|
||||
* Has the output finished playing #current_chunk?
|
||||
*/
|
||||
bool consumed;
|
||||
|
||||
public:
|
||||
void Init(const MusicPipe &_pipe) {
|
||||
pipe = &_pipe;
|
||||
chunk = nullptr;
|
||||
}
|
||||
|
||||
void Deinit() {
|
||||
pipe = nullptr;
|
||||
chunk = nullptr;
|
||||
}
|
||||
|
||||
const MusicPipe &GetPipe() {
|
||||
assert(pipe != nullptr);
|
||||
|
||||
return *pipe;
|
||||
}
|
||||
|
||||
bool IsInitial() {
|
||||
return chunk == nullptr;
|
||||
}
|
||||
|
||||
void Cancel() {
|
||||
chunk = nullptr;
|
||||
}
|
||||
|
||||
const MusicChunk *Get();
|
||||
|
||||
void Consume(gcc_unused const MusicChunk &_chunk) {
|
||||
assert(chunk != nullptr);
|
||||
assert(chunk == &_chunk);
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool IsConsumed(const MusicChunk &_chunk) const;
|
||||
|
||||
void ClearTail(gcc_unused const MusicChunk &_chunk) {
|
||||
assert(chunk == &_chunk);
|
||||
assert(consumed);
|
||||
chunk = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue