From 74617389c88ccf630b8cce4b54d9e2fa5afb2259 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 16 Sep 2011 23:31:48 +0200
Subject: output_plugin: the plugin allocates the audio_output object

Pass audio_output objects around instead of void pointers.  This will
give some more control to the plugin, and prepares for non-blocking
audio outputs.
---
 Makefile.am                         |   3 +-
 src/output/alsa_output_plugin.c     |  41 +++++----
 src/output/ao_output_plugin.c       |  35 +++++---
 src/output/ffado_output_plugin.c    |  35 +++++---
 src/output/fifo_output_plugin.c     |  38 ++++----
 src/output/httpd_internal.h         |   3 +
 src/output/httpd_output_plugin.c    |  68 +++++++++------
 src/output/jack_output_plugin.c     |  49 ++++++-----
 src/output/mvp_output_plugin.c      |  41 +++++----
 src/output/null_output_plugin.c     |  36 ++++----
 src/output/openal_output_plugin.c   |  36 ++++----
 src/output/oss_output_plugin.c      |  49 +++++++----
 src/output/osx_output_plugin.c      |  37 ++++----
 src/output/pipe_output_plugin.c     |  32 ++++---
 src/output/pulse_output_plugin.c    |  53 +++++++-----
 src/output/raop_output_plugin.c     |  44 ++++++----
 src/output/recorder_output_plugin.c |  34 +++++---
 src/output/roar_output_plugin.c     |  41 +++++----
 src/output/shout_output_plugin.c    |  63 ++++++++------
 src/output/solaris_output_plugin.c  |  37 ++++----
 src/output/winmm_output_plugin.c    |  40 +++++----
 src/output_all.c                    |  52 +++++------
 src/output_api.h                    |   1 +
 src/output_control.c                |   2 +-
 src/output_control.h                |   5 --
 src/output_finish.c                 |  14 ++-
 src/output_init.c                   | 168 ++++++++++++++++++++++--------------
 src/output_internal.h               |  23 +++--
 src/output_plugin.c                 | 108 +++++++++++++++++++++++
 src/output_plugin.h                 | 135 +++++++++--------------------
 src/output_thread.c                 |  31 +++----
 test/run_output.c                   |  28 +++---
 32 files changed, 830 insertions(+), 552 deletions(-)
 create mode 100644 src/output_plugin.c

diff --git a/Makefile.am b/Makefile.am
index c85422ada..e85a2967f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -45,7 +45,6 @@ mpd_headers = \
 	src/audio_parser.h \
 	src/output_internal.h \
 	src/output_api.h \
-	src/output_plugin.h \
 	src/output_list.h \
 	src/output_all.h \
 	src/output_thread.h \
@@ -716,6 +715,7 @@ OUTPUT_API_SRC = \
 	src/output_state.c \
 	src/output_print.c \
 	src/output_command.c \
+	src/output_plugin.c src/output_plugin.h \
 	src/output_finish.c \
 	src/output_init.c
 
@@ -1144,6 +1144,7 @@ test_run_output_SOURCES = test/run_output.c \
 	src/page.c \
 	src/socket_util.c \
 	src/output_init.c src/output_finish.c src/output_list.c \
+	src/output_plugin.c \
 	$(ENCODER_SRC) \
 	src/mixer_api.c \
 	src/mixer_control.c \
diff --git a/src/output/alsa_output_plugin.c b/src/output/alsa_output_plugin.c
index fb0498e96..41293272d 100644
--- a/src/output/alsa_output_plugin.c
+++ b/src/output/alsa_output_plugin.c
@@ -43,6 +43,8 @@ typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
 					snd_pcm_uframes_t size);
 
 struct alsa_data {
+	struct audio_output base;
+
 	/** the configured name of the ALSA device; NULL for the
 	    default device */
 	char *device;
@@ -143,23 +145,27 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param)
 #endif
 }
 
-static void *
-alsa_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-	  const struct config_param *param,
-	  G_GNUC_UNUSED GError **error)
+static struct audio_output *
+alsa_init(const struct config_param *param, GError **error_r)
 {
 	struct alsa_data *ad = alsa_data_new();
 
+	if (!ao_base_init(&ad->base, &alsa_output_plugin, param, error_r)) {
+		g_free(ad);
+		return NULL;
+	}
+
 	alsa_configure(ad, param);
 
-	return ad;
+	return &ad->base;
 }
 
 static void
-alsa_finish(void *data)
+alsa_finish(struct audio_output *ao)
 {
-	struct alsa_data *ad = data;
+	struct alsa_data *ad = (struct alsa_data *)ao;
 
+	ao_base_finish(&ad->base);
 	alsa_data_free(ad);
 
 	/* free libasound's config cache */
@@ -530,9 +536,9 @@ error:
 }
 
 static bool
-alsa_open(void *data, struct audio_format *audio_format, GError **error)
+alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
 {
-	struct alsa_data *ad = data;
+	struct alsa_data *ad = (struct alsa_data *)ao;
 	int err;
 	bool success;
 
@@ -594,9 +600,9 @@ alsa_recover(struct alsa_data *ad, int err)
 }
 
 static void
-alsa_drain(void *data)
+alsa_drain(struct audio_output *ao)
 {
-	struct alsa_data *ad = data;
+	struct alsa_data *ad = (struct alsa_data *)ao;
 
 	if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
 		return;
@@ -628,9 +634,9 @@ alsa_drain(void *data)
 }
 
 static void
-alsa_cancel(void *data)
+alsa_cancel(struct audio_output *ao)
 {
-	struct alsa_data *ad = data;
+	struct alsa_data *ad = (struct alsa_data *)ao;
 
 	ad->period_position = 0;
 
@@ -638,17 +644,18 @@ alsa_cancel(void *data)
 }
 
 static void
-alsa_close(void *data)
+alsa_close(struct audio_output *ao)
 {
-	struct alsa_data *ad = data;
+	struct alsa_data *ad = (struct alsa_data *)ao;
 
 	snd_pcm_close(ad->pcm);
 }
 
 static size_t
-alsa_play(void *data, const void *chunk, size_t size, GError **error)
+alsa_play(struct audio_output *ao, const void *chunk, size_t size,
+	  GError **error)
 {
-	struct alsa_data *ad = data;
+	struct alsa_data *ad = (struct alsa_data *)ao;
 
 	size /= ad->frame_size;
 
diff --git a/src/output/ao_output_plugin.c b/src/output/ao_output_plugin.c
index e85d97d6e..c0790681e 100644
--- a/src/output/ao_output_plugin.c
+++ b/src/output/ao_output_plugin.c
@@ -33,6 +33,8 @@ static const ao_sample_format OUR_AO_FORMAT_INITIALIZER;
 static unsigned ao_output_ref;
 
 struct ao_data {
+	struct audio_output base;
+
 	size_t write_size;
 	int driver;
 	ao_option *options;
@@ -79,12 +81,17 @@ ao_output_error(GError **error_r)
 		    "%s", error);
 }
 
-static void *
-ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-	       const struct config_param *param,
+static struct audio_output *
+ao_output_init(const struct config_param *param,
 	       GError **error)
 {
 	struct ao_data *ad = g_new(struct ao_data, 1);
+
+	if (!ao_base_init(&ad->base, &ao_output_plugin, param, error)) {
+		g_free(ad);
+		return NULL;
+	}
+
 	ao_info *ai;
 	const char *value;
 
@@ -107,6 +114,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 		g_set_error(error, ao_output_quark(), 0,
 			    "\"%s\" is not a valid ao driver",
 			    value);
+		ao_base_finish(&ad->base);
 		g_free(ad);
 		return NULL;
 	}
@@ -114,6 +122,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	if ((ai = ao_driver_info(ad->driver)) == NULL) {
 		g_set_error(error, ao_output_quark(), 0,
 			    "problems getting driver info");
+		ao_base_finish(&ad->base);
 		g_free(ad);
 		return NULL;
 	}
@@ -132,6 +141,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 				g_set_error(error, ao_output_quark(), 0,
 					    "problems parsing options \"%s\"",
 					    options[i]);
+				ao_base_finish(&ad->base);
 				g_free(ad);
 				return NULL;
 			}
@@ -145,15 +155,16 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 		g_strfreev(options);
 	}
 
-	return ad;
+	return &ad->base;
 }
 
 static void
-ao_output_finish(void *data)
+ao_output_finish(struct audio_output *ao)
 {
-	struct ao_data *ad = (struct ao_data *)data;
+	struct ao_data *ad = (struct ao_data *)ao;
 
 	ao_free_options(ad->options);
+	ao_base_finish(&ad->base);
 	g_free(ad);
 
 	ao_output_ref--;
@@ -163,19 +174,19 @@ ao_output_finish(void *data)
 }
 
 static void
-ao_output_close(void *data)
+ao_output_close(struct audio_output *ao)
 {
-	struct ao_data *ad = (struct ao_data *)data;
+	struct ao_data *ad = (struct ao_data *)ao;
 
 	ao_close(ad->device);
 }
 
 static bool
