From b376536a3bcbcc8840c4df3dc5f3850fb7b09ac5 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 4 May 2016 18:29:31 +0200
Subject: [PATCH] encoder/Interface: convert PreparedEncoder to abstract class

---
 src/encoder/EncoderInterface.hxx              | 43 +++++------------
 src/encoder/EncoderPlugin.hxx                 | 12 +----
 src/encoder/plugins/FlacEncoderPlugin.cxx     | 42 +++++-----------
 src/encoder/plugins/LameEncoderPlugin.cxx     | 44 +++++------------
 src/encoder/plugins/NullEncoderPlugin.cxx     | 34 +++----------
 src/encoder/plugins/OpusEncoderPlugin.cxx     | 48 ++++---------------
 src/encoder/plugins/ShineEncoderPlugin.cxx    | 42 +++++-----------
 src/encoder/plugins/TwolameEncoderPlugin.cxx  | 44 +++++------------
 src/encoder/plugins/VorbisEncoderPlugin.cxx   | 48 +++++--------------
 src/encoder/plugins/WaveEncoderPlugin.cxx     | 39 ++++-----------
 src/output/plugins/RecorderOutputPlugin.cxx   |  3 +-
 src/output/plugins/httpd/HttpdInternal.hxx    |  2 +-
 .../plugins/httpd/HttpdOutputPlugin.cxx       |  6 +--
 test/run_encoder.cxx                          |  2 +-
 test/test_vorbis_encoder.cxx                  |  2 +-
 15 files changed, 110 insertions(+), 301 deletions(-)

diff --git a/src/encoder/EncoderInterface.hxx b/src/encoder/EncoderInterface.hxx
index a2bbab7a3..791752f85 100644
--- a/src/encoder/EncoderInterface.hxx
+++ b/src/encoder/EncoderInterface.hxx
@@ -26,6 +26,7 @@
 #include <assert.h>
 #include <stddef.h>
 
+struct AudioFormat;
 struct Tag;
 
 class Encoder {
@@ -112,19 +113,9 @@ public:
 	virtual size_t Read(void *dest, size_t length) = 0;
 };
 
