Bug 1395393 - Update libcubeb to 2e5814de. r?achronop draft
authorPaul Adenot <paul@paul.cx>
Fri, 01 Sep 2017 17:39:06 +0200
changeset 657504 170547b55d8b04838523a255502c58991da8ffdb
parent 657491 79c73def3bdf45398212e2cd53137667098d560f
child 729447 4761d64a749cad736f724d440cdc2316685b59dd
push id77541
push userpaul@paul.cx
push dateFri, 01 Sep 2017 15:39:27 +0000
reviewersachronop
bugs1395393
milestone57.0a1
Bug 1395393 - Update libcubeb to 2e5814de. r?achronop MozReview-Commit-ID: JWDv93mjAAE
media/libcubeb/README_MOZILLA
media/libcubeb/gtest/test_sanity.cpp
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_jack.cpp
media/libcubeb/src/cubeb_log.cpp
media/libcubeb/src/cubeb_log.h
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_ringbuffer.h
media/libcubeb/src/cubeb_strings.c
media/libcubeb/src/cubeb_strings.h
media/libcubeb/src/cubeb_utils.c
media/libcubeb/src/cubeb_utils.h
media/libcubeb/src/cubeb_wasapi.cpp
media/libcubeb/src/cubeb_winmm.c
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 d59010398cee559349ba8f8363a7250b5279aa69 (2017-08-07 22:45:24 +1200)
+The git commit ID used was 2e5814de4fd9830d201d61b9d35ed24c2bba6d0f (2017-08-31 13:51:29 +0300)
--- a/media/libcubeb/gtest/test_sanity.cpp
+++ b/media/libcubeb/gtest/test_sanity.cpp
@@ -628,8 +628,47 @@ TEST(cubeb, DISABLED_eos_during_prefill)
 {
   // This test needs to be implemented.
 }
 
 TEST(cubeb, DISABLED_stream_destroy_pending_drain)
 {
   // This test needs to be implemented.
 }
+
+TEST(cubeb, stable_devid)
+{
+  /* Test that the devid field of cubeb_device_info is stable
+   * (ie. compares equal) over two invocations of
+   * cubeb_enumerate_devices(). */
+
+  int r;
+  cubeb * ctx;
+  cubeb_device_collection first;
+  cubeb_device_collection second;
+  cubeb_device_type all_devices =
+    (cubeb_device_type) (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
+  size_t n;
+
+  r = common_init(&ctx, "test_sanity");
+  ASSERT_EQ(r, CUBEB_OK);
+  ASSERT_NE(ctx, nullptr);
+
+  r = cubeb_enumerate_devices(ctx, all_devices, &first);
+  if (r == CUBEB_ERROR_NOT_SUPPORTED)
+    return;
+
+  ASSERT_EQ(r, CUBEB_OK);
+
+  r = cubeb_enumerate_devices(ctx, all_devices, &second);
+  ASSERT_EQ(r, CUBEB_OK);
+
+  ASSERT_EQ(first.count, second.count);
+  for (n = 0; n < first.count; n++) {
+    ASSERT_EQ(first.device[n].devid, second.device[n].devid);
+  }
+
+  r = cubeb_device_collection_destroy(ctx, &first);
+  ASSERT_EQ(r, CUBEB_OK);
+  r = cubeb_device_collection_destroy(ctx, &second);
+  ASSERT_EQ(r, CUBEB_OK);
+  cubeb_destroy(ctx);
+}
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -12,17 +12,16 @@
 #include <sys/time.h>
 #include <assert.h>
 #include <limits.h>
 #include <poll.h>
 #include <unistd.h>
 #include <alsa/asoundlib.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
-#include "cubeb_utils.h"
 
 #define CUBEB_STREAM_MAX 16
 #define CUBEB_WATCHDOG_MS 10000
 
 #define CUBEB_ALSA_PCM_NAME "default"
 
 #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
 
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -58,16 +58,18 @@ const char * DISPATCH_QUEUE_LABEL = "org
  * 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 audiounit_close_stream(cubeb_stream *stm);
 static int audiounit_setup_stream(cubeb_stream *stm);
+static std::vector<AudioObjectID>
+audiounit_get_devices_of_type(cubeb_device_type devtype);
 
 extern cubeb_ops const audiounit_ops;
 
 struct cubeb {
   cubeb_ops const * ops = &audiounit_ops;
   owned_critical_section mutex;
   std::atomic<int> active_streams{ 0 };
   uint32_t global_latency_frames = 0;
@@ -2869,45 +2871,16 @@ int audiounit_stream_register_device_cha
   auto_lock dev_cb_lock(stream->device_changed_callback_lock);
   /* Note: second register without unregister first causes 'nope' error.
    * Current implementation requires unregister before register a new cb. */
   assert(!stream->device_changed_callback);
   stream->device_changed_callback = device_changed_callback;
   return CUBEB_OK;
 }
 
-static OSStatus
-audiounit_get_devices(std::vector<AudioObjectID> & devices)
-{
-  OSStatus ret;
-  UInt32 size = 0;
-  AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices,
-                                     kAudioObjectPropertyScopeGlobal,
-                                     kAudioObjectPropertyElementMaster };
-
-  ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size);
-  if (ret != noErr) {
-    return ret;
-  }
-
-  uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
-  if (count == 0) {
-    return -1;
-  }
-  assert(devices.empty());
-  devices.resize(count);
-
-  ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, devices.data());
-  if (ret != noErr) {
-    devices.clear();
-  }
-
-  return ret;
-}
-
 static char *
 audiounit_strref_to_cstr_utf8(CFStringRef strref)
 {
   CFIndex len, size;
   char * ret;
   if (strref == NULL) {
     return NULL;
   }
@@ -3104,43 +3077,51 @@ audiounit_create_device_from_hwdev(cubeb
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
                             cubeb_device_collection * collection)
 {
-  std::vector<AudioObjectID> hwdevs;
-  uint32_t i;
-  OSStatus err;
-
-  err = audiounit_get_devices(hwdevs);
-  if (err != noErr) {
-    return CUBEB_ERROR;
+  std::vector<AudioObjectID> input_devs;
+  std::vector<AudioObjectID> output_devs;
+
+  // Count number of input and output devices.  This is not
+  // necessarily the same as the count of raw devices supported by the
+  // system since, for example, with Soundflower installed, some
+  // devices may report as being both input *and* output and cubeb
+  // separates those into two different devices.
+
+  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+    output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
   }
 
-  auto devices = new cubeb_device_info[hwdevs.size()];
+  if (type & CUBEB_DEVICE_TYPE_INPUT) {
+    input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
+  }
+
+  auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()];
   collection->count = 0;
 
   if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
-    for (i = 0; i < hwdevs.size(); i++) {
+    for (auto dev: output_devs) {
       auto device = &devices[collection->count];
-      auto err = audiounit_create_device_from_hwdev(device, hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT);
+      auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT);
       if (err != CUBEB_OK) {
         continue;
       }
       collection->count += 1;
     }
   }
 
   if (type & CUBEB_DEVICE_TYPE_INPUT) {
-    for (i = 0; i < hwdevs.size(); i++) {
+    for (auto dev: input_devs) {
       auto device = &devices[collection->count];
-      auto err = audiounit_create_device_from_hwdev(device, hwdevs[i], CUBEB_DEVICE_TYPE_INPUT);
+      auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT);
       if (err != CUBEB_OK) {
         continue;
       }
       collection->count += 1;
     }
   }
 
   if (collection->count > 0) {
--- a/media/libcubeb/src/cubeb_jack.cpp
+++ b/media/libcubeb/src/cubeb_jack.cpp
@@ -91,16 +91,18 @@ static void cbjack_destroy(cubeb * conte
 static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch);
 static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes);
 static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes);
 static int cbjack_stream_device_destroy(cubeb_stream * stream,
                                         cubeb_device * device);
 static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
 static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
                                     cubeb_device_collection * collection);
