Bug 837564 - Implement switchable backends for cubeb. r=padenot
authorMatthew Gregan <kinetik@flim.org>
Fri, 08 Mar 2013 17:51:59 +1300
changeset 124341 463a06b91cbf99b76e58353e9bc4deae4afc11f8
parent 124340 5f92acf341046fc0713fe333793ad3894dbdd701
child 124342 9cad83c03950a81d4b0ea600526d4e760b583607
push id24417
push userryanvm@gmail.com
push dateMon, 11 Mar 2013 23:58:07 +0000
treeherdermozilla-central@7433bc4545c9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs837564
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 837564 - Implement switchable backends for cubeb. r=padenot
media/libcubeb/README_MOZILLA
media/libcubeb/include/cubeb.h
media/libcubeb/src/Makefile.in
media/libcubeb/src/cubeb-internal.h
media/libcubeb/src/cubeb.c
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiounit.c
media/libcubeb/src/cubeb_opensl.c
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_sndio.c
media/libcubeb/src/cubeb_winmm.c
media/libcubeb/update.sh
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 2ab9cc1bd458d5bc3d3ffb3afc9173d276c20c4b.
+The git commit ID used was b56cf57ceb0bf4c9f675fe977b457b54d1a8e08b.
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -114,19 +114,20 @@ typedef enum {
   CUBEB_STATE_STARTED, /**< Stream started. */
   CUBEB_STATE_STOPPED, /**< Stream stopped. */
   CUBEB_STATE_DRAINED, /**< Stream drained. */
   CUBEB_STATE_ERROR    /**< Stream disabled due to error. */
 } cubeb_state;
 
 /** Result code enumeration. */
 enum {
-  CUBEB_OK = 0,                   /**< Success. */
-  CUBEB_ERROR = -1,               /**< Unclassified error. */
-  CUBEB_ERROR_INVALID_FORMAT = -2 /**< Unsupported #cubeb_stream_params requested. */
+  CUBEB_OK = 0,                       /**< Success. */
+  CUBEB_ERROR = -1,                   /**< Unclassified error. */
+  CUBEB_ERROR_INVALID_FORMAT = -2,    /**< Unsupported #cubeb_stream_params requested. */
+  CUBEB_ERROR_INVALID_PARAMETER = -3  /**< Invalid parameter specified. */
 };
 
 /** User supplied data callback.
     @param stream
     @param user_ptr
     @param buffer
     @param nframes
     @retval Number of frames written to buffer, which must equal nframes except at end of stream.
--- a/media/libcubeb/src/Makefile.in
+++ b/media/libcubeb/src/Makefile.in
@@ -11,53 +11,63 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE          = cubeb
 LIBRARY_NAME    = cubeb
 FORCE_STATIC_LIB= 1
 ifeq ($(OS_TARGET),WINNT)
 VISIBILITY_FLAGS =
 endif
 
+CSRCS           = \
+                cubeb.c \
+                $(NULL)
+
 ifeq ($(OS_TARGET),WINNT)
-CSRCS           = \
+CSRCS           += \
                 cubeb_winmm.c \
                 $(NULL)
+DEFINES         += -DUSE_WINMM
 endif
 
 ifeq ($(OS_TARGET),Android)
 ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
-CSRCS         = \
-              cubeb_opensl.c \
-              $(NULL)
+CSRCS           += \
+                cubeb_opensl.c \
+                $(NULL)
+DEFINES         += -DUSE_OPENSL
 endif
 # No Android implementation of libcubeb yet.
 endif
 
 ifeq ($(OS_TARGET),Darwin)
-CSRCS           = \
+CSRCS           += \
                 cubeb_audiounit.c \
                 $(NULL)
+DEFINES         += -DUSE_AUDIOUNIT
 endif
 
 ifeq ($(OS_ARCH),OpenBSD)
-CSRCS           = \
+CSRCS           += \
                 cubeb_sndio.c \
                 $(NULL)
+DEFINES         += -DUSE_SNDIO
 endif
 
 ifdef MOZ_ALSA
-CSRCS         = \
-              cubeb_alsa.c \
-              $(NULL)
+CSRCS           += \
+                cubeb_alsa.c \
+                $(NULL)
+DEFINES         += -DUSE_ALSA
 endif
 
 ifdef MOZ_PULSEAUDIO
-CSRCS		= \
-		cubeb_pulse.c \
-		$(NULL)
+CSRCS           += \
+                cubeb_pulse.c \
+                $(NULL)
+DEFINES         += -DUSE_PULSE
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 CFLAGS += \
-	$(MOZ_ALSA_CFLAGS) \
-	$(MOZ_PULSEAUDIO_CFLAGS) \
-	$(NULL)
+        $(MOZ_ALSA_CFLAGS) \
+        $(MOZ_PULSEAUDIO_CFLAGS) \
+        $(NULL)
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2013 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#ifndef   CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
+#define   CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
+
+#include "cubeb/cubeb.h"
+
+struct cubeb_ops {
+  int (* init)(cubeb ** context, char const * context_name);
+  char const * (* get_backend_id)(cubeb * context);
+  void (* destroy)(cubeb * context);
+  int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                      cubeb_stream_params stream_params, unsigned int latency,
+                      cubeb_data_callback data_callback,
+                      cubeb_state_callback state_callback,
+                      void * user_ptr);
+  void (* stream_destroy)(cubeb_stream * stream);
+  int (* stream_start)(cubeb_stream * stream);
+  int (* stream_stop)(cubeb_stream * stream);
+  int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
+};
+
+#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright © 2013 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#include <stddef.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
+
+struct cubeb {
+  struct cubeb_ops * ops;
+};
+
+struct cubeb_stream {
+  struct cubeb * context;
+};
+
+#ifdef USE_PULSE
+int pulse_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_ALSA
+int alsa_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_AUDIOQUEUE
+int audioqueue_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_AUDIOUNIT
+int audiounit_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_DIRECTSOUND
+int directsound_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_WINMM
+int winmm_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_SNDIO
+int sndio_init(cubeb ** context, char const * context_name);
+#endif
+#ifdef USE_OPENSL
+int opensl_init(cubeb ** context, char const * context_name);
+#endif
+
+int
+validate_stream_params(cubeb_stream_params stream_params)
+{
+  if (stream_params.rate < 1 || stream_params.rate > 192000 ||
+      stream_params.channels < 1 || stream_params.channels > 8) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  switch (stream_params.format) {
+  case CUBEB_SAMPLE_S16LE:
+  case CUBEB_SAMPLE_S16BE:
+  case CUBEB_SAMPLE_FLOAT32LE:
+  case CUBEB_SAMPLE_FLOAT32BE:
+    return CUBEB_OK;
+  }
+
+  return CUBEB_ERROR_INVALID_FORMAT;
+}
+
+int
+validate_latency(int latency)
+{
+  if (latency < 1 || latency > 2000) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+  return CUBEB_OK;
+}
+
+int
+cubeb_init(cubeb ** context, char const * context_name)
+{
+  int (* init[])(cubeb **, char const *) = {
+#ifdef USE_PULSE
+    pulse_init,
+#endif
+#ifdef USE_ALSA
+    alsa_init,
+#endif
+#ifdef USE_AUDIOUNIT
+    audiounit_init,
+#endif
+#ifdef USE_AUDIOQUEUE
+    audioqueue_init,
+#endif
+#ifdef USE_WINMM
+    winmm_init,
+#endif
+#ifdef USE_DIRECTSOUND
+    directsound_init,
+#endif
+#ifdef USE_SNDIO
+    sndio_init,
+#endif
+#ifdef USE_OPENSL
+    opensl_init,
+#endif
+  };
+  int i;
+
+  if (!context) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  for (i = 0; i < NELEMS(init); ++i) {
+    if (init[i](context, context_name) == CUBEB_OK) {
+      return CUBEB_OK;
+    }
+  }
+
+  return CUBEB_ERROR;
+}
+
+char const *
+cubeb_get_backend_id(cubeb * context)
+{
+  if (!context) {
+    return NULL;
+  }
+
+  return context->ops->get_backend_id(context);
+}
+
+void
+cubeb_destroy(cubeb * context)
+{
+  if (!context) {
+    return;
+  }
+
+  context->ops->destroy(context);
+}
+
+int
+cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                  cubeb_stream_params stream_params, unsigned int latency,
+                  cubeb_data_callback data_callback,
+                  cubeb_state_callback state_callback,
+                  void * user_ptr)
+{
+  int r;
+
+  if (!context || !stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if ((r = validate_stream_params(stream_params)) != CUBEB_OK ||
+      (r = validate_latency(latency)) != CUBEB_OK) {
+    return r;
+  }
+
+  return context->ops->stream_init(context, stream, stream_name,
+                                   stream_params, latency,
+                                   data_callback,
+                                   state_callback,
+                                   user_ptr);
+}
+
+void
+cubeb_stream_destroy(cubeb_stream * stream)
+{
+  if (!stream) {
+    return;
+  }
+
+  stream->context->ops->stream_destroy(stream);
+}
+
+int
+cubeb_stream_start(cubeb_stream * stream)
+{
+  if (!stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return stream->context->ops->stream_start(stream);
+}
+
+int
+cubeb_stream_stop(cubeb_stream * stream)
+{
+  if (!stream) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return stream->context->ops->stream_stop(stream);
+}
+
+int
+cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
+{
+  if (!stream || !position) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return stream->context->ops->stream_get_position(stream, position);
+}
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -10,41 +10,46 @@
 #include <pthread.h>
 #include <sys/time.h>
 #include <assert.h>
 #include <limits.h>
 #include <poll.h>
 #include <unistd.h>
 #include <alsa/asoundlib.h>
 #include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
 
 #define CUBEB_STREAM_MAX 16
 #define CUBEB_WATCHDOG_MS 10000
 
 #define CUBEB_ALSA_PCM_NAME "default"
 
 #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
 
 /* ALSA is not thread-safe.  snd_pcm_t instances are individually protected
    by the owning cubeb_stream's mutex.  snd_pcm_t creation and destruction
    is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
    so those calls must be wrapped in the following mutex. */
 static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
 static int cubeb_alsa_error_handler_set = 0;
 