-struct PreparedEncoder {
-	const EncoderPlugin &plugin;
-
-	explicit PreparedEncoder(const EncoderPlugin &_plugin)
-		:plugin(_plugin) {}
-
-
-	/**
-	 * Frees an #Encoder object.
-	 */
-	void Dispose() {
-		plugin.finish(this);
-	}
+class PreparedEncoder {
+public:
+	virtual ~PreparedEncoder() {}
 
 	/**
 	 * Opens the object.  You must call this prior to using it.
@@ -139,24 +130,16 @@ struct PreparedEncoder {
 	 * may modify the struct to adapt it to its abilities
 	 * @return true on success
 	 */
-	Encoder *Open(AudioFormat &audio_format, Error &error) {
-		return plugin.open(this, audio_format, error);
-	}
+	virtual Encoder *Open(AudioFormat &audio_format, Error &error) = 0;
 
+	/**
+	 * Get mime type of encoded content.
+	 *
+	 * @return an constant string, nullptr on failure
+	 */
+	virtual const char *GetMimeType() const {
+		return nullptr;
+	}
 };
 
-/**
- * Get mime type of encoded content.
- *
- * @return an constant string, nullptr on failure
- */
-static inline const char *
-encoder_get_mime_type(PreparedEncoder *encoder)
-{
-	/* this method is optional */
-	return encoder->plugin.get_mime_type != nullptr
-		? encoder->plugin.get_mime_type(encoder)
-		: nullptr;
-}
-
 #endif
diff --git a/src/encoder/EncoderPlugin.hxx b/src/encoder/EncoderPlugin.hxx
index de37f6d35..83792f3a7 100644
--- a/src/encoder/EncoderPlugin.hxx
+++ b/src/encoder/EncoderPlugin.hxx
@@ -20,9 +20,7 @@
 #ifndef MPD_ENCODER_PLUGIN_HXX
 #define MPD_ENCODER_PLUGIN_HXX
 
-struct PreparedEncoder;
-class Encoder;
-struct AudioFormat;
+class PreparedEncoder;
 struct ConfigBlock;
 class Error;
 
@@ -31,14 +29,6 @@ struct EncoderPlugin {
 
 	PreparedEncoder *(*init)(const ConfigBlock &block,
 				 Error &error);
-
-	void (*finish)(PreparedEncoder *encoder);
-
-	Encoder *(*open)(PreparedEncoder *encoder,
-			 AudioFormat &audio_format,
-			 Error &error);
-
-	const char *(*get_mime_type)(PreparedEncoder *encoder);
 };
 
 /**
diff --git a/src/encoder/plugins/FlacEncoderPlugin.cxx b/src/encoder/plugins/FlacEncoderPlugin.cxx
index f6c87ed6a..f26efd8e0 100644
--- a/src/encoder/plugins/FlacEncoderPlugin.cxx
+++ b/src/encoder/plugins/FlacEncoderPlugin.cxx
@@ -88,14 +88,18 @@ private:
 	}
 };
 
-struct PreparedFlacEncoder {
-	PreparedEncoder encoder;
-
+class PreparedFlacEncoder final : public PreparedEncoder {
 	unsigned compression;
 
-	PreparedFlacEncoder():encoder(flac_encoder_plugin) {}
-
+public:
 	bool Configure(const ConfigBlock &block, Error &error);
+
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override;
+
+	const char *GetMimeType() const override {
+		return  "audio/flac";
+	}
 };
 
 static constexpr Domain flac_encoder_domain("vorbis_encoder");
@@ -119,17 +123,7 @@ flac_encoder_init(const ConfigBlock &block, Error &error)
 		return nullptr;
 	}
 
-	return &encoder->encoder;
-}
-
-static void
-flac_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedFlacEncoder *)_encoder;
-
-	/* the real libFLAC cleanup was already performed by
-	   flac_encoder_close(), so no real work here */
-	delete encoder;
+	return encoder;
 }
 
 static bool
@@ -190,10 +184,9 @@ FlacEncoder::Init(Error &error)
 	return true;
 }
 
-static Encoder *
-flac_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format, Error &error)
+Encoder *
+PreparedFlacEncoder::Open(AudioFormat &audio_format, Error &error)
 {
-	auto *encoder = (PreparedFlacEncoder *)_encoder;
 	unsigned bits_per_sample;
 
 	/* FIXME: flac should support 32bit as well */
@@ -222,7 +215,7 @@ flac_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format, Error &e
 		return nullptr;
 	}
 
-	if (!flac_encoder_setup(fse, encoder->compression,
+	if (!flac_encoder_setup(fse, compression,
 				audio_format, bits_per_sample, error)) {
 		FLAC__stream_encoder_delete(fse);
 		return nullptr;
@@ -304,17 +297,8 @@ FlacEncoder::Write(const void *data, size_t length, Error &error)
 	return true;
 }
 
-static const char *
-flac_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return "audio/flac";
-}
-
 const EncoderPlugin flac_encoder_plugin = {
 	"flac",
 	flac_encoder_init,
-	flac_encoder_finish,
-	flac_encoder_open,
-	flac_encoder_get_mime_type,
 };
 
diff --git a/src/encoder/plugins/LameEncoderPlugin.cxx b/src/encoder/plugins/LameEncoderPlugin.cxx
index e011d65bf..8d4c0b2bf 100644
--- a/src/encoder/plugins/LameEncoderPlugin.cxx
+++ b/src/encoder/plugins/LameEncoderPlugin.cxx
@@ -53,15 +53,19 @@ public:
 	size_t Read(void *dest, size_t length) override;
 };
 