+static int cbjack_device_collection_destroy(cubeb * context,
+                                            cubeb_device_collection * collection);
 static int cbjack_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,
@@ -114,17 +116,17 @@ static int cbjack_stream_set_volume(cube
 static struct cubeb_ops const cbjack_ops = {
   .init = jack_init,
   .get_backend_id = cbjack_get_backend_id,
   .get_max_channel_count = cbjack_get_max_channel_count,
   .get_min_latency = cbjack_get_min_latency,
   .get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
   .get_preferred_channel_layout = NULL,
   .enumerate_devices = cbjack_enumerate_devices,
-  .device_collection_destroy = cubeb_utils_default_device_collection_destroy,
+  .device_collection_destroy = cbjack_device_collection_destroy,
   .destroy = cbjack_destroy,
   .stream_init = cbjack_stream_init,
   .stream_destroy = cbjack_stream_destroy,
   .stream_start = cbjack_stream_start,
   .stream_stop = cbjack_stream_stop,
   .stream_reset_default_device = NULL,
   .stream_get_position = cbjack_stream_get_position,
   .stream_get_latency = cbjack_get_latency,
@@ -969,60 +971,63 @@ cbjack_stream_device_destroy(cubeb_strea
   if (device->input_name)
     free(device->input_name);
   if (device->output_name)
     free(device->output_name);
   free(device);
   return CUBEB_OK;
 }
 
+#define JACK_DEFAULT_IN "JACK capture"
+#define JACK_DEFAULT_OUT "JACK playback"
+
 static int
 cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
                          cubeb_device_collection * collection)
 {
   if (!context)
     return CUBEB_ERROR;
 
   uint32_t rate;
   cbjack_get_preferred_sample_rate(context, &rate);
-  const char * j_in = "JACK capture";
-  const char * j_out = "JACK playback";
 
   cubeb_device_info * devices = new cubeb_device_info[2];
-    reinterpret_cast<cubeb_device_info *>(calloc(2, sizeof(cubeb_device_info)));
+  if (!devices)
+    return CUBEB_ERROR;
+  PodZero(devices, 2);
   collection->count = 0;
 
   if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
     cubeb_device_info * cur = &devices[collection->count];
-    cur->device_id = strdup(j_out);
+    cur->device_id = JACK_DEFAULT_OUT;
     cur->devid = (cubeb_devid) cur->device_id;
-    cur->friendly_name = strdup(j_out);
-    cur->group_id = strdup(j_out);
-    cur->vendor_name = strdup(j_out);
+    cur->friendly_name = JACK_DEFAULT_OUT;
+    cur->group_id = JACK_DEFAULT_OUT;
+    cur->vendor_name = JACK_DEFAULT_OUT;
     cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
     cur->state = CUBEB_DEVICE_STATE_ENABLED;
     cur->preferred = CUBEB_DEVICE_PREF_ALL;
     cur->format = CUBEB_DEVICE_FMT_F32NE;
     cur->default_format = CUBEB_DEVICE_FMT_F32NE;
     cur->max_channels = MAX_CHANNELS;
     cur->min_rate = rate;
     cur->max_rate = rate;
     cur->default_rate = rate;
     cur->latency_lo = 0;
     cur->latency_hi = 0;
     collection->count +=1 ;
   }
 
   if (type & CUBEB_DEVICE_TYPE_INPUT) {
     cubeb_device_info * cur = &devices[collection->count];
-    cur->device_id = strdup(j_in);
+    cur->device_id = JACK_DEFAULT_IN;
     cur->devid = (cubeb_devid) cur->device_id;
-    cur->friendly_name = strdup(j_in);
-    cur->group_id = strdup(j_in);
-    cur->vendor_name = strdup(j_in);
+    cur->friendly_name = JACK_DEFAULT_IN;
+    cur->group_id = JACK_DEFAULT_IN;
+    cur->vendor_name = JACK_DEFAULT_IN;
     cur->type = CUBEB_DEVICE_TYPE_INPUT;
     cur->state = CUBEB_DEVICE_STATE_ENABLED;
     cur->preferred = CUBEB_DEVICE_PREF_ALL;
     cur->format = CUBEB_DEVICE_FMT_F32NE;
     cur->default_format = CUBEB_DEVICE_FMT_F32NE;
     cur->max_channels = MAX_CHANNELS;
     cur->min_rate = rate;
     cur->max_rate = rate;
@@ -1031,8 +1036,17 @@ cbjack_enumerate_devices(cubeb * context
     cur->latency_hi = 0;
     collection->count += 1;
   }
 
   collection->device = devices;
 
   return CUBEB_OK;
 }
+
+static int
+cbjack_device_collection_destroy(cubeb * /*ctx*/,
+                                 cubeb_device_collection * collection)
+{
+  XASSERT(collection);
+  delete [] collection->device;
+  return CUBEB_OK;
+}
--- a/media/libcubeb/src/cubeb_log.cpp
+++ b/media/libcubeb/src/cubeb_log.cpp
@@ -90,16 +90,22 @@ public:
             break;
           }
           sleep_duration = remainder;
         } while (remainder.tv_sec || remainder.tv_nsec);
 #endif
       }
     }).detach();
   }