+static struct cubeb_ops const alsa_ops;
+
 struct cubeb {
+  struct cubeb_ops const * ops;
+
   pthread_t thread;
 
   /* Mutex for streams array, must not be held while blocked in poll(2). */
   pthread_mutex_t mutex;
 
   /* Sparse array of streams managed by this context. */
   cubeb_stream * streams[CUBEB_STREAM_MAX];
 
-  /* fds and nfds are only updated by cubeb_run when rebuild is set. */
+  /* fds and nfds are only updated by alsa_run when rebuild is set. */
   struct pollfd * fds;
   nfds_t nfds;
   int rebuild;
 
   int shutdown;
 
   /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */
   int control_fd_read;
@@ -222,31 +227,31 @@ static void
 set_timeout(struct timeval * timeout, unsigned int ms)
 {
   gettimeofday(timeout, NULL);
   timeout->tv_sec += ms / 1000;
   timeout->tv_usec += (ms % 1000) * 1000;
 }
 
 static void
-cubeb_set_stream_state(cubeb_stream * stm, enum stream_state state)
+alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
 {
   cubeb * ctx;
   int r;
 
   ctx = stm->context;
   stm->state = state;
   r = pthread_cond_broadcast(&stm->cond);
   assert(r == 0);
   ctx->rebuild = 1;
   poll_wake(ctx);
 }
 
 static enum stream_state
-cubeb_refill_stream(cubeb_stream * stm)
+alsa_refill_stream(cubeb_stream * stm)
 {
   int r;
   unsigned short revents;
   snd_pcm_sframes_t avail;
   long got;
   void * p;
   int draining;
 
@@ -329,17 +334,17 @@ cubeb_refill_stream(cubeb_stream * stm)
   }
 
   free(p);
   pthread_mutex_unlock(&stm->mutex);
   return draining ? DRAINING : RUNNING;
 }
 
 static int
-cubeb_run(cubeb * ctx)
+alsa_run(cubeb * ctx)
 {
   int r;
   int timeout;
   int i;
   char dummy;
   cubeb_stream * stm;
   enum stream_state state;
 
@@ -373,51 +378,51 @@ cubeb_run(cubeb * ctx)
         pthread_mutex_unlock(&ctx->mutex);
         return -1;
       }
     }
 
     for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
       stm = ctx->streams[i];
       if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
-        cubeb_set_stream_state(stm, PROCESSING);
+        alsa_set_stream_state(stm, PROCESSING);
         pthread_mutex_unlock(&ctx->mutex);
-        state = cubeb_refill_stream(stm);
+        state = alsa_refill_stream(stm);
         pthread_mutex_lock(&ctx->mutex);
-        cubeb_set_stream_state(stm, state);
+        alsa_set_stream_state(stm, state);
       }
     }
   } else if (r == 0) {
     for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
       stm = ctx->streams[i];
       if (stm) {
         if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
-          cubeb_set_stream_state(stm, INACTIVE);
+          alsa_set_stream_state(stm, INACTIVE);
           stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
         } else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
-          cubeb_set_stream_state(stm, ERROR);
+          alsa_set_stream_state(stm, ERROR);
           stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
         }
       }
     }
   }
 
   pthread_mutex_unlock(&ctx->mutex);
 
   return 0;
 }
 
 static void *
-cubeb_run_thread(void * context)
+alsa_run_thread(void * context)
 {
   cubeb * ctx = context;
   int r;
 
   do {
-    r = cubeb_run(ctx);
+    r = alsa_run(ctx);
   } while (r >= 0);
 
   return NULL;
 }
 
 static snd_config_t *
 get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
 {
@@ -562,62 +567,62 @@ init_local_config_with_workaround(char c
 
   snd_config_delete(lconf);
 
   return NULL;
 }
 
 
 static int
-cubeb_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * local_config)
+alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * local_config)
 {
   int r;
 
   pthread_mutex_lock(&cubeb_alsa_mutex);
   if (local_config) {
     r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
   } else {
     r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
   }
   pthread_mutex_unlock(&cubeb_alsa_mutex);
 
   return r;
 }
 
 static int
-cubeb_locked_pcm_close(snd_pcm_t * pcm)
+alsa_locked_pcm_close(snd_pcm_t * pcm)
 {
   int r;
 
   pthread_mutex_lock(&cubeb_alsa_mutex);
   r = snd_pcm_close(pcm);
   pthread_mutex_unlock(&cubeb_alsa_mutex);
 
   return r;
 }
 
 static int
-cubeb_register_stream(cubeb * ctx, cubeb_stream * stm)
+alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
 {
   int i;
 
   pthread_mutex_lock(&ctx->mutex);
   for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
     if (!ctx->streams[i]) {
       ctx->streams[i] = stm;
       break;
     }
   }
   pthread_mutex_unlock(&ctx->mutex);
 
   return i == CUBEB_STREAM_MAX;
 }
 
 static void
-cubeb_unregister_stream(cubeb_stream * stm)
+alsa_unregister_stream(cubeb_stream * stm)
 {
   cubeb * ctx;
   int i;
 
   ctx = stm->context;
 
   pthread_mutex_lock(&ctx->mutex);
   for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
@@ -630,18 +635,18 @@ cubeb_unregister_stream(cubeb_stream * s
 }
 
 static void
 silent_error_handler(char const * file, int line, char const * function,
                      int err, char const * fmt, ...)
 {
 }
 
-int
-cubeb_init(cubeb ** context, char const * context_name)
+/*static*/ int
+alsa_init(cubeb ** context, char const * context_name)
 {
   cubeb * ctx;
   int r;
   int i;
   int fd[2];
   pthread_attr_t attr;
   snd_pcm_t * dummy;
 
@@ -653,84 +658,86 @@ cubeb_init(cubeb ** context, char const 
     snd_lib_error_set_handler(silent_error_handler);
     cubeb_alsa_error_handler_set = 1;
   }
   pthread_mutex_unlock(&cubeb_alsa_mutex);
 
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
+  ctx->ops = &alsa_ops;
+
   r = pthread_mutex_init(&ctx->mutex, NULL);
   assert(r == 0);
 
   r = pipe(fd);
   assert(r == 0);
 
   for (i = 0; i < 2; ++i) {
     fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
     fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
   }
 
   ctx->control_fd_read = fd[0];
   ctx->control_fd_write = fd[1];
 
-  /* Force an early rebuild when cubeb_run is first called to ensure fds and
+  /* Force an early rebuild when alsa_run is first called to ensure fds and
      nfds have been initialized. */
   ctx->rebuild = 1;
 
   r = pthread_attr_init(&attr);
   assert(r == 0);
 
   r = pthread_attr_setstacksize(&attr, 256 * 1024);
   assert(r == 0);
 
-  r = pthread_create(&ctx->thread, &attr, cubeb_run_thread, ctx);
+  r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
   assert(r == 0);
 
   r = pthread_attr_destroy(&attr);
   assert(r == 0);
 
   /* Open a dummy PCM to force the configuration space to be evaluated so that
      init_local_config_with_workaround can find and modify the default node. */
-  r = cubeb_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, NULL);
+  r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, NULL);
   if (r >= 0) {
-    cubeb_locked_pcm_close(dummy);
+    alsa_locked_pcm_close(dummy);
   }
   ctx->is_pa = 0;
   pthread_mutex_lock(&cubeb_alsa_mutex);
   ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
   pthread_mutex_unlock(&cubeb_alsa_mutex);
   if (ctx->local_config) {
     ctx->is_pa = 1;
-    r = cubeb_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
+    r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
     /* If we got a local_config, we found a PA PCM.  If opening a PCM with that
        config fails with EINVAL, the PA PCM is too old for this workaround. */
     if (r == -EINVAL) {
       pthread_mutex_lock(&cubeb_alsa_mutex);
       snd_config_delete(ctx->local_config);
       pthread_mutex_unlock(&cubeb_alsa_mutex);
       ctx->local_config = NULL;
     } else if (r >= 0) {
-      cubeb_locked_pcm_close(dummy);
+      alsa_locked_pcm_close(dummy);
     }
   }
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
-char const *
-cubeb_get_backend_id(cubeb * ctx)
+static char const *
+alsa_get_backend_id(cubeb * ctx)
 {
   return "alsa";
 }
 
-void
-cubeb_destroy(cubeb * ctx)
+static void
+alsa_destroy(cubeb * ctx)
 {
   int r;
 
   assert(ctx);
 
   pthread_mutex_lock(&ctx->mutex);
   ctx->shutdown = 1;
   poll_wake(ctx);
@@ -748,36 +755,32 @@ cubeb_destroy(cubeb * ctx)
     pthread_mutex_lock(&cubeb_alsa_mutex);
     snd_config_delete(ctx->local_config);
     pthread_mutex_unlock(&cubeb_alsa_mutex);
   }
 
   free(ctx);
 }
 