-struct PreparedLameEncoder final {
-	PreparedEncoder encoder;
-
+class PreparedLameEncoder final : public PreparedEncoder {
 	float quality;
 	int bitrate;
 
-	PreparedLameEncoder():encoder(lame_encoder_plugin) {}
-
+public:
 	bool Configure(const ConfigBlock &block, Error &error);
+
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override;
+
+	const char *GetMimeType() const override {
+		return "audio/mpeg";
+	}
 };
 
 static constexpr Domain lame_encoder_domain("lame_encoder");
@@ -126,17 +130,7 @@ lame_encoder_init(const ConfigBlock &block, Error &error)
 		return nullptr;
 	}
 
-	return &encoder->encoder;
-}
-
-static void
-lame_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedLameEncoder *)_encoder;
-
-	/* the real liblame cleanup was already performed by
-	   lame_encoder_close(), so no real work here */
-	delete encoder;
+	return encoder;
 }
 
 static bool
@@ -193,12 +187,9 @@ lame_encoder_setup(lame_global_flags *gfp, float quality, int bitrate,
 	return true;
 }
 
-static Encoder *
-lame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
-		  Error &error)
+Encoder *
+PreparedLameEncoder::Open(AudioFormat &audio_format, Error &error)
 {
-	auto *encoder = (PreparedLameEncoder *)_encoder;
-
 	audio_format.format = SampleFormat::S16;
 	audio_format.channels = 2;
 
@@ -208,7 +199,7 @@ lame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
 		return nullptr;
 	}
 
-	if (!lame_encoder_setup(gfp, encoder->quality, encoder->bitrate,
+	if (!lame_encoder_setup(gfp, quality, bitrate,
 				audio_format, error)) {
 		lame_close(gfp);
 		return nullptr;
@@ -269,16 +260,7 @@ LameEncoder::Read(void *dest, size_t length)
 	return length;
 }
 
-static const char *
-lame_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return "audio/mpeg";
-}
-
 const EncoderPlugin lame_encoder_plugin = {
 	"lame",
 	lame_encoder_init,
-	lame_encoder_finish,
-	lame_encoder_open,
-	lame_encoder_get_mime_type,
 };
diff --git a/src/encoder/plugins/NullEncoderPlugin.cxx b/src/encoder/plugins/NullEncoderPlugin.cxx
index 308ecc8fd..7f64effbe 100644
--- a/src/encoder/plugins/NullEncoderPlugin.cxx
+++ b/src/encoder/plugins/NullEncoderPlugin.cxx
@@ -23,8 +23,6 @@
 #include "util/DynamicFifoBuffer.hxx"
 #include "Compiler.h"
 
-#include <assert.h>
-
 class NullEncoder final : public Encoder {
 	DynamicFifoBuffer<uint8_t> buffer;
 
@@ -44,40 +42,22 @@ public:
 	}
 };
 
-struct PreparedNullEncoder final {
-	PreparedEncoder encoder;
-
-	PreparedNullEncoder()
-		:encoder(null_encoder_plugin) {}
+class PreparedNullEncoder final : public PreparedEncoder {
+public:
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &, Error &) override {
+		return new NullEncoder();
+	}
 };
 
 static PreparedEncoder *
 null_encoder_init(gcc_unused const ConfigBlock &block,
 		  gcc_unused Error &error)
 {
-	auto *encoder = new PreparedNullEncoder();
-	return &encoder->encoder;
-}
-
-static void
-null_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedNullEncoder *)_encoder;
-	delete encoder;
-}
-
-static Encoder *
-null_encoder_open(gcc_unused PreparedEncoder *encoder,
-		  gcc_unused AudioFormat &audio_format,
-		  gcc_unused Error &error)
-{
-	return new NullEncoder();
+	return new PreparedNullEncoder();
 }
 
 const EncoderPlugin null_encoder_plugin = {
 	"null",
 	null_encoder_init,
-	null_encoder_finish,
-	null_encoder_open,
-	nullptr,
 };