+  // Tell the underlying queue the producer thread has changed, so it does not
+  // assert in debug. This should be called with the thread stopped.
+  void reset_producer_thread()
+  {
+    msg_queue.reset_thread_ids();
+  }
 private:
 #ifndef _WIN32
   const struct timespec sleep_for = {
     CUBEB_LOG_BATCH_PRINT_INTERVAL_MS/1000,
     (CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000
   };
 #endif
   cubeb_async_logger()
@@ -123,8 +129,16 @@ void cubeb_async_log(char const * fmt, .
   // 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);
 }
+
+void cubeb_async_log_reset_threads()
+{
+  if (!g_cubeb_log_callback) {
+    return;
+  }
+  cubeb_async_logger::get().reset_producer_thread();
+}
--- a/media/libcubeb/src/cubeb_log.h
+++ b/media/libcubeb/src/cubeb_log.h
@@ -18,16 +18,17 @@ extern "C" {
 #define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
 #else
 #define PRINTF_FORMAT(fmt, args)
 #endif
 
 extern cubeb_log_level g_cubeb_log_level;
 extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
 void cubeb_async_log(const char * fmt, ...);
+void cubeb_async_log_reset_threads();
 
 #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__)
 
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -8,17 +8,17 @@
 #include <assert.h>
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <pulse/pulseaudio.h>
 #include <string.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 #include "cubeb_mixer.h"
-#include "cubeb_utils.h"
+#include "cubeb_strings.h"
 #include <stdio.h>
 
 #ifdef DISABLE_LIBPULSE_DLOPEN
 #define WRAP(x) x
 #else
 #define WRAP(x) cubeb_##x
 #define LIBPULSE_API_VISIT(X)                   \
   X(pa_channel_map_can_balance)                 \
@@ -97,16 +97,17 @@ struct cubeb {
   void * libpulse;
   pa_threaded_mainloop * mainloop;
   pa_context * context;
   pa_sink_info * default_sink_info;
   char * context_name;
   int error;
   cubeb_device_collection_changed_callback collection_changed_callback;
   void * collection_changed_user_ptr;
+  cubeb_strings * device_ids;
 };
 
 struct cubeb_stream {
   cubeb * context;
   pa_stream * output_stream;
   pa_stream * input_stream;
   cubeb_data_callback data_callback;
   cubeb_state_callback state_callback;
@@ -122,16 +123,34 @@ struct cubeb_stream {
 static const float PULSE_NO_GAIN = -1.0;
 
 enum cork_state {
   UNCORK = 0,
   CORK = 1 << 0,
   NOTIFY = 1 << 1
 };
 
+static int
+intern_device_id(cubeb * ctx, char const ** id)
+{
+  char const * interned;
+
+  assert(ctx);
+  assert(id);
+
+  interned = cubeb_strings_intern(ctx->device_ids, *id);
+  if (!interned) {
+    return CUBEB_ERROR;
+  }
+
+  *id = interned;
+
+  return CUBEB_OK;
+}
+
 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));
@@ -603,16 +622,20 @@ pulse_init(cubeb ** context, char const 
 #undef LOAD
 #endif
 
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
   ctx->ops = &pulse_ops;
   ctx->libpulse = libpulse;
+  if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
+    pulse_destroy(ctx);
+    return CUBEB_ERROR;
+  }
 
   ctx->mainloop = WRAP(pa_threaded_mainloop_new)();
   ctx->default_sink_info = NULL;
 
   WRAP(pa_threaded_mainloop_start)(ctx->mainloop);
 
   ctx->context_name = context_name ? strdup(context_name) : NULL;
   if (pulse_context_init(ctx) != 0) {
@@ -712,16 +735,20 @@ pulse_destroy(cubeb * ctx)
     pulse_context_destroy(ctx);
   }
 
   if (ctx->mainloop) {
     WRAP(pa_threaded_mainloop_stop)(ctx->mainloop);
     WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
   }
 
+  if (ctx->device_ids) {
+    cubeb_strings_destroy(ctx->device_ids);
+  }
+
   if (ctx->libpulse) {
     dlclose(ctx->libpulse);
   }
   free(ctx->default_sink_info);
   free(ctx);
 }
 
 static void pulse_stream_destroy(cubeb_stream * stm);
@@ -747,18 +774,18 @@ 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);
   assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
          (stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
-         stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
-         CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels)));
+         (stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
+         CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels))));
   *pa_stm = NULL;
   pa_sample_spec ss;
   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;
 
