Bug 1432779 - Update libcubeb to revision 77cb1a9. r=padenot
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 19 Mar 2018 16:22:26 +0100
changeset 409793 bca1660fc65cf338bcad03bb8e02911519bab1ec
parent 409792 20b381d4bfc1ce951a1350946cd1845c22ec48da
child 409794 b98af64fa15c8e3ab4b5a7c13d25cb41723e098b
push id33701
push useraciure@mozilla.com
push dateSat, 24 Mar 2018 22:39:15 +0000
treeherdermozilla-central@b4aeb99d1cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1432779
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1432779 - Update libcubeb to revision 77cb1a9. r=padenot MozReview-Commit-ID: DAfCxUBb41U
media/libcubeb/README_MOZILLA
media/libcubeb/gtest/common.h
media/libcubeb/gtest/test_audio.cpp
media/libcubeb/gtest/test_loopback.cpp
media/libcubeb/gtest/test_mixer.cpp
media/libcubeb/gtest/test_sanity.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_mixer.cpp
media/libcubeb/src/cubeb_mixer.h
media/libcubeb/src/cubeb_opensl.c
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_sndio.c
media/libcubeb/src/cubeb_utils.cpp
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 
+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 eb3409e201daaaf03401ff234de5e1f69ba10c8d (2018-03-01 13:31:19 +0100)
+The git commit ID used was 77cb1a9a37bb9fcc43743770727cb6a31b27ab74 (2018-03-23 14:38:34 +1000)
--- a/media/libcubeb/gtest/common.h
+++ b/media/libcubeb/gtest/common.h
@@ -6,16 +6,17 @@
  */
 #if !defined(TEST_COMMON)
 #define TEST_COMMON
 
 #if defined( _WIN32)
 #ifndef WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #endif
+#include <objbase.h>
 #include <windows.h>
 #else
 #include <unistd.h>
 #endif
 
 #include <cstdarg>
 #include "cubeb/cubeb.h"
 #include "cubeb_mixer.h"
@@ -39,41 +40,19 @@ void delay(unsigned int ms)
 
 #if !defined(M_PI)
 #define M_PI 3.14159265358979323846
 #endif
 
 typedef struct {
   char const * name;
   unsigned int const channels;
-  cubeb_channel_layout const layout;
+  uint32_t const layout;
 } layout_info;
 
-layout_info const layout_infos[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 },
-  { "3f",             3,  CUBEB_LAYOUT_3F },
-  { "3f lfe",         4,  CUBEB_LAYOUT_3F_LFE },
-  { "2f1",            3,  CUBEB_LAYOUT_2F1 },
-  { "2f1 lfe",        4,  CUBEB_LAYOUT_2F1_LFE },
-  { "3f1",            4,  CUBEB_LAYOUT_3F1 },
-  { "3f1 lfe",        5,  CUBEB_LAYOUT_3F1_LFE },
-  { "2f2",            4,  CUBEB_LAYOUT_2F2 },
-  { "2f2 lfe",        5,  CUBEB_LAYOUT_2F2_LFE },
-  { "3f2",            5,  CUBEB_LAYOUT_3F2 },
-  { "3f2 lfe",        6,  CUBEB_LAYOUT_3F2_LFE },
-  { "3f3r lfe",       7,  CUBEB_LAYOUT_3F3R_LFE },
-  { "3f4 lfe",        8,  CUBEB_LAYOUT_3F4_LFE }
-};
-
 int has_available_input_device(cubeb * ctx)
 {
   cubeb_device_collection devices;
   int input_device_available = 0;
   int r;
   /* Bail out early if the host does not have input devices. */
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &devices);
   if (r != CUBEB_OK) {
@@ -126,9 +105,29 @@ int common_init(cubeb ** ctx, char const
       fprintf(stderr, "Requested backend `%s', got `%s'\n",
               backend, ctx_backend);
     }
   }
 
   return r;
 }
 
+#if defined( _WIN32)
+class TestEnvironment : public ::testing::Environment {
+public:
+  void SetUp() override {
+    hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+  }
+
+  void TearDown() override {
+    if (SUCCEEDED(hr)) {
+      CoUninitialize();
+    }
+  }
+
+private:
+  HRESULT hr;
+};
+
+::testing::Environment* const foo_env = ::testing::AddGlobalTestEnvironment(new TestEnvironment);
+#endif
+
 #endif /* TEST_COMMON */
--- a/media/libcubeb/gtest/test_audio.cpp
+++ b/media/libcubeb/gtest/test_audio.cpp
@@ -89,17 +89,17 @@ int supports_float32(string backend_id)
 
 /* Some backends don't have code to deal with more than mono or stereo. */
 int supports_channel_count(string backend_id, int nchannels)
 {
   return nchannels <= 2 ||
     (backend_id != "opensl" && backend_id != "audiotrack");
 }
 
-int run_test(int num_channels, layout_info layout, int sampling_rate, int is_float)
+int run_test(int num_channels, int sampling_rate, int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
 
   r = common_init(&ctx, "Cubeb audio test: channels");
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
@@ -111,23 +111,23 @@ int run_test(int num_channels, layout_in
   const char * backend_id = cubeb_get_backend_id(ctx);
 
   if ((is_float && !supports_float32(backend_id)) ||
       !supports_channel_count(backend_id, num_channels)) {
     /* don't treat this as a test failure. */
     return CUBEB_OK;
   }
 
-  fprintf(stderr, "Testing %d channel(s), layout: %s, %d Hz, %s (%s)\n", num_channels, layout.name, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
+  fprintf(stderr, "Testing %d channel(s), %d Hz, %s (%s)\n", num_channels, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
 
   cubeb_stream_params params;
   params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
   params.rate = sampling_rate;
   params.channels = num_channels;
-  params.layout = layout.layout;
+  params.layout = CUBEB_LAYOUT_UNDEFINED;
   params.prefs = CUBEB_STREAM_PREF_NONE;
 
   synth_state synth(params.channels, params.rate);
 
   cubeb_stream *stream = NULL;
   r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
       4096, is_float ? &data_cb<float> : &data_cb<short>, state_cb_audio, &synth);
   if (r != CUBEB_OK) {
@@ -241,17 +241,13 @@ TEST(cubeb, run_channel_rate_test)
     44100,
     48000,
   };
 
   for(auto channels : channel_values) {
     for(auto freq : freq_values) {
       ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
       fprintf(stderr, "--------------------------\n");
-      for (auto layout : layout_infos) {
-        if (layout.channels == channels) {
-          ASSERT_EQ(run_test(channels, layout, freq, 0), CUBEB_OK);
-          ASSERT_EQ(run_test(channels, layout, freq, 1), CUBEB_OK);
-        }
-      }
+      ASSERT_EQ(run_test(channels, freq, 0), CUBEB_OK);
+      ASSERT_EQ(run_test(channels, freq, 1), CUBEB_OK);
     }
   }
 }
--- a/media/libcubeb/gtest/test_loopback.cpp
+++ b/media/libcubeb/gtest/test_loopback.cpp
@@ -289,17 +289,17 @@ void run_loopback_duplex_test(bool is_fl
                         is_float ? data_cb_loop_duplex<float> : data_cb_loop_duplex<short>,
                         state_cb_loop, user_data.get());
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
 
   std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
     cleanup_stream_at_exit(stream, cubeb_stream_destroy);
 
   cubeb_stream_start(stream);
-  delay(150);
+  delay(300);
   cubeb_stream_stop(stream);
 
   /* access after stop should not happen, but lock just in case and to appease sanitization tools */
   std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
   std::vector<double> & output_frames = user_data->output_frames;
   std::vector<double> & input_frames = user_data->input_frames;
   ASSERT_EQ(output_frames.size(), input_frames.size())
     << "#Output frames != #input frames";
@@ -372,17 +372,17 @@ void run_loopback_separate_streams_test(
                         state_cb_loop, user_data.get());
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
 
   std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
     cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
 
   cubeb_stream_start(input_stream);
   cubeb_stream_start(output_stream);
-  delay(150);
+  delay(300);
   cubeb_stream_stop(output_stream);
   cubeb_stream_stop(input_stream);
 
   /* access after stop should not happen, but lock just in case and to appease sanitization tools */
   std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
   std::vector<double> & output_frames = user_data->output_frames;
   std::vector<double> & input_frames = user_data->input_frames;
   ASSERT_LE(output_frames.size(), input_frames.size())
@@ -439,26 +439,26 @@ void run_loopback_silence_test(bool is_f
                         is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
                         state_cb_loop, user_data.get());
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
 
   std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
     cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
 
   cubeb_stream_start(input_stream);
-  delay(50);
+  delay(300);
   cubeb_stream_stop(input_stream);
 
   /* access after stop should not happen, but lock just in case and to appease sanitization tools */
   std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
   std::vector<double> & input_frames = user_data->input_frames;
 
   /* expect to have at least ~50ms of frames */
   ASSERT_GE(input_frames.size(), SAMPLE_FREQUENCY / 20);
-  double EPISILON = 0.000001;
+  double EPISILON = 0.0001;
   /* frames should be 0.0, but use epsilon to avoid possible issues with impls
   that may use ~0.0 silence values. */
   for (double frame : input_frames) {
     ASSERT_LT(abs(frame), EPISILON);
   }
 }
 
 TEST(cubeb, loopback_silence)
@@ -539,17 +539,17 @@ void run_loopback_device_selection_test(
                         state_cb_loop, user_data.get());
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
 
   std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
     cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
 
   cubeb_stream_start(input_stream);
   cubeb_stream_start(output_stream);
-  delay(150);
+  delay(300);
   cubeb_stream_stop(output_stream);
   cubeb_stream_stop(input_stream);
 
   /* access after stop should not happen, but lock just in case and to appease sanitization tools */
   std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
   std::vector<double> & output_frames = user_data->output_frames;
   std::vector<double> & input_frames = user_data->input_frames;
   ASSERT_LE(output_frames.size(), input_frames.size())
deleted file mode 100644
--- a/media/libcubeb/gtest/test_mixer.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright © 2016 Mozilla Foundation
- *
- * This program is made available under an ISC-style license.  See the
- * accompanying file LICENSE for details.
- */
-#include "gtest/gtest.h"
-#include "cubeb/cubeb.h"
-#include "cubeb_mixer.h"
-#include "common.h"
-#include <memory>
-#include <vector>
-
-using std::vector;
-
-#define STREAM_FREQUENCY 48000
-#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
-
-float const M = 1.0f;     // Mono
-float const L = 2.0f;     // Left
-float const R = 3.0f;     // Right
-float const C = 4.0f;     // Center
-float const LS = 5.0f;    // Left Surround
-float const RS = 6.0f;    // Right Surround
-float const RLS = 7.0f;   // Rear Left Surround
-float const RC = 8.0f;    // Rear Center
-float const RRS = 9.0f;   // Rear Right Surround
-float const LFE = 10.0f;  // Low Frequency Effects
-
-float const INV_SQRT_2 = 0.707106f; // 1/sqrt(2)
-static float const DOWNMIX_3F2_RESULTS[2][12][5] = {
-  // 3F2
-  {
-    { INV_SQRT_2*(L+R) + C + 0.5f*(LS+RS) },                          // Mono
-    { INV_SQRT_2*(L+R) + C + 0.5f*(LS+RS), 0 },                       // Mono-LFE
-    { L + INV_SQRT_2*(C+LS), R + INV_SQRT_2*(C+RS) },                 // Stereo
-    { L + INV_SQRT_2*(C+LS), R + INV_SQRT_2*(C+RS), 0 },              // Stereo-LFE
-    { L + INV_SQRT_2*LS, R + INV_SQRT_2*RS, C },                      // 3F
-    { L + INV_SQRT_2*LS, R + INV_SQRT_2*RS, C, 0 },                   // 3F-LFE
-    { L + C*INV_SQRT_2, R + C*INV_SQRT_2, INV_SQRT_2*(LS+RS) },       // 2F1
-    { L + C*INV_SQRT_2, R + C*INV_SQRT_2, 0, INV_SQRT_2*(LS+RS) },    // 2F1-LFE
-    { L, R, C, INV_SQRT_2*(LS+RS) },                                  // 3F1
-    { L, R, C, 0, INV_SQRT_2*(LS+RS) },                               // 3F1-LFE
-    { L + INV_SQRT_2*C, R + INV_SQRT_2*C, LS, RS },                   // 2F2
-    { L + INV_SQRT_2*C, R + INV_SQRT_2*C, 0, LS, RS }                 // 2F2-LFE
-  },
-  // 3F2-LFE
-  {
-    { INV_SQRT_2*(L+R) + C + 0.5f*(LS+RS) },                          // Mono
-    { INV_SQRT_2*(L+R) + C + 0.5f*(LS+RS), LFE },                     // Mono-LFE
-    { L + INV_SQRT_2*(C+LS), R + INV_SQRT_2*(C+RS) },                 // Stereo
-    { L + INV_SQRT_2*(C+LS), R + INV_SQRT_2*(C+RS), LFE },            // Stereo-LFE
-    { L + INV_SQRT_2*LS, R + INV_SQRT_2*RS, C },                      // 3F
-    { L + INV_SQRT_2*LS, R + INV_SQRT_2*RS, C, LFE },                 // 3F-LFE
-    { L + C*INV_SQRT_2, R + C*INV_SQRT_2, INV_SQRT_2*(LS+RS) },       // 2F1
-    { L + C*INV_SQRT_2, R + C*INV_SQRT_2, LFE, INV_SQRT_2*(LS+RS) },  // 2F1-LFE
-    { L, R, C, INV_SQRT_2*(LS+RS) },                                  // 3F1
-    { L, R, C, LFE, INV_SQRT_2*(LS+RS) },                             // 3F1-LFE
-    { L + INV_SQRT_2*C, R + INV_SQRT_2*C, LS, RS },                   // 2F2
-    { L + INV_SQRT_2*C, R + INV_SQRT_2*C, LFE, LS, RS }               // 2F2-LFE
-  }
-};
-
-typedef struct {
-  cubeb_channel_layout layout;
-  float data[10];
-} audio_input;
-
-audio_input audio_inputs[CUBEB_LAYOUT_MAX] = {
-  { CUBEB_LAYOUT_UNDEFINED,     { } },
-  { CUBEB_LAYOUT_DUAL_MONO,     { L, R } },
-  { CUBEB_LAYOUT_DUAL_MONO_LFE, { L, R, LFE } },
-  { CUBEB_LAYOUT_MONO,          { M } },
-  { CUBEB_LAYOUT_MONO_LFE,      { M, LFE } },
-  { CUBEB_LAYOUT_STEREO,        { L, R } },
-  { CUBEB_LAYOUT_STEREO_LFE,    { L, R, LFE } },
-  { CUBEB_LAYOUT_3F,            { L, R, C } },
-  { CUBEB_LAYOUT_3F_LFE,        { L, R, C, LFE } },
-  { CUBEB_LAYOUT_2F1,           { L, R, RC } },
-  { CUBEB_LAYOUT_2F1_LFE,       { L, R, LFE, RC } },
-  { CUBEB_LAYOUT_3F1,           { L, R, C, RC } },
-  { CUBEB_LAYOUT_3F1_LFE,       { L, R, C, LFE, RC } },
-  { CUBEB_LAYOUT_2F2,           { L, R, LS, RS } },
-  { CUBEB_LAYOUT_2F2_LFE,       { L, R, LFE, LS, RS } },
-  { CUBEB_LAYOUT_3F2,           { L, R, C, LS, RS } },
-  { CUBEB_LAYOUT_3F2_LFE,       { L, R, C, LFE, LS, RS } },
-  { CUBEB_LAYOUT_3F3R_LFE,      { L, R, C, LFE, RC, LS, RS } },
-  { CUBEB_LAYOUT_3F4_LFE,       { L, R, C, LFE, RLS, RRS, LS, RS } }
-};
-
-char const * channel_names[CHANNEL_UNMAPPED + 1] = {
-  "mono",                   // CHANNEL_MONO
-  "left",                   // CHANNEL_LEFT
-  "right",                  // CHANNEL_RIGHT
-  "center",                 // CHANNEL_CENTER
-  "left surround",          // CHANNEL_LS
-  "right surround",         // CHANNEL_RS
-  "rear left surround",     // CHANNEL_RLS
-  "rear center",            // CHANNEL_RCENTER
-  "rear right surround",    // CHANNEL_RRS
-  "low frequency effects",  // CHANNEL_LFE
-  "unmapped"                // CHANNEL_UNMAPPED
-};
-
-// The test cases must be aligned with cubeb_downmix.
-void
-downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
-{
-  if (in_layout == CUBEB_LAYOUT_UNDEFINED) {
-    return; // Only possible output layout would be UNDEFINED.
-  }
-
-  cubeb_stream_params in_params = {
-    STREAM_FORMAT,
-    STREAM_FREQUENCY,
-    layout_infos[in_layout].channels,
-    in_layout,
-    CUBEB_STREAM_PREF_NONE
-  };
-
-  cubeb_stream_params out_params = {
-    STREAM_FORMAT,
-    STREAM_FREQUENCY,
-    // To downmix audio data with undefined layout, its channel number must be
-    // smaller than or equal to the input channels.
-    (out_layout == CUBEB_LAYOUT_UNDEFINED) ?
-      layout_infos[in_layout].channels : layout_infos[out_layout].channels,
-    out_layout,
-    CUBEB_STREAM_PREF_NONE
-   };
-
-  if (!cubeb_should_downmix(&in_params, &out_params)) {
-    return;
-  }
-
-  fprintf(stderr, "Downmix from %s to %s\n", layout_infos[in_layout].name, layout_infos[out_layout].name);
-
-  unsigned int const inframes = 10;
-  vector<float> in(in_params.channels * inframes);
-#if defined(__APPLE__)
-  // The mixed buffer size doesn't be changed based on the channel layout set on OSX.
-  // Please see the comment above downmix_3f2 in cubeb_mixer.cpp.
-  vector<float> out(in_params.channels * inframes);
-#else
-  // In normal case, the mixed buffer size is based on the mixing channel layout.
-  vector<float> out(out_params.channels * inframes);
-#endif
-
-  for (unsigned int offset = 0 ; offset < inframes * in_params.channels ; offset += in_params.channels) {
-    for (unsigned int i = 0 ; i < in_params.channels ; ++i) {
-      in[offset + i] = data[i];
-    }
-  }
-
-  // Create a mixer for downmix only.
-  std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)>
-    mixer(cubeb_mixer_create(in_params.format, CUBEB_MIXER_DIRECTION_DOWNMIX), cubeb_mixer_destroy);
-
-  assert(!in.empty() && !out.empty() && out.size() <= in.size());
-  cubeb_mixer_mix(mixer.get(), inframes, in.data(), in.size(), out.data(), out.size(), &in_params, &out_params);
-
-  uint32_t in_layout_mask = 0;
-  for (unsigned int i = 0 ; i < in_params.channels; ++i) {
-    in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i];
-  }
-
-  uint32_t out_layout_mask = 0;
-  for (unsigned int i = 0 ; out_layout != CUBEB_LAYOUT_UNDEFINED && i < out_params.channels; ++i) {
-    out_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[out_layout][i];
-  }
-
-  for (unsigned int i = 0 ; i < out.size() ; ++i) {
-    assert(in_params.channels && out_params.channels); // to pass the scan-build warning: Division by zero.
-#if defined(__APPLE__)
-    // The size of audio mix buffer(vector out above) on OS X is same as input,
-    // so we need to check whether the out[i] will be dropped or not.
-    unsigned int index = i % in_params.channels;
-    if (index >= out_params.channels) {
-      // The out[i] will be dropped, so we don't care the data inside.
-      fprintf(stderr, "\tOS X: %d will be dropped. Ignore it.\n", i);
-      continue;
-    }
-#else
-    unsigned int index = i % out_params.channels;
-#endif
-
-    // downmix_3f2
-    if ((in_layout == CUBEB_LAYOUT_3F2 || in_layout == CUBEB_LAYOUT_3F2_LFE) &&
-        out_layout >= CUBEB_LAYOUT_MONO && out_layout <= CUBEB_LAYOUT_2F2_LFE) {
-      auto & downmix_results = DOWNMIX_3F2_RESULTS[in_layout - CUBEB_LAYOUT_3F2][out_layout - CUBEB_LAYOUT_MONO];
-      fprintf(stderr, "\t[3f2] %d(%s) - Expect: %lf, Get: %lf\n", i, channel_names[ CHANNEL_INDEX_TO_ORDER[out_layout][index] ], downmix_results[index], out[i]);
-      ASSERT_EQ(downmix_results[index], out[i]);
-      continue;
-    }
-
-#if defined(__APPLE__)
-    fprintf(stderr, "\tOS X: We only support downmix for audio 5.1 currently.\n");
-    return;
-#endif
-
-    // mix_remap
-    if (out_layout_mask & in_layout_mask) {
-      uint32_t mask = 1 << CHANNEL_INDEX_TO_ORDER[out_layout][index];
-      fprintf(stderr, "\t[remap] %d(%s) - Expect: %lf, Get: %lf\n", i, channel_names[ CHANNEL_INDEX_TO_ORDER[out_layout][index] ], (mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[i]);
-      ASSERT_EQ((mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[i]);
-      continue;
-    }
-
-    // downmix_fallback
-    fprintf(stderr, "\t[fallback] %d - Expect: %lf, Get: %lf\n", i, audio_inputs[in_layout].data[index], out[i]);
-    ASSERT_EQ(audio_inputs[in_layout].data[index], out[i]);
-  }
-}
-
-TEST(cubeb, mixer)
-{
-  for (auto audio_input : audio_inputs) {
-    for (auto audio_output : layout_infos) {
-      downmix_test(audio_input.data, audio_input.layout, audio_output.layout);
-    }
-  }
-}
--- a/media/libcubeb/gtest/test_sanity.cpp
+++ b/media/libcubeb/gtest/test_sanity.cpp
@@ -109,17 +109,16 @@ TEST(cubeb, init_destroy_multiple_contex
   }
 }
 
 TEST(cubeb, context_variables)
 {
   int r;
   cubeb * ctx;
   uint32_t value;
-  cubeb_channel_layout layout;
   cubeb_stream_params params;
 
   r = common_init(&ctx, "test_context_variables");
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   params.channels = STREAM_CHANNELS;
   params.format = STREAM_FORMAT;
@@ -134,21 +133,16 @@ TEST(cubeb, context_variables)
   }
 
   r = cubeb_get_preferred_sample_rate(ctx, &value);
   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
     ASSERT_TRUE(value > 0);
   }
 