diff --git a/src/encoder/plugins/OpusEncoderPlugin.cxx b/src/encoder/plugins/OpusEncoderPlugin.cxx
index 87c6d56f8..86d18be5d 100644
--- a/src/encoder/plugins/OpusEncoderPlugin.cxx
+++ b/src/encoder/plugins/OpusEncoderPlugin.cxx
@@ -77,20 +77,20 @@ private:
 	void GenerateTags();
 };
 
-struct PreparedOpusEncoder {
-	/** the base class */
-	PreparedEncoder encoder;
-
-	/* configuration */
-
+class PreparedOpusEncoder final : public PreparedEncoder {
 	opus_int32 bitrate;
 	int complexity;
 	int signal;
 
-	PreparedOpusEncoder():encoder(opus_encoder_plugin) {}
-
+public:
 	bool Configure(const ConfigBlock &block, Error &error);
-	Encoder *Open(AudioFormat &audio_format, Error &error);
+
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override;
+
+	const char *GetMimeType() const override {
+		return "audio/ogg";
+	}
 };
 
 static constexpr Domain opus_encoder_domain("opus_encoder");
@@ -146,17 +146,7 @@ opus_encoder_init(const ConfigBlock &block, Error &error)
 		return nullptr;
 	}
 
-	return &encoder->encoder;
-}
-
-static void
-opus_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedOpusEncoder *)_encoder;
-
-	/* the real libopus cleanup was already performed by
-	   opus_encoder_close(), so no real work here */
-	delete encoder;
+	return encoder;
 }
 
 OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc)
@@ -214,15 +204,6 @@ PreparedOpusEncoder::Open(AudioFormat &audio_format, Error &error)
 	return new OpusEncoder(audio_format, enc);
 }
 
-static Encoder *
-opus_encoder_open(PreparedEncoder *_encoder,
-		  AudioFormat &audio_format,
-		  Error &error)
-{
-	auto &encoder = *(PreparedOpusEncoder *)_encoder;
-	return encoder.Open(audio_format, error);
-}
-
 OpusEncoder::~OpusEncoder()
 {
 	stream.Deinitialize();
@@ -405,18 +386,9 @@ OpusEncoder::Read(void *dest, size_t length)
 	return stream.PageOut(dest, length);
 }
 
-static const char *
-opus_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return  "audio/ogg";
-}
-
 }
 
 const EncoderPlugin opus_encoder_plugin = {
 	"opus",
 	opus_encoder_init,
-	opus_encoder_finish,
-	opus_encoder_open,
-	opus_encoder_get_mime_type,
 };
diff --git a/src/encoder/plugins/ShineEncoderPlugin.cxx b/src/encoder/plugins/ShineEncoderPlugin.cxx
index 06623795d..39f30d060 100644
--- a/src/encoder/plugins/ShineEncoderPlugin.cxx
+++ b/src/encoder/plugins/ShineEncoderPlugin.cxx
@@ -84,16 +84,18 @@ public:
 	}
 };
 
-struct PreparedShineEncoder {
-	PreparedEncoder encoder;
-
+class PreparedShineEncoder final : public PreparedEncoder {
 	shine_config_t config;
 
-	PreparedShineEncoder():encoder(shine_encoder_plugin) {}
-
+public:
 	bool Configure(const ConfigBlock &block, Error &error);
 
-	bool Setup(Error &error);
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override;
+
+	const char *GetMimeType() const override {
+		return  "audio/mpeg";
+	}
 };
 
 inline bool
@@ -117,15 +119,7 @@ shine_encoder_init(const ConfigBlock &block, Error &error)
 		return nullptr;
 	}
 
-	return &encoder->encoder;
-}
-
-static void
-shine_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedShineEncoder *)_encoder;
-
-	delete encoder;
+	return encoder;
 }
 
 static shine_t
@@ -159,13 +153,10 @@ SetupShine(shine_config_t config, AudioFormat &audio_format,
 	return shine;
 }
 