-int
-cubeb_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
-                  cubeb_stream_params stream_params, unsigned int latency,
-                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
-                  void * user_ptr)
+static void alsa_stream_destroy(cubeb_stream * stm);
+
+static int
+alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                 cubeb_stream_params stream_params, unsigned int latency,
+                 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+                 void * user_ptr)
 {
   cubeb_stream * stm;
   int r;
   snd_pcm_format_t format;
 
   assert(ctx && stream);
 
   *stream = NULL;
 
-  if (stream_params.rate < 1 || stream_params.rate > 192000 ||
-      stream_params.channels < 1 || stream_params.channels > 32 ||
-      latency < 1 || latency > 2000) {
-    return CUBEB_ERROR_INVALID_FORMAT;
-  }
-
   switch (stream_params.format) {
   case CUBEB_SAMPLE_S16LE:
     format = SND_PCM_FORMAT_S16_LE;
     break;
   case CUBEB_SAMPLE_S16BE:
     format = SND_PCM_FORMAT_S16_BE;
     break;
   case CUBEB_SAMPLE_FLOAT32LE:
@@ -806,19 +809,19 @@ cubeb_stream_init(cubeb * ctx, cubeb_str
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
   stm->params = stream_params;
   stm->state = INACTIVE;
 
   r = pthread_mutex_init(&stm->mutex, NULL);
   assert(r == 0);
 
-  r = cubeb_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
+  r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
   if (r < 0) {
-    cubeb_stream_destroy(stm);
+    alsa_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   r = snd_pcm_nonblock(stm->pcm, 1);
   assert(r == 0);
 
   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
      possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
@@ -826,17 +829,17 @@ cubeb_stream_init(cubeb * ctx, cubeb_str
   if (!ctx->local_config && ctx->is_pa) {
     latency = latency < 500 ? 500 : latency;
   }
 
   r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                          stm->params.channels, stm->params.rate, 1,
                          latency * 1000);
   if (r < 0) {
-    cubeb_stream_destroy(stm);
+    alsa_stream_destroy(stm);
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
   assert(r == 0);
 
   stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
   assert(stm->nfds > 0);
@@ -844,109 +847,109 @@ cubeb_stream_init(cubeb * ctx, cubeb_str
   stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
   assert(stm->saved_fds);
   r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
   assert((nfds_t) r == stm->nfds);
 
   r = pthread_cond_init(&stm->cond, NULL);
   assert(r == 0);
 
-  if (cubeb_register_stream(ctx, stm) != 0) {
-    cubeb_stream_destroy(stm);
+  if (alsa_register_stream(ctx, stm) != 0) {
+    alsa_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
-void
-cubeb_stream_destroy(cubeb_stream * stm)
+static void
+alsa_stream_destroy(cubeb_stream * stm)
 {
   int r;
   cubeb * ctx;
 
   assert(stm && (stm->state == INACTIVE || stm->state == ERROR));
 
   ctx = stm->context;
 
   pthread_mutex_lock(&stm->mutex);
   if (stm->pcm) {
-    cubeb_locked_pcm_close(stm->pcm);
+    alsa_locked_pcm_close(stm->pcm);
     stm->pcm = NULL;
   }
   free(stm->saved_fds);
   pthread_mutex_unlock(&stm->mutex);
   pthread_mutex_destroy(&stm->mutex);
 
   r = pthread_cond_destroy(&stm->cond);
   assert(r == 0);
 
-  cubeb_unregister_stream(stm);
+  alsa_unregister_stream(stm);
 
   pthread_mutex_lock(&ctx->mutex);
   assert(ctx->active_streams >= 1);
   ctx->active_streams -= 1;
   pthread_mutex_unlock(&ctx->mutex);
 
   free(stm);
 }
 
-int
-cubeb_stream_start(cubeb_stream * stm)
+static int
+alsa_stream_start(cubeb_stream * stm)
 {
   cubeb * ctx;
 
   assert(stm);
   ctx = stm->context;
 
   pthread_mutex_lock(&stm->mutex);
   snd_pcm_pause(stm->pcm, 0);
   gettimeofday(&stm->last_activity, NULL);
   pthread_mutex_unlock(&stm->mutex);
 
   pthread_mutex_lock(&ctx->mutex);
   if (stm->state != INACTIVE) {
     pthread_mutex_unlock(&ctx->mutex);
     return CUBEB_ERROR;
   }
-  cubeb_set_stream_state(stm, RUNNING);
+  alsa_set_stream_state(stm, RUNNING);
   pthread_mutex_unlock(&ctx->mutex);
 
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_stop(cubeb_stream * stm)
+static int
+alsa_stream_stop(cubeb_stream * stm)
 {
   cubeb * ctx;
   int r;
 
   assert(stm);
   ctx = stm->context;
 
   pthread_mutex_lock(&ctx->mutex);
   while (stm->state == PROCESSING) {
     r = pthread_cond_wait(&stm->cond, &ctx->mutex);
     assert(r == 0);
   }
 
-  cubeb_set_stream_state(stm, INACTIVE);
+  alsa_set_stream_state(stm, INACTIVE);
   pthread_mutex_unlock(&ctx->mutex);
 
   pthread_mutex_lock(&stm->mutex);
   snd_pcm_pause(stm->pcm, 1);
   pthread_mutex_unlock(&stm->mutex);
 
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+static int
+alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   snd_pcm_sframes_t delay;
 
   assert(stm && position);
 
   pthread_mutex_lock(&stm->mutex);
 
   delay = -1;
@@ -964,8 +967,19 @@ cubeb_stream_get_position(cubeb_stream *
     *position = stm->write_position - delay;
   }
 
   stm->last_position = *position;
 
   pthread_mutex_unlock(&stm->mutex);
   return CUBEB_OK;
 }
+
+static struct cubeb_ops const alsa_ops = {
+  .init = alsa_init,
+  .get_backend_id = alsa_get_backend_id,
+  .destroy = alsa_destroy,
+  .stream_init = alsa_stream_init,
+  .stream_destroy = alsa_stream_destroy,
+  .stream_start = alsa_stream_start,
+  .stream_stop = alsa_stream_stop,
+  .stream_get_position = alsa_stream_get_position
+};
--- a/media/libcubeb/src/cubeb_audiounit.c
+++ b/media/libcubeb/src/cubeb_audiounit.c
@@ -5,36 +5,44 @@
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
 #include <assert.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <AudioUnit/AudioUnit.h>
 #include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
 
 #define NBUFS 4
 
+static struct cubeb_ops const audiounit_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+};
+
 struct cubeb_stream {
+  cubeb * context;
   AudioUnit unit;
   cubeb_data_callback data_callback;
   cubeb_state_callback state_callback;
   void * user_ptr;
   AudioStreamBasicDescription sample_spec;
   pthread_mutex_t mutex;
   uint64_t frames_played;
   uint64_t frames_queued;
   int shutdown;
   int draining;
 };
 
 static OSStatus
-audio_unit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
-                           AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
-                           AudioBufferList * bufs)
+audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
+                          AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
+                          AudioBufferList * bufs)
 {
   cubeb_stream * stm;
   unsigned char * buf;
   long got;
   OSStatus r;
 
   assert(bufs->mNumberBuffers == 1);
   buf = bufs->mBuffers[0].mData;
@@ -74,63 +82,69 @@ audio_unit_output_callback(void * user_p
 
   stm->frames_played = stm->frames_queued;
   stm->frames_queued += got;
   pthread_mutex_unlock(&stm->mutex);
 
   return noErr;
 }
 
-int
-cubeb_init(cubeb ** context, char const * context_name)
+/*static*/ int
+audiounit_init(cubeb ** context, char const * context_name)
 {
-  *context = (void *) 0xdeadbeef;
+  cubeb * ctx;
+
+  *context = NULL;
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  ctx->ops = &audiounit_ops;
+
+  *context = ctx;
+
   return CUBEB_OK;
 }
 
-char const *
-cubeb_get_backend_id(cubeb * ctx)
+static char const *
+audiounit_get_backend_id(cubeb * ctx)
 {
   return "audiounit";
 }
 
-void
-cubeb_destroy(cubeb * ctx)
+static void
+audiounit_destroy(cubeb * ctx)
 {
-  assert(ctx == (void *) 0xdeadbeef);
+  free(ctx);
 }
 
-int
-cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
-                  cubeb_stream_params stream_params, unsigned int latency,
-                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
-                  void * user_ptr)
+static void audiounit_stream_destroy(cubeb_stream * stm);
+
+static int
+audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+                      cubeb_stream_params stream_params, unsigned int latency,
+                      cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+                      void * user_ptr)
 {
   AudioStreamBasicDescription ss;
 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   ComponentDescription desc;
   Component comp;
 #else
   AudioComponentDescription desc;
   AudioComponent comp;
 #endif
   cubeb_stream * stm;
   AURenderCallbackStruct input;
   unsigned int buffer_size;
   OSStatus r;
 
-  assert(context == (void *) 0xdeadbeef);
+  assert(context);
   *stream = NULL;
 
-  if (stream_params.rate < 1 || stream_params.rate > 192000 ||
-      stream_params.channels < 1 || stream_params.channels > 32 ||
-      latency < 1 || latency > 2000) {
-    return CUBEB_ERROR_INVALID_FORMAT;
-  }
-
   memset(&ss, 0, sizeof(ss));
   ss.mFormatFlags = 0;
 
   switch (stream_params.format) {
   case CUBEB_SAMPLE_S16LE:
     ss.mBitsPerChannel = 16;
     ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
     break;
@@ -171,16 +185,17 @@ cubeb_stream_init(cubeb * context, cubeb
 #else
   comp = AudioComponentFindNext(NULL, &desc);
 #endif
   assert(comp);
 
   stm = calloc(1, sizeof(*stm));
   assert(stm);
 
+  stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
 
   stm->sample_spec = ss;
 
   r = pthread_mutex_init(&stm->mutex, NULL);
   assert(r == 0);
@@ -189,55 +204,55 @@ cubeb_stream_init(cubeb * context, cubeb
   stm->frames_queued = 0;
 
 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   r = OpenAComponent(comp, &stm->unit);
 #else
   r = AudioComponentInstanceNew(comp, &stm->unit);
 #endif
   if (r != 0) {
-    cubeb_stream_destroy(stm);
+    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
-  input.inputProc = audio_unit_output_callback;
+  input.inputProc = audiounit_output_callback;
   input.inputProcRefCon = stm;
   r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback,
                            kAudioUnitScope_Global, 0, &input, sizeof(input));
   if (r != 0) {
-    cubeb_stream_destroy(stm);
+    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
                            0, &ss, sizeof(ss));
   if (r != 0) {
-    cubeb_stream_destroy(stm);
+    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   buffer_size = ss.mSampleRate / 1000.0 * latency * ss.mBytesPerFrame / NBUFS;
   if (buffer_size % ss.mBytesPerFrame != 0) {
     buffer_size += ss.mBytesPerFrame - (buffer_size % ss.mBytesPerFrame);
   }
   assert(buffer_size % ss.mBytesPerFrame == 0);
 
   r = AudioUnitInitialize(stm->unit);
   if (r != 0) {
-    cubeb_stream_destroy(stm);
+    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
-void
-cubeb_stream_destroy(cubeb_stream * stm)
+static void
+audiounit_stream_destroy(cubeb_stream * stm)
 {
   int r;
 
   stm->shutdown = 1;
 
   if (stm->unit) {
     AudioOutputUnitStop(stm->unit);
     AudioUnitUninitialize(stm->unit);
@@ -249,36 +264,47 @@ cubeb_stream_destroy(cubeb_stream * stm)
   }
 
   r = pthread_mutex_destroy(&stm->mutex);
   assert(r == 0);
 
   free(stm);
 }
 
-int
-cubeb_stream_start(cubeb_stream * stm)
+static int
+audiounit_stream_start(cubeb_stream * stm)
 {
   OSStatus r;
   r = AudioOutputUnitStart(stm->unit);
   assert(r == 0);
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_stop(cubeb_stream * stm)
+static int
+audiounit_stream_stop(cubeb_stream * stm)
 {
   OSStatus r;
   r = AudioOutputUnitStop(stm->unit);
   assert(r == 0);
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+static int
+audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   pthread_mutex_lock(&stm->mutex);
   *position = stm->frames_played;
   pthread_mutex_unlock(&stm->mutex);
   return CUBEB_OK;
 }
+
+static struct cubeb_ops const audiounit_ops = {
+  .init = audiounit_init,
+  .get_backend_id = audiounit_get_backend_id,
+  .destroy = audiounit_destroy,
+  .stream_init = audiounit_stream_init,
+  .stream_destroy = audiounit_stream_destroy,
+  .stream_start = audiounit_stream_start,
+  .stream_stop = audiounit_stream_stop,
+  .stream_get_position = audiounit_stream_get_position
+};
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -1,22 +1,26 @@
 /*
  * Copyright © 2012 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
-#include "cubeb/cubeb.h"
 #include <assert.h>
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <SLES/OpenSLES.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+static struct cubeb_ops const opensl_ops;
 
 struct cubeb {
+  struct cubeb_ops const * ops;
   void * lib;
   SLInterfaceID SL_IID_BUFFERQUEUE;
   SLInterfaceID SL_IID_PLAY;
   SLObjectItf engObj;
   SLEngineItf eng;
   SLObjectItf outmixObj;
 };
 
@@ -71,18 +75,20 @@ bufferqueue_callback(SLBufferQueueItf ca
 
     if ((written * stm->framesize) < stm->queuebuf_len) {
       stm->draining = 1;
       return;
     }
   }
 }
 
-int
-cubeb_init(cubeb ** context, char const * context_name)
+static void opensl_destroy(cubeb * ctx);
+
+/*static*/ int
+opensl_init(cubeb ** context, char const * context_name)
 {
   cubeb * ctx;
 
   *context = NULL;
 
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
@@ -104,80 +110,82 @@ cubeb_init(cubeb ** context, char const 
   SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
   ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
   ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
   if (!f_slCreateEngine ||
       !SL_IID_ENGINE ||
       !SL_IID_OUTPUTMIX ||
       !ctx->SL_IID_BUFFERQUEUE ||
       !ctx->SL_IID_PLAY) {
-    cubeb_destroy(ctx);
+    opensl_destroy(ctx);
     return CUBEB_ERROR;
   }
 
 
   const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
 
   SLresult res;
   res = f_slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_destroy(ctx);
+    opensl_destroy(ctx);
     return CUBEB_ERROR;
   }
 
   res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_destroy(ctx);
+    opensl_destroy(ctx);
     return CUBEB_ERROR;
   }
 
   res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_destroy(ctx);
+    opensl_destroy(ctx);
     return CUBEB_ERROR;
   }
 
   const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
   const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
   res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_destroy(ctx);
+    opensl_destroy(ctx);
     return CUBEB_ERROR;
   }
 
   res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_destroy(ctx);
+    opensl_destroy(ctx);
     return CUBEB_ERROR;
   }
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
-char const *
-cubeb_get_backend_id(cubeb * ctx)
+static char const *
+opensl_get_backend_id(cubeb * ctx)
 {
   return "opensl";
 }
 
-void
-cubeb_destroy(cubeb * ctx)
+static void
+opensl_destroy(cubeb * ctx)
 {
   if (ctx->outmixObj)
     (*ctx->outmixObj)->Destroy(ctx->outmixObj);
   if (ctx->engObj)
     (*ctx->engObj)->Destroy(ctx->engObj);
   dlclose(ctx->lib);
   free(ctx);
 }
 
-int
-cubeb_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+static void opensl_stream_destroy(cubeb_stream * stm);
+
+static int
+opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                   cubeb_stream_params stream_params, unsigned int latency,
                   cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                   void * user_ptr)
 {
   cubeb_stream * stm;
 
   assert(ctx);
 
@@ -244,82 +252,92 @@ cubeb_stream_init(cubeb * ctx, cubeb_str
   sink.pLocator = &loc_outmix;
   sink.pFormat = NULL;
 
   const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE};
   const SLboolean req[] = {SL_BOOLEAN_TRUE};
   SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
                                                 &source, &sink, 1, ids, req);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_stream_destroy(stm);
+    opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_stream_destroy(stm);
+    opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_stream_destroy(stm);
+    opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE,
                                     &stm->bufq);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_stream_destroy(stm);
+    opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
   if (res != SL_RESULT_SUCCESS) {
-    cubeb_stream_destroy(stm);
+    opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
-void
-cubeb_stream_destroy(cubeb_stream * stm)
+static void
+opensl_stream_destroy(cubeb_stream * stm)
 {
   if (stm->playerObj)
     (*stm->playerObj)->Destroy(stm->playerObj);
   free(stm);
 }
 
-int
-cubeb_stream_start(cubeb_stream * stm)
+static int
+opensl_stream_start(cubeb_stream * stm)
 {
   SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
   if (res != SL_RESULT_SUCCESS)
     return CUBEB_ERROR;
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
   bufferqueue_callback(NULL, stm);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_stop(cubeb_stream * stm)
+static int
+opensl_stream_stop(cubeb_stream * stm)
 {
   SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
   if (res != SL_RESULT_SUCCESS)
     return CUBEB_ERROR;
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+static int
+opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   SLmillisecond msec;
   SLresult res = (*stm->play)->GetPosition(stm->play, &msec);
   if (res != SL_RESULT_SUCCESS)
     return CUBEB_ERROR;
   *position = (stm->bytespersec / (1000 * stm->framesize)) * msec;
   return CUBEB_OK;
 }
 
+static struct cubeb_ops const opensl_ops = {
+  .init = opensl_init,
+  .get_backend_id = opensl_get_backend_id,
+  .destroy = opensl_destroy,
+  .stream_init = opensl_stream_init,
+  .stream_destroy = opensl_stream_destroy,
+  .stream_start = opensl_stream_start,
+  .stream_stop = opensl_stream_stop,
+  .stream_get_position = opensl_stream_get_position
+};
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -1,21 +1,68 @@
 /*
  * Copyright © 2011 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
 #include <assert.h>
+#include <dlfcn.h>
 #include <stdlib.h>
 #include <pulse/pulseaudio.h>
 #include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x
+MAKE_TYPEDEF(pa_channel_map_init_auto);
+MAKE_TYPEDEF(pa_context_connect);
+MAKE_TYPEDEF(pa_context_disconnect);
+MAKE_TYPEDEF(pa_context_drain);
+MAKE_TYPEDEF(pa_context_get_state);
+MAKE_TYPEDEF(pa_context_new);
+MAKE_TYPEDEF(pa_context_rttime_new);
+MAKE_TYPEDEF(pa_context_set_state_callback);
+MAKE_TYPEDEF(pa_context_unref);
+MAKE_TYPEDEF(pa_frame_size);
+MAKE_TYPEDEF(pa_operation_get_state);
+MAKE_TYPEDEF(pa_operation_unref);
+MAKE_TYPEDEF(pa_rtclock_now);
+MAKE_TYPEDEF(pa_stream_begin_write);
+MAKE_TYPEDEF(pa_stream_cancel_write);
+MAKE_TYPEDEF(pa_stream_connect_playback);
+MAKE_TYPEDEF(pa_stream_cork);
+MAKE_TYPEDEF(pa_stream_disconnect);
+MAKE_TYPEDEF(pa_stream_get_latency);
+MAKE_TYPEDEF(pa_stream_get_state);
+MAKE_TYPEDEF(pa_stream_get_time);
+MAKE_TYPEDEF(pa_stream_new);
+MAKE_TYPEDEF(pa_stream_set_state_callback);
+MAKE_TYPEDEF(pa_stream_set_write_callback);
+MAKE_TYPEDEF(pa_stream_unref);
+MAKE_TYPEDEF(pa_stream_update_timing_info);
+MAKE_TYPEDEF(pa_stream_write);
+MAKE_TYPEDEF(pa_threaded_mainloop_free);
+MAKE_TYPEDEF(pa_threaded_mainloop_get_api);
+MAKE_TYPEDEF(pa_threaded_mainloop_lock);
+MAKE_TYPEDEF(pa_threaded_mainloop_new);
+MAKE_TYPEDEF(pa_threaded_mainloop_signal);
+MAKE_TYPEDEF(pa_threaded_mainloop_start);
+MAKE_TYPEDEF(pa_threaded_mainloop_stop);
+MAKE_TYPEDEF(pa_threaded_mainloop_unlock);
+MAKE_TYPEDEF(pa_threaded_mainloop_wait);
+MAKE_TYPEDEF(pa_usec_to_bytes);
+#undef MAKE_TYPEDEF
+#define WRAP(x) cubeb_##x
+
+static struct cubeb_ops const pulse_ops;
 
 struct cubeb {
+  struct cubeb_ops const * ops;
+  void * libpulse;
   pa_threaded_mainloop * mainloop;
   pa_context * context;
   int error;
 };
 
 struct cubeb_stream {
   struct cubeb * context;
   pa_stream * stream;
@@ -32,54 +79,54 @@ enum cork_state {
   CORK = 1 << 0,
   NOTIFY = 1 << 1
 };
 
 static void
 context_state_callback(pa_context * c, void * u)
 {
   cubeb * ctx = u;
-  if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(c))) {
+  if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) {
     ctx->error = 1;
   }
-  pa_threaded_mainloop_signal(ctx->mainloop, 0);
+  WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
 }
 
 static void
 context_notify_callback(pa_context * c, void * u)
 {
   cubeb * ctx = u;
-  pa_threaded_mainloop_signal(ctx->mainloop, 0);
+  WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
 }
 
 static void
 stream_success_callback(pa_stream * s, int success, void * u)
 {
   cubeb_stream * stm = u;
-  pa_threaded_mainloop_signal(stm->context->mainloop, 0);
+  WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
 }
 
 static void
 stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u)
 {
   cubeb_stream * stm = u;
   /* there's no pa_rttime_free, so use this instead. */
   a->time_free(stm->drain_timer);
   stm->drain_timer = NULL;
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
 }
 
 static void
 stream_state_callback(pa_stream * s, void * u)
 {
   cubeb_stream * stm = u;
-  if (!PA_STREAM_IS_GOOD(pa_stream_get_state(s))) {
+  if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) {
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
   }
-  pa_threaded_mainloop_signal(stm->context->mainloop, 0);
+  WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
 }
 
 static void
 stream_request_callback(pa_stream * s, size_t nbytes, void * u)
 {
   cubeb_stream * stm;
   void * buffer;
   size_t size;
@@ -88,206 +135,260 @@ stream_request_callback(pa_stream * s, s
   size_t towrite;
   size_t frame_size;
 
   stm = u;
 
   if (stm->shutdown)
     return;
 
-  frame_size = pa_frame_size(&stm->sample_spec);
+  frame_size = WRAP(pa_frame_size)(&stm->sample_spec);
 
   assert(nbytes % frame_size == 0);
 
   towrite = nbytes;
 
   while (towrite) {
     size = towrite;
-    r = pa_stream_begin_write(s, &buffer, &size);
+    r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
     assert(r == 0);
     assert(size > 0);
     assert(size % frame_size == 0);
 
     got = stm->data_callback(stm, stm->user_ptr, buffer, size / frame_size);
     if (got < 0) {
-      pa_stream_cancel_write(s);
+      WRAP(pa_stream_cancel_write)(s);
       stm->shutdown = 1;
       return;
     }
 
-    r = pa_stream_write(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
+    r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
     assert(r == 0);
 
     if ((size_t) got < size / frame_size) {
       pa_usec_t latency = 0;
-      r = pa_stream_get_latency(s, &latency, NULL);
+      r = WRAP(pa_stream_get_latency)(s, &latency, NULL);
       if (r == -PA_ERR_NODATA) {
         /* this needs a better guess. */
         latency = 100 * PA_USEC_PER_MSEC;
       }
       assert(r == 0 || r == -PA_ERR_NODATA);
       /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
       /* arbitrary safety margin: double the current latency. */
-      stm->drain_timer = pa_context_rttime_new(stm->context->context, pa_rtclock_now() + 2 * latency, stream_drain_callback, stm);
+      stm->drain_timer = WRAP(pa_context_rttime_new)(stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, stream_drain_callback, stm);
       stm->shutdown = 1;
       return;
     }
 
     towrite -= size;
   }
 
   assert(towrite == 0);
 }
 
 static int
 wait_until_context_ready(cubeb * ctx)
 {
   for (;;) {
-    pa_context_state_t state = pa_context_get_state(ctx->context);
+    pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context);
     if (!PA_CONTEXT_IS_GOOD(state))
       return -1;
     if (state == PA_CONTEXT_READY)
       break;
-    pa_threaded_mainloop_wait(ctx->mainloop);
+    WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
   }
   return 0;
 }
 
 static int
 wait_until_stream_ready(cubeb_stream * stm)
 {
   for (;;) {
-    pa_stream_state_t state = pa_stream_get_state(stm->stream);
+    pa_stream_state_t state = WRAP(pa_stream_get_state)(stm->stream);
     if (!PA_STREAM_IS_GOOD(state))
       return -1;
     if (state == PA_STREAM_READY)
       break;
-    pa_threaded_mainloop_wait(stm->context->mainloop);
+    WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop);
   }
   return 0;
 }
 
 static int
 operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o)
 {
-  while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-    pa_threaded_mainloop_wait(ctx->mainloop);
-    if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(ctx->context)))
+  while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) {
+    WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
+    if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context)))
       return -1;
-    if (stream && !PA_STREAM_IS_GOOD(pa_stream_get_state(stream)))
+    if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream)))
       return -1;
   }
   return 0;
 }
 
 static void
 stream_cork(cubeb_stream * stm, enum cork_state state)
 {
   pa_operation * o;
 
-  pa_threaded_mainloop_lock(stm->context->mainloop);
-  o = pa_stream_cork(stm->stream, state & CORK, stream_success_callback, stm);
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  o = WRAP(pa_stream_cork)(stm->stream, state & CORK, stream_success_callback, stm);
   if (o) {
     operation_wait(stm->context, stm->stream, o);
-    pa_operation_unref(o);
+    WRAP(pa_operation_unref)(o);
   }
-  pa_threaded_mainloop_unlock(stm->context->mainloop);
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   if (state & NOTIFY) {
     stm->state_callback(stm, stm->user_ptr,
                         state & CORK ? CUBEB_STATE_STOPPED : CUBEB_STATE_STARTED);
   }
 }
 