-ao_output_open(void *data, struct audio_format *audio_format,
+ao_output_open(struct audio_output *ao, struct audio_format *audio_format,
 	       GError **error)
 {
 	ao_sample_format format = OUR_AO_FORMAT_INITIALIZER;
-	struct ao_data *ad = (struct ao_data *)data;
+	struct ao_data *ad = (struct ao_data *)ao;
 
 	switch (audio_format->format) {
 	case SAMPLE_FORMAT_S8:
@@ -227,10 +238,10 @@ static int ao_play_deconst(ao_device *device, const void *output_samples,
 }
 
 static size_t
-ao_output_play(void *data, const void *chunk, size_t size,
+ao_output_play(struct audio_output *ao, const void *chunk, size_t size,
 	       GError **error)
 {
-	struct ao_data *ad = (struct ao_data *)data;
+	struct ao_data *ad = (struct ao_data *)ao;
 
 	if (size > ad->write_size)
 		size = ad->write_size;
diff --git a/src/output/ffado_output_plugin.c b/src/output/ffado_output_plugin.c
index af74419a9..ba239a4ad 100644
--- a/src/output/ffado_output_plugin.c
+++ b/src/output/ffado_output_plugin.c
@@ -54,6 +54,8 @@ struct mpd_ffado_stream {
 };
 
 struct mpd_ffado_device {
+	struct audio_output base;
+
 	char *device_name;
 	int verbose;
 	unsigned period_size, nb_buffers;
@@ -83,21 +85,26 @@ ffado_output_quark(void)
 	return g_quark_from_static_string("ffado_output");
 }
 
-static void *
-ffado_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-	   const struct config_param *param,
+static struct audio_output *
+ffado_init(const struct config_param *param,
 	   GError **error_r)
 {
 	g_debug("using libffado version %s, API=%d",
 		ffado_get_version(), ffado_get_api_version());
 
 	struct mpd_ffado_device *fd = g_new(struct mpd_ffado_device, 1);
+	if (!ao_base_init(&fd->base, &ffado_output_plugin, param, error_r)) {
+		g_free(fd);
+		return NULL;
+	}
+
 	fd->device_name = config_dup_block_string(param, "device", NULL);
 	fd->verbose = config_get_block_unsigned(param, "verbose", 0);
 
 	fd->period_size = config_get_block_unsigned(param, "period_size",
 						    1024);
 	if (fd->period_size == 0 || fd->period_size > 1024 * 1024) {
+		ao_base_finish(&fd->base);
 		g_set_error(error_r, ffado_output_quark(), 0,
 			    "invalid period_size setting");
 		return false;
@@ -105,20 +112,22 @@ ffado_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 
 	fd->nb_buffers = config_get_block_unsigned(param, "nb_buffers", 3);
 	if (fd->nb_buffers == 0 || fd->nb_buffers > 1024) {
+		ao_base_finish(&fd->base);
 		g_set_error(error_r, ffado_output_quark(), 0,
 			    "invalid nb_buffers setting");
 		return false;
 	}
 
-	return fd;
+	return &fd->base;
 }
 
 static void
-ffado_finish(void *data)
+ffado_finish(struct audio_output *ao)
 {
-	struct mpd_ffado_device *fd = data;
+	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao;
 
 	g_free(fd->device_name);
+	ao_base_finish(&fd->base);
 	g_free(fd);
 }
 
@@ -228,9 +237,10 @@ ffado_configure(struct mpd_ffado_device *fd, struct audio_format *audio_format,
 }
 
 static bool
-ffado_open(void *data, struct audio_format *audio_format, GError **error_r)
+ffado_open(struct audio_output *ao, struct audio_format *audio_format,
+	   GError **error_r)
 {
-	struct mpd_ffado_device *fd = data;
+	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao;
 
 	/* will be converted to floating point, choose best input
 	   format */
@@ -274,9 +284,9 @@ ffado_open(void *data, struct audio_format *audio_format, GError **error_r)
 }
 
 static void
-ffado_close(void *data)
+ffado_close(struct audio_output *ao)
 {
-	struct mpd_ffado_device *fd = data;
+	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao;
 
 	ffado_streaming_stop(fd->dev);
 	ffado_streaming_finish(fd->dev);
@@ -288,9 +298,10 @@ ffado_close(void *data)
 }
 
 static size_t
-ffado_play(void *data, const void *chunk, size_t size, GError **error_r)
+ffado_play(struct audio_output *ao, const void *chunk, size_t size,
+	   GError **error_r)
 {
-	struct mpd_ffado_device *fd = data;
+	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao;
 
 	/* wait for prefious buffer to finish (if it was full) */
 
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index db250e81c..f7c88cdc8 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -39,6 +39,8 @@
 #define FIFO_BUFFER_SIZE 65536 /* pipe capacity on Linux >= 2.6.11 */
 
 struct fifo_data {
+	struct audio_output base;
+
 	char *path;
 	int input;
 	int output;
@@ -176,9 +178,8 @@ fifo_open(struct fifo_data *fd, GError **error)
 	return true;
 }
 
-static void *
-fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		 const struct config_param *param,
+static struct audio_output *
+fifo_output_init(const struct config_param *param,
 		 GError **error_r)
 {
 	struct fifo_data *fd;
@@ -197,28 +198,35 @@ fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	fd = fifo_data_new();
 	fd->path = path;
 
+	if (!ao_base_init(&fd->base, &fifo_output_plugin, param, error_r)) {
+		fifo_data_free(fd);
+		return NULL;
+	}
+
 	if (!fifo_open(fd, error_r)) {
+		ao_base_finish(&fd->base);
 		fifo_data_free(fd);
 		return NULL;
 	}
 
-	return fd;
+	return &fd->base;
 }
 
 static void
-fifo_output_finish(void *data)
+fifo_output_finish(struct audio_output *ao)
 {
-	struct fifo_data *fd = (struct fifo_data *)data;
+	struct fifo_data *fd = (struct fifo_data *)ao;
 
 	fifo_close(fd);
+	ao_base_finish(&fd->base);
 	fifo_data_free(fd);
 }
 
 static bool
-fifo_output_open(void *data, struct audio_format *audio_format,
+fifo_output_open(struct audio_output *ao, struct audio_format *audio_format,
 		 G_GNUC_UNUSED GError **error)
 {
-	struct fifo_data *fd = (struct fifo_data *)data;
+	struct fifo_data *fd = (struct fifo_data *)ao;
 
 	fd->timer = timer_new(audio_format);
 
@@ -226,17 +234,17 @@ fifo_output_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-fifo_output_close(void *data)
+fifo_output_close(struct audio_output *ao)
 {
-	struct fifo_data *fd = (struct fifo_data *)data;
+	struct fifo_data *fd = (struct fifo_data *)ao;
 
 	timer_free(fd->timer);
 }
 
 static void
-fifo_output_cancel(void *data)
+fifo_output_cancel(struct audio_output *ao)
 {
-	struct fifo_data *fd = (struct fifo_data *)data;
+	struct fifo_data *fd = (struct fifo_data *)ao;
 	char buf[FIFO_BUFFER_SIZE];
 	int bytes = 1;
 
@@ -252,10 +260,10 @@ fifo_output_cancel(void *data)
 }
 
 static size_t
-fifo_output_play(void *data, const void *chunk, size_t size,
+fifo_output_play(struct audio_output *ao, const void *chunk, size_t size,
 		 GError **error)
 {
-	struct fifo_data *fd = (struct fifo_data *)data;
+	struct fifo_data *fd = (struct fifo_data *)ao;
 	ssize_t bytes;
 
 	if (!fd->timer->started)
@@ -274,7 +282,7 @@ fifo_output_play(void *data, const void *chunk, size_t size,
 			switch (errno) {
 			case EAGAIN:
 				/* The pipe is full, so empty it */
-				fifo_output_cancel(fd);
+				fifo_output_cancel(&fd->base);
 				continue;
 			case EINTR:
 				continue;
diff --git a/src/output/httpd_internal.h b/src/output/httpd_internal.h
index 3e6e9768d..5dcb8ab9b 100644
--- a/src/output/httpd_internal.h
+++ b/src/output/httpd_internal.h
@@ -25,6 +25,7 @@
 #ifndef MPD_OUTPUT_HTTPD_INTERNAL_H
 #define MPD_OUTPUT_HTTPD_INTERNAL_H
 
+#include "output_internal.h"
 #include "timer.h"
 
 #include <glib.h>
@@ -34,6 +35,8 @@
 struct httpd_client;
 
 struct httpd_output {
+	struct audio_output base;
+
 	/**
 	 * True if the audio output is open and accepts client
 	 * connections.
diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c
index abbd78ad2..ab663badf 100644
--- a/src/output/httpd_output_plugin.c
+++ b/src/output/httpd_output_plugin.c
@@ -79,12 +79,16 @@ httpd_output_unbind(struct httpd_output *httpd)
 	g_mutex_unlock(httpd->mutex);
 }
 
-static void *
-httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		  const struct config_param *param,
+static struct audio_output *
+httpd_output_init(const struct config_param *param,
 		  GError **error)
 {
 	struct httpd_output *httpd = g_new(struct httpd_output, 1);
+	if (!ao_base_init(&httpd->base, &httpd_output_plugin, param, error)) {
+		g_free(httpd);
+		return NULL;
+	}
+
 	const char *encoder_name, *bind_to_address;
 	const struct encoder_plugin *encoder_plugin;
 	guint port;
@@ -104,6 +108,7 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	if (encoder_plugin == NULL) {
 		g_set_error(error, httpd_output_quark(), 0,
 			    "No such encoder: %s", encoder_name);
+		ao_base_finish(&httpd->base);
 		g_free(httpd);
 		return NULL;
 	}
@@ -121,8 +126,11 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 		? server_socket_add_host(httpd->server_socket, bind_to_address,
 					 port, error)
 		: server_socket_add_port(httpd->server_socket, port, error);
-	if (!success)
+	if (!success) {
+		ao_base_finish(&httpd->base);
+		g_free(httpd);
 		return NULL;
+	}
 
 	/* initialize metadata */
 	httpd->metadata = NULL;
@@ -131,8 +139,11 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	/* initialize encoder */
 
 	httpd->encoder = encoder_init(encoder_plugin, param, error);
-	if (httpd->encoder == NULL)
+	if (httpd->encoder == NULL) {
+		ao_base_finish(&httpd->base);
+		g_free(httpd);
 		return NULL;
+	}
 
 	/* determine content type */
 	httpd->content_type = encoder_get_mime_type(httpd->encoder);
@@ -142,13 +153,13 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 
 	httpd->mutex = g_mutex_new();
 
-	return httpd;
+	return &httpd->base;
 }
 
 static void
-httpd_output_finish(void *data)
+httpd_output_finish(struct audio_output *ao)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	if (httpd->metadata)
 		page_unref(httpd->metadata);
@@ -156,6 +167,7 @@ httpd_output_finish(void *data)
 	encoder_finish(httpd->encoder);
 	server_socket_free(httpd->server_socket);
 	g_mutex_free(httpd->mutex);
+	ao_base_finish(&httpd->base);
 	g_free(httpd);
 }
 
@@ -287,26 +299,26 @@ httpd_output_encoder_open(struct httpd_output *httpd,
 }
 
 static bool
-httpd_output_enable(void *data, GError **error_r)
+httpd_output_enable(struct audio_output *ao, GError **error_r)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	return httpd_output_bind(httpd, error_r);
 }
 
 static void
-httpd_output_disable(void *data)
+httpd_output_disable(struct audio_output *ao)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	httpd_output_unbind(httpd);
 }
 
 static bool
-httpd_output_open(void *data, struct audio_format *audio_format,
+httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
 		  GError **error)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 	bool success;
 
 	g_mutex_lock(httpd->mutex);
@@ -339,9 +351,10 @@ httpd_client_delete(gpointer data, G_GNUC_UNUSED gpointer user_data)
 	httpd_client_free(client);
 }
 
-static void httpd_output_close(void *data)
+static void
+httpd_output_close(struct audio_output *ao)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	g_mutex_lock(httpd->mutex);
 
@@ -380,9 +393,9 @@ httpd_output_send_header(struct httpd_output *httpd,
 }
 
 static unsigned
-httpd_output_delay(void *data)
+httpd_output_delay(struct audio_output *ao)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	return httpd->timer->started
 		? timer_delay(httpd->timer)
@@ -458,9 +471,10 @@ httpd_output_encode_and_play(struct httpd_output *httpd,
 }
 
 static size_t
-httpd_output_play(void *data, const void *chunk, size_t size, GError **error)
+httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
+		  GError **error)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 	bool has_clients;
 
 	g_mutex_lock(httpd->mutex);
@@ -484,9 +498,9 @@ httpd_output_play(void *data, const void *chunk, size_t size, GError **error)
 }
 
 static bool
-httpd_output_pause(void *data)
+httpd_output_pause(struct audio_output *ao)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	g_mutex_lock(httpd->mutex);
 	bool has_clients = httpd->clients != NULL;
@@ -494,7 +508,7 @@ httpd_output_pause(void *data)
 
 	if (has_clients) {
 		static const char silence[1020];
-		return httpd_output_play(data, silence, sizeof(silence),
+		return httpd_output_play(ao, silence, sizeof(silence),
 					 NULL) > 0;
 	} else {
 		g_usleep(100000);
@@ -512,9 +526,9 @@ httpd_send_metadata(gpointer data, gpointer user_data)
 }
 
 static void
-httpd_output_tag(void *data, const struct tag *tag)
+httpd_output_tag(struct audio_output *ao, const struct tag *tag)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	assert(tag != NULL);
 
@@ -571,9 +585,9 @@ httpd_client_cancel_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
 }
 
 static void
-httpd_output_cancel(void *data)
+httpd_output_cancel(struct audio_output *ao)
 {
-	struct httpd_output *httpd = data;
+	struct httpd_output *httpd = (struct httpd_output *)ao;
 
 	g_mutex_lock(httpd->mutex);
 	g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL);
diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c
index 189b52a6f..cd769088b 100644
--- a/src/output/jack_output_plugin.c
+++ b/src/output/jack_output_plugin.c
@@ -44,6 +44,8 @@ enum {
 static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t);
 
 struct jack_data {
+	struct audio_output base;
+
 	/**
 	 * libjack options passed to jack_client_open().
 	 */
@@ -292,14 +294,18 @@ parse_port_list(int line, const char *source, char **dest, GError **error_r)
 	return n;
 }
 
-static void *
-mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-	      const struct config_param *param, GError **error_r)
+static struct audio_output *
+mpd_jack_init(const struct config_param *param, GError **error_r)
 {
-	struct jack_data *jd;
+	struct jack_data *jd = g_new(struct jack_data, 1);
+
+	if (!ao_base_init(&jd->base, &jack_output_plugin, param, error_r)) {
+		g_free(jd);
+		return NULL;
+	}
+
 	const char *value;
 
-	jd = g_new(struct jack_data, 1);
 	jd->options = JackNullOption;
 
 	jd->name = config_get_block_string(param, "client_name", NULL);
@@ -362,13 +368,13 @@ mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	jack_set_info_function(mpd_jack_info);
 #endif
 
-	return jd;
+	return &jd->base;
 }
 
 static void
-mpd_jack_finish(void *data)
+mpd_jack_finish(struct audio_output *ao)
 {
-	struct jack_data *jd = data;
+	struct jack_data *jd = (struct jack_data *)ao;
 
 	for (unsigned i = 0; i < jd->num_source_ports; ++i)
 		g_free(jd->source_ports[i]);
@@ -376,13 +382,14 @@ mpd_jack_finish(void *data)
 	for (unsigned i = 0; i < jd->num_destination_ports; ++i)
 		g_free(jd->destination_ports[i]);
 
+	ao_base_finish(&jd->base);
 	g_free(jd);
 }
 
 static bool
-mpd_jack_enable(void *data, GError **error_r)
+mpd_jack_enable(struct audio_output *ao, GError **error_r)
 {
-	struct jack_data *jd = (struct jack_data *)data;
+	struct jack_data *jd = (struct jack_data *)ao;
 
 	for (unsigned i = 0; i < jd->num_source_ports; ++i)
 		jd->ringbuffer[i] = NULL;
@@ -391,9 +398,9 @@ mpd_jack_enable(void *data, GError **error_r)
 }
 
 static void
-mpd_jack_disable(void *data)
+mpd_jack_disable(struct audio_output *ao)
 {
-	struct jack_data *jd = (struct jack_data *)data;
+	struct jack_data *jd = (struct jack_data *)ao;
 
 	if (jd->client != NULL)
 		mpd_jack_disconnect(jd);
@@ -556,9 +563,10 @@ mpd_jack_start(struct jack_data *jd, GError **error_r)
 }
 
 static bool
-mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r)
+mpd_jack_open(struct audio_output *ao, struct audio_format *audio_format,
+	      GError **error_r)
 {
-	struct jack_data *jd = data;
+	struct jack_data *jd = (struct jack_data *)ao;
 
 	assert(jd != NULL);
 
@@ -577,9 +585,9 @@ mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r)
 }
 
 static void
-mpd_jack_close(G_GNUC_UNUSED void *data)
+mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao)
 {
-	struct jack_data *jd = data;
+	struct jack_data *jd = (struct jack_data *)ao;
 
 	mpd_jack_stop(jd);
 }
@@ -649,9 +657,10 @@ mpd_jack_write_samples(struct jack_data *jd, const void *src,
 }
 
 static size_t
-mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r)
+mpd_jack_play(struct audio_output *ao, const void *chunk, size_t size,
+	      GError **error_r)
 {
-	struct jack_data *jd = data;
+	struct jack_data *jd = (struct jack_data *)ao;
 	const size_t frame_size = audio_format_frame_size(&jd->audio_format);
 	size_t space = 0, space1;
 
@@ -693,9 +702,9 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r)
 }
 
 static bool
-mpd_jack_pause(void *data)
+mpd_jack_pause(struct audio_output *ao)
 {
-	struct jack_data *jd = data;
+	struct jack_data *jd = (struct jack_data *)ao;
 
 	if (jd->shutdown)
 		return false;
diff --git a/src/output/mvp_output_plugin.c b/src/output/mvp_output_plugin.c
index 6f058b53e..aec09248f 100644
--- a/src/output/mvp_output_plugin.c
+++ b/src/output/mvp_output_plugin.c
@@ -70,6 +70,8 @@ typedef struct {
 #define MVP_GET_AUD_REGS		_IOW('a',28,aud_ctl_regs_t*)
 
 struct mvp_data {
+	struct audio_output base;
+
 	struct audio_format audio_format;
 	int fd;
 };
@@ -131,21 +133,26 @@ mvp_output_test_default_device(void)
 	return false;
 }
 
-static void *
-mvp_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		G_GNUC_UNUSED const struct config_param *param,
-		G_GNUC_UNUSED GError **error)
+static struct audio_output *
+mvp_output_init(G_GNUC_UNUSED const struct config_param *param, GError **error)
 {
 	struct mvp_data *md = g_new(struct mvp_data, 1);
+
+	if (!ao_base_init(&md->base, &mvp_output_plugin, param, error)) {
+		g_free(md);
+		return NULL;
+	}
+
 	md->fd = -1;
 
-	return md;
+	return &md->base;
 }
 
 static void
-mvp_output_finish(void *data)
+mvp_output_finish(struct audio_output *ao)
 {
-	struct mvp_data *md = data;
+	struct mvp_data *md = (struct mvp_data *)ao;
+	ao_base_finish(&md->base);
 	g_free(md);
 }
 
@@ -226,9 +233,10 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format,
 }
 
 static bool