-  r = cubeb_get_preferred_channel_layout(ctx, &layout);
-  ASSERT_TRUE(r == CUBEB_ERROR_NOT_SUPPORTED ||
-              (r == CUBEB_OK && layout != CUBEB_LAYOUT_UNDEFINED) ||
-              (r == CUBEB_ERROR && layout == CUBEB_LAYOUT_UNDEFINED));
-
   cubeb_destroy(ctx);
 }
 
 TEST(cubeb, init_destroy_stream)
 {
   int r;
   cubeb * ctx;
   cubeb_stream * stream;
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -152,90 +152,93 @@ typedef void const * cubeb_devid;
 
 /** Level (verbosity) of logging for a particular cubeb context. */
 typedef enum {
   CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
   CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
   CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
 } cubeb_log_level;
 
-/** SMPTE channel layout (also known as wave order)
- * DUAL-MONO      L   R
- * DUAL-MONO-LFE  L   R   LFE
- * MONO           M
- * MONO-LFE       M   LFE
- * STEREO         L   R
- * STEREO-LFE     L   R   LFE
- * 3F             L   R   C
- * 3F-LFE         L   R   C    LFE
- * 2F1            L   R   RC
- * 2F1-LFE        L   R   LFE  RC
- * 3F1            L   R   C    RC
- * 3F1-LFE        L   R   C    LFE  RC
- * 2F2            L   R   LS   RS
- * 2F2-LFE        L   R   LFE  LS   RS
- * 3F2            L   R   C    LS   RS
- * 3F2-LFE        L   R   C    LFE  LS   RS
- * 3F3R-LFE       L   R   C    LFE  RC   LS   RS
- * 3F4-LFE        L   R   C    LFE  RLS  RRS  LS   RS
- *
- * The abbreviation of channel name is defined in following table:
- * Abbr  Channel name
- * ---------------------------
- * M     Mono
- * L     Left
- * R     Right
- * C     Center
- * LS    Left Surround
- * RS    Right Surround
- * RLS   Rear Left Surround
- * RC    Rear Center
- * RRS   Rear Right Surround
- * LFE   Low Frequency Effects
- */
+typedef enum {
+  CHANNEL_UNKNOWN = 0,
+  CHANNEL_FRONT_LEFT = 1 << 0,
+  CHANNEL_FRONT_RIGHT = 1 << 1,
+  CHANNEL_FRONT_CENTER = 1 << 2,
+  CHANNEL_LOW_FREQUENCY = 1 << 3,
+  CHANNEL_BACK_LEFT = 1 << 4,
+  CHANNEL_BACK_RIGHT = 1 << 5,
+  CHANNEL_FRONT_LEFT_OF_CENTER = 1 << 6,
+  CHANNEL_FRONT_RIGHT_OF_CENTER = 1 << 7,
+  CHANNEL_BACK_CENTER = 1 << 8,
+  CHANNEL_SIDE_LEFT = 1 << 9,
+  CHANNEL_SIDE_RIGHT = 1 << 10,
+  CHANNEL_TOP_CENTER = 1 << 11,
+  CHANNEL_TOP_FRONT_LEFT = 1 << 12,
+  CHANNEL_TOP_FRONT_CENTER = 1 << 13,
+  CHANNEL_TOP_FRONT_RIGHT = 1 << 14,
+  CHANNEL_TOP_BACK_LEFT = 1 << 15,
+  CHANNEL_TOP_BACK_CENTER = 1 << 16,
+  CHANNEL_TOP_BACK_RIGHT = 1 << 17
+} cubeb_channel;
 
-typedef enum {
-  CUBEB_LAYOUT_UNDEFINED, // Indicate the speaker's layout is undefined.
-  CUBEB_LAYOUT_DUAL_MONO,
-  CUBEB_LAYOUT_DUAL_MONO_LFE,
-  CUBEB_LAYOUT_MONO,
-  CUBEB_LAYOUT_MONO_LFE,
-  CUBEB_LAYOUT_STEREO,
-  CUBEB_LAYOUT_STEREO_LFE,
-  CUBEB_LAYOUT_3F,
-  CUBEB_LAYOUT_3F_LFE,
-  CUBEB_LAYOUT_2F1,
-  CUBEB_LAYOUT_2F1_LFE,
-  CUBEB_LAYOUT_3F1,
-  CUBEB_LAYOUT_3F1_LFE,
-  CUBEB_LAYOUT_2F2,
-  CUBEB_LAYOUT_2F2_LFE,
-  CUBEB_LAYOUT_3F2,
-  CUBEB_LAYOUT_3F2_LFE,
-  CUBEB_LAYOUT_3F3R_LFE,
-  CUBEB_LAYOUT_3F4_LFE,
-  CUBEB_LAYOUT_MAX
-} cubeb_channel_layout;
+typedef uint32_t cubeb_channel_layout;
+// Some common layout definitions.
+enum {
+  CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
+  CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
+  CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
+  CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F =
+    CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
+  CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_2F1 =
+    CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
+  CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                     CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
+  CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                     CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
+  CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                      CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
+  CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                     CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
+                     CHANNEL_SIDE_RIGHT,
+  CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
+  CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
+  CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                          CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
+                          CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
+                          CHANNEL_SIDE_RIGHT,
+  CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+                         CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
+                         CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
+                         CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
+};
 
 /** Miscellaneous stream preferences. */
 typedef enum {
   CUBEB_STREAM_PREF_NONE     = 0x00, /**< No stream preferences are requested. */
   CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be
                                          specified on the input params and an
                                          output device to loopback from should
                                          be passed in place of an input device. */
 } cubeb_stream_prefs;
 
 /** Stream format initialization parameters. */
 typedef struct {
   cubeb_sample_format format;   /**< Requested sample format.  One of
                                      #cubeb_sample_format. */
   uint32_t rate;                /**< Requested sample rate.  Valid range is [1000, 192000]. */
   uint32_t channels;            /**< Requested channel count.  Valid range is [1, 8]. */
-  cubeb_channel_layout layout;  /**< Requested channel layout. This must be consistent with the provided channels. */
+  cubeb_channel_layout layout;  /**< Requested channel layout. This must be consistent with the provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
   cubeb_stream_prefs prefs;     /**< Requested preferences. */
 } cubeb_stream_params;
 
 /** Audio device description */
 typedef struct {
   char * output_name; /**< The name of the output device */
   char * input_name; /**< The name of the input device */
 } cubeb_device;
@@ -361,18 +364,22 @@ typedef struct {
       all the frames have been output.
     @param stream The stream for which this callback fired.
     @param user_ptr The pointer passed to cubeb_stream_init.
     @param input_buffer A pointer containing the input data, or nullptr
                         if this is an output-only stream.
     @param output_buffer A pointer to a buffer to be filled with audio samples,
                          or nullptr if this is an input-only stream.
     @param nframes The number of frames of the two buffer.
-    @retval Number of frames written to the output buffer. If this number is
-            less than nframes, then the stream will start to drain.
+    @retval If the stream has output, this is the number of frames written to
+            the output buffer. In this case, if this number is less than
+            nframes then the stream will start to drain. If the stream is
+            input only, then returning nframes indicates data has been read.
+            In this case, a value less than nframes will result in the stream
+            being stopped.
     @retval CUBEB_ERROR on error, in which case the data callback will stop
             and the stream will enter a shutdown state. */
 typedef long (* cubeb_data_callback)(cubeb_stream * stream,
                                      void * user_ptr,
                                      void const * input_buffer,
                                      void * output_buffer,
                                      long nframes);
 
@@ -396,16 +403,20 @@ typedef void (* cubeb_device_changed_cal
 typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
                                                           void * user_ptr);
 
 /** User supplied callback called when a message needs logging. */
 typedef void (* cubeb_log_callback)(char const * fmt, ...);
 
 /** Initialize an application context.  This will perform any library or
     application scoped initialization.
+
+    Note: On Windows platforms, COM must be initialized in MTA mode on
+    any thread that will call the cubeb API.
+
     @param context A out param where an opaque pointer to the application
                    context will be returned.
     @param context_name A name for the context. Depending on the platform this
                         can appear in different locations.
     @param backend_name The name of the cubeb backend user desires to select.
                         Accepted values self-documented in cubeb.c: init_oneshot
                         If NULL, a default ordering is used for backend choice.
                         A valid choice overrides all other possible backends,
@@ -449,25 +460,16 @@ CUBEB_EXPORT int cubeb_get_min_latency(c
     platform dependent, and can avoid resampling, and/or trigger fastpaths.
     @param context A pointer to the cubeb context.
     @param rate The samplerate (in Hz) the current configuration prefers.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED */
 CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
 
-/** Get the preferred layout for this backend: this is hardware and
-    platform dependent.
-    @param context A pointer to the cubeb context.
-    @param layout The layout of the current speaker configuration.
-    @retval CUBEB_OK
-    @retval CUBEB_ERROR_INVALID_PARAMETER
-    @retval CUBEB_ERROR_NOT_SUPPORTED */
-CUBEB_EXPORT int cubeb_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layout);
-
 /** Destroy an application context. This must be called after all stream have
  *  been destroyed.
     @param context A pointer to the cubeb context.*/
 CUBEB_EXPORT void cubeb_destroy(cubeb * context);
 
 /** Initialize a stream associated with the supplied application context.
     @param context A pointer to the cubeb context.
     @param stream An out parameter to be filled with the an opaque pointer to a
--- a/media/libcubeb/src/cubeb-internal.h
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -28,33 +28,24 @@
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
 #if defined(__cplusplus)
 }
 #endif
 
-typedef struct {
-  char const * name;
-  unsigned int const channels;
-  cubeb_channel_layout const layout;
-} cubeb_layout_map;
-
-extern cubeb_layout_map const CUBEB_CHANNEL_LAYOUT_MAPS[CUBEB_LAYOUT_MAX];
-
 struct cubeb_ops {
   int (* init)(cubeb ** context, char const * context_name);
   char const * (* get_backend_id)(cubeb * context);
   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,
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -275,30 +275,16 @@ cubeb_get_preferred_sample_rate(cubeb * 
 
   if (!context->ops->get_preferred_sample_rate) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
   return context->ops->get_preferred_sample_rate(context, rate);
 }
 
-int
-cubeb_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layout)
-{
-  if (!context || !layout) {
-    return CUBEB_ERROR_INVALID_PARAMETER;
-  }
-
-  if (!context->ops->get_preferred_channel_layout) {
-    return CUBEB_ERROR_NOT_SUPPORTED;
-  }
-
-  return context->ops->get_preferred_channel_layout(context, layout);
-}
-
 void
 cubeb_destroy(cubeb * context)
 {
   if (!context) {
     return;
   }
 
   context->ops->destroy(context);
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -1353,17 +1353,16 @@ alsa_device_collection_destroy(cubeb * c
 }
 
 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 = alsa_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_reset_default_device = NULL,
--- a/media/libcubeb/src/cubeb_audiotrack.c
+++ b/media/libcubeb/src/cubeb_audiotrack.c
@@ -418,17 +418,16 @@ 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_reset_default_device = NULL,
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -82,16 +82,17 @@ struct cubeb {
   void * collection_changed_user_ptr = nullptr;
   /* Differentiate input from output devices. */
   cubeb_device_type collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN;
   vector<AudioObjectID> devtype_device_array;
   // The queue is asynchronously deallocated once all references to it are released
   dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
   // Current used channel layout
   atomic<cubeb_channel_layout> layout{ CUBEB_LAYOUT_UNDEFINED };
+  uint32_t channels = 0;
 };
 
 static unique_ptr<AudioChannelLayout, decltype(&free)>
 make_sized_audio_channel_layout(size_t sz)
 {
     assert(sz >= sizeof(AudioChannelLayout));
     AudioChannelLayout * acl = reinterpret_cast<AudioChannelLayout *>(calloc(1, sz));
     assert(acl); // Assert the allocation works.
@@ -183,63 +184,119 @@ struct cubeb_stream {
   unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
   /* This is true if a device change callback is currently running.  */
   atomic<bool> switching_device{ false };
   atomic<bool> buffer_size_change_state{ false };
   AudioDeviceID aggregate_device_id = 0;    // the aggregate device id
   AudioObjectID plugin_id = 0;              // used to create aggregate device
   /* Mixer interface */
   unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
+  /* Buffer where remixing/resampling will occur when upmixing is required */
+  /* Only accessed from callback thread */
+  unique_ptr<uint8_t[]> temp_buffer;
+  size_t temp_buffer_size = 0; // size in bytes.
 };
 
 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;
-    case kAudioChannelLabel_Unknown: return CHANNEL_UNMAPPED;
-    default: return CHANNEL_INVALID;
+    case kAudioChannelLabel_Left:
+      return CHANNEL_FRONT_LEFT;
+    case kAudioChannelLabel_Right:
+      return CHANNEL_FRONT_RIGHT;
+    case kAudioChannelLabel_Center:
+      return CHANNEL_FRONT_CENTER;
+    case kAudioChannelLabel_LFEScreen:
+      return CHANNEL_LOW_FREQUENCY;
+    case kAudioChannelLabel_LeftSurround:
+      return CHANNEL_BACK_LEFT;
+    case kAudioChannelLabel_RightSurround:
+      return CHANNEL_BACK_RIGHT;
+    case kAudioChannelLabel_LeftCenter:
+      return CHANNEL_FRONT_LEFT_OF_CENTER;
+    case kAudioChannelLabel_RightCenter:
+      return CHANNEL_FRONT_RIGHT_OF_CENTER;
+    case kAudioChannelLabel_CenterSurround:
+      return CHANNEL_BACK_CENTER;
+    case kAudioChannelLabel_LeftSurroundDirect:
+      return CHANNEL_SIDE_LEFT;
+    case kAudioChannelLabel_RightSurroundDirect:
+      return CHANNEL_SIDE_RIGHT;
+    case kAudioChannelLabel_TopCenterSurround:
+      return CHANNEL_TOP_CENTER;
+    case kAudioChannelLabel_VerticalHeightLeft:
+      return CHANNEL_TOP_FRONT_LEFT;
+    case kAudioChannelLabel_VerticalHeightCenter:
+      return CHANNEL_TOP_FRONT_CENTER;
+    case kAudioChannelLabel_VerticalHeightRight:
+      return CHANNEL_TOP_FRONT_RIGHT;
+    case kAudioChannelLabel_TopBackLeft:
+      return CHANNEL_TOP_BACK_LEFT;
+    case kAudioChannelLabel_TopBackCenter:
+      return CHANNEL_TOP_BACK_CENTER;
+    case kAudioChannelLabel_TopBackRight:
+      return CHANNEL_TOP_BACK_RIGHT;
+    default:
+      return CHANNEL_UNKNOWN;
   }
 }
 
 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;