-int
-cubeb_init(cubeb ** context, char const * context_name)
+static void pulse_destroy(cubeb * ctx);
+
+/*static*/ int
+pulse_init(cubeb ** context, char const * context_name)
 {
+  void * libpulse;
   cubeb * ctx;
 
   *context = NULL;
 
+  libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
+  if (!libpulse) {
+    return CUBEB_ERROR;
+  }
+
+#define LOAD(x) do { \
+    cubeb_##x = dlsym(libpulse, #x); \
+    if (!cubeb_##x) { \
+      dlclose(libpulse); \
+      return CUBEB_ERROR; \
+    } \
+  } while(0)
+  LOAD(pa_channel_map_init_auto);
+  LOAD(pa_context_connect);
+  LOAD(pa_context_disconnect);
+  LOAD(pa_context_drain);
+  LOAD(pa_context_get_state);
+  LOAD(pa_context_new);
+  LOAD(pa_context_rttime_new);
+  LOAD(pa_context_set_state_callback);
+  LOAD(pa_context_unref);
+  LOAD(pa_frame_size);
+  LOAD(pa_operation_get_state);
+  LOAD(pa_operation_unref);
+  LOAD(pa_rtclock_now);
+  LOAD(pa_stream_begin_write);
+  LOAD(pa_stream_cancel_write);
+  LOAD(pa_stream_connect_playback);
+  LOAD(pa_stream_cork);
+  LOAD(pa_stream_disconnect);
+  LOAD(pa_stream_get_latency);
+  LOAD(pa_stream_get_state);
+  LOAD(pa_stream_get_time);
+  LOAD(pa_stream_new);
+  LOAD(pa_stream_set_state_callback);
+  LOAD(pa_stream_set_write_callback);
+  LOAD(pa_stream_unref);
+  LOAD(pa_stream_update_timing_info);
+  LOAD(pa_stream_write);
+  LOAD(pa_threaded_mainloop_free);
+  LOAD(pa_threaded_mainloop_get_api);
+  LOAD(pa_threaded_mainloop_lock);
+  LOAD(pa_threaded_mainloop_new);
+  LOAD(pa_threaded_mainloop_signal);
+  LOAD(pa_threaded_mainloop_start);
+  LOAD(pa_threaded_mainloop_stop);
+  LOAD(pa_threaded_mainloop_unlock);
+  LOAD(pa_threaded_mainloop_wait);
+  LOAD(pa_usec_to_bytes);
+#undef LOAD
+
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
-  ctx->mainloop = pa_threaded_mainloop_new();
-  ctx->context = pa_context_new(pa_threaded_mainloop_get_api(ctx->mainloop), context_name);
+  ctx->ops = &pulse_ops;
+  ctx->libpulse = libpulse;
+
+  ctx->mainloop = WRAP(pa_threaded_mainloop_new)();
+  ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), context_name);
 
