Bug 1341238 - Update cubeb to revision d974ad. draft
authorPaul Adenot <paul@paul.cx>
Tue, 14 Mar 2017 17:34:33 +0100
changeset 498420 a08de26a9459d9695c3a55f13475e26e43ba8b66
parent 498373 6690e9b920dae16f4f1c5c895757d036a909f00d
child 498421 c97bc807031f522e94d41b203e2b5f376e7c240e
push id49176
push userpaul@paul.cx
push dateTue, 14 Mar 2017 17:54:33 +0000
bugs1341238
milestone55.0a1
Bug 1341238 - Update cubeb to revision d974ad. MozReview-Commit-ID: HFcyEgYm8jn
media/libcubeb/README_MOZILLA
media/libcubeb/gtest/moz.build
media/libcubeb/gtest/test_audio.cpp
media/libcubeb/gtest/test_devices.cpp
media/libcubeb/gtest/test_duplex.cpp
media/libcubeb/gtest/test_latency.cpp
media/libcubeb/gtest/test_overload_callback.cpp
media/libcubeb/gtest/test_record.cpp
media/libcubeb/gtest/test_sanity.cpp
media/libcubeb/gtest/test_tone.cpp
media/libcubeb/include/cubeb.h
media/libcubeb/src/cubeb.c
media/libcubeb/src/cubeb_log.cpp
media/libcubeb/src/cubeb_log.h
media/libcubeb/src/cubeb_ringbuffer.h
media/libcubeb/src/cubeb_wasapi.cpp
media/libcubeb/src/moz.build
media/libcubeb/update.sh
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was f07ee6d5c536e2106ffe7a847074d7685ca07bd3 (2017-03-09 11:46:48 +0100)
+The git commit ID used was d974ad6b5fb71bae0c0089cbeb54645dfe4ecbc9 (2017-03-14 12:00:31 +1300)
--- a/media/libcubeb/gtest/moz.build
+++ b/media/libcubeb/gtest/moz.build
@@ -2,16 +2,17 @@
 # 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/.
 
 UNIFIED_SOURCES += [
 #  'test_duplex.cpp', # DISABLED: See bug 1314514.
 #  'test_record.cpp', # DISABLED: See bug 1314514.
+  'test_overload_callback.cpp',
   'test_tone.cpp',
   'test_utils.cpp'
 ]
 
 if CONFIG['MOZ_PULSEAUDIO'] or CONFIG['OS_TARGET'] in ('Darwin', 'WINNT', 'Android'):
   UNIFIED_SOURCES += [
     'test_resampler.cpp',
   ]
--- a/media/libcubeb/gtest/test_audio.cpp
+++ b/media/libcubeb/gtest/test_audio.cpp
@@ -114,17 +114,17 @@ int supports_channel_count(string backen
 }
 
 int run_test(int num_channels, layout_info layout, int sampling_rate, int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
 
-  r = cubeb_init(&ctx, "Cubeb audio test: channels");
+  r = cubeb_init(&ctx, "Cubeb audio test: channels", NULL);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     return r;
   }
   CubebCleaner cleanup_cubeb_at_exit(ctx);
 
   const char * backend_id = cubeb_get_backend_id(ctx);
 
@@ -163,17 +163,17 @@ int run_test(int num_channels, layout_in
 }
 
 int run_panning_volume_test(int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
 
-  r = cubeb_init(&ctx, "Cubeb audio test");
+  r = cubeb_init(&ctx, "Cubeb audio test", NULL);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     return r;
   }
 
   CubebCleaner cleanup_cubeb_at_exit(ctx);
 
   const char * backend_id = cubeb_get_backend_id(ctx);
--- a/media/libcubeb/gtest/test_devices.cpp
+++ b/media/libcubeb/gtest/test_devices.cpp
@@ -104,17 +104,17 @@ print_device_collection(cubeb_device_col
 }
 
 TEST(cubeb, enumerate_devices)
 {
   int r;
   cubeb * ctx = NULL;
   cubeb_device_collection * collection = NULL;
 
-  r = cubeb_init(&ctx, "Cubeb audio test");
+  r = cubeb_init(&ctx, "Cubeb audio test", NULL);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     ASSERT_EQ(r, CUBEB_OK);
   }
 
   fprintf(stdout, "Enumerating input devices for backend %s\n",
       cubeb_get_backend_id(ctx));
 
--- a/media/libcubeb/gtest/test_duplex.cpp
+++ b/media/libcubeb/gtest/test_duplex.cpp
@@ -77,17 +77,17 @@ TEST(cubeb, duplex)
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params input_params;
   cubeb_stream_params output_params;
   int r;
   user_state_duplex stream_state = { false };
   uint32_t latency_frames = 0;
 