-    case CHANNEL_UNMAPPED: return kAudioChannelLabel_Unknown;
-    default: return kAudioChannelLabel_Unknown;
+    case CHANNEL_FRONT_LEFT:
+      return kAudioChannelLabel_Left;
+    case CHANNEL_FRONT_RIGHT:
+      return kAudioChannelLabel_Right;
+    case CHANNEL_FRONT_CENTER:
+      return kAudioChannelLabel_Center;
+    case CHANNEL_LOW_FREQUENCY:
+      return kAudioChannelLabel_LFEScreen;
+    case CHANNEL_BACK_LEFT:
+      return kAudioChannelLabel_LeftSurround;
+    case CHANNEL_BACK_RIGHT:
+      return kAudioChannelLabel_RightSurround;
+    case CHANNEL_FRONT_LEFT_OF_CENTER:
+      return kAudioChannelLabel_LeftCenter;
+    case CHANNEL_FRONT_RIGHT_OF_CENTER:
+      return kAudioChannelLabel_RightCenter;
+    case CHANNEL_BACK_CENTER:
+      return kAudioChannelLabel_CenterSurround;
+    case CHANNEL_SIDE_LEFT:
+      return kAudioChannelLabel_LeftSurroundDirect;
+    case CHANNEL_SIDE_RIGHT:
+      return kAudioChannelLabel_RightSurroundDirect;
+    case CHANNEL_TOP_CENTER:
+      return kAudioChannelLabel_TopCenterSurround;
+    case CHANNEL_TOP_FRONT_LEFT:
+      return kAudioChannelLabel_VerticalHeightLeft;
+    case CHANNEL_TOP_FRONT_CENTER:
+      return kAudioChannelLabel_VerticalHeightCenter;
+    case CHANNEL_TOP_FRONT_RIGHT:
+      return kAudioChannelLabel_VerticalHeightRight;
+    case CHANNEL_TOP_BACK_LEFT:
+      return kAudioChannelLabel_TopBackLeft;
+    case CHANNEL_TOP_BACK_CENTER:
+      return kAudioChannelLabel_TopBackCenter;
+    case CHANNEL_TOP_BACK_RIGHT:
+      return kAudioChannelLabel_TopBackRight;
+    default:
+      return CHANNEL_UNKNOWN;
   }
 }
 
 #if TARGET_OS_IPHONE
 typedef UInt32 AudioDeviceID;
 typedef UInt32 AudioObjectID;
 
 #define AudioGetCurrentHostTime mach_absolute_time
@@ -377,16 +434,22 @@ audiounit_input_callback(void * user_ptr
     auto_lock l(stm->input_linear_buffer_lock);
     assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
     long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
     long outframes = cubeb_resampler_fill(stm->resampler.get(),
                                           stm->input_linear_buffer->data(),
                                           &total_input_frames,
                                           NULL,
                                           0);
+    if (outframes < total_input_frames) {
+      OSStatus r = AudioOutputUnitStop(stm->input_unit);
+      assert(r == 0);
+      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+      return noErr;
+    }
     assert(outframes >= 0);
 
     // Reset input buffer
     stm->input_linear_buffer->clear();
   }
 
   return noErr;
 }
@@ -406,34 +469,36 @@ is_extra_input_needed(cubeb_stream * stm
     * switching, we add some silence as well to compensate for the fact that
     * we're lacking some input data. */
   return stm->frames_read == 0 ||
          stm->available_input_frames.load() < minimum_resampling_input_frames(stm);
 }
 
 static void
 audiounit_mix_output_buffer(cubeb_stream * stm,
-                            long output_frames,
+                            size_t output_frames,
+                            void * input_buffer,
+                            size_t input_buffer_size,
                             void * output_buffer,
-                            unsigned long output_buffer_length)
+                            size_t output_buffer_size)
 {
-  cubeb_stream_params output_mixer_params = {
-    stm->output_stream_params.format,
-    stm->output_stream_params.rate,
-    CUBEB_CHANNEL_LAYOUT_MAPS[stm->context->layout].channels,
-    stm->context->layout,
-    CUBEB_STREAM_PREF_NONE
-  };
-
-  // The downmixing(from 5.1) supports in-place conversion, so we can use
-  // the same buffer for both input and output of the mixer.
-  cubeb_mixer_mix(stm->mixer.get(), output_frames,
-                  output_buffer, output_buffer_length,
-                  output_buffer, output_buffer_length,
-                  &stm->output_stream_params, &output_mixer_params);
+  assert(input_buffer_size >=
+         cubeb_sample_size(stm->output_stream_params.format) *
+           stm->output_stream_params.channels * output_frames);
+  assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames);
+
+  int r = cubeb_mixer_mix(stm->mixer.get(),
+                          output_frames,
+                          input_buffer,
+                          input_buffer_size,
+                          output_buffer,
+                          output_buffer_size);
+  if (r != 0) {
+    LOG("Remix error = %d", r);
+  }
 }
 
 static OSStatus
 audiounit_output_callback(void * user_ptr,
                           AudioUnitRenderActionFlags * /* flags */,
                           AudioTimeStamp const * tstamp,
                           UInt32 bus,
                           UInt32 output_frames,
@@ -468,18 +533,32 @@ audiounit_output_callback(void * user_pt
     if (stm->input_unit) {
       r = AudioOutputUnitStop(stm->input_unit);
       assert(r == 0);
     }
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
+
   /* Get output buffer. */
-  output_buffer = outBufferList->mBuffers[0].mData;
+  if (stm->mixer) {
+    // If remixing needs to occur, we can't directly work in our final
+    // destination buffer as data may be overwritten or too small to start with.
+    size_t size_needed = output_frames * stm->output_stream_params.channels *
+                         cubeb_sample_size(stm->output_stream_params.format);
+    if (stm->temp_buffer_size < size_needed) {
+      stm->temp_buffer.reset(new uint8_t[size_needed]);
+      stm->temp_buffer_size = size_needed;
+    }
+    output_buffer = stm->temp_buffer.get();
+  } else {
+    output_buffer = outBufferList->mBuffers[0].mData;
+  }
+
   /* If Full duplex get also input buffer */
   if (stm->input_unit != NULL) {
     if (is_extra_input_needed(stm)) {
       uint32_t min_input_frames = minimum_resampling_input_frames(stm);
       {
         auto_lock l(stm->input_linear_buffer_lock);
         stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame);
       }
@@ -520,43 +599,49 @@ audiounit_output_callback(void * user_pt
       r = AudioOutputUnitStop(stm->input_unit);
       assert(r == 0);
     }
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
 
-  size_t outbpf = stm->output_desc.mBytesPerFrame;
   stm->draining = (UInt32) outframes < output_frames;
   stm->frames_played = stm->frames_queued;
   stm->frames_queued += outframes;
 
   AudioFormatFlags outaff = stm->output_desc.mFormatFlags;
   float panning = (stm->output_desc.mChannelsPerFrame == 2) ?
       stm->panning.load(memory_order_relaxed) : 0.0f;
 
   /* Post process output samples. */
   if (stm->draining) {
+    size_t outbpf = cubeb_sample_size(stm->output_stream_params.format);
     /* Clear missing frames (silence) */
     memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf);
   }
-  /* Pan stereo. */
-  if (panning != 0.0f) {
-    if (outaff & kAudioFormatFlagIsFloat) {
-      cubeb_pan_stereo_buffer_float((float*)output_buffer, outframes, panning);
-    } else if (outaff & kAudioFormatFlagIsSignedInteger) {
-      cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning);
-    }
-  }
 
   /* Mixing */
-  if (stm->output_stream_params.layout != CUBEB_LAYOUT_UNDEFINED) {
-    unsigned long output_buffer_length = outBufferList->mBuffers[0].mDataByteSize;
-    audiounit_mix_output_buffer(stm, output_frames, output_buffer, output_buffer_length);
+  if (stm->mixer) {
+    audiounit_mix_output_buffer(stm,
+                                output_frames,
+                                output_buffer,
+                                stm->temp_buffer_size,
+                                outBufferList->mBuffers[0].mData,
+                                outBufferList->mBuffers[0].mDataByteSize);
+  } else {
+    /* Pan stereo. */
+    if (panning != 0.0f) {
+      if (outaff & kAudioFormatFlagIsFloat) {
+        cubeb_pan_stereo_buffer_float(
+          (float*)output_buffer, outframes, panning);
+      } else if (outaff & kAudioFormatFlagIsSignedInteger) {
+        cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning);
+      }
+    }
   }
 
   return noErr;
 }
 
 extern "C" {
 int
 audiounit_init(cubeb ** context, char const * /* context_name */)
@@ -1115,38 +1200,41 @@ audiounit_get_preferred_sample_rate(cube
   *rate = static_cast<uint32_t>(fsamplerate);
 #endif
   return CUBEB_OK;
 }
 
 static cubeb_channel_layout
 audiounit_convert_channel_layout(AudioChannelLayout * layout)
 {
+  // When having on or two channel, force mono or stereo. Some devices (namely,
+  //  Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
+  //  some reason.
+  if (layout->mNumberChannelDescriptions == 1) {
+    return CUBEB_LAYOUT_MONO;
+  } else if (layout->mNumberChannelDescriptions == 2) {
+    return CUBEB_LAYOUT_STEREO;
+  }
+
   if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) {
     // kAudioChannelLayoutTag_UseChannelBitmap
     // kAudioChannelLayoutTag_Mono
     // kAudioChannelLayoutTag_Stereo
     // ....
     LOG("Only handle UseChannelDescriptions for now.\n");
     return CUBEB_LAYOUT_UNDEFINED;
   }
 
-  // This devices has more channels that we can support, bail out.
-  if (layout->mNumberChannelDescriptions >= CHANNEL_MAX) {
-    LOG("Audio device has more than %d channels, bailing out.", CHANNEL_MAX);
-    return CUBEB_LAYOUT_UNDEFINED;
+  cubeb_channel_layout cl = 0;
+  for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
+    cl |= channel_label_to_cubeb_channel(
+      layout->mChannelDescriptions[i].mChannelLabel);
   }
 
-  cubeb_channel_map cm;
-  cm.channels = layout->mNumberChannelDescriptions;
-  for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
-    cm.map[i] = channel_label_to_cubeb_channel(layout->mChannelDescriptions[i].mChannelLabel);
-  }
-
-  return cubeb_channel_map_to_layout(&cm);
+  return cl;
 }
 
 static cubeb_channel_layout
 audiounit_get_current_channel_layout(AudioUnit output_unit)
 {
   OSStatus rv = noErr;
   UInt32 size = 0;
   rv = AudioUnitGetPropertyInfo(output_unit,
@@ -1171,86 +1259,18 @@ audiounit_get_current_channel_layout(Aud
   if (rv != noErr) {
     LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
     return CUBEB_LAYOUT_UNDEFINED;
   }
 
   return audiounit_convert_channel_layout(layout.get());
 }
 
-static cubeb_channel_layout
-audiounit_get_preferred_channel_layout()
-{
-  OSStatus rv = noErr;
-  UInt32 size = 0;
-  AudioDeviceID id;
-
-  id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
-  if (id == kAudioObjectUnknown) {
-    return CUBEB_LAYOUT_UNDEFINED;
-  }
-
-  AudioObjectPropertyAddress adr = { kAudioDevicePropertyPreferredChannelLayout,
-                                     kAudioDevicePropertyScopeOutput,
-                                     kAudioObjectPropertyElementMaster };
-  rv = AudioObjectGetPropertyDataSize(id, &adr, 0, NULL, &size);
-  if (rv != noErr) {
-    return CUBEB_LAYOUT_UNDEFINED;
-  }
-  assert(size > 0);
-
-  auto layout = make_sized_audio_channel_layout(size);
-  rv = AudioObjectGetPropertyData(id, &adr, 0, NULL, &size, layout.get());
-  if (rv != noErr) {
-    return CUBEB_LAYOUT_UNDEFINED;
-  }
-
-  return audiounit_convert_channel_layout(layout.get());
-}
-
 static int audiounit_create_unit(AudioUnit * unit, device_info * device);
 
-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.
-  *layout = audiounit_get_preferred_channel_layout();
-
-  // If the preferred channel layout is UNDEFINED, then we try to access the
-  // current applied channel layout.
-  if (*layout == CUBEB_LAYOUT_UNDEFINED) {
-    // If we already have at least one cubeb stream, then the current channel
-    // layout must be updated. We can return it directly.
-    if (ctx->active_streams) {
-      *layout = ctx->layout;
-      return CUBEB_OK;
-    }
-
-    // If there is no existed stream, then we create a default ouput unit and
-    // use it to get the current used channel layout.
-    AudioUnit output_unit = nullptr;
-    device_info default_out_device;
-    default_out_device.id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
-    default_out_device.flags = (DEV_OUTPUT | DEV_SYSTEM_DEFAULT);
-    if (default_out_device.id != kAudioObjectUnknown) {
-      audiounit_create_unit(&output_unit, &default_out_device);
-      *layout = audiounit_get_current_channel_layout(output_unit);
-    }
-  }
-
-  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) {
@@ -1309,90 +1329,70 @@ audio_stream_desc_init(AudioStreamBasicD
   ss->mReserved = 0;
 
   return CUBEB_OK;
 }
 
 void
 audiounit_init_mixer(cubeb_stream * stm)
 {
-  // We only handle downmixing for now.
-  // The audio rendering mechanism on OS X will drop the extra channels beyond
-  // the channels that audio device can provide, so we need to downmix the
+  // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio
+  // data, it silently drop the channels so we need to remix the
   // audio data by ourselves to keep all the information.
   stm->mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
-                                      CUBEB_MIXER_DIRECTION_DOWNMIX));
+                                      stm->output_stream_params.channels,
+                                      stm->output_stream_params.layout,
+                                      stm->context->channels,
+                                      stm->context->layout));
+  assert(stm->mixer);
 }
 
 static int
 audiounit_set_channel_layout(AudioUnit unit,
                              io_side side,
-                             const cubeb_stream_params * stream_params)
+                             cubeb_channel_layout layout)
 {
   if (side != OUTPUT) {
     return CUBEB_ERROR;
   }
 
-  assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
-  assert(stream_params->channels == CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels);
+  if (layout == CUBEB_LAYOUT_UNDEFINED) {
+    // We leave everything as-is...
+    return CUBEB_OK;
+  }
+
 
   OSStatus r;
-  size_t size = sizeof(AudioChannelLayout);
-  auto layout = make_sized_audio_channel_layout(size);
-
-  switch (stream_params->layout) {
-    case CUBEB_LAYOUT_DUAL_MONO:
-    case CUBEB_LAYOUT_STEREO:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
-      break;
-    case CUBEB_LAYOUT_MONO:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
-      break;
-    case CUBEB_LAYOUT_3F:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_0;
-      break;
-    case CUBEB_LAYOUT_2F1:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_1;
-      break;
-    case CUBEB_LAYOUT_3F1:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_1;
-      break;
-    case CUBEB_LAYOUT_2F2:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_2;
-      break;
-    case CUBEB_LAYOUT_3F2:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_2;
-      break;
-    case CUBEB_LAYOUT_3F2_LFE:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
-      break;
-    default:
-      layout->mChannelLayoutTag = kAudioChannelLayoutTag_Unknown;
-      break;
-  }
-
-  // For those layouts that can't be matched to coreaudio's predefined layout,
-  // we use customized layout.
-  if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Unknown) {
-    size = offsetof(AudioChannelLayout, mChannelDescriptions[stream_params->channels]);
-    layout = make_sized_audio_channel_layout(size);
-    layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
-    layout->mNumberChannelDescriptions = stream_params->channels;
-    for (UInt32 i = 0 ; i < stream_params->channels ; ++i) {
-      layout->mChannelDescriptions[i].mChannelLabel =
-        cubeb_channel_to_channel_label(CHANNEL_INDEX_TO_ORDER[stream_params->layout][i]);
-      layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
+  int channels = cubeb_channel_layout_nb_channels(layout);
+
+  // We do not use CoreAudio standard layout for lack of documentation on what
+  // the actual channel orders are. So we set a custom layout.
+  size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[channels]);
+  auto au_layout = make_sized_audio_channel_layout(size);
+  au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+  au_layout->mNumberChannelDescriptions = channels;
+  cubeb_channel_layout channelMap = layout;
+  int i = 0;
+  while (channelMap != 0) {
+    XASSERT(i < channels);
+    uint32_t channel = (channelMap & 1) << i;
+    if (channel != 0) {
+      au_layout->mChannelDescriptions[i].mChannelLabel =
+        cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel));
+      au_layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
+      i += 1;
     }