-  pa_context_set_state_callback(ctx->context, context_state_callback, ctx);
-  pa_threaded_mainloop_start(ctx->mainloop);
+  WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx);
+  WRAP(pa_threaded_mainloop_start)(ctx->mainloop);
 
-  pa_threaded_mainloop_lock(ctx->mainloop);
-  pa_context_connect(ctx->context, NULL, 0, NULL);
+  WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
+  WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
 
   if (wait_until_context_ready(ctx) != 0) {
-    pa_threaded_mainloop_unlock(ctx->mainloop);
-    cubeb_destroy(ctx);
+    WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
+    pulse_destroy(ctx);
     return CUBEB_ERROR;
   }
-  pa_threaded_mainloop_unlock(ctx->mainloop);
+  WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
-char const *
-cubeb_get_backend_id(cubeb * ctx)
+static char const *
+pulse_get_backend_id(cubeb * ctx)
 {
   return "pulse";
 }
 
-void
-cubeb_destroy(cubeb * ctx)
+static void
+pulse_destroy(cubeb * ctx)
 {
   pa_operation * o;
 
   if (ctx->context) {
-    pa_threaded_mainloop_lock(ctx->mainloop);
-    o = pa_context_drain(ctx->context, context_notify_callback, ctx);
+    WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
+    o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx);
     if (o) {
       operation_wait(ctx, NULL, o);
-      pa_operation_unref(o);
+      WRAP(pa_operation_unref)(o);
     }
-    pa_context_set_state_callback(ctx->context, NULL, NULL);
-    pa_context_disconnect(ctx->context);
-    pa_context_unref(ctx->context);
-    pa_threaded_mainloop_unlock(ctx->mainloop);
+    WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL);
+    WRAP(pa_context_disconnect)(ctx->context);
+    WRAP(pa_context_unref)(ctx->context);
+    WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
   }
 
   if (ctx->mainloop) {
-    pa_threaded_mainloop_stop(ctx->mainloop);
-    pa_threaded_mainloop_free(ctx->mainloop);
+    WRAP(pa_threaded_mainloop_stop)(ctx->mainloop);
+    WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
   }
 
