output/Thread: move MusicPipe code to class SharedPipeConsumer

This commit is contained in:
Max Kellermann 2016-12-12 15:24:38 +01:00
parent 99659e4cf9
commit 2775d747ac
8 changed files with 177 additions and 82 deletions

View File

@ -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 \

View File

@ -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);
}

View File

@ -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);
/**

View File

@ -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);
}
}

View File

@ -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 = &mp;
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 = &mp;
pipe.Init(mp);
if (!thread.IsDefined())
StartThread();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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