-mvp_output_open(void *data, struct audio_format *audio_format, GError **error)
+mvp_output_open(struct audio_output *ao, struct audio_format *audio_format,
+		GError **error)
 {
-	struct mvp_data *md = data;
+	struct mvp_data *md = (struct mvp_data *)ao;
 	long long int stc = 0;
 	int mix[5] = { 0, 2, 7, 1, 0 };
 	bool success;
@@ -274,17 +282,17 @@ mvp_output_open(void *data, struct audio_format *audio_format, GError **error)
 	return true;
 }
 
-static void mvp_output_close(void *data)
+static void mvp_output_close(struct audio_output *ao)
 {
-	struct mvp_data *md = data;
+	struct mvp_data *md = (struct mvp_data *)ao;
 	if (md->fd >= 0)
 		close(md->fd);
 	md->fd = -1;
 }
 
-static void mvp_output_cancel(void *data)
+static void mvp_output_cancel(struct audio_output *ao)
 {
-	struct mvp_data *md = data;
+	struct mvp_data *md = (struct mvp_data *)ao;
 	if (md->fd >= 0) {
 		ioctl(md->fd, MVP_SET_AUD_RESET, 0x11);
 		close(md->fd);
@@ -293,16 +301,17 @@ static void mvp_output_cancel(void *data)
 }
 
 static size_t
-mvp_output_play(void *data, const void *chunk, size_t size, GError **error)
+mvp_output_play(struct audio_output *ao, const void *chunk, size_t size,
+		GError **error)
 {
-	struct mvp_data *md = data;
+	struct mvp_data *md = (struct mvp_data *)ao;
 	ssize_t ret;
 
 	/* reopen the device since it was closed by dropBufferedAudio */
 	if (md->fd < 0) {
 		bool success;
 
-		success = mvp_output_open(md, &md->audio_format, error);
+		success = mvp_output_open(ao, &md->audio_format, error);
 		if (!success)
 			return 0;
 	}
diff --git a/src/output/null_output_plugin.c b/src/output/null_output_plugin.c
index 94c0c5321..e680e617c 100644
--- a/src/output/null_output_plugin.c
+++ b/src/output/null_output_plugin.c
@@ -27,39 +27,45 @@
 #include <assert.h>
 
 struct null_data {
+	struct audio_output base;
+
 	bool sync;
 
 	struct timer *timer;
 };
 
-static void *
-null_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-	  G_GNUC_UNUSED const struct config_param *param,
-	  G_GNUC_UNUSED GError **error)
+static struct audio_output *
+null_init(const struct config_param *param, GError **error_r)
 {
 	struct null_data *nd = g_new(struct null_data, 1);
 
+	if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) {
+		g_free(nd);
+		return NULL;
+	}
+
 	nd->sync = config_get_block_bool(param, "sync", true);
 	nd->timer = NULL;
 
-	return nd;
+	return &nd->base;
 }
 
 static void
-null_finish(void *data)
+null_finish(struct audio_output *ao)
 {
-	struct null_data *nd = data;
+	struct null_data *nd = (struct null_data *)ao;
 
 	assert(nd->timer == NULL);
 
+	ao_base_finish(&nd->base);
 	g_free(nd);
 }
 
 static bool
-null_open(void *data, struct audio_format *audio_format,
+null_open(struct audio_output *ao, struct audio_format *audio_format,
 	  G_GNUC_UNUSED GError **error)
 {
-	struct null_data *nd = data;
+	struct null_data *nd = (struct null_data *)ao;
 
 	if (nd->sync)
 		nd->timer = timer_new(audio_format);
@@ -68,9 +74,9 @@ null_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-null_close(void *data)
+null_close(struct audio_output *ao)
 {
-	struct null_data *nd = data;
+	struct null_data *nd = (struct null_data *)ao;
 
 	if (nd->timer != NULL) {
 		timer_free(nd->timer);
@@ -79,10 +85,10 @@ null_close(void *data)
 }
 
 static size_t
-null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size,
+null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
 	  G_GNUC_UNUSED GError **error)
 {
-	struct null_data *nd = data;
+	struct null_data *nd = (struct null_data *)ao;
 	struct timer *timer = nd->timer;
 
 	if (!nd->sync)
@@ -99,9 +105,9 @@ null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size,
 }
 
 static void
-null_cancel(void *data)
+null_cancel(struct audio_output *ao)
 {
-	struct null_data *nd = data;
+	struct null_data *nd = (struct null_data *)ao;
 
 	if (!nd->sync)
 		return;
diff --git a/src/output/openal_output_plugin.c b/src/output/openal_output_plugin.c
index 56afc4463..1473659f0 100644
--- a/src/output/openal_output_plugin.c
+++ b/src/output/openal_output_plugin.c
@@ -39,6 +39,8 @@
 #define NUM_BUFFERS 16
 
 struct openal_data {
+	struct audio_output base;
+
 	const char *device_name;
 	ALCdevice *device;
 	ALCcontext *context;
@@ -126,10 +128,8 @@ openal_unqueue_buffers(struct openal_data *od)
 	}
 }
 
-static void *
-openal_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-	    const struct config_param *param,
-	    G_GNUC_UNUSED GError **error)
+static struct audio_output *
+openal_init(const struct config_param *param, GError **error_r)
 {
 	const char *device_name = config_get_block_string(param, "device", NULL);
 	struct openal_data *od;
@@ -139,24 +139,30 @@ openal_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	}
 
 	od = g_new(struct openal_data, 1);
+	if (!ao_base_init(&od->base, &openal_output_plugin, param, error_r)) {
+		g_free(od);
+		return NULL;
+	}
+
 	od->device_name = device_name;
 
-	return od;
+	return &od->base;
 }
 
 static void
-openal_finish(void *data)
+openal_finish(struct audio_output *ao)
 {
-	struct openal_data *od = data;
+	struct openal_data *od = (struct openal_data *)ao;
 
+	ao_base_finish(&od->base);
 	g_free(od);
 }
 
 static bool
