diff --git a/src/output/plugins/httpd/HttpdInternal.hxx b/src/output/plugins/httpd/HttpdInternal.hxx
index f42ebb8af238db89e0fd93ff10b9e0362556d9ab..a55981d7c828a358c6140f9421b7b8e15e4477f2 100644
--- a/src/output/plugins/httpd/HttpdInternal.hxx
+++ b/src/output/plugins/httpd/HttpdInternal.hxx
@@ -26,6 +26,7 @@
 #define MPD_OUTPUT_HTTPD_INTERNAL_H
 
 #include "HttpdClient.hxx"
+#include "output/Wrapper.hxx"
 #include "output/Internal.hxx"
 #include "output/Timer.hxx"
 #include "thread/Mutex.hxx"
@@ -49,6 +50,8 @@ class Encoder;
 struct Tag;
 
 class HttpdOutput final : ServerSocket, DeferredMonitor {
+	friend struct AudioOutputWrapper<HttpdOutput>;
+
 	AudioOutput base;
 
 	/**
@@ -152,9 +155,7 @@ public:
 	HttpdOutput(EventLoop &_loop, const ConfigBlock &block);
 	~HttpdOutput();
 
-	operator AudioOutput *() {
-		return &base;
-	}
+	static HttpdOutput *Create(const ConfigBlock &block);
 
 #if CLANG_OR_GCC_VERSION(4,7)
 	constexpr
@@ -168,6 +169,14 @@ public:
 	void Bind();
 	void Unbind();
 
+	void Enable() {
+		Bind();
+	}
+
+	void Disable() {
+		Unbind();
+	}
+
 	/**
 	 * Caller must lock the mutex.
 	 *
@@ -249,6 +258,9 @@ public:
 
 	void CancelAllClients();
 
+	void Cancel();
+	bool Pause();
+
 private:
 	virtual void RunDeferred() override;
 
diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.cxx b/src/output/plugins/httpd/HttpdOutputPlugin.cxx
index d8ca25d5e5df4488f8fad5b36fb2f70058680c88..d54eccde65ea0f52242798fbe4953160a9d3cc36 100644
--- a/src/output/plugins/httpd/HttpdOutputPlugin.cxx
+++ b/src/output/plugins/httpd/HttpdOutputPlugin.cxx
@@ -117,18 +117,10 @@ HttpdOutput::Unbind()
 		});
 }
 
-static AudioOutput *
-httpd_output_init(const ConfigBlock &block)
+HttpdOutput *
+HttpdOutput::Create(const ConfigBlock &block)
 {
-	return *new HttpdOutput(io_thread_get(), block);
-}
-
-static void
-httpd_output_finish(AudioOutput *ao)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	delete httpd;
+	return new HttpdOutput(io_thread_get(), block);
 }
 
 /**
@@ -247,22 +239,6 @@ HttpdOutput::ReadPage()
 	return Page::Copy(buffer, size);
 }
 
-static void
-httpd_output_enable(AudioOutput *ao)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	httpd->Bind();
-}
-
-static void
-httpd_output_disable(AudioOutput *ao)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	httpd->Unbind();
-}
-
 inline void
 HttpdOutput::OpenEncoder(AudioFormat &audio_format)
 {
@@ -282,6 +258,8 @@ HttpdOutput::Open(AudioFormat &audio_format)
 	assert(!open);
 	assert(clients.empty());
 
+	const std::lock_guard<Mutex> protect(mutex);
+
 	OpenEncoder(audio_format);
 
 	/* initialize other attributes */
@@ -291,20 +269,12 @@ HttpdOutput::Open(AudioFormat &audio_format)
 	open = true;
 }
 
-static void
-httpd_output_open(AudioOutput *ao, AudioFormat &audio_format)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	const std::lock_guard<Mutex> protect(httpd->mutex);
-	httpd->Open(audio_format);
-}
-
 inline void
 HttpdOutput::Close()
 {
-	assert(open);
+	const std::lock_guard<Mutex> protect(mutex);
 
+	assert(open);
 	open = false;
 
 	delete timer;
@@ -319,15 +289,6 @@ HttpdOutput::Close()
 	delete encoder;
 }
 
-static void
-httpd_output_close(AudioOutput *ao)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	const std::lock_guard<Mutex> protect(httpd->mutex);
-	httpd->Close();
-}
-
 void
 HttpdOutput::RemoveClient(HttpdClient &client)
 {
@@ -365,14 +326,6 @@ HttpdOutput::Delay() const
 		: std::chrono::steady_clock::duration::zero();
 }
 
-static std::chrono::steady_clock::duration
-httpd_output_delay(AudioOutput *ao)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	return httpd->Delay();
-}
-
 void
 HttpdOutput::BroadcastPage(Page *page)
 {
@@ -426,22 +379,12 @@ HttpdOutput::Play(const void *chunk, size_t size)
 	return size;
 }
 
-static size_t
-httpd_output_play(AudioOutput *ao, const void *chunk, size_t size)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	return httpd->Play(chunk, size);
-}
-
-static bool
-httpd_output_pause(AudioOutput *ao)
+bool
+HttpdOutput::Pause()
 {
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	if (httpd->LockHasClients()) {
+	if (LockHasClients()) {
 		static const char silence[1020] = { 0 };
-		httpd->Play(silence, sizeof(silence));
+		Play(silence, sizeof(silence));
 	}
 
 	return true;
@@ -503,14 +446,6 @@ HttpdOutput::SendTag(const Tag &tag)
 	}
 }
 
-static void
-httpd_output_tag(AudioOutput *ao, const Tag &tag)
-{
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	httpd->SendTag(tag);
-}
-
 inline void
 HttpdOutput::CancelAllClients()
 {
@@ -528,30 +463,30 @@ HttpdOutput::CancelAllClients()
 	cond.broadcast();
 }
 
-static void
-httpd_output_cancel(AudioOutput *ao)
+void
+HttpdOutput::Cancel()
 {
-	HttpdOutput *httpd = HttpdOutput::Cast(ao);
-
-	BlockingCall(io_thread_get(), [httpd](){
-			httpd->CancelAllClients();
+	BlockingCall(io_thread_get(), [this](){
+			CancelAllClients();
 		});
 }
 
+typedef AudioOutputWrapper<HttpdOutput> Wrapper;
+
 const struct AudioOutputPlugin httpd_output_plugin = {
 	"httpd",
 	nullptr,
-	httpd_output_init,
-	httpd_output_finish,
-	httpd_output_enable,
-	httpd_output_disable,
-	httpd_output_open,
-	httpd_output_close,
-	httpd_output_delay,
-	httpd_output_tag,
-	httpd_output_play,
+	&Wrapper::Init,
+	&Wrapper::Finish,
+	&Wrapper::Enable,
+	&Wrapper::Disable,
+	&Wrapper::Open,
+	&Wrapper::Close,
+	&Wrapper::Delay,
+	&Wrapper::SendTag,
+	&Wrapper::Play,
 	nullptr,
-	httpd_output_cancel,
-	httpd_output_pause,
+	&Wrapper::Cancel,
+	&Wrapper::Pause,
 	nullptr,
 };