+  dlclose(ctx->libpulse);
   free(ctx);
 }
 
-int
-cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+static void pulse_stream_destroy(cubeb_stream * stm);
+
+static int
+pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                   cubeb_stream_params stream_params, unsigned int latency,
                   cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                   void * user_ptr)
 {
   pa_sample_spec ss;
   cubeb_stream * stm;
   pa_operation * o;
   pa_buffer_attr battr;
   pa_channel_map map;
   int r;
 
   assert(context);
 
   *stream = NULL;
 
-  if (stream_params.rate < 1 || stream_params.rate > 192000 ||
-      stream_params.channels < 1 || stream_params.channels > 32 ||
-      latency < 1 || latency > 2000) {
-    return CUBEB_ERROR_INVALID_FORMAT;
-  }
-
   switch (stream_params.format) {
   case CUBEB_SAMPLE_S16LE:
     ss.format = PA_SAMPLE_S16LE;
     break;
   case CUBEB_SAMPLE_S16BE:
     ss.format = PA_SAMPLE_S16BE;
     break;
   case CUBEB_SAMPLE_FLOAT32LE:
@@ -299,115 +400,125 @@ cubeb_stream_init(cubeb * context, cubeb
   default:
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   ss.rate = stream_params.rate;
   ss.channels = stream_params.channels;
 
   /* XXX check that this does the right thing for Vorbis and WaveEx */
-  pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+  WRAP(pa_channel_map_init_auto)(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
 
   stm = calloc(1, sizeof(*stm));
   assert(stm);
 
   stm->context = context;
 
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
 
   stm->sample_spec = ss;
 
   battr.maxlength = -1;
-  battr.tlength = pa_usec_to_bytes(latency * PA_USEC_PER_MSEC, &stm->sample_spec);
+  battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, &stm->sample_spec);
   battr.prebuf = -1;
   battr.minreq = battr.tlength / 4;
   battr.fragsize = -1;
 
-  pa_threaded_mainloop_lock(stm->context->mainloop);
-  stm->stream = pa_stream_new(stm->context->context, stream_name, &ss, &map);
-  pa_stream_set_state_callback(stm->stream, stream_state_callback, stm);
-  pa_stream_set_write_callback(stm->stream, stream_request_callback, stm);
-  pa_stream_connect_playback(stm->stream, NULL, &battr,
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  stm->stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &map);
+  WRAP(pa_stream_set_state_callback)(stm->stream, stream_state_callback, stm);
+  WRAP(pa_stream_set_write_callback)(stm->stream, stream_request_callback, stm);
+  WRAP(pa_stream_connect_playback)(stm->stream, NULL, &battr,
                              PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
                              PA_STREAM_START_CORKED,
                              NULL, NULL);
 
   r = wait_until_stream_ready(stm);
   if (r == 0) {
     /* force a timing update now, otherwise timing info does not become valid
        until some point after initialization has completed. */
-    o = pa_stream_update_timing_info(stm->stream, stream_success_callback, stm);
+    o = WRAP(pa_stream_update_timing_info)(stm->stream, stream_success_callback, stm);
     if (o) {
       r = operation_wait(stm->context, stm->stream, o);
-      pa_operation_unref(o);
+      WRAP(pa_operation_unref)(o);
     }
   }
-  pa_threaded_mainloop_unlock(stm->context->mainloop);
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   if (r != 0) {
-    cubeb_stream_destroy(stm);
+    pulse_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
-void
-cubeb_stream_destroy(cubeb_stream * stm)
+static void
+pulse_stream_destroy(cubeb_stream * stm)
 {
   if (stm->stream) {
     stream_cork(stm, CORK);
 
-    pa_threaded_mainloop_lock(stm->context->mainloop);
+    WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
 
     if (stm->drain_timer) {
       /* there's no pa_rttime_free, so use this instead. */
-      pa_threaded_mainloop_get_api(stm->context->mainloop)->time_free(stm->drain_timer);
+      WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer);
     }
 
-    pa_stream_set_state_callback(stm->stream, NULL, NULL);
-    pa_stream_disconnect(stm->stream);
-    pa_stream_unref(stm->stream);
-    pa_threaded_mainloop_unlock(stm->context->mainloop);
+    WRAP(pa_stream_set_state_callback)(stm->stream, NULL, NULL);
+    WRAP(pa_stream_disconnect)(stm->stream);
+    WRAP(pa_stream_unref)(stm->stream);
+    WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   }
 
   free(stm);
 }
 
-int
-cubeb_stream_start(cubeb_stream * stm)
+static int
+pulse_stream_start(cubeb_stream * stm)
 {
   stream_cork(stm, UNCORK | NOTIFY);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_stop(cubeb_stream * stm)
+static int
+pulse_stream_stop(cubeb_stream * stm)
 {
   stream_cork(stm, CORK | NOTIFY);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+static int
+pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   int r;
   pa_usec_t r_usec;
   uint64_t bytes;
 
-  pa_threaded_mainloop_lock(stm->context->mainloop);
-  r = pa_stream_get_time(stm->stream, &r_usec);
-  pa_threaded_mainloop_unlock(stm->context->mainloop);
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+  r = WRAP(pa_stream_get_time)(stm->stream, &r_usec);
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   if (r != 0) {
     return CUBEB_ERROR;
   }
 
-  bytes = pa_usec_to_bytes(r_usec, &stm->sample_spec);
-  *position = bytes / pa_frame_size(&stm->sample_spec);
+  bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->sample_spec);
+  *position = bytes / WRAP(pa_frame_size)(&stm->sample_spec);
 
   return CUBEB_OK;
 }
 
+static struct cubeb_ops const pulse_ops = {
+  .init = pulse_init,
+  .get_backend_id = pulse_get_backend_id,
+  .destroy = pulse_destroy,
+  .stream_init = pulse_stream_init,
+  .stream_destroy = pulse_stream_destroy,
+  .stream_start = pulse_stream_start,
+  .stream_stop = pulse_stream_stop,
+  .stream_get_position = pulse_stream_get_position
+};
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -5,205 +5,221 @@
  * accompanying file LICENSE for details.
  */
 #include <poll.h>
 #include <pthread.h>
 #include <sndio.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
 
 #ifdef CUBEB_SNDIO_DEBUG
 #define DPR(...) fprintf(stderr, __VA_ARGS__);
 #else
 #define DPR(...) do {} while(0)
 #endif
 
+static struct cubeb_ops const sndio_ops;
+
+struct cubeb {
+  struct cubeb_ops const * ops;
+};
+
 struct cubeb_stream {
+  cubeb * context;
   pthread_t th;			  /* to run real-time audio i/o */
   pthread_mutex_t mtx;		  /* protects hdl and pos */
   struct sio_hdl *hdl;		  /* link us to sndio */
   int active;			  /* cubec_start() called */
   int conv;			  /* need float->s16 conversion */
   unsigned char *buf;		  /* data is prepared here */
   unsigned int nfr;		  /* number of frames in buf */
   unsigned int bpf;		  /* bytes per frame */
   unsigned int pchan;		  /* number of play channels */
   uint64_t rdpos;		  /* frame number Joe hears right now */
   uint64_t wrpos;		  /* number of written frames */
   cubeb_data_callback data_cb;    /* cb to preapare data */
   cubeb_state_callback state_cb;  /* cb to notify about state changes */
   void *arg;			  /* user arg to {data,state}_cb */
 };
 
-void
+static void
 float_to_s16(void *ptr, long nsamp)
 {
   int16_t *dst = ptr;
   float *src = ptr;
 
   while (nsamp-- > 0)
     *(dst++) = *(src++) * 32767;
 }
 
-void
-cubeb_onmove(void *arg, int delta)
+static void
+sndio_onmove(void *arg, int delta)
 {
   struct cubeb_stream *s = (struct cubeb_stream *)arg;
 
   s->rdpos += delta;
 }
 
-void *
-cubeb_mainloop(void *arg)
+static void *
+sndio_mainloop(void *arg)
 {
 #define MAXFDS 8
   struct pollfd pfds[MAXFDS];
   struct cubeb_stream *s = arg;
   int n, nfds, revents, state;
   size_t start = 0, end = 0;
   long nfr;
 
-  DPR("cubeb_mainloop()\n");
+  DPR("sndio_mainloop()\n");
   s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
   pthread_mutex_lock(&s->mtx);
   if (!sio_start(s->hdl)) {
     pthread_mutex_unlock(&s->mtx);
     return NULL;
   }
-  DPR("cubeb_mainloop(), started\n");
+  DPR("sndio_mainloop(), started\n");
 
   start = end = s->nfr;
   for (;;) {
     if (!s->active) {
-      DPR("cubeb_mainloop() stopped\n");
+      DPR("sndio_mainloop() stopped\n");
       state = CUBEB_STATE_STOPPED;
       break;
     }
     if (start == end) {
       if (end < s->nfr) {
-        DPR("cubeb_mainloop() drained\n");
+        DPR("sndio_mainloop() drained\n");
         state = CUBEB_STATE_DRAINED;
         break;
       }
       pthread_mutex_unlock(&s->mtx);
       nfr = s->data_cb(s, s->arg, s->buf, s->nfr);
       pthread_mutex_lock(&s->mtx);
       if (nfr < 0) {
-        DPR("cubeb_mainloop() cb err\n");
+        DPR("sndio_mainloop() cb err\n");
         state = CUBEB_STATE_ERROR;
         break;
       }
       if (s->conv)
         float_to_s16(s->buf, nfr * s->pchan);
       start = 0;
       end = nfr * s->bpf;
     }
     if (end == 0)
       continue;
-    nfds = sio_pollfd(s->hdl, pfds, POLLOUT);    
+    nfds = sio_pollfd(s->hdl, pfds, POLLOUT);
     if (nfds > 0) {
       pthread_mutex_unlock(&s->mtx);
       n = poll(pfds, nfds, -1);
       pthread_mutex_lock(&s->mtx);
       if (n < 0)
         continue;
     }
     revents = sio_revents(s->hdl, pfds);
     if (revents & POLLHUP)
       break;
     if (revents & POLLOUT) {
       n = sio_write(s->hdl, s->buf + start, end - start);
       if (n == 0) {
-        DPR("cubeb_mainloop() werr\n");
+        DPR("sndio_mainloop() werr\n");
         state = CUBEB_STATE_ERROR;
         break;
       }
       s->wrpos = 0;
       start += n;
     }
   }
   sio_stop(s->hdl);
   s->rdpos = s->wrpos;
   pthread_mutex_unlock(&s->mtx);
   s->state_cb(s, s->arg, state);
   return NULL;
 }
 
-int
-cubeb_init(cubeb **context, char const *context_name)
+/*static*/ int
+sndio_init(cubeb **context, char const *context_name)
 {
-  DPR("cubeb_init(%s)\n", context_name);
-  *context = (void *)0xdeadbeef;
+  DPR("sndio_init(%s)\n", context_name);
+  *context = malloc(sizeof(*context));
+  (*context)->ops = &sndio_ops;
   (void)context_name;
   return CUBEB_OK;
 }
 
-void
-cubeb_destroy(cubeb *context)
+static char const *
+sndio_get_backend_id(cubeb *context)
 {
-  DPR("cubeb_destroy()\n");
-  (void)context;
+  return "sndio";
 }
 
-int
-cubeb_stream_init(cubeb *context,
-    cubeb_stream **stream,
-    char const *stream_name,
-    cubeb_stream_params stream_params, unsigned int latency,
-    cubeb_data_callback data_callback,
-    cubeb_state_callback state_callback,
-    void *user_ptr)
+static void
+sndio_destroy(cubeb *context)
+{
+  DPR("sndio_destroy()\n");
+  free(context);
+}
+
+static int
+sndio_stream_init(cubeb *context,
+                  cubeb_stream **stream,
+                  char const *stream_name,
+                  cubeb_stream_params stream_params, unsigned int latency,
+                  cubeb_data_callback data_callback,
+                  cubeb_state_callback state_callback,
+                  void *user_ptr)
 {
   struct cubeb_stream *s;
   struct sio_par wpar, rpar;
-  DPR("cubeb_stream_init(%s)\n", stream_name);
+  DPR("sndio_stream_init(%s)\n", stream_name);
   size_t size;
 
   s = malloc(sizeof(struct cubeb_stream));
   if (s == NULL)
     return CUBEB_ERROR;
+  s->context = context;
   s->hdl = sio_open(NULL, SIO_PLAY, 0);
   if (s->hdl == NULL) {
     free(s);
-    DPR("cubeb_stream_init(), sio_open() failed\n");
+    DPR("sndio_stream_init(), sio_open() failed\n");
     return CUBEB_ERROR;
   }
   sio_initpar(&wpar);
   wpar.sig = 1;
   wpar.bits = 16;
   switch (stream_params.format) {
   case CUBEB_SAMPLE_S16LE:
     wpar.le = 1;
     break;
   case CUBEB_SAMPLE_S16BE:
     wpar.le = 0;
     break;
   case CUBEB_SAMPLE_FLOAT32NE:
     wpar.le = SIO_LE_NATIVE;
     break;
   default:
-    DPR("cubeb_stream_init() unsupported format\n");
+    DPR("sndio_stream_init() unsupported format\n");
     return CUBEB_ERROR_INVALID_FORMAT;
   }
   wpar.rate = stream_params.rate;
   wpar.pchan = stream_params.channels;
   wpar.appbufsz = latency * wpar.rate / 1000;
   if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
     sio_close(s->hdl);
     free(s);
-    DPR("cubeb_stream_init(), sio_setpar() failed\n");
+    DPR("sndio_stream_init(), sio_setpar() failed\n");
     return CUBEB_ERROR;
   }
   if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
       rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
       rpar.pchan != wpar.pchan) {
     sio_close(s->hdl);
     free(s);
-    DPR("cubeb_stream_init() unsupported params\n");
+    DPR("sndio_stream_init() unsupported params\n");
     return CUBEB_ERROR_INVALID_FORMAT;
   }
   sio_onmove(s->hdl, cubeb_onmove, s);
   s->active = 0;
   s->nfr = rpar.round;
   s->bpf = rpar.bps * rpar.pchan;
   s->pchan = rpar.pchan;
   s->data_cb = data_callback;
@@ -220,69 +236,80 @@ cubeb_stream_init(cubeb *context,
   }
   s->buf = malloc(size);
   if (s->buf == NULL) {
     sio_close(s->hdl);
     free(s);
     return CUBEB_ERROR;
   }
   *stream = s;
-  DPR("cubeb_stream_init() end, ok\n");
+  DPR("sndio_stream_init() end, ok\n");
   (void)context;
   (void)stream_name;
   return CUBEB_OK;
 }
 
-void
-cubeb_stream_destroy(cubeb_stream *s)
+static void
+sndio_stream_destroy(cubeb_stream *s)
 {
-  DPR("cubeb_stream_destroy()\n");
+  DPR("sndio_stream_destroy()\n");
   sio_close(s->hdl);
   free(s);
 }
 
-int
-cubeb_stream_start(cubeb_stream *s)
+static int
+sndio_stream_start(cubeb_stream *s)
 {
   int err;
 
-  DPR("cubeb_stream_start()\n");
+  DPR("sndio_stream_start()\n");
   s->active = 1;
   err = pthread_create(&s->th, NULL, cubeb_mainloop, s);
   if (err) {
     s->active = 0;
     return CUBEB_ERROR;
   }
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_stop(cubeb_stream *s)
+static int
+sndio_stream_stop(cubeb_stream *s)
 {
   void *dummy;
 
-  DPR("cubeb_stream_stop()\n");
+  DPR("sndio_stream_stop()\n");
   if (s->active) {
     s->active = 0;
     pthread_join(s->th, &dummy);
   }
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_get_position(cubeb_stream *s, uint64_t *p)
+static int
+sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
 {
   pthread_mutex_lock(&s->mtx);
-  DPR("cubeb_stream_get_position() %lld\n", s->rdpos);
+  DPR("sndio_stream_get_position() %lld\n", s->rdpos);
   *p = s->rdpos;
   pthread_mutex_unlock(&s->mtx);
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_set_volume(cubeb_stream *s, float volume)
+static int
+sndio_stream_set_volume(cubeb_stream *s, float volume)
 {
-  DPR("cubeb_stream_set_volume(%f)\n", volume);
+  DPR("sndio_stream_set_volume(%f)\n", volume);
   pthread_mutex_lock(&s->mtx);
   sio_setvol(s->hdl, SIO_MAXVOL * volume);
   pthread_mutex_unlock(&s->mtx);
   return CUBEB_OK;
 }
+
+static struct cubeb_ops const sndio_ops = {
+  .init = sndio_init,
+  .get_backend_id = sndio_get_backend_id,
+  .destroy = sndio_destroy,
+  .stream_init = sndio_stream_init,
+  .stream_destroy = sndio_stream_destroy,
+  .stream_start = sndio_stream_start,
+  .stream_stop = sndio_stream_stop,
+  .stream_get_position = sndio_stream_get_position
+};
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -11,16 +11,17 @@
 #include <malloc.h>
 #include <assert.h>
 #include <windows.h>
 #include <mmreg.h>
 #include <mmsystem.h>
 #include <process.h>
 #include <stdlib.h>
 #include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
 
 /* This is missing from the MinGW headers. Use a safe fallback. */
 #ifndef MEMORY_ALLOCATION_ALIGNMENT
 #define MEMORY_ALLOCATION_ALIGNMENT 16
 #endif
 
 #define CUBEB_STREAM_MAX 32
 #define NBUFS 4
@@ -30,17 +31,20 @@ const GUID KSDATAFORMAT_SUBTYPE_PCM =
 const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
 { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
 
 struct cubeb_stream_item {
   SLIST_ENTRY head;
   cubeb_stream * stream;
 };
 
+static struct cubeb_ops const winmm_ops;
+
 struct cubeb {
+  struct cubeb_ops const * ops;
   HANDLE event;
   HANDLE thread;
   int shutdown;
   PSLIST_HEADER work;
   CRITICAL_SECTION lock;
   unsigned int active_streams;
   unsigned int minimum_latency;
 };
@@ -77,32 +81,32 @@ bytes_per_frame(cubeb_stream_params para
   default:
     assert(0);
   }
 
   return bytes * params.channels;
 }
 
 static WAVEHDR *
-cubeb_get_next_buffer(cubeb_stream * stm)
+winmm_get_next_buffer(cubeb_stream * stm)
 {
   WAVEHDR * hdr = NULL;
 
   assert(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);
   hdr = &stm->buffers[stm->next_buffer];
   assert(hdr->dwFlags & WHDR_PREPARED ||
          (hdr->dwFlags & WHDR_DONE && !(hdr->dwFlags & WHDR_INQUEUE)));
   stm->next_buffer = (stm->next_buffer + 1) % NBUFS;
   stm->free_buffers -= 1;
 
   return hdr;
 }
 
 static void
-cubeb_refill_stream(cubeb_stream * stm)
+winmm_refill_stream(cubeb_stream * stm)
 {
   WAVEHDR * hdr;
   long got;
   long wanted;
   MMRESULT r;
 
   EnterCriticalSection(&stm->lock);
   stm->free_buffers += 1;
@@ -118,17 +122,17 @@ cubeb_refill_stream(cubeb_stream * stm)
   }
 
   if (stm->shutdown) {
     LeaveCriticalSection(&stm->lock);
     SetEvent(stm->event);
     return;
   }
 
-  hdr = cubeb_get_next_buffer(stm);
+  hdr = winmm_get_next_buffer(stm);
 
   wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);
 
   /* It is assumed that the caller is holding this lock.  It must be dropped
      during the callback to avoid deadlocks. */
   LeaveCriticalSection(&stm->lock);
   got = stm->data_callback(stm, stm->user_ptr, hdr->lpData, wanted);
   EnterCriticalSection(&stm->lock);
@@ -152,17 +156,17 @@ cubeb_refill_stream(cubeb_stream * stm)
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
     return;
   }
 
   LeaveCriticalSection(&stm->lock);
 }
 
 static unsigned __stdcall
-cubeb_buffer_thread(void * user_ptr)
+winmm_buffer_thread(void * user_ptr)
 {
   cubeb * ctx = (cubeb *) user_ptr;
   assert(ctx);
 
   for (;;) {
     DWORD rv;
     PSLIST_ENTRY item;
 
@@ -170,31 +174,31 @@ cubeb_buffer_thread(void * user_ptr)
     assert(rv == WAIT_OBJECT_0);
 
     /* Process work items in batches so that a single stream can't
        starve the others by continuously adding new work to the top of
        the work item stack. */
     item = InterlockedFlushSList(ctx->work);
     while (item != NULL) {
       PSLIST_ENTRY tmp = item;
-      cubeb_refill_stream(((struct cubeb_stream_item *) tmp)->stream);
+      winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream);
       item = item->Next;
       _aligned_free(tmp);
     }
 
     if (ctx->shutdown) {
       break;
     }
   }
 
   return 0;
 }
 
 static void CALLBACK