-openal_open(void *data, struct audio_format *audio_format,
+openal_open(struct audio_output *ao, struct audio_format *audio_format,
 	    GError **error)
 {
-	struct openal_data *od = data;
+	struct openal_data *od = (struct openal_data *)ao;
 
 	od->format = openal_audio_format(audio_format);
 
@@ -198,9 +204,9 @@ openal_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-openal_close(void *data)
+openal_close(struct audio_output *ao)
 {
-	struct openal_data *od = data;
+	struct openal_data *od = (struct openal_data *)ao;
 
 	timer_free(od->timer);
 	alcMakeContextCurrent(od->context);
@@ -211,10 +217,10 @@ openal_close(void *data)
 }
 
 static size_t
-openal_play(void *data, const void *chunk, size_t size,
+openal_play(struct audio_output *ao, const void *chunk, size_t size,
 	    G_GNUC_UNUSED GError **error)
 {
-	struct openal_data *od = data;
+	struct openal_data *od = (struct openal_data *)ao;
 	ALuint buffer;
 	ALint num, state;
 
@@ -257,9 +263,9 @@ openal_play(void *data, const void *chunk, size_t size,
 }
 
 static void
-openal_cancel(void *data)
+openal_cancel(struct audio_output *ao)
 {
-	struct openal_data *od = data;
+	struct openal_data *od = (struct openal_data *)ao;
 
 	od->filled = 0;
 	alcMakeContextCurrent(od->context);
diff --git a/src/output/oss_output_plugin.c b/src/output/oss_output_plugin.c
index c9aef52a6..c8fce84ea 100644
--- a/src/output/oss_output_plugin.c
+++ b/src/output/oss_output_plugin.c
@@ -52,6 +52,8 @@
 #endif
 
 struct oss_data {
+	struct audio_output base;
+
 	int fd;
 	const char *device;
 
@@ -143,7 +145,7 @@ oss_output_test_default_device(void)
 	return false;
 }
 
-static void *
+static struct audio_output *
 oss_open_default(GError **error)
 {
 	int i;
@@ -154,8 +156,14 @@ oss_open_default(GError **error)
 		ret[i] = oss_stat_device(default_devices[i], &err[i]);
 		if (ret[i] == OSS_STAT_NO_ERROR) {
 			struct oss_data *od = oss_data_new();
+			if (!ao_base_init(&od->base, &oss_output_plugin, NULL,
+					  error)) {
+				g_free(od);
+				return NULL;
+			}
+
 			od->device = default_devices[i];
-			return od;
+			return &od->base;
 		}
 	}
 
@@ -185,26 +193,31 @@ oss_open_default(GError **error)
 	return NULL;
 }
 
-static void *
-oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		const struct config_param *param,
-		GError **error)
+static struct audio_output *
+oss_output_init(const struct config_param *param, GError **error)
 {
 	const char *device = config_get_block_string(param, "device", NULL);
 	if (device != NULL) {
 		struct oss_data *od = oss_data_new();
+		if (!ao_base_init(&od->base, &oss_output_plugin, param,
+				  error)) {
+			g_free(od);
+			return NULL;
+		}
+
 		od->device = device;
-		return od;
+		return &od->base;
 	}
 
 	return oss_open_default(error);
 }
 
 static void
-oss_output_finish(void *data)
+oss_output_finish(struct audio_output *ao)
 {
-	struct oss_data *od = data;
+	struct oss_data *od = (struct oss_data *)ao;
 
+	ao_base_finish(&od->base);
 	oss_data_free(od);
 }
 
@@ -607,9 +620,10 @@ oss_reopen(struct oss_data *od, GError **error_r)
 }
 
 static bool
-oss_output_open(void *data, struct audio_format *audio_format, GError **error)
+oss_output_open(struct audio_output *ao, struct audio_format *audio_format,
+		GError **error)
 {
-	struct oss_data *od = data;
+	struct oss_data *od = (struct oss_data *)ao;
 
 	od->fd = open_cloexec(od->device, O_WRONLY, 0);
 	if (od->fd < 0) {
@@ -629,17 +643,17 @@ oss_output_open(void *data, struct audio_format *audio_format, GError **error)
 }
 
 static void
-oss_output_close(void *data)
+oss_output_close(struct audio_output *ao)
 {
-	struct oss_data *od = data;
+	struct oss_data *od = (struct oss_data *)ao;
 
 	oss_close(od);
 }
 
 static void
-oss_output_cancel(void *data)
+oss_output_cancel(struct audio_output *ao)
 {
-	struct oss_data *od = data;
+	struct oss_data *od = (struct oss_data *)ao;
 
 	if (od->fd >= 0) {
 		ioctl(od->fd, SNDCTL_DSP_RESET, 0);
@@ -648,9 +662,10 @@ oss_output_cancel(void *data)
 }
 
 static size_t
-oss_output_play(void *data, const void *chunk, size_t size, GError **error)
+oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
+		GError **error)
 {
-	struct oss_data *od = data;
+	struct oss_data *od = (struct oss_data *)ao;
 	ssize_t ret;
 
 	/* reopen the device since it was closed by dropBufferedAudio */
diff --git a/src/output/osx_output_plugin.c b/src/output/osx_output_plugin.c
index f33148c25..f07aedc08 100644
--- a/src/output/osx_output_plugin.c
+++ b/src/output/osx_output_plugin.c
@@ -29,6 +29,8 @@
 #define G_LOG_DOMAIN "osx"
 
 struct osx_output {
+	struct audio_output base;
+
 	/* configuration settings */
 	OSType component_subtype;
 	/* only applicable with kAudioUnitSubType_HALOutput */
@@ -80,12 +82,14 @@ osx_output_configure(struct osx_output *oo, const struct config_param *param)
 	}
 }
 
-static void *
-osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		G_GNUC_UNUSED const struct config_param *param,
-		G_GNUC_UNUSED GError **error)
+static struct audio_output *
+osx_output_init(const struct config_param *param, GError **error_r)
 {
 	struct osx_output *oo = g_new(struct osx_output, 1);
+	if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) {
+		g_free(oo);
+		return NULL;
+	}
 
 	osx_output_configure(oo, param);
 	oo->mutex = g_mutex_new();
@@ -96,12 +100,13 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	oo->buffer = NULL;
 	oo->buffer_size = 0;
 
-	return oo;
+	return &oo->base;
 }
 
-static void osx_output_finish(void *data)
+static void
+osx_output_finish(struct audio_output *ao)
 {
-	struct osx_output *od = data;
+	struct osx_output *od = (struct osx_output *)ao;
 
 	g_free(od->buffer);
 	g_mutex_free(od->mutex);
@@ -109,18 +114,20 @@ static void osx_output_finish(void *data)
 	g_free(od);
 }
 
-static void osx_output_cancel(void *data)
+static void
+osx_output_cancel(struct audio_output *ao)
 {
-	struct osx_output *od = data;
+	struct osx_output *od = (struct osx_output *)ao;
 
 	g_mutex_lock(od->mutex);
 	od->len = 0;
 	g_mutex_unlock(od->mutex);
 }
 
-static void osx_output_close(void *data)
+static void
+osx_output_close(struct audio_output *ao)
 {
-	struct osx_output *od = data;
+	struct osx_output *od = (struct osx_output *)ao;
 
 	AudioOutputUnitStop(od->au);
 	AudioUnitUninitialize(od->au);
@@ -266,9 +273,9 @@ done:
 }
 
 static bool
-osx_output_open(void *data, struct audio_format *audio_format, GError **error)
+osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
 {
-	struct osx_output *od = data;
+	struct osx_output *od = (struct osx_output *)ao;
 	ComponentDescription desc;
 	Component comp;
 	AURenderCallbackStruct callback;
@@ -385,10 +392,10 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
 }
 
 static size_t
-osx_output_play(void *data, const void *chunk, size_t size,
+osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
 		G_GNUC_UNUSED GError **error)
 {
-	struct osx_output *od = data;
+	struct osx_output *od = (struct osx_output *)ao;
 	size_t start, nbytes;
 
 	g_mutex_lock(od->mutex);
diff --git a/src/output/pipe_output_plugin.c b/src/output/pipe_output_plugin.c
index 08f195068..90c5a5331 100644
--- a/src/output/pipe_output_plugin.c
+++ b/src/output/pipe_output_plugin.c
@@ -25,6 +25,8 @@
 #include <errno.h>
 
 struct pipe_output {
+	struct audio_output base;
+
 	char *cmd;
 	FILE *fh;
 };
@@ -38,13 +40,17 @@ pipe_output_quark(void)
 	return g_quark_from_static_string("pipe_output");
 }
 
-static void *
-pipe_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		 const struct config_param *param,
+static struct audio_output *
+pipe_output_init(const struct config_param *param,
 		 GError **error)
 {
 	struct pipe_output *pd = g_new(struct pipe_output, 1);
 
+	if (!ao_base_init(&pd->base, &pipe_output_plugin, param, error)) {
+		g_free(pd);
+		return NULL;
+	}
+
 	pd->cmd = config_dup_block_string(param, "command", NULL);
 	if (pd->cmd == NULL) {
 		g_set_error(error, pipe_output_quark(), 0,
@@ -52,23 +58,25 @@ pipe_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 		return NULL;
 	}
 
-	return pd;
+	return &pd->base;
 }
 
 static void
-pipe_output_finish(void *data)
+pipe_output_finish(struct audio_output *ao)
 {
-	struct pipe_output *pd = data;
+	struct pipe_output *pd = (struct pipe_output *)ao;
 
 	g_free(pd->cmd);
+	ao_base_finish(&pd->base);
 	g_free(pd);
 }
 
 static bool
-pipe_output_open(void *data, G_GNUC_UNUSED struct audio_format *audio_format,
+pipe_output_open(struct audio_output *ao,
+		 G_GNUC_UNUSED struct audio_format *audio_format,
 		 G_GNUC_UNUSED GError **error)
 {
-	struct pipe_output *pd = data;
+	struct pipe_output *pd = (struct pipe_output *)ao;
 
 	pd->fh = popen(pd->cmd, "w");
 	if (pd->fh == NULL) {
@@ -82,17 +90,17 @@ pipe_output_open(void *data, G_GNUC_UNUSED struct audio_format *audio_format,
 }
 
 static void
-pipe_output_close(void *data)
+pipe_output_close(struct audio_output *ao)
 {
-	struct pipe_output *pd = data;
+	struct pipe_output *pd = (struct pipe_output *)ao;
 
 	pclose(pd->fh);
 }
 
 static size_t
-pipe_output_play(void *data, const void *chunk, size_t size, GError **error)
+pipe_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error)
 {
-	struct pipe_output *pd = data;
+	struct pipe_output *pd = (struct pipe_output *)ao;
 	size_t ret;
 
 	ret = fwrite(chunk, 1, size, pd->fh);
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index 22e21d729..0dc9be0e4 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -46,6 +46,8 @@
 #endif
 
 struct pulse_output {
+	struct audio_output base;
+
 	const char *name;
 	const char *server;
 	const char *sink;
@@ -342,16 +344,19 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r)
 	return true;
 }
 
-static void *
-pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		  const struct config_param *param,
-		  G_GNUC_UNUSED GError **error_r)
+static struct audio_output *
+pulse_output_init(const struct config_param *param, GError **error_r)
 {
 	struct pulse_output *po;
 
 	g_setenv("PULSE_PROP_media.role", "music", true);
 
 	po = g_new(struct pulse_output, 1);
+	if (!ao_base_init(&po->base, &pulse_output_plugin, param, error_r)) {
+		g_free(po);
+		return NULL;
+	}
+
 	po->name = config_get_block_string(param, "name", "mpd_pulse");
 	po->server = config_get_block_string(param, "server", NULL);
 	po->sink = config_get_block_string(param, "sink", NULL);
@@ -361,21 +366,22 @@ pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	po->context = NULL;
 	po->stream = NULL;
 
-	return po;
+	return &po->base;
 }
 
 static void
-pulse_output_finish(void *data)
+pulse_output_finish(struct audio_output *ao)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 
+	ao_base_finish(&po->base);
 	g_free(po);
 }
 
 static bool
-pulse_output_enable(void *data, GError **error_r)
+pulse_output_enable(struct audio_output *ao, GError **error_r)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 
 	assert(po->mainloop == NULL);
 	assert(po->context == NULL);
@@ -419,9 +425,9 @@ pulse_output_enable(void *data, GError **error_r)
 }
 
 static void
-pulse_output_disable(void *data)
+pulse_output_disable(struct audio_output *ao)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 
 	assert(po->mainloop != NULL);
 
@@ -573,10 +579,10 @@ pulse_output_setup_stream(struct pulse_output *po, const pa_sample_spec *ss,
 }
 
 static bool
-pulse_output_open(void *data, struct audio_format *audio_format,
+pulse_output_open(struct audio_output *ao, struct audio_format *audio_format,
 		  GError **error_r)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 	pa_sample_spec ss;
 	int error;
 
@@ -647,9 +653,9 @@ pulse_output_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-pulse_output_close(void *data)
+pulse_output_close(struct audio_output *ao)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 	pa_operation *o;
 
 	assert(po->mainloop != NULL);
@@ -796,9 +802,10 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause,
 }
 
 static size_t
-pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
+pulse_output_play(struct audio_output *ao, const void *chunk, size_t size,
+		  GError **error_r)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 	int error;
 
 	assert(po->mainloop != NULL);
@@ -866,9 +873,9 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
 }
 
 static void
-pulse_output_cancel(void *data)
+pulse_output_cancel(struct audio_output *ao)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 	pa_operation *o;
 
 	assert(po->mainloop != NULL);
@@ -898,9 +905,9 @@ pulse_output_cancel(void *data)
 }
 
 static bool
-pulse_output_pause(void *data)
+pulse_output_pause(struct audio_output *ao)
 {
-	struct pulse_output *po = data;
+	struct pulse_output *po = (struct pulse_output *)ao;
 	GError *error = NULL;
 
 	assert(po->mainloop != NULL);
@@ -945,12 +952,12 @@ pulse_output_test_default_device(void)
 	struct pulse_output *po;
 	bool success;
 
-	po = pulse_output_init(NULL, NULL, NULL);
+	po = (struct pulse_output *)pulse_output_init(NULL, NULL);
 	if (po == NULL)
 		return false;
 
 	success = pulse_output_wait_connection(po, NULL);
-	pulse_output_finish(po);
+	pulse_output_finish(&po->base);
 
 	return success;
 }
diff --git a/src/output/raop_output_plugin.c b/src/output/raop_output_plugin.c
index 772112e51..dce7b5beb 100644
--- a/src/output/raop_output_plugin.c
+++ b/src/output/raop_output_plugin.c
@@ -79,6 +79,8 @@ struct encrypt_data {
 /*********************************************************************/
 
 struct raop_data {
+	struct audio_output base;
+
 	struct rtspcl_data *rtspcl;
 	const char *addr; // target host address
 	short rtsp_port;
@@ -209,9 +211,13 @@ raop_session_new(GError **error_r)
 }
 
 static struct raop_data *
-new_raop_data(GError **error_r)
+new_raop_data(const struct config_param *param, GError **error_r)
 {
 	struct raop_data *ret = g_new(struct raop_data, 1);
+	if (!ao_base_init(&ret->base, &raop_output_plugin, param, error_r)) {
+		g_free(ret);
+		return NULL;
+	}
 
 	ret->control_mutex = g_mutex_new();
 
@@ -223,6 +229,7 @@ new_raop_data(GError **error_r)
 	if (raop_session == NULL &&
 	    (raop_session = raop_session_new(error_r)) == NULL) {
 		g_mutex_free(ret->control_mutex);
+		ao_base_finish(&ret->base);
 		g_free(ret);
 		return NULL;
 	}
@@ -721,10 +728,8 @@ send_audio_data(int fd, GError **error_r)
 	return true;
 }
 
-static void *
-raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		 G_GNUC_UNUSED const struct config_param *param,
-		 GError **error_r)
+static struct audio_output *
+raop_output_init(const struct config_param *param, GError **error_r)
 {
 	const char *host = config_get_block_string(param, "host", NULL);
 	if (host == NULL) {
@@ -735,14 +740,14 @@ raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 
 	struct raop_data *rd;
 
-	rd = new_raop_data(error_r);
+	rd = new_raop_data(param, error_r);
 	if (rd == NULL)
 		return NULL;
 
 	rd->addr = host;
 	rd->rtsp_port = config_get_block_unsigned(param, "port", 5000);
 	rd->volume = config_get_block_unsigned(param, "volume", 75);
-	return rd;
+	return &rd->base;
 }
 
 static bool
@@ -755,14 +760,15 @@ raop_set_volume_local(struct raop_data *rd, int volume, GError **error_r)
 
 
 static void
-raop_output_finish(void *data)
+raop_output_finish(struct audio_output *ao)
 {
-	struct raop_data *rd = data;
+	struct raop_data *rd = (struct raop_data *)ao;
 
 	if (rd->rtspcl)
 		rtspcl_close(rd->rtspcl);
 
 	g_mutex_free(rd->control_mutex);
+	ao_base_finish(&rd->base);
 	g_free(rd);
 }
 
@@ -797,11 +803,11 @@ raop_set_volume(struct raop_data *rd, unsigned volume, GError **error_r)
 }
 
 static void
-raop_output_cancel(void *data)
+raop_output_cancel(struct audio_output *ao)
 {
 	//flush
 	struct key_data kd;
-	struct raop_data *rd = (struct raop_data *) data;
+	struct raop_data *rd = (struct raop_data *)ao;
 	int flush_diff = 1;
 
 	rd->started = 0;
@@ -825,9 +831,9 @@ raop_output_cancel(void *data)
 }
 
 static bool
-raop_output_pause(void *data)
+raop_output_pause(struct audio_output *ao)
 {
-	struct raop_data *rd = (struct raop_data *) data;
+	struct raop_data *rd = (struct raop_data *)ao;
 
 	rd->paused = true;
 	return true;
@@ -870,10 +876,10 @@ raop_output_remove(struct raop_data *rd)
 }
 
 static void