-  r = cubeb_init(&ctx, "Cubeb duplex example");
+  r = cubeb_init(&ctx, "Cubeb duplex example", NULL);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     ASSERT_EQ(r, CUBEB_OK);
   }
 
   /* This test needs an available input device, skip it if this host does not
    * have one. */
   if (!has_available_input_device(ctx)) {
--- a/media/libcubeb/gtest/test_latency.cpp
+++ b/media/libcubeb/gtest/test_latency.cpp
@@ -6,17 +6,17 @@ TEST(cubeb, latency)
 {
   cubeb * ctx = NULL;
   int r;
   uint32_t max_channels;
   uint32_t preferred_rate;
   uint32_t latency_frames;
   cubeb_channel_layout layout;
 
-  r = cubeb_init(&ctx, "Cubeb audio test");
+  r = cubeb_init(&ctx, "Cubeb audio test", NULL);
   ASSERT_EQ(r, CUBEB_OK);
 
   r = cubeb_get_max_channel_count(ctx, &max_channels);
   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
     ASSERT_GT(max_channels, 0u);
   }
 
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/gtest/test_overload_callback.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2017 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#include "gtest/gtest.h"
+#if !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <atomic>
+#include "cubeb/cubeb.h"
+#include "common.h"
+
+#define SAMPLE_FREQUENCY 48000
+#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
+
+std::atomic<bool> load_callback{ false };
+
+long data_cb(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
+{
+  if (load_callback) {
+    printf("Sleeping...\n");
+    delay(100000);
+    printf("Sleeping done\n");
+  }
+  return nframes;
+}
+
+void state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
+{
+  assert(stream);
+
+  switch (state) {
+  case CUBEB_STATE_STARTED:
+    printf("stream started\n"); break;
+  case CUBEB_STATE_STOPPED:
+    printf("stream stopped\n"); break;
+  case CUBEB_STATE_DRAINED:
+    assert(false && "this test is not supposed to drain"); break;
+  case CUBEB_STATE_ERROR:
+    printf("stream error\n"); break;
+  default:
+    assert(false && "this test is not supposed to have a weird state"); break;
+  }
+}
+
+TEST(cubeb, overload_callback)
+{
+  cubeb * ctx;
+  cubeb_stream * stream;
+  cubeb_stream_params output_params;
+  int r;
+  uint32_t latency_frames = 0;
+
+  r = cubeb_init(&ctx, "Cubeb callback overload", NULL);
+  ASSERT_EQ(r, CUBEB_OK);
+
+  output_params.format = STREAM_FORMAT;
+  output_params.rate = 48000;
+  output_params.channels = 2;
+  output_params.layout = CUBEB_LAYOUT_STEREO;
+
+  r = cubeb_get_min_latency(ctx, output_params, &latency_frames);
+  ASSERT_EQ(r, CUBEB_OK);
+
+  r = cubeb_stream_init(ctx, &stream, "Cubeb",
+                        NULL, NULL, NULL, &output_params,
+                        latency_frames, data_cb, state_cb, NULL);
+  ASSERT_EQ(r, CUBEB_OK);
+
+  cubeb_stream_start(stream);
+  delay(500);
+  // This causes the callback to sleep for a large number of seconds.
+  load_callback = true;
+  delay(500);
+  cubeb_stream_stop(stream);
+
+  cubeb_stream_destroy(stream);
+  cubeb_destroy(ctx);
+}
--- a/media/libcubeb/gtest/test_record.cpp
+++ b/media/libcubeb/gtest/test_record.cpp
@@ -71,17 +71,17 @@ TEST(cubeb, record)
     printf("Set log callback failed\n");
   }
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params params;
   int r;
   user_state_record stream_state = { false };
 
-  r = cubeb_init(&ctx, "Cubeb record example");
+  r = cubeb_init(&ctx, "Cubeb record example", NULL);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     ASSERT_EQ(r, CUBEB_OK);
   }
 
   /* This test needs an available input device, skip it if this host does not
    * have one. */
   if (!has_available_input_device(ctx)) {
--- a/media/libcubeb/gtest/test_sanity.cpp
+++ b/media/libcubeb/gtest/test_sanity.cpp
@@ -80,17 +80,17 @@ test_state_callback(cubeb_stream * /*stm
 }
 
 TEST(cubeb, init_destroy_context)
 {
   int r;
   cubeb * ctx;
   char const* backend_id;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   backend_id = cubeb_get_backend_id(ctx);
   ASSERT_TRUE(backend_id);
 
   fprintf(stderr, "Backend: %s\n", backend_id);
 
@@ -101,17 +101,17 @@ TEST(cubeb, init_destroy_multiple_contex
 {
   size_t i;
   int r;
   cubeb * ctx[4];
   int order[4] = {2, 0, 3, 1};
   ASSERT_EQ(ARRAY_LENGTH(ctx), ARRAY_LENGTH(order));
 
   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
-    r = cubeb_init(&ctx[i], NULL);
+    r = cubeb_init(&ctx[i], NULL, NULL);
     ASSERT_EQ(r, CUBEB_OK);
     ASSERT_NE(ctx[i], nullptr);
   }
 
   /* destroy in a different order */
   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
     cubeb_destroy(ctx[order[i]]);
   }
@@ -119,17 +119,17 @@ TEST(cubeb, init_destroy_multiple_contex
 
 TEST(cubeb, context_variables)
 {
   int r;
   cubeb * ctx;
   uint32_t value;
   cubeb_stream_params params;
 
-  r = cubeb_init(&ctx, "test_context_variables");
+  r = cubeb_init(&ctx, "test_context_variables", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.channels = STREAM_CHANNELS;
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
@@ -152,17 +152,17 @@ TEST(cubeb, context_variables)
 
 TEST(cubeb, init_destroy_stream)
 {
   int r;
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params params;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
@@ -181,17 +181,17 @@ TEST(cubeb, init_destroy_stream)
 TEST(cubeb, init_destroy_multiple_streams)
 {
   size_t i;
   int r;
   cubeb * ctx;
   cubeb_stream * stream[8];
   cubeb_stream_params params;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
@@ -214,17 +214,17 @@ TEST(cubeb, init_destroy_multiple_stream
 
 TEST(cubeb, configure_stream)
 {
   int r;
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params params;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = 2; // panning
   params.layout = CUBEB_LAYOUT_STEREO;
 #if defined(__ANDROID__)
@@ -250,17 +250,17 @@ static void
 test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
 {
   size_t i;
   int r;
   cubeb * ctx;
   cubeb_stream * stream[8];
   cubeb_stream_params params;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
@@ -350,17 +350,17 @@ TEST(cubeb, init_destroy_multiple_contex
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
   params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
 #endif
 
   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
-    r = cubeb_init(&ctx[i], "test_sanity");
+    r = cubeb_init(&ctx[i], "test_sanity", NULL);
     ASSERT_EQ(r, CUBEB_OK);
     ASSERT_NE(ctx[i], nullptr);
 
     for (j = 0; j < streams_per_ctx; ++j) {
       r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
                             test_data_callback, test_state_callback, &dummy);
       ASSERT_EQ(r, CUBEB_OK);
       ASSERT_NE(stream[i * streams_per_ctx + j], nullptr);
@@ -378,17 +378,17 @@ TEST(cubeb, init_destroy_multiple_contex
 TEST(cubeb, basic_stream_operations)
 {
   int r;
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params params;
   uint64_t position;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
@@ -429,17 +429,17 @@ TEST(cubeb, stream_position)
   int r;
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params params;
   uint64_t position, last_position;
 
   total_frames_written = 0;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
@@ -569,17 +569,17 @@ TEST(cubeb, drain)
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params params;
   uint64_t position;
 
   delay_callback = 0;
   total_frames_written = 0;
 
-  r = cubeb_init(&ctx, "test_sanity");
+  r = cubeb_init(&ctx, "test_sanity", NULL);
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.format = STREAM_FORMAT;
   params.rate = STREAM_RATE;
   params.channels = STREAM_CHANNELS;
   params.layout = STREAM_LAYOUT;
 #if defined(__ANDROID__)
--- a/media/libcubeb/gtest/test_tone.cpp
+++ b/media/libcubeb/gtest/test_tone.cpp
@@ -96,17 +96,17 @@ void state_cb_tone(cubeb_stream *stream,
 TEST(cubeb, tone)
 {
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params params;
   struct cb_user_data *user_data;
   int r;
 
-  r = cubeb_init(&ctx, "Cubeb tone example");
+  r = cubeb_init(&ctx, "Cubeb tone example", NULL);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     ASSERT_EQ(r, CUBEB_OK);
   }
 
   params.format = STREAM_FORMAT;
   params.rate = SAMPLE_FREQUENCY;
   params.channels = 1;
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -409,20 +409,26 @@ typedef void (* cubeb_device_collection_
 typedef void (* cubeb_log_callback)(char const * 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.
+    @param backend_name The name of the cubeb backend user desires to select.
+                        Accepted values self-documented in cubeb.c: init_oneshot
+                        If NULL, a default ordering is used for backend choice.
+                        A valid choice overrides all other possible backends,
+                        so long as the backend was included at compile time.
     @retval CUBEB_OK in case of success.
     @retval CUBEB_ERROR in case of error, for example because the host
                         has no audio hardware. */
-CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name);
+CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name,
+                                              char const * backend_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. */
 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.
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -3,24 +3,22 @@
  *
  * 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>
+#include <string.h>
 #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;
 };
 
@@ -100,19 +98,72 @@ validate_latency(int latency)
 {
   if (latency < 1 || latency > 96000) {
     return CUBEB_ERROR_INVALID_PARAMETER;
   }
   return CUBEB_OK;
 }
 
 int
-cubeb_init(cubeb ** context, char const * context_name)
+cubeb_init(cubeb ** context, char const * context_name, char const * backend_name)
 {
-  int (* init[])(cubeb **, char const *) = {
+  int (* init_oneshot)(cubeb **, char const *) = NULL;
+
+  if (backend_name != NULL) {
+    if (!strcmp(backend_name, "pulse")) {
+#if defined(USE_PULSE)
+      init_oneshot = pulse_init;
+#endif
+    } else if (!strcmp(backend_name, "jack")) {
+#if defined(USE_JACK)
+      init_oneshot = jack_init;
+#endif
+    } else if (!strcmp(backend_name, "alsa")) {
+#if defined(USE_ALSA)
+      init_oneshot = alsa_init;
+#endif
+    } else if (!strcmp(backend_name, "audiounit")) {
+#if defined(USE_AUDIOUNIT)
+      init_oneshot = audiounit_init;
+#endif
+    } else if (!strcmp(backend_name, "wasapi")) {
+#if defined(USE_WASAPI)
+      init_oneshot = wasapi_init;
+#endif
+    } else if (!strcmp(backend_name, "winmm")) {
+#if defined(USE_WINMM)
+      init_oneshot = winmm_init;
+#endif
+    } else if (!strcmp(backend_name, "sndio")) {
+#if defined(USE_SNDIO)
+      init_oneshot = sndio_init;
+#endif
+    } else if (!strcmp(backend_name, "opensl")) {
+#if defined(USE_OPENSL)
+      init_oneshot = opensl_init;
+#endif
+    } else if (!strcmp(backend_name, "audiotrack")) {
+#if defined(USE_AUDIOTRACK)
+      init_oneshot = audiotrack_init;
+#endif
+    } else if (!strcmp(backend_name, "kai")) {
+#if defined(USE_KAI)
+      init_oneshot = kai_init;
+#endif
+    } else {
+      /* Already set */
+    }
+  }
+
+  int (* default_init[])(cubeb **, char const *) = {
+    /*
+     * init_oneshot must be at the top to allow user
+     * to override all other choices
+     */
+    init_oneshot,
 #if defined(USE_PULSE)
     pulse_init,
 #endif
 #if defined(USE_JACK)
     jack_init,
 #endif
 #if defined(USE_ALSA)
     alsa_init,
@@ -140,31 +191,30 @@ cubeb_init(cubeb ** context, char const 
 #endif
   };
   int i;
 
   if (!context) {
     return CUBEB_ERROR_INVALID_PARAMETER;
   }
 
-  for (i = 0; i < NELEMS(init); ++i) {
-    if (init[i](context, context_name) == CUBEB_OK) {
+#define OK(fn) assert((* context)->ops->fn)
+  for (i = 0; i < NELEMS(default_init); ++i) {
+    if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
       /* Assert that the minimal API is implemented. */
-#define OK(fn) assert((* context)->ops->fn)
       OK(get_backend_id);
       OK(destroy);
       OK(stream_init);
       OK(stream_destroy);
       OK(stream_start);
       OK(stream_stop);
       OK(stream_get_position);
       return CUBEB_OK;
     }
   }
-
   return CUBEB_ERROR;
 }
 
 char const *
 cubeb_get_backend_id(cubeb * context)
 {
   if (!context) {
     return NULL;
@@ -562,11 +612,20 @@ int cubeb_set_log_callback(cubeb_log_lev
 
   if (g_log_callback && log_callback) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
   g_log_callback = log_callback;
   g_log_level = log_level;
 
+  // Logging a message here allows to initialize the asynchronous logger from a
+  // thread that is not the audio rendering thread, and especially to not
+  // initialize it the first time we find a verbose log, which is often in the
+  // audio rendering callback, that runs from the audio rendering thread, and
+  // that is high priority, and that we don't want to block.
+  if (log_level >= CUBEB_LOG_VERBOSE) {
+    ALOGV("Starting cubeb log");
+  }
+
   return CUBEB_OK;
 }
 
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_log.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#define NOMINMAX
+
+#include "cubeb_log.h"
+#include "cubeb_ringbuffer.h"
+#include <cstdarg>
+
+cubeb_log_level g_log_level;
+cubeb_log_callback g_log_callback;
+
+/** The maximum size of a log message, after having been formatted. */
+const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
+/** The maximum number of log messages that can be queued before dropping
+ * messages. */
+const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
+/** Number of milliseconds to wait before dequeuing log messages. */
+const std::chrono::milliseconds CUBEB_LOG_BATCH_PRINT_INTERVAL_MS =
+                                              std::chrono::milliseconds(10);
+
+/**
+  * This wraps an inline buffer, that represents a log message, that must be
+  * null-terminated.
+  * This class should not use system calls or other potentially blocking code.
+  */
+class cubeb_log_message
+{
+public:
+  cubeb_log_message()
+  {
+    *storage = '\0';
+  }
+  cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
+  {
+    size_t length = strlen(str);
+    /* paranoia against malformed message */
+    assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
+    if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
+      return;
+    }
+    PodCopy(storage, str, length);
+    storage[length + 1] = '\0';
+  }
+  char const * get() {
+    return storage;
+  }
+private:
+  char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
+};
+
+/** Lock-free asynchronous logger, made so that logging from a
+ *  real-time audio callback does not block the audio thread. */
+class cubeb_async_logger
+{
+public:
+  /* This is thread-safe since C++11 */
+  static cubeb_async_logger & get() {
+    static cubeb_async_logger instance;
+    return instance;
+  }
+  void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
+  {
+    cubeb_log_message msg(str);
+    msg_queue.enqueue(msg);
+  }
+  void run()
+  {
+    std::thread([this]() {
+      while (true) {
+        cubeb_log_message msg;
+        while (msg_queue.dequeue(&msg, 1)) {
+          LOGV("%s", msg.get());
+        }
+        std::this_thread::sleep_for(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
+      }
+    }).detach();
+  }
+private:
+  cubeb_async_logger()
+    : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH)
+  {
+    run();
+  }
+  /** This is quite a big data structure, but is only instantiated if the
+   * asynchronous logger is used.*/
+  lock_free_queue<cubeb_log_message> msg_queue;
+};
+
+
+void cubeb_async_log(char const * fmt, ...)
+{
+  if (!g_log_callback) {
+    return;
+  }
+  // This is going to copy a 256 bytes array around, which is fine.
+  // We don't want to allocate memory here, because this is made to
+  // be called from a real-time callback.
+  va_list args;
+  va_start(args, fmt);
+  char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
+  vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
+  cubeb_async_logger::get().push(msg);
+  va_end(args);
+}
--- a/media/libcubeb/src/cubeb_log.h
+++ b/media/libcubeb/src/cubeb_log.h
@@ -3,35 +3,44 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 
 #ifndef CUBEB_LOG
 #define CUBEB_LOG
 
+#include "cubeb/cubeb.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #if defined(__GNUC__) || defined(__clang__)
 #define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
 #else
 #define PRINTF_FORMAT(fmt, args)
 #endif
 
 extern cubeb_log_level g_log_level;
 extern cubeb_log_callback g_log_callback PRINTF_FORMAT(1, 2);
+void cubeb_async_log(const char * fmt, ...);
 
 #ifdef __cplusplus
 }
 #endif
 
 #define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
 #define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
 
 #define LOG_INTERNAL(level, fmt, ...) do {                                   \
     if (g_log_callback && level <= g_log_level) {                            \
       g_log_callback("%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
     }                                                                        \
   } while(0)
 
+/* Asynchronous verbose logging, to log in real-time callbacks. */
+#define ALOGV(fmt, ...)                   \
+do {                                      \
+  cubeb_async_log(fmt, ##__VA_ARGS__);    \
+} while(0)
+
 #endif // CUBEB_LOG
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_ringbuffer.h
@@ -0,0 +1,532 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_RING_BUFFER_H
+#define CUBEB_RING_BUFFER_H
+
+#include "cubeb_utils.h"
+#include <algorithm>
+#include <atomic>
+#include <cstdint>
+#include <memory>
+#include <thread>
+
+/* This enum allows choosing the behaviour of the queue. */
+enum ThreadSafety
+{
+  /* No attempt to synchronize the queue is made. The queue is only safe when
+   * used on a single thread. */
+  Unsafe,
+  /** Atomics are used to synchronize read and write. The queue is safe when
+   * used from two thread: one producer, one consumer. */
+  Safe
+};
+
+/** Policy to enable thread safety on the queue. */
+template<ThreadSafety>
+struct ThreadSafePolicy;
+
+typedef int RingBufferIndex;
+
+/** Policy for thread-safe internal index for the queue. */
+template<>
+struct ThreadSafePolicy<Safe>
+{
+  typedef std::atomic<RingBufferIndex> IndexType;
+};
+
+/**
+ * This is the version with a simple `int` for index, for use when only a single
+ * thread is producing and releasing data.
+ */
+template<>
+struct ThreadSafePolicy<Unsafe>
+{
+  typedef RingBufferIndex IndexType;
+};
+
+/**
+ * Single producer single consumer lock-free and wait-free ring buffer.
+ *
+ * This data structure allows producing data from one thread, and consuming it on
+ * another thread, safely and without explicit synchronization. If used on two
+ * threads, this data structure uses atomics for thread safety. It is possible
+ * to disable the use of atomics at compile time and only use this data
+ * structure on one thread.
+ *
+ * The role for the producer and the consumer must be constant, i.e., the
+ * producer should always be on one thread and the consumer should always be on
+ * another thread.
+ *
+ * Some words about the inner workings of this class:
+ * - Capacity is fixed. Only one allocation is performed, in the constructor.
+ *   When reading and writing, the return value of the method allows checking if
+ *   the ring buffer is empty or full.
+ * - We always keep the read index at least one element ahead of the write
+ *   index, so we can distinguish between an empty and a full ring buffer: an
+ *   empty ring buffer is when the write index is at the same position as the
+ *   read index. A full buffer is when the write index is exactly one position
+ *   before the read index.
+ * - We synchronize updates to the read index after having read the data, and
+ *   the write index after having written the data. This means that the each
+ *   thread can only touch a portion of the buffer that is not touched by the
+ *   other thread.
+ * - Callers are expected to provide buffers. When writing to the queue,
+ *   elements are copied into the internal storage from the buffer passed in.
+ *   When reading from the queue, the user is expected to provide a buffer.
+ *   Because this is a ring buffer, data might not be contiguous in memory,
+ *   providing an external buffer to copy into is an easy way to have linear
+ *   data for further processing.
+ */
+template <typename T,
+          ThreadSafety Safety = ThreadSafety::Safe>
+class ring_buffer_base
+{
+public:
+  /**
+   * Constructor for a ring buffer.
+   *
+   * This performs an allocation, but is the only allocation that will happen
+   * for the life time of a `ring_buffer_base`.
+   *
+   * @param capacity The maximum number of element this ring buffer will hold.
+   */
+  ring_buffer_base(RingBufferIndex capacity)
+    /* One more element to distinguish from empty and full buffer. */
+    : capacity_(capacity + 1)
+  {
+    assert(storage_capacity() <
+           std::numeric_limits<RingBufferIndex>::max() / 2 &&
+           "buffer too large for the type of index used.");
+    assert(capacity_ > 0);
+
+    data_.reset(new T[storage_capacity()]);
+    /* If this queue is using atomics, initializing those members as the last
+     * action in the constructor acts as a full barrier, and allow capacity() to
+     * be thread-safe. */
+    write_index_ = 0;
+    read_index_ = 0;
+  }
+  /**
+   * Push `count` zero or default constructed elements in the array.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param count The number of elements to enqueue.
+   * @return The number of element enqueued.
+   */
+  RingBufferIndex enqueue_default(RingBufferIndex count)
+  {
+    return enqueue(nullptr, count);
+  }
+  /**
+   * @brief Put an element in the queue
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param element The element to put in the queue.
+   *
+   * @return 1 if the element was inserted, 0 otherwise.
+   */
+  RingBufferIndex enqueue(T& element)
+  {
+    return enqueue(&element, 1);
+  }
+  /**
+   * Push `count` elements in the ring buffer.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param elements a pointer to a buffer containing at least `count` elements.
+   * If `elements` is nullptr, zero or default constructed elements are enqueued.
+   * @param count The number of elements to read from `elements`
+   * @return The number of elements successfully coped from `elements` and inserted
+   * into the ring buffer.
+   */
+  RingBufferIndex enqueue(T * elements, RingBufferIndex count)
+  {
+#ifndef NDEBUG
+    assert_correct_thread(producer_id);
+#endif
+
+    RingBufferIndex rd_idx = read_index_;
+    RingBufferIndex wr_idx = write_index_;
+
+    if (full_internal(rd_idx, wr_idx)) {
+      return 0;
+    }
+
+    RingBufferIndex to_write =
+      std::min(available_write_internal(rd_idx, wr_idx), count);
+
+    /* First part, from the write index to the end of the array. */
+    RingBufferIndex first_part = std::min(storage_capacity() - wr_idx,
+                                          to_write);
+    /* Second part, from the beginning of the array */
+    RingBufferIndex second_part = to_write - first_part;
+
+    if (elements) {
+      Copy(data_.get() + wr_idx, elements, first_part);
+      Copy(data_.get(), elements + first_part, second_part);
+    } else {
+      ConstructDefault(data_.get() + wr_idx, first_part);
+      ConstructDefault(data_.get(), second_part);
+    }
+
+    write_index_ = increment_index(wr_idx, to_write);
+
+    return to_write;
+  }
+  /**
+   * Retrieve at most `count` elements from the ring buffer, and copy them to
+   * `elements`, if non-null.
+   *
+   * Only safely called on the consumer side.
+   *
+   * @param elements A pointer to a buffer with space for at least `count`
+   * elements. If `elements` is `nullptr`, `count` element will be discarded.
+   * @param count The maximum number of elements to dequeue.
+   * @return The number of elements written to `elements`.
+   */
+  RingBufferIndex dequeue(T * elements, RingBufferIndex count)
+  {
+#ifndef NDEBUG
+    assert_correct_thread(consumer_id);
+#endif
+
+    RingBufferIndex  wr_idx = write_index_;
+    RingBufferIndex  rd_idx = read_index_;
+
+    if (empty_internal(rd_idx, wr_idx)) {
+      return 0;
+    }
+
+    RingBufferIndex to_read =
+      std::min(available_read_internal(rd_idx, wr_idx), count);
+
+    RingBufferIndex first_part = std::min(storage_capacity() - rd_idx, to_read);
+    RingBufferIndex second_part = to_read - first_part;
+
+    if (elements) {
+      Copy(elements, data_.get() + rd_idx, first_part);
+      Copy(elements + first_part, data_.get(), second_part);
+    }
+
+    read_index_ = increment_index(rd_idx, to_read);
+
+    return to_read;
+  }
+  /**
+   * Get the number of available element for consuming.
+   *
+   * Only safely called on the consumer thread.
+   *
+   * @return The number of available elements for reading.
+   */
+  RingBufferIndex available_read() const
+  {
+#ifndef NDEBUG
+    assert_correct_thread(consumer_id);
+#endif
+    return available_read_internal(read_index_, write_index_);
+  }
+  /**
+   * Get the number of available elements for consuming.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @return The number of empty slots in the buffer, available for writing.
+   */
+  RingBufferIndex available_write() const
+  {
+#ifndef NDEBUG
+    assert_correct_thread(producer_id);
+#endif
+    return available_write_internal(read_index_, write_index_);
+  }
+  /**
+   * Get the total capacity, for this ring buffer.
+   *
+   * Can be called safely on any thread.
+   *
+   * @return The maximum capacity of this ring buffer.
+   */
+  RingBufferIndex capacity() const
+  {
+    return storage_capacity() - 1;
+  }
+private:
+  /** Return true if the ring buffer is empty.
+   *
+   * @param read_index the read index to consider
+   * @param write_index the write index to consider
+   * @return true if the ring buffer is empty, false otherwise.
+   **/
+  bool empty_internal(RingBufferIndex read_index,
+                      RingBufferIndex write_index) const
+  {
+    return write_index == read_index;
+  }
+  /** Return true if the ring buffer is full.
+   *
+   * This happens if the write index is exactly one element behind the read
+   * index.
+   *
+   * @param read_index the read index to consider
+   * @param write_index the write index to consider
+   * @return true if the ring buffer is full, false otherwise.
+   **/
+  bool full_internal(RingBufferIndex read_index,
+                     RingBufferIndex write_index) const
+  {
+    return (write_index + 1) % storage_capacity() == read_index;
+  }
+  /**
+   * Return the size of the storage. It is one more than the number of elements
+   * that can be stored in the buffer.
+   *
+   * @return the number of elements that can be stored in the buffer.
+   */
+  int storage_capacity() const
+  {
+    return capacity_;
+  }
+  /**
+   * Returns the number of elements available for reading.
+   *
+   * @return the number of available elements for reading.
+   */
+  RingBufferIndex
+  available_read_internal(RingBufferIndex read_index,
+                          RingBufferIndex write_index) const
+  {
+    if (write_index >= read_index) {
+      return write_index - read_index;
+    } else {
+      return write_index + storage_capacity() - read_index;
+    }
+  }
+  /**
+   * Returns the number of empty elements, available for writing.
+   *
+   * @return the number of elements that can be written into the array.
+   */
+  RingBufferIndex
+  available_write_internal(RingBufferIndex read_index,
+                           RingBufferIndex write_index) const
+  {
+    /* We substract one element here to always keep at least one sample
+     * free in the buffer, to distinguish between full and empty array. */
+    int rv = read_index - write_index - 1;
+    if (write_index >= read_index) {
+      rv += storage_capacity();
+    }
+    return rv;
+  }
+  /**
+   * Increments an index, wrapping it around the storage.
+   *
+   * @param index a reference to the index to increment.
+   * @param increment the number by which `index` is incremented.
+   * @return the new index.
+   */
+  RingBufferIndex
+  increment_index(RingBufferIndex index, RingBufferIndex increment) const
+  {
+    assert(increment >= 0);
+    return (index + increment) % storage_capacity();
+  }
+  /**
+   * @brief This allows checking that enqueue (resp. dequeue) are always called
+   * by the right thread.
+   *
+   * @param id the id of the thread that has called the calling method first.
+   */
+#ifndef NDEBUG
+  static void assert_correct_thread(std::thread::id& id)
+  {
+    if (id == std::thread::id()) {
+      id = std::this_thread::get_id();
+      return;
+    }
+    assert(id == std::this_thread::get_id());
+  }
+#endif
+  /** Index at which the oldest element is at, in samples. */
+  typename ThreadSafePolicy<Safety>::IndexType read_index_;
+  /** Index at which to write new elements. `write_index` is always at
+   * least one element ahead of `read_index_`. */
+  typename ThreadSafePolicy<Safety>::IndexType write_index_;
+  /** Maximum number of elements that can be stored in the ring buffer. */
+  const int capacity_;
+  /** Data storage */
+  std::unique_ptr<T[]> data_;
+#ifndef NDEBUG
+  /** The id of the only thread that is allowed to read from the queue. */
+  mutable std::thread::id consumer_id;
+  /** The id of the only thread that is allowed to write from the queue. */
+  mutable std::thread::id producer_id;
+#endif
+};
+
+/**
+ * Adapter for `ring_buffer_base` that exposes an interface in frames.
+ */
+template <typename T,
+          ThreadSafety Safety = ThreadSafety::Safe>
+class audio_ring_buffer_base
+{
+public:
+  /**
+   * @brief Constructor.
+   *
+   * @param channel_count       Number of channels.
+   * @param capacity_in_frames  The capacity in frames.
+   */
+  audio_ring_buffer_base(int channel_count, int capacity_in_frames)
+    : channel_count(channel_count)
+    , ring_buffer(frames_to_samples(capacity_in_frames))
+  {
+    assert(channel_count > 0);
+  }
+  /**
+   * @brief Enqueue silence.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @param frame_count The number of frames of silence to enqueue.
+   * @return  The number of frames of silence actually written to the queue.
+   */
+  int enqueue_default(int frame_count)
+  {
+    return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
+  }
+  /**
+   * @brief Enqueue `frames_count` frames of audio.
+   * 
+   * Only safely called from the producer thread.
+   *
+   * @param [in] frames If non-null, the frames to enqueue.
+   *                    Otherwise, silent frames are enqueued.
+   * @param frame_count The number of frames to enqueue.
+   *
+   * @return The number of frames enqueued
+   */
+
+  int enqueue(T * frames, int frame_count)
+  {
+    return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
+  }
+
+  /**
+   * @brief Removes `frame_count` frames from the buffer, and
+   *        write them to `frames` if it is non-null.
+   *
+   * Only safely called on the consumer thread.
+   *
+   * @param frames      If non-null, the frames are copied to `frames`.
+   *                    Otherwise, they are dropped.
+   * @param frame_count The number of frames to remove.
+   *
+   * @return  The number of frames actually dequeud.
+   */
+  int dequeue(T * frames, int frame_count)
+  {
+    return samples_to_frames(ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
+  }
+  /**
+   * Get the number of available frames of audio for consuming.
+   *
+   * Only safely called on the consumer thread.
+   *
+   * @return The number of available frames of audio for reading.
+   */
+  int available_read() const
+  {
+    return samples_to_frames(ring_buffer.available_read());
+  }
+  /**
+   * Get the number of available frames of audio for consuming.
+   *
+   * Only safely called on the producer thread.
+   *
+   * @return The number of empty slots in the buffer, available for writing.
+   */
+  int available_write() const
+  {
+    return samples_to_frames(ring_buffer.available_write());
+  }
+  /**
+   * Get the total capacity, for this ring buffer.
+   *
+   * Can be called safely on any thread.
+   *
+   * @return The maximum capacity of this ring buffer.
+   */
+  int capacity() const
+  {
+    return samples_to_frames(ring_buffer.capacity());
+  }
+private:
+  /**
+   * @brief Frames to samples conversion.
+   *
+   * @param frames The number of frames.
+   *
+   * @return  A number of samples.
+   */
+  int frames_to_samples(int frames) const
+  {
+    return frames * channel_count;
+  }
+  /**
+   * @brief Samples to frames conversion.
+   *
+   * @param samples The number of samples.
+   *
+   * @return  A number of frames.
+   */
+  int samples_to_frames(int samples) const
+  {
+    return samples / channel_count;
+  }
+  /** Number of channels of audio that will stream through this ring buffer. */
+  int channel_count;
+  /** The underlying ring buffer that is used to store the data. */
+  ring_buffer_base<T, Safety> ring_buffer;
+};
+
+/**
+ * Lock-free instantiation of the `ring_buffer_base` type. This is safe to use
+ * from two threads, one producer, one consumer (that never change role),
+ * without explicit synchronization.
+ */
+template<typename T>
+using lock_free_queue = ring_buffer_base<T, Safe>;
+/**
+ * An instantiation of the `ring_buffer_base` type, to be used on a single
+ * thread: it is not safe to use from multiple threads without explicit external
+ * synchronization.
+ */
+template<typename T>
+using queue = ring_buffer_base<T, Unsafe>;
+/**
+ * Lock-free instantiation of the `audio_ring_buffer` type. This is safe to use
+ * from two threads, one producer, one consumer (that never change role),
+ * without explicit synchronization.
+ */
+template<typename T>
+using lock_free_audio_ring_buffer = audio_ring_buffer_base<T, Safe>;
+/**
+ * An instantiation of the `audio_ring_buffer` type, to be used on a single
+ * thread: it is not safe to use from multiple threads without explicit external
+ * synchronization.
+ */
+template<typename T>
+using audio_ring_buffer = audio_ring_buffer_base<T, Unsafe>;
+
+#endif // CUBEB_RING_BUFFER_H
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -729,18 +729,19 @@ refill_callback_duplex(cubeb_stream * st
   }
 
   /* This can only happen when debugging, and having breakpoints set in the
    * callback in a way that it makes the stream underrun. */
   if (output_frames == 0) {
     return true;
   }
 
-  LOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
-       stm->linear_input_buffer.length(), output_frames);
+
+  ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
+        stm->linear_input_buffer.length(), output_frames);
 
   refill(stm,
          stm->linear_input_buffer.data(),
          stm->linear_input_buffer.length(),
          output_buffer,
          output_frames);
 
   stm->linear_input_buffer.clear();
@@ -765,17 +766,17 @@ refill_callback_input(cubeb_stream * stm
     return rv;
   }
 
   // This can happen at the very beginning of the stream.
   if (!stm->linear_input_buffer.length()) {
     return true;
   }
 
-  LOGV("Input callback: input frames: %Iu", stm->linear_input_buffer.length());
+  ALOGV("Input callback: input frames: %Iu", stm->linear_input_buffer.length());
 
   long read = refill(stm,
                      stm->linear_input_buffer.data(),
                      stm->linear_input_buffer.length(),
                      nullptr,
                      0);
 
   XASSERT(read >= 0);
@@ -806,18 +807,18 @@ refill_callback_output(cubeb_stream * st
   }
 
   long got = refill(stm,
                     nullptr,
                     0,
                     output_buffer,
                     output_frames);
 
-  LOGV("Output callback: output frames requested: %Iu, got %ld",
-       output_frames, got);
+  ALOGV("Output callback: output frames requested: %Iu, got %ld",
+        output_frames, got);
 
   XASSERT(got >= 0);
   XASSERT((unsigned long) got == output_frames || stm->draining);
 
   hr = stm->render_client->ReleaseBuffer(got, 0);
   if (FAILED(hr)) {
     LOG("failed to release buffer: %lx", hr);
     return false;
@@ -1161,16 +1162,22 @@ bool stop_and_join_render_thread(cubeb_s
 {
   bool rv = true;
   LOG("Stop and join render thread.");
   if (!stm->thread) {
     LOG("No thread present.");
     return true;
   }
 
+  // If we've already leaked the thread, just return,
+  // there is not much we can do.
+  if (!stm->emergency_bailout.load()) {
+    return false;
+  }
+
   BOOL ok = SetEvent(stm->shutdown_event);
   if (!ok) {
     LOG("Destroy SetEvent failed: %lx", 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);
--- a/media/libcubeb/src/moz.build
+++ b/media/libcubeb/src/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEFINES['CUBEB_GECKO_BUILD'] = True
 
 Library('cubeb')
 
 SOURCES += [
     'cubeb.c',
+    'cubeb_log.cpp',
     'cubeb_mixer.cpp',
     'cubeb_panner.cpp'
 ]
 
 if CONFIG['MOZ_ALSA']:
     SOURCES += [
         'cubeb_alsa.c',
     ]
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -6,50 +6,53 @@ 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_array_queue.h src
 cp $1/src/cubeb_audiotrack.c src
 cp $1/src/cubeb_audiounit.cpp src
+cp $1/src/cubeb_jack.cpp src
+cp $1/src/cubeb_log.cpp src
+cp $1/src/cubeb_log.h src
+cp $1/src/cubeb_mixer.cpp src
+cp $1/src/cubeb_mixer.h src
+cp $1/src/cubeb_opensl.c 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_array_queue.h src
 cp $1/src/cubeb_panner.cpp src
 cp $1/src/cubeb_panner.h src
 cp $1/src/cubeb_pulse.c src
 cp $1/src/cubeb_resampler.cpp src
 cp $1/src/cubeb_resampler.h src
 cp $1/src/cubeb_resampler_internal.h src
 cp $1/src/cubeb_ring_array.h src
+cp $1/src/cubeb_ringbuffer.h src
 cp $1/src/cubeb_sndio.c src
 cp $1/src/cubeb_utils.h src
 cp $1/src/cubeb_utils_unix.h src
 cp $1/src/cubeb_utils_win.h src
 cp $1/src/cubeb_wasapi.cpp src
 cp $1/src/cubeb_winmm.c src
-cp $1/src/cubeb_mixer.h src
-cp $1/src/cubeb_mixer.cpp src
 cp $1/test/common.h gtest
 cp $1/test/test_audio.cpp gtest
 cp $1/test/test_devices.cpp gtest
 cp $1/test/test_duplex.cpp gtest
 cp $1/test/test_latency.cpp gtest
+cp $1/test/test_mixer.cpp gtest
+cp $1/test/test_overload_callback.cpp gtest
 cp $1/test/test_record.cpp gtest
 cp $1/test/test_resampler.cpp gtest
 cp $1/test/test_ring_array.cpp gtest
 cp $1/test/test_sanity.cpp gtest
 cp $1/test/test_tone.cpp gtest
 cp $1/test/test_utils.cpp gtest
-cp $1/test/test_mixer.cpp gtest
 
 if [ -d $1/.git ]; then
   rev=$(cd $1 && git rev-parse --verify HEAD)
   date=$(cd $1 && git show -s --format=%ci HEAD)
   dirty=$(cd $1 && git diff-index --name-only HEAD)
 fi
 
 if [ -n "$rev" ]; then