Bug 1366707 - Update cubeb from upstream to 96cdb17. r?kinetik draft
authorAlex Chronopoulos <achronop@gmail.com>
Tue, 23 May 2017 11:29:33 +0300
changeset 582858 4e3fd1b4d82b3de5df613b2ea51b66bb07cb415d
parent 582257 9851fcb0bf4d855c36729d7de19f0fa5c9f69776
child 582859 49a2197f60f71faf4c932a342e73740f33a6dd11
push id60214
push userachronop@gmail.com
push dateTue, 23 May 2017 08:31:51 +0000
reviewerskinetik
bugs1366707
milestone55.0a1
Bug 1366707 - Update cubeb from upstream to 96cdb17. r?kinetik MozReview-Commit-ID: 3aiEtrl289U
media/libcubeb/README_MOZILLA
media/libcubeb/gtest/common.h
media/libcubeb/gtest/test_devices.cpp
media/libcubeb/gtest/test_resampler.cpp
media/libcubeb/include/cubeb.h
media/libcubeb/src/cubeb-internal.h
media/libcubeb/src/cubeb.c
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiotrack.c
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_jack.cpp
media/libcubeb/src/cubeb_opensl.c
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_sndio.c
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 26a50b08889a16d3f1e7fe05845cc107c7553f70 (2017-05-05 19:29:05 +1200)
+The git commit ID used was 96cdb173f86dfc86cbd21d097b24ec1e256d69fc (2017-05-22 11:55:15 +0300)
--- a/media/libcubeb/gtest/common.h
+++ b/media/libcubeb/gtest/common.h
@@ -77,31 +77,32 @@ int has_available_input_device(cubeb * c
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &devices);
   if (r != CUBEB_OK) {
     fprintf(stderr, "error enumerating devices.");
     return 0;
   }
 
   if (devices->count == 0) {
     fprintf(stderr, "no input device available, skipping test.\n");
+    cubeb_device_collection_destroy(ctx, devices);
     return 0;
   }
 
   for (uint32_t i = 0; i < devices->count; i++) {
     input_device_available |= (devices->device[i]->state ==
                                CUBEB_DEVICE_STATE_ENABLED);
   }
 
   if (!input_device_available) {
     fprintf(stderr, "there are input devices, but they are not "
         "available, skipping\n");
-    return 0;
   }
 
-  return 1;
+  cubeb_device_collection_destroy(ctx, devices);
+  return !!input_device_available;
 }
 
 void print_log(const char * msg, ...)
 {
   va_list args;
   va_start(args, msg);
   vprintf(msg, args);
   va_end(args);
--- a/media/libcubeb/gtest/test_devices.cpp
+++ b/media/libcubeb/gtest/test_devices.cpp
@@ -124,20 +124,20 @@ TEST(cubeb, enumerate_devices)
     fprintf(stderr, "Device enumeration not supported"
                     " for this backend, skipping this test.\n");
     r = CUBEB_OK;
   }
   ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
 
   fprintf(stdout, "Found %u input devices\n", collection->count);
   print_device_collection(collection, stdout);
-  cubeb_device_collection_destroy(collection);
+  cubeb_device_collection_destroy(ctx, collection);
 
   fprintf(stdout, "Enumerating output devices for backend %s\n",
           cubeb_get_backend_id(ctx));
 
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
   ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
 
   fprintf(stdout, "Found %u output devices\n", collection->count);
   print_device_collection(collection, stdout);
-  cubeb_device_collection_destroy(collection);
+  cubeb_device_collection_destroy(ctx, collection);
 }
--- a/media/libcubeb/gtest/test_resampler.cpp
+++ b/media/libcubeb/gtest/test_resampler.cpp
@@ -572,16 +572,18 @@ TEST(cubeb, resampler_passthrough_output
 
   float output_buffer[output_channels * 256];
 
   long got;
   for (uint32_t i = 0; i < 30; i++) {
     got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256);
     ASSERT_EQ(got, 256);
   }