-raop_output_close(void *data)
+raop_output_close(struct audio_output *ao)
 {
 	//teardown
-	struct raop_data *rd = data;
+	struct raop_data *rd = (struct raop_data *)ao;
 
 	raop_output_remove(rd);
 
@@ -887,10 +893,10 @@ raop_output_close(void *data)
 
 
 static bool
-raop_output_open(void *data, struct audio_format *audio_format, GError **error_r)
+raop_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error_r)
 {
 	//setup, etc.
-	struct raop_data *rd = data;
+	struct raop_data *rd = (struct raop_data *)ao;
 
 	g_mutex_lock(raop_session->list_mutex);
 	if (raop_session->raop_list == NULL) {
@@ -940,11 +946,11 @@ raop_output_open(void *data, struct audio_format *audio_format, GError **error_r
 }
 
 static size_t
-raop_output_play(void *data, const void *chunk, size_t size,
+raop_output_play(struct audio_output *ao, const void *chunk, size_t size,
 		 GError **error_r)
 {
 	//raopcl_send_sample
-	struct raop_data *rd = data;
+	struct raop_data *rd = (struct raop_data *)ao;
 	size_t rval = 0, orig_size = size;
 
 	rd->paused = false;
diff --git a/src/output/recorder_output_plugin.c b/src/output/recorder_output_plugin.c
index 981f92e44..00adc5d19 100644
--- a/src/output/recorder_output_plugin.c
+++ b/src/output/recorder_output_plugin.c
@@ -35,6 +35,8 @@
 #define G_LOG_DOMAIN "recorder"
 
 struct recorder_output {
+	struct audio_output base;
+
 	/**
 	 * The configured encoder plugin.
 	 */
@@ -65,11 +67,16 @@ recorder_output_quark(void)
 	return g_quark_from_static_string("recorder_output");
 }
 
-static void *
-recorder_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		     const struct config_param *param, GError **error_r)
+static struct audio_output *
+recorder_output_init(const struct config_param *param, GError **error_r)
 {
 	struct recorder_output *recorder = g_new(struct recorder_output, 1);
+	if (!ao_base_init(&recorder->base, &recorder_output_plugin, param,
+			  error_r)) {
+		g_free(recorder);
+		return NULL;
+	}
+
 	const char *encoder_name;
 	const struct encoder_plugin *encoder_plugin;
 
@@ -96,19 +103,21 @@ recorder_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
 	if (recorder->encoder == NULL)
 		goto failure;
 
-	return recorder;
+	return &recorder->base;
 
 failure:
+	ao_base_finish(&recorder->base);
 	g_free(recorder);
 	return NULL;
 }
 
 static void
-recorder_output_finish(void *data)
+recorder_output_finish(struct audio_output *ao)
 {
-	struct recorder_output *recorder = data;
+	struct recorder_output *recorder = (struct recorder_output *)ao;
 
 	encoder_finish(recorder->encoder);
+	ao_base_finish(&recorder->base);
 	g_free(recorder);
 }
 
@@ -155,10 +164,11 @@ recorder_output_encoder_to_file(struct recorder_output *recorder,
 }
 
 static bool
-recorder_output_open(void *data, struct audio_format *audio_format,
+recorder_output_open(struct audio_output *ao,
+		     struct audio_format *audio_format,
 		     GError **error_r)
 {
-	struct recorder_output *recorder = data;
+	struct recorder_output *recorder = (struct recorder_output *)ao;
 	bool success;
 
 	/* create the output file */
@@ -186,9 +196,9 @@ recorder_output_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-recorder_output_close(void *data)
+recorder_output_close(struct audio_output *ao)
 {
-	struct recorder_output *recorder = data;
+	struct recorder_output *recorder = (struct recorder_output *)ao;
 
 	/* flush the encoder and write the rest to the file */
 
@@ -203,10 +213,10 @@ recorder_output_close(void *data)
 }
 
 static size_t
-recorder_output_play(void *data, const void *chunk, size_t size,
+recorder_output_play(struct audio_output *ao, const void *chunk, size_t size,
 		     GError **error_r)
 {
-	struct recorder_output *recorder = data;
+	struct recorder_output *recorder = (struct recorder_output *)ao;
 
 	return encoder_write(recorder->encoder, chunk, size, error_r) &&
 		recorder_output_encoder_to_file(recorder, error_r)
diff --git a/src/output/roar_output_plugin.c b/src/output/roar_output_plugin.c
index c6f3e99f5..d15569fd5 100644
--- a/src/output/roar_output_plugin.c
+++ b/src/output/roar_output_plugin.c
@@ -38,6 +38,8 @@
 
 typedef struct roar
 {
+	struct audio_output base;
+
 	roar_vs_t * vss;
 	int err;
 	char *host;
@@ -114,34 +116,39 @@ roar_configure(struct roar * self, const struct config_param *param)
 		: ROAR_ROLE_MUSIC;
 }
 
-static void *
-roar_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		const struct config_param *param,
-		G_GNUC_UNUSED GError **error)
+static struct audio_output *
+roar_init(const struct config_param *param, GError **error_r)
 {
 	struct roar *self = g_new0(struct roar, 1);
+
+	if (!ao_base_init(&self->base, &roar_output_plugin, param, error_r)) {
+		g_free(self);
+		return NULL;
+	}
+
 	self->lock = g_mutex_new();
 	self->err = ROAR_ERROR_NONE;
 	roar_configure(self, param);
-	return self;
+	return &self->base;
 }
 
 static void
-roar_finish(void *data)
+roar_finish(struct audio_output *ao)
 {
-	roar_t * self = data;
+	struct roar *self = (struct roar *)ao;
 
 	g_free(self->host);
 	g_free(self->name);
 	g_mutex_free(self->lock);
 
+	ao_base_finish(&self->base);
 	g_free(self);
 }
 
 static bool
-roar_open(void *data, struct audio_format *audio_format, GError **error)
+roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
 {
-	roar_t * self = data;
+	struct roar *self = (struct roar *)ao;
 	g_mutex_lock(self->lock);
 
 	if (roar_simple_connect(&(self->con), self->host, self->name) < 0)
@@ -205,9 +212,9 @@ roar_open(void *data, struct audio_format *audio_format, GError **error)
 }
 
 static void
-roar_close(void *data)
+roar_close(struct audio_output *ao)
 {
-	roar_t * self = data;
+	struct roar *self = (struct roar *)ao;
 	g_mutex_lock(self->lock);
 	self->alive = false;
 
@@ -246,9 +253,9 @@ roar_cancel_locked(struct roar *self)
 }
 
 static void
-roar_cancel(void *data)
+roar_cancel(struct audio_output *ao)
 {
-	roar_t * self = data;
+	struct roar *self = (struct roar *)ao;
 
 	g_mutex_lock(self->lock);
 	roar_cancel_locked(self);
@@ -256,9 +263,9 @@ roar_cancel(void *data)
 }
 
 static size_t
-roar_play(void *data, const void *chunk, size_t size, GError **error)
+roar_play(struct audio_output *ao, const void *chunk, size_t size, GError **error)
 {
-	struct roar * self = data;
+	struct roar *self = (struct roar *)ao;
 	ssize_t rc;
 
 	if (self->vss == NULL)
@@ -323,9 +330,9 @@ roar_tag_convert(enum tag_type type, bool *is_uuid)
 }
 
 static void
-roar_send_tag(void *data, const struct tag *meta)
+roar_send_tag(struct audio_output *ao, const struct tag *meta)
 {
-	struct roar * self = data;
+	struct roar *self = (struct roar *)ao;
 
 	if (self->vss == NULL)
 		return;
diff --git a/src/output/shout_output_plugin.c b/src/output/shout_output_plugin.c
index 299f5b24d..35356d659 100644
--- a/src/output/shout_output_plugin.c
+++ b/src/output/shout_output_plugin.c
@@ -42,6 +42,8 @@ struct shout_buffer {
 };
 
 struct shout_data {
+	struct audio_output base;
+
 	shout_t *shout_conn;
 	shout_metadata_t *shout_meta;
 
@@ -108,9 +110,8 @@ static void free_shout_data(struct shout_data *sd)
 		}							\
 	}
 
-static void *
-my_shout_init_driver(const struct audio_format *audio_format,
-		     const struct config_param *param,
+static struct audio_output *
+my_shout_init_driver(const struct config_param *param,
 		     GError **error)
 {
 	struct shout_data *sd;
@@ -129,15 +130,23 @@ my_shout_init_driver(const struct audio_format *audio_format,
 	const struct block_param *block_param;
 	int public;
 
-	if (audio_format == NULL ||
-	    !audio_format_fully_defined(audio_format)) {
+	sd = new_shout_data();
+
+	if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
+		free_shout_data(sd);
+		return NULL;
+	}
+
+	const struct audio_format *audio_format =
+		&sd->base.config_audio_format;
+	if (!audio_format_fully_defined(audio_format)) {
 		g_set_error(error, shout_output_quark(), 0,
 			    "Need full audio format specification");
+		ao_base_finish(&sd->base);
+		free_shout_data(sd);
 		return NULL;
 	}
 
-	sd = new_shout_data();
-
 	if (shout_init_count == 0)
 		shout_init();
 
@@ -307,9 +316,10 @@ my_shout_init_driver(const struct audio_format *audio_format,
 		}
 	}
 
-	return sd;
+	return &sd->base;
 
 failure:
+	ao_base_finish(&sd->base);
 	free_shout_data(sd);
 	return NULL;
 }
@@ -379,12 +389,14 @@ static void close_shout_conn(struct shout_data * sd)
 	}
 }
 
-static void my_shout_finish_driver(void *data)
+static void
+my_shout_finish_driver(struct audio_output *ao)
 {
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 
 	encoder_finish(sd->encoder);
 
+	ao_base_finish(&sd->base);
 	free_shout_data(sd);
 
 	shout_init_count--;
@@ -393,17 +405,19 @@ static void my_shout_finish_driver(void *data)
 		shout_shutdown();
 }
 
-static void my_shout_drop_buffered_audio(void *data)
+static void
+my_shout_drop_buffered_audio(struct audio_output *ao)
 {
 	G_GNUC_UNUSED
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 
 	/* needs to be implemented for shout */
 }
 
-static void my_shout_close_device(void *data)
+static void
+my_shout_close_device(struct audio_output *ao)
 {
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 
 	close_shout_conn(sd);
 }
@@ -430,10 +444,10 @@ shout_connect(struct shout_data *sd, GError **error)
 }
 
 static bool
-my_shout_open_device(void *data, struct audio_format *audio_format,
+my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format,
 		     GError **error)
 {
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 	bool ret;
 
 	ret = shout_connect(sd, error);
@@ -453,9 +467,9 @@ my_shout_open_device(void *data, struct audio_format *audio_format,
 }
 
 static unsigned
-my_shout_delay(void *data)
+my_shout_delay(struct audio_output *ao)
 {
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 
 	int delay = shout_delay(sd->shout_conn);
 	if (delay < 0)
@@ -465,9 +479,10 @@ my_shout_delay(void *data)
 }
 
 static size_t
-my_shout_play(void *data, const void *chunk, size_t size, GError **error)
+my_shout_play(struct audio_output *ao, const void *chunk, size_t size,
+	      GError **error)
 {
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 
 	return encoder_write(sd->encoder, chunk, size, error) &&
 		write_page(sd, error)
@@ -476,11 +491,11 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error)
 }
 
 static bool
-my_shout_pause(void *data)
+my_shout_pause(struct audio_output *ao)
 {
 	static const char silence[1020];
 
-	return my_shout_play(data, silence, sizeof(silence), NULL);
+	return my_shout_play(ao, silence, sizeof(silence), NULL);
 }
 
 static void
@@ -509,10 +524,10 @@ shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size)
 	snprintf(dest, size, "%s - %s", artist, title);
 }
 
-static void my_shout_set_tag(void *data,
+static void my_shout_set_tag(struct audio_output *ao,
 			     const struct tag *tag)
 {
-	struct shout_data *sd = (struct shout_data *)data;
+	struct shout_data *sd = (struct shout_data *)ao;
 	bool ret;
 	GError *error = NULL;
 
diff --git a/src/output/solaris_output_plugin.c b/src/output/solaris_output_plugin.c
index efa7a4e32..796c7806d 100644
--- a/src/output/solaris_output_plugin.c
+++ b/src/output/solaris_output_plugin.c
@@ -36,6 +36,8 @@
 #define G_LOG_DOMAIN "solaris_output"
 
 struct solaris_output {
+	struct audio_output base;
+
 	/* configuration */
 	const char *device;
 
@@ -60,31 +62,35 @@ solaris_output_test_default_device(void)
 		access("/dev/audio", W_OK) == 0;
 }
 
-static void *
-solaris_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		    const struct config_param *param,
-		    G_GNUC_UNUSED GError **error)
+static struct audio_output *
+solaris_output_init(const struct config_param *param, GError **error_r)
 {
 	struct solaris_output *so = g_new(struct solaris_output, 1);
 
+	if (!ao_base_init(&so->base, &solaris_output_plugin, param, error_r)) {
+		g_free(so);
+		return NULL;
+	}
+
 	so->device = config_get_block_string(param, "device", "/dev/audio");
 
-	return so;
+	return &so->base;
 }
 
 static void
-solaris_output_finish(void *data)
+solaris_output_finish(struct audio_output *ao)
 {
-	struct solaris_output *so = data;
+	struct solaris_output *so = (struct solaris_output *)ao;
 
+	ao_base_finish(&so->base);
 	g_free(so);
 }
 
 static bool
-solaris_output_open(void *data, struct audio_format *audio_format,
+solaris_output_open(struct audio_output *ao, struct audio_format *audio_format,
 		    GError **error)
 {
-	struct solaris_output *so = data;
+	struct solaris_output *so = (struct solaris_output *)ao;
 	struct audio_info info;
 	int ret, flags;
 
@@ -135,17 +141,18 @@ solaris_output_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-solaris_output_close(void *data)
+solaris_output_close(struct audio_output *ao)
 {
-	struct solaris_output *so = data;
+	struct solaris_output *so = (struct solaris_output *)ao;
 
 	close(so->fd);
 }
 
 static size_t
-solaris_output_play(void *data, const void *chunk, size_t size, GError **error)
+solaris_output_play(struct audio_output *ao, const void *chunk, size_t size,
+		    GError **error)
 {
-	struct solaris_output *so = data;
+	struct solaris_output *so = (struct solaris_output *)ao;
 	ssize_t nbytes;
 
 	nbytes = write(so->fd, chunk, size);
@@ -159,9 +166,9 @@ solaris_output_play(void *data, const void *chunk, size_t size, GError **error)
 }
 
 static void
-solaris_output_cancel(void *data)
+solaris_output_cancel(struct audio_output *ao)
 {
-	struct solaris_output *so = data;
+	struct solaris_output *so = (struct solaris_output *)ao;
 
 	ioctl(so->fd, I_FLUSH);
 }
diff --git a/src/output/winmm_output_plugin.c b/src/output/winmm_output_plugin.c
index d11a23e6d..66e693dd4 100644
--- a/src/output/winmm_output_plugin.c
+++ b/src/output/winmm_output_plugin.c
@@ -38,6 +38,8 @@ struct winmm_buffer {
 };
 
 struct winmm_output {
+	struct audio_output base;
+
 	UINT device_id;
 	HWAVEOUT handle;
 
@@ -101,30 +103,34 @@ get_device_id(const char *device_name)
 	return WAVE_MAPPER;
 }
 
-static void *
-winmm_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
-		  G_GNUC_UNUSED const struct config_param *param,
-		  G_GNUC_UNUSED GError **error)
+static struct audio_output *
+winmm_output_init(const struct config_param *param, GError **error_r)
 {
 	struct winmm_output *wo = g_new(struct winmm_output, 1);
+	if (!ao_base_init(&wo->base, &winmm_output_plugin, param, error_r)) {
+		g_free(wo);
+		return NULL;
+	}
+
 	const char *device = config_get_block_string(param, "device", NULL);
 	wo->device_id = get_device_id(device);
-	return wo;
+	return &wo->base;
 }
 
 static void
-winmm_output_finish(void *data)
+winmm_output_finish(struct audio_output *ao)
 {
-	struct winmm_output *wo = data;
+	struct winmm_output *wo = (struct winmm_output *)ao;
 
+	ao_base_finish(&wo->base);
 	g_free(wo);
 }
 
 static bool
-winmm_output_open(void *data, struct audio_format *audio_format,
+winmm_output_open(struct audio_output *ao, struct audio_format *audio_format,
 		  GError **error_r)
 {
-	struct winmm_output *wo = data;
+	struct winmm_output *wo = (struct winmm_output *)ao;
 
 	wo->event = CreateEvent(NULL, false, false, NULL);
 	if (wo->event == NULL) {
@@ -180,9 +186,9 @@ winmm_output_open(void *data, struct audio_format *audio_format,
 }
 
 static void
-winmm_output_close(void *data)
+winmm_output_close(struct audio_output *ao)
 {
-	struct winmm_output *wo = data;
+	struct winmm_output *wo = (struct winmm_output *)ao;
 
 	for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i)
 		pcm_buffer_deinit(&wo->buffers[i].buffer);
@@ -253,9 +259,9 @@ winmm_drain_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
 }
 
 static size_t
-winmm_output_play(void *data, const void *chunk, size_t size, GError **error_r)
+winmm_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r)
 {
-	struct winmm_output *wo = data;
+	struct winmm_output *wo = (struct winmm_output *)ao;
 
 	/* get the next buffer from the ring and prepare it */
 	struct winmm_buffer *buffer = &wo->buffers[wo->next_buffer];
@@ -308,18 +314,18 @@ winmm_stop(struct winmm_output *wo)
 }
 
 static void
-winmm_output_drain(void *data)
+winmm_output_drain(struct audio_output *ao)
 {
-	struct winmm_output *wo = data;
+	struct winmm_output *wo = (struct winmm_output *)ao;
 
 	if (!winmm_drain_all_buffers(wo, NULL))
 		winmm_stop(wo);
 }
 
 static void
-winmm_output_cancel(void *data)
+winmm_output_cancel(struct audio_output *ao)
 {
-	struct winmm_output *wo = data;
+	struct winmm_output *wo = (struct winmm_output *)ao;
 
 	winmm_stop(wo);
 }
diff --git a/src/output_all.c b/src/output_all.c
index 7f4694a8b..f56cd04ee 100644
--- a/src/output_all.c
+++ b/src/output_all.c
@@ -41,7 +41,7 @@
 
 static struct audio_format input_audio_format;
 
-static struct audio_output *audio_outputs;
+static struct audio_output **audio_outputs;
 static unsigned int num_audio_outputs;
 
 /**
@@ -70,7 +70,9 @@ audio_output_get(unsigned i)
 {
 	assert(i < num_audio_outputs);
 
-	return &audio_outputs[i];
+	assert(audio_outputs[i] != NULL);
+
+	return audio_outputs[i];
 }
 
 struct audio_output *
@@ -110,11 +112,10 @@ audio_output_all_init(struct player_control *pc)
 	notify_init(&audio_output_client_notify);
 
 	num_audio_outputs = audio_output_config_count();
-	audio_outputs = g_new(struct audio_output, num_audio_outputs);
+	audio_outputs = g_new(struct audio_output *, num_audio_outputs);
 
 	for (i = 0; i < num_audio_outputs; i++)
 	{
-		struct audio_output *output = &audio_outputs[i];
 		unsigned int j;
 
 		param = config_get_next_param(CONF_AUDIO_OUTPUT, param);
@@ -122,7 +123,8 @@ audio_output_all_init(struct player_control *pc)
 		/* only allow param to be NULL if there just one audioOutput */
 		assert(param || (num_audio_outputs == 1));
 
-		if (!audio_output_init(output, param, pc, &error)) {
+		struct audio_output *output = audio_output_new(param, pc, &error);
+		if (output == NULL) {
 			if (param != NULL)
 				MPD_ERROR("line %i: %s",
 					  param->line, error->message);
@@ -130,9 +132,11 @@ audio_output_all_init(struct player_control *pc)
 				MPD_ERROR("%s", error->message);
 		}
 
+		audio_outputs[i] = output;
+
 		/* require output names to be unique: */
 		for (j = 0; j < i; j++) {
-			if (!strcmp(output->name, audio_outputs[j].name)) {
+			if (!strcmp(output->name, audio_outputs[j]->name)) {
 				MPD_ERROR("output devices with identical "
 					  "names: %s\n", output->name);
 			}
@@ -146,8 +150,8 @@ audio_output_all_finish(void)
 	unsigned int i;
 
 	for (i = 0; i < num_audio_outputs; i++) {
-		audio_output_disable(&audio_outputs[i]);
-		audio_output_finish(&audio_outputs[i]);
+		audio_output_disable(audio_outputs[i]);
+		audio_output_finish(audio_outputs[i]);
 	}
 
 	g_free(audio_outputs);
@@ -161,7 +165,7 @@ void
 audio_output_all_enable_disable(void)
 {
 	for (unsigned i = 0; i < num_audio_outputs; i++) {
-		struct audio_output *ao = &audio_outputs[i];
+		struct audio_output *ao = audio_outputs[i];
 		bool enabled;
 
 		g_mutex_lock(ao->mutex);
@@ -185,7 +189,7 @@ static bool
 audio_output_all_finished(void)
 {
 	for (unsigned i = 0; i < num_audio_outputs; ++i) {
-		struct audio_output *ao = &audio_outputs[i];
+		struct audio_output *ao = audio_outputs[i];
 		bool not_finished;
 
 		g_mutex_lock(ao->mutex);
@@ -213,7 +217,7 @@ static void
 audio_output_allow_play_all(void)
 {
 	for (unsigned i = 0; i < num_audio_outputs; ++i)
-		audio_output_allow_play(&audio_outputs[i]);
+		audio_output_allow_play(audio_outputs[i]);
 }
 
 static void
@@ -238,7 +242,7 @@ static void
 audio_output_all_reset_reopen(void)
 {
 	for (unsigned i = 0; i < num_audio_outputs; ++i) {
-		struct audio_output *ao = &audio_outputs[i];
+		struct audio_output *ao = audio_outputs[i];
 
 		audio_output_reset_reopen(ao);
 	}
@@ -259,7 +263,7 @@ audio_output_all_update(void)
 		return false;
 
 	for (i = 0; i < num_audio_outputs; ++i)
-		ret = audio_output_update(&audio_outputs[i],
+		ret = audio_output_update(audio_outputs[i],
 					  &input_audio_format, g_mp) || ret;
 
 	return ret;
@@ -283,7 +287,7 @@ audio_output_all_play(struct music_chunk *chunk)
 	music_pipe_push(g_mp, chunk);
 
 	for (i = 0; i < num_audio_outputs; ++i)
-		audio_output_play(&audio_outputs[i]);
+		audio_output_play(audio_outputs[i]);
 
 	return true;
 }
@@ -322,10 +326,10 @@ audio_output_all_open(const struct audio_format *audio_format,
 	audio_output_all_update();
 
 	for (i = 0; i < num_audio_outputs; ++i) {
-		if (audio_outputs[i].enabled)
+		if (audio_outputs[i]->enabled)
 			enabled = true;
 
-		if (audio_outputs[i].open)
+		if (audio_outputs[i]->open)
 			ret = true;
 	}
 
@@ -369,7 +373,7 @@ static bool
 chunk_is_consumed(const struct music_chunk *chunk)
 {
 	for (unsigned i = 0; i < num_audio_outputs; ++i) {
-		const struct audio_output *ao = &audio_outputs[i];
+		const struct audio_output *ao = audio_outputs[i];
 		bool consumed;
 
 		g_mutex_lock(ao->mutex);
@@ -394,7 +398,7 @@ clear_tail_chunk(G_GNUC_UNUSED const struct music_chunk *chunk, bool *locked)
 	assert(music_pipe_contains(g_mp, chunk));
 
 	for (unsigned i = 0; i < num_audio_outputs; ++i) {
-		struct audio_output *ao = &audio_outputs[i];
+		struct audio_output *ao = audio_outputs[i];
 
 		/* this mutex will be unlocked by the caller when it's
 		   ready */
@@ -451,7 +455,7 @@ audio_output_all_check(void)
 			   by clear_tail_chunk() */
 			for (unsigned i = 0; i < num_audio_outputs; ++i)
 				if (locked[i])
-					g_mutex_unlock(audio_outputs[i].mutex);
+					g_mutex_unlock(audio_outputs[i]->mutex);
 
 		/* return the chunk to the buffer */
 		music_buffer_return(g_music_buffer, shifted);
@@ -484,7 +488,7 @@ audio_output_all_pause(void)
 	audio_output_all_update();
 
 	for (i = 0; i < num_audio_outputs; ++i)
-		audio_output_pause(&audio_outputs[i]);
+		audio_output_pause(audio_outputs[i]);
 
 	audio_output_wait_all();
 }
@@ -493,7 +497,7 @@ void
 audio_output_all_drain(void)
 {
 	for (unsigned i = 0; i < num_audio_outputs; ++i)
-		audio_output_drain_async(&audio_outputs[i]);
+		audio_output_drain_async(audio_outputs[i]);
 
 	audio_output_wait_all();
 }
@@ -506,7 +510,7 @@ audio_output_all_cancel(void)
 	/* send the cancel() command to all audio outputs */
 
 	for (i = 0; i < num_audio_outputs; ++i)
-		audio_output_cancel(&audio_outputs[i]);
+		audio_output_cancel(audio_outputs[i]);
 
 	audio_output_wait_all();
 
@@ -531,7 +535,7 @@ audio_output_all_close(void)
 	unsigned int i;
 
 	for (i = 0; i < num_audio_outputs; ++i)
-		audio_output_close(&audio_outputs[i]);
+		audio_output_close(audio_outputs[i]);
 
 	if (g_mp != NULL) {
 		assert(g_music_buffer != NULL);
@@ -554,7 +558,7 @@ audio_output_all_release(void)
 	unsigned int i;
 
 	for (i = 0; i < num_audio_outputs; ++i)
-		audio_output_release(&audio_outputs[i]);
+		audio_output_release(audio_outputs[i]);
 
 	if (g_mp != NULL) {
 		assert(g_music_buffer != NULL);
diff --git a/src/output_api.h b/src/output_api.h
index 302a0cdc9..dfeef3518 100644
--- a/src/output_api.h
+++ b/src/output_api.h
@@ -21,6 +21,7 @@
 #define MPD_OUTPUT_API_H
 
 #include "output_plugin.h"
+#include "output_internal.h"
 #include "audio_format.h"
 #include "tag.h"
 #include "conf.h"
diff --git a/src/output_control.c b/src/output_control.c
index 7ddcb8b19..69553145a 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -335,5 +335,5 @@ void audio_output_finish(struct audio_output *ao)
 		ao->thread = NULL;
 	}
 
-	audio_output_destruct(ao);
+	audio_output_free(ao);
 }
diff --git a/src/output_control.h b/src/output_control.h
index f58a113e6..874a53518 100644
--- a/src/output_control.h
+++ b/src/output_control.h
@@ -37,11 +37,6 @@ audio_output_quark(void)
 	return g_quark_from_static_string("audio_output");
 }
 
-bool
-audio_output_init(struct audio_output *ao, const struct config_param *param,
-		  struct player_control *pc,
-		  GError **error_r);
-
 /**
  * Enables the device.
  */
diff --git a/src/output_finish.c b/src/output_finish.c
index ac7f7e0c2..e11b43675 100644
--- a/src/output_finish.c
+++ b/src/output_finish.c
@@ -26,7 +26,7 @@
 #include <assert.h>
 
 void
-audio_output_destruct(struct audio_output *ao)
+ao_base_finish(struct audio_output *ao)
 {
 	assert(!ao->open);
 	assert(ao->fail_timer == NULL);
@@ -35,8 +35,6 @@ audio_output_destruct(struct audio_output *ao)
 	if (ao->mixer != NULL)
 		mixer_free(ao->mixer);
 
-	ao_plugin_finish(ao->plugin, ao->data);
-
 	g_cond_free(ao->cond);
 	g_mutex_free(ao->mutex);
 
@@ -50,3 +48,13 @@ audio_output_destruct(struct audio_output *ao)
 
 	pcm_buffer_deinit(&ao->cross_fade_buffer);
 }
+
+void
+audio_output_free(struct audio_output *ao)
+{
+	assert(!ao->open);
+	assert(ao->fail_timer == NULL);
+	assert(ao->thread == NULL);
+
+	ao_plugin_finish(ao);
+}
diff --git a/src/output_init.c b/src/output_init.c
index c52dcc8cd..51fe36c47 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -94,7 +94,8 @@ audio_output_mixer_type(const struct config_param *param)
 }
 
 static struct mixer *
-audio_output_load_mixer(void *ao, const struct config_param *param,
+audio_output_load_mixer(struct audio_output *ao,
+			const struct config_param *param,
 			const struct mixer_plugin *plugin,
 			struct filter *filter_chain,
 			GError **error_r)
@@ -126,33 +127,22 @@ audio_output_load_mixer(void *ao, const struct config_param *param,
 }
 
 bool
-audio_output_init(struct audio_output *ao, const struct config_param *param,
-		  struct player_control *pc,
-		  GError **error_r)
+ao_base_init(struct audio_output *ao,
+	     const struct audio_output_plugin *plugin,
+	     const struct config_param *param, GError **error_r)
 {
 	assert(ao != NULL);
-	assert(pc != NULL);
+	assert(plugin != NULL);
+	assert(plugin->finish != NULL);
+	assert(plugin->open != NULL);
+	assert(plugin->close != NULL);
+	assert(plugin->play != NULL);
 
-	const struct audio_output_plugin *plugin = NULL;
 	GError *error = NULL;
 
 	if (param) {
 		const char *p;
 
-		p = config_get_block_string(param, AUDIO_OUTPUT_TYPE, NULL);
-		if (p == NULL) {
-			g_set_error(error_r, audio_output_quark(), 0,
-				    "Missing \"type\" configuration");
-			return false;
-		}
-
-		plugin = audio_output_plugin_get(p);
-		if (plugin == NULL) {
-			g_set_error(error_r, audio_output_quark(), 0,
-				    "No such audio output plugin: %s", p);
-			return false;
-		}
-
 		ao->name = config_get_block_string(param, AUDIO_OUTPUT_NAME,
 						   NULL);
 		if (ao->name == NULL) {
@@ -172,16 +162,6 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
 		} else
 			audio_format_clear(&ao->config_audio_format);
 	} else {
-		g_warning("No \"%s\" defined in config file\n",
-			  CONF_AUDIO_OUTPUT);
-
-		plugin = audio_output_detect(error_r);
-		if (plugin == NULL)
-			return false;
-
-		g_message("Successfully detected a %s audio device",
-			  plugin->name);
-
 		ao->name = "default detected output";
 
 		audio_format_clear(&ao->config_audio_format);
@@ -203,29 +183,6 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
 	ao->filter = filter_chain_new();
 	assert(ao->filter != NULL);
 
-	/* create the replay_gain filter */
-
-	const char *replay_gain_handler =
-		config_get_block_string(param, "replay_gain_handler",
-					"software");
-
-	if (strcmp(replay_gain_handler, "none") != 0) {
-		ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
-						    param, NULL);
-		assert(ao->replay_gain_filter != NULL);
-
-		ao->replay_gain_serial = 0;
-
-		ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
-							  param, NULL);
-		assert(ao->other_replay_gain_filter != NULL);
-
-		ao->other_replay_gain_serial = 0;
-	} else {
-		ao->replay_gain_filter = NULL;
-		ao->other_replay_gain_filter = NULL;
-	}
-
 	/* create the normalization filter (if configured) */
 
 	if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) {
@@ -254,16 +211,54 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
 	ao->command = AO_COMMAND_NONE;
 	ao->mutex = g_mutex_new();
 	ao->cond = g_cond_new();
-	ao->player_control = pc;
 
-	ao->data = ao_plugin_init(plugin,
-				  &ao->config_audio_format,
-				  param, error_r);
-	if (ao->data == NULL)
-		return false;
+	ao->mixer = NULL;
+
+	/* the "convert" filter must be the last one in the chain */
+
+	ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
+	assert(ao->convert_filter != NULL);
+
+	filter_chain_append(ao->filter, ao->convert_filter);
+
+	/* done */
+
+	return true;
+}
+
+static bool
+audio_output_setup(struct audio_output *ao, const struct config_param *param,
+		   GError **error_r)
+{
+
+	/* create the replay_gain filter */
 
-	ao->mixer = audio_output_load_mixer(ao->data, param,
-					    plugin->mixer_plugin,
+	const char *replay_gain_handler =
+		config_get_block_string(param, "replay_gain_handler",
+					"software");
+
+	if (strcmp(replay_gain_handler, "none") != 0) {
+		ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+						    param, NULL);
+		assert(ao->replay_gain_filter != NULL);
+
+		ao->replay_gain_serial = 0;
+
+		ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+							  param, NULL);
+		assert(ao->other_replay_gain_filter != NULL);
+
+		ao->other_replay_gain_serial = 0;
+	} else {
+		ao->replay_gain_filter = NULL;
+		ao->other_replay_gain_filter = NULL;
+	}
+
+	/* set up the mixer */
+
+	GError *error = NULL;
+	ao->mixer = audio_output_load_mixer(ao, param,
+					    ao->plugin->mixer_plugin,
 					    ao->filter, &error);
 	if (ao->mixer == NULL && error != NULL) {
 		g_warning("Failed to initialize hardware mixer for '%s': %s",
@@ -286,14 +281,53 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
 		return false;
 	}
 
-	/* the "convert" filter must be the last one in the chain */
+	return true;
+}
 
-	ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
-	assert(ao->convert_filter != NULL);
+struct audio_output *
+audio_output_new(const struct config_param *param,
+		 struct player_control *pc,
+		 GError **error_r)
+{
+	const struct audio_output_plugin *plugin;
 
-	filter_chain_append(ao->filter, ao->convert_filter);
+	if (param) {
+		const char *p;
 
-	/* done */
+		p = config_get_block_string(param, AUDIO_OUTPUT_TYPE, NULL);
+		if (p == NULL) {
+			g_set_error(error_r, audio_output_quark(), 0,
+				    "Missing \"type\" configuration");
+			return false;
+		}
 
-	return true;
+		plugin = audio_output_plugin_get(p);
+		if (plugin == NULL) {
+			g_set_error(error_r, audio_output_quark(), 0,
+				    "No such audio output plugin: %s", p);
+			return false;
+		}
+	} else {
+		g_warning("No \"%s\" defined in config file\n",
+			  CONF_AUDIO_OUTPUT);
+
+		plugin = audio_output_detect(error_r);
+		if (plugin == NULL)
+			return false;
+
+		g_message("Successfully detected a %s audio device",
+			  plugin->name);
+	}
+
+	struct audio_output *ao = ao_plugin_init(plugin, param, error_r);
+	if (ao == NULL)
+		return NULL;
+
+	if (!audio_output_setup(ao, param, error_r)) {
+		ao_plugin_finish(ao);
+		return NULL;
+	}
+
+	ao->player_control = pc;
+	return ao;
 }
diff --git a/src/output_internal.h b/src/output_internal.h
index eba3aed91..9d975d789 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -27,6 +27,8 @@
 
 #include <time.h>
 
+struct config_param;
+
 enum audio_output_command {
 	AO_COMMAND_NONE = 0,
 	AO_COMMAND_ENABLE,
@@ -63,12 +65,6 @@ struct audio_output {
 	 */
 	const struct audio_output_plugin *plugin;
 
-	/**
-	 * The plugin's internal data.  It is passed to every plugin
-	 * method.
-	 */
-	void *data;
-
 	/**
 	 * The #mixer object associated with this audio output device.
 	 * May be NULL if none is available, or if software volume is
@@ -254,7 +250,20 @@ audio_output_command_is_finished(const struct audio_output *ao)
 	return ao->command == AO_COMMAND_NONE;
 }
 
+struct audio_output *
+audio_output_new(const struct config_param *param,
+		 struct player_control *pc,
+		 GError **error_r);
+
+bool
+ao_base_init(struct audio_output *ao,
+	     const struct audio_output_plugin *plugin,
+	     const struct config_param *param, GError **error_r);
+
+void
+ao_base_finish(struct audio_output *ao);
+
 void
-audio_output_destruct(struct audio_output *ao);
+audio_output_free(struct audio_output *ao);
 
 #endif
diff --git a/src/output_plugin.c b/src/output_plugin.c
new file mode 100644
index 000000000..1e41f25f8
--- /dev/null
+++ b/src/output_plugin.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2003-2011 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 "output_plugin.h"
+#include "output_internal.h"
+
+struct audio_output *
+ao_plugin_init(const struct audio_output_plugin *plugin,
+	       const struct config_param *param,
+	       GError **error)
+{
+	assert(plugin != NULL);
+	assert(plugin->init != NULL);
+
+	return plugin->init(param, error);
+}
+
+void
+ao_plugin_finish(struct audio_output *ao)
+{
+	ao->plugin->finish(ao);
+}
+
+bool
+ao_plugin_enable(struct audio_output *ao, GError **error_r)
+{
+	return ao->plugin->enable != NULL
+		? ao->plugin->enable(ao, error_r)
+		: true;
+}
+
+void
+ao_plugin_disable(struct audio_output *ao)
+{
+	if (ao->plugin->disable != NULL)
+		ao->plugin->disable(ao);
+}
+
+bool
+ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format,
+	       GError **error)
+{
+	return ao->plugin->open(ao, audio_format, error);
+}
+
+void
+ao_plugin_close(struct audio_output *ao)
+{
+	ao->plugin->close(ao);
+}
+
+unsigned
+ao_plugin_delay(struct audio_output *ao)
+{
+	return ao->plugin->delay != NULL
+		? ao->plugin->delay(ao)
+		: 0;
+}
+
+void
+ao_plugin_send_tag(struct audio_output *ao, const struct tag *tag)
+{
+	if (ao->plugin->send_tag != NULL)
+		ao->plugin->send_tag(ao, tag);
+}
+
+size_t
+ao_plugin_play(struct audio_output *ao, const void *chunk, size_t size,
+	       GError **error)
+{
+	return ao->plugin->play(ao, chunk, size, error);
+}
+
+void
+ao_plugin_drain(struct audio_output *ao)
+{
+	if (ao->plugin->drain != NULL)
+		ao->plugin->drain(ao);
+}
+
+void
+ao_plugin_cancel(struct audio_output *ao)
+{
+	if (ao->plugin->cancel != NULL)
+		ao->plugin->cancel(ao);
+}
+
+bool
+ao_plugin_pause(struct audio_output *ao)
+{
+	return ao->plugin->pause != NULL && ao->plugin->pause(ao);
+}
diff --git a/src/output_plugin.h b/src/output_plugin.h
index 72b519d13..209ca6221 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -48,8 +48,6 @@ struct audio_output_plugin {
 	 * Configure and initialize the device, but do not open it
 	 * yet.
 	 *
-	 * @param audio_format the configured audio format, or NULL if
-	 * none is configured
 	 * @param param the configuration section, or NULL if there is
 	 * no configuration
 	 * @param error location to store the error occurring, or NULL
@@ -57,14 +55,13 @@ struct audio_output_plugin {
 	 * @return NULL on error, or an opaque pointer to the plugin's
 	 * data
 	 */
-	void *(*init)(const struct audio_format *audio_format,
-		      const struct config_param *param,
-		      GError **error);
+	struct audio_output *(*init)(const struct config_param *param,
+				     GError **error);
 
 	/**
 	 * Free resources allocated by this device.
 	 */
-	void (*finish)(void *data);
+	void (*finish)(struct audio_output *data);
 
 	/**
 	 * Enable the device.  This may allocate resources, preparing
@@ -76,13 +73,13 @@ struct audio_output_plugin {
 	 * NULL to ignore errors
 	 * @return true on success, false on error
 	 */
-	bool (*enable)(void *data, GError **error_r);
+	bool (*enable)(struct audio_output *data, GError **error_r);
 
 	/**
 	 * Disables the device.  It is closed before this method is
 	 * called.
 	 */
-	void (*disable)(void *data);
+	void (*disable)(struct audio_output *data);
 
 	/**
 	 * Really open the device.
@@ -92,13 +89,13 @@ struct audio_output_plugin {
 	 * @param error location to store the error occurring, or NULL
 	 * to ignore errors
 	 */
-	bool (*open)(void *data, struct audio_format *audio_format,
+	bool (*open)(struct audio_output *data, struct audio_format *audio_format,
 		     GError **error);
 
 	/**
 	 * Close the device.
 	 */
-	void (*close)(void *data);
+	void (*close)(struct audio_output *data);
 
 	/**
 	 * Returns a positive number if the output thread shall delay
@@ -108,13 +105,13 @@ struct audio_output_plugin {
 	 *
 	 * @return the number of milliseconds to wait
 	 */
-	unsigned (*delay)(void *data);
+	unsigned (*delay)(struct audio_output *data);
 
 	/**
 	 * Display metadata for the next chunk.  Optional method,
 	 * because not all devices can display metadata.
 	 */
-	void (*send_tag)(void *data, const struct tag *tag);
+	void (*send_tag)(struct audio_output *data, const struct tag *tag);
 
 	/**
 	 * Play a chunk of audio data.
@@ -123,19 +120,20 @@ struct audio_output_plugin {
 	 * to ignore errors
 	 * @return the number of bytes played, or 0 on error
 	 */
-	size_t (*play)(void *data, const void *chunk, size_t size,
+	size_t (*play)(struct audio_output *data,
+		       const void *chunk, size_t size,
 		       GError **error);
 
 	/**
 	 * Wait until the device has finished playing.
 	 */
-	void (*drain)(void *data);
+	void (*drain)(struct audio_output *data);
 
 	/**
 	 * Try to cancel data which may still be in the device's
 	 * buffers.
 	 */
-	void (*cancel)(void *data);
+	void (*cancel)(struct audio_output *data);
 
 	/**
 	 * Pause the device.  If supported, it may perform a special
@@ -148,7 +146,7 @@ struct audio_output_plugin {
 	 * @return false on error (output will be closed then), true
 	 * for continue to pause
 	 */
-	bool (*pause)(void *data);
+	bool (*pause)(struct audio_output *data);
 
 	/**
 	 * The mixer plugin associated with this output plugin.  This
@@ -167,95 +165,46 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin)
 		: false;
 }
 
-static inline void *
+G_GNUC_MALLOC
+struct audio_output *
 ao_plugin_init(const struct audio_output_plugin *plugin,
-	       const struct audio_format *audio_format,
 	       const struct config_param *param,
-	       GError **error)
-{
-	return plugin->init(audio_format, param, error);
-}
+	       GError **error);
 
-static inline void
-ao_plugin_finish(const struct audio_output_plugin *plugin, void *data)
-{
-	plugin->finish(data);
-}
+void
+ao_plugin_finish(struct audio_output *ao);
 
-static inline bool
-ao_plugin_enable(const struct audio_output_plugin *plugin, void *data,
-		 GError **error_r)
-{
-	return plugin->enable != NULL
-		? plugin->enable(data, error_r)
-		: true;
-}
+bool
+ao_plugin_enable(struct audio_output *ao, GError **error_r);
 
-static inline void
-ao_plugin_disable(const struct audio_output_plugin *plugin, void *data)
-{
-	if (plugin->disable != NULL)
-		plugin->disable(data);
-}
+void
+ao_plugin_disable(struct audio_output *ao);
 
-static inline bool
-ao_plugin_open(const struct audio_output_plugin *plugin,
-	       void *data, struct audio_format *audio_format,
-	       GError **error)
-{
-	return plugin->open(data, audio_format, error);
-}
+bool
+ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format,
+	       GError **error);
 
-static inline void
-ao_plugin_close(const struct audio_output_plugin *plugin, void *data)
-{
-	plugin->close(data);
-}
+void
+ao_plugin_close(struct audio_output *ao);
 
-static inline unsigned
-ao_plugin_delay(const struct audio_output_plugin *plugin, void *data)
-{
-	return plugin->delay != NULL
-		? plugin->delay(data)
-		: 0;
-}
+G_GNUC_PURE
+unsigned
+ao_plugin_delay(struct audio_output *ao);
 
-static inline void
-ao_plugin_send_tag(const struct audio_output_plugin *plugin,
-		   void *data, const struct tag *tag)
-{
-	if (plugin->send_tag != NULL)
-		plugin->send_tag(data, tag);
-}
+void
+ao_plugin_send_tag(struct audio_output *ao, const struct tag *tag);
 
-static inline size_t
-ao_plugin_play(const struct audio_output_plugin *plugin,
-	       void *data, const void *chunk, size_t size,
-	       GError **error)
-{
-	return plugin->play(data, chunk, size, error);
-}
+size_t
+ao_plugin_play(struct audio_output *ao, const void *chunk, size_t size,
+	       GError **error);
 
-static inline void
-ao_plugin_drain(const struct audio_output_plugin *plugin, void *data)
-{
-	if (plugin->drain != NULL)
-		plugin->drain(data);
-}
+void
+ao_plugin_drain(struct audio_output *ao);
 
-static inline void
-ao_plugin_cancel(const struct audio_output_plugin *plugin, void *data)
-{
-	if (plugin->cancel != NULL)
-		plugin->cancel(data);
-}
+void
+ao_plugin_cancel(struct audio_output *ao);
 
-static inline bool
-ao_plugin_pause(const struct audio_output_plugin *plugin, void *data)
-{
-	return plugin->pause != NULL
-		? plugin->pause(data)
-		: false;
-}
+bool
+ao_plugin_pause(struct audio_output *ao);
 
 #endif
diff --git a/src/output_thread.c b/src/output_thread.c
index c36ba5f4f..e194edc92 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -60,7 +60,7 @@ ao_enable(struct audio_output *ao)
 		return true;
 
 	g_mutex_unlock(ao->mutex);
-	success = ao_plugin_enable(ao->plugin, ao->data, &error);
+	success = ao_plugin_enable(ao, &error);
 	g_mutex_lock(ao->mutex);
 	if (!success) {
 		g_warning("Failed to enable \"%s\" [%s]: %s\n",
@@ -86,7 +86,7 @@ ao_disable(struct audio_output *ao)
 		ao->really_enabled = false;
 
 		g_mutex_unlock(ao->mutex);
-		ao_plugin_disable(ao->plugin, ao->data);
+		ao_plugin_disable(ao);
 		g_mutex_lock(ao->mutex);
 	}
 }
@@ -175,9 +175,7 @@ ao_open(struct audio_output *ao)
 				&ao->config_audio_format);
 
 	g_mutex_unlock(ao->mutex);
-	success = ao_plugin_open(ao->plugin, ao->data,
-				 &ao->out_audio_format,
-				 &error);
+	success = ao_plugin_open(ao, &ao->out_audio_format, &error);
 	g_mutex_lock(ao->mutex);
 
 	assert(!ao->open);
@@ -221,11 +219,11 @@ ao_close(struct audio_output *ao, bool drain)
 	g_mutex_unlock(ao->mutex);
 
 	if (drain)
-		ao_plugin_drain(ao->plugin, ao->data);
+		ao_plugin_drain(ao);
 	else
-		ao_plugin_cancel(ao->plugin, ao->data);
+		ao_plugin_cancel(ao);
 
-	ao_plugin_close(ao->plugin, ao->data);
+	ao_plugin_close(ao);
 	ao_filter_close(ao);
 
 	g_mutex_lock(ao->mutex);
@@ -257,7 +255,7 @@ ao_reopen_filter(struct audio_output *ao)
 		ao->fail_timer = g_timer_new();
 
 		g_mutex_unlock(ao->mutex);
-		ao_plugin_close(ao->plugin, ao->data);
+		ao_plugin_close(ao);
 		g_mutex_lock(ao->mutex);
 
 		return;
@@ -302,7 +300,7 @@ static bool
 ao_wait(struct audio_output *ao)
 {
 	while (true) {
-		unsigned delay = ao_plugin_delay(ao->plugin, ao->data);
+		unsigned delay = ao_plugin_delay(ao);
 		if (delay == 0)
 			return true;
 
@@ -434,7 +432,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
 
 	if (chunk->tag != NULL) {
 		g_mutex_unlock(ao->mutex);
-		ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag);
+		ao_plugin_send_tag(ao, chunk->tag);
 		g_mutex_lock(ao->mutex);
 	}
 
@@ -456,8 +454,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
 			break;
 
 		g_mutex_unlock(ao->mutex);
-		nbytes = ao_plugin_play(ao->plugin, ao->data, data, size,
-					&error);
+		nbytes = ao_plugin_play(ao, data, size, &error);
 		g_mutex_lock(ao->mutex);
 		if (nbytes == 0) {
 			/* play()==0 means failure */
@@ -547,7 +544,7 @@ static void ao_pause(struct audio_output *ao)
 	bool ret;
 
 	g_mutex_unlock(ao->mutex);
-	ao_plugin_cancel(ao->plugin, ao->data);
+	ao_plugin_cancel(ao);
 	g_mutex_lock(ao->mutex);
 
 	ao->pause = true;
@@ -558,7 +555,7 @@ static void ao_pause(struct audio_output *ao)
 			break;
 
 		g_mutex_unlock(ao->mutex);
-		ret = ao_plugin_pause(ao->plugin, ao->data);
+		ret = ao_plugin_pause(ao);
 		g_mutex_lock(ao->mutex);
 
 		if (!ret) {
@@ -632,7 +629,7 @@ static gpointer audio_output_task(gpointer arg)
 				assert(music_pipe_peek(ao->pipe) == NULL);
 
 				g_mutex_unlock(ao->mutex);
-				ao_plugin_drain(ao->plugin, ao->data);
+				ao_plugin_drain(ao);
 				g_mutex_lock(ao->mutex);
 			}
 
@@ -644,7 +641,7 @@ static gpointer audio_output_task(gpointer arg)
 
 			if (ao->open) {
 				g_mutex_unlock(ao->mutex);
-				ao_plugin_cancel(ao->plugin, ao->data);
+				ao_plugin_cancel(ao);
 				g_mutex_lock(ao->mutex);
 			}
 
diff --git a/test/run_output.c b/test/run_output.c
index c1d7a8120..5e688f2c7 100644
--- a/test/run_output.c
+++ b/test/run_output.c
@@ -94,11 +94,10 @@ find_named_config_block(const char *block, const char *name)
 	return NULL;
 }
 
-static bool
-load_audio_output(struct audio_output *ao, const char *name)
+static struct audio_output *
+load_audio_output(const char *name)
 {
 	const struct config_param *param;
-	bool success;
 	GError *error = NULL;
 
 	param = find_named_config_block(CONF_AUDIO_OUTPUT, name);
@@ -109,13 +108,14 @@ load_audio_output(struct audio_output *ao, const char *name)
 
 	static struct player_control dummy_player_control;
 
-	success = audio_output_init(ao, param, &dummy_player_control, &error);
-	if (!success) {
+	struct audio_output *ao =
+		audio_output_new(param, &dummy_player_control, &error);
+	if (ao == NULL) {
 		g_printerr("%s\n", error->message);
 		g_error_free(error);
 	}
 
-	return success;
+	return ao;
 }
 
 static bool
@@ -124,7 +124,7 @@ run_output(struct audio_output *ao, struct audio_format *audio_format)
 	/* open the audio output */
 
 	GError *error = NULL;
-	if (!ao_plugin_open(ao->plugin, ao->data, audio_format, &error)) {
+	if (!ao_plugin_open(ao, audio_format, &error)) {
 		g_printerr("Failed to open audio output: %s\n",
 			   error->message);
 		g_error_free(error);
@@ -153,11 +153,11 @@ run_output(struct audio_output *ao, struct audio_format *audio_format)
 
 		size_t play_length = (length / frame_size) * frame_size;
 		if (play_length > 0) {
-			size_t consumed = ao_plugin_play(ao->plugin, ao->data,
+			size_t consumed = ao_plugin_play(ao,
 							 buffer, play_length,
 							 &error);
 			if (consumed == 0) {
-				ao_plugin_close(ao->plugin, ao->data);
+				ao_plugin_close(ao);
 				g_printerr("Failed to play: %s\n",
 					   error->message);
 				g_error_free(error);
@@ -172,13 +172,12 @@ run_output(struct audio_output *ao, struct audio_format *audio_format)
 		}
 	}
 
-	ao_plugin_close(ao->plugin, ao->data);
+	ao_plugin_close(ao);
 	return true;
 }
 
 int main(int argc, char **argv)
 {
-	struct audio_output ao;
 	struct audio_format audio_format;
 	bool success;
 	GError *error = NULL;
@@ -211,7 +210,8 @@ int main(int argc, char **argv)
 
 	/* initialize the audio output */
 
-	if (!load_audio_output(&ao, argv[2]))
+	struct audio_output *ao = load_audio_output(argv[2]);
+	if (ao == NULL)
 		return 1;
 
 	/* parse the audio format */
@@ -229,11 +229,11 @@ int main(int argc, char **argv)
 
 	/* do it */
 
-	success = run_output(&ao, &audio_format);
+	success = run_output(ao, &audio_format);
 
 	/* cleanup and exit */
 
-	audio_output_destruct(&ao);
+	audio_output_free(ao);
 
 	io_thread_deinit();
 
-- 
cgit v1.2.3