-static Encoder *
-shine_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
-		   Error &error)
+Encoder *
+PreparedShineEncoder::Open(AudioFormat &audio_format, Error &error)
 {
-	auto *encoder = (PreparedShineEncoder *)_encoder;
-
-	auto shine = SetupShine(encoder->config, audio_format, error);
+	auto shine = SetupShine(config, audio_format, error);
 	if (!shine)
 		return nullptr;
 
@@ -238,16 +229,7 @@ ShineEncoder::Flush(gcc_unused Error &error)
 	return true;
 }
 
-static const char *
-shine_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return "audio/mpeg";
-}
-
 const EncoderPlugin shine_encoder_plugin = {
 	"shine",
 	shine_encoder_init,
-	shine_encoder_finish,
-	shine_encoder_open,
-	shine_encoder_get_mime_type,
 };
diff --git a/src/encoder/plugins/TwolameEncoderPlugin.cxx b/src/encoder/plugins/TwolameEncoderPlugin.cxx
index c416f3631..7ef02addb 100644
--- a/src/encoder/plugins/TwolameEncoderPlugin.cxx
+++ b/src/encoder/plugins/TwolameEncoderPlugin.cxx
@@ -72,15 +72,19 @@ public:
 	size_t Read(void *dest, size_t length) override;
 };
 
-struct PreparedTwolameEncoder final {
-	PreparedEncoder encoder;
-
+class PreparedTwolameEncoder final : public PreparedEncoder {
 	float quality;
 	int bitrate;
 
-	PreparedTwolameEncoder():encoder(twolame_encoder_plugin) {}
-
+public:
 	bool Configure(const ConfigBlock &block, Error &error);
+
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override;
+
+	const char *GetMimeType() const override {
+		return  "audio/mpeg";
+	}
 };
 
 static constexpr Domain twolame_encoder_domain("twolame_encoder");
@@ -148,17 +152,7 @@ twolame_encoder_init(const ConfigBlock &block, Error &error_r)
 		return nullptr;
 	}
 
-	return &encoder->encoder;
-}
-
-static void
-twolame_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedTwolameEncoder *)_encoder;
-
-	/* the real libtwolame cleanup was already performed by
-	   twolame_encoder_close(), so no real work here */
-	delete encoder;
+	return encoder;
 }
 
 static bool
@@ -210,12 +204,9 @@ twolame_encoder_setup(twolame_options *options, float quality, int bitrate,
 	return true;
 }
 
-static Encoder *
-twolame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
-		     Error &error)
+Encoder *
+PreparedTwolameEncoder::Open(AudioFormat &audio_format, Error &error)
 {
-	auto *encoder = (PreparedTwolameEncoder *)_encoder;
-
 	audio_format.format = SampleFormat::S16;
 	audio_format.channels = 2;
 
@@ -225,7 +216,7 @@ twolame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
 		return nullptr;
 	}
 
-	if (!twolame_encoder_setup(options, encoder->quality, encoder->bitrate,
+	if (!twolame_encoder_setup(options, quality, bitrate,
 				   audio_format, error)) {
 		twolame_close(&options);
 		return nullptr;
@@ -291,16 +282,7 @@ TwolameEncoder::Read(void *dest, size_t length)
 	return length;
 }
 
-static const char *
-twolame_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return "audio/mpeg";
-}
-
 const EncoderPlugin twolame_encoder_plugin = {
 	"twolame",
 	twolame_encoder_init,
-	twolame_encoder_finish,
-	twolame_encoder_open,
-	twolame_encoder_get_mime_type,
 };
diff --git a/src/encoder/plugins/VorbisEncoderPlugin.cxx b/src/encoder/plugins/VorbisEncoderPlugin.cxx
index ad00a858b..4baca33e5 100644
--- a/src/encoder/plugins/VorbisEncoderPlugin.cxx
+++ b/src/encoder/plugins/VorbisEncoderPlugin.cxx
@@ -73,18 +73,19 @@ private:
 	void Clear();
 };
 