+
+  cubeb_resampler_destroy(resampler);
 }
 
 // gtest does not support using ASSERT_EQ and friend in a function that returns
 // a value.
 void check_input(const void * input_buffer, void * output_buffer, long frame_count)
 {
   ASSERT_EQ(output_buffer, nullptr);
   ASSERT_EQ(frame_count, 256);
@@ -615,16 +617,18 @@ TEST(cubeb, resampler_passthrough_input_
   float input_buffer[input_channels * 256];
 
   long got;
   for (uint32_t i = 0; i < 30; i++) {
     long int frames = 256;
     got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0);
     ASSERT_EQ(got, 256);
   }
+
+  cubeb_resampler_destroy(resampler);
 }
 
 template<typename T>
 long seq(T* array, int stride, long start, long count)
 {
   for(int i = 0; i < count; i++) {
     for (int j = 0; j < stride; j++) {
       array[i + j] = static_cast<T>(start + i);
@@ -743,9 +747,11 @@ TEST(cubeb, resampler_passthrough_duplex
       seq_idx = seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE);
       long normal_input_frame_count = 256;
       got = cubeb_resampler_fill(resampler, input_buffer_normal, &normal_input_frame_count, output_buffer, BUF_BASE_SIZE);
       is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
       output_seq_idx += BUF_BASE_SIZE;
     }
     ASSERT_EQ(got, BUF_BASE_SIZE);
   }
+
+  cubeb_resampler_destroy(resampler);
 }
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -325,19 +325,20 @@ typedef enum {
   CUBEB_DEVICE_PREF_NONE          = 0x00,
   CUBEB_DEVICE_PREF_MULTIMEDIA    = 0x01,
   CUBEB_DEVICE_PREF_VOICE         = 0x02,
   CUBEB_DEVICE_PREF_NOTIFICATION  = 0x04,
   CUBEB_DEVICE_PREF_ALL           = 0x0F
 } cubeb_device_pref;
 
 /** This structure holds the characteristics
- *  of an input or output audio device. It can be obtained using
- *  `cubeb_enumerate_devices`, and must be destroyed using
- *  `cubeb_device_info_destroy`. */
+ *  of an input or output audio device. It is obtained using
+ *  `cubeb_enumerate_devices`, which returns these structures via
+ *  `cubeb_device_collection` and must be destroyed via
+ *  `cubeb_device_collection_destroy`. */
 typedef struct {
   cubeb_devid devid;          /**< Device identifier handle. */
   char const * device_id;     /**< Device identifier which might be presented in a UI. */
   char const * friendly_name; /**< Friendly device name which might be presented in a UI. */
   char const * group_id;      /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
   char const * vendor_name;   /**< Optional vendor name, may be NULL. */
 
   cubeb_device_type type;     /**< Type of device (Input/Output). */
@@ -350,20 +351,22 @@ typedef struct {
   unsigned int default_rate;  /**< Default/Preferred sample rate. */
   unsigned int max_rate;      /**< Maximum sample rate supported. */
   unsigned int min_rate;      /**< Minimum sample rate supported. */
 
   unsigned int latency_lo; /**< Lowest possible latency in frames. */
   unsigned int latency_hi; /**< Higest possible latency in frames. */
 } cubeb_device_info;
 
-/** Device collection. */
+/** Device collection.
+ *  Returned by `cubeb_enumerate_devices` and destroyed by
+ *  `cubeb_device_collection_destroy`. */
 typedef struct {
   uint32_t count;                 /**< Device count in collection. */
-  cubeb_device_info * device[1];   /**< Array of pointers to device info. */
+  cubeb_device_info * device[1];  /**< Array of pointers to device info. */
 } cubeb_device_collection;
 
 /** User supplied data callback.
     - Calling other cubeb functions from this callback is unsafe.
     - The code in the callback should be non-blocking.
     - Returning less than the number of frames this callback asks for or
       provides puts the stream in drain mode. This callback will not be called
       again, and the state callback will be called with CUBEB_STATE_DRAINED when
@@ -607,26 +610,22 @@ CUBEB_EXPORT int cubeb_stream_register_d
     @retval CUBEB_OK in case of success
     @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
     @retval CUBEB_ERROR_NOT_SUPPORTED */
 CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
                                          cubeb_device_type devtype,
                                          cubeb_device_collection ** collection);
 
 /** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
+    @param context
     @param collection collection to destroy
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
-CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb_device_collection * collection);
-
-/** Destroy a cubeb_device_info structure.
-    @param info pointer to device info structure
-    @retval CUBEB_OK
-    @retval CUBEB_ERROR_INVALID_PARAMETER if info is an invalid pointer */
-CUBEB_EXPORT int cubeb_device_info_destroy(cubeb_device_info * info);
+CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
+                                                 cubeb_device_collection * collection);
 
 /** Registers a callback which is called when the system detects
     a new device or a device is removed.
     @param context
     @param devtype device type to include
     @param callback a function called whenever the system device list changes.
            Passing NULL allow to unregister a function
     @param user_ptr pointer to user specified data which will be present in
--- a/media/libcubeb/src/cubeb-internal.h
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -47,16 +47,17 @@ struct cubeb_ops {
   int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels);
   int (* get_min_latency)(cubeb * context,
                           cubeb_stream_params params,
                           uint32_t * latency_ms);
   int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
   int (* get_preferred_channel_layout)(cubeb * context, cubeb_channel_layout * layout);
   int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
                             cubeb_device_collection ** collection);
+  int (* device_collection_destroy)(cubeb * context, cubeb_device_collection * collection);
   void (* destroy)(cubeb * context);
   int (* 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,
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -566,43 +566,26 @@ int cubeb_enumerate_devices(cubeb * cont
     for (uint32_t i = 0; i < (*collection)->count; i++) {
       log_device((*collection)->device[i]);
     }
   }
 
   return rv;
 }
 
-int cubeb_device_collection_destroy(cubeb_device_collection * collection)
+int cubeb_device_collection_destroy(cubeb * context,
+                                    cubeb_device_collection * collection)
 {
-  uint32_t i;
-
-  if (collection == NULL)
+  if (context == NULL || collection == NULL)
     return CUBEB_ERROR_INVALID_PARAMETER;
 
-  for (i = 0; i < collection->count; i++)
-    cubeb_device_info_destroy(collection->device[i]);
-
-  free(collection);
-  return CUBEB_OK;
-}
+  if (!context->ops->device_collection_destroy)
+    return CUBEB_ERROR_NOT_SUPPORTED;
 
-int cubeb_device_info_destroy(cubeb_device_info * info)
-{
-  if (info == NULL) {
-    return CUBEB_ERROR_INVALID_PARAMETER;
-  }
-
-  free((void *) info->device_id);
-  free((void *) info->friendly_name);
-  free((void *) info->group_id);
-  free((void *) info->vendor_name);
-
-  free(info);
-  return CUBEB_OK;
+  return context->ops->device_collection_destroy(context, collection);
 }
 
 int cubeb_register_device_collection_changed(cubeb * context,
                                              cubeb_device_type devtype,
                                              cubeb_device_collection_changed_callback callback,
                                              void * user_ptr)
 {
   if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -12,16 +12,17 @@
 #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"
 
@@ -1337,16 +1338,17 @@ alsa_enumerate_devices(cubeb * context, 
 static struct cubeb_ops const alsa_ops = {
   .init = alsa_init,
   .get_backend_id = alsa_get_backend_id,
   .get_max_channel_count = alsa_get_max_channel_count,
   .get_min_latency = alsa_get_min_latency,
   .get_preferred_sample_rate = alsa_get_preferred_sample_rate,
   .get_preferred_channel_layout = NULL,
   .enumerate_devices = alsa_enumerate_devices,
+  .device_collection_destroy = cubeb_utils_default_device_collection_destroy,
   .destroy = alsa_destroy,
   .stream_init = alsa_stream_init,
   .stream_destroy = alsa_stream_destroy,
   .stream_start = alsa_stream_start,
   .stream_stop = alsa_stream_stop,
   .stream_get_position = alsa_stream_get_position,
   .stream_get_latency = alsa_stream_get_latency,
   .stream_set_volume = alsa_stream_set_volume,
--- a/media/libcubeb/src/cubeb_audiotrack.c
+++ b/media/libcubeb/src/cubeb_audiotrack.c
@@ -418,16 +418,17 @@ audiotrack_stream_set_volume(cubeb_strea
 static struct cubeb_ops const audiotrack_ops = {
   .init = audiotrack_init,
   .get_backend_id = audiotrack_get_backend_id,
   .get_max_channel_count = audiotrack_get_max_channel_count,
   .get_min_latency = audiotrack_get_min_latency,
   .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate,
   .get_preferred_channel_layout = NULL,
   .enumerate_devices = NULL,
+  .device_collection_destroy = NULL,
   .destroy = audiotrack_destroy,
   .stream_init = audiotrack_stream_init,
   .stream_destroy = audiotrack_stream_destroy,
   .stream_start = audiotrack_stream_start,
   .stream_stop = audiotrack_stream_stop,
   .stream_get_position = audiotrack_stream_get_position,
   .stream_get_latency = audiotrack_stream_get_latency,
   .stream_set_volume = audiotrack_stream_set_volume,
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -598,29 +598,38 @@ audiounit_get_input_device_id(AudioDevic
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
 static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
+static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
 
 static int
 audiounit_reinit_stream(cubeb_stream * stm)
 {
   auto_lock context_lock(stm->context->mutex);
   if (!stm->shutdown) {
     audiounit_stream_stop_internal(stm);
   }
 
+  int r = audiounit_uninstall_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not uninstall the device changed callback", stm);
+  }
+
   {
     auto_lock lock(stm->mutex);
     float volume = 0.0;
-    int vol_rv = audiounit_stream_get_volume(stm, &volume);
+    int vol_rv = CUBEB_ERROR;
+    if (has_output(stm)) {
+      vol_rv = audiounit_stream_get_volume(stm, &volume);
+    }
 
     audiounit_close_stream(stm);
 
     if (audiounit_setup_stream(stm) != CUBEB_OK) {
       LOG("(%p) Stream reinit failed.", stm);
       return CUBEB_ERROR;
     }
 
@@ -672,21 +681,18 @@ audiounit_property_listener_callback(Aud
             return noErr;
           }
           // Allow restart to choose the new default. Event register only for input.
           stm->input_device = 0;
         }
         break;
       case kAudioDevicePropertyDataSource: {
           LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource", (unsigned int) i);
-          if (has_output(stm)) {
-              stm->output_device = 0;
-          }
+          return noErr;
         }
-        break;
       default:
         LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
         return noErr;
     }
   }
 
   for (UInt32 i = 0; i < address_count; i++) {
     switch(addresses[i].mSelector) {
@@ -2511,21 +2517,16 @@ audiounit_stream_init(cubeb * context,
   return CUBEB_OK;
 }
 
 static void
 audiounit_close_stream(cubeb_stream *stm)
 {
   stm->mutex.assert_current_thread_owns();
 
-  int r = audiounit_uninstall_device_changed_callback(stm);
-  if (r != CUBEB_OK) {
-    LOG("(%p) Could not uninstall the device changed callback", stm);
-  }
-
   if (stm->input_unit) {
     AudioUnitUninitialize(stm->input_unit);
     AudioComponentInstanceDispose(stm->input_unit);
     stm->input_unit = nullptr;
   }
 
   stm->input_linear_buffer.reset();
 
@@ -2549,23 +2550,30 @@ audiounit_stream_destroy(cubeb_stream * 
 {
   stm->shutdown = true;
 
   int r = audiounit_uninstall_system_changed_callback(stm);
   if (r != CUBEB_OK) {
     LOG("(%p) Could not uninstall the device changed callback", stm);
   }
 
+  r = audiounit_uninstall_device_changed_callback(stm);
+  if (r != CUBEB_OK) {
+    LOG("(%p) Could not uninstall the device changed callback", stm);
+  }
+
   auto_lock context_lock(stm->context->mutex);
   audiounit_stream_stop_internal(stm);
 
-  {
+  // Execute close in serial queue to avoid collision
+  // with reinit when un/plug devices
+  dispatch_sync(stm->context->serial_queue, ^() {
     auto_lock lock(stm->mutex);
     audiounit_close_stream(stm);
-  }
+  });
 
   assert(stm->context->active_streams >= 1);
   stm->context->active_streams -= 1;
 
   LOG("Cubeb stream (%p) destroyed successful.", stm);
   delete stm;
 }
 
@@ -3292,16 +3300,17 @@ int audiounit_register_device_collection
 cubeb_ops const audiounit_ops = {
   /*.init =*/ audiounit_init,
   /*.get_backend_id =*/ audiounit_get_backend_id,
   /*.get_max_channel_count =*/ audiounit_get_max_channel_count,
   /*.get_min_latency =*/ audiounit_get_min_latency,
   /*.get_preferred_sample_rate =*/ audiounit_get_preferred_sample_rate,
   /*.get_preferred_channel_layout =*/ audiounit_get_preferred_channel_layout,
   /*.enumerate_devices =*/ audiounit_enumerate_devices,
