diff --git a/Makefile.am b/Makefile.am
index f82ff3e32..2b08c1f8e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/src/output/Internal.cxx b/src/output/Internal.cxx
index 07bb5b128..a6045f487 100644
--- a/src/output/Internal.cxx
+++ b/src/output/Internal.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);
 }
diff --git a/src/output/Internal.hxx b/src/output/Internal.hxx
index 0832a7012..1d9d5c1f7 100644
--- a/src/output/Internal.hxx
+++ b/src/output/Internal.hxx
@@ -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);
 
 	/**
diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx
index 2da317eff..a9228b457 100644
--- a/src/output/MultipleOutputs.cxx
+++ b/src/output/MultipleOutputs.cxx
@@ -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);
 	}
 }
 
diff --git a/src/output/OutputControl.cxx b/src/output/OutputControl.cxx
index 15125e2cc..c8b25c101 100644
--- a/src/output/OutputControl.cxx
+++ b/src/output/OutputControl.cxx
@@ -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();
diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx
index b9d7a43a9..18af1923e 100644
--- a/src/output/OutputThread.cxx
+++ b/src/output/OutputThread.cxx
@@ -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;
 		}
diff --git a/src/output/SharedPipeConsumer.cxx b/src/output/SharedPipeConsumer.cxx
new file mode 100644
index 000000000..c52e8e02d
--- /dev/null
+++ b/src/output/SharedPipeConsumer.cxx
@@ -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;
+}
diff --git a/src/output/SharedPipeConsumer.hxx b/src/output/SharedPipeConsumer.hxx
new file mode 100644
index 000000000..fe5039901
--- /dev/null
+++ b/src/output/SharedPipeConsumer.hxx
@@ -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