-cubeb_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
+winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
 {
   cubeb_stream * stm = (cubeb_stream *) user_ptr;
   struct cubeb_stream_item * item;
 
   if (msg != WOM_DONE) {
     return;
   }
 
@@ -229,63 +233,67 @@ calculate_minimum_latency(void)
 
   if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) {
     return 200;
   }
 
   return 0;
 }
 
-int
-cubeb_init(cubeb ** context, char const * context_name)
+static void winmm_destroy(cubeb * ctx);
+
+/*static*/ int
+winmm_init(cubeb ** context, char const * context_name)
 {
   cubeb * ctx;
 
   assert(context);
   *context = NULL;
 
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
+  ctx->ops = &winmm_ops;
+
   ctx->work = _aligned_malloc(sizeof(*ctx->work), MEMORY_ALLOCATION_ALIGNMENT);
   assert(ctx->work);
   InitializeSListHead(ctx->work);
 
   ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
   if (!ctx->event) {
-    cubeb_destroy(ctx);
+    winmm_destroy(ctx);
     return CUBEB_ERROR;
   }
 
-  ctx->thread = (HANDLE) _beginthreadex(NULL, 64 * 1024, cubeb_buffer_thread, ctx, 0, NULL);
+  ctx->thread = (HANDLE) _beginthreadex(NULL, 64 * 1024, winmm_buffer_thread, ctx, 0, NULL);
   if (!ctx->thread) {
-    cubeb_destroy(ctx);
+    winmm_destroy(ctx);
     return CUBEB_ERROR;
   }
 
   SetThreadPriority(ctx->thread, THREAD_PRIORITY_TIME_CRITICAL);
 
   InitializeCriticalSection(&ctx->lock);
   ctx->active_streams = 0;
 
   ctx->minimum_latency = calculate_minimum_latency();
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
-char const *
-cubeb_get_backend_id(cubeb * ctx)
+static char const *
+winmm_get_backend_id(cubeb * ctx)
 {
   return "winmm";
 }
 
-void
-cubeb_destroy(cubeb * ctx)
+static void
+winmm_destroy(cubeb * ctx)
 {
   DWORD rv;
 
   assert(ctx->active_streams == 0);
   assert(!InterlockedPopEntrySList(ctx->work));
 
   DeleteCriticalSection(&ctx->lock);
 
@@ -301,40 +309,36 @@ cubeb_destroy(cubeb * ctx)
     CloseHandle(ctx->event);
   }
 
   _aligned_free(ctx->work);
 
   free(ctx);
 }
 