+    channelMap = channelMap >> 1;
   }
 
   r = AudioUnitSetProperty(unit,
                            kAudioUnitProperty_AudioChannelLayout,
                            kAudioUnitScope_Input,
                            AU_OUT_BUS,
-                           layout.get(),
+                           au_layout.get(),
                            size);
   if (r != noErr) {
     LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", to_string(side), r);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
@@ -1400,24 +1400,19 @@ audiounit_set_channel_layout(AudioUnit u
 void
 audiounit_layout_init(cubeb_stream * stm, io_side side)
 {
   // We currently don't support the input layout setting.
   if (side == INPUT) {
     return;
   }
 
-  audiounit_set_channel_layout(stm->output_unit, OUTPUT, &stm->output_stream_params);
-
-  // Update the current used channel layout for the cubeb context.
-  // Notice that this channel layout may be different from the layout we set above,
-  // because OSX doesn't return error when the output device can NOT provide
-  // our desired layout. Thus, we update the layout evertime when the cubeb_stream
-  // is created and use it when we need to mix audio data.
   stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit);
+
+  audiounit_set_channel_layout(stm->output_unit, OUTPUT, stm->context->layout);
 }
 
 static vector<AudioObjectID>
 audiounit_get_sub_devices(AudioDeviceID device_id)
 {
   vector<AudioDeviceID> sub_devices;
   AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList,
                                                   kAudioObjectPropertyScopeGlobal,
@@ -2305,16 +2300,35 @@ audiounit_configure_output(cubeb_stream 
                            &output_hw_desc,
                            &size);
   if (r != noErr) {
     LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
     return CUBEB_ERROR;
   }
   stm->output_hw_rate = output_hw_desc.mSampleRate;
   LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
+  stm->context->channels = output_hw_desc.mChannelsPerFrame;
+
+  // Set the input layout to match the output device layout.
+  audiounit_layout_init(stm, OUTPUT);
+  if (stm->context->channels != stm->output_stream_params.channels ||
+      stm->context->layout != stm->output_stream_params.layout) {
+    LOG("Incompatible channel layouts detected, setting up remixer");
+    audiounit_init_mixer(stm);
+    // We will be remixing the data before it reaches the output device.
+    // We need to adjust the number of channels and other
+    // AudioStreamDescription details.
+    stm->output_desc.mChannelsPerFrame = stm->context->channels;
+    stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) *
+                                      stm->output_desc.mChannelsPerFrame;
+    stm->output_desc.mBytesPerPacket =
+      stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket;
+  } else {
+    stm->mixer = nullptr;
+  }
 
   r = AudioUnitSetProperty(stm->output_unit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Input,
                            AU_OUT_BUS,
                            &stm->output_desc,
                            sizeof(AudioStreamBasicDescription));
   if (r != noErr) {
@@ -2348,20 +2362,16 @@ audiounit_configure_output(cubeb_stream 
                            AU_OUT_BUS,
                            &aurcbs_out,
                            sizeof(aurcbs_out));
   if (r != noErr) {
     LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback rv=%d", r);
     return CUBEB_ERROR;
   }
 
-  if (stm->output_stream_params.layout != CUBEB_LAYOUT_UNDEFINED) {
-    audiounit_layout_init(stm, OUTPUT);
-    audiounit_init_mixer(stm);
-  }
   LOG("(%p) Output audiounit init successfully.", stm);
   return CUBEB_OK;
 }
 
 static int
 audiounit_setup_stream(cubeb_stream * stm)
 {
   stm->mutex.assert_current_thread_owns();
@@ -3463,17 +3473,16 @@ 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 =*/ audiounit_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_reset_default_device =*/ nullptr,
--- a/media/libcubeb/src/cubeb_jack.cpp
+++ b/media/libcubeb/src/cubeb_jack.cpp
@@ -114,17 +114,16 @@ static int cbjack_stream_get_position(cu
 static int cbjack_stream_set_volume(cubeb_stream * stm, float volume);
 
 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 = 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,
--- a/media/libcubeb/src/cubeb_mixer.cpp
+++ b/media/libcubeb/src/cubeb_mixer.cpp
@@ -1,571 +1,663 @@
 /*
  * Copyright © 2016 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
+ *
+ * Adapted from code based on libswresample's rematrix.c
  */
 
+#define NOMINMAX
+
+#include <algorithm>
 #include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+#include <memory>
+#include <type_traits>
 #include "cubeb-internal.h"
 #include "cubeb_mixer.h"
+#include "cubeb_utils.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))
+#ifndef FF_ARRAY_ELEMS
+#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+#endif
 
-cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map)
+#define CHANNELS_MAX 32
+#define FRONT_LEFT             0
+#define FRONT_RIGHT            1
+#define FRONT_CENTER           2
+#define LOW_FREQUENCY          3
+#define BACK_LEFT              4
+#define BACK_RIGHT             5
+#define FRONT_LEFT_OF_CENTER   6
+#define FRONT_RIGHT_OF_CENTER  7
+#define BACK_CENTER            8
+#define SIDE_LEFT              9
+#define SIDE_RIGHT             10
+#define TOP_CENTER             11
+#define TOP_FRONT_LEFT         12
+#define TOP_FRONT_CENTER       13
+#define TOP_FRONT_RIGHT        14
+#define TOP_BACK_LEFT          15
+#define TOP_BACK_CENTER        16
+#define TOP_BACK_RIGHT         17
+#define NUM_NAMED_CHANNELS     18
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2      0.70710678118654752440  /* 1/sqrt(2) */
+#endif
+#ifndef M_SQRT2
+#define M_SQRT2        1.41421356237309504880  /* sqrt(2) */
+#endif
+#define SQRT3_2      1.22474487139158904909  /* sqrt(3/2) */
+
+#define  C30DB  M_SQRT2
+#define  C15DB  1.189207115
+#define C__0DB  1.0
+#define C_15DB  0.840896415
+#define C_30DB  M_SQRT1_2
+#define C_45DB  0.594603558
+#define C_60DB  0.5
+
+static cubeb_channel_layout
+cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
 {
-  uint32_t channel_mask = 0;
-  for (uint8_t i = 0 ; i < channel_map->channels ; ++i) {
-    if (channel_map->map[i] == CHANNEL_INVALID ||
-        channel_map->map[i] == CHANNEL_UNMAPPED) {
-      return CUBEB_LAYOUT_UNDEFINED;
+    if (l == CUBEB_LAYOUT_UNDEFINED) {
+      switch (c) {
+        case 1: return CUBEB_LAYOUT_MONO;
+        case 2: return CUBEB_LAYOUT_STEREO;
+      }
     }
-    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;
-  }
+    return l;
 }
 
-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 },
-  { "3f",             3,  CUBEB_LAYOUT_3F },
-  { "3f lfe",         4,  CUBEB_LAYOUT_3F_LFE },
-  { "2f1",            3,  CUBEB_LAYOUT_2F1 },
-  { "2f1 lfe",        4,  CUBEB_LAYOUT_2F1_LFE },
-  { "3f1",            4,  CUBEB_LAYOUT_3F1 },
-  { "3f1 lfe",        5,  CUBEB_LAYOUT_3F1_LFE },
-  { "2f2",            4,  CUBEB_LAYOUT_2F2 },
-  { "2f2 lfe",        5,  CUBEB_LAYOUT_2F2_LFE },
-  { "3f2",            5,  CUBEB_LAYOUT_3F2 },
-  { "3f2 lfe",        6,  CUBEB_LAYOUT_3F2_LFE },
-  { "3f3r lfe",       7,  CUBEB_LAYOUT_3F3R_LFE },
-  { "3f4 lfe",        8,  CUBEB_LAYOUT_3F4_LFE }
-};
-
-static int const CHANNEL_ORDER_TO_INDEX[CUBEB_LAYOUT_MAX][CHANNEL_MAX] = {
-//  M | L | R | C | LS | RS | RLS | RC | RRS | LFE
-  { -1, -1, -1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // UNDEFINED
-  { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // DUAL_MONO
-  { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,   2 }, // DUAL_MONO_LFE
-  {  0, -1, -1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // MONO
-  {  0, -1, -1, -1,  -1,  -1,   -1,  -1,   -1,   1 }, // MONO_LFE
-  { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // STEREO
-  { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,   2 }, // STEREO_LFE
-  { -1,  0,  1,  2,  -1,  -1,   -1,  -1,   -1,  -1 }, // 3F
-  { -1,  0,  1,  2,  -1,  -1,   -1,  -1,   -1,   3 }, // 3F_LFE
-  { -1,  0,  1, -1,  -1,  -1,   -1,   2,   -1,  -1 }, // 2F1
-  { -1,  0,  1, -1,  -1,  -1,   -1,   3,   -1,   2 }, // 2F1_LFE
-  { -1,  0,  1,  2,  -1,  -1,   -1,   3,   -1,  -1 }, // 3F1
-  { -1,  0,  1,  2,  -1,  -1,   -1,   4,   -1,   3 }, // 3F1_LFE
-  { -1,  0,  1, -1,   2,   3,   -1,  -1,   -1,  -1 }, // 2F2
-  { -1,  0,  1, -1,   3,   4,   -1,  -1,   -1,   2 }, // 2F2_LFE
-  { -1,  0,  1,  2,   3,   4,   -1,  -1,   -1,  -1 }, // 3F2
-  { -1,  0,  1,  2,   4,   5,   -1,  -1,   -1,   3 }, // 3F2_LFE
-  { -1,  0,  1,  2,   5,   6,   -1,   4,   -1,   3 }, // 3F3R_LFE
-  { -1,  0,  1,  2,   6,   7,    4,  -1,    5,   3 }, // 3F4_LFE
-};
-
-// The downmix matrix from TABLE 2 in the ITU-R BS.775-3[1] defines a way to
-// convert 3F2 input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 output data. We extend it
-// to convert 3F2-LFE input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs
-// output data.
-// [1] https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.775-3-201208-I!!PDF-E.pdf
-
-// Number of converted layouts: 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs.
-unsigned int const SUPPORTED_LAYOUT_NUM = 12;
-// Number of input channel for downmix conversion.
-unsigned int const INPUT_CHANNEL_NUM = 6; // 3F2-LFE
-// Max number of possible output channels.
-unsigned int const MAX_OUTPUT_CHANNEL_NUM = 5; // 2F2-LFE or 3F1-LFE
-float const INV_SQRT_2 = 0.707106f; // 1/sqrt(2)
-// Each array contains coefficients that will be multiplied with
-// { L, R, C, LFE, LS, RS } channels respectively.
-static float const DOWNMIX_MATRIX_3F2_LFE[SUPPORTED_LAYOUT_NUM][MAX_OUTPUT_CHANNEL_NUM][INPUT_CHANNEL_NUM] =
+unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
 {
-// 1F Mono
-  {
-    { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M
-  },
-// 1F Mono-LFE
-  {
-    { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M
-    { 0, 0, 0, 1, 0, 0 }                        // LFE
-  },
-// 2F Stereo
-  {
-    { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 },     // L
-    { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 }      // R
-  },
-// 2F Stereo-LFE
-  {
-    { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 },     // L
-    { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 },     // R
-    { 0, 0, 0, 1, 0, 0 }                        // LFE
-  },
-// 3F
-  {
-    { 1, 0, 0, 0, INV_SQRT_2, 0 },              // L
-    { 0, 1, 0, 0, 0, INV_SQRT_2 },              // R
-    { 0, 0, 1, 0, 0, 0 }                        // C
-  },
-// 3F-LFE
-  {
-    { 1, 0, 0, 0, INV_SQRT_2, 0 },              // L
-    { 0, 1, 0, 0, 0, INV_SQRT_2 },              // R
-    { 0, 0, 1, 0, 0, 0 },                       // C
-    { 0, 0, 0, 1, 0, 0 }                        // LFE
-  },
-// 2F1
+#if __GNUC__ || __clang__
+  return __builtin_popcount (x);
+#elif _MSC_VER
+  return __popcnt(x);
+#else
+  x -= (x >> 1) & 0x55555555;
+  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+  x = (x + (x >> 4)) & 0x0F0F0F0F;
+  x += x >> 8;
+  return (x + (x >> 16)) & 0x3F;
+#endif
+}
+struct MixerContext {
+  MixerContext(cubeb_sample_format f,
+               uint32_t in_channels,
+               cubeb_channel_layout in,
+               uint32_t out_channels,
+               cubeb_channel_layout out)
+    : _format(f)
+    , _in_ch_layout(cubeb_channel_layout_check(in, in_channels))
+    , _out_ch_layout(cubeb_channel_layout_check(out, out_channels))
+    , _in_ch_count(in_channels)
+    , _out_ch_count(out_channels)
   {
-    { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
-    { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
-    { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
-  },
-// 2F1-LFE
-  {
-    { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
-    { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
-    { 0, 0, 0, 1, 0, 0 },                       // LFE
-    { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
-  },
-// 3F1
-  {
-    { 1, 0, 0, 0, 0, 0 },                       // L
-    { 0, 1, 0, 0, 0, 0 },                       // R
-    { 0, 0, 1, 0, 0, 0 },                       // C
-    { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
-  },
-// 3F1-LFE
-  {
-    { 1, 0, 0, 0, 0, 0 },                       // L
-    { 0, 1, 0, 0, 0, 0 },                       // R
-    { 0, 0, 1, 0, 0, 0 },                       // C
-    { 0, 0, 0, 1, 0, 0 },                       // LFE
-    { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
-  },
-// 2F2
-  {
-    { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
-    { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
-    { 0, 0, 0, 0, 1, 0 },                       // LS
-    { 0, 0, 0, 0, 0, 1 }                        // RS
-  },
-// 2F2-LFE
-  {
-    { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
-    { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
-    { 0, 0, 0, 1, 0, 0 },                       // LFE
-    { 0, 0, 0, 0, 1, 0 },                       // LS
-    { 0, 0, 0, 0, 0, 1 }                        // RS
+    if (in_channels != cubeb_channel_layout_nb_channels(in) ||
+        out_channels != cubeb_channel_layout_nb_channels(out)) {
+      // Mismatch between channels and layout, aborting.
+      return;
+    }
+    _valid = init() >= 0;
   }
-};
 
-// Convert audio data from 3F2(-LFE) to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs.
-//
-// ITU-R BS.775-3[1] provides spec for downmixing 3F2 data to 1F, 2F, 3F, 2F1,
-// 3F1, 2F2 data. We simply add LFE to its defined matrix. If both the input
-// and output have LFE channel, then we pass it's data. If only input or output
-// has LFE, then we either drop it or append 0 to the LFE channel.
-//
-// Fig. 1:
-// |<-------------- 1 -------------->|<-------------- 2 -------------->|
-// +----+----+----+------+-----+-----+----+----+----+------+-----+-----+
-// | L0 | R0 | C0 | LFE0 | LS0 | RS0 | L1 | R1 | C1 | LFE1 | LS1 | RS1 | ...
-// +----+----+----+------+-----+-----+----+----+----+------+-----+-----+
-//
-// Fig. 2:
-// |<-- 1 -->|<-- 2 -->|
-// +----+----+----+----+
-// | L0 | R0 | L1 | R1 | ...
-// +----+----+----+----+
-//
-// The figures above shows an example for downmixing from 3F2-LFE(Fig. 1) to
-// to stereo(Fig. 2), where L0 = L0 + 0.707 * (C0 + LS0),
-// R0 = R0 + 0.707 * (C0 + RS0), L1 = L1 + 0.707 * (C1 + LS1),
-// R1 = R1 + 0.707 * (C1 + RS1), ...
-//
-// Nevertheless, the downmixing method is a little bit different on OSX.
-// The audio rendering mechanism on OS X will drop the extra channels beyond
-// the channels that audio device can provide. The trick here is that OSX allows
-// us to set the layout containing other channels that the output device can
-// NOT provide. For example, setting 3F2-LFE layout to a stereo device is fine.
-// Therefore, OSX expects we fill the buffer for playing sound by the defined
-// layout, so there are some will-be-dropped data in the buffer:
-//
-// +---+---+---+-----+----+----+
-// | L | R | C | LFE | LS | RS | ...
-// +---+---+---+-----+----+----+
-//           ^    ^    ^    ^
-//           The data for these four channels will be dropped!
-//
-// To keep all the information, we need to downmix the data before it's dropped.
-// The figure below shows an example for downmixing from 3F2-LFE(Fig. 1)
-// to stereo(Fig. 3) on OSX, where the LO, R0, L1, R0 are same as above.
-//
-// Fig. 3:
-// |<---------- 1 ---------->|<---------- 2 ---------->|
-// +----+----+---+---+---+---+----+----+---+---+---+---+
-// | L0 | R0 | x | x | x | x | L1 | R1 | x | x | x | x | ...
-// +----+----+---+---+---+---+----+----+---+---+---+---+
-//           |<--  dummy  -->|         |<--  dummy  -->|
-template<typename T>
-bool
-downmix_3f2(unsigned long inframes,
-            T const * const in, unsigned long in_len,
-            T * out, unsigned long out_len,
-            cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
-{
-  if ((in_layout != CUBEB_LAYOUT_3F2 && in_layout != CUBEB_LAYOUT_3F2_LFE) ||
-      out_layout < CUBEB_LAYOUT_MONO || out_layout > CUBEB_LAYOUT_2F2_LFE) {
+  static bool even(cubeb_channel_layout layout)
+  {
+    if (!layout) {
+      return true;
+    }
+    if (layout & (layout - 1)) {
+      return true;
+    }
     return false;
   }
 
-  unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
-  unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
+  // Ensure that the layout is sane (that is have symmetrical left/right
+  // channels), if not, layout will be treated as mono.
+  static cubeb_channel_layout clean_layout(cubeb_channel_layout layout)
+  {
+    if (layout && layout != CHANNEL_FRONT_LEFT && !(layout & (layout - 1))) {
+      LOG("Treating layout as mono");
+      return CHANNEL_FRONT_CENTER;
+    }
+
+    return layout;
+  }
 
-  // Conversion from 3F2 to 2F2-LFE or 3F1-LFE is allowed, so we use '<=' instead of '<'.
-  assert(out_channels <= in_channels);
+  static bool sane_layout(cubeb_channel_layout layout)
+  {
+    if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker
+      return false;
+    }
+    if (!even(layout & (CHANNEL_FRONT_LEFT |
+                        CHANNEL_FRONT_RIGHT))) { // no asymetric front
+      return false;
+    }
+    if (!even(layout &
+              (CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side
+      return false;
+    }
+    if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) {
+      return false;
+    }
+    if (!even(layout &
+              (CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) {
+      return false;
+    }
+    if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) {
+      return false;
+    }
+    return true;
+  }
+
+  int auto_matrix();
+  int init();
 
-  auto & downmix_matrix = DOWNMIX_MATRIX_3F2_LFE[out_layout - CUBEB_LAYOUT_MONO]; // The matrix is started from mono.
-  unsigned long out_index = 0;
-  for (unsigned long i = 0 ; i < inframes * in_channels; i += in_channels) {
-    for (unsigned int j = 0; j < out_channels; ++j) {
-      T sample = 0;
-      for (unsigned int k = 0 ; k < INPUT_CHANNEL_NUM ; ++k) {
-        // 3F2-LFE has 6 channels: L, R, C, LFE, LS, RS, while 3F2 has only 5
-        // channels: L, R, C, LS, RS. Thus, we need to append 0 to LFE(index 3)
-        // to simulate a 3F2-LFE data when input layout is 3F2.
-        assert((in_layout == CUBEB_LAYOUT_3F2_LFE || k < 3) ? (i + k < in_len) : (k == 3) ? true : (i + k - 1 < in_len));
-        T data = (in_layout == CUBEB_LAYOUT_3F2_LFE) ? in[i + k] : (k == 3) ? 0 : in[i + ((k < 3) ? k : k - 1)];
-        sample += downmix_matrix[j][k] * data;
+  const cubeb_sample_format _format;
+  const cubeb_channel_layout _in_ch_layout;              ///< input channel layout
+  const cubeb_channel_layout _out_ch_layout;             ///< output channel layout
+  const uint32_t _in_ch_count;                           ///< input channel count
+  const uint32_t _out_ch_count;                          ///< output channel count
+  const float _surround_mix_level = C_30DB;              ///< surround mixing level
+  const float _center_mix_level = C_30DB;                ///< center mixing level
+  const float _lfe_mix_level = 1;                        ///< LFE mixing level
+  double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }};        ///< floating point rematrixing coefficients
+  float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }};     ///< single precision floating point rematrixing coefficients
+  int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }};     ///< 17.15 fixed point rematrixing coefficients
+  uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }};  ///< Lists of input channels per output channel that have non zero rematrixing coefficients
+  bool _clipping = false;                          ///< Set to true if clipping detection is required
+  bool _valid = false;                             ///< Set to true if context is valid.
+};
+
+int MixerContext::auto_matrix()
+{
+  double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } };
+  double maxcoef = 0;
+  float maxval;
+
+  cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout);
+  cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout);
+
+  if (!sane_layout(in_ch_layout)) {
+    // Channel Not Supported
+    LOG("Input Layout %x is not supported", _in_ch_layout);
+    return -1;
+  }
+
+  if (!sane_layout(out_ch_layout)) {
+    LOG("Output Layout %x is not supported", _out_ch_layout);
+    return -1;
+  }
+
+  for (uint32_t i = 0; i < FF_ARRAY_ELEMS(matrix); i++) {
+    if (in_ch_layout & out_ch_layout & (1U << i)) {
+      matrix[i][i] = 1.0;
+    }
+  }
+
+  cubeb_channel_layout unaccounted = in_ch_layout & ~out_ch_layout;
+
+  // Rematrixing is done via a matrix of coefficient that should be applied to
+  // all channels. Channels are treated as pair and must be symmetrical (if a
+  // left channel exists, the corresponding right should exist too) unless the
+  // output layout has similar layout. Channels are then mixed toward the front
+  // center or back center if they exist with a slight bias toward the front.
+
+  if (unaccounted & CHANNEL_FRONT_CENTER) {
+    if ((out_ch_layout & CUBEB_LAYOUT_STEREO) == CUBEB_LAYOUT_STEREO) {
+      if (in_ch_layout & CUBEB_LAYOUT_STEREO) {
+        matrix[FRONT_LEFT][FRONT_CENTER] += _center_mix_level;
+        matrix[FRONT_RIGHT][FRONT_CENTER] += _center_mix_level;
+      } else {
+        matrix[FRONT_LEFT][FRONT_CENTER] += M_SQRT1_2;
+        matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
       }
-      assert(out_index + j < out_len);
-      out[out_index + j] = sample;
     }
-#if defined(USE_AUDIOUNIT)
-    out_index += in_channels;
-#else
-    out_index += out_channels;
-#endif
+  }
+  if (unaccounted & CUBEB_LAYOUT_STEREO) {
+    if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][FRONT_LEFT] += M_SQRT1_2;
+      matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
+      if (in_ch_layout & CHANNEL_FRONT_CENTER)
+        matrix[FRONT_CENTER][FRONT_CENTER] = _center_mix_level * M_SQRT2;
+    }
   }
 
-  return true;
-}
-
-/* Map the audio data by channel name. */
-template<class T>
-bool
-mix_remap(long inframes,
-          T const * const in, unsigned long in_len,
-          T * out, unsigned long out_len,
-          cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
-{
-  assert(in_layout != out_layout && inframes >= 0);
-
-  // We might overwrite the data before we copied them to the mapped index
-  // (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L])
-  if (in == out) {
-    return false;
+  if (unaccounted & CHANNEL_BACK_CENTER) {
+    if (out_ch_layout & CHANNEL_BACK_LEFT) {
+      matrix[BACK_LEFT][BACK_CENTER] += M_SQRT1_2;
+      matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
+      matrix[SIDE_LEFT][BACK_CENTER] += M_SQRT1_2;
+      matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      if (unaccounted & (CHANNEL_BACK_LEFT | CHANNEL_SIDE_LEFT)) {
+        matrix[FRONT_LEFT][BACK_CENTER] -= _surround_mix_level * M_SQRT1_2;
+        matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
+      } else {
+        matrix[FRONT_LEFT][BACK_CENTER] -= _surround_mix_level;
+        matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level;
+      }
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][BACK_CENTER] +=
+        _surround_mix_level * M_SQRT1_2;
+    }
+  }
+  if (unaccounted & CHANNEL_BACK_LEFT) {
+    if (out_ch_layout & CHANNEL_BACK_CENTER) {
+      matrix[BACK_CENTER][BACK_LEFT] += M_SQRT1_2;
+      matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
+      if (in_ch_layout & CHANNEL_SIDE_LEFT) {
+        matrix[SIDE_LEFT][BACK_LEFT] += M_SQRT1_2;
+        matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
+      } else {
+        matrix[SIDE_LEFT][BACK_LEFT] += 1.0;
+        matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
+      }
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][BACK_LEFT] -= _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_LEFT][BACK_RIGHT] -= _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][BACK_LEFT] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][BACK_LEFT] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_CENTER][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2;
+    }
   }
 
-  unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
-  unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
-
-  uint32_t in_layout_mask = 0;
-  for (unsigned int i = 0 ; i < in_channels ; ++i) {
-    in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i];
+  if (unaccounted & CHANNEL_SIDE_LEFT) {
+    if (out_ch_layout & CHANNEL_BACK_LEFT) {
+      /* if back channels do not exist in the input, just copy side
+         channels to back channels, otherwise mix side into back */
+      if (in_ch_layout & CHANNEL_BACK_LEFT) {
+        matrix[BACK_LEFT][SIDE_LEFT] += M_SQRT1_2;
+        matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
+      } else {
+        matrix[BACK_LEFT][SIDE_LEFT] += 1.0;
+        matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
+      }
+    } else if (out_ch_layout & CHANNEL_BACK_CENTER) {
+      matrix[BACK_CENTER][SIDE_LEFT] += M_SQRT1_2;
+      matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][SIDE_LEFT] -= _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_LEFT][SIDE_RIGHT] -= _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2;
+      matrix[FRONT_CENTER][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2;
+    }
   }
 
-  uint32_t out_layout_mask = 0;
-  for (unsigned int i = 0 ; i < out_channels ; ++i) {
-    out_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[out_layout][i];
+  if (unaccounted & CHANNEL_FRONT_LEFT_OF_CENTER) {
+    if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0;
+      matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
+    } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += M_SQRT1_2;
+      matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
+    }
+  }
+  /* mix LFE into front left/right or center */
+  if (unaccounted & CHANNEL_LOW_FREQUENCY) {
+    if (out_ch_layout & CHANNEL_FRONT_CENTER) {
+      matrix[FRONT_CENTER][LOW_FREQUENCY] += _lfe_mix_level;
+    } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
+      matrix[FRONT_LEFT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
+      matrix[FRONT_RIGHT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
+    }
   }
 
-  // If there is no matched channel, then do nothing.
-  if (!(out_layout_mask & in_layout_mask)) {
-    return false;
+  // Normalize the conversion matrix.
+  for (uint32_t out_i = 0, i = 0; i < CHANNELS_MAX; i++) {
+    double sum = 0;
+    int in_i = 0;
+    if ((out_ch_layout & (1U << i)) == 0) {
+      continue;
+    }
+    for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
+      if ((in_ch_layout & (1U << j)) == 0) {
+        continue;
+      }
+      if (i < FF_ARRAY_ELEMS(matrix) && j < FF_ARRAY_ELEMS(matrix[0])) {
+        _matrix[out_i][in_i] = matrix[i][j];
+      } else {
+        _matrix[out_i][in_i] =
+          i == j && (in_ch_layout & out_ch_layout & (1U << i));
+      }
+      sum += fabs(_matrix[out_i][in_i]);
+      in_i++;
+    }
+    maxcoef = std::max(maxcoef, sum);
+    out_i++;
   }
 
-  for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
-    for (unsigned int j = 0; j < out_channels; ++j) {
-      cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
-      uint32_t channel_mask = 1 << channel;
-      int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
-      assert(channel_index >= -1);
-      assert(out_index + j < out_len);
-      if (in_layout_mask & channel_mask) {
-        assert(channel_index != -1);
-        assert(i + channel_index < in_len);
-        out[out_index + j] = in[i + channel_index];
-      } else {
-        assert(channel_index == -1);
-        out[out_index + j] = 0;
+  if (_format == CUBEB_SAMPLE_S16NE) {
+    maxval = 1.0;
+  } else {
+    maxval = INT_MAX;
+  }
+
+  // Normalize matrix if needed.
+  if (maxcoef > maxval) {
+    maxcoef /= maxval;
+    for (uint32_t i = 0; i < CHANNELS_MAX; i++)
+      for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
+        _matrix[i][j] /= maxcoef;
+      }
+  }
+
+  if (_format == CUBEB_SAMPLE_FLOAT32NE) {
+    for (uint32_t i = 0; i < FF_ARRAY_ELEMS(_matrix); i++) {
+      for (uint32_t j = 0; j < FF_ARRAY_ELEMS(_matrix[0]); j++) {
+        _matrix_flt[i][j] = _matrix[i][j];
       }
     }
   }
 
-  return true;
+  return 0;
 }
 
-/* Drop the extra channels beyond the provided output channels. */
-template<typename T>
-void
-downmix_fallback(long inframes,
-                 T const * const in, unsigned long in_len,
-                 T * out, unsigned long out_len,
-                 unsigned int in_channels, unsigned int out_channels)
+int MixerContext::init()
 {
-  assert(in_channels >= out_channels && inframes >= 0);
+  int r = auto_matrix();
+  if (r) {
+    return r;
+  }
+
+  // Determine if matrix operation would overflow
+  if (_format == CUBEB_SAMPLE_S16NE) {
+    int maxsum = 0;
+    for (uint32_t i = 0; i < _out_ch_count; i++) {
+      double rem = 0;
+      int sum = 0;
 
-  if (in_channels == out_channels && in == out) {
-    return;
+      for (uint32_t j = 0; j < _in_ch_count; j++) {
+        double target = _matrix[i][j] * 32768 + rem;
+        int value = lrintf(target);
+        rem += target - value;
+        sum += std::abs(value);
+      }
+      maxsum = std::max(maxsum, sum);
+    }
+    if (maxsum > 32768) {
+      _clipping = true;
+    }
   }
 
-  for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
-    for (unsigned int j = 0; j < out_channels; ++j) {
-      assert(i + j < in_len && out_index + j < out_len);
-      out[out_index + j] = in[i + j];
+  // FIXME quantize for integers
+  for (uint32_t i = 0; i < CHANNELS_MAX; i++) {
+    int ch_in = 0;
+    for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
+      _matrix32[i][j] = lrintf(_matrix[i][j] * 32768);
+      if (_matrix[i][j]) {
+        _matrix_ch[i][++ch_in] = j;
+      }
     }
+    _matrix_ch[i][0] = ch_in;
+  }
+
+  return 0;
+}
+
+template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
+void
+sum2(TYPE_SAMPLE * out,
+     uint32_t stride_out,
+     const TYPE_SAMPLE * in1,
+     const TYPE_SAMPLE * in2,
+     uint32_t stride_in,
+     TYPE_COEFF coeff1,
+     TYPE_COEFF coeff2,
+     F&& operand,
+     uint32_t frames)
+{
+  static_assert(
+    std::is_same<TYPE_COEFF,
+                 typename std::result_of<F(TYPE_COEFF)>::type>::value,
+    "function must return the same type as used by matrix_coeff");
+  for (uint32_t i = 0; i < frames; i++) {
+    *out = operand(coeff1 * *in1 + coeff2 * *in2);
+    out += stride_out;
+    in1 += stride_in;
+    in2 += stride_in;
   }
 }
 
-
-template<typename T>
+template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
 void
-cubeb_downmix(long inframes,
-              T const * const in, unsigned long in_len,
-              T * out, unsigned long out_len,
-              cubeb_stream_params const * stream_params,
-              cubeb_stream_params const * mixer_params)
+copy(TYPE_SAMPLE * out,
+     uint32_t stride_out,
+     const TYPE_SAMPLE * in,
+     uint32_t stride_in,
+     TYPE_COEFF coeff,
+     F&& operand,
+     uint32_t frames)
 {
-  assert(in && out);
-  assert(inframes);
-  assert(stream_params->channels >= mixer_params->channels &&
-         mixer_params->channels > 0);
-  assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
-
-  unsigned int in_channels = stream_params->channels;
-  cubeb_channel_layout in_layout = stream_params->layout;
-
-  unsigned int out_channels = mixer_params->channels;
-  cubeb_channel_layout out_layout = mixer_params->layout;
-
-  // If the channel number is different from the layout's setting,
-  // then we use fallback downmix mechanism.
-  if (out_channels == CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels &&
-      in_channels == CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels) {
-    if (downmix_3f2(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
-      return;
-    }
-
-#if defined(USE_AUDIOUNIT)
-  // We only support downmix for audio 5.1 on OS X currently.
-  return;
-#endif
-
-    if (mix_remap(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
-      return;
-    }
-  }
-
-  downmix_fallback(inframes, in, in_len, out, out_len, in_channels, out_channels);
-}
-
-/* Upmix function, copies a mono channel into L and R. */
-template<typename T>
-void
-mono_to_stereo(long insamples, T const * in, unsigned long in_len,
-               T * out, unsigned long out_len, unsigned int out_channels)
-{
-  for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) {
-    assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len);
-    out[j] = out[j + 1] = in[i];
+  static_assert(
+    std::is_same<TYPE_COEFF,
+                 typename std::result_of<F(TYPE_COEFF)>::type>::value,
+    "function must return the same type as used by matrix_coeff");
+  for (uint32_t i = 0; i < frames; i++) {
+    *out = operand(coeff * *in);
+    out += stride_out;
+    in += stride_in;
   }
 }
 
-template<typename T>
-void
-cubeb_upmix(long inframes,
-            T const * const in, unsigned long in_len,
-            T * out, unsigned long out_len,
-            cubeb_stream_params const * stream_params,
-            cubeb_stream_params const * mixer_params)
+template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
+static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
+                    const TYPE_COEFF (&matrix_coeff)[COLS][COLS],
+                    F&& aF, uint32_t frames)
 {
-  assert(in && out);
-  assert(inframes);
-  assert(mixer_params->channels >= stream_params->channels &&
-         stream_params->channels > 0);
+  static_assert(
+    std::is_same<TYPE_COEFF,
+                 typename std::result_of<F(TYPE_COEFF)>::type>::value,
+    "function must return the same type as used by matrix_coeff");
 
-  unsigned int in_channels = stream_params->channels;
-  unsigned int out_channels = mixer_params->channels;
+  for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
+    TYPE* out = aOut + out_i;
+    switch (s->_matrix_ch[out_i][0]) {
+      case 0:
+        for (uint32_t i = 0; i < frames; i++) {
+          out[i * s->_out_ch_count] = 0;
+        }
+        break;
+      case 1: {
+        int in_i = s->_matrix_ch[out_i][1];
+        copy(out,
+             s->_out_ch_count,
+             aIn + in_i,
+             s->_in_ch_count,
+             matrix_coeff[out_i][in_i],
+             aF,
+             frames);
+      } break;
+      case 2:
+        sum2(out,
+             s->_out_ch_count,
+             aIn + s->_matrix_ch[out_i][1],
+             aIn + s->_matrix_ch[out_i][2],
+             s->_in_ch_count,
+             matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
+             matrix_coeff[out_i][s->_matrix_ch[out_i][2]],
+             aF,
+             frames);
+        break;
+      default:
+        for (uint32_t i = 0; i < frames; i++) {
+          TYPE_COEFF v = 0;
+          for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
+            uint32_t in_i = s->_matrix_ch[out_i][1 + j];
+            v +=
+              *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
+          }
+          out[i * s->_out_ch_count] = aF(v);
+        }
+        break;
+    }
+  }
+  return 0;
+}
 
-  /* Either way, if we have 2 or more channels, the first two are L and R. */
-  /* If we are playing a mono stream over stereo speakers, copy the data over. */
-  if (in_channels == 1 && out_channels >= 2) {
-    mono_to_stereo(inframes, in, in_len, out, out_len, out_channels);
-  } else {
-    /* Copy through. */
-    for (unsigned int i = 0, o = 0; i < inframes * in_channels;
-        i += in_channels, o += out_channels) {
-      for (unsigned int j = 0; j < in_channels; ++j) {
-        assert(i + j < in_len && o + j < out_len);
-        out[o + j] = in[i + j];
+struct cubeb_mixer
+{
+  cubeb_mixer(cubeb_sample_format format,
+              uint32_t in_channels,
+              cubeb_channel_layout in_layout,
+              uint32_t out_channels,
+              cubeb_channel_layout out_layout)
+    : _context(format, in_channels, in_layout, out_channels, out_layout)
+  {
+  }
+
+  template<typename T>
+  void copy_and_trunc(size_t frames,
+                      const T * input_buffer,
+                      T * output_buffer) const
+  {
+    if (_context._in_ch_count <= _context._out_ch_count) {
+      // Not enough channels to copy, fill the gaps with silence.
+      for (uint32_t i = 0; i < frames; i++) {
+        PodCopy(output_buffer, input_buffer, _context._in_ch_count);
+        output_buffer += _context._in_ch_count;
+        input_buffer += _context._in_ch_count;
+        PodZero(output_buffer, _context._out_ch_count - _context._in_ch_count);
+        output_buffer += _context._out_ch_count - _context._in_ch_count;
+      }
+    } else {
+      for (uint32_t i = 0; i < frames; i++) {
+        PodCopy(output_buffer, input_buffer, _context._out_ch_count);
+        output_buffer += _context._out_ch_count;
+        input_buffer += _context._in_ch_count;
       }
     }
   }
 
-  /* Check if more channels. */
-  if (out_channels <= 2) {
-    return;
-  }
+  int mix(size_t frames,
+          const void * input_buffer,
+          size_t input_buffer_size,
+          void * output_buffer,
+          size_t output_buffer_size) const
+  {
+    if (frames <= 0 || _context._out_ch_count == 0) {
+      return 0;
+    }
 
-  /* Put silence in remaining channels. */
-  for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
-    for (unsigned int j = in_channels; j < out_channels; ++j) {
-      assert((unsigned long)o + j < out_len);
-      out[o + j] = 0.0;
+    // Check if output buffer is of sufficient size.
+    size_t size_read_needed =
+      frames * _context._in_ch_count * cubeb_sample_size(_context._format);
+    if (input_buffer_size < size_read_needed) {
+      // We don't have enough data to read!
+      return -1;
+    }
+    if (output_buffer_size * _context._in_ch_count <
+        size_read_needed * _context._out_ch_count) {
+      return -1;
+    }
+
+    if (!valid()) {
+      // The channel layouts were invalid or unsupported, instead we will simply
+      // either drop the extra channels, or fill with silence the missing ones
+      if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
+        copy_and_trunc(frames,
+                       static_cast<const float*>(input_buffer),
+                       static_cast<float*>(output_buffer));
+      } else {
+        assert(_context._format == CUBEB_SAMPLE_S16NE);
+        copy_and_trunc(frames,
+                       static_cast<const int16_t*>(input_buffer),
+                       reinterpret_cast<int16_t*>(output_buffer));
+      }
+      return 0;
     }
-  }
-}
 
-bool
-cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
-{
-  return mixer->channels > stream->channels;
-}
+    switch (_context._format)
+    {
+      case CUBEB_SAMPLE_FLOAT32NE: {
+        auto f = [](float x) { return x; };
+        return rematrix(&_context,
+                        static_cast<float*>(output_buffer),
+                        static_cast<const float*>(input_buffer),
+                        _context._matrix_flt,
+                        f,
+                        frames);
+      }
+      case CUBEB_SAMPLE_S16NE:
+        if (_context._clipping) {
+          auto f = [](int x) {
+            int y = (x + 16384) >> 15;
+            // clip the signed integer value into the -32768,32767 range.
+            if ((y + 0x8000U) & ~0xFFFF) {
+              return (y >> 31) ^ 0x7FFF;
+            }
+            return y;
+          };
+          return rematrix(&_context,
+                          static_cast<int16_t*>(output_buffer),
+                          static_cast<const int16_t*>(input_buffer),
+                          _context._matrix32,
+                          f,
+                          frames);
+        } else {
+          auto f = [](int x) { return (x + 16384) >> 15; };
+          return rematrix(&_context,
+                          static_cast<int16_t*>(output_buffer),
+                          static_cast<const int16_t*>(input_buffer),
+                          _context._matrix32,
+                          f,
+                          frames);
+        }
+        break;
+      default:
+        assert(false);
+        break;
+    }
 
-bool
-cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
-{
-  if (mixer->channels > stream->channels || mixer->layout == stream->layout) {
-    return false;
+    return -1;
   }
 
-  return mixer->channels < stream->channels ||
-         // When mixer.channels == stream.channels
-         mixer->layout == CUBEB_LAYOUT_UNDEFINED ||  // fallback downmix
-         (stream->layout == CUBEB_LAYOUT_3F2 &&      // 3f2 downmix
-          (mixer->layout == CUBEB_LAYOUT_2F2_LFE ||
-           mixer->layout == CUBEB_LAYOUT_3F1_LFE));
-}
+  // Return false if any of the input or ouput layout were invalid.
+  bool valid() const { return _context._valid; }
 
-bool
-cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
-{
-  return stream->layout != CUBEB_LAYOUT_UNDEFINED &&
-         (cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer));
-}
+  virtual ~cubeb_mixer(){};
 
-struct cubeb_mixer {
-  virtual void mix(long frames,
-                   void * input_buffer, unsigned long input_buffer_length,
-                   void * output_buffer, unsigned long output_buffer_length,
-                   cubeb_stream_params const * stream_params,
-                   cubeb_stream_params const * mixer_params) = 0;
-  virtual ~cubeb_mixer() {};
+  MixerContext _context;
 };
 
-template<typename T>
-struct cubeb_mixer_impl : public cubeb_mixer {
-  explicit cubeb_mixer_impl(unsigned int d)
-    : direction(d)
-  {
-  }
-
-  void mix(long frames,
-           void * input_buffer, unsigned long input_buffer_length,
-           void * output_buffer, unsigned long output_buffer_length,
-           cubeb_stream_params const * stream_params,
-           cubeb_stream_params const * mixer_params)
-  {
-    if (frames <= 0) {
-      return;
-    }
-
-    T * in = static_cast<T*>(input_buffer);
-    T * out = static_cast<T*>(output_buffer);
-
-    if ((direction & CUBEB_MIXER_DIRECTION_DOWNMIX) &&
-        cubeb_should_downmix(stream_params, mixer_params)) {
-      cubeb_downmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
-    } else if ((direction & CUBEB_MIXER_DIRECTION_UPMIX) &&
-               cubeb_should_upmix(stream_params, mixer_params)) {
-      cubeb_upmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
-    }
-  }
-
-  ~cubeb_mixer_impl() {};
-
-  unsigned char const direction;
-};
-
-cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
-                                 unsigned char direction)
+cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format,
+                                uint32_t in_channels,
+                                cubeb_channel_layout in_layout,
+                                uint32_t out_channels,
+                                cubeb_channel_layout out_layout)
 {
-  assert(direction & CUBEB_MIXER_DIRECTION_DOWNMIX ||
-         direction & CUBEB_MIXER_DIRECTION_UPMIX);
-  switch(format) {
-    case CUBEB_SAMPLE_S16NE:
-      return new cubeb_mixer_impl<short>(direction);
-    case CUBEB_SAMPLE_FLOAT32NE:
-      return new cubeb_mixer_impl<float>(direction);
-    default:
-      assert(false);
-      return nullptr;
-  }
+  return new cubeb_mixer(
+    format, in_channels, in_layout, out_channels, out_layout);
 }
 
 void cubeb_mixer_destroy(cubeb_mixer * mixer)
 {
   delete mixer;
 }
 
-void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
-                     void * input_buffer, unsigned long input_buffer_length,
-                     void * output_buffer, unsigned long output_buffer_length,
-                     cubeb_stream_params const * stream_params,
-                     cubeb_stream_params const * mixer_params)
+int cubeb_mixer_mix(cubeb_mixer * mixer,
+                    size_t frames,
+                    const void * input_buffer,
+                    size_t input_buffer_size,
+                    void * output_buffer,
+                    size_t output_buffer_size)
 {
-  assert(mixer);
-  mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, output_buffer_length,
-             stream_params, mixer_params);
+  return mixer->mix(
+    frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size);
 }
--- a/media/libcubeb/src/cubeb_mixer.h
+++ b/media/libcubeb/src/cubeb_mixer.h
@@ -3,88 +3,35 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 
 #ifndef CUBEB_MIXER
 #define CUBEB_MIXER
 
-#include "cubeb/cubeb.h" // for cubeb_channel_layout ,CUBEB_CHANNEL_LAYOUT_MAPS and cubeb_stream_params.
-#include <stdbool.h>
+#include "cubeb/cubeb.h" // for cubeb_channel_layout and cubeb_stream_params.
 
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
-typedef enum {
-  CHANNEL_INVALID = -1,
-  CHANNEL_MONO = 0,
-  CHANNEL_LEFT,
-  CHANNEL_RIGHT,
-  CHANNEL_CENTER,
-  CHANNEL_LS,
-  CHANNEL_RS,
-  CHANNEL_RLS,
-  CHANNEL_RCENTER,
-  CHANNEL_RRS,
-  CHANNEL_LFE,
-  CHANNEL_UNMAPPED,
-  CHANNEL_MAX = 256 // Max number of supported channels.
-} cubeb_channel;
-
-static cubeb_channel const CHANNEL_INDEX_TO_ORDER[CUBEB_LAYOUT_MAX][CHANNEL_MAX] = {
-  { CHANNEL_INVALID },                                                                                            // UNDEFINED
-  { CHANNEL_LEFT, CHANNEL_RIGHT },                                                                                // DUAL_MONO
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE },                                                                   // DUAL_MONO_LFE
-  { CHANNEL_MONO },                                                                                               // MONO
-  { CHANNEL_MONO, CHANNEL_LFE },                                                                                  // MONO_LFE
-  { CHANNEL_LEFT, CHANNEL_RIGHT },                                                                                // STEREO
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE },                                                                   // STEREO_LFE
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER },                                                                // 3F
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE },                                                   // 3F_LFE
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_RCENTER },                                                               // 2F1
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_RCENTER },                                                  // 2F1_LFE
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_RCENTER },                                               // 3F1
-  { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RCENTER },                                  // 3F1_LFE
-  { 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
-  // When more channels are present, the stream is considered unmapped to a
-  // particular speaker set.
-};
-
-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);
-
-bool cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer);
-
-typedef enum {
-  CUBEB_MIXER_DIRECTION_DOWNMIX = 0x01,
-  CUBEB_MIXER_DIRECTION_UPMIX   = 0x02,
-} cubeb_mixer_direction;
-
 typedef struct cubeb_mixer cubeb_mixer;
 cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
