Bug 1337805 - Update cubeb from upstream 21e96ac. r?padenot draft
authorAlex Chronopoulos <achronop@gmail.com>
Fri, 10 Feb 2017 13:20:15 +0200
changeset 481686 449bde3195cd552147dff7a17cd759a437a8c4a0
parent 481595 25a94c1047e793ef096d8556fa3c26dd72bd37d7
child 481687 1a1fa8b4d6fe59fa4afa1a78be90f452404c03d8
child 481851 4b3fa42b248e8fd698fa9b4f9180669138857e06
push id44919
push userachronop@gmail.com
push dateFri, 10 Feb 2017 13:10:22 +0000
reviewerspadenot
bugs1337805
milestone54.0a1
Bug 1337805 - Update cubeb from upstream 21e96ac. r?padenot MozReview-Commit-ID: 62VM613n915
media/libcubeb/README_MOZILLA
media/libcubeb/gtest/test_latency.cpp
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_mixer.cpp
media/libcubeb/src/cubeb_mixer.h
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_wasapi.cpp
--- 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 927877c3204d6b8467b6dc782ca2aa740d240d41.
+The git commit ID used was 21e96ac7fd456dc957cd9947a61da1366a1f862d.
--- a/media/libcubeb/gtest/test_latency.cpp
+++ b/media/libcubeb/gtest/test_latency.cpp
@@ -22,17 +22,18 @@ TEST(cubeb, latency)
 
   r = cubeb_get_preferred_sample_rate(ctx, &preferred_rate);
   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
     ASSERT_GT(preferred_rate, 0u);
   }
 
   r = cubeb_get_preferred_channel_layout(ctx, &layout);
-  ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
+  ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED ||
+              (r == CUBEB_ERROR && layout == CUBEB_LAYOUT_UNDEFINED));
 
   cubeb_stream_params params = {
     CUBEB_SAMPLE_FLOAT32NE,
     preferred_rate,
     max_channels,
     (r == CUBEB_OK) ? layout : CUBEB_LAYOUT_UNDEFINED
 #if defined(__ANDROID__)
     , CUBEB_STREAM_TYPE_MUSIC
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -311,22 +311,22 @@ alsa_process_stream(cubeb_stream * stm)
 
       gettimeofday(&stm->last_activity, NULL);
     }
   }
 
   /* Capture: Pass read frames to callback function */
   if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
       (!stm->other_stream || stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
-    long wrote = stm->bufframes;
+    snd_pcm_sframes_t wrote = stm->bufframes;
     struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
     void * other_buffer = stm->other_stream ? stm->other_stream->buffer + stm->other_stream->bufframes : NULL;
 
     /* Correct write size to the other stream available space */
-    if (stm->other_stream && wrote > stm->other_stream->buffer_size - stm->other_stream->bufframes) {
+    if (stm->other_stream && wrote > (snd_pcm_sframes_t) (stm->other_stream->buffer_size - stm->other_stream->bufframes)) {
       wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
     }
 
     pthread_mutex_unlock(&stm->mutex);
     wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, other_buffer, wrote);
     pthread_mutex_lock(&stm->mutex);
 
     if (wrote < 0) {
@@ -336,24 +336,24 @@ alsa_process_stream(cubeb_stream * stm)
 
       if (stm->other_stream) {
         stm->other_stream->bufframes += wrote;
       }
     }
   }
 
   /* Playback: Don't have enough data? Let's ask for more. */
-  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > stm->bufframes &&
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes &&
       (!stm->other_stream || stm->other_stream->bufframes > 0)) {
     long got = avail - stm->bufframes;
     void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
     char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
 
     /* Correct read size to the other stream available frames */
-    if (stm->other_stream && got > stm->other_stream->bufframes) {
+    if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) {
       got = stm->other_stream->bufframes;
     }
 
     pthread_mutex_unlock(&stm->mutex);
     got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got);
     pthread_mutex_lock(&stm->mutex);
 
     if (got < 0) {
@@ -363,17 +363,17 @@ alsa_process_stream(cubeb_stream * stm)
 
       if (stm->other_stream) {
         stream_buffer_decrement(stm->other_stream, got);
       }
     }
   }
 
   /* Playback: Still don't have enough data? Add some silence. */