@@ -1198,28 +1225,35 @@ pulse_get_state_from_sink_port(pa_sink_p
 }
 
 static void
 pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
                    int eol, void * user_data)
 {
   pulse_dev_list_data * list_data = user_data;
   cubeb_device_info * devinfo;
-  const char * prop;
+  char const * prop = NULL;
+  char const * device_id = NULL;
 
   (void)context;
 
   if (eol || info == NULL)
     return;
 
+  device_id = info->name;
+  if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
+    assert(false);
+    return;
+  }
+
   pulse_ensure_dev_list_data_list_size(list_data);
   devinfo = &list_data->devinfo[list_data->count];
   memset(devinfo, 0, sizeof(cubeb_device_info));
 
-  devinfo->device_id = strdup(info->name);
+  devinfo->device_id = device_id;
   devinfo->devid = (cubeb_devid) devinfo->device_id;
   devinfo->friendly_name = strdup(info->description);
   prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
   if (prop)
     devinfo->group_id = strdup(prop);
   prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
   if (prop)
     devinfo->vendor_name = strdup(prop);
@@ -1260,28 +1294,35 @@ pulse_get_state_from_source_port(pa_sour
 }
 
 static void
 pulse_source_info_cb(pa_context * context, const pa_source_info * info,
     int eol, void * user_data)
 {
   pulse_dev_list_data * list_data = user_data;
   cubeb_device_info * devinfo;
-  const char * prop;
+  char const * prop = NULL;
+  char const * device_id = NULL;
 
   (void)context;
 
   if (eol)
     return;
 
+  device_id = info->name;
+  if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
+    assert(false);
+    return;
+  }
+
   pulse_ensure_dev_list_data_list_size(list_data);
   devinfo = &list_data->devinfo[list_data->count];
   memset(devinfo, 0, sizeof(cubeb_device_info));
 
-  devinfo->device_id = strdup(info->name);
+  devinfo->device_id = device_id;
   devinfo->devid = (cubeb_devid) devinfo->device_id;
   devinfo->friendly_name = strdup(info->description);
   prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
   if (prop)
     devinfo->group_id = strdup(prop);
   prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
   if (prop)
     devinfo->vendor_name = strdup(prop);
@@ -1360,16 +1401,31 @@ pulse_enumerate_devices(cubeb * context,
   collection->count = user_data.count;
 
   free(user_data.default_sink_name);
   free(user_data.default_source_name);
   return CUBEB_OK;
 }
 
 static int
+pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection)
+{
+  size_t n;
+
+  for (n = 0; n < collection->count; n++) {
+    free((void *) collection->device[n].friendly_name);
+    free((void *) collection->device[n].vendor_name);
+    free((void *) collection->device[n].group_id);
+  }
+
+  free(collection->device);
+  return CUBEB_OK;
+}
+
+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) {
@@ -1488,17 +1544,17 @@ pulse_register_device_collection_changed
 static struct cubeb_ops const pulse_ops = {
   .init = pulse_init,
   .get_backend_id = pulse_get_backend_id,
   .get_max_channel_count = pulse_get_max_channel_count,
   .get_min_latency = pulse_get_min_latency,
   .get_preferred_sample_rate = pulse_get_preferred_sample_rate,
   .get_preferred_channel_layout = pulse_get_preferred_channel_layout,
   .enumerate_devices = pulse_enumerate_devices,
-  .device_collection_destroy = cubeb_utils_default_device_collection_destroy,
+  .device_collection_destroy = pulse_device_collection_destroy,
   .destroy = pulse_destroy,
   .stream_init = pulse_stream_init,
   .stream_destroy = pulse_stream_destroy,
   .stream_start = pulse_stream_start,
   .stream_stop = pulse_stream_stop,
   .stream_reset_default_device = NULL,
   .stream_get_position = pulse_stream_get_position,
   .stream_get_latency = pulse_stream_get_latency,
--- a/media/libcubeb/src/cubeb_ringbuffer.h
+++ b/media/libcubeb/src/cubeb_ringbuffer.h
@@ -221,16 +221,27 @@ public:
    * Can be called safely on any thread.
    *
    * @return The maximum capacity of this ring buffer.
    */
   int capacity() const
   {
     return storage_capacity() - 1;
   }
+  /**
+   * Reset the consumer and producer thread identifier, in case the thread are
+   * being changed. This has to be externally synchronized. This is no-op when
+   * asserts are disabled.
+   */
+  void reset_thread_ids()
+  {
+#ifndef NDEBUG
+    consumer_id = producer_id = std::thread::id();
+#endif
+  }
 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(int read_index,
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_strings.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#include "cubeb_strings.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CUBEB_STRINGS_INLINE_COUNT 4
+
+struct cubeb_strings {
+  uint32_t size;
+  uint32_t count;
+  char ** data;
+  char * small_store[CUBEB_STRINGS_INLINE_COUNT];
+};
+
+int
+cubeb_strings_init(cubeb_strings ** strings)
+{
+  cubeb_strings* strs = NULL;
+
+  if (!strings) {
+    return CUBEB_ERROR;
+  }
+
+  strs = calloc(1, sizeof(cubeb_strings));
+  assert(strs);
+
+  if (!strs) {
+    return CUBEB_ERROR;
+  }
+
+  strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]);
+  strs->count = 0;
+  strs->data = strs->small_store;
+
+  *strings = strs;
+
+  return CUBEB_OK;
+}
+
+void
+cubeb_strings_destroy(cubeb_strings * strings)
+{
+  char ** sp = NULL;
+  char ** se = NULL;
+
+  if (!strings) {
+    return;
+  }
+
+  sp = strings->data;
+  se = sp + strings->count;
+
+  for ( ;  sp != se; sp++) {
+    if (*sp) {
+      free(*sp);
+    }
+  }
+
+  if (strings->data != strings->small_store) {
+    free(strings->data);
+  }
+
+  free(strings);
+}
+
+/** Look for string in string storage.
+    @param strings Opaque pointer to interned string storage.
+    @param s String to look up.
+    @retval Read-only string or NULL if not found. */
+static char const *
+cubeb_strings_lookup(cubeb_strings * strings, char const * s)
+{
+  char ** sp = NULL;
+  char ** se = NULL;
+
+  if (!strings || !s) {
+    return NULL;
+  }
+
+  sp = strings->data;
+  se = sp + strings->count;
+
+  for ( ; sp != se; sp++) {
+    if (*sp && strcmp(*sp, s) == 0) {
+      return *sp;
+    }
+  }
+
+  return NULL;
+}
+
+static char const *
+cubeb_strings_push(cubeb_strings * strings, char const * s)
+{
+  char * is = NULL;
+
+  if (strings->count == strings->size) {
+    char ** new_data;
+    uint32_t value_size = sizeof(char const *);
+    uint32_t new_size = strings->size * 2;
+    if (!new_size || value_size > (uint32_t)-1 / new_size) {
+      // overflow
+      return NULL;
+    }
+
+    if (strings->small_store == strings->data) {
+      // First time heap allocation.
+      new_data = malloc(new_size * value_size);
+      if (new_data) {
+        memcpy(new_data, strings->small_store, sizeof(strings->small_store));
+      }
+    } else {
+      new_data = realloc(strings->data, new_size * value_size);
+    }
+
+    if (!new_data) {
+      // out of memory
+      return NULL;
+    }
+
+    strings->size = new_size;
+    strings->data = new_data;
+  }
+
+  is = strdup(s);
+  strings->data[strings->count++] = is;
+
+  return is;
+}
+
+char const *
+cubeb_strings_intern(cubeb_strings * strings, char const * s)
+{
+  char const * is = NULL;
+
+  if (!strings || !s) {
+    return NULL;
+  }
+
+  is = cubeb_strings_lookup(strings, s);
+  if (is) {
+    return is;
+  }
+
+  return cubeb_strings_push(strings, s);
+}
+
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_strings.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#ifndef CUBEB_STRINGS_H
+#define CUBEB_STRINGS_H
+
+#include "cubeb/cubeb.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/** Opaque handle referencing interned string storage. */
+typedef struct cubeb_strings cubeb_strings;
+
+/** Initialize an interned string structure.
+    @param strings An out param where an opaque pointer to the
+    interned string storage will be returned.
+    @retval CUBEB_OK in case of success.
+    @retval CUBEB_ERROR in case of error. */
+CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);
+
+/** Destroy an interned string structure freeing all associated memory.
+    @param strings An opaque pointer to the interned string storage to
+                   destroy. */
+CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
+
+/** Add string to internal storage.
+    @param strings Opaque pointer to interned string storage.
+    @param s String to add to storage.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR
+ */
+CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // !CUBEB_STRINGS_H
deleted file mode 100644
--- a/media/libcubeb/src/cubeb_utils.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright © 2016 Mozilla Foundation
- *
- * This program is made available under an ISC-style license.  See the
- * accompanying file LICENSE for details.
- */
-
-#include "cubeb_utils.h"
-#include "cubeb_assert.h"
-
-#include <stdlib.h>
-
-static void
-device_info_destroy(cubeb_device_info * info)
-{
-  XASSERT(info);
-
-  free((void *) info->device_id);
-  free((void *) info->friendly_name);
-  free((void *) info->group_id);
-  free((void *) info->vendor_name);
-}
-
-int
-cubeb_utils_default_device_collection_destroy(cubeb * context,
-                                              cubeb_device_collection * collection)
-{
-  uint32_t i;
-  XASSERT(collection);
-
-  (void) context;
-
-  for (i = 0; i < collection->count; i++)
-    device_info_destroy(&collection->device[i]);
-
-  free(collection->device);
-  return CUBEB_OK;
-}
--- a/media/libcubeb/src/cubeb_utils.h
+++ b/media/libcubeb/src/cubeb_utils.h
@@ -331,22 +331,9 @@ struct auto_array_wrapper_impl : public 
 
 private:
   auto_array<T> ar;
 };
 
 using auto_lock = std::lock_guard<owned_critical_section>;
 #endif // __cplusplus
 
-// C language helpers
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int cubeb_utils_default_device_collection_destroy(cubeb * context,
-                                                  cubeb_device_collection * collection);
-
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* CUBEB_UTILS */
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -25,16 +25,17 @@
 #include <limits>
 #include <atomic>
 #include <vector>
 
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 #include "cubeb_mixer.h"
 #include "cubeb_resampler.h"
+#include "cubeb_strings.h"
 #include "cubeb_utils.h"
 
 #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