-int
-cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+static void winmm_stream_destroy(cubeb_stream * stm);
+
+static int
+winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                   cubeb_stream_params stream_params, unsigned int latency,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback,
                   void * user_ptr)
 {
   MMRESULT r;
   WAVEFORMATEXTENSIBLE wfx;
   cubeb_stream * stm;
   int i;
   size_t bufsz;
 
   assert(context);
   assert(stream);
 
   *stream = NULL;
 
-  if (stream_params.rate < 1 || stream_params.rate > 192000 ||
-      stream_params.channels < 1 || stream_params.channels > 32 ||
-      latency < 1 || latency > 2000) {
-    return CUBEB_ERROR_INVALID_FORMAT;
-  }
-
   memset(&wfx, 0, sizeof(wfx));
   if (stream_params.channels > 2) {
     wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
     wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);
   } else {
     wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
     if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) {
       wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
@@ -397,60 +401,60 @@ cubeb_stream_init(cubeb * context, cubeb
   assert(bufsz % bytes_per_frame(stm->params) == 0);
 
   stm->buffer_size = bufsz;
 
   InitializeCriticalSection(&stm->lock);
 
   stm->event = CreateEvent(NULL, FALSE, FALSE, NULL);
   if (!stm->event) {
-    cubeb_stream_destroy(stm);
+    winmm_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
-  /* cubeb_buffer_callback will be called during waveOutOpen, so all
+  /* winmm_buffer_callback will be called during waveOutOpen, so all
      other initialization must be complete before calling it. */
   r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
-                  (DWORD_PTR) cubeb_buffer_callback, (DWORD_PTR) stm,
+                  (DWORD_PTR) winmm_buffer_callback, (DWORD_PTR) stm,
                   CALLBACK_FUNCTION);
   if (r != MMSYSERR_NOERROR) {
-    cubeb_stream_destroy(stm);
+    winmm_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   r = waveOutPause(stm->waveout);
   if (r != MMSYSERR_NOERROR) {
-    cubeb_stream_destroy(stm);
+    winmm_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   for (i = 0; i < NBUFS; ++i) {
     WAVEHDR * hdr = &stm->buffers[i];
 
     hdr->lpData = calloc(1, bufsz);
     assert(hdr->lpData);
     hdr->dwBufferLength = bufsz;
     hdr->dwFlags = 0;
 
     r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr));
     if (r != MMSYSERR_NOERROR) {
-      cubeb_stream_destroy(stm);
+      winmm_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
-    cubeb_refill_stream(stm);
+    winmm_refill_stream(stm);
   }
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
-void
-cubeb_stream_destroy(cubeb_stream * stm)
+static void
+winmm_stream_destroy(cubeb_stream * stm)
 {
   DWORD rv;
   int i;
   int enqueued;
 
   if (stm->waveout) {
     EnterCriticalSection(&stm->lock);
     stm->shutdown = 1;
@@ -496,54 +500,54 @@ cubeb_stream_destroy(cubeb_stream * stm)
   EnterCriticalSection(&stm->context->lock);
   assert(stm->context->active_streams >= 1);
   stm->context->active_streams -= 1;
   LeaveCriticalSection(&stm->context->lock);
 
   free(stm);
 }
 
-int
-cubeb_stream_start(cubeb_stream * stm)
+static int
+winmm_stream_start(cubeb_stream * stm)
 {
   MMRESULT r;
 
   EnterCriticalSection(&stm->lock);
   r = waveOutRestart(stm->waveout);
   LeaveCriticalSection(&stm->lock);
 
   if (r != MMSYSERR_NOERROR) {
     return CUBEB_ERROR;
   }
 
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
 
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_stop(cubeb_stream * stm)
+static int
+winmm_stream_stop(cubeb_stream * stm)
 {
   MMRESULT r;
 
   EnterCriticalSection(&stm->lock);
   r = waveOutPause(stm->waveout);
   LeaveCriticalSection(&stm->lock);
 
   if (r != MMSYSERR_NOERROR) {
     return CUBEB_ERROR;
   }
 
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
 
   return CUBEB_OK;
 }
 
-int
-cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+static int
+winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   MMRESULT r;
   MMTIME time;
 
   EnterCriticalSection(&stm->lock);
   time.wType = TIME_SAMPLES;
   r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
   LeaveCriticalSection(&stm->lock);
@@ -552,8 +556,18 @@ cubeb_stream_get_position(cubeb_stream *
     return CUBEB_ERROR;
   }
 
   *position = time.u.sample;
 
   return CUBEB_OK;
 }
 
+static struct cubeb_ops const winmm_ops = {
+  /*.init =*/ winmm_init,
+  /*.get_backend_id =*/ winmm_get_backend_id,
+  /*.destroy =*/ winmm_destroy,
+  /*.stream_init =*/ winmm_stream_init,
+  /*.stream_destroy =*/ winmm_stream_destroy,
+  /*.stream_start =*/ winmm_stream_start,
+  /*.stream_stop =*/ winmm_stream_stop,
+  /*.stream_get_position =*/ winmm_stream_get_position
+};
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -1,12 +1,14 @@
 # Usage: sh update.sh <upstream_src_directory>
 set -e
 
 cp $1/include/cubeb/cubeb.h include
+cp $1/src/cubeb.c src
+cp $1/src/cubeb-internal.h src
 cp $1/src/cubeb_alsa.c src
 cp $1/src/cubeb_winmm.c src
 cp $1/src/cubeb_audiounit.c src
 cp $1/src/cubeb_pulse.c src
 cp $1/src/cubeb_sndio.c src
 cp $1/src/cubeb_opensl.c src
 cp $1/LICENSE .
 cp $1/README .