-                                 unsigned char direction);
+                                 uint32_t in_channels,
+                                 cubeb_channel_layout in_layout,
+                                 uint32_t out_channels,
+                                 cubeb_channel_layout out_layout);
 void cubeb_mixer_destroy(cubeb_mixer * mixer);
-void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
-                     void * input_buffer, unsigned long input_buffer_length,
-                     void * output_buffer, unsigned long output_buffer_length,
-                     cubeb_stream_params const * stream_params,
-                     cubeb_stream_params const * mixer_params);
+int cubeb_mixer_mix(cubeb_mixer * mixer,
+                    size_t frames,
+                    const void * input_buffer,
+                    size_t input_buffer_size,
+                    void * output_buffer,
+                    size_t output_buffer_size);
+
+unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
 
 #if defined(__cplusplus)
 }
 #endif
 
 #endif // CUBEB_MIXER
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -1529,17 +1529,16 @@ 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 = NULL,
   .get_preferred_sample_rate = NULL,
-  .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_reset_default_device = NULL,
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -2,24 +2,24 @@
  * Copyright © 2011 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
 #include <assert.h>
 #include <dlfcn.h>
+#include <pulse/pulseaudio.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <pulse/pulseaudio.h>
 #include <string.h>
+#include "cubeb-internal.h"
 #include "cubeb/cubeb.h"