@@ -168,16 +169,17 @@ void close_wasapi_stream(cubeb_stream * 
 int setup_wasapi_stream(cubeb_stream * stm);
 static char const * wstr_to_utf8(wchar_t const * str);
 static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
 
 }
 
 struct cubeb {
   cubeb_ops const * ops = &wasapi_ops;
+  cubeb_strings * device_ids;
 };
 
 class wasapi_endpoint_notification_client;
 
 /* We have three possible callbacks we can use with a stream:
  * - input only
  * - output only
  * - synchronized input and output
@@ -377,16 +379,33 @@ public:
   }
 private:
   /* refcount for this instance, necessary to implement MSCOM semantics. */
   LONG ref_count;
   HANDLE reconfigure_event;
 };
 
 namespace {
+
+char const *
+intern_device_id(cubeb * ctx, wchar_t const * id)
+{
+  XASSERT(id);
+
+  char const * tmp = wstr_to_utf8(id);
+  if (!tmp)
+    return nullptr;
+
+  char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
+
+  free((void *) tmp);
+
+  return interned;
+}
+
 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;
@@ -422,35 +441,35 @@ static DWORD
 channel_layout_to_mask(cubeb_channel_layout layout)
 {
   XASSERT(layout < CUBEB_LAYOUT_MAX && "invalid conversion.");
 
   // This variable may be used for multiple times, so we should avoid to
   // allocate it in stack, or it will be created and removed repeatedly.
   // Use static to allocate this local variable in data space instead of stack.
   static DWORD map[CUBEB_LAYOUT_MAX] = {
-    0,                    // CUBEB_LAYOUT_UNDEFINED
-    MASK_DUAL_MONO,       // CUBEB_LAYOUT_DUAL_MONO
-    MASK_DUAL_MONO_LFE,   // CUBEB_LAYOUT_DUAL_MONO_LFE
-    MASK_MONO,            // CUBEB_LAYOUT_MONO
-    MASK_MONO_LFE,        // CUBEB_LAYOUT_MONO_LFE
-    MASK_STEREO,          // CUBEB_LAYOUT_STEREO
-    MASK_STEREO_LFE,      // CUBEB_LAYOUT_STEREO_LFE
-    MASK_3F,              // CUBEB_LAYOUT_3F
-    MASK_3F_LFE,          // CUBEB_LAYOUT_3F_LFE
-    MASK_2F1,             // CUBEB_LAYOUT_2F1
-    MASK_2F1_LFE,         // CUBEB_LAYOUT_2F1_LFE
-    MASK_3F1,             // CUBEB_LAYOUT_3F1
-    MASK_3F1_LFE,         // CUBEB_LAYOUT_3F1_LFE
-    MASK_2F2,             // CUBEB_LAYOUT_2F2
-    MASK_2F2_LFE,         // CUBEB_LAYOUT_2F2_LFE
-    MASK_3F2,             // CUBEB_LAYOUT_3F2
-    MASK_3F2_LFE,         // CUBEB_LAYOUT_3F2_LFE
-    MASK_3F3R_LFE,        // CUBEB_LAYOUT_3F3R_LFE
-    MASK_3F4_LFE,         // CUBEB_LAYOUT_3F4_LFE
+    KSAUDIO_SPEAKER_DIRECTOUT, // CUBEB_LAYOUT_UNDEFINED
+    MASK_DUAL_MONO,            // CUBEB_LAYOUT_DUAL_MONO
+    MASK_DUAL_MONO_LFE,        // CUBEB_LAYOUT_DUAL_MONO_LFE
+    MASK_MONO,                 // CUBEB_LAYOUT_MONO
+    MASK_MONO_LFE,             // CUBEB_LAYOUT_MONO_LFE
+    MASK_STEREO,               // CUBEB_LAYOUT_STEREO
+    MASK_STEREO_LFE,           // CUBEB_LAYOUT_STEREO_LFE
+    MASK_3F,                   // CUBEB_LAYOUT_3F
+    MASK_3F_LFE,               // CUBEB_LAYOUT_3F_LFE
+    MASK_2F1,                  // CUBEB_LAYOUT_2F1
+    MASK_2F1_LFE,              // CUBEB_LAYOUT_2F1_LFE
+    MASK_3F1,                  // CUBEB_LAYOUT_3F1
+    MASK_3F1_LFE,              // CUBEB_LAYOUT_3F1_LFE
+    MASK_2F2,                  // CUBEB_LAYOUT_2F2
+    MASK_2F2_LFE,              // CUBEB_LAYOUT_2F2_LFE
+    MASK_3F2,                  // CUBEB_LAYOUT_3F2
+    MASK_3F2_LFE,              // CUBEB_LAYOUT_3F2_LFE
+    MASK_3F3R_LFE,             // CUBEB_LAYOUT_3F3R_LFE
+    MASK_3F4_LFE,              // CUBEB_LAYOUT_3F4_LFE
   };
   return map[layout];
 }
 
 cubeb_channel_layout
 mask_to_channel_layout(WAVEFORMATEX const * fmt)
 {
   DWORD mask = 0;
@@ -1148,16 +1167,20 @@ int wasapi_init(cubeb ** context, char c
   if (FAILED(hr)) {
     LOG("Could not get device: %lx", hr);
     return CUBEB_ERROR;
   }
 
   cubeb * ctx = new cubeb();
 
   ctx->ops = &wasapi_ops;
+  if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
+    free(ctx);
+    return CUBEB_ERROR;
+  }
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 }
 
 namespace {
@@ -1215,16 +1238,20 @@ bool stop_and_join_render_thread(cubeb_s
     stm->shutdown_event = 0;
   }
 
   return rv;
 }
 
 void wasapi_destroy(cubeb * context)
 {
+  if (context->device_ids) {
+    cubeb_strings_destroy(context->device_ids);
+  }
+
   delete context;
 }
 
 char const * wasapi_get_backend_id(cubeb * context)
 {
   return "wasapi";
 }
 
@@ -1402,18 +1429,16 @@ waveformatex_update_derived_properties(W
   }
 }
 
 /* Based on the mix format and the stream format, try to find a way to play
    what the user requested. */
 static void
 handle_channel_layout(cubeb_stream * stm,  EDataFlow direction, com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params)
 {
-  // The CUBEB_LAYOUT_UNDEFINED can be used for input but it's not allowed for output.
-  XASSERT(direction == eCapture || stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
   com_ptr<IAudioClient> & audio_client = (direction == eRender) ? stm->output_client : stm->input_client;
   XASSERT(audio_client);
   /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
      so the reinterpret_cast below should be safe. In practice, this is not
      true, and we just want to bail out and let the rest of the code find a good
      conversion path instead of trying to make WASAPI do it by itself.
      [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
   if (mix_format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
@@ -1967,16 +1992,17 @@ int wasapi_stream_start(cubeb_stream * s
   }
 
   stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->shutdown_event) {
     LOG("Can't create the shutdown event, error: %lx", GetLastError());
     return CUBEB_ERROR;
   }
 
+  cubeb_async_log_reset_threads();
   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.");
     return CUBEB_ERROR;
   }
 
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
 
@@ -2169,18 +2195,18 @@ wasapi_is_default_device(EDataFlow flow,
       com_heap_ptr<wchar_t> defdevid(tmp);
       ret = (wcscmp(defdevid.get(), device_id) == 0);
     }
   }
 
   return ret;
 }
 
