Bug 1311346 - Update libcubeb to revision 9eacd3144. r=kinetik
authorPaul Adenot <paul@paul.cx>
Thu, 20 Oct 2016 16:01:01 +0200
changeset 345534 657b9462cbec8db4578b29f456458d165ddc131b
parent 345533 6b3dc11025136983d14bff43dc3b483fc25832d9
child 345535 2b6efa007b233d0c48f1531f7f0f550989c694c3
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1311346
milestone52.0a1
Bug 1311346 - Update libcubeb to revision 9eacd3144. r=kinetik
media/libcubeb/README.md
media/libcubeb/README_MOZILLA
media/libcubeb/include/cubeb.h
media/libcubeb/include/moz.build
media/libcubeb/src/cubeb-internal.h
media/libcubeb/src/cubeb.c
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_panner.cpp
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_resampler.cpp
media/libcubeb/src/cubeb_utils.h
media/libcubeb/src/cubeb_utils_unix.h
media/libcubeb/src/cubeb_utils_win.h
media/libcubeb/src/cubeb_wasapi.cpp
media/libcubeb/tests/test_resampler.cpp
media/libcubeb/tests/test_sanity.cpp
media/libcubeb/update.sh
--- a/media/libcubeb/README.md
+++ b/media/libcubeb/README.md
@@ -1,5 +1,5 @@
 [![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb)
 
-See INSTALL for build instructions.
+See INSTALL.md for build instructions.
 
 Licensed under an ISC-style license.  See LICENSE for details.
--- 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 a317ba01a7b9afa0ba31b77ab9419244ebc4cf23.
+The git commit ID used was 9eacd3144b62ce2356c66c3260c9bc4151274192.
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -3,16 +3,17 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382)
 #define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
 
 #include <stdint.h>
+#include "cubeb_export.h"
 
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
 /** @mainpage
 
     @section intro Introduction
@@ -163,16 +164,23 @@ typedef enum {
     CUBEB_STREAM_TYPE_MAX
 } cubeb_stream_type;
 #endif
 
 /** An opaque handle used to refer a particular input or output device
  *  across calls. */
 typedef void * cubeb_devid;
 
+/** Level (verbosity) of logging for a particular cubeb context. */
+typedef enum {
+  CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
+  CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
+  CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
+} cubeb_log_level;
+
 /** Stream format initialization parameters. */
 typedef struct {
   cubeb_sample_format format; /**< Requested sample format.  One of
                                    #cubeb_sample_format. */
   unsigned int rate;          /**< Requested sample rate.  Valid range is [1000, 192000]. */
   unsigned int channels;      /**< Requested channel count.  Valid range is [1, 8]. */
 #if defined(__ANDROID__)
   cubeb_stream_type stream_type; /**< Used to map Android audio stream types */
@@ -333,69 +341,72 @@ typedef void (* cubeb_device_changed_cal
 
 /**
  * User supplied callback called when the underlying device collection changed.
  * @param context A pointer to the cubeb context.
  * @param user_ptr The pointer passed to cubeb_stream_init. */
 typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
                                                           void * user_ptr);
 
+/** User supplied callback called when a message needs logging. */
+typedef void (* cubeb_log_callback)(const char * fmt, ...);
+
 /** Initialize an application context.  This will perform any library or
     application scoped initialization.
     @param context A out param where an opaque pointer to the application
                    context will be returned.
     @param context_name A name for the context. Depending on the platform this
                         can appear in different locations.
     @retval CUBEB_OK in case of success.
     @retval CUBEB_ERROR in case of error, for example because the host
                         has no audio hardware. */
-int cubeb_init(cubeb ** context, char const * context_name);
+CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name);
 
 /** Get a read-only string identifying this context's current backend.
     @param context A pointer to the cubeb context.
     @retval Read-only string identifying current backend. */
-char const * cubeb_get_backend_id(cubeb * context);
+CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
 
 /** Get the maximum possible number of channels.
     @param context A pointer to the cubeb context.
     @param max_channels The maximum number of channels.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED
     @retval CUBEB_ERROR */
-int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
+CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
 
 /** Get the minimal latency value, in frames, that is guaranteed to work
     when creating a stream for the specified sample rate. This is platform,
     hardware and backend dependant.
     @param context A pointer to the cubeb context.
     @param params On some backends, the minimum achievable latency depends on
                   the characteristics of the stream.
     @param latency_frames The latency value, in frames, to pass to
                           cubeb_stream_init.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_get_min_latency(cubeb * context,
-                          cubeb_stream_params params,
-                          uint32_t * latency_frames);
+CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
+                                       cubeb_stream_params params,
+                                       uint32_t * latency_frames);
 
 /** Get the preferred sample rate for this backend: this is hardware and
     platform dependant, and can avoid resampling, and/or trigger fastpaths.
     @param context A pointer to the cubeb context.
     @param rate The samplerate (in Hz) the current configuration prefers.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
+CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
 
 /** Destroy an application context. This must be called after all stream have
  *  been destroyed.
     @param context A pointer to the cubeb context.*/
-void cubeb_destroy(cubeb * context);
+CUBEB_EXPORT void cubeb_destroy(cubeb * context);
 
 /** Initialize a stream associated with the supplied application context.
     @param context A pointer to the cubeb context.
     @param stream An out parameter to be filled with the an opaque pointer to a
                   cubeb stream.
     @param stream_name A name for this stream.
     @param input_device Device for the input side of the stream. If NULL the
                         default input device is used.
@@ -411,149 +422,160 @@ void cubeb_destroy(cubeb * context);
                          started by cubeb_stream_start.
     @param state_callback A pointer to a state callback.
     @param user_ptr A pointer that will be passed to the callbacks. This pointer
                     must outlive the life time of the stream.
     @retval CUBEB_OK
     @retval CUBEB_ERROR
     @retval CUBEB_ERROR_INVALID_FORMAT
     @retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
-int cubeb_stream_init(cubeb * context,
-                      cubeb_stream ** stream,
-                      char const * stream_name,
-                      cubeb_devid input_device,
-                      cubeb_stream_params * input_stream_params,
-                      cubeb_devid output_device,
-                      cubeb_stream_params * output_stream_params,
-                      unsigned int latency_frames,
-                      cubeb_data_callback data_callback,
-                      cubeb_state_callback state_callback,
-                      void * user_ptr);
+CUBEB_EXPORT int cubeb_stream_init(cubeb * context,
+                                   cubeb_stream ** stream,
+                                   char const * stream_name,
+                                   cubeb_devid input_device,
+                                   cubeb_stream_params * input_stream_params,
+                                   cubeb_devid output_device,
+                                   cubeb_stream_params * output_stream_params,
+                                   unsigned int latency_frames,
+                                   cubeb_data_callback data_callback,
+                                   cubeb_state_callback state_callback,
+                                   void * user_ptr);
 
 /** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
     stream.
     @param stream The stream to destroy. */
-void cubeb_stream_destroy(cubeb_stream * stream);
+CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream);
 
 /** Start playback.
     @param stream
     @retval CUBEB_OK
     @retval CUBEB_ERROR */
-int cubeb_stream_start(cubeb_stream * stream);
+CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
 
 /** Stop playback.
     @param stream
     @retval CUBEB_OK
     @retval CUBEB_ERROR */
-int cubeb_stream_stop(cubeb_stream * stream);
+CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
 
 /** Get the current stream playback position.
     @param stream
     @param position Playback position in frames.
     @retval CUBEB_OK
     @retval CUBEB_ERROR */
-int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
+CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
 
 /** Get the latency for this stream, in frames. This is the number of frames
     between the time cubeb acquires the data in the callback and the listener
     can hear the sound.
     @param stream
     @param latency Current approximate stream latency in frames.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_NOT_SUPPORTED
     @retval CUBEB_ERROR */
-int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
+CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
 
 /** Set the volume for a stream.
     @param stream the stream for which to adjust the volume.
     @param volume a float between 0.0 (muted) and 1.0 (maximum volume)
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or
             stream is an invalid pointer
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
+CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
 
 /** If the stream is stereo, set the left/right panning. If the stream is mono,
     this has no effect.
     @param stream the stream for which to change the panning
     @param panning a number from -1.0 to 1.0. -1.0 means that the stream is
            fully mixed in the left channel, 1.0 means the stream is fully
            mixed in the right channel. 0.0 is equal power in the right and
            left channel (default).
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER if stream is null or if panning is
             outside the [-1.0, 1.0] range.
     @retval CUBEB_ERROR_NOT_SUPPORTED
     @retval CUBEB_ERROR stream is not mono nor stereo */
-int cubeb_stream_set_panning(cubeb_stream * stream, float panning);
+CUBEB_EXPORT int cubeb_stream_set_panning(cubeb_stream * stream, float panning);
 
 /** Get the current output device for this stream.
     @param stm the stream for which to query the current output device
     @param device a pointer in which the current output device will be stored.
     @retval CUBEB_OK in case of success
     @retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
             invalid pointers
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_stream_get_current_device(cubeb_stream * stm,
-                                    cubeb_device ** const device);
+CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
+                                                 cubeb_device ** const device);
 
 /** Destroy a cubeb_device structure.
     @param stream the stream passed in cubeb_stream_get_current_device
     @param devices the devices to destroy
     @retval CUBEB_OK in case of success
     @retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_stream_device_destroy(cubeb_stream * stream,
-                                cubeb_device * devices);
+CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
+                                             cubeb_device * devices);
 
 /** Set a callback to be notified when the output device changes.
     @param stream the stream for which to set the callback.
     @param device_changed_callback a function called whenever the device has
            changed. Passing NULL allow to unregister a function
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
             device_changed_callback are invalid pointers.
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
-                                                  cubeb_device_changed_callback device_changed_callback);
+CUBEB_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
+                                                               cubeb_device_changed_callback device_changed_callback);
 
 /** Returns enumerated devices.
     @param context
     @param devtype device type to include
     @param collection output collection. Must be destroyed with cubeb_device_collection_destroy
     @retval CUBEB_OK in case of success
     @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_enumerate_devices(cubeb * context,
-                            cubeb_device_type devtype,
-                            cubeb_device_collection ** collection);
+CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
+                                         cubeb_device_type devtype,
+                                         cubeb_device_collection ** collection);
 
 /** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
     @param collection collection to destroy
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
-int cubeb_device_collection_destroy(cubeb_device_collection * collection);
+CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb_device_collection * collection);
 
 /** Destroy a cubeb_device_info structure.
     @param info pointer to device info structure
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER if info is an invalid pointer */
-int cubeb_device_info_destroy(cubeb_device_info * info);
+CUBEB_EXPORT int cubeb_device_info_destroy(cubeb_device_info * info);
 
 /** Registers a callback which is called when the system detects
     a new device or a device is removed.
     @param context
     @param devtype device type to include
     @param callback a function called whenever the system device list changes.
            Passing NULL allow to unregister a function
     @param user_ptr pointer to user specified data which will be present in
            subsequent callbacks.
     @retval CUBEB_ERROR_NOT_SUPPORTED */
-int cubeb_register_device_collection_changed(cubeb * context,
-                                       cubeb_device_type devtype,
-                                       cubeb_device_collection_changed_callback callback,
-                                       void * user_ptr);
+CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
+                                                          cubeb_device_type devtype,
+                                                          cubeb_device_collection_changed_callback callback,
+                                                          void * user_ptr);
+
+/** Set a callback to be called with a message.
+    @param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
+    @param log_callback A function called with a message when there is
+                        something to log. Pass NULL to unregister.
+    @retval CUBEB_OK in case of success.
+    @retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
+                                          invalid pointers, or if level is not
+                                          in cubeb_log_level. */
+CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
+                                        cubeb_log_callback log_callback);
 
 #if defined(__cplusplus)
 }
 #endif
 
 #endif /* CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 */
--- a/media/libcubeb/include/moz.build
+++ b/media/libcubeb/include/moz.build
@@ -1,10 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.cubeb += [
     'cubeb.h',
+    'cubeb_export.h'
 ]
 
--- a/media/libcubeb/src/cubeb-internal.h
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -3,16 +3,17 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5)
 #define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
 
 #include "cubeb/cubeb.h"
+#include "cubeb_log.h"
 #include <stdio.h>
 #include <string.h>
 
 #ifdef __clang__
 #ifndef CLANG_ANALYZER_NORETURN
 #if __has_feature(attribute_analyzer_noreturn)
 #define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
 #else
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -3,24 +3,24 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
 #include <assert.h>
 #include <stddef.h>
 #include <stdlib.h>
-#if defined(HAVE_CONFIG_H)
-#include "config.h"
-#endif
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 
 #define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
 
+cubeb_log_level g_log_level;
+cubeb_log_callback g_log_callback;
+
 struct cubeb {
   struct cubeb_ops * ops;
 };
 
 struct cubeb_stream {
   struct cubeb * context;
 };
 