+  /*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy,
   /*.destroy =*/ audiounit_destroy,
   /*.stream_init =*/ audiounit_stream_init,
   /*.stream_destroy =*/ audiounit_stream_destroy,
   /*.stream_start =*/ audiounit_stream_start,
   /*.stream_stop =*/ audiounit_stream_stop,
   /*.stream_get_position =*/ audiounit_stream_get_position,
   /*.stream_get_latency =*/ audiounit_stream_get_latency,
   /*.stream_set_volume =*/ audiounit_stream_set_volume,
--- a/media/libcubeb/src/cubeb_jack.cpp
+++ b/media/libcubeb/src/cubeb_jack.cpp
@@ -14,16 +14,17 @@
 #include <string.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <math.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 #include "cubeb_resampler.h"
+#include "cubeb_utils.h"
 
 #include <jack/jack.h>
 #include <jack/statistics.h>
 
 #define JACK_API_VISIT(X)                       \
   X(jack_activate)                              \
   X(jack_client_close)                          \
   X(jack_client_open)                           \
@@ -111,16 +112,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,
   .destroy = cbjack_destroy,
   .stream_init = cbjack_stream_init,
   .stream_destroy = cbjack_stream_destroy,
   .stream_start = cbjack_stream_start,
   .stream_stop = cbjack_stream_stop,
   .stream_get_position = cbjack_stream_get_position,
   .stream_get_latency = cbjack_get_latency,
   .stream_set_volume = cbjack_stream_set_volume,
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -1735,16 +1735,17 @@ opensl_stream_set_volume(cubeb_stream * 
 static struct cubeb_ops const opensl_ops = {
   .init = opensl_init,
   .get_backend_id = opensl_get_backend_id,
   .get_max_channel_count = opensl_get_max_channel_count,
   .get_min_latency = opensl_get_min_latency,
   .get_preferred_sample_rate = opensl_get_preferred_sample_rate,
   .get_preferred_channel_layout = NULL,
   .enumerate_devices = NULL,
+  .device_collection_destroy = NULL,
   .destroy = opensl_destroy,
   .stream_init = opensl_stream_init,
   .stream_destroy = opensl_stream_destroy,
   .stream_start = opensl_stream_start,
   .stream_stop = opensl_stream_stop,
   .stream_get_position = opensl_stream_get_position,
   .stream_get_latency = opensl_stream_get_latency,
   .stream_set_volume = opensl_stream_set_volume,
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -8,16 +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 <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)                 \
@@ -1490,16 +1491,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,
   .destroy = pulse_destroy,
   .stream_init = pulse_stream_init,
   .stream_destroy = pulse_stream_destroy,
   .stream_start = pulse_stream_start,
   .stream_stop = pulse_stream_stop,
   .stream_get_position = pulse_stream_get_position,
   .stream_get_latency = pulse_stream_get_latency,
   .stream_set_volume = pulse_stream_set_volume,
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -365,16 +365,17 @@ sndio_stream_get_latency(cubeb_stream * 
 static struct cubeb_ops const sndio_ops = {
   .init = sndio_init,
   .get_backend_id = sndio_get_backend_id,
   .get_max_channel_count = sndio_get_max_channel_count,
   .get_min_latency = sndio_get_min_latency,
   .get_preferred_sample_rate = sndio_get_preferred_sample_rate,
   .get_preferred_channel_layout = NULL,
   .enumerate_devices = NULL,
+  .device_collection_destroy = NULL,
   .destroy = sndio_destroy,
   .stream_init = sndio_stream_init,
   .stream_destroy = sndio_stream_destroy,
   .stream_start = sndio_stream_start,
   .stream_stop = sndio_stream_stop,
   .stream_get_position = sndio_stream_get_position,
   .stream_get_latency = sndio_stream_get_latency,
   .stream_set_volume = sndio_stream_set_volume,
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_utils.c
@@ -0,0 +1,40 @@
+/*
+ * 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);
+
+  free(info);
+}
+
+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);
+  return CUBEB_OK;
+}
--- a/media/libcubeb/src/cubeb_utils.h
+++ b/media/libcubeb/src/cubeb_utils.h
@@ -3,16 +3,20 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 
 #if !defined(CUBEB_UTILS)
 #define CUBEB_UTILS
 
+#include "cubeb/cubeb.h"
+
+#ifdef __cplusplus
+
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
 #include <mutex>
 #include <type_traits>
 #if defined(WIN32)
 #include "cubeb_utils_win.h"
 #else
@@ -325,10 +329,24 @@ struct auto_array_wrapper_impl : public 
     ar.clear();
   }
 
 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
@@ -416,24 +416,23 @@ double stream_to_mix_samplerate_ratio(cu
 #define MASK_3F2            (MASK_3F | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
 #define MASK_3F2_LFE        (KSAUDIO_SPEAKER_5POINT1_SURROUND)
 #define MASK_3F3R_LFE       (MASK_3F2_LFE | SPEAKER_BACK_CENTER)
 #define MASK_3F4_LFE        (KSAUDIO_SPEAKER_7POINT1_SURROUND)
 
 static DWORD
 channel_layout_to_mask(cubeb_channel_layout layout)
 {
-  XASSERT(layout > CUBEB_LAYOUT_UNDEFINED && layout < CUBEB_LAYOUT_MAX &&
-          "This mask conversion is not allowed.");
+  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 (this won't be used.)
+    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
@@ -1385,19 +1384,22 @@ waveformatex_update_derived_properties(W
     WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(format);
     format_pcm->Samples.wValidBitsPerSample = format->wBitsPerSample;
   }
 }
 
 /* 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,  com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params)
+handle_channel_layout(cubeb_stream * stm,  EDataFlow direction, com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params)
 {
-  XASSERT(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
+  // 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) {
     return;
   }
@@ -1410,19 +1412,19 @@ handle_channel_layout(cubeb_stream * stm
   /* Get the channel mask by the channel layout.
      If the layout is not supported, we will get a closest settings below. */
   format_pcm->dwChannelMask = channel_layout_to_mask(stream_params->layout);
   mix_format->nChannels = stream_params->channels;
   waveformatex_update_derived_properties(mix_format.get());
 
   /* Check if wasapi will accept our channel layout request. */
   WAVEFORMATEX * closest;
-  HRESULT hr = stm->output_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
-                                                     mix_format.get(),
-                                                     &closest);
+  HRESULT hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
+                                               mix_format.get(),
+                                               &closest);
   if (hr == S_FALSE) {
     /* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
        and handle the eventual upmix/downmix ourselves. Ignore the subformat of
        the suggestion, since it seems to always be IEEE_FLOAT. */
     LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
     WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
     format_pcm->dwChannelMask = closest_pcm->dwChannelMask;
     mix_format->nChannels = closest->nChannels;
@@ -1508,22 +1510,21 @@ int setup_wasapi_stream_one_side(cubeb_s
     return CUBEB_ERROR;
   }
   com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
 
   WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
   mix_format->wBitsPerSample = stm->bytes_per_sample * 8;
   format_pcm->SubFormat = stm->waveformatextensible_sub_format;
   waveformatex_update_derived_properties(mix_format.get());
-
   /* Set channel layout only when there're more than two channels. Otherwise,
    * use the default setting retrieved from the stream format of the audio
    * engine's internal processing by GetMixFormat. */
   if (mix_format->nChannels > 2) {
-    handle_channel_layout(stm, mix_format, stream_params);
+    handle_channel_layout(stm, direction ,mix_format, stream_params);
   }
 
   mix_params->format = stream_params->format;
   mix_params->rate = mix_format->nSamplesPerSec;
   mix_params->channels = mix_format->nChannels;
   mix_params->layout = mask_to_channel_layout(format_pcm->dwChannelMask);
   if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) {
     LOG("Output using undefined layout!\n");
@@ -1747,17 +1748,18 @@ wasapi_stream_init(cubeb * context, cube
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
   if (input_stream_params) {
     stm->input_stream_params = *input_stream_params;
     stm->input_device = utf8_to_wstr(reinterpret_cast<char const *>(input_device));
     // Make sure the layout matches the channel count.
-    XASSERT(stm->input_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->input_stream_params.layout].channels);
+    XASSERT(stm->input_stream_params.layout == CUBEB_LAYOUT_UNDEFINED ||
+            stm->input_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->input_stream_params.layout].channels);
   }
   if (output_stream_params) {
     stm->output_stream_params = *output_stream_params;
     stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
     // Make sure the layout matches the channel count.
     XASSERT(stm->output_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->output_stream_params.layout].channels);
   }
 