-static int
-wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
+int
+wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
 {
   com_ptr<IMMEndpoint> endpoint;
   com_ptr<IMMDevice> devnode;
   com_ptr<IAudioClient> client;
   EDataFlow flow;
   DWORD state = DEVICE_STATE_NOTPRESENT;
   com_ptr<IPropertyStore> propstore;
   REFERENCE_TIME def_period, min_period;
@@ -2199,93 +2225,97 @@ wasapi_create_device(cubeb_device_info *
   hr = endpoint->GetDataFlow(&flow);
   if (FAILED(hr)) return CUBEB_ERROR;
 
   wchar_t * tmp = nullptr;
   hr = dev->GetId(&tmp);
   if (FAILED(hr)) return CUBEB_ERROR;
   com_heap_ptr<wchar_t> device_id(tmp);
 
+  char const * device_id_intern = intern_device_id(ctx, device_id.get());
+  if (!device_id_intern) {
+    return CUBEB_ERROR;
+  }
+
   hr = dev->OpenPropertyStore(STGM_READ, propstore.receive());
   if (FAILED(hr)) return CUBEB_ERROR;
 
   hr = dev->GetState(&state);
   if (FAILED(hr)) return CUBEB_ERROR;
 
-  XASSERT(ret);
-  ret->device_id = wstr_to_utf8(device_id.get());
-  ret->devid = reinterpret_cast<cubeb_devid>(ret->device_id);
+  ret.device_id = device_id_intern;
+  ret.devid = reinterpret_cast<cubeb_devid>(ret.device_id);
   prop_variant namevar;
   hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
   if (SUCCEEDED(hr))
-    ret->friendly_name = wstr_to_utf8(namevar.pwszVal);
+    ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
 
   devnode = wasapi_get_device_node(enumerator, dev);
   if (devnode) {
     com_ptr<IPropertyStore> ps;
     hr = devnode->OpenPropertyStore(STGM_READ, ps.receive());
     if (FAILED(hr)) return CUBEB_ERROR;
 
     prop_variant instancevar;
     hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
     if (SUCCEEDED(hr)) {
-      ret->group_id = wstr_to_utf8(instancevar.pwszVal);
+      ret.group_id = wstr_to_utf8(instancevar.pwszVal);
     }
   }
 
-  ret->preferred = CUBEB_DEVICE_PREF_NONE;
+  ret.preferred = CUBEB_DEVICE_PREF_NONE;
   if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
-    ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
+    ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
   if (wasapi_is_default_device(flow, eCommunications, device_id.get(), enumerator))
-    ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE);
+    ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
   if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
-    ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
+    ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
 
-  if (flow == eRender) ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
-  else if (flow == eCapture) ret->type = CUBEB_DEVICE_TYPE_INPUT;
+  if (flow == eRender) ret.type = CUBEB_DEVICE_TYPE_OUTPUT;
+  else if (flow == eCapture) ret.type = CUBEB_DEVICE_TYPE_INPUT;
   switch (state) {
     case DEVICE_STATE_ACTIVE:
-      ret->state = CUBEB_DEVICE_STATE_ENABLED;
+      ret.state = CUBEB_DEVICE_STATE_ENABLED;
       break;
     case DEVICE_STATE_UNPLUGGED:
-      ret->state = CUBEB_DEVICE_STATE_UNPLUGGED;
+      ret.state = CUBEB_DEVICE_STATE_UNPLUGGED;
       break;
     default:
-      ret->state = CUBEB_DEVICE_STATE_DISABLED;
+      ret.state = CUBEB_DEVICE_STATE_DISABLED;
       break;
   };
 
-  ret->format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
-  ret->default_format = CUBEB_DEVICE_FMT_F32NE;
+  ret.format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
+  ret.default_format = CUBEB_DEVICE_FMT_F32NE;
   prop_variant fmtvar;
   hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
   if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
     if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
       const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(fmtvar.blob.pBlobData);
 
-      ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec;
-      ret->max_channels = pcm->wf.nChannels;
+      ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
+      ret.max_channels = pcm->wf.nChannels;
     } else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
       WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(fmtvar.blob.pBlobData);
 
       if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
           wfx->wFormatTag == WAVE_FORMAT_PCM) {
-        ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec;
-        ret->max_channels = wfx->nChannels;
+        ret.max_rate = ret.min_rate = ret.default_rate = wfx->nSamplesPerSec;
+        ret.max_channels = wfx->nChannels;
       }
     }
   }
 
   if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) &&
       SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
-    ret->latency_lo = hns_to_frames(ret->default_rate, min_period);
-    ret->latency_hi = hns_to_frames(ret->default_rate, def_period);
+    ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
+    ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
   } else {
-    ret->latency_lo = 0;
-    ret->latency_hi = 0;
+    ret.latency_lo = 0;
+    ret.latency_hi = 0;
   }
 
   return CUBEB_OK;
 }
 
 static int
 wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
                          cubeb_device_collection * out)
@@ -2318,48 +2348,63 @@ wasapi_enumerate_devices(cubeb * context
     return CUBEB_ERROR;
   }
 
   hr = collection->GetCount(&cc);
   if (FAILED(hr)) {
     LOG("IMMDeviceCollection::GetCount() failed: %lx", hr);
     return CUBEB_ERROR;
   }
