diff --git a/src/input/MemoryInputStream.cxx b/src/input/MemoryInputStream.cxx
new file mode 100644
index 000000000..4cac0dc3a
--- /dev/null
+++ b/src/input/MemoryInputStream.cxx
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright The Music Player Daemon Project
+
+#include "MemoryInputStream.hxx"
+
+#include <algorithm> // for std::copy()
+
+void
+MemoryInputStream::Seek(std::unique_lock<Mutex> &,
+			offset_type new_offset)
+{
+	if (std::cmp_greater(new_offset, src.size()))
+		throw std::runtime_error{"Bad offset"};
+
+	offset = new_offset;
+}
+
+size_t
+MemoryInputStream::Read(std::unique_lock<Mutex> &, std::span<std::byte> dest)
+{
+	const std::size_t _offset = static_cast<std::size_t>(offset);
+	std::size_t remaining = src.size() - _offset;
+	std::size_t nbytes = std::min(dest.size(), remaining);
+
+	const auto s = src.subspan(_offset, nbytes);
+	std::copy(s.begin(), s.end(), dest.begin());
+	return nbytes;
+}
diff --git a/src/input/MemoryInputStream.hxx b/src/input/MemoryInputStream.hxx
new file mode 100644
index 000000000..7d6c4ec51
--- /dev/null
+++ b/src/input/MemoryInputStream.hxx
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright The Music Player Daemon Project
+
+#include "InputStream.hxx"
+
+class MemoryInputStream final : public InputStream {
+	std::span<const std::byte> src;
+
+public:
+	MemoryInputStream(const char *_uri, Mutex &_mutex,
+			  std::span<const std::byte> _src) noexcept
+		:InputStream(_uri, _mutex), src(_src)
+	{
+		size = src.size();
+		seekable = true;
+		SetReady();
+	}
+
+	/* virtual methods from InputStream */
+
+	[[nodiscard]] bool IsEOF() const noexcept override {
+		return GetOffset() >= GetSize();
+	}
+
+	size_t Read(std::unique_lock<Mutex> &lock,
+		    std::span<std::byte> dest) override;
+	void Seek(std::unique_lock<Mutex> &lock,
+		  offset_type offset) override;
+};
diff --git a/src/input/meson.build b/src/input/meson.build
index 8050d17aa..b1f6b78b8 100644
--- a/src/input/meson.build
+++ b/src/input/meson.build
@@ -23,6 +23,7 @@ input_basic = static_library(
   'input_basic',
   'AsyncInputStream.cxx',
   'LastInputStream.cxx',
+  'MemoryInputStream.cxx',
   'ProxyInputStream.cxx',
   'RewindInputStream.cxx',
   'TextInputStream.cxx',