-#include "cubeb-internal.h"
 #include "cubeb_mixer.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)                 \
   X(pa_channel_map_init)                        \
@@ -506,102 +506,102 @@ stream_update_timing_info(cubeb_stream *
   }
 
   return r;
 }
 
 static pa_channel_position_t
 cubeb_channel_to_pa_channel(cubeb_channel channel)
 {
-  assert(channel != CHANNEL_INVALID);
-
-  // 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 pa_channel_position_t map[CHANNEL_MAX] = {
-    // PA_CHANNEL_POSITION_INVALID,      // CHANNEL_INVALID
-    PA_CHANNEL_POSITION_MONO,         // CHANNEL_MONO
-    PA_CHANNEL_POSITION_FRONT_LEFT,   // CHANNEL_LEFT
-    PA_CHANNEL_POSITION_FRONT_RIGHT,  // CHANNEL_RIGHT
-    PA_CHANNEL_POSITION_FRONT_CENTER, // CHANNEL_CENTER
-    PA_CHANNEL_POSITION_SIDE_LEFT,    // CHANNEL_LS
-    PA_CHANNEL_POSITION_SIDE_RIGHT,   // CHANNEL_RS
-    PA_CHANNEL_POSITION_REAR_LEFT,    // CHANNEL_RLS
-    PA_CHANNEL_POSITION_REAR_CENTER,  // CHANNEL_RCENTER
-    PA_CHANNEL_POSITION_REAR_RIGHT,   // CHANNEL_RRS
-    PA_CHANNEL_POSITION_LFE           // CHANNEL_LFE
-  };
-
-  return map[channel];
-}
-
-static cubeb_channel
-pa_channel_to_cubeb_channel(pa_channel_position_t channel)
-{
-  assert(channel != PA_CHANNEL_POSITION_INVALID);
-  switch(channel) {
-    case PA_CHANNEL_POSITION_MONO: return CHANNEL_MONO;
-    case PA_CHANNEL_POSITION_FRONT_LEFT: return CHANNEL_LEFT;
-    case PA_CHANNEL_POSITION_FRONT_RIGHT: return CHANNEL_RIGHT;
-    case PA_CHANNEL_POSITION_FRONT_CENTER: return CHANNEL_CENTER;
-    case PA_CHANNEL_POSITION_SIDE_LEFT: return CHANNEL_LS;
-    case PA_CHANNEL_POSITION_SIDE_RIGHT: return CHANNEL_RS;
-    case PA_CHANNEL_POSITION_REAR_LEFT: return CHANNEL_RLS;
-    case PA_CHANNEL_POSITION_REAR_CENTER: return CHANNEL_RCENTER;
-    case PA_CHANNEL_POSITION_REAR_RIGHT: return CHANNEL_RRS;
-    case PA_CHANNEL_POSITION_LFE: return CHANNEL_LFE;
-    default: return CHANNEL_INVALID;
+  switch (channel) {
+    case CHANNEL_FRONT_LEFT:
+      return PA_CHANNEL_POSITION_FRONT_LEFT;
+    case CHANNEL_FRONT_RIGHT:
+      return PA_CHANNEL_POSITION_FRONT_RIGHT;
+    case CHANNEL_FRONT_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_CENTER;
+    case CHANNEL_LOW_FREQUENCY:
+      return PA_CHANNEL_POSITION_LFE;
+    case CHANNEL_BACK_LEFT:
+      return PA_CHANNEL_POSITION_REAR_LEFT;
+    case CHANNEL_BACK_RIGHT:
+      return PA_CHANNEL_POSITION_REAR_RIGHT;
+    case CHANNEL_FRONT_LEFT_OF_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+    case CHANNEL_FRONT_RIGHT_OF_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+    case CHANNEL_BACK_CENTER:
+      return PA_CHANNEL_POSITION_REAR_CENTER;
+    case CHANNEL_SIDE_LEFT:
+      return PA_CHANNEL_POSITION_SIDE_LEFT;
+    case CHANNEL_SIDE_RIGHT:
+      return PA_CHANNEL_POSITION_SIDE_RIGHT;
+    case CHANNEL_TOP_CENTER:
+      return PA_CHANNEL_POSITION_TOP_CENTER;
+    case CHANNEL_TOP_FRONT_LEFT:
+      return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+    case CHANNEL_TOP_FRONT_CENTER:
+      return PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
+    case CHANNEL_TOP_FRONT_RIGHT:
+      return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+    case CHANNEL_TOP_BACK_LEFT:
+      return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+    case CHANNEL_TOP_BACK_CENTER:
+      return PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+    case CHANNEL_TOP_BACK_RIGHT:
+      return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+    default:
+      return PA_CHANNEL_POSITION_INVALID;
   }
 }
 
 static void
 layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm)
 {
   assert(cm && layout != CUBEB_LAYOUT_UNDEFINED);
 
   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]);
+
+  uint32_t channels = 0;
+  cubeb_channel_layout channelMap = layout;
+  for (uint32_t i = 0 ; channelMap != 0; ++i) {
+    uint32_t channel = (channelMap & 1) << i;
+    if (channel != 0) {
+      cm->map[channels] = cubeb_channel_to_pa_channel(channel);
+      channels++;
+    }
+    channelMap = channelMap >> 1;
   }