-  cubeb_device_info * devices =
-    (cubeb_device_info *) calloc(cc, sizeof(cubeb_device_info));
-  if (!devices) {
+  cubeb_device_info * devices = new cubeb_device_info[cc];
+  if (!devices)
     return CUBEB_ERROR;
-  }
+
+  PodZero(devices, cc);
   out->count = 0;
   for (i = 0; i < cc; i++) {
     com_ptr<IMMDevice> dev;
     hr = collection->Item(i, dev.receive());
     if (FAILED(hr)) {
       LOG("IMMDeviceCollection::Item(%u) failed: %lx", i-1, hr);
       continue;
     }
-    auto cur = &devices[out->count];
-    if (wasapi_create_device(cur, enumerator.get(), dev.get()) == CUBEB_OK) {
+    if (wasapi_create_device(context, devices[out->count],
+                             enumerator.get(), dev.get()) == CUBEB_OK) {
       out->count += 1;
     }
   }
 
   out->device = devices;
   return CUBEB_OK;
 }
 
+static int
+wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * collection)
+{
+  XASSERT(collection);
+
+  for (size_t n = 0; n < collection->count; n++) {
+    cubeb_device_info& dev = collection->device[n];
+    delete [] dev.friendly_name;
+    delete [] dev.group_id;
+  }
+
+  delete [] collection->device;
+  return CUBEB_OK;
+}
+
 cubeb_ops const wasapi_ops = {
   /*.init =*/ wasapi_init,
   /*.get_backend_id =*/ wasapi_get_backend_id,
   /*.get_max_channel_count =*/ wasapi_get_max_channel_count,
   /*.get_min_latency =*/ wasapi_get_min_latency,
   /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate,
   /*.get_preferred_channel_layout =*/ wasapi_get_preferred_channel_layout,
   /*.enumerate_devices =*/ wasapi_enumerate_devices,
-  /*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy,
+  /*.device_collection_destroy =*/ wasapi_device_collection_destroy,
   /*.destroy =*/ wasapi_destroy,
   /*.stream_init =*/ wasapi_stream_init,
   /*.stream_destroy =*/ wasapi_stream_destroy,
   /*.stream_start =*/ wasapi_stream_start,
   /*.stream_stop =*/ wasapi_stream_stop,
   /*.stream_reset_default_device =*/ wasapi_stream_reset_default_device,
   /*.stream_get_position =*/ wasapi_stream_get_position,
   /*.stream_get_latency =*/ wasapi_stream_get_latency,
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -14,17 +14,16 @@
 #include <mmreg.h>
 #include <mmsystem.h>
 #include <process.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
-#include "cubeb_utils.h"
 
 /* This is missing from the MinGW headers. Use a safe fallback. */
 #if !defined(MEMORY_ALLOCATION_ALIGNMENT)
 #define MEMORY_ALLOCATION_ALIGNMENT 16
 #endif
 
 /**This is also missing from the MinGW headers. It  also appears to be undocumented by Microsoft.*/
 #ifndef WAVE_FORMAT_48M08
@@ -1012,25 +1011,45 @@ winmm_enumerate_devices(cubeb * context,
     }
   }
 
   collection->device = devices;
 
   return CUBEB_OK;
 }
 
+static int
+winmm_device_collection_destroy(cubeb * ctx,
+                                cubeb_device_collection * collection)
+{
+  uint32_t i;
+  XASSERT(collection);
+
+  (void) ctx;
+
+  for (i = 0; i < collection->count; i++) {
+    free((void *) collection->device[i].device_id);
+    free((void *) collection->device[i].friendly_name);
+    free((void *) collection->device[i].group_id);
+    free((void *) collection->device[i].vendor_name);
+  }
+
+  free(collection->device);
+  return CUBEB_OK;
+}
+
 static struct cubeb_ops const winmm_ops = {
   /*.init =*/ winmm_init,
   /*.get_backend_id =*/ winmm_get_backend_id,
   /*.get_max_channel_count=*/ winmm_get_max_channel_count,
   /*.get_min_latency=*/ winmm_get_min_latency,
   /*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
   /*.get_preferred_channel_layout =*/ NULL,
   /*.enumerate_devices =*/ winmm_enumerate_devices,
-  /*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy,
+  /*.device_collection_destroy =*/ winmm_device_collection_destroy,
   /*.destroy =*/ winmm_destroy,
   /*.stream_init =*/ winmm_stream_init,
   /*.stream_destroy =*/ winmm_stream_destroy,
   /*.stream_start =*/ winmm_stream_start,
   /*.stream_stop =*/ winmm_stream_stop,
   /*.stream_reset_default_device =*/ NULL,
   /*.stream_get_position =*/ winmm_stream_get_position,
   /*.stream_get_latency = */ winmm_stream_get_latency,
--- a/media/libcubeb/src/moz.build
+++ b/media/libcubeb/src/moz.build
@@ -8,16 +8,17 @@ DEFINES['CUBEB_GECKO_BUILD'] = True
 
 Library('cubeb')
 
 SOURCES += [
     'cubeb.c',
     'cubeb_log.cpp',
     'cubeb_mixer.cpp',
     'cubeb_panner.cpp',
+    'cubeb_strings.c',
     'cubeb_utils.c'
 ]
 
 if CONFIG['MOZ_ALSA']:
     SOURCES += [
         'cubeb_alsa.c',
     ]
     DEFINES['USE_ALSA'] = True
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -25,17 +25,18 @@ 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.c src
+cp $1/src/cubeb_strings.c src
+cp $1/src/cubeb_strings.h 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/test/common.h gtest
 cp $1/test/test_audio.cpp gtest
 cp $1/test/test_devices.cpp gtest