@@ -2316,16 +2318,17 @@ wasapi_enumerate_devices(cubeb * context
 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,
   /*.destroy =*/ wasapi_destroy,
   /*.stream_init =*/ wasapi_stream_init,
   /*.stream_destroy =*/ wasapi_stream_destroy,
   /*.stream_start =*/ wasapi_stream_start,
   /*.stream_stop =*/ wasapi_stream_stop,
   /*.stream_get_position =*/ wasapi_stream_get_position,
   /*.stream_get_latency =*/ wasapi_stream_get_latency,
   /*.stream_set_volume =*/ wasapi_stream_set_volume,
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -14,16 +14,17 @@
 #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
@@ -1045,16 +1046,17 @@ winmm_enumerate_devices(cubeb * context,
 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,
   /*.destroy =*/ winmm_destroy,
   /*.stream_init =*/ winmm_stream_init,
   /*.stream_destroy =*/ winmm_stream_destroy,
   /*.stream_start =*/ winmm_stream_start,
   /*.stream_stop =*/ winmm_stream_stop,
   /*.stream_get_position =*/ winmm_stream_get_position,
   /*.stream_get_latency = */ winmm_stream_get_latency,
   /*.stream_set_volume =*/ winmm_stream_set_volume,
--- a/media/libcubeb/src/moz.build
+++ b/media/libcubeb/src/moz.build
@@ -7,17 +7,18 @@
 DEFINES['CUBEB_GECKO_BUILD'] = True
 
 Library('cubeb')
 
 SOURCES += [
     'cubeb.c',
     'cubeb_log.cpp',
     'cubeb_mixer.cpp',
-    'cubeb_panner.cpp'
+    'cubeb_panner.cpp',
+    '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,16 +25,17 @@ 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_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