-}
-
-static cubeb_channel_layout
-channel_map_to_layout(pa_channel_map * cm)
-{
-  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]);
-  }
-  return cubeb_channel_map_to_layout(&cubeb_map);
+  cm->channels = cubeb_channel_layout_nb_channels(layout);
 }
 
 static void pulse_context_destroy(cubeb * ctx);
 static void pulse_destroy(cubeb * ctx);
 
 static int
 pulse_context_init(cubeb * ctx)
 {
+  int r;
+
   if (ctx->context) {
     assert(ctx->error == 1);
     pulse_context_destroy(ctx);
   }
 
   ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop),
                                       ctx->context_name);
   if (!ctx->context) {
     return -1;
   }
   WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx);
 
   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
-  WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
+  r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
 
-  if (wait_until_context_ready(ctx) != 0) {
+  if (r < 0 || wait_until_context_ready(ctx) != 0) {
     WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
     pulse_context_destroy(ctx);
     ctx->context = NULL;
     return -1;
   }
 
   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
@@ -710,30 +710,16 @@ pulse_get_preferred_sample_rate(cubeb * 
     return CUBEB_ERROR;
 
   *rate = ctx->default_sink_info->sample_spec_rate;
 
   return CUBEB_OK;
 }
 
 static int
-pulse_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout)
-{
-  assert(ctx && layout);
-  (void)ctx;
-
-  if (!ctx->default_sink_info)
-    return CUBEB_ERROR;
-
-  *layout = channel_map_to_layout(&ctx->default_sink_info->channel_map);
-
-  return CUBEB_OK;
-}
-
-static int
 pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   (void)ctx;
   // According to PulseAudio developers, this is a safe minimum.
   *latency_frames = 25 * params.rate / 1000;
 
   return CUBEB_OK;
 }
@@ -803,17 +789,17 @@ 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))));
+         cubeb_channel_layout_nb_channels(stream_params->layout) == stream_params->channels))));
   if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
   *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;
@@ -1274,17 +1260,17 @@ pulse_sink_info_cb(pa_context * context,
     return;
   }
 
   if (info == NULL)
     return;
 
   device_id = info->name;
   if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
-    assert(false);
+    assert(NULL);
     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 = device_id;
@@ -1343,17 +1329,17 @@ pulse_source_info_cb(pa_context * contex
 
   if (eol) {
     WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
     return;
   }
 
   device_id = info->name;
   if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
-    assert(false);
+    assert(NULL);
     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 = device_id;
@@ -1583,17 +1569,16 @@ 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 = 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,
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -531,17 +531,16 @@ sndio_device_collection_destroy(cubeb * 
 }
 
 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 = sndio_enumerate_devices,
   .device_collection_destroy = sndio_device_collection_destroy,
   .destroy = sndio_destroy,
   .stream_init = sndio_stream_init,
   .stream_destroy = sndio_stream_destroy,
   .stream_start = sndio_stream_start,
   .stream_stop = sndio_stream_stop,
   .stream_reset_default_device = NULL,
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_utils.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2018 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#include "cubeb_utils.h"
+
+size_t cubeb_sample_size(cubeb_sample_format format)
+{
+  switch (format) {
+    case CUBEB_SAMPLE_S16LE:
+    case CUBEB_SAMPLE_S16BE:
+      return sizeof(int16_t);
+    case CUBEB_SAMPLE_FLOAT32LE:
+    case CUBEB_SAMPLE_FLOAT32BE:
+      return sizeof(float);
+    default:
+      // should never happen as all cases are handled above.
+      assert(false);
+  }
+}
--- a/media/libcubeb/src/cubeb_utils.h
+++ b/media/libcubeb/src/cubeb_utils.h
@@ -328,12 +328,16 @@ struct auto_array_wrapper_impl : public 
   ~auto_array_wrapper_impl() {
     ar.clear();
   }
 
 private:
   auto_array<T> ar;
 };
 
+extern "C" {
+  size_t cubeb_sample_size(cubeb_sample_format format);
+}
+
 using auto_lock = std::lock_guard<owned_critical_section>;
 #endif // __cplusplus
 
 #endif /* CUBEB_UTILS */
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -130,42 +130,16 @@ private:
       ptr = nullptr;
       temp->Release();
     }
   }
 
   T * ptr = nullptr;
 };
 
-struct auto_com {
-  auto_com() {
-    result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
-  }
-  ~auto_com() {
-    if (result == RPC_E_CHANGED_MODE) {
-      // This is not an error, COM was not initialized by this function, so it is
-      // not necessary to uninit it.
-      LOG("COM was already initialized in STA.");
-    } else if (result == S_FALSE) {
-      // This is not an error. We are allowed to call CoInitializeEx more than
-      // once, as long as it is matches by an CoUninitialize call.
-      // We do that in the dtor which is guaranteed to be called.
-      LOG("COM was already initialized in MTA");
-    }
-    if (SUCCEEDED(result)) {
-      CoUninitialize();
-    }
-  }
-  bool ok() {
-    return result == RPC_E_CHANGED_MODE || SUCCEEDED(result);
-  }
-private:
-  HRESULT result;
-};
-
 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);
@@ -268,19 +242,20 @@ struct cubeb_stream {
      client, frames_written, mix_params, total_frames_written, prev_position. */
   owned_critical_section stream_reset_lock;
   /* Maximum number of frames that can be passed down in a callback. */
   uint32_t input_buffer_frame_count = 0;
   /* Maximum number of frames that can be requested in a callback. */
   uint32_t output_buffer_frame_count = 0;
   /* Resampler instance. Resampling will only happen if necessary. */
   std::unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler = { nullptr, cubeb_resampler_destroy };
-  /* Mixer interface */
-  std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer = { nullptr, cubeb_mixer_destroy };
-  /* A buffer for up/down mixing multi-channel audio. */
+  /* Mixer interfaces */
+  std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> output_mixer = { nullptr, cubeb_mixer_destroy };
+  std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> input_mixer = { nullptr, cubeb_mixer_destroy };
+  /* A buffer for up/down mixing multi-channel audio output. */
   std::vector<BYTE> mix_buffer;
   /* WASAPI input works in "packets". We re-linearize the audio packets
    * into this buffer before handing it to the resampler. */
   std::unique_ptr<auto_array_wrapper> linear_input_buffer;
   /* Bytes per sample. This multiplied by the number of channels is the number
    * of bytes per frame. */
   size_t bytes_per_sample = 0;
   /* WAVEFORMATEXTENSIBLE sub-format: either PCM or float. */
@@ -420,116 +395,34 @@ bool has_output(cubeb_stream * stm)
 
 double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream_params & mixer)
 {
   return double(stream.rate) / mixer.rate;
 }
 
 /* Convert the channel layout into the corresponding KSAUDIO_CHANNEL_CONFIG.
    See more: https://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx */
-#define MASK_DUAL_MONO      (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
-#define MASK_DUAL_MONO_LFE  (MASK_DUAL_MONO | SPEAKER_LOW_FREQUENCY)
-#define MASK_MONO           (KSAUDIO_SPEAKER_MONO)
-#define MASK_MONO_LFE       (MASK_MONO | SPEAKER_LOW_FREQUENCY)
-#define MASK_STEREO         (KSAUDIO_SPEAKER_STEREO)
-#define MASK_STEREO_LFE     (MASK_STEREO | SPEAKER_LOW_FREQUENCY)
-#define MASK_3F             (MASK_STEREO | SPEAKER_FRONT_CENTER)
-#define MASK_3F_LFE         (MASK_3F | SPEAKER_LOW_FREQUENCY)
-#define MASK_2F1            (MASK_STEREO | SPEAKER_BACK_CENTER)
-#define MASK_2F1_LFE        (MASK_2F1 | SPEAKER_LOW_FREQUENCY)
-#define MASK_3F1            (KSAUDIO_SPEAKER_SURROUND)
-#define MASK_3F1_LFE        (MASK_3F1 | SPEAKER_LOW_FREQUENCY)
-#define MASK_2F2            (MASK_STEREO | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
-#define MASK_2F2_LFE        (MASK_2F2 | SPEAKER_LOW_FREQUENCY)
-#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_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] = {
-    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;
+  cubeb_channel_layout mask = 0;
 
   if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
     WAVEFORMATEXTENSIBLE const * ext = reinterpret_cast<WAVEFORMATEXTENSIBLE const *>(fmt);
     mask = ext->dwChannelMask;
   } else if (fmt->wFormatTag == WAVE_FORMAT_PCM ||
              fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
     if (fmt->nChannels == 1) {
-      mask = MASK_MONO;
+      mask = CHANNEL_FRONT_CENTER;
     } else if (fmt->nChannels == 2) {
-      mask = MASK_STEREO;
+      mask = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT;
     }
   }
-
-  switch (mask) {
-    // MASK_DUAL_MONO(_LFE) is same as STEREO(_LFE), so we skip it.
-    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;
-    // Special case similar to MASK_2F2 but with rear left and right
-    // speakers instead of side left and right. This mapping is a band-aid as
-    // cubeb does not current have an enum to differentiate this and MASK_2F2,
-    // but is preferred to returning an undefined layout.
-    // See: https://github.com/kinetiknz/cubeb/issues/178 and https://bugzilla.mozilla.org/show_bug.cgi?id=1325023
-    case KSAUDIO_SPEAKER_QUAD: 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;
-    // Special case similar to MASK_3F2_LFE but with rear left and right
-    // speakers instead of side left and right. his mapping is a band-aid as
-    // cubeb does not current have an enum to differentiate this and MASK_3F2_LFE,
-    // but is preferred to returning an undefined layout.
-    // See: https://github.com/kinetiknz/cubeb/issues/178 and https://bugzilla.mozilla.org/show_bug.cgi?id=1325023
-    case KSAUDIO_SPEAKER_5POINT1: 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 mask;
 }
 
 uint32_t
 get_rate(cubeb_stream * stm)
 {
   return has_input(stm) ? stm->input_stream_params.rate
                         : stm->output_stream_params.rate;
 }
@@ -569,17 +462,17 @@ long
 refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
        void * output_buffer, long output_frames_needed)
 {
   XASSERT(!stm->draining);
   /* If we need to upmix after resampling, resample into the mix buffer to
      avoid a copy. Avoid exposing output if it is a dummy stream. */
   void * dest = nullptr;
   if (has_output(stm) && !stm->has_dummy_output) {
-    if (cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
+    if (stm->output_mixer) {
       dest = stm->mix_buffer.data();
     } else {
       dest = output_buffer;
     }
   }
 
   long out_frames = cubeb_resampler_fill(stm->resampler.get(),
                                          input_buffer,
@@ -589,35 +482,46 @@ refill(cubeb_stream * stm, void * input_
   /* TODO: Report out_frames < 0 as an error via the API. */
   XASSERT(out_frames >= 0);
 
   {
     auto_lock lock(stm->stream_reset_lock);
     stm->frames_written += out_frames;
   }
 
-  /* Go in draining mode if we got fewer frames than requested. */
-  if (out_frames < output_frames_needed) {
+  /* Go in draining mode if we got fewer frames than requested. If the stream
+     has no output we still expect the callback to return number of frames read
+     from input, otherwise we stop. */
+  if ((out_frames < output_frames_needed) ||
+      (!has_output(stm) && out_frames < input_frames_count)) {
     LOG("start draining.");
     stm->draining = true;
   }
 
   /* If this is not true, there will be glitches.
      It is alright to have produced less frames if we are draining, though. */
   XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm) || stm->has_dummy_output);
 
   // We don't bother mixing dummy output as it will be silenced, otherwise mix output if needed
-  if (!stm->has_dummy_output && has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
+  if (!stm->has_dummy_output && has_output(stm) && stm->output_mixer) {
     XASSERT(dest == stm->mix_buffer.data());
-    unsigned long dest_len = out_frames * stm->output_stream_params.channels;
-    XASSERT(dest_len <= stm->mix_buffer.size() / stm->bytes_per_sample);
-    unsigned long output_buffer_len = out_frames * stm->output_mix_params.channels;
-    cubeb_mixer_mix(stm->mixer.get(), out_frames,
-                    dest, dest_len, output_buffer, output_buffer_len,
-                    &stm->output_stream_params, &stm->output_mix_params);
+    size_t dest_size =
+      out_frames * stm->output_stream_params.channels * stm->bytes_per_sample;
+    XASSERT(dest_size <= stm->mix_buffer.size());
+    size_t output_buffer_size =
+      out_frames * stm->output_mix_params.channels * stm->bytes_per_sample;
+    int ret = cubeb_mixer_mix(stm->output_mixer.get(),
+                              out_frames,
+                              dest,
+                              dest_size,
+                              output_buffer,
+                              output_buffer_size);
+    if (ret < 0) {
+      LOG("Error remixing content (%d)", ret);
+    }
   }
 
   return out_frames;
 }
 
 int wasapi_stream_reset_default_device(cubeb_stream * stm);
 
 /* This helper grabs all the frames available from a capture client, put them in
@@ -650,69 +554,77 @@ bool get_input_buffer(cubeb_stream * stm
       return true;
     }
 
     if (FAILED(hr)) {
       LOG("cannot get next packet size: %lx", hr);
       return false;
     }
 
-    UINT32 packet_size;
+    UINT32 frames;
     hr = stm->capture_client->GetBuffer(&input_packet,
-                                        &packet_size,
+                                        &frames,
                                         &flags,
                                         &dev_pos,
                                         NULL);
     if (FAILED(hr)) {
       LOG("GetBuffer failed for capture: %lx", hr);
       return false;
     }
-    XASSERT(packet_size == next);
+    XASSERT(frames == next);
+
+    UINT32 input_stream_samples = frames * stm->input_stream_params.channels;
     // We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
     // flag. There a two primary (non exhaustive) scenarios we anticipate this
     // flag being set in:
     //   - The first GetBuffer after Start has this flag undefined. In this
     //     case the flag may be set but is meaningless and can be ignored.
     //   - If a glitch is introduced into the input. This should not happen
     //     for event based inputs, and should be mitigated by using a dummy
     //     stream to drive input in the case of input only loopback. Without
     //     a dummy output, input only loopback would glitch on silence. However,
     //     the dummy input should push silence to the loopback and prevent
     //     discontinuities. See https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
     // As the first scenario can be ignored, and we anticipate the second
     // scenario is mitigated, we ignore the flag.
     // For more info: https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx,
     // https://msdn.microsoft.com/en-us/library/windows/desktop/dd371458(v=vs.85).aspx
     if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
-      LOG("insert silence: ps=%u", packet_size);
-      stm->linear_input_buffer->push_silence(packet_size * stm->input_stream_params.channels);
+      LOG("insert silence: ps=%u", frames);
+      stm->linear_input_buffer->push_silence(input_stream_samples);
     } else {
-      if (cubeb_should_mix(&stm->input_mix_params, &stm->input_stream_params)) {
-        bool ok = stm->linear_input_buffer->reserve(stm->linear_input_buffer->length() +
-                                                   packet_size * stm->input_stream_params.channels);
+      if (stm->input_mixer) {
+        bool ok = stm->linear_input_buffer->reserve(
+          stm->linear_input_buffer->length() + input_stream_samples);
         XASSERT(ok);
-        unsigned long input_packet_length = packet_size * stm->input_mix_params.channels;
-        unsigned long linear_input_buffer_length = packet_size * stm->input_stream_params.channels;
-        cubeb_mixer_mix(stm->mixer.get(), packet_size,
-                        input_packet, input_packet_length,
-                        stm->linear_input_buffer->end(), linear_input_buffer_length,
-                        &stm->input_mix_params,
-                        &stm->input_stream_params);
-        stm->linear_input_buffer->set_length(stm->linear_input_buffer->length() + linear_input_buffer_length);
+        size_t input_packet_size =
+          frames * stm->input_mix_params.channels *
+          cubeb_sample_size(stm->input_mix_params.format);
+        size_t linear_input_buffer_size =
+          input_stream_samples *
+          cubeb_sample_size(stm->input_stream_params.format);
+        cubeb_mixer_mix(stm->input_mixer.get(),
+                        frames,
+                        input_packet,
+                        input_packet_size,
+                        stm->linear_input_buffer->end(),
+                        linear_input_buffer_size);
+        stm->linear_input_buffer->set_length(
+          stm->linear_input_buffer->length() + input_stream_samples);
       } else {
-        stm->linear_input_buffer->push(input_packet,
-                                      packet_size * stm->input_stream_params.channels);
+        stm->linear_input_buffer->push(
+          input_packet, input_stream_samples);
       }
     }
-    hr = stm->capture_client->ReleaseBuffer(packet_size);
+    hr = stm->capture_client->ReleaseBuffer(frames);
     if (FAILED(hr)) {
       LOG("FAILED to release intput buffer");
       return false;
     }
-    offset += packet_size;
+    offset += input_stream_samples;
   }
 
   XASSERT(stm->linear_input_buffer->length() >= offset);
 
   return true;
 }
 
 /* Get an output buffer from the render_client. It has to be released before
@@ -899,25 +811,25 @@ refill_callback_output(cubeb_stream * st
                     0,
                     output_buffer,
                     output_frames);
 
   ALOGV("Output callback: output frames requested: %Iu, got %ld",
         output_frames, got);
 
   XASSERT(got >= 0);
-  XASSERT((unsigned long) got == output_frames || stm->draining);
+  XASSERT(size_t(got) == output_frames || stm->draining);
 
   hr = stm->render_client->ReleaseBuffer(got, 0);
   if (FAILED(hr)) {
     LOG("failed to release buffer: %lx", hr);
     return false;
   }
 
-  return (unsigned long) got == output_frames || stm->draining;
+  return size_t(got) == output_frames || stm->draining;
 }
 
 static unsigned int __stdcall
 wasapi_stream_render_loop(LPVOID stream)
 {
   cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
   std::atomic<bool> * emergency_bailout = stm->emergency_bailout;
 
@@ -926,22 +838,25 @@ wasapi_stream_render_loop(LPVOID stream)
     stm->shutdown_event,
     stm->reconfigure_event,
     stm->refill_event,
     stm->input_available_event
   };
   HANDLE mmcss_handle = NULL;
   HRESULT hr = 0;
   DWORD mmcss_task_index = 0;
-  auto_com com;
-  if (!com.ok()) {
-    LOG("COM initialization failed on render_loop thread.");
-    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
-    return 0;
-  }
+  struct auto_com {
+    auto_com() {
+      HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+      XASSERT(SUCCEEDED(hr));
+    }
+    ~auto_com() {
+      CoUninitialize();
+    }
+  } com;
 
   /* We could consider using "Pro Audio" here for WebAudio and
      maybe WebRTC. */
   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());
   }
@@ -1074,16 +989,17 @@ void wasapi_destroy(cubeb * context);
 
 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);