-  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > stm->bufframes) {
+  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes) {
     long drain_frames = avail - stm->bufframes;
     double drain_time = (double) drain_frames / stm->params.rate;
 
     char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
     memset(buftail, 0, snd_pcm_frames_to_bytes(stm->pcm, drain_frames));
     stm->bufframes = avail;
 
     /* Mark as draining, unless we're waiting for capture */
@@ -928,16 +928,19 @@ alsa_stream_init_single(cubeb * ctx, cub
   stm->buffer = NULL;
   stm->bufframes = 0;
   stm->stream_type = stream_type;
   stm->other_stream = NULL;
 
   r = pthread_mutex_init(&stm->mutex, NULL);
   assert(r == 0);
 
+  r = pthread_cond_init(&stm->cond, NULL);
+  assert(r == 0);
+
   r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, ctx->local_config);
   if (r < 0) {
     alsa_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   r = snd_pcm_nonblock(stm->pcm, 1);
   assert(r == 0);
@@ -971,19 +974,16 @@ alsa_stream_init_single(cubeb * ctx, cub
   stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
   assert(stm->nfds > 0);
 
   stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
   assert(stm->saved_fds);
   r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
   assert((nfds_t) r == stm->nfds);
 
-  r = pthread_cond_init(&stm->cond, NULL);
-  assert(r == 0);
-
   if (alsa_register_stream(ctx, stm) != 0) {
     alsa_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
 
   return CUBEB_OK;
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -17,16 +17,17 @@
 #include <CoreAudio/AudioHardware.h>
 #include <CoreAudio/HostTime.h>
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 #include <CoreAudio/CoreAudioTypes.h>
 #include <AudioToolbox/AudioToolbox.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
+#include "cubeb_mixer.h"
 #include "cubeb_panner.h"
 #if !TARGET_OS_IPHONE
 #include "cubeb_osx_run_loop.h"
 #endif
 #include "cubeb_resampler.h"
 #include "cubeb_ring_array.h"
 #include "cubeb_utils.h"
 #include <algorithm>
@@ -35,32 +36,38 @@
 
 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
 typedef UInt32  AudioFormatFlags;
 #endif
 
 #define AU_OUT_BUS    0
 #define AU_IN_BUS     1
 
+#define fieldOffset(type, field) ((size_t) &((type *) 0)->field)
+
 #define PRINT_ERROR_CODE(str, r) do {                           \
     LOG("System call failed: %s (rv: %d)", str, (int) r);       \
   } while(0)
 
 const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
 
 /* Testing empirically, some headsets report a minimal latency that is very
  * low, but this does not work in practice. Lie and say the minimum is 256
  * frames. */
 const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
 
 void audiounit_stream_stop_internal(cubeb_stream * stm);
 void audiounit_stream_start_internal(cubeb_stream * stm);
 static void audiounit_close_stream(cubeb_stream *stm);
 static int audiounit_setup_stream(cubeb_stream *stm);
+static int audiounit_create_unit(AudioUnit * unit, bool is_input,
+                                 const cubeb_stream_params * /* stream_params */,
+                                 AudioDeviceID device);
+static cubeb_channel_layout audiounit_get_channel_layout(bool preferred);
 
 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;
@@ -78,16 +85,64 @@ struct auto_array_wrapper {
   virtual size_t length() = 0;
   virtual void push_silence(size_t length) = 0;
   virtual bool pop(size_t length) = 0;
   virtual void * data() = 0;
   virtual void clear() = 0;
   virtual ~auto_array_wrapper() {}
 };
 
+class auto_channel_layout
+{
+public:
+  auto_channel_layout()
+    : layout(nullptr)
+  {
+  }
+
+  auto_channel_layout(size_t size)
+    : layout(reinterpret_cast<AudioChannelLayout*>(malloc(size)))
+  {
+    memset(layout, 0, size);
+  }
+
+  ~auto_channel_layout()
+  {
+    release();
+  }
+
+  void reset(size_t size)
+  {
+    release();
+    layout = reinterpret_cast<AudioChannelLayout*>(malloc(size));
+    memset(layout, 0, size);
+  }
+
+  AudioChannelLayout* get()
+  {
+    return layout;
+  }
+
+  size_t size()
+  {
+    return sizeof(*layout);
+  }
+
+private:
+  void release()
+  {
+    if (layout) {
+      free(layout);
+      layout = NULL;
+    }
+  }
+
+  AudioChannelLayout * layout;
+};
+
 template <typename T>
 struct auto_array_wrapper_impl : public auto_array_wrapper {
   explicit auto_array_wrapper_impl(uint32_t size)
     : ar(size)
   {}
 
   void push(void * elements, size_t length) override {
     auto_lock l(lock);
@@ -125,16 +180,21 @@ struct auto_array_wrapper_impl : public 
     ar.clear();
   }
 
 private:
   owned_critical_section lock;
   auto_array<T> ar;
 };
 
+enum io_side {
+  INPUT,
+  OUTPUT,
+};
+
 struct cubeb_stream {
   explicit cubeb_stream(cubeb * context);
 
   cubeb * context;
   cubeb_data_callback data_callback = nullptr;
   cubeb_state_callback state_callback = nullptr;
   cubeb_device_changed_callback device_changed_callback = nullptr;
   /* Stream creation parameters */
@@ -190,16 +250,52 @@ 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;
 }
 
+cubeb_channel
+channel_label_to_cubeb_channel(UInt32 label)
+{
+  switch (label) {
+    case kAudioChannelLabel_Mono: return CHANNEL_MONO;
+    case kAudioChannelLabel_Left: return CHANNEL_LEFT;
+    case kAudioChannelLabel_Right: return CHANNEL_RIGHT;
+    case kAudioChannelLabel_Center: return CHANNEL_CENTER;
+    case kAudioChannelLabel_LFEScreen: return CHANNEL_LFE;
+    case kAudioChannelLabel_LeftSurround: return CHANNEL_LS;
+    case kAudioChannelLabel_RightSurround: return CHANNEL_RS;
+    case kAudioChannelLabel_RearSurroundLeft: return CHANNEL_RLS;
+    case kAudioChannelLabel_RearSurroundRight: return CHANNEL_RRS;
+    case kAudioChannelLabel_CenterSurround: return CHANNEL_RCENTER;
+    default: return CHANNEL_INVALID;
+  }
+}
+
+AudioChannelLabel
+cubeb_channel_to_channel_label(cubeb_channel channel)
+{
+  switch (channel) {
+    case CHANNEL_MONO: return kAudioChannelLabel_Mono;
+    case CHANNEL_LEFT: return kAudioChannelLabel_Left;
+    case CHANNEL_RIGHT: return kAudioChannelLabel_Right;
+    case CHANNEL_CENTER: return kAudioChannelLabel_Center;
+    case CHANNEL_LFE: return kAudioChannelLabel_LFEScreen;
+    case CHANNEL_LS: return kAudioChannelLabel_LeftSurround;
+    case CHANNEL_RS: return kAudioChannelLabel_RightSurround;
+    case CHANNEL_RLS: return kAudioChannelLabel_RearSurroundLeft;
+    case CHANNEL_RRS: return kAudioChannelLabel_RearSurroundRight;
+    case CHANNEL_RCENTER: return kAudioChannelLabel_CenterSurround;
+    default: return kAudioChannelLabel_Unknown;
+  }
+}
+
 #if TARGET_OS_IPHONE
 typedef UInt32 AudioDeviceID;
 typedef UInt32 AudioObjectID;
 
 #define AudioGetCurrentHostTime mach_absolute_time
 
 uint64_t
 AudioConvertHostTimeToNanos(uint64_t host_time)
@@ -953,16 +1049,36 @@ audiounit_get_preferred_sample_rate(cube
     return CUBEB_ERROR;
   }
 
   *rate = static_cast<uint32_t>(fsamplerate);
 #endif
   return CUBEB_OK;
 }
 
+static int
+audiounit_get_preferred_channel_layout(cubeb * /* ctx */, cubeb_channel_layout * layout)
+{
+  // The preferred layout is only returned when the connected sound device
+  // (e.g. ASUS Xonar U7), has preferred layout setting.
+  // For default output on Mac, there is no preferred channel layout,
+  // so it might return UNDEFINED.
+  // In that case, we should get the channel configuration directly.
+  *layout = audiounit_get_channel_layout(true);
+  if (*layout == CUBEB_LAYOUT_UNDEFINED) {
+    *layout = audiounit_get_channel_layout(false);
+  }
+
+  if (*layout == CUBEB_LAYOUT_UNDEFINED) {
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
 static OSStatus audiounit_remove_device_listener(cubeb * context);
 
 static void
 audiounit_destroy(cubeb * ctx)
 {
   // Disabling this assert for bug 1083664 -- we seem to leak a stream
   // assert(ctx->active_streams == 0);
   if (ctx->active_streams > 0) {
@@ -1017,16 +1133,90 @@ audio_stream_desc_init(AudioStreamBasicD
   ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket;
 
   ss->mReserved = 0;
 
   return CUBEB_OK;
 }
 
 static int
+audiounit_layout_init(AudioUnit * unit,
+                      io_side side,
+                      const cubeb_stream_params * stream_params)
+{
+  assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
+  assert(stream_params->channels == CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels);
+
+  OSStatus r;
+  auto_channel_layout layout(sizeof(AudioChannelLayout));
+
+  switch (stream_params->layout) {
+    case CUBEB_LAYOUT_DUAL_MONO:
+    case CUBEB_LAYOUT_STEREO:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
+      break;
+    case CUBEB_LAYOUT_MONO:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
+      break;
+    case CUBEB_LAYOUT_3F:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_0;
+      break;
+    case CUBEB_LAYOUT_2F1:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_1;
+      break;
+    case CUBEB_LAYOUT_3F1:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_1;
+      break;
+    case CUBEB_LAYOUT_2F2:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_2;
+      break;
+    case CUBEB_LAYOUT_3F2:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_2;
+      break;
+    case CUBEB_LAYOUT_3F2_LFE:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
+      break;
+    default:
+      layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_Unknown;
+      break;
+  }
+
+  // For those layouts that can't be matched to coreaudio's predefined layout,
+  // we use customized layout.
+  if (layout.get()->mChannelLayoutTag == kAudioChannelLayoutTag_Unknown) {
+    size_t size = fieldOffset(AudioChannelLayout, mChannelDescriptions[stream_params->channels]);
+    layout.reset(size);
+    layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+    layout.get()->mNumberChannelDescriptions = stream_params->channels;
+    for (UInt32 i = 0 ; i < stream_params->channels ; ++i) {
+      layout.get()->mChannelDescriptions[i].mChannelLabel =
+        cubeb_channel_to_channel_label(CHANNEL_INDEX_TO_ORDER[stream_params->layout][i]);
+      layout.get()->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
+    }
+  }
+
+  r = AudioUnitSetProperty(*unit,
+                           kAudioUnitProperty_AudioChannelLayout,
+                           side == INPUT ? kAudioUnitScope_Output : kAudioUnitScope_Input,
+                           side == INPUT ? AU_IN_BUS : AU_OUT_BUS,
+                           layout.get(),
+                           layout.size());
+  if (r != noErr) {
+    if (side == INPUT) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_AudioChannelLayout", r);
+    } else {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_AudioChannelLayout", r);
+    }
+    return CUBEB_ERROR;
+  }
+
+  return CUBEB_OK;
+}
+
+static int
 audiounit_create_unit(AudioUnit * unit,
                       bool is_input,
                       const cubeb_stream_params * /* stream_params */,
                       AudioDeviceID device)
 {
   AudioComponentDescription desc;
   AudioComponent comp;
   UInt32 enable;
@@ -1238,46 +1428,41 @@ buffer_size_changed_callback(void * inCl
             au_type, new_buffer_size, inScope);
       }
       stm->buffer_size_change_state = true;
       break;
     }
   }
 }
 
-enum set_buffer_size_side {
-  INPUT,
-  OUTPUT,
-};
-
 static int
-audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, set_buffer_size_side set_side)
+audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side side)
 {
   AudioUnit au = stm->output_unit;
   AudioUnitScope au_scope = kAudioUnitScope_Input;
   AudioUnitElement au_element = AU_OUT_BUS;
   const char * au_type = "output";
 
-  if (set_side == INPUT) {
+  if (side == INPUT) {
     au = stm->input_unit;
     au_scope = kAudioUnitScope_Output;
     au_element = AU_IN_BUS;
     au_type = "input";
   }
 
   uint32_t buffer_frames = 0;
   UInt32 size = sizeof(buffer_frames);
   int r = AudioUnitGetProperty(au,
                                kAudioDevicePropertyBufferFrameSize,
                                au_scope,
                                au_element,
                                &buffer_frames,
                                &size);
   if (r != noErr) {
-    if (set_side == INPUT) {
+    if (side == INPUT) {
       PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
     } else {
       PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
     }
     return CUBEB_ERROR;
   }
 
   if (new_size_frames == buffer_frames) {
@@ -1285,45 +1470,45 @@ audiounit_set_buffer_size(cubeb_stream *
     return CUBEB_OK;
   }
 
   r = AudioUnitAddPropertyListener(au,
                                    kAudioDevicePropertyBufferFrameSize,
                                    buffer_size_changed_callback,
                                    stm);
   if (r != noErr) {
-    if (set_side == INPUT) {
+    if (side == INPUT) {
       PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
     } else {
       PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
     }
     return CUBEB_ERROR;
   }
 
   stm->buffer_size_change_state = false;
 
   r = AudioUnitSetProperty(au,
                            kAudioDevicePropertyBufferFrameSize,
                            au_scope,
                            au_element,
                            &new_size_frames,
                            sizeof(new_size_frames));
   if (r != noErr) {
-    if (set_side == INPUT) {
+    if (side == INPUT) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
     } else {
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
     }
 
     r = AudioUnitRemovePropertyListenerWithUserData(au,
                                                     kAudioDevicePropertyBufferFrameSize,
                                                     buffer_size_changed_callback,
                                                     stm);
     if (r != noErr) {
-      if (set_side == INPUT) {
+      if (side == INPUT) {
         PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
       } else {
         PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
       }
     }
 
     return CUBEB_ERROR;
   }
@@ -1339,17 +1524,17 @@ audiounit_set_buffer_size(cubeb_stream *
     LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count);
   }
 
   r = AudioUnitRemovePropertyListenerWithUserData(au,
                                                   kAudioDevicePropertyBufferFrameSize,
                                                   buffer_size_changed_callback,
                                                   stm);
   if (r != noErr) {
-    if (set_side == INPUT) {
+    if (side == INPUT) {
       PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
     } else {
       PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
     }
     return CUBEB_ERROR;
   }
 
   if (!stm->buffer_size_change_state && count >= 30) {
@@ -1484,17 +1669,17 @@ audiounit_configure_output(cubeb_stream 
   memset(&output_hw_desc, 0, size);
   r = AudioUnitGetProperty(stm->output_unit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Output,
                            AU_OUT_BUS,
                            &output_hw_desc,
                            &size);
   if (r != noErr) {
-    PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
+    PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat", r);
     return CUBEB_ERROR;
   }
   stm->output_hw_rate = output_hw_desc.mSampleRate;
   LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
 
   r = AudioUnitSetProperty(stm->output_unit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Input,
@@ -1533,16 +1718,18 @@ audiounit_configure_output(cubeb_stream 
                            AU_OUT_BUS,
                            &aurcbs_out,
                            sizeof(aurcbs_out));
   if (r != noErr) {
     PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
     return CUBEB_ERROR;
   }
 
+  audiounit_layout_init(&stm->output_unit, OUTPUT, &stm->output_stream_params);
+
   LOG("(%p) Output audiounit init successfully.", stm);
   return CUBEB_OK;
 }
 
 static int
 audiounit_setup_stream(cubeb_stream * stm)
 {
   stm->mutex.assert_current_thread_owns();
@@ -2523,23 +2710,86 @@ int audiounit_register_device_collection
                                         collection_changed_callback,
                                         user_ptr);
   } else {
     ret = audiounit_remove_device_listener(context);
   }
   return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
 }
 
+static cubeb_channel_layout
+audiounit_get_channel_layout(bool preferred)
+{
+  OSStatus rv = noErr;
+
+  // Get the default ouput unit
+  AudioUnit output_unit;
+  audiounit_create_unit(&output_unit, false, nullptr, 0);
+
+  // Get the channel layout
+  UInt32 size = 0;
+  rv = AudioUnitGetPropertyInfo(output_unit,
+                                preferred ? kAudioDevicePropertyPreferredChannelLayout :
+                                            kAudioUnitProperty_AudioChannelLayout,
+                                kAudioUnitScope_Output,
+                                AU_OUT_BUS,
+                                &size,
+                                nullptr);
+  if (rv != noErr) {
+    if (preferred) {
+      PRINT_ERROR_CODE("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout", rv);
+    } else {
+      PRINT_ERROR_CODE("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout", rv);
+    }
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+  assert(size > 0);
+
+  auto_channel_layout layout(size);
+  rv = AudioUnitGetProperty(output_unit,
+                            preferred ? kAudioDevicePropertyPreferredChannelLayout :
+                                        kAudioUnitProperty_AudioChannelLayout,
+                            kAudioUnitScope_Output,
+                            AU_OUT_BUS,
+                            layout.get(),
+                            &size);
+  if (rv != noErr) {
+    if (preferred) {
+      PRINT_ERROR_CODE("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout", rv);
+    } else {
+      PRINT_ERROR_CODE("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout", rv);
+    }
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+
+  if (layout.get()->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) {
+    // kAudioChannelLayoutTag_UseChannelBitmap
+    // kAudioChannelLayoutTag_Mono
+    // kAudioChannelLayoutTag_Stereo
+    // ....
+    LOG("Only handle UseChannelDescriptions for now.\n");
+    return CUBEB_LAYOUT_UNDEFINED;
+  }
+
+  cubeb_channel_map cm;
+  cm.channels = layout.get()->mNumberChannelDescriptions;
+  for (UInt32 i = 0; i < layout.get()->mNumberChannelDescriptions; ++i) {
+    cm.map[i] = channel_label_to_cubeb_channel(layout.get()->mChannelDescriptions[i].mChannelLabel);
+  }
+
+  return cubeb_channel_map_to_layout(&cm);
+}
+
 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 =*/ nullptr,
+  /*.get_preferred_channel_layout =*/ audiounit_get_preferred_channel_layout,
   /*.enumerate_devices =*/ audiounit_enumerate_devices,
   /*.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,
--- a/media/libcubeb/src/cubeb_mixer.cpp
+++ b/media/libcubeb/src/cubeb_mixer.cpp
@@ -4,16 +4,65 @@
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 
 #include <cassert>
 #include "cubeb-internal.h"
 #include "cubeb_mixer.h"
 
+// DUAL_MONO(_LFE) is same as STEREO(_LFE).
+#define MASK_MONO         (1 << CHANNEL_MONO)
+#define MASK_MONO_LFE     (MASK_MONO | (1 << CHANNEL_LFE))
+#define MASK_STEREO       ((1 << CHANNEL_LEFT) | (1 << CHANNEL_RIGHT))
+#define MASK_STEREO_LFE   (MASK_STEREO | (1 << CHANNEL_LFE))
+#define MASK_3F           (MASK_STEREO | (1 << CHANNEL_CENTER))
+#define MASK_3F_LFE       (MASK_3F | (1 << CHANNEL_LFE))
+#define MASK_2F1          (MASK_STEREO | (1 << CHANNEL_RCENTER))
+#define MASK_2F1_LFE      (MASK_2F1 | (1 << CHANNEL_LFE))
+#define MASK_3F1          (MASK_3F | (1 << CHANNEL_RCENTER))
+#define MASK_3F1_LFE      (MASK_3F1 | (1 << CHANNEL_LFE))
+#define MASK_2F2          (MASK_STEREO | (1 << CHANNEL_LS) | (1 << CHANNEL_RS))
+#define MASK_2F2_LFE      (MASK_2F2 | (1 << CHANNEL_LFE))
+#define MASK_3F2          (MASK_2F2 | (1 << CHANNEL_CENTER))
+#define MASK_3F2_LFE      (MASK_3F2 | (1 << CHANNEL_LFE))
+#define MASK_3F3R_LFE     (MASK_3F2_LFE | (1 << CHANNEL_RCENTER))
+#define MASK_3F4_LFE      (MASK_3F2_LFE | (1 << CHANNEL_RLS) | (1 << CHANNEL_RRS))
+
+cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map)
+{
+  uint32_t channel_mask = 0;
+  for (uint8_t i = 0 ; i < channel_map->channels ; ++i) {
+    if (channel_map->map[i] == CHANNEL_INVALID) {
+      return CUBEB_LAYOUT_UNDEFINED;
+    }
+    channel_mask |= 1 << channel_map->map[i];
+  }
+
+  switch(channel_mask) {
+    case MASK_MONO: return CUBEB_LAYOUT_MONO;
+    case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE;
+    case MASK_STEREO: return CUBEB_LAYOUT_STEREO;
+    case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE;
+    case MASK_3F: return CUBEB_LAYOUT_3F;
+    case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE;
+    case MASK_2F1: return CUBEB_LAYOUT_2F1;
+    case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE;
+    case MASK_3F1: return CUBEB_LAYOUT_3F1;
+    case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE;
+    case MASK_2F2: return CUBEB_LAYOUT_2F2;
+    case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE;
+    case MASK_3F2: return CUBEB_LAYOUT_3F2;
+    case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE;
+    case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE;
+    case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE;
+    default: return CUBEB_LAYOUT_UNDEFINED;
+  }
+}
+
 cubeb_layout_map const CUBEB_CHANNEL_LAYOUT_MAPS[CUBEB_LAYOUT_MAX] = {
   { "undefined",      0,  CUBEB_LAYOUT_UNDEFINED },
   { "dual mono",      2,  CUBEB_LAYOUT_DUAL_MONO },
   { "dual mono lfe",  3,  CUBEB_LAYOUT_DUAL_MONO_LFE },
   { "mono",           1,  CUBEB_LAYOUT_MONO },
   { "mono lfe",       2,  CUBEB_LAYOUT_MONO_LFE },
   { "stereo",         2,  CUBEB_LAYOUT_STEREO },
   { "stereo lfe",     3,  CUBEB_LAYOUT_STEREO_LFE },
--- a/media/libcubeb/src/cubeb_mixer.h
+++ b/media/libcubeb/src/cubeb_mixer.h
@@ -47,16 +47,23 @@ static cubeb_channel const CHANNEL_INDEX
   { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS },                                                        // 2F2
   { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_LS, CHANNEL_RS },                                           // 2F2_LFE
   { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LS, CHANNEL_RS },                                        // 3F2
   { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_LS, CHANNEL_RS },                           // 3F2_LFE
   { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RCENTER, CHANNEL_LS, CHANNEL_RS },          // 3F3R_LFE
   { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RLS, CHANNEL_RRS, CHANNEL_LS, CHANNEL_RS }  // 3F4_LFE
 };
 
+typedef struct {
+  unsigned int channels;
+  cubeb_channel map[CHANNEL_MAX];
+} cubeb_channel_map;
+
+cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map);
+
 bool cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer);
 
 bool cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer);
 
 void cubeb_downmix_float(float * const in, long inframes, float * out,
                          unsigned int in_channels, unsigned int out_channels,
                          cubeb_channel_layout in_layout, cubeb_channel_layout out_layout);
 
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -520,65 +520,25 @@ layout_to_channel_map(cubeb_channel_layo
 
   WRAP(pa_channel_map_init)(cm);
   cm->channels = CUBEB_CHANNEL_LAYOUT_MAPS[layout].channels;
   for (uint8_t i = 0 ; i < cm->channels ; ++i) {
     cm->map[i] = cubeb_channel_to_pa_channel(CHANNEL_INDEX_TO_ORDER[layout][i]);
   }
 }
 
-// DUAL_MONO(_LFE) is same as STEREO(_LFE).
-#define MASK_MONO         (1 << CHANNEL_MONO)
-#define MASK_MONO_LFE     (MASK_MONO | (1 << CHANNEL_LFE))
-#define MASK_STEREO       ((1 << CHANNEL_LEFT) | (1 << CHANNEL_RIGHT))
-#define MASK_STEREO_LFE   (MASK_STEREO | (1 << CHANNEL_LFE))
-#define MASK_3F           (MASK_STEREO | (1 << CHANNEL_CENTER))
-#define MASK_3F_LFE       (MASK_3F | (1 << CHANNEL_LFE))
-#define MASK_2F1          (MASK_STEREO | (1 << CHANNEL_RCENTER))
-#define MASK_2F1_LFE      (MASK_2F1 | (1 << CHANNEL_LFE))
-#define MASK_3F1          (MASK_3F | (1 << CHANNEL_RCENTER))
-#define MASK_3F1_LFE      (MASK_3F1 | (1 << CHANNEL_LFE))
-#define MASK_2F2          (MASK_STEREO | (1 << CHANNEL_LS) | (1 << CHANNEL_RS))
-#define MASK_2F2_LFE      (MASK_2F2 | (1 << CHANNEL_LFE))
-#define MASK_3F2          (MASK_2F2 | (1 << CHANNEL_CENTER))
-#define MASK_3F2_LFE      (MASK_3F2 | (1 << CHANNEL_LFE))
-#define MASK_3F3R_LFE     (MASK_3F2_LFE | (1 << CHANNEL_RCENTER))
-#define MASK_3F4_LFE      (MASK_3F2_LFE | (1 << CHANNEL_RLS) | (1 << CHANNEL_RRS))
-
 static cubeb_channel_layout
 channel_map_to_layout(pa_channel_map * cm)
 {
-  uint32_t channel_mask = 0;
-  for (uint8_t i = 0 ; i < cm->channels ; ++i) {
-    cubeb_channel channel = pa_channel_to_cubeb_channel(cm->map[i]);
-    if (channel == CHANNEL_INVALID) {
-      return CUBEB_LAYOUT_UNDEFINED;
-    }
-    channel_mask |= 1 << channel;
+  cubeb_channel_map cubeb_map;
+  cubeb_map.channels = cm->channels;
+  for (uint32_t i = 0 ; i < cm->channels ; ++i) {
+    cubeb_map.map[i] = pa_channel_to_cubeb_channel(cm->map[i]);
   }
-
-  switch(channel_mask) {
-    case MASK_MONO: return CUBEB_LAYOUT_MONO;
-    case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE;
-    case MASK_STEREO: return CUBEB_LAYOUT_STEREO;
-    case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE;
-    case MASK_3F: return CUBEB_LAYOUT_3F;
-    case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE;
-    case MASK_2F1: return CUBEB_LAYOUT_2F1;
-    case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE;
-    case MASK_3F1: return CUBEB_LAYOUT_3F1;
-    case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE;
-    case MASK_2F2: return CUBEB_LAYOUT_2F2;
-    case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE;
-    case MASK_3F2: return CUBEB_LAYOUT_3F2;
-    case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE;
-    case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE;
-    case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE;
-    default: return CUBEB_LAYOUT_UNDEFINED;
-  }
+  return cubeb_channel_map_to_layout(&cubeb_map);
 }
 
 static void pulse_context_destroy(cubeb * ctx);
 static void pulse_destroy(cubeb * ctx);
 
 static int
 pulse_context_init(cubeb * ctx)
 {
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -1,19 +1,15 @@
 /*
  * Copyright © 2013 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
-// Explicitly define NTDDI_VERSION rather than letting the value be derived
-// from _WIN32_WINNT because we depend on values defined for XP SP2 and WS03
-// SP1.
-#define _WIN32_WINNT 0x0502
-#define NTDDI_VERSION 0x05020100
+#define _WIN32_WINNT 0x0600
 #define NOMINMAX
 
 #include <initguid.h>
 #include <windows.h>
 #include <mmdeviceapi.h>
 #include <windef.h>
 #include <audioclient.h>
 #include <devicetopology.h>
@@ -159,38 +155,29 @@ struct auto_com {
   }
   bool ok() {
     return result == RPC_E_CHANGED_MODE || SUCCEEDED(result);
   }
 private:
   HRESULT result;
 };
 
-typedef HANDLE (WINAPI *set_mm_thread_characteristics_function)(
-                                      const char * TaskName, LPDWORD TaskIndex);
-typedef BOOL (WINAPI *revert_mm_thread_characteristics_function)(HANDLE handle);
-
 extern cubeb_ops const wasapi_ops;
 
 int wasapi_stream_stop(cubeb_stream * stm);
 int wasapi_stream_start(cubeb_stream * stm);
 void close_wasapi_stream(cubeb_stream * stm);
 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;
-  /* Library dynamically opened to increase the render thread priority, and
-     the two function pointers we need. */
-  HMODULE mmcss_module = nullptr;
-  set_mm_thread_characteristics_function set_mm_thread_characteristics = nullptr;
-  revert_mm_thread_characteristics_function revert_mm_thread_characteristics = nullptr;
 };
 
 class wasapi_endpoint_notification_client;
 
 /* We have three possible callbacks we can use with a stream:
  * - input only
  * - output only
  * - synchronized input and output
@@ -859,18 +846,17 @@ wasapi_stream_render_loop(LPVOID stream)
   if (!com.ok()) {
     LOG("COM initialization failed on render_loop thread.");
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
     return 0;
   }
 
   /* We could consider using "Pro Audio" here for WebAudio and
      maybe WebRTC. */
-  mmcss_handle =
-    stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index);
+  mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
   if (!mmcss_handle) {
     /* This is not fatal, but we might glitch under heavy load. */
     LOG("Unable to use mmcss to bump the render thread priority: %lx", GetLastError());
   }
 
   /* WaitForMultipleObjects timeout can trigger in cases where we don't want to
      treat it as a timeout, such as across a system sleep/wake cycle.  Trigger
      the timeout error handling only when the timeout_limit is reached, which is
@@ -962,34 +948,24 @@ wasapi_stream_render_loop(LPVOID stream)
     }
   }
 
   if (FAILED(hr)) {
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
   }
 
   if (mmcss_handle) {
-    stm->context->revert_mm_thread_characteristics(mmcss_handle);
+    AvRevertMmThreadCharacteristics(mmcss_handle);
   }
 
   return 0;
 }
 
 void wasapi_destroy(cubeb * context);
 
-HANDLE WINAPI set_mm_thread_characteristics_noop(const char *, LPDWORD mmcss_task_index)
-{
-  return (HANDLE)1;
-}
-
-BOOL WINAPI revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
-{
-  return true;
-}
-
 HRESULT register_notification_client(cubeb_stream * stm)
 {
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(stm->device_enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
     return hr;
@@ -1157,37 +1133,16 @@ int wasapi_init(cubeb ** context, char c
     LOG("Could not get device: %lx", hr);
     return CUBEB_ERROR;
   }
 
   cubeb * ctx = new cubeb();
 
   ctx->ops = &wasapi_ops;
 
-  ctx->mmcss_module = LoadLibraryA("Avrt.dll");
-
-  if (ctx->mmcss_module) {
-    ctx->set_mm_thread_characteristics =
-      (set_mm_thread_characteristics_function) GetProcAddress(
-          ctx->mmcss_module, "AvSetMmThreadCharacteristicsA");
-    ctx->revert_mm_thread_characteristics =
-      (revert_mm_thread_characteristics_function) GetProcAddress(
-          ctx->mmcss_module, "AvRevertMmThreadCharacteristics");
-    if (!(ctx->set_mm_thread_characteristics && ctx->revert_mm_thread_characteristics)) {
-      LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %lx", GetLastError());
-      FreeLibrary(ctx->mmcss_module);
-    }
-  } else {
-    // This is not a fatal error, but we might end up glitching when
-    // the system is under high load.
-    LOG("Could not load Avrt.dll");
-    ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
-    ctx->revert_mm_thread_characteristics = &revert_mm_thread_characteristics_noop;
-  }
-
   *context = ctx;
 
   return CUBEB_OK;
 }
 }
 
 namespace {
 bool stop_and_join_render_thread(cubeb_stream * stm)
@@ -1234,19 +1189,16 @@ bool stop_and_join_render_thread(cubeb_s
     stm->shutdown_event = 0;
   }
 
   return rv;
 }
 
 void wasapi_destroy(cubeb * context)
 {
-  if (context->mmcss_module) {
-    FreeLibrary(context->mmcss_module);
-  }
   delete context;
 }
 
 char const * wasapi_get_backend_id(cubeb * context)
 {
   return "wasapi";
 }