@@ -51,17 +51,17 @@ int opensl_init(cubeb ** context, char c
 #if defined(USE_AUDIOTRACK)
 int audiotrack_init(cubeb ** context, char const * context_name);
 #endif
 #if defined(USE_KAI)
 int kai_init(cubeb ** context, char const * context_name);
 #endif
 
 
-int
+static int
 validate_stream_params(cubeb_stream_params * input_stream_params,
                        cubeb_stream_params * output_stream_params)
 {
   XASSERT(input_stream_params || output_stream_params);
   if (output_stream_params) {
     if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
         output_stream_params->channels < 1 || output_stream_params->channels > 8) {
       return CUBEB_ERROR_INVALID_FORMAT;
@@ -93,17 +93,17 @@ validate_stream_params(cubeb_stream_para
     return CUBEB_OK;
   }
 
   return CUBEB_ERROR_INVALID_FORMAT;
 }
 
 
 
-int
+static int
 validate_latency(int latency)
 {
   if (latency < 1 || latency > 96000) {
     return CUBEB_ERROR_INVALID_PARAMETER;
   }
   return CUBEB_OK;
 }
 
@@ -437,15 +437,36 @@ int cubeb_register_device_collection_cha
 
   if (!context->ops->register_device_collection_changed) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
   return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
 }
 
-void cubeb_crash()
+int cubeb_set_log_callback(cubeb_log_level log_level,
+                           cubeb_log_callback log_callback)
+{
+  if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  if (g_log_callback && log_callback) {
+    return CUBEB_ERROR_NOT_SUPPORTED;
+  }
+
+  g_log_callback = log_callback;
+  g_log_level = log_level;
+
+  return CUBEB_OK;
+}
+
+void
+cubeb_crash()
 {
   abort();
   *((volatile int *) NULL) = 0;
 }
 
-
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -637,21 +637,27 @@ alsa_unregister_stream(cubeb_stream * st
   }
   pthread_mutex_unlock(&ctx->mutex);
 }
 
 static void
 silent_error_handler(char const * file, int line, char const * function,
                      int err, char const * fmt, ...)
 {
+  (void)file;
+  (void)line;
+  (void)function;
+  (void)err;
+  (void)fmt;
 }
 
 /*static*/ int
 alsa_init(cubeb ** context, char const * context_name)
 {
+  (void)context_name;
   cubeb * ctx;
   int r;
   int i;
   int fd[2];
   pthread_attr_t attr;
   snd_pcm_t * dummy;
 
   assert(context);
@@ -727,16 +733,17 @@ alsa_init(cubeb ** context, char const *
   *context = ctx;
 
   return CUBEB_OK;
 }
 
 static char const *
 alsa_get_backend_id(cubeb * ctx)
 {
+  (void)ctx;
   return "alsa";
 }
 
 static void
 alsa_destroy(cubeb * ctx)
 {
   int r;
 
@@ -771,16 +778,17 @@ alsa_stream_init(cubeb * ctx, cubeb_stre
                  cubeb_devid input_device,
                  cubeb_stream_params * input_stream_params,
                  cubeb_devid output_device,
                  cubeb_stream_params * output_stream_params,
                  unsigned int latency_frames,
                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                  void * user_ptr)
 {
+  (void)stream_name;
   cubeb_stream * stm;
   int r;
   snd_pcm_format_t format;
   snd_pcm_uframes_t period_size;
   int latency_us = 0;
 
 
   assert(ctx && stream);
@@ -956,16 +964,17 @@ alsa_get_max_channel_count(cubeb * ctx, 
 
   alsa_stream_destroy(stm);
 
   return CUBEB_OK;
 }
 
 static int
 alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
+  (void)ctx;
   int r, dir;
   snd_pcm_t * pcm;
   snd_pcm_hw_params_t * hw_params;
 
   snd_pcm_hw_params_alloca(&hw_params);
 
   /* get a pcm, disabling resampling, so we get a rate the
    * hardware/dmix/pulse/etc. supports. */
@@ -999,16 +1008,17 @@ alsa_get_preferred_sample_rate(cubeb * c
   snd_pcm_close(pcm);
 
   return CUBEB_OK;
 }
 
 static int
 alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
+  (void)ctx;
   /* 40ms is found to be an acceptable minimum, even on a super low-end
    * machine. */
   *latency_frames = 40 * params.rate / 1000;
 
   return CUBEB_OK;
 }
 
 static int
@@ -1085,32 +1095,32 @@ alsa_stream_get_position(cubeb_stream * 
   }
 
   stm->last_position = *position;
 
   pthread_mutex_unlock(&stm->mutex);
   return CUBEB_OK;
 }
 
-int
+static int
 alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
   snd_pcm_sframes_t delay;
   /* This function returns the delay in frames until a frame written using
      snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
   if (snd_pcm_delay(stm->pcm, &delay)) {
     return CUBEB_ERROR;
   }
 
   *latency = delay;
 
   return CUBEB_OK;
 }
 
-int
+static int
 alsa_stream_set_volume(cubeb_stream * stm, float volume)
 {
   /* setting the volume using an API call does not seem very stable/supported */
   pthread_mutex_lock(&stm->mutex);
   stm->volume = volume;
   pthread_mutex_unlock(&stm->mutex);
 
   return CUBEB_OK;
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -49,46 +49,37 @@
 typedef UInt32  AudioFormatFlags;
 #endif
 
 #define CUBEB_STREAM_MAX 8
 
 #define AU_OUT_BUS    0
 #define AU_IN_BUS     1
 
-//#define LOGGING_ENABLED
-#ifdef LOGGING_ENABLED
-#define LOG(...) do {                           \
-    fprintf(stderr, __VA_ARGS__);               \
-    fprintf(stderr, "(line: %d)\n", __LINE__);  \
-  } while(0)
-#else
-#define LOG(...)
-#endif
-
-#ifdef LOGGING_ENABLED
 #define PRINT_ERROR_CODE(str, r) do {                                \
-    fprintf(stderr, "Error %s (line: %d) (%d)\n", str, __LINE__, r); \
-  } while (0)
-#else
-#define PRINT_ERROR_CODE(str, r)
-#endif
+  LOG("System call failed: %s (rv: %d)", str, r);                    \
+} while(0)
 
 /* Testing empirically, some headsets report a minimal latency that is very
  * low, but this does not work in practice. Lie and say the minimum is 256
  * frames. */
 const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
 
+void audiounit_stream_stop_internal(cubeb_stream * stm);
+void audiounit_stream_start_internal(cubeb_stream * stm);
+static void close_audiounit_stream(cubeb_stream * stm);
+static int setup_audiounit_stream(cubeb_stream * stm);
+
 extern cubeb_ops const audiounit_ops;
 
 struct cubeb {
   cubeb_ops const * ops;
   owned_critical_section mutex;
-  int active_streams;
+  std::atomic<int> active_streams;
   int limit_streams;
   cubeb_device_collection_changed_callback collection_changed_callback;
   void * collection_changed_user_ptr;
   /* Differentiate input from output devices. */
   cubeb_device_type collection_changed_devtype;
   uint32_t devtype_device_count;
   AudioObjectID * devtype_device_array;
 };
@@ -102,94 +93,135 @@ public:
   {assert((float_ar && !short_ar) || (!float_ar && short_ar));}
 
   explicit auto_array_wrapper(auto_array<short> * ar)
   : float_ar(nullptr)
   , short_ar(ar)
   {assert((float_ar && !short_ar) || (!float_ar && short_ar));}
 
   ~auto_array_wrapper() {
+    auto_lock l(lock);
     assert((float_ar && !short_ar) || (!float_ar && short_ar));
     delete float_ar;
     delete short_ar;
   }
 
   void push(void * elements, size_t length){
     assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    auto_lock l(lock);
     if (float_ar)
       return float_ar->push(static_cast<float*>(elements), length);
     return short_ar->push(static_cast<short*>(elements), length);
   }
 
-  size_t length() const {
+  size_t length() {
     assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    auto_lock l(lock);
     if (float_ar)
       return float_ar->length();
     return short_ar->length();
   }
 
   void push_silence(size_t length) {
     assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    auto_lock l(lock);
     if (float_ar)
       return float_ar->push_silence(length);
     return short_ar->push_silence(length);
   }
 
   bool pop(void * elements, size_t length) {
     assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    auto_lock l(lock);
     if (float_ar)
       return float_ar->pop(static_cast<float*>(elements), length);
     return short_ar->pop(static_cast<short*>(elements), length);
   }
 
-  void * data() const {
+  void * data() {
     assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    auto_lock l(lock);
     if (float_ar)
       return float_ar->data();
     return short_ar->data();
   }
 
+  void clear() {
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    auto_lock l(lock);
+    if (float_ar) {
+      float_ar->clear();
+    } else {
+      short_ar->clear();
+    }
+  }
+
 private:
   auto_array<float> * float_ar;
   auto_array<short> * short_ar;
+  owned_critical_section lock;
 };
 
 struct cubeb_stream {
   cubeb * context;
   cubeb_data_callback data_callback;
   cubeb_state_callback state_callback;
   cubeb_device_changed_callback device_changed_callback;
+  /* Stream creation parameters */
+  cubeb_stream_params input_stream_params;
+  cubeb_stream_params output_stream_params;
+  cubeb_devid input_device;
+  cubeb_devid output_device;
   /* User pointer of data_callback */
   void * user_ptr;
   /* Format descriptions */
   AudioStreamBasicDescription input_desc;
   AudioStreamBasicDescription output_desc;
   /* I/O AudioUnits */
   AudioUnit input_unit;
   AudioUnit output_unit;
   /* Sample rate of input device*/
   Float64 input_hw_rate;
   owned_critical_section mutex;
   /* Hold the input samples in every
    * input callback iteration */
   auto_array_wrapper * input_linear_buffer;
   /* Frames on input buffer */
-  uint32_t input_buffer_frames;
+  std::atomic<uint32_t> input_buffer_frames;
   /* Frame counters */
   uint64_t frames_played;
   uint64_t frames_queued;
-  uint64_t frames_read;
-  int shutdown;
-  int draining;
-  uint64_t current_latency_frames;
+  std::atomic<int64_t> frames_read;
+  std::atomic<bool> shutdown;
+  std::atomic<bool> draining;
+  /* Latency requested by the user. */
+  uint64_t latency_frames;
+  std::atomic<uint64_t> current_latency_frames;
   uint64_t hw_latency_frames;
   std::atomic<float> panning;
   cubeb_resampler * resampler;
+  /* This is the number of output callback we got in a row. This is usually one,
+   * but can be two when the input and output rate are different, and more when
+   * a device has been plugged or unplugged, as there can be some time before
+   * the device is ready. */
+  std::atomic<int> output_callback_in_a_row;
+  /* This is true if a device change callback is currently running.  */
+  std::atomic<bool> switching_device;
 };
 
+bool has_input(cubeb_stream * stm)
+{
+  return stm->input_stream_params.rate != 0;
+}
+
+bool has_output(cubeb_stream * stm)
+{
+  return stm->output_stream_params.rate != 0;
+}
+
 #if TARGET_OS_IPHONE
 typedef UInt32 AudioDeviceID;
 typedef UInt32 AudioObjectID;
 
 #define AudioGetCurrentHostTime mach_absolute_time
 
 uint64_t
 AudioConvertHostTimeToNanos(uint64_t host_time)
@@ -257,21 +289,21 @@ audiounit_render_input(cubeb_stream * st
     audiounit_make_silent(&input_buffer_list.mBuffers[0]);
     return r;
   }
 
   /* Copy input data in linear buffer. */
   stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
                                  input_frames * stm->input_desc.mChannelsPerFrame);
 
-  LOG("- input:  buffers %d, size %d, channels %d, frames %d.",
-      input_buffer_list.mNumberBuffers,
-      input_buffer_list.mBuffers[0].mDataByteSize,
-      input_buffer_list.mBuffers[0].mNumberChannels,
-      input_frames);
+  LOGV("input:  buffers %d, size %d, channels %d, frames %d.",
+       input_buffer_list.mNumberBuffers,
+       input_buffer_list.mBuffers[0].mDataByteSize,
+       input_buffer_list.mBuffers[0].mNumberChannels,
+       input_frames);
 
   /* Advance input frame counter. */
   assert(input_frames > 0);
   stm->frames_read += input_frames;
 
   return noErr;
 }
 