-struct PreparedVorbisEncoder {
-	/** the base class */
-	PreparedEncoder encoder;
-
-	/* configuration */
-
+class PreparedVorbisEncoder final : public PreparedEncoder {
 	float quality;
 	int bitrate;
 
-	PreparedVorbisEncoder():encoder(vorbis_encoder_plugin) {}
-
+public:
 	bool Configure(const ConfigBlock &block, Error &error);
+
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override;
+
+	const char *GetMimeType() const override {
+		return "audio/ogg";
+	}
 };
 
 static constexpr Domain vorbis_encoder_domain("vorbis_encoder");
@@ -148,17 +149,7 @@ vorbis_encoder_init(const ConfigBlock &block, Error &error)
 		return nullptr;
 	}
 
-	return &encoder->encoder;
-}
-
-static void
-vorbis_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedVorbisEncoder *)_encoder;
-
-	/* the real libvorbis/libogg cleanup was already performed by
-	   vorbis_encoder_close(), so no real work here */
-	delete encoder;
+	return encoder;
 }
 
 bool
@@ -228,15 +219,11 @@ VorbisEncoder::SendHeader()
 	vorbis_comment_clear(&vc);
 }
 
-static Encoder *
-vorbis_encoder_open(PreparedEncoder *_encoder,
-		    AudioFormat &audio_format,
-		    Error &error)
+Encoder *
+PreparedVorbisEncoder::Open(AudioFormat &audio_format, Error &error)
 {
-	auto &encoder = *(PreparedVorbisEncoder *)_encoder;
-
 	auto *e = new VorbisEncoder();
-	if (!e->Open(encoder.quality, encoder.bitrate, audio_format, error)) {
+	if (!e->Open(quality, bitrate, audio_format, error)) {
 		delete e;
 		return nullptr;
 	}
@@ -348,16 +335,7 @@ VorbisEncoder::Write(const void *data, size_t length, gcc_unused Error &error)
 	return true;
 }
 
-static const char *
-vorbis_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return  "audio/ogg";
-}
-
 const EncoderPlugin vorbis_encoder_plugin = {
 	"vorbis",
 	vorbis_encoder_init,
-	vorbis_encoder_finish,
-	vorbis_encoder_open,
-	vorbis_encoder_get_mime_type,
 };
diff --git a/src/encoder/plugins/WaveEncoderPlugin.cxx b/src/encoder/plugins/WaveEncoderPlugin.cxx
index f09e7b160..140266984 100644
--- a/src/encoder/plugins/WaveEncoderPlugin.cxx
+++ b/src/encoder/plugins/WaveEncoderPlugin.cxx
@@ -44,10 +44,15 @@ public:
 	}
 };
 