+    XASSERT(hr != CO_E_NOTINITIALIZED);
     return hr;
   }
 
   stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event));
 
   hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client.get());
   if (FAILED(hr)) {
     LOG("Could not register endpoint notification callback: %lx", hr);
@@ -1120,16 +1036,17 @@ HRESULT unregister_notification_client(c
 HRESULT get_endpoint(com_ptr<IMMDevice> & device, LPCWSTR devid)
 {
   com_ptr<IMMDeviceEnumerator> enumerator;
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
+    XASSERT(hr != CO_E_NOTINITIALIZED);
     return hr;
   }
 
   hr = enumerator->GetDevice(devid, device.receive());
   if (FAILED(hr)) {
     LOG("Could not get device: %lx", hr);
     return hr;
   }
@@ -1140,16 +1057,17 @@ HRESULT get_endpoint(com_ptr<IMMDevice> 
 HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction)
 {
   com_ptr<IMMDeviceEnumerator> enumerator;
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
+    XASSERT(hr != CO_E_NOTINITIALIZED);
     return hr;
   }
   hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device.receive());
   if (FAILED(hr)) {
     LOG("Could not get default audio endpoint: %lx", hr);
     return hr;
   }
 
@@ -1224,27 +1142,21 @@ stream_set_volume(cubeb_stream * stm, fl
 
   return CUBEB_OK;
 }
 } // namespace anonymous
 
 extern "C" {
 int wasapi_init(cubeb ** context, char const * context_name)
 {
-  HRESULT hr;
-  auto_com com;
-  if (!com.ok()) {
-    return CUBEB_ERROR;
-  }
-
   /* We don't use the device yet, but need to make sure we can initialize one
      so that this backend is not incorrectly enabled on platforms that don't
      support WASAPI. */
   com_ptr<IMMDevice> device;
-  hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender);
   if (FAILED(hr)) {
     LOG("Could not get device: %lx", hr);
     return CUBEB_ERROR;
   }
 
   cubeb * ctx = new cubeb();
 
   ctx->ops = &wasapi_ops;
@@ -1329,26 +1241,20 @@ void wasapi_destroy(cubeb * context)
 char const * wasapi_get_backend_id(cubeb * context)
 {
   return "wasapi";
 }
 
 int
 wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
 {
-  HRESULT hr;
-  auto_com com;
-  if (!com.ok()) {
-    return CUBEB_ERROR;
-  }
-
   XASSERT(ctx && max_channels);
 
   com_ptr<IMMDevice> device;
-  hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
@@ -1366,44 +1272,38 @@ wasapi_get_max_channel_count(cubeb * ctx
   *max_channels = mix_format->nChannels;
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
-  HRESULT hr;
-  REFERENCE_TIME default_period;
-  auto_com com;
-  if (!com.ok()) {
-    return CUBEB_ERROR;
-  }
-
   if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) {
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   com_ptr<IMMDevice> device;
-  hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender);
   if (FAILED(hr)) {
     LOG("Could not get default endpoint: %lx", hr);
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
   if (FAILED(hr)) {
     LOG("Could not activate device for latency: %lx", hr);
     return CUBEB_ERROR;
   }
 
   /* The second parameter is for exclusive mode, that we don't use. */
+  REFERENCE_TIME default_period;
   hr = client->GetDevicePeriod(&default_period, NULL);
   if (FAILED(hr)) {
     LOG("Could not get device period: %lx", hr);
     return CUBEB_ERROR;
   }
 
   LOG("default device period: %I64d", default_period);
 
@@ -1416,24 +1316,18 @@ wasapi_get_min_latency(cubeb * ctx, cube
   LOG("Minimum latency in frames: %u", *latency_frames);
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
-  HRESULT hr;
-  auto_com com;
-  if (!com.ok()) {
-    return CUBEB_ERROR;
-  }
-
   com_ptr<IMMDevice> device;
-  hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
@@ -1450,53 +1344,16 @@ wasapi_get_preferred_sample_rate(cubeb *
 
   *rate = mix_format->nSamplesPerSec;
 
   LOG("Preferred sample rate for output: %u", *rate);
 
   return CUBEB_OK;
 }
 
-int
-wasapi_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layout)
-{
-  HRESULT hr;
-  auto_com com;
-  if (!com.ok()) {
-    return CUBEB_ERROR;
-  }
-
-  com_ptr<IMMDevice> device;
-  hr = get_default_endpoint(device, eRender);
-  if (FAILED(hr)) {
-    return CUBEB_ERROR;
-  }
-
-  com_ptr<IAudioClient> client;
-  hr = device->Activate(__uuidof(IAudioClient),
-                        CLSCTX_INPROC_SERVER,
-                        NULL, client.receive_vpp());
-  if (FAILED(hr)) {
-    return CUBEB_ERROR;
-  }
-
-  WAVEFORMATEX * tmp = nullptr;
-  hr = client->GetMixFormat(&tmp);
-  if (FAILED(hr)) {
-    return CUBEB_ERROR;
-  }
-  com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
-
-  *layout = mask_to_channel_layout(mix_format.get());
-
-  LOG("Preferred channel layout: %s", CUBEB_CHANNEL_LAYOUT_MAPS[*layout].name);
-
-  return CUBEB_OK;
-}
-
 void wasapi_stream_destroy(cubeb_stream * stm);
 
 static void
 waveformatex_update_derived_properties(WAVEFORMATEX * format)
 {
   format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8;
   format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
   if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
@@ -1523,17 +1380,17 @@ handle_channel_layout(cubeb_stream * stm
 
   WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
 
   /* Stash a copy of the original mix format in case we need to restore it later. */
   WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm;
 
   /* 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);
+  format_pcm->dwChannelMask = 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 = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
                                                mix_format.get(),
                                                &closest);
@@ -1643,41 +1500,34 @@ int setup_wasapi_stream_one_side(cubeb_s
   com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
 
   mix_format->wBitsPerSample = stm->bytes_per_sample * 8;
   if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
     WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
     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, 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(mix_format.get());
-  if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) {
-    LOG("Stream using undefined layout! Any mixing may be unpredictable!\n");
-  } else if (mix_format->nChannels != CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels) {
-    // The CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels may be
-    // different from the mix_params->channels. 6 channel ouput with stereo
-    // layout is acceptable in Windows. If this happens, it should not downmix
-    // audio according to layout.
-    LOG("Channel count is different from the layout standard!\n");
-  }
-  LOG("Setup requested=[f=%d r=%u c=%u l=%s] mix=[f=%d r=%u c=%u l=%s]",
+
+  LOG("Setup requested=[f=%d r=%u c=%u l=%u] mix=[f=%d r=%u c=%u l=%u]",
       stream_params->format, stream_params->rate, stream_params->channels,
-      CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].name,
+      stream_params->layout,
       mix_params->format, mix_params->rate, mix_params->channels,
-      CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].name);
+      mix_params->layout);
 
   DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
 
   // Check if a loopback device should be requested. Note that event callbacks
   // do not work with loopback devices, so only request these if not looping.
   if (is_loopback) {
     flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
   } else {
@@ -1696,20 +1546,16 @@ int setup_wasapi_stream_one_side(cubeb_s
   }
 
   hr = audio_client->GetBufferSize(buffer_frame_count);
   if (FAILED(hr)) {
     LOG("Could not get the buffer size from the client"
         " for %s %lx.", DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
-  // Input is up/down mixed when depacketized in get_input_buffer.
-  if (has_output(stm) && cubeb_should_mix(stream_params, mix_params)) {
-    stm->mix_buffer.resize(frames_to_bytes_before_mix(stm, *buffer_frame_count));
-  }
 
   // Events are used if not looping back
   if (!is_loopback) {
     hr = audio_client->SetEventHandle(event);
     if (FAILED(hr)) {
       LOG("Could set the event handle for the %s client %lx.",
           DIRECTION_NAME, hr);
       return CUBEB_ERROR;
@@ -1724,27 +1570,20 @@ int setup_wasapi_stream_one_side(cubeb_s
 
   return CUBEB_OK;
 }
 
 #undef DIRECTION_NAME
 
 int setup_wasapi_stream(cubeb_stream * stm)
 {
-  HRESULT hr;
   int rv;
 
   stm->stream_reset_lock.assert_current_thread_owns();
 
-  auto_com com;
-  if (!com.ok()) {
-    LOG("Failure to initialize COM.");
-    return CUBEB_ERROR;
-  }
-
   XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
 
   if (has_input(stm)) {
     LOG("(%p) Setup capture: device=%p", stm, stm->input_device.get());
     rv = setup_wasapi_stream_one_side(stm,
                                       &stm->input_stream_params,
                                       stm->input_device.get(),
                                       eCapture,
@@ -1807,18 +1646,18 @@ int setup_wasapi_stream(cubeb_stream * s
                                       stm->refill_event,
                                       stm->render_client,
                                       &stm->output_mix_params);
     if (rv != CUBEB_OK) {
       LOG("Failure to open the output side.");
       return rv;
     }
 
-    hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
-                                        stm->audio_stream_volume.receive_vpp());
+    HRESULT hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
+                                                stm->audio_stream_volume.receive_vpp());
     if (FAILED(hr)) {
       LOG("Could not get the IAudioStreamVolume: %lx", hr);
       return CUBEB_ERROR;
     }
 
     XASSERT(stm->frames_written == 0);
     hr = stm->output_client->GetService(__uuidof(IAudioClock),
                                         stm->audio_clock.receive_vpp());
@@ -1876,35 +1715,63 @@ int setup_wasapi_stream(cubeb_stream * s
   if (has_input(stm) && has_output(stm)) {
     stm->refill_callback = refill_callback_duplex;
   } else if (has_input(stm)) {
     stm->refill_callback = refill_callback_input;
   } else if (has_output(stm)) {
     stm->refill_callback = refill_callback_output;
   }
 
+  // Create input mixer.
+  if (has_input(stm) &&
+      ((stm->input_mix_params.layout != CUBEB_LAYOUT_UNDEFINED &&
+        stm->input_mix_params.layout != stm->input_stream_params.layout) ||
+       (stm->input_mix_params.channels != stm->input_stream_params.channels))) {
+    if (stm->input_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) {
+      LOG("Input stream using undefined layout! Any mixing may be "
+          "unpredictable!\n");
+    }
+    stm->input_mixer.reset(cubeb_mixer_create(stm->input_stream_params.format,
+                                              stm->input_mix_params.channels,
+                                              stm->input_mix_params.layout,
+                                              stm->input_stream_params.channels,
+                                              stm->input_stream_params.layout));
+    assert(stm->input_mixer);
+  }
+
+  // Create output mixer.
+  if (has_output(stm) && stm->output_mix_params.layout != stm->output_stream_params.layout) {
+    if (stm->output_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) {
+      LOG("Output stream using undefined layout! Any mixing may be unpredictable!\n");
+    }
+    stm->output_mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
+                                               stm->output_stream_params.channels,
+                                               stm->output_stream_params.layout,
+                                               stm->output_mix_params.channels,
+                                               stm->output_mix_params.layout));
+    assert(stm->output_mixer);
+    // Input is up/down mixed when depacketized in get_input_buffer.
+    stm->mix_buffer.resize(
+      frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count));
+  }
+
   return CUBEB_OK;
 }
 
 int
 wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
                    char const * stream_name,
                    cubeb_devid input_device,
                    cubeb_stream_params * input_stream_params,
                    cubeb_devid output_device,
                    cubeb_stream_params * output_stream_params,
                    unsigned int latency_frames, cubeb_data_callback data_callback,
                    cubeb_state_callback state_callback, void * user_ptr)
 {
-  HRESULT hr;
   int rv;
-  auto_com com;
-  if (!com.ok()) {
-    return CUBEB_ERROR;
-  }
 
   XASSERT(context && stream && (input_stream_params || output_stream_params));
 
   if (output_stream_params && input_stream_params &&
       output_stream_params->format != input_stream_params->format) {
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
@@ -1912,45 +1779,36 @@ 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.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.layout == CUBEB_LAYOUT_UNDEFINED ||
-            stm->output_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->output_stream_params.layout].channels);
   }
 
   switch (output_stream_params ? output_stream_params->format : input_stream_params->format) {
     case CUBEB_SAMPLE_S16NE:
       stm->bytes_per_sample = sizeof(short);
       stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_PCM;
       stm->linear_input_buffer.reset(new auto_array_wrapper_impl<short>);
       break;
     case CUBEB_SAMPLE_FLOAT32NE:
       stm->bytes_per_sample = sizeof(float);
       stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
       stm->linear_input_buffer.reset(new auto_array_wrapper_impl<float>);
       break;
     default:
       return CUBEB_ERROR_INVALID_FORMAT;
   }
-  stm->mixer.reset(cubeb_mixer_create(output_stream_params ? output_stream_params->format :
-                                                             input_stream_params->format,
-                                      CUBEB_MIXER_DIRECTION_DOWNMIX | CUBEB_MIXER_DIRECTION_UPMIX));
 
   stm->latency = latency_frames;
 
   stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->reconfigure_event) {
     LOG("Can't create the reconfigure event, error: %lx", GetLastError());
     return CUBEB_ERROR;
   }
@@ -1974,17 +1832,17 @@ wasapi_stream_init(cubeb * context, cube
        assert that the lock is held in the function. */
     auto_lock lock(stm->stream_reset_lock);
     rv = setup_wasapi_stream(stm.get());
   }
   if (rv != CUBEB_OK) {
     return rv;
   }
 
-  hr = register_notification_client(stm.get());
+  HRESULT hr = register_notification_client(stm.get());
   if (FAILED(hr)) {
     /* this is not fatal, we can still play audio, but we won't be able
        to keep using the default audio endpoint if it changes. */
     LOG("failed to register notification client, %lx", hr);
   }
 
   *stream = stm.release();
 
@@ -2006,17 +1864,18 @@ void close_wasapi_stream(cubeb_stream * 
 
   stm->audio_stream_volume = nullptr;
 
   stm->audio_clock = nullptr;
   stm->total_frames_written += static_cast<UINT64>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
   stm->frames_written = 0;
 
   stm->resampler.reset();
-
+  stm->output_mixer.reset();
+  stm->input_mixer.reset();
   stm->mix_buffer.clear();
 }
 
 void wasapi_stream_destroy(cubeb_stream * stm)
 {
   XASSERT(stm);
   LOG("Stream destroy (%p)", stm);
 
@@ -2031,17 +1890,16 @@ void wasapi_stream_destroy(cubeb_stream 
   unregister_notification_client(stm);
 
   CloseHandle(stm->reconfigure_event);
   CloseHandle(stm->refill_event);
   CloseHandle(stm->input_available_event);
 
   // The variables intialized in wasapi_stream_init,
   // must be destroyed in wasapi_stream_destroy.
-  stm->mixer.reset();
   stm->linear_input_buffer.reset();
 
   {
     auto_lock lock(stm->stream_reset_lock);
     close_wasapi_stream(stm);
   }
 
   delete stm;
@@ -2437,30 +2295,27 @@ wasapi_create_device(cubeb * ctx, cubeb_
 
   return CUBEB_OK;
 }
 
 static int
 wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
                          cubeb_device_collection * out)
 {
-  auto_com com;
   com_ptr<IMMDeviceEnumerator> enumerator;
   com_ptr<IMMDeviceCollection> collection;
   HRESULT hr;
   UINT cc, i;
   EDataFlow flow;
 
-  if (!com.ok())
-    return CUBEB_ERROR;
-
   hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
-      CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.receive()));
+                        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
+    XASSERT(hr != CO_E_NOTINITIALIZED);
     return CUBEB_ERROR;
   }
 
   if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
   else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
   else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) flow = eAll;
   else return CUBEB_ERROR;
 
@@ -2514,17 +2369,16 @@ wasapi_device_collection_destroy(cubeb *
 }
 
 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 =*/ 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,
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -1045,17 +1045,16 @@ winmm_device_collection_destroy(cubeb * 
 }
 
 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 =*/ 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,
--- a/media/libcubeb/src/moz.build
+++ b/media/libcubeb/src/moz.build
@@ -8,17 +8,18 @@ DEFINES['CUBEB_GECKO_BUILD'] = True
 
 Library('cubeb')
 
 SOURCES += [
     'cubeb.c',
     'cubeb_log.cpp',
     'cubeb_mixer.cpp',
     'cubeb_panner.cpp',
-    'cubeb_strings.c'
+    'cubeb_strings.c',
+    'cubeb_utils.cpp'
 ]
 
 if CONFIG['MOZ_ALSA']:
     SOURCES += [
         'cubeb_alsa.c',
     ]
     DEFINES['USE_ALSA'] = True
 
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -32,27 +32,27 @@ 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_strings.c src
 cp $1/src/cubeb_strings.h src
 cp $1/src/cubeb_utils.h src
+cp $1/src/cubeb_utils.cpp 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
 cp $1/test/test_duplex.cpp gtest
 cp $1/test/test_latency.cpp gtest
 cp $1/test/test_loopback.cpp gtest
-cp $1/test/test_mixer.cpp gtest
 cp $1/test/test_overload_callback.cpp gtest
 cp $1/test/test_record.cpp gtest
 cp $1/test/test_resampler.cpp gtest
 cp $1/test/test_ring_array.cpp gtest
 cp $1/test/test_sanity.cpp gtest
 cp $1/test/test_tone.cpp gtest
 cp $1/test/test_utils.cpp gtest