@@ -281,51 +313,60 @@ audiounit_input_callback(void * user_ptr
                          AudioTimeStamp const * tstamp,
                          UInt32 bus,
                          UInt32 input_frames,
                          AudioBufferList * bufs)
 {
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
   long outframes, frames;
 
-  auto_lock lock(stm->mutex);
-
   assert(stm->input_unit != NULL);
   assert(AU_IN_BUS == bus);
 
   if (stm->shutdown) {
-    LOG("- input shutdown");
+    LOG("input shutdown");
     return noErr;
   }
 
   OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
   if (r != noErr) {
     return r;
   }
 
   // Full Duplex. We'll call data_callback in the AudioUnit output callback.
   if (stm->output_unit != NULL) {
-    // User callback will be called by output callback
+    // This happens when we're finally getting a new input callback after having
+    // switched device, we can clear the input buffer now, only keeping the data
+    // we just got.
+    if (stm->output_callback_in_a_row > 2) {
+      stm->input_linear_buffer->pop(
+          nullptr,
+          stm->input_linear_buffer->length() -
+          input_frames * stm->input_stream_params.channels);
+    }
+
+    stm->output_callback_in_a_row = 0;
+
     return noErr;
   }
 
   /* Input only. Call the user callback through resampler.
      Resampler will deliver input buffer in the correct rate. */
-  frames = input_frames;
   assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
+  long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
   outframes = cubeb_resampler_fill(stm->resampler,
                                    stm->input_linear_buffer->data(),
-                                   &frames,
+                                   &total_input_frames,
                                    NULL,
                                    0);
   // Reset input buffer
-  stm->input_linear_buffer->pop(nullptr, frames * stm->input_desc.mChannelsPerFrame);
+  stm->input_linear_buffer->clear();
 
   if (outframes < 0 || outframes != input_frames) {
-    stm->shutdown = 1;
+    stm->shutdown = true;
     return noErr;
   }
 
   return noErr;
 }
 
 static OSStatus
 audiounit_output_callback(void * user_ptr,
@@ -335,27 +376,27 @@ audiounit_output_callback(void * user_pt
                           UInt32 output_frames,
                           AudioBufferList * outBufferList)
 {
   assert(AU_OUT_BUS == bus);
   assert(outBufferList->mNumberBuffers == 1);
 
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
 
-  LOG("- output(%p): buffers %d, size %d, channels %d, frames %d.", stm,
-      outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
-      outBufferList->mBuffers[0].mNumberChannels, output_frames);
+  stm->output_callback_in_a_row++;
+
+  LOGV("output(%p): buffers %d, size %d, channels %d, frames %d.", stm,
+       outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
+       outBufferList->mBuffers[0].mNumberChannels, output_frames);
 
   long outframes = 0, input_frames = 0;
   void * output_buffer = NULL, * input_buffer = NULL;
 
-  auto_lock lock(stm->mutex);
-
   if (stm->shutdown) {
-    LOG("- output shutdown.");
+    LOG("output shutdown.");
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
 
   stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
   if (stm->draining) {
     OSStatus r = AudioOutputUnitStop(stm->output_unit);
     assert(r == 0);
@@ -366,26 +407,27 @@ audiounit_output_callback(void * user_pt
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
   /* Get output buffer. */
   output_buffer = outBufferList->mBuffers[0].mData;
   /* If Full duplex get also input buffer */
   if (stm->input_unit != NULL) {
-    /* Output callback came first */
-    if (stm->frames_read == 0) {
+    /* If the output callback came first and this is a duplex stream, we need to
+     * fill in some additional silence in the resampler.
+     * Otherwise, if we had more than two callback in a row, or we're currently
+     * switching, we add some silence as well to compensate for the fact that
+     * we're lacking some input data. */
+    if (stm->frames_read == 0 ||
+        (stm->input_linear_buffer->length() == 0 &&
+        (stm->output_callback_in_a_row > 2 || stm->switching_device))) {
       LOG("Output callback came first send silent.");
       stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
-                                            stm->input_desc.mChannelsPerFrame);
-    }
-    /* Input samples stored previously in input callback. */
-    if (stm->input_linear_buffer->length() == 0) {
-      /* Do nothing, there should be enough pre-buffered data to consume. */
-      LOG("Input hole. Requested more input than ouput.");
+                                             stm->input_desc.mChannelsPerFrame);
     }
     // The input buffer
     input_buffer = stm->input_linear_buffer->data();
     // Number of input frames in the buffer
     input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
   }
 
   /* Call user callback through resampler. */
@@ -395,17 +437,17 @@ audiounit_output_callback(void * user_pt
                                    output_buffer,
                                    output_frames);
 
   if (input_buffer) {
     stm->input_linear_buffer->pop(nullptr, input_frames * stm->input_desc.mChannelsPerFrame);
   }
 
   if (outframes < 0) {
-    stm->shutdown = 1;
+    stm->shutdown = true;
     return noErr;
   }
 
   size_t outbpf = stm->output_desc.mBytesPerFrame;
   stm->draining = outframes < output_frames;
   stm->frames_played = stm->frames_queued;
   stm->frames_queued += outframes;
 
@@ -518,32 +560,79 @@ audiounit_get_input_device_id(AudioDevic
 }
 
 static OSStatus
 audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
                                      const AudioObjectPropertyAddress * addresses,
                                      void * user)
 {
   cubeb_stream * stm = (cubeb_stream*) user;
+  int rv;
+  bool was_running = false;
+
+  stm->switching_device = true;
+
+  // Note if the stream was running or not
+  was_running = !stm->shutdown;
+
+  LOG("Audio device changed, %d events.", address_count);
+  if (g_log_level) {
+    for (UInt32 i = 0; i < address_count; i++) {
+      switch(addresses[i].mSelector) {
+        case kAudioHardwarePropertyDefaultOutputDevice:
+          LOG("%d mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
+          break;
+        case kAudioHardwarePropertyDefaultInputDevice:
+          LOG("%d mSelector == kAudioHardwarePropertyDefaultInputDevice", i);
+          break;
+        case kAudioDevicePropertyDataSource:
+          LOG("%d mSelector == kAudioHardwarePropertyDataSource", i);
+          break;
+      }
+    }
+  }
 
   for (UInt32 i = 0; i < address_count; i++) {
     switch(addresses[i].mSelector) {
     case kAudioHardwarePropertyDefaultOutputDevice:
     case kAudioHardwarePropertyDefaultInputDevice:
       /* fall through */
     case kAudioDevicePropertyDataSource: {
         auto_lock lock(stm->mutex);
         if (stm->device_changed_callback) {
           stm->device_changed_callback(stm->user_ptr);
         }
         break;
       }
     }
   }
 
+  // This means the callback won't be called again.
+  audiounit_stream_stop_internal(stm);
+
+  {
+    auto_lock lock(stm->mutex);
+    close_audiounit_stream(stm);
+    rv = setup_audiounit_stream(stm);
+    if (rv != CUBEB_OK) {
+      LOG("Could not reopen a stream after switching.");
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+      return noErr;
+    }
+
+    stm->frames_read = 0;
+
+    // If the stream was running, start it again.
+    if (was_running) {
+      audiounit_stream_start_internal(stm);
+    }
+  }
+
+  stm->switching_device = false;
+
   return noErr;
 }
 
 OSStatus
 audiounit_add_listener(cubeb_stream * stm, AudioDeviceID id, AudioObjectPropertySelector selector,
     AudioObjectPropertyScope scope, AudioObjectPropertyListenerProc listener)
 {
   AudioObjectPropertyAddress address = {
@@ -1074,439 +1163,475 @@ audiounit_clamp_latency(cubeb_stream * s
     upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
   }
 
   return std::max(std::min<uint32_t>(latency_frames, upper_latency_limit),
                   SAFE_MIN_LATENCY_FRAMES);
 }
 
 static int
-audiounit_stream_init(cubeb * context,
-                      cubeb_stream ** stream,
-                      char const * stream_name,
-                      cubeb_devid input_device,
-                      cubeb_stream_params * input_stream_params,
-                      cubeb_devid output_device,
-                      cubeb_stream_params * output_stream_params,
-                      unsigned int latency_frames,
-                      cubeb_data_callback data_callback,
-                      cubeb_state_callback state_callback,
-                      void * user_ptr)
+setup_audiounit_stream(cubeb_stream * stm)
 {
-  cubeb_stream * stm;
+  stm->mutex.assert_current_thread_owns();
+
   AudioUnit input_unit;
   AudioUnit output_unit;
   int r;
   AURenderCallbackStruct aurcbs_in;
   AURenderCallbackStruct aurcbs_out;
   UInt32 size;
 
-  assert(context);
-  *stream = NULL;
-
-  {
-    auto_lock lock(context->mutex);
-    if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
-      LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
-      return CUBEB_ERROR;
-    }
-    context->active_streams += 1;
-  }
-
-  if (input_stream_params != NULL) {
-    r = audiounit_create_unit(&input_unit, true,
-                              input_stream_params,
-                              input_device);
+  if (has_input(stm)) {
+    r = audiounit_create_unit(&stm->input_unit, true,
+                              &stm->input_stream_params,
+                              stm->input_device);
     if (r != CUBEB_OK) {
       LOG("AudioUnit creation for input failed.");
       return r;
     }
   }
 
-  if (output_stream_params != NULL) {
-    r = audiounit_create_unit(&output_unit, false,
-                              output_stream_params,
-                              output_device);
+  if (has_output(stm)) {
+    r = audiounit_create_unit(&stm->output_unit, false,
+                              &stm->output_stream_params,
+                              stm->output_device);
     if (r != CUBEB_OK) {
       LOG("AudioUnit creation for output failed.");
       return r;
     }
   }
 
-  stm = (cubeb_stream *) calloc(1, sizeof(cubeb_stream));
-  assert(stm);
-  // Placement new to call the ctors of cubeb_stream members.
-  new (stm) cubeb_stream();
-
-  /* These could be different in the future if we have both
-   * full-duplex stream and different devices for input vs output. */
-  stm->input_unit  = (input_stream_params != NULL) ? input_unit : NULL;
-  stm->output_unit = (output_stream_params != NULL) ? output_unit : NULL;
-  stm->context = context;
-  stm->data_callback = data_callback;
-  stm->state_callback = state_callback;
-  stm->user_ptr = user_ptr;
-  stm->device_changed_callback = NULL;
-
-  /* Init data members where necessary */
-  stm->hw_latency_frames = UINT64_MAX;
-
-  /* Silently clamp the latency down to the platform default, because we
-   * synthetize the clock from the callbacks, and we want the clock to update
-   * often. */
-  latency_frames = audiounit_clamp_latency(stm, latency_frames);
-  assert(latency_frames > 0);
-
   /* Setup Input Stream! */
-  if (input_stream_params != NULL) {
+  if (has_input(stm)) {
     /* Get input device sample rate. */
     AudioStreamBasicDescription input_hw_desc;
     size = sizeof(AudioStreamBasicDescription);
     r = AudioUnitGetProperty(stm->input_unit,
                             kAudioUnitProperty_StreamFormat,
                             kAudioUnitScope_Input,
                             AU_IN_BUS,
                             &input_hw_desc,
                             &size);
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
     stm->input_hw_rate = input_hw_desc.mSampleRate;
 
     /* Set format description according to the input params. */
-    r = audio_stream_desc_init(&stm->input_desc, input_stream_params);
+    r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
     if (r != CUBEB_OK) {
       LOG("Setting format description for input failed.");
-      audiounit_stream_destroy(stm);
       return r;
     }
 
     // Use latency to set buffer size
-    stm->input_buffer_frames = latency_frames;
-    LOG("Input buffer frame count %u.", stm->input_buffer_frames);
+    stm->input_buffer_frames = stm->latency_frames;
+    LOG("Input buffer frame count %u.", unsigned(stm->input_buffer_frames));
     r = AudioUnitSetProperty(stm->input_unit,
                              kAudioDevicePropertyBufferFrameSize,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &stm->input_buffer_frames,
                              sizeof(UInt32));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     AudioStreamBasicDescription src_desc = stm->input_desc;
     /* Input AudioUnit must be configured with device's sample rate.
        we will resample inside input callback. */
     src_desc.mSampleRate = stm->input_hw_rate;
 
     r = AudioUnitSetProperty(stm->input_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &src_desc,
                              sizeof(AudioStreamBasicDescription));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     /* Frames per buffer in the input callback. */
     r = AudioUnitSetProperty(stm->input_unit,
                              kAudioUnitProperty_MaximumFramesPerSlice,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &stm->input_buffer_frames,
                              sizeof(UInt32));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     // Input only capacity
     unsigned int array_capacity = 1;
-    if (output_stream_params) {
+    if (has_output(stm)) {
       // Full-duplex increase capacity
       array_capacity = 8;
     }
     if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     assert(stm->input_unit != NULL);
     aurcbs_in.inputProc = audiounit_input_callback;
     aurcbs_in.inputProcRefCon = stm;
 
     r = AudioUnitSetProperty(stm->input_unit,
                              kAudioOutputUnitProperty_SetInputCallback,
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_in,
                              sizeof(aurcbs_in));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
     LOG("Input audiounit init successfully.");
   }
 
   /* Setup Output Stream! */
-  if (output_stream_params != NULL) {
-    r = audio_stream_desc_init(&stm->output_desc, output_stream_params);
+  if (has_output(stm)) {
+    r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
     if (r != CUBEB_OK) {
       LOG("Could not initialize the audio stream description.");
-      audiounit_stream_destroy(stm);
       return r;
     }
 
     /* Get output device sample rate. */
     AudioStreamBasicDescription output_hw_desc;
     size = sizeof(AudioStreamBasicDescription);
     memset(&output_hw_desc, 0, size);
     r = AudioUnitGetProperty(stm->output_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              AU_OUT_BUS,
                              &output_hw_desc,
                              &size);
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     r = AudioUnitSetProperty(stm->output_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Input,
                              AU_OUT_BUS,
                              &stm->output_desc,
                              sizeof(AudioStreamBasicDescription));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     // Use latency to calculate buffer size
-    uint32_t output_buffer_frames = latency_frames;
+    uint32_t output_buffer_frames = stm->latency_frames;
     LOG("Output buffer frame count %u.", output_buffer_frames);
     r = AudioUnitSetProperty(stm->output_unit,
                              kAudioDevicePropertyBufferFrameSize,
                              kAudioUnitScope_Input,
                              AU_OUT_BUS,
                              &output_buffer_frames,
                              sizeof(output_buffer_frames));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     assert(stm->output_unit != NULL);
     aurcbs_out.inputProc = audiounit_output_callback;
     aurcbs_out.inputProcRefCon = stm;
     r = AudioUnitSetProperty(stm->output_unit,
                              kAudioUnitProperty_SetRenderCallback,
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_out,
                              sizeof(aurcbs_out));
     if (r != noErr) {
-      audiounit_stream_destroy(stm);
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
       return CUBEB_ERROR;
     }
     LOG("Output audiounit init successfully.");
   }
 
   // Setting the latency doesn't work well for USB headsets (eg. plantronics).
   // Keep the default latency for now.
 #if 0
   buffer_size = latency;
 
   /* Get the range of latency this particular device can work with, and clamp
    * the requested latency to this acceptable range. */
 #if !TARGET_OS_IPHONE
   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
-    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   if (buffer_size < (unsigned int) latency_range.mMinimum) {
     buffer_size = (unsigned int) latency_range.mMinimum;
   } else if (buffer_size > (unsigned int) latency_range.mMaximum) {
     buffer_size = (unsigned int) latency_range.mMaximum;
   }
 
   /**
    * Get the default buffer size. If our latency request is below the default,
    * set it. Otherwise, use the default latency.
    **/
   size = sizeof(default_buffer_size);
   if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
         kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) {
-    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   if (buffer_size < default_buffer_size) {
     /* Set the maximum number of frame that the render callback will ask for,
      * effectively setting the latency of the stream. This is process-wide. */
     if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
           kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) {
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
   }
 #else  // TARGET_OS_IPHONE
   //TODO: [[AVAudioSession sharedInstance] inputLatency]
   // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios
 #endif
 #endif
 
   /* We use a resampler because input AudioUnit operates
    * reliable only in the capture device sample rate.
    * Resampler will convert it to the user sample rate
    * and deliver it to the callback. */
   uint32_t target_sample_rate;
-  if (input_stream_params) {
-    target_sample_rate = input_stream_params->rate;
+  if (has_input(stm)) {
+    target_sample_rate = stm->input_stream_params.rate;
   } else {
-    assert(output_stream_params);
-    target_sample_rate = output_stream_params->rate;
+    assert(has_output(stm));
+    target_sample_rate = stm->output_stream_params.rate;
   }
 
   cubeb_stream_params input_unconverted_params;
-  if (input_stream_params) {
-    input_unconverted_params = *input_stream_params;
+  if (has_input(stm)) {
+    input_unconverted_params = stm->input_stream_params;
     /* Use the rate of the input device. */
     input_unconverted_params.rate = stm->input_hw_rate;
   }
 
   /* Create resampler. Output params are unchanged
    * because we do not need conversion on the output. */
   stm->resampler = cubeb_resampler_create(stm,
-                                          input_stream_params ? &input_unconverted_params : NULL,
-                                          output_stream_params,
+                                          has_input(stm) ? &input_unconverted_params : NULL,
+                                          has_output(stm) ? &stm->output_stream_params : NULL,
                                           target_sample_rate,
                                           stm->data_callback,
                                           stm->user_ptr,
                                           CUBEB_RESAMPLER_QUALITY_DESKTOP);
   if (!stm->resampler) {
     LOG("Could not create resampler.");
-    audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   if (stm->input_unit != NULL) {
     r = AudioUnitInitialize(stm->input_unit);
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitInitialize/input", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
   }
 
   if (stm->output_unit != NULL) {
     r = AudioUnitInitialize(stm->output_unit);
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitInitialize/output", r);
-      audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
   }
+  return CUBEB_OK;
+}
+
+static int
+audiounit_stream_init(cubeb * context,
+                      cubeb_stream ** stream,
+                      char const * stream_name,
+                      cubeb_devid input_device,
+                      cubeb_stream_params * input_stream_params,
+                      cubeb_devid output_device,
+                      cubeb_stream_params * output_stream_params,
+                      unsigned int latency_frames,
+                      cubeb_data_callback data_callback,
+                      cubeb_state_callback state_callback,
+                      void * user_ptr)
+{
+  cubeb_stream * stm;
+  int r;
+
+  assert(context);
+  *stream = NULL;
+
+  if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
+    LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
+    return CUBEB_ERROR;
+  }
+  context->active_streams += 1;
+
+  stm = (cubeb_stream *) calloc(1, sizeof(cubeb_stream));
+  assert(stm);
+  // Placement new to call the ctors of cubeb_stream members.
+  new (stm) cubeb_stream();
+
+  /* These could be different in the future if we have both
+   * full-duplex stream and different devices for input vs output. */
+  stm->context = context;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+  stm->device_changed_callback = NULL;
+  if (input_stream_params) {
+    stm->input_stream_params = *input_stream_params;
+    stm->input_device = input_device;
+  }
+  if (output_stream_params) {
+    stm->output_stream_params = *output_stream_params;
+    stm->output_device = output_device;
+  }
+
+  /* Init data members where necessary */
+  stm->hw_latency_frames = UINT64_MAX;
+
+  /* Silently clamp the latency down to the platform default, because we
+   * synthetize the clock from the callbacks, and we want the clock to update
+   * often. */
+  stm->latency_frames = audiounit_clamp_latency(stm, latency_frames);
+  assert(latency_frames > 0);
+
+  stm->switching_device = false;
+
+  {
+    // It's not critical to lock here, because no other thread has been started
+    // yet, but it allows to assert that the lock has been taken in
+    // `setup_audiounit_stream`.
+    auto_lock lock(stm->mutex);
+    r = setup_audiounit_stream(stm);
+  }
+
+  if (r != CUBEB_OK) {
+    LOG("Could not setup the audiounit stream.");
+    audiounit_stream_destroy(stm);
+    return r;
+  }
+
+  r = audiounit_install_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("Could not install the device change callback.");
+    return r;
+  }
 
   *stream = stm;
   LOG("Cubeb stream (%p) init successful.", stm);
   return CUBEB_OK;
 }
 
 static void
-audiounit_stream_destroy(cubeb_stream * stm)
+close_audiounit_stream(cubeb_stream * stm)
 {
-  stm->shutdown = 1;
-
-  if (stm->input_unit != NULL) {
-    AudioOutputUnitStop(stm->input_unit);
+  stm->mutex.assert_current_thread_owns();
+  if (stm->input_unit) {
     AudioUnitUninitialize(stm->input_unit);
     AudioComponentInstanceDispose(stm->input_unit);
   }
 
   audiounit_destroy_input_linear_buffer(stm);
 
-  if (stm->output_unit != NULL) {
-    AudioOutputUnitStop(stm->output_unit);
+  if (stm->output_unit) {
     AudioUnitUninitialize(stm->output_unit);
     AudioComponentInstanceDispose(stm->output_unit);
   }
 
   cubeb_resampler_destroy(stm->resampler);
+}
+
+static void
+audiounit_stream_destroy(cubeb_stream * stm)
+{
+  stm->shutdown = true;
+
+  audiounit_stream_stop_internal(stm);
+
+  {
+    auto_lock lock(stm->mutex);
+    close_audiounit_stream(stm);
+  }
 
 #if !TARGET_OS_IPHONE
-  audiounit_uninstall_device_changed_callback(stm);
+  int r = audiounit_uninstall_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("Could not uninstall the device changed callback");
+  }
 #endif
 
-  {
-    auto_lock lock(stm->context->mutex);
-    assert(stm->context->active_streams >= 1);
-    stm->context->active_streams -= 1;
-  }
+  assert(stm->context->active_streams >= 1);
+  stm->context->active_streams -= 1;
 
   stm->~cubeb_stream();
   free(stm);
 }
 
-static int
-audiounit_stream_start(cubeb_stream * stm)
+void
+audiounit_stream_start_internal(cubeb_stream * stm)
 {
-  {
-    auto_lock lock(stm->mutex);
-    stm->shutdown = 0;
-    stm->draining = 0;
-  }
-
   OSStatus r;
   if (stm->input_unit != NULL) {
     r = AudioOutputUnitStart(stm->input_unit);
     assert(r == 0);
   }
   if (stm->output_unit != NULL) {
     r = AudioOutputUnitStart(stm->output_unit);
     assert(r == 0);
   }
+}
+
+static int
+audiounit_stream_start(cubeb_stream * stm)
+{
+  stm->shutdown = false;
+  stm->draining = false;
+
+  audiounit_stream_start_internal(stm);
+
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+
   LOG("Cubeb stream (%p) started successfully.", stm);
   return CUBEB_OK;
 }
 
-static int
-audiounit_stream_stop(cubeb_stream * stm)
+void
+audiounit_stream_stop_internal(cubeb_stream * stm)
 {
-  {
-    auto_lock lock(stm->mutex);
-    stm->shutdown = 1;
-  }
-
   OSStatus r;
   if (stm->input_unit != NULL) {
     r = AudioOutputUnitStop(stm->input_unit);
     assert(r == 0);
   }
   if (stm->output_unit != NULL) {
     r = AudioOutputUnitStop(stm->output_unit);
     assert(r == 0);
   }
+}
+
+static int
+audiounit_stream_stop(cubeb_stream * stm)
+{
+  stm->shutdown = true;
+
+  audiounit_stream_stop_internal(stm);
+
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+
   LOG("Cubeb stream (%p) stopped successfully.", stm);
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   auto_lock lock(stm->mutex);
@@ -1717,35 +1842,27 @@ int audiounit_stream_device_destroy(cube
 {
   delete [] device->output_name;
   delete [] device->input_name;
   delete device;
   return CUBEB_OK;
 }
 
 int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
-                                                      cubeb_device_changed_callback  device_changed_callback)
+                                                      cubeb_device_changed_callback device_changed_callback)
 {
   /* Note: second register without unregister first causes 'nope' error.
    * Current implementation requires unregister before register a new cb. */
   assert(!stream->device_changed_callback);
 
   auto_lock lock(stream->mutex);
 
   stream->device_changed_callback = device_changed_callback;
-  int r = CUBEB_OK;
-#if !TARGET_OS_IPHONE
-  if (device_changed_callback) {
-    r = audiounit_install_device_changed_callback(stream);
-  } else {
-    r = audiounit_uninstall_device_changed_callback(stream);
-  }
-#endif
 
-  return r;
+  return CUBEB_OK;
 }
 
 static OSStatus
 audiounit_get_devices(AudioObjectID ** devices, uint32_t * count)
 {
   OSStatus ret;
   UInt32 size = 0;
   AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices,
@@ -1930,24 +2047,21 @@ audiounit_create_device_from_hwdev(Audio
     UInt32 ds;
     size = sizeof(UInt32);
     adr.mSelector = kAudioDevicePropertyDataSource;
     if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds) == noErr) {
       CFStringRef dsname;
       AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) };
       adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
       size = sizeof(AudioValueTranslation);
+      // If there is a datasource for this device, use it instead of the device
+      // name.
       if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl) == noErr) {
-        CFStringRef fullstr = CFStringCreateWithFormat(NULL, NULL,
-            CFSTR("%@ (%@)"), str, dsname);
-        CFRelease(dsname);
-        if (fullstr != NULL) {
-          CFRelease(str);
-          str = fullstr;
-        }
+        CFRelease(str);
+        str = dsname;
       }
     }
 
     ret->friendly_name = audiounit_strref_to_cstr_utf8(str);
     CFRelease(str);
   }
 
   size = sizeof(CFStringRef);
--- a/media/libcubeb/src/cubeb_panner.cpp
+++ b/media/libcubeb/src/cubeb_panner.cpp
@@ -24,37 +24,37 @@ template<typename T>
 void cubeb_pan_stereo_buffer(T * buf, uint32_t frames, float pan)
 {
   if (pan == 0.0) {
     return;
   }
   /* rescale in [0; 1] */
   pan += 1;
   pan /= 2;
-  float left_gain  = cos(pan * M_PI * 0.5);
-  float right_gain = sin(pan * M_PI * 0.5);
+  float left_gain  = float(cos(pan * M_PI * 0.5));
+  float right_gain = float(sin(pan * M_PI * 0.5));
 
   /* In we are panning on the left, pan the right channel into the left one and
    * vice-versa. */
   if (pan < 0.5) {
     for (uint32_t i = 0; i < frames * 2; i+=2) {
-      buf[i]     = buf[i] + buf[i + 1] * left_gain;
-      buf[i + 1] = buf[i + 1] * right_gain;
+      buf[i]     = T(buf[i] + buf[i + 1] * left_gain);
+      buf[i + 1] = T(buf[i + 1] * right_gain);
     }
   } else {
     for (uint32_t i = 0; i < frames * 2; i+=2) {
-      buf[i]     = buf[i] * left_gain;
-      buf[i + 1] = buf[i + 1] + buf[i] * right_gain;
+      buf[i]     = T(buf[i] * left_gain);
+      buf[i + 1] = T(buf[i + 1] + buf[i] * right_gain);
     }
   }
 }
 }
 
 void cubeb_pan_stereo_buffer_float(float * buf, uint32_t frames, float pan)
 {
-  cubeb_pan_stereo_buffer(reinterpret_cast<float*>(buf), frames, pan);
+  cubeb_pan_stereo_buffer(buf, frames, pan);
 }
 
 void cubeb_pan_stereo_buffer_int(short * buf, uint32_t frames, float pan)
 {
-  cubeb_pan_stereo_buffer(reinterpret_cast<short*>(buf), frames, pan);
+  cubeb_pan_stereo_buffer(buf, frames, pan);
 }
 
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -82,25 +82,16 @@
   X(pa_context_subscribe)                       \
   X(pa_mainloop_api_once)                       \
 
 #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
 LIBPULSE_API_VISIT(MAKE_TYPEDEF);
 #undef MAKE_TYPEDEF
 #endif
 
-//#define LOGGING_ENABLED
-#ifdef LOGGING_ENABLED
-#define LOG(...) do {                           \
-    fprintf(stderr, __VA_ARGS__);               \
-  } while(0)
-#else
-#define LOG(...)
-#endif
-
 static struct cubeb_ops const pulse_ops;
 
 struct cubeb {
   struct cubeb_ops const * ops;
   void * libpulse;
   pa_threaded_mainloop * mainloop;
   pa_context * context;
   pa_sink_info * default_sink_info;
@@ -120,27 +111,28 @@ struct cubeb_stream {
   pa_time_event * drain_timer;
   pa_sample_spec output_sample_spec;
   pa_sample_spec input_sample_spec;
   int shutdown;
   float volume;
   cubeb_state state;
 };
 
-const float PULSE_NO_GAIN = -1.0;
+static const float PULSE_NO_GAIN = -1.0;
 
 enum cork_state {
   UNCORK = 0,
   CORK = 1 << 0,
   NOTIFY = 1 << 1
 };
 
 static void
 sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
 {
+  (void)context;
   cubeb * ctx = u;
   if (!eol) {
     free(ctx->default_sink_info);
     ctx->default_sink_info = malloc(sizeof(pa_sink_info));
     memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info));
   }
   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
 }
@@ -159,37 +151,43 @@ context_state_callback(pa_context * c, v
     ctx->error = 1;
   }
   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
 }
 
 static void
 context_notify_callback(pa_context * c, void * u)
 {
+  (void)c;
   cubeb * ctx = u;
   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
 }
 
 static void
 stream_success_callback(pa_stream * s, int success, void * u)
 {
+  (void)s;
+  (void)success;
   cubeb_stream * stm = u;
   WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
 }
 
 static void
 stream_state_change_callback(cubeb_stream * stm, cubeb_state s)
 {
   stm->state = s;
   stm->state_callback(stm, stm->user_ptr, s);
 }
 
 static void
 stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u)
 {
+  (void)a;
+  (void)e;
+  (void)tv;
   cubeb_stream * stm = u;
   stream_state_change_callback(stm, CUBEB_STATE_DRAINED);
   /* there's no pa_rttime_free, so use this instead. */
   a->time_free(stm->drain_timer);
   stm->drain_timer = NULL;
   WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
 }
 
@@ -221,17 +219,17 @@ trigger_user_callback(pa_stream * s, voi
   while (towrite) {
     size = towrite;
     r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
     // Note: this has failed running under rr on occassion - needs investigation.
     assert(r == 0);
     assert(size > 0);
     assert(size % frame_size == 0);
 
-    LOG("Trigger user callback with output buffer size=%zd, read_offset=%zd\n", size, read_offset);
+    LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", size, read_offset);
     got = stm->data_callback(stm, stm->user_ptr, (uint8_t const *)input_data + read_offset, buffer, size / frame_size);
     if (got < 0) {
       WRAP(pa_stream_cancel_write)(s);
       stm->shutdown = 1;
       return;
     }
     // If more iterations move offset of read buffer
     if (input_data) {
@@ -290,17 +288,17 @@ read_from_input(pa_stream * s, void cons
     }
   }
   return readable_size;
 }
 
 static void
 stream_write_callback(pa_stream * s, size_t nbytes, void * u)
 {
-  LOG("Output callback to be written buffer size %zd\n", nbytes);
+  LOGV("Output callback to be written buffer size %zd", nbytes);
   cubeb_stream * stm = u;
   if (stm->shutdown ||
       stm->state != CUBEB_STATE_STARTED) {
     return;
   }
 
   if (!stm->input_stream){
     // Output/playback only operation.
@@ -308,17 +306,17 @@ stream_write_callback(pa_stream * s, siz
     assert(!stm->input_stream && stm->output_stream);
     trigger_user_callback(s, NULL, nbytes, stm);
   }
 }
 
 static void
 stream_read_callback(pa_stream * s, size_t nbytes, void * u)
 {
-  LOG("Input callback buffer size %zd\n", nbytes);
+  LOGV("Input callback buffer size %zd", nbytes);
   cubeb_stream * stm = u;
   if (stm->shutdown) {
     return;
   }
 
   void const * read_data = NULL;
   size_t read_size;
   while (read_from_input(s, &read_data, &read_size) > 0) {
@@ -553,22 +551,24 @@ pulse_init(cubeb ** context, char const 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
 static char const *
 pulse_get_backend_id(cubeb * ctx)
 {
+  (void)ctx;
   return "pulse";
 }
 
 static int
 pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
 {
+  (void)ctx;
   assert(ctx && max_channels);
 
   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
   while (!ctx->default_sink_info) {
     WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
   }
   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
@@ -576,31 +576,33 @@ pulse_get_max_channel_count(cubeb * ctx,
 
   return CUBEB_OK;
 }
 
 static int
 pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   assert(ctx && rate);
+  (void)ctx;
 
   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
   while (!ctx->default_sink_info) {
     WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
   }
   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
   *rate = ctx->default_sink_info->sample_spec.rate;
 
   return CUBEB_OK;
 }
 
 static int
 pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
+  (void)ctx;
   // According to PulseAudio developers, this is a safe minimum.
   *latency_frames = 25 * params.rate / 1000;
 
   return CUBEB_OK;
 }
 
 static void
 pulse_context_destroy(cubeb * ctx)
@@ -640,18 +642,18 @@ pulse_destroy(cubeb * ctx)
   if (ctx->default_sink_info) {
     free(ctx->default_sink_info);
   }
   free(ctx);
 }
 
 static void pulse_stream_destroy(cubeb_stream * stm);
 
-pa_sample_format_t
-cubeb_to_pulse_format(cubeb_sample_format format)
+static pa_sample_format_t
+to_pulse_format(cubeb_sample_format format)
 {
   switch (format) {
   case CUBEB_SAMPLE_S16LE:
     return PA_SAMPLE_S16LE;
   case CUBEB_SAMPLE_S16BE:
     return PA_SAMPLE_S16BE;
   case CUBEB_SAMPLE_FLOAT32LE:
     return PA_SAMPLE_FLOAT32LE;
@@ -666,17 +668,17 @@ static int
 create_pa_stream(cubeb_stream * stm,
                  pa_stream ** pa_stm,
                  cubeb_stream_params * stream_params,
                  char const * stream_name)
 {
   assert(stm && stream_params);
   *pa_stm = NULL;
   pa_sample_spec ss;
-  ss.format = cubeb_to_pulse_format(stream_params->format);
+  ss.format = to_pulse_format(stream_params->format);
   if (ss.format == PA_SAMPLE_INVALID)
     return CUBEB_ERROR_INVALID_FORMAT;
   ss.rate = stream_params->rate;
   ss.channels = stream_params->channels;
 
   *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
   return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK;
 }
@@ -686,17 +688,17 @@ set_buffering_attribute(unsigned int lat
 {
   pa_buffer_attr battr;
   battr.maxlength = -1;
   battr.prebuf    = -1;
   battr.tlength   = latency_frames * WRAP(pa_frame_size)(sample_spec);
   battr.minreq    = battr.tlength / 4;
   battr.fragsize  = battr.minreq;
 
-  LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",
+  LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",
       battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
 
   return battr;
 }
 
 static int
 pulse_stream_init(cubeb * context,
                   cubeb_stream ** stream,
@@ -788,31 +790,31 @@ pulse_stream_init(cubeb * context,
 
   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   if (r != 0) {
     pulse_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
-#ifdef LOGGING_ENABLED
-  if (output_stream_params){
-    const pa_buffer_attr * output_att;
-    output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
-    LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",output_att->maxlength, output_att->tlength,
-        output_att->prebuf, output_att->minreq, output_att->fragsize);
-  }
+  if (g_log_level) {
+    if (output_stream_params){
+      const pa_buffer_attr * output_att;
+      output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
+      LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",output_att->maxlength, output_att->tlength,
+          output_att->prebuf, output_att->minreq, output_att->fragsize);
+    }
 
-  if (input_stream_params){
-    const pa_buffer_attr * input_att;
-    input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
-    LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",input_att->maxlength, input_att->tlength,
-        input_att->prebuf, input_att->minreq, input_att->fragsize);
+    if (input_stream_params){
+      const pa_buffer_attr * input_att;
+      input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
+      LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",input_att->maxlength, input_att->tlength,
+          input_att->prebuf, input_att->minreq, input_att->fragsize);
+    }
   }
-#endif
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
 static void
 pulse_stream_destroy(cubeb_stream * stm)
@@ -839,19 +841,20 @@ pulse_stream_destroy(cubeb_stream * stm)
     WRAP(pa_stream_disconnect)(stm->input_stream);
     WRAP(pa_stream_unref)(stm->input_stream);
   }
   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   free(stm);
 }
 
-void
+static void
 pulse_defer_event_cb(pa_mainloop_api * a, void * userdata)
 {
+  (void)a;
   cubeb_stream * stm = userdata;
   size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream);
   trigger_user_callback(stm->output_stream, NULL, writable_size, stm);
 }
 
 static int
 pulse_stream_start(cubeb_stream * stm)
 {
@@ -912,17 +915,17 @@ pulse_stream_get_position(cubeb_stream *
   }
 
   bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec);
   *position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec);
 
   return CUBEB_OK;
 }
 
-int
+static int
 pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
   pa_usec_t r_usec;
   int negative, r;
 
   if (!stm || !stm->output_stream) {
     return CUBEB_ERROR;
   }
@@ -932,24 +935,27 @@ pulse_stream_get_latency(cubeb_stream * 
   if (r) {
     return CUBEB_ERROR;
   }
 
   *latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC;
   return CUBEB_OK;
 }
 
-void volume_success(pa_context *c, int success, void *userdata)
+static void
+volume_success(pa_context *c, int success, void *userdata)
 {
+  (void)success;
+  (void)c;
   cubeb_stream * stream = userdata;
   assert(success);
   WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
 }
 
-int
+static int
 pulse_stream_set_volume(cubeb_stream * stm, float volume)
 {
   uint32_t index;
   pa_operation * op;
   pa_volume_t vol;
   pa_cvolume cvol;
   const pa_sample_spec * ss;
 
@@ -984,17 +990,17 @@ pulse_stream_set_volume(cubeb_stream * s
     }
   }
 
   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   return CUBEB_OK;
 }
 
-int
+static int
 pulse_stream_set_panning(cubeb_stream * stream, float panning)
 {
   const pa_channel_map * map;
   pa_cvolume vol;
 
   if (!stream->output_stream) {
     return CUBEB_ERROR;
   }
@@ -1226,17 +1232,18 @@ pulse_enumerate_devices(cubeb * context,
     (*collection)->device[i] = user_data.devinfo[i];
 
   free(user_data.default_sink_name);
   free(user_data.default_source_name);
   free(user_data.devinfo);
   return CUBEB_OK;
 }
 
-int pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
+static int
+pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
 {
 #if PA_CHECK_VERSION(0, 9, 8)
   *device = calloc(1, sizeof(cubeb_device));
   if (*device == NULL)
     return CUBEB_ERROR;
 
   if (stm->input_stream) {
     const char * name = WRAP(pa_stream_get_device_name)(stm->input_stream);
@@ -1249,69 +1256,78 @@ int pulse_stream_get_current_device(cube
   }
 
   return CUBEB_OK;
 #else
   return CUBEB_ERROR_NOT_SUPPORTED;
 #endif
 }
 
-int pulse_stream_device_destroy(cubeb_stream * stream,
-                                cubeb_device * device)
+static int
+pulse_stream_device_destroy(cubeb_stream * stream,
+                            cubeb_device * device)
 {
+  (void)stream;
   free(device->input_name);
   free(device->output_name);
   free(device);
   return CUBEB_OK;
 }
 
-void pulse_subscribe_callback(pa_context * ctx,
-                              pa_subscription_event_type_t t,
-                              uint32_t index, void * userdata)
+static void
+pulse_subscribe_callback(pa_context * ctx,
+                         pa_subscription_event_type_t t,
+                         uint32_t index, void * userdata)
 {
+  (void)ctx;
   cubeb * context = userdata;
 
   switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
   case PA_SUBSCRIPTION_EVENT_SOURCE:
   case PA_SUBSCRIPTION_EVENT_SINK:
 
-#ifdef LOGGING_ENABLED
-    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
-        (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
-      LOG("Removing sink index %d\n", index);
-    else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
-        (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
-      LOG("Adding sink index %d\n", index);
-    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
-        (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
-      LOG("Removing source index %d\n", index);
-    else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
-        (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
-      LOG("Adding source index %d\n", index);
-#endif
+    if (g_log_level) {
+      if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+        LOG("Removing sink index %d", index);
+      } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+        LOG("Adding sink index %d", index);
+      }
+      if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+        LOG("Removing source index %d", index);
+      } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
+          (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+        LOG("Adding source index %d", index);
+      }
+    }
 
     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
         (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
       context->collection_changed_callback(context, context->collection_changed_user_ptr);
     }
     break;
   }
 }
 
-void subscribe_success(pa_context *c, int success, void *userdata)
+static void
+subscribe_success(pa_context *c, int success, void *userdata)
 {
+  (void)c;
   cubeb * context = userdata;
   assert(success);
   WRAP(pa_threaded_mainloop_signal)(context->mainloop, 0);
 }
 
-int pulse_register_device_collection_changed(cubeb * context,
-                                             cubeb_device_type devtype,
-                                             cubeb_device_collection_changed_callback collection_changed_callback,
-                                             void * user_ptr)
+static int
+pulse_register_device_collection_changed(cubeb * context,
+                                         cubeb_device_type devtype,
+                                         cubeb_device_collection_changed_callback collection_changed_callback,
+                                         void * user_ptr)
 {
   context->collection_changed_callback = collection_changed_callback;
   context->collection_changed_user_ptr = user_ptr;
 
   WRAP(pa_threaded_mainloop_lock)(context->mainloop);
 
   pa_subscription_mask_t mask;
   if (context->collection_changed_callback == NULL) {
@@ -1326,17 +1342,17 @@ int pulse_register_device_collection_cha
       mask = PA_SUBSCRIPTION_MASK_SINK;
     else
       mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE;
   }
 
   pa_operation * o;
   o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
   if (o == NULL) {
-    LOG("Context subscribe failed\n");
+    LOG("Context subscribe failed");
     return CUBEB_ERROR;
   }
   operation_wait(context, NULL, o);
   WRAP(pa_operation_unref)(o);
 
   WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
 
   return CUBEB_OK;
--- a/media/libcubeb/src/cubeb_resampler.cpp
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -1,28 +1,24 @@
 /*
  * Copyright © 2014 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
-
 #ifndef NOMINMAX
 #define NOMINMAX
 #endif // NOMINMAX
 
 #include <algorithm>
 #include <cmath>
 #include <cassert>
 #include <cstring>
 #include <cstddef>
 #include <cstdio>
-#if defined(HAVE_CONFIG_H)
-#include "config.h"
-#endif
 #include "cubeb_resampler.h"
 #include "cubeb-speex-resampler.h"
 #include "cubeb_resampler_internal.h"
 #include "cubeb_utils.h"
 
 int
 to_speex_quality(cubeb_resampler_quality q)
 {
--- a/media/libcubeb/src/cubeb_utils.h
+++ b/media/libcubeb/src/cubeb_utils.h
@@ -6,40 +6,44 @@
  */
 
 #if !defined(CUBEB_UTILS)
 #define CUBEB_UTILS
 
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
+#include <type_traits>
 #if defined(WIN32)
 #include "cubeb_utils_win.h"
 #else
 #include "cubeb_utils_unix.h"
 #endif
 
 /** Similar to memcpy, but accounts for the size of an element. */
 template<typename T>
 void PodCopy(T * destination, const T * source, size_t count)
 {
+  static_assert(std::is_trivial<T>::value, "Requires trivial type");
   memcpy(destination, source, count * sizeof(T));
 }
 
 /** Similar to memmove, but accounts for the size of an element. */
 template<typename T>
 void PodMove(T * destination, const T * source, size_t count)
 {
+  static_assert(std::is_trivial<T>::value, "Requires trivial type");
   memmove(destination, source, count * sizeof(T));
 }
 
 /** Similar to a memset to zero, but accounts for the size of an element. */
 template<typename T>
 void PodZero(T * destination, size_t count)
 {
+  static_assert(std::is_trivial<T>::value, "Requires trivial type");
   memset(destination, 0,  count * sizeof(T));
 }
 
 template<typename T>
 class auto_array
 {
 public:
   explicit auto_array(uint32_t capacity = 0)
@@ -203,9 +207,23 @@ struct auto_lock {
   ~auto_lock()
   {
     lock.leave();
   }
 private:
   owned_critical_section & lock;
 };
 
+struct auto_unlock {
+  explicit auto_unlock(owned_critical_section & lock)
+    : lock(lock)
+  {
+    lock.leave();
+  }
+  ~auto_unlock()
+  {
+    lock.enter();
+  }
+private:
+  owned_critical_section & lock;
+};
+
 #endif /* CUBEB_UTILS */
--- a/media/libcubeb/src/cubeb_utils_unix.h
+++ b/media/libcubeb/src/cubeb_utils_unix.h
@@ -15,69 +15,69 @@
 /* This wraps a critical section to track the owner in debug mode. */
 class owned_critical_section
 {
 public:
   owned_critical_section()
   {
     pthread_mutexattr_t attr;
     pthread_mutexattr_init(&attr);
-#ifdef DEBUG
+#ifndef NDEBUG
     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
 #else
     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
 #endif
 
-#ifdef DEBUG
+#ifndef NDEBUG
     int r =
 #endif
     pthread_mutex_init(&mutex, &attr);
-#ifdef DEBUG
+#ifndef NDEBUG
     assert(r == 0);
 #endif
 
     pthread_mutexattr_destroy(&attr);
   }
 
   ~owned_critical_section()
   {
-#ifdef DEBUG
+#ifndef NDEBUG
     int r =
 #endif
     pthread_mutex_destroy(&mutex);
-#ifdef DEBUG
+#ifndef NDEBUG
     assert(r == 0);
 #endif
   }
 
   void enter()
   {
-#ifdef DEBUG
+#ifndef NDEBUG
     int r =
 #endif
     pthread_mutex_lock(&mutex);
-#ifdef DEBUG
+#ifndef NDEBUG
     assert(r == 0 && "Deadlock");
 #endif
   }
 
   void leave()
   {
-#ifdef DEBUG
+#ifndef NDEBUG
     int r =
 #endif
     pthread_mutex_unlock(&mutex);
-#ifdef DEBUG
+#ifndef NDEBUG
     assert(r == 0 && "Unlocking unlocked mutex");
 #endif
   }
 
   void assert_current_thread_owns()
   {
-#ifdef DEBUG
+#ifndef NDEBUG
     int r = pthread_mutex_lock(&mutex);
     assert(r == EDEADLK);
 #endif
   }
 
 private:
   pthread_mutex_t mutex;
 
--- a/media/libcubeb/src/cubeb_utils_win.h
+++ b/media/libcubeb/src/cubeb_utils_win.h
@@ -12,59 +12,59 @@
 #include "cubeb-internal.h"
 
 /* This wraps a critical section to track the owner in debug mode, adapted from
    NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
 class owned_critical_section
 {
 public:
   owned_critical_section()
-#ifdef DEBUG
+#ifndef NDEBUG
     : owner(0)
 #endif
   {
     InitializeCriticalSection(&critical_section);
   }
 
   ~owned_critical_section()
   {
     DeleteCriticalSection(&critical_section);
   }
 
   void enter()
   {
     EnterCriticalSection(&critical_section);
-#ifdef DEBUG
+#ifndef NDEBUG
     XASSERT(owner != GetCurrentThreadId() && "recursive locking");
     owner = GetCurrentThreadId();
 #endif
   }
 
   void leave()
   {
-#ifdef DEBUG
+#ifndef NDEBUG
     /* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
     owner = 0;
 #endif
     LeaveCriticalSection(&critical_section);
   }
 
   /* This is guaranteed to have the good behaviour if it succeeds. The behaviour
      is undefined otherwise. */
   void assert_current_thread_owns()
   {
-#ifdef DEBUG
+#ifndef NDEBUG
     /* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
     XASSERT(owner == GetCurrentThreadId());
 #endif
   }
 
 private:
   CRITICAL_SECTION critical_section;
-#ifdef DEBUG
+#ifndef NDEBUG
   DWORD owner;
 #endif
 
   // Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
   owned_critical_section(const owned_critical_section&);
   owned_critical_section& operator=(const owned_critical_section&);
 };
 
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -1,19 +1,16 @@
 /*
  * Copyright © 2013 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #define NOMINMAX
 
-#if defined(HAVE_CONFIG_H)
-#include "config.h"
-#endif
 #include <initguid.h>
 #include <windows.h>
 #include <mmdeviceapi.h>
 #include <windef.h>
 #include <audioclient.h>
 #include <devicetopology.h>
 #include <process.h>
 #include <avrt.h>
@@ -43,30 +40,23 @@
 
 #ifndef PKEY_Device_FriendlyName
 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,    0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING
 #endif
 #ifndef PKEY_Device_InstanceId
 DEFINE_PROPERTYKEY(PKEY_Device_InstanceId,      0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); //    VT_LPWSTR
 #endif
 
-// #define LOGGING_ENABLED
-
-#ifdef LOGGING_ENABLED
-#define LOG(...) do {                           \
-    fprintf(stderr, __VA_ARGS__);               \
-  } while(0)
-#else
-#define LOG(...)
-#endif
-
-#define ARRAY_LENGTH(array_)                    \
-  (sizeof(array_) / sizeof(array_[0]))
-
 namespace {
+template<typename T, size_t N>
+constexpr size_t
+ARRAY_LENGTH(T(&)[N])
+{
+  return N;
+}
 
 void
 SafeRelease(HANDLE handle)
 {
   if (handle) {
     CloseHandle(handle);
   }
 }
@@ -82,22 +72,22 @@ void SafeRelease(T * ptr)
 struct auto_com {
   auto_com() {
     result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
   }
   ~auto_com() {
     if (result == RPC_E_CHANGED_MODE) {
       // This is not an error, COM was not initialized by this function, so it is
       // not necessary to uninit it.
-      LOG("COM was already initialized in STA.\n");
+      LOG("COM was already initialized in STA.");
     } else if (result == S_FALSE) {
       // This is not an error. We are allowed to call CoInitializeEx more than
       // once, as long as it is matches by an CoUninitialize call.
       // We do that in the dtor which is guaranteed to be called.
-      LOG("COM was already initialized in MTA\n");
+      LOG("COM was already initialized in MTA");
     }
     if (SUCCEEDED(result)) {
       CoUninitialize();
     }
   }
   bool ok() {
     return result == RPC_E_CHANGED_MODE || SUCCEEDED(result);
   }
@@ -275,56 +265,56 @@ public:
   { }
 
   virtual ~wasapi_endpoint_notification_client()
   { }
 
   HRESULT STDMETHODCALLTYPE
   OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
   {
-    LOG("Audio device default changed.\n");
+    LOG("Audio device default changed.");
 
     /* we only support a single stream type for now. */
     if (flow != eRender && role != eConsole) {
       return S_OK;
     }
 
     BOOL ok = SetEvent(reconfigure_event);
     if (!ok) {
-      LOG("SetEvent on reconfigure_event failed: %x\n", GetLastError());
+      LOG("SetEvent on reconfigure_event failed: %x", GetLastError());
     }
 
     return S_OK;
   }
 
   /* The remaining methods are not implemented, they simply log when called (if
      log is enabled), for debugging. */
   HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
   {
-    LOG("Audio device added.\n");
+    LOG("Audio device added.");
     return S_OK;
   };
 
   HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
   {
-    LOG("Audio device removed.\n");
+    LOG("Audio device removed.");
     return S_OK;
   }
 
   HRESULT STDMETHODCALLTYPE
   OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
   {
-    LOG("Audio device state changed.\n");
+    LOG("Audio device state changed.");
     return S_OK;
   }
 
   HRESULT STDMETHODCALLTYPE
   OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
   {
-    LOG("Audio device property value changed.\n");
+    LOG("Audio device property value changed.");
     return S_OK;
   }
 private:
   /* refcount for this instance, necessary to implement MSCOM semantics. */
   LONG ref_count;
   HANDLE reconfigure_event;
 };
 
@@ -496,17 +486,17 @@ refill(cubeb_stream * stm, float * input
 
   {
     auto_lock lock(stm->stream_reset_lock);
     stm->frames_written += out_frames;
   }
 
   /* Go in draining mode if we got fewer frames than requested. */
   if (out_frames < output_frames_needed) {
-    LOG("start draining.\n");
+    LOG("start draining.");
     stm->draining = true;
   }
 
   /* If this is not true, there will be glitches.
      It is alright to have produced less frames if we are draining, though. */
   XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm));
 
   if (has_output(stm)) {
@@ -529,54 +519,54 @@ bool get_input_buffer(cubeb_stream * stm
 {
   HRESULT hr;
   UINT32 padding_in;
 
   XASSERT(has_input(stm));
 
   hr = stm->input_client->GetCurrentPadding(&padding_in);
   if (FAILED(hr)) {
-    LOG("Failed to get padding\n");
+    LOG("Failed to get padding");
     return false;
   }
   XASSERT(padding_in <= stm->input_buffer_frame_count);
   UINT32 total_available_input = padding_in;
 
   BYTE * input_packet = NULL;
   DWORD flags;
   UINT64 dev_pos;
   UINT32 next;
   /* Get input packets until we have captured enough frames, and put them in a
    * contiguous buffer. */
   uint32_t offset = 0;
   while (offset != total_available_input) {
     hr = stm->capture_client->GetNextPacketSize(&next);
     if (FAILED(hr)) {
-      LOG("cannot get next packet size: %x\n", hr);
+      LOG("cannot get next packet size: %x", hr);
       return false;
     }
     /* This can happen if the capture stream has stopped. Just return in this
      * case. */
     if (!next) {
       break;
     }
 
     UINT32 packet_size;
     hr = stm->capture_client->GetBuffer(&input_packet,
                                         &packet_size,
                                         &flags,
                                         &dev_pos,
                                         NULL);
     if (FAILED(hr)) {
-      LOG("GetBuffer failed for capture: %x\n", hr);
+      LOG("GetBuffer failed for capture: %x", hr);
       return false;
     }
     XASSERT(packet_size == next);
     if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
-      LOG("insert silence: ps=%u\n", packet_size);
+      LOG("insert silence: ps=%u", packet_size);
       stm->linear_input_buffer.push_silence(packet_size * stm->input_stream_params.channels);
     } else {
       if (should_upmix(stm->input_mix_params, stm->input_stream_params)) {
         bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
                                                    packet_size * stm->input_stream_params.channels);
         assert(ok);
         upmix(reinterpret_cast<float*>(input_packet), packet_size,
               stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
@@ -617,17 +607,17 @@ bool get_output_buffer(cubeb_stream * st
 {
   UINT32 padding_out;
   HRESULT hr;
 
   XASSERT(has_output(stm));
 
   hr = stm->output_client->GetCurrentPadding(&padding_out);
   if (FAILED(hr)) {
-    LOG("Failed to get padding: %x\n", hr);
+    LOG("Failed to get padding: %x", hr);
     return false;
   }
   XASSERT(padding_out <= stm->output_buffer_frame_count);
 
   if (stm->draining) {
     if (padding_out == 0) {
       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
       return false;
@@ -635,17 +625,17 @@ bool get_output_buffer(cubeb_stream * st
     return true;
   }
 
   frame_count = stm->output_buffer_frame_count - padding_out;
   BYTE * output_buffer;
 
   hr = stm->render_client->GetBuffer(frame_count, &output_buffer);
   if (FAILED(hr)) {
-    LOG("cannot get render buffer\n");
+    LOG("cannot get render buffer");
     return false;
   }
 
   buffer = reinterpret_cast<float*>(output_buffer);
 
   return true;
 }
 
@@ -685,31 +675,31 @@ refill_callback_duplex(cubeb_stream * st
     return true;
   }
 
   // When WASAPI has not filled the input buffer yet, send silence.
   double output_duration = double(output_frames) / stm->output_mix_params.rate;
   double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
   if (input_duration < output_duration) {
     size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
-    LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding);
+    LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
     stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
   }
 
   refill(stm,
          stm->linear_input_buffer.data(),
          stm->linear_input_buffer.length(),
          output_buffer,
          output_frames);
 
   stm->linear_input_buffer.clear();
 
   hr = stm->render_client->ReleaseBuffer(output_frames, 0);
   if (FAILED(hr)) {
-    LOG("failed to release buffer: %x\n", hr);
+    LOG("failed to release buffer: %x", hr);
     return false;
   }
   return true;
 }
 
 bool
 refill_callback_input(cubeb_stream * stm)
 {
@@ -717,16 +707,21 @@ refill_callback_input(cubeb_stream * stm
 
   XASSERT(has_input(stm) && !has_output(stm));
 
   rv = get_input_buffer(stm);
   if (!rv) {
     return rv;
   }
 
+  // This can happen at the very beginning of the stream.
+  if (!stm->linear_input_buffer.length()) {
+    return true;
+  }
+
   long read = refill(stm,
                      stm->linear_input_buffer.data(),
                      stm->linear_input_buffer.length(),
                      nullptr,
                      0);
 
   consumed_all_buffer = read == stm->linear_input_buffer.length();
 
@@ -759,17 +754,17 @@ refill_callback_output(cubeb_stream * st
                     0,
                     output_buffer,
                     output_frames);
   XASSERT(got >= 0);
   XASSERT(got == output_frames || stm->draining);
 
   hr = stm->render_client->ReleaseBuffer(got, 0);
   if (FAILED(hr)) {
-    LOG("failed to release buffer: %x\n", hr);
+    LOG("failed to release buffer: %x", hr);
     return false;
   }
 
   return got == output_frames || stm->draining;
 }
 
 static unsigned int __stdcall
 wasapi_stream_render_loop(LPVOID stream)
@@ -783,28 +778,28 @@ wasapi_stream_render_loop(LPVOID stream)
     stm->refill_event,
     stm->input_available_event
   };
   HANDLE mmcss_handle = NULL;
   HRESULT hr = 0;
   DWORD mmcss_task_index = 0;
   auto_com com;
   if (!com.ok()) {
-    LOG("COM initialization failed on render_loop thread.\n");
+    LOG("COM initialization failed on render_loop thread.");
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
     return 0;
   }
 
   /* We could consider using "Pro Audio" here for WebAudio and
      maybe WebRTC. */
   mmcss_handle =
     stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index);
   if (!mmcss_handle) {
     /* This is not fatal, but we might glitch under heavy load. */
-    LOG("Unable to use mmcss to bump the render thread priority: %x\n", GetLastError());
+    LOG("Unable to use mmcss to bump the render thread priority: %x", GetLastError());
   }
 
 
   /* WaitForMultipleObjects timeout can trigger in cases where we don't want to
      treat it as a timeout, such as across a system sleep/wake cycle.  Trigger
      the timeout error handling only when the timeout_limit is reached, which is
      reset on each successful loop. */
   unsigned timeout_count = 0;
@@ -903,25 +898,25 @@ BOOL WINAPI revert_mm_thread_characteris
 }
 
 HRESULT register_notification_client(cubeb_stream * stm)
 {
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(&stm->device_enumerator));
   if (FAILED(hr)) {
-    LOG("Could not get device enumerator: %x\n", hr);
+    LOG("Could not get device enumerator: %x", hr);
     return hr;
   }
 
   stm->notification_client = new wasapi_endpoint_notification_client(stm->reconfigure_event);
 
   hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client);
   if (FAILED(hr)) {
-    LOG("Could not register endpoint notification callback: %x\n", hr);
+    LOG("Could not register endpoint notification callback: %x", hr);
     SafeRelease(stm->notification_client);
     stm->notification_client = nullptr;
     SafeRelease(stm->device_enumerator);
     stm->device_enumerator = nullptr;
   }
 
   return hr;
 }
@@ -951,45 +946,45 @@ HRESULT unregister_notification_client(c
 
 HRESULT get_endpoint(IMMDevice ** device, LPCWSTR devid)
 {
   IMMDeviceEnumerator * enumerator;
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(&enumerator));
   if (FAILED(hr)) {
-    LOG("Could not get device enumerator: %x\n", hr);
+    LOG("Could not get device enumerator: %x", hr);
     return hr;
   }
 
   hr = enumerator->GetDevice(devid, device);
   if (FAILED(hr)) {
-    LOG("Could not get device: %x\n", hr);
+    LOG("Could not get device: %x", hr);
     SafeRelease(enumerator);
     return hr;
   }
 
   SafeRelease(enumerator);
 
   return S_OK;
 }
 
 HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
 {
   IMMDeviceEnumerator * enumerator;
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(&enumerator));
   if (FAILED(hr)) {
-    LOG("Could not get device enumerator: %x\n", hr);
+    LOG("Could not get device enumerator: %x", hr);
     return hr;
   }
   hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device);
   if (FAILED(hr)) {
-    LOG("Could not get default audio endpoint: %x\n", hr);
+    LOG("Could not get default audio endpoint: %x", hr);
     SafeRelease(enumerator);
     return hr;
   }
 
   SafeRelease(enumerator);
 
   return ERROR_SUCCESS;
 }
@@ -1004,24 +999,24 @@ current_stream_delay(cubeb_stream * stm)
      before the error callback has propogated back. */
   if (!stm->audio_clock) {
     return 0;
   }
 
   UINT64 freq;
   HRESULT hr = stm->audio_clock->GetFrequency(&freq);
   if (FAILED(hr)) {
-    LOG("GetFrequency failed: %x\n", hr);
+    LOG("GetFrequency failed: %x", hr);
     return 0;
   }
 
   UINT64 pos;
   hr = stm->audio_clock->GetPosition(&pos, NULL);
   if (FAILED(hr)) {
-    LOG("GetPosition failed: %x\n", hr);
+    LOG("GetPosition failed: %x", hr);
     return 0;
   }
 
   double cur_pos = static_cast<double>(pos) / freq;
   double max_pos = static_cast<double>(stm->frames_written)  / stm->output_mix_params.rate;
   double delay = max_pos - cur_pos;
   XASSERT(delay >= 0);
 
@@ -1035,33 +1030,33 @@ stream_set_volume(cubeb_stream * stm, fl
 
   if (!stm->audio_stream_volume) {
     return CUBEB_ERROR;
   }
 
   uint32_t channels;
   HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels);
   if (hr != S_OK) {
-    LOG("could not get the channel count: %x\n", hr);
+    LOG("could not get the channel count: %x", hr);
     return CUBEB_ERROR;
   }
 
   /* up to 9.1 for now */
   if (channels > 10) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
   float volumes[10];
   for (uint32_t i = 0; i < channels; i++) {
     volumes[i] = volume;
   }
 
   hr = stm->audio_stream_volume->SetAllVolumes(channels,  volumes);
   if (hr != S_OK) {
-    LOG("could not set the channels volume: %x\n", hr);
+    LOG("could not set the channels volume: %x", hr);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 } // namespace anonymous
 
 extern "C" {
@@ -1074,17 +1069,17 @@ int wasapi_init(cubeb ** context, char c
   }
 
   /* We don't use the device yet, but need to make sure we can initialize one
      so that this backend is not incorrectly enabled on platforms that don't
      support WASAPI. */
   IMMDevice * device;
   hr = get_default_endpoint(&device, eRender);
   if (FAILED(hr)) {
-    LOG("Could not get device: %x\n", hr);
+    LOG("Could not get device: %x", hr);
     return CUBEB_ERROR;
   }
   SafeRelease(device);
 
   cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb));
   if (!ctx) {
     return CUBEB_ERROR;
   }
@@ -1096,23 +1091,23 @@ int wasapi_init(cubeb ** context, char c
   if (ctx->mmcss_module) {
     ctx->set_mm_thread_characteristics =
       (set_mm_thread_characteristics_function) GetProcAddress(
           ctx->mmcss_module, "AvSetMmThreadCharacteristicsA");
     ctx->revert_mm_thread_characteristics =
       (revert_mm_thread_characteristics_function) GetProcAddress(
           ctx->mmcss_module, "AvRevertMmThreadCharacteristics");
     if (!(ctx->set_mm_thread_characteristics && ctx->revert_mm_thread_characteristics)) {
-      LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %x\n", GetLastError());
+      LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %x", GetLastError());
       FreeLibrary(ctx->mmcss_module);
     }
   } else {
     // This is not a fatal error, but we might end up glitching when
     // the system is under high load.
-    LOG("Could not load Avrt.dll\n");
+    LOG("Could not load Avrt.dll");
     ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
     ctx->revert_mm_thread_characteristics = &revert_mm_thread_characteristics_noop;
   }
 
   *context = ctx;
 
   return CUBEB_OK;
 }
@@ -1122,30 +1117,30 @@ namespace {
 void stop_and_join_render_thread(cubeb_stream * stm)
 {
   if (!stm->thread) {
     return;
   }
 
   BOOL ok = SetEvent(stm->shutdown_event);
   if (!ok) {
-    LOG("Destroy SetEvent failed: %d\n", GetLastError());
+    LOG("Destroy SetEvent failed: %d", GetLastError());
   }
 
   /* Wait five seconds for the rendering thread to return. It's supposed to
    * check its event loop very often, five seconds is rather conservative. */
   DWORD r = WaitForSingleObject(stm->thread, 5000);
   if (r == WAIT_TIMEOUT) {
     /* Something weird happened, leak the thread and continue the shutdown
      * process. */
     LOG("Destroy WaitForSingleObject on thread timed out,"
-        " leaking the thread: %d\n", GetLastError());
+        " leaking the thread: %d", GetLastError());
   }
   if (r == WAIT_FAILED) {
-    LOG("Destroy WaitForSingleObject on thread failed: %d\n", GetLastError());
+    LOG("Destroy WaitForSingleObject on thread failed: %d", GetLastError());
   }
 
   CloseHandle(stm->thread);
   stm->thread = NULL;
 
   CloseHandle(stm->shutdown_event);
   stm->shutdown_event = 0;
 }
@@ -1217,38 +1212,38 @@ wasapi_get_min_latency(cubeb * ctx, cube
 
   if (params.format != CUBEB_SAMPLE_FLOAT32NE) {
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   IMMDevice * device;
   hr = get_default_endpoint(&device, eRender);
   if (FAILED(hr)) {
-    LOG("Could not get default endpoint: %x\n", hr);
+    LOG("Could not get default endpoint: %x", hr);
     return CUBEB_ERROR;
   }
 
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, (void **)&client);
   SafeRelease(device);
   if (FAILED(hr)) {
-    LOG("Could not activate device for latency: %x\n", hr);
+    LOG("Could not activate device for latency: %x", hr);
     return CUBEB_ERROR;
   }
 
   /* The second parameter is for exclusive mode, that we don't use. */
   hr = client->GetDevicePeriod(&default_period, NULL);
   if (FAILED(hr)) {
     SafeRelease(client);
-    LOG("Could not get device period: %x\n", hr);
+    LOG("Could not get device period: %x", hr);
     return CUBEB_ERROR;
   }
 
-  LOG("default device period: %lld\n", default_period);
+  LOG("default device period: %lld", default_period);
 
   /* According to the docs, the best latency we can achieve is by synchronizing
      the stream and the engine.
      http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
 
   *latency_frames = hns_to_frames(params.rate, default_period);
 
   SafeRelease(client);
@@ -1345,30 +1340,30 @@ handle_channel_layout(cubeb_stream * stm
   /* Check if wasapi will accept our channel layout request. */
   WAVEFORMATEX * closest;
   HRESULT hr = stm->output_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
                                                      *mix_format,
                                                      &closest);
   if (hr == S_FALSE) {
     /* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
        eventual upmix/downmix ourselves */
-    LOG("Using WASAPI suggested format: channels: %d\n", closest->nChannels);
+    LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
     WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
     XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
     CoTaskMemFree(*mix_format);
     *mix_format = closest;
   } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
     /* Not supported, no suggestion. This should not happen, but it does in the
        field with some sound cards. We restore the mix format, and let the rest
        of the code figure out the right conversion path. */
     *reinterpret_cast<WAVEFORMATEXTENSIBLE *>(*mix_format) = hw_mix_format;
   } else if (hr == S_OK) {
-    LOG("Requested format accepted by WASAPI.\n");
+    LOG("Requested format accepted by WASAPI.");
   } else {
-    LOG("IsFormatSupported unhandled error: %x\n", hr);
+    LOG("IsFormatSupported unhandled error: %x", hr);
   }
 }
 
 #define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
 
 template<typename T>
 int setup_wasapi_stream_one_side(cubeb_stream * stm,
                                  cubeb_stream_params * stream_params,
@@ -1381,103 +1376,118 @@ int setup_wasapi_stream_one_side(cubeb_s
                                  T ** render_or_capture_client,
                                  cubeb_stream_params * mix_params)
 {
   IMMDevice * device;
   WAVEFORMATEX * mix_format;
   HRESULT hr;
 
   stm->stream_reset_lock.assert_current_thread_owns();
+  bool try_again = false;
+  // This loops until we find a device that works, or we've exhausted all
+  // possibilities.
+  do {
+    if (devid) {
+      std::unique_ptr<const wchar_t> id;
+      id.reset(utf8_to_wstr(reinterpret_cast<char*>(devid)));
+      hr = get_endpoint(&device, id.get());
+      if (FAILED(hr)) {
+        LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
+        return CUBEB_ERROR;
+      }
+    }
+    else {
+      hr = get_default_endpoint(&device, direction);
+      if (FAILED(hr)) {
+        LOG("Could not get default %s endpoint, error: %x\n", DIRECTION_NAME, hr);
+        return CUBEB_ERROR;
+      }
+    }
 
-  if (devid) {
-    std::unique_ptr<const wchar_t> id;
-    id.reset(utf8_to_wstr(reinterpret_cast<char*>(devid)));
-    hr = get_endpoint(&device, id.get());
-    if (FAILED(hr)) {
-      LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
-      return CUBEB_ERROR;
-    }
-  } else {
-    hr = get_default_endpoint(&device, direction);
+    /* Get a client. We will get all other interfaces we need from
+     * this pointer. */
+    hr = device->Activate(__uuidof(IAudioClient),
+                          CLSCTX_INPROC_SERVER,
+                          NULL, (void **)audio_client);
+    SafeRelease(device);
     if (FAILED(hr)) {
-      LOG("Could not get default %s endpoint, error: %x\n", DIRECTION_NAME, hr);
-      return CUBEB_ERROR;
+      LOG("Could not activate the device to get an audio"
+          " client for %s: error: %x\n", DIRECTION_NAME, hr);
+      // A particular device can't be activated because it has been
+      // unplugged, try fall back to the default audio device.
+      if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) {
+        LOG("Trying again with the default %s audio device.", DIRECTION_NAME);
+        devid = nullptr;
+        try_again = true;
+      } else {
+        return CUBEB_ERROR;
+      }
+    } else {
+      try_again = false;
     }
-  }
-
-  /* Get a client. We will get all other interfaces we need from
-   * this pointer. */
-  hr = device->Activate(__uuidof(IAudioClient),
-                        CLSCTX_INPROC_SERVER,
-                        NULL, (void **)audio_client);
-  SafeRelease(device);
-  if (FAILED(hr)) {
-    LOG("Could not activate the device to get an audio"
-        " client for %s: error: %x\n", DIRECTION_NAME, hr);
-    return CUBEB_ERROR;
-  }
+  } while (try_again);
 
   /* We have to distinguish between the format the mixer uses,
    * and the format the stream we want to play uses. */
   hr = (*audio_client)->GetMixFormat(&mix_format);
   if (FAILED(hr)) {
     LOG("Could not fetch current mix format from the audio"
-        " client for %s: error: %x\n", DIRECTION_NAME, hr);
+        " client for %s: error: %x", DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
 
   handle_channel_layout(stm, &mix_format, stream_params);
 
   /* Shared mode WASAPI always supports float32 sample format, so this
    * is safe. */
   mix_params->format = CUBEB_SAMPLE_FLOAT32NE;
   mix_params->rate = mix_format->nSamplesPerSec;
   mix_params->channels = mix_format->nChannels;
-  LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]\n",
+  LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]",
       stream_params->format, stream_params->rate, stream_params->channels,
       mix_params->format, mix_params->rate, mix_params->channels);
 
   hr = (*audio_client)->Initialize(AUDCLNT_SHAREMODE_SHARED,
                                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
                                    AUDCLNT_STREAMFLAGS_NOPERSIST,
                                    frames_to_hns(stm, stm->latency),
                                    0,
                                    mix_format,
                                    NULL);
   if (FAILED(hr)) {
-    LOG("Unable to initialize audio client for %s: %x.\n", DIRECTION_NAME, hr);
+    LOG("Unable to initialize audio client for %s: %x.", DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
 
   CoTaskMemFree(mix_format);
 
   hr = (*audio_client)->GetBufferSize(buffer_frame_count);
   if (FAILED(hr)) {
     LOG("Could not get the buffer size from the client"
-        " for %s %x.\n", DIRECTION_NAME, hr);
+        " for %s %x.", DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
 
   // Input is up/down mixed when depacketized in get_input_buffer.
   if (has_output(stm) &&
       (should_upmix(*stream_params, *mix_params) ||
        should_downmix(*stream_params, *mix_params))) {
     stm->mix_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, *buffer_frame_count));
   }
 
   hr = (*audio_client)->SetEventHandle(event);
   if (FAILED(hr)) {
-    LOG("Could set the event handle for the %s client %x.\n",
+    LOG("Could set the event handle for the %s client %x.",
         DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
 
   hr = (*audio_client)->GetService(riid, (void **)render_or_capture_client);
   if (FAILED(hr)) {
-    LOG("Could not get the %s client %x.\n", DIRECTION_NAME, hr);
+    LOG("Could not get the %s client %x.", DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 #undef DIRECTION_NAME
 
@@ -1491,34 +1501,34 @@ int setup_wasapi_stream(cubeb_stream * s
   auto_com com;
   if (!com.ok()) {
     return CUBEB_ERROR;
   }
 
   XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
 
   if (has_input(stm)) {
-    LOG("Setup capture: device=%x\n", (int)stm->input_device);
+    LOG("Setup capture: device=%x", (int)stm->input_device);
     rv = setup_wasapi_stream_one_side(stm,
                                       &stm->input_stream_params,
                                       stm->input_device,
                                       eCapture,
                                       __uuidof(IAudioCaptureClient),
                                       &stm->input_client,
                                       &stm->input_buffer_frame_count,
                                       stm->input_available_event,
                                       &stm->capture_client,
                                       &stm->input_mix_params);
     if (rv != CUBEB_OK) {
       return rv;
     }
   }
 
   if (has_output(stm)) {
-    LOG("Setup render: device=%x\n", (int)stm->output_device);
+    LOG("Setup render: device=%x", (int)stm->output_device);
     rv = setup_wasapi_stream_one_side(stm,
                                       &stm->output_stream_params,
                                       stm->output_device,
                                       eRender,
                                       __uuidof(IAudioRenderClient),
                                       &stm->output_client,
                                       &stm->output_buffer_frame_count,
                                       stm->refill_event,
@@ -1526,25 +1536,25 @@ int setup_wasapi_stream(cubeb_stream * s
                                       &stm->output_mix_params);
     if (rv != CUBEB_OK) {
       return rv;
     }
 
     hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
                                         (void **)&stm->audio_stream_volume);
     if (FAILED(hr)) {
-      LOG("Could not get the IAudioStreamVolume: %x\n", hr);
+      LOG("Could not get the IAudioStreamVolume: %x", hr);
       return CUBEB_ERROR;
     }
 
     XASSERT(stm->frames_written == 0);
     hr = stm->output_client->GetService(__uuidof(IAudioClock),
                                         (void **)&stm->audio_clock);
     if (FAILED(hr)) {
-      LOG("Could not get the IAudioClock: %x\n", hr);
+      LOG("Could not get the IAudioClock: %x", hr);
       return CUBEB_ERROR;
     }
 
     /* Restore the stream volume over a device change. */
     if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
       return CUBEB_ERROR;
     }
   }
@@ -1575,17 +1585,17 @@ int setup_wasapi_stream(cubeb_stream * s
     cubeb_resampler_create(stm,
                            has_input(stm) ? &input_params : nullptr,
                            has_output(stm) ? &output_params : nullptr,
                            target_sample_rate,
                            stm->data_callback,
                            stm->user_ptr,
                            CUBEB_RESAMPLER_QUALITY_DESKTOP);
   if (!stm->resampler) {
-    LOG("Could not get a resampler\n");
+    LOG("Could not get a resampler");
     return CUBEB_ERROR;
   }
 
   XASSERT(has_input(stm) || has_output(stm));
 
   if (has_input(stm) && has_output(stm)) {
     stm->refill_callback = refill_callback_duplex;
   } else if (has_input(stm)) {
@@ -1642,32 +1652,32 @@ wasapi_stream_init(cubeb * context, cube
   stm->latency = latency_frames;
   stm->volume = 1.0;
 
   // Placement new to call ctor.
   new (&stm->stream_reset_lock) owned_critical_section();
 
   stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->reconfigure_event) {
-    LOG("Can't create the reconfigure event, error: %x\n", GetLastError());
+    LOG("Can't create the reconfigure event, error: %x", GetLastError());
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   /* Unconditionally create the two events so that the wait logic is simpler. */
   stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->refill_event) {
-    LOG("Can't create the refill event, error: %x\n", GetLastError());
+    LOG("Can't create the refill event, error: %x", GetLastError());
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   stm->input_available_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->input_available_event) {
-    LOG("Can't create the input available event , error: %x\n", GetLastError());
+    LOG("Can't create the input available event , error: %x", GetLastError());
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
 
   {
     /* Locking here is not strictly necessary, because we don't have a
        notification client that can reset the stream yet, but it lets us
@@ -1679,17 +1689,17 @@ wasapi_stream_init(cubeb * context, cube
     wasapi_stream_destroy(stm);
     return rv;
   }
 
   hr = register_notification_client(stm);
   if (FAILED(hr)) {
     /* this is not fatal, we can still play audio, but we won't be able
        to keep using the default audio endpoint if it changes. */
-    LOG("failed to register notification client, %x\n", hr);
+    LOG("failed to register notification client, %x", hr);
   }
 
   *stream = stm;
 
   return CUBEB_OK;
 }
 
 void close_wasapi_stream(cubeb_stream * stm)
@@ -1753,40 +1763,40 @@ enum StreamDirection {
 
 int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
 {
   XASSERT((dir == OUTPUT && stm->output_client) ||
           (dir == INPUT && stm->input_client));
 
   HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
   if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
-    LOG("audioclient invalidated for %s device, reconfiguring\n",
+    LOG("audioclient invalidated for %s device, reconfiguring",
         dir == OUTPUT ? "output" : "input");
 
     BOOL ok = ResetEvent(stm->reconfigure_event);
     if (!ok) {
-      LOG("resetting reconfig event failed for %s stream: %x\n",
+      LOG("resetting reconfig event failed for %s stream: %x",
           dir == OUTPUT ? "output" : "input", GetLastError());
     }
 
     close_wasapi_stream(stm);
     int r = setup_wasapi_stream(stm);
     if (r != CUBEB_OK) {
-      LOG("reconfigure failed\n");
+      LOG("reconfigure failed");
       return r;
     }
 
     HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
     if (FAILED(hr2)) {
-      LOG("could not start the %s stream after reconfig: %x\n",
+      LOG("could not start the %s stream after reconfig: %x",
           dir == OUTPUT ? "output" : "input", hr);
       return CUBEB_ERROR;
     }
   } else if (FAILED(hr)) {
-    LOG("could not start the %s stream: %x.\n",
+    LOG("could not start the %s stream: %x.",
         dir == OUTPUT ? "output" : "input", hr);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 int wasapi_stream_start(cubeb_stream * stm)
@@ -1807,23 +1817,23 @@ int wasapi_stream_start(cubeb_stream * s
     int rv = stream_start_one_side(stm, INPUT);
     if (rv != CUBEB_OK) {
       return rv;
     }
   }
 
   stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->shutdown_event) {
-    LOG("Can't create the shutdown event, error: %x\n", GetLastError());
+    LOG("Can't create the shutdown event, error: %x", GetLastError());
     return CUBEB_ERROR;
   }
 
   stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
   if (stm->thread == NULL) {
-    LOG("could not create WASAPI render thread.\n");
+    LOG("could not create WASAPI render thread.");
     return CUBEB_ERROR;
   }
 
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
 
   return CUBEB_OK;
 }
 
@@ -1833,25 +1843,25 @@ int wasapi_stream_stop(cubeb_stream * st
   HRESULT hr;
 
   {
     auto_lock lock(stm->stream_reset_lock);
 
     if (stm->output_client) {
       hr = stm->output_client->Stop();
       if (FAILED(hr)) {
-        LOG("could not stop AudioClient (output)\n");
+        LOG("could not stop AudioClient (output)");
         return CUBEB_ERROR;
       }
     }
 
     if (stm->input_client) {
       hr = stm->input_client->Stop();
       if (FAILED(hr)) {
-        LOG("could not stop AudioClient (input)\n");
+        LOG("could not stop AudioClient (input)");
         return CUBEB_ERROR;
       }
     }
 
 
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   }
 
@@ -2137,46 +2147,46 @@ wasapi_enumerate_devices(cubeb * context
   *out = NULL;
 
   if (!com.ok())
     return CUBEB_ERROR;
 
   hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
       CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
   if (FAILED(hr)) {
-    LOG("Could not get device enumerator: %x\n", hr);
+    LOG("Could not get device enumerator: %x", hr);
     return CUBEB_ERROR;
   }
 
   if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
   else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
   else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll;
   else return CUBEB_ERROR;
 
   hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, &collection);
   if (FAILED(hr)) {
-    LOG("Could not enumerate audio endpoints: %x\n", hr);
+    LOG("Could not enumerate audio endpoints: %x", hr);
     return CUBEB_ERROR;
   }
 
   hr = collection->GetCount(&cc);
   if (FAILED(hr)) {
-    LOG("IMMDeviceCollection::GetCount() failed: %x\n", hr);
+    LOG("IMMDeviceCollection::GetCount() failed: %x", hr);
     return CUBEB_ERROR;
   }
   *out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) +
       sizeof(cubeb_device_info*) * (cc > 0 ? cc - 1 : 0));
   if (!*out) {
     return CUBEB_ERROR;
   }
   (*out)->count = 0;
   for (i = 0; i < cc; i++) {
     hr = collection->Item(i, &dev);
     if (FAILED(hr)) {
-      LOG("IMMDeviceCollection::Item(%u) failed: %x\n", i-1, hr);
+      LOG("IMMDeviceCollection::Item(%u) failed: %x", i-1, hr);
     } else if ((cur = wasapi_create_device(enumerator, dev)) != NULL) {
       (*out)->device[(*out)->count++] = cur;
     }
   }
 
   SafeRelease(collection);
   SafeRelease(enumerator);
   return CUBEB_OK;
--- a/media/libcubeb/tests/test_resampler.cpp
+++ b/media/libcubeb/tests/test_resampler.cpp
@@ -1,21 +1,21 @@
 /*
  * Copyright © 2016 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
 
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
-#ifndef CUBEB_GECKO_BUILD
-#include "config.h"
-#endif
 #include "cubeb_resampler_internal.h"
 #include <assert.h>
 #include <stdio.h>
 #include <algorithm>
 #include <iostream>
 
 /* Windows cmath USE_MATH_DEFINE thing... */
 const float PI = 3.14159265359f;
--- a/media/libcubeb/tests/test_sanity.cpp
+++ b/media/libcubeb/tests/test_sanity.cpp
@@ -13,29 +13,35 @@
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 #include "common.h"
 #ifdef CUBEB_GECKO_BUILD
 #include "TestHarness.h"
 #endif
 
-#define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0]))
 #define BEGIN_TEST fprintf(stderr, "START %s\n", __func__)
 #define END_TEST fprintf(stderr, "END %s\n", __func__)
 
 #define STREAM_RATE 44100
 #define STREAM_LATENCY 100 * STREAM_RATE / 1000
 #define STREAM_CHANNELS 1
 #if (defined(_WIN32) || defined(__WIN32__))
 #define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
 #else
 #define STREAM_FORMAT CUBEB_SAMPLE_S16LE
 #endif
 
+template<typename T, size_t N>
+constexpr size_t
+ARRAY_LENGTH(T(&)[N])
+{
+  return N;
+}
+
 static int dummy;
 static uint64_t total_frames_written;
 static int delay_callback;
 
 static long
 test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
 {
   assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -6,16 +6,17 @@ cp $1/LICENSE .
 cp $1/README.md .
 cp $1/include/cubeb/cubeb.h include
 cp $1/src/android/audiotrack_definitions.h src/android
 cp $1/src/android/sles_definitions.h src/android
 cp $1/src/cubeb-internal.h src
 cp $1/src/cubeb-speex-resampler.h src
 cp $1/src/cubeb.c src
 cp $1/src/cubeb_alsa.c src
+cp $1/src/cubeb_log.h src
 cp $1/src/cubeb_audiotrack.c src
 cp $1/src/cubeb_audiounit.cpp src
 cp $1/src/cubeb_osx_run_loop.h src
 cp $1/src/cubeb_jack.cpp src
 cp $1/src/cubeb_opensl.c src
 cp $1/src/cubeb_panner.cpp src
 cp $1/src/cubeb_panner.h src
 cp $1/src/cubeb_pulse.c src
@@ -51,15 +52,8 @@ if [ -n "$rev" ]; then
     version=$version-dirty
     echo "WARNING: updating from a dirty git repository."
   fi
   sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\}\./$version./" README_MOZILLA
   rm README_MOZILLA.bak
 else
   echo "Remember to update README_MOZILLA with the version details."
 fi
-
-echo "Applying a patch on top of $version"
-patch -p1 < ./wasapi-drift.patch
-echo "Applying another patch on top of $version"
-patch -p1 < ./wasapi-stereo-mic.patch
-echo "Applying another patch on top of $version"
-patch -p1 < ./bug1308418-mutex-copy-ctor.patch