-struct PreparedWaveEncoder {
-	PreparedEncoder encoder;
+class PreparedWaveEncoder final : public PreparedEncoder {
+	/* virtual methods from class PreparedEncoder */
+	Encoder *Open(AudioFormat &audio_format, Error &) override {
+		return new WaveEncoder(audio_format);
+	}
 
-	PreparedWaveEncoder():encoder(wave_encoder_plugin) {}
+	const char *GetMimeType() const override {
+		return "audio/wav";
+	}
 };
 
 struct WaveHeader {
@@ -96,16 +101,7 @@ static PreparedEncoder *
 wave_encoder_init(gcc_unused const ConfigBlock &block,
 		  gcc_unused Error &error)
 {
-	auto *encoder = new PreparedWaveEncoder();
-	return &encoder->encoder;
-}
-
-static void
-wave_encoder_finish(PreparedEncoder *_encoder)
-{
-	auto *encoder = (PreparedWaveEncoder *)_encoder;
-
-	delete encoder;
+	return new PreparedWaveEncoder();
 }
 
 WaveEncoder::WaveEncoder(AudioFormat &audio_format)
@@ -151,14 +147,6 @@ WaveEncoder::WaveEncoder(AudioFormat &audio_format)
 	buffer.Append(sizeof(*header));
 }
 
-static Encoder *
-wave_encoder_open(gcc_unused PreparedEncoder *_encoder,
-		  AudioFormat &audio_format,
-		  gcc_unused Error &error)
-{
-	return new WaveEncoder(audio_format);
-}
-
 static size_t
 pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
 {
@@ -239,16 +227,7 @@ WaveEncoder::Write(const void *src, size_t length,
 	return true;
 }
 
-static const char *
-wave_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
-{
-	return "audio/wav";
-}
-
 const EncoderPlugin wave_encoder_plugin = {
 	"wave",
 	wave_encoder_init,
-	wave_encoder_finish,
-	wave_encoder_open,
-	wave_encoder_get_mime_type,
 };
diff --git a/src/output/plugins/RecorderOutputPlugin.cxx b/src/output/plugins/RecorderOutputPlugin.cxx
index 51fafdcfc..da03668ca 100644
--- a/src/output/plugins/RecorderOutputPlugin.cxx
+++ b/src/output/plugins/RecorderOutputPlugin.cxx
@@ -76,8 +76,7 @@ class RecorderOutput {
 		:base(recorder_output_plugin) {}
 
 	~RecorderOutput() {
-		if (prepared_encoder != nullptr)
-			prepared_encoder->Dispose();
+		delete prepared_encoder;
 	}
 
 	bool Initialize(const ConfigBlock &block, Error &error_r) {
diff --git a/src/output/plugins/httpd/HttpdInternal.hxx b/src/output/plugins/httpd/HttpdInternal.hxx
index 29d3897f2..f554c7cf6 100644
--- a/src/output/plugins/httpd/HttpdInternal.hxx
+++ b/src/output/plugins/httpd/HttpdInternal.hxx
@@ -45,7 +45,7 @@ class EventLoop;
 class ServerSocket;
 class HttpdClient;
 class Page;
-struct PreparedEncoder;
+class PreparedEncoder;
 class Encoder;
 struct Tag;
 
diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.cxx b/src/output/plugins/httpd/HttpdOutputPlugin.cxx
index 42aab7001..8438b736d 100644
--- a/src/output/plugins/httpd/HttpdOutputPlugin.cxx
+++ b/src/output/plugins/httpd/HttpdOutputPlugin.cxx
@@ -63,9 +63,7 @@ HttpdOutput::~HttpdOutput()
 	if (metadata != nullptr)
 		metadata->Unref();
 
-	if (prepared_encoder != nullptr)
-		prepared_encoder->Dispose();
-
+	delete prepared_encoder;
 }
 
 inline bool
@@ -128,7 +126,7 @@ HttpdOutput::Configure(const ConfigBlock &block, Error &error)
 		return false;
 
 	/* determine content type */
-	content_type = encoder_get_mime_type(prepared_encoder);
+	content_type = prepared_encoder->GetMimeType();
 	if (content_type == nullptr)
 		content_type = "application/octet-stream";
 
diff --git a/test/run_encoder.cxx b/test/run_encoder.cxx
index ad4d1444a..234a86d3f 100644
--- a/test/run_encoder.cxx
+++ b/test/run_encoder.cxx
@@ -111,7 +111,7 @@ int main(int argc, char **argv)
 		EncoderToOutputStream(os, *encoder);
 
 		delete encoder;
-		p_encoder->Dispose();
+		delete p_encoder;
 
 		return EXIT_SUCCESS;
 	} catch (const std::exception &e) {
diff --git a/test/test_vorbis_encoder.cxx b/test/test_vorbis_encoder.cxx
index e0ccd5ea6..fa13b2849 100644
--- a/test/test_vorbis_encoder.cxx
+++ b/test/test_vorbis_encoder.cxx
@@ -103,7 +103,7 @@ main(gcc_unused int argc, gcc_unused char **argv)
 		EncoderToOutputStream(os, *encoder);
 
 		delete encoder;
-		p_encoder->Dispose();
+		delete p_encoder;
 
 		return EXIT_SUCCESS;
 	} catch (const std::exception &e) {