Bug 1531833 - Update libcubeb to 241e3c (and rebase an in-tree patch). r=kinetik
authorPaul Adenot <paul@paul.cx>
Tue, 16 Apr 2019 15:44:47 +0000
changeset 469695 c749a1823112
parent 469694 0a351368e1f7
child 469696 a1e8c5a64ceb
push id35879
push usernerli@mozilla.com
push dateTue, 16 Apr 2019 22:01:48 +0000
treeherdermozilla-central@12a60898fdc1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1531833
milestone68.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 1531833 - Update libcubeb to 241e3c (and rebase an in-tree patch). r=kinetik This has been reviewed by snorp, kinetik, achronop, in bug 1531833, and the rollup has been rubberstamped by achronop in https://github.com/kinetiknz/cubeb/pull/501. Differential Revision: https://phabricator.services.mozilla.com/D26937
media/libcubeb/disable-device-switching.patch
media/libcubeb/include/cubeb.h
media/libcubeb/moz.yaml
media/libcubeb/src/android/sles_definitions.h
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_opensl.c
media/libcubeb/src/cubeb_wasapi.cpp
--- a/media/libcubeb/disable-device-switching.patch
+++ b/media/libcubeb/disable-device-switching.patch
@@ -1,39 +1,8 @@
-diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h
---- a/media/libcubeb/include/cubeb.h
-+++ b/media/libcubeb/include/cubeb.h
-@@ -216,20 +216,23 @@ enum {
-                          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_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_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
-+                                                          default device on OS
-+                                                          changes. */
- } 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]. */
 diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp
 --- a/media/libcubeb/src/cubeb_wasapi.cpp
 +++ b/media/libcubeb/src/cubeb_wasapi.cpp
 @@ -1829,21 +1829,26 @@ 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());
    }
@@ -80,8 +49,31 @@ diff --git a/media/libcubeb/src/cubeb_wa
  
    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->linear_input_buffer.reset();
+diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h
+--- a/media/libcubeb/include/cubeb.h
++++ a/media/libcubeb/include/cubeb.h
+@@ -222,16 +222,19 @@
+ 
+ /** 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_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
++                                                          default device on OS
++                                                          changes. */
+   CUBEB_STREAM_PREF_VOICE = 0x04  /**< This stream is going to transport voice data.
+                                        Depending on the backend and platform, this can
+                                        change the audio input or output devices
+                                        selected, as well as the quality of the stream,
+                                        for example to accomodate bluetooth SCO modes on
+                                        bluetooth devices. */
+ } cubeb_stream_prefs;
+ 
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -219,22 +219,28 @@ enum {
                          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. */
+                                         specified on the input params and an
+                                         output device to loopback from should
+                                         be passed in place of an input device. */
   CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
                                                           default device on OS
                                                           changes. */
+  CUBEB_STREAM_PREF_VOICE = 0x04  /**< This stream is going to transport voice data.
+                                       Depending on the backend and platform, this can
+                                       change the audio input or output devices
+                                       selected, as well as the quality of the stream,
+                                       for example to accomodate bluetooth SCO modes on
+                                       bluetooth devices. */
 } 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]. */
--- a/media/libcubeb/moz.yaml
+++ b/media/libcubeb/moz.yaml
@@ -14,10 +14,10 @@ bugzilla:
 origin:
   name: "cubeb"
   description: "Cross platform audio library"
 
   url: "https://github.com/kinetiknz/cubeb"
   license: "ISC"
 
   # update.sh will update this value
-  release: "66d9c48d916f00c396482f9c5075feacc2bc0db8 (2019-04-03 12:41:20 +0300)"
+  release: "241e3c7b8a6ce76ad9e075ee5761cd4d0906bc16 (2019-04-16 17:39:01 +0200)"
 
--- a/media/libcubeb/src/android/sles_definitions.h
+++ b/media/libcubeb/src/android/sles_definitions.h
@@ -38,20 +38,19 @@
 #define SL_ANDROID_RECORDING_PRESET_GENERIC             ((SLuint32) 0x00000001)
 /**   uses the microphone audio source with the same orientation as the camera
  *     if available, the main device microphone otherwise */
 #define SL_ANDROID_RECORDING_PRESET_CAMCORDER           ((SLuint32) 0x00000002)
 /**   uses the main microphone tuned for voice recognition */
 #define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION   ((SLuint32) 0x00000003)
 /**   uses the main microphone tuned for audio communications */
 #define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)
+/**   uses the main microphone unprocessed */
+#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED         ((SLuint32) 0x00000005)
 
-/** Audio recording get session ID (read only) */
-/** Audio recording get session ID key */
-#define SL_ANDROID_KEY_RECORDING_SESSION_ID ((const SLchar*) "androidRecordingSessionId")
 
 /*---------------------------------------------------------------------------*/
 /* Android AudioPlayer configuration                                         */
 /*---------------------------------------------------------------------------*/
 
 /** Audio playback stream type */
 /** Audio playback stream type key */
 #define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType")
@@ -64,14 +63,40 @@
 /*      same as android.media.AudioManager.STREAM_RING */
 #define SL_ANDROID_STREAM_RING         ((SLint32) 0x00000002)
 /*      same as android.media.AudioManager.STREAM_MUSIC */
 #define SL_ANDROID_STREAM_MEDIA        ((SLint32) 0x00000003)
 /*      same as android.media.AudioManager.STREAM_ALARM */
 #define SL_ANDROID_STREAM_ALARM        ((SLint32) 0x00000004)
 /*      same as android.media.AudioManager.STREAM_NOTIFICATION */
 #define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005)
-/*      same as android.media.AudioManager.STREAM_BLUETOOTH_SCO */
-#define SL_ANDROID_STREAM_BLUETOOTH_SCO ((SLint32) 0x00000006)
-/*      same as android.media.AudioManager.STREAM_SYSTEM_ENFORCED */
-#define SL_ANDROID_STREAM_SYSTEM_ENFORCED ((SLint32) 0x00000007)
+
+
+/*---------------------------------------------------------------------------*/
+/* Android AudioPlayer and AudioRecorder configuration                       */
+/*---------------------------------------------------------------------------*/
+
+/** Audio Performance mode.
+ * Performance mode tells the framework how to configure the audio path
+ * for a player or recorder according to application performance and
+ * functional requirements.
+ * It affects the output or input latency based on acceptable tradeoffs on
+ * battery drain and use of pre or post processing effects.
+ * Performance mode should be set before realizing the object and should be
+ * read after realizing the object to check if the requested mode could be
+ * granted or not.
+ */
+/** Audio Performance mode key */
+#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
+
+/** Audio performance values */
+/*      No specific performance requirement. Allows HW and SW pre/post processing. */
+#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000)
+/*      Priority given to latency. No HW or software pre/post processing.
+ *      This is the default if no performance mode is specified. */
+#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001)
+/*      Priority given to latency while still allowing HW pre and post processing. */
+#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002)
+/*      Priority given to power saving if latency is not a concern.
+ *      Allows HW and SW pre/post processing. */
+#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003)
 
 #endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -241,18 +241,18 @@ struct cubeb_stream {
   /* Latency requested by the user. */
   uint32_t latency_frames = 0;
   atomic<uint32_t> current_latency_frames{ 0 };
   atomic<float> panning{ 0 };
   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
+  AudioDeviceID aggregate_device_id = kAudioObjectUnknown;  // the aggregate device id
+  AudioObjectID plugin_id = kAudioObjectUnknown;            // 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.
   /* Listeners indicating what system events are monitored. */
   unique_ptr<property_listener> default_input_listener;
@@ -811,41 +811,41 @@ audiounit_reinit_stream(cubeb_stream * s
     audiounit_close_stream(stm);
 
     /* Reinit occurs in one of the following case:
      * - When the device is not alive any more
      * - When the default system device change.
      * - The bluetooth device changed from A2DP to/from HFP/HSP profile
      * We first attempt to re-use the same device id, should that fail we will
      * default to the (potentially new) default device. */
-    AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : 0;
+    AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
     if (flags & DEV_INPUT) {
       r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
       if (r != CUBEB_OK) {
         LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm);
         return CUBEB_ERROR;
       }
     }
 
     /* Always use the default output on reinit. This is not correct in every
      * case but it is sufficient for Firefox and prevent reinit from reporting
      * failures. It will change soon when reinit mechanism will be updated. */
-    r = audiounit_set_device_info(stm, 0, io_side::OUTPUT);
+    r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
     if (r != CUBEB_OK) {
       LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm);
       return CUBEB_ERROR;
     }
 
     if (audiounit_setup_stream(stm) != CUBEB_OK) {
       LOG("(%p) Stream reinit failed.", stm);
-      if (flags & DEV_INPUT && input_device != 0) {
+      if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
         // Attempt to re-use the same device-id failed, so attempt again with
         // default input device.
         audiounit_close_stream(stm);
-        if (audiounit_set_device_info(stm, 0, io_side::INPUT) != CUBEB_OK ||
+        if (audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::INPUT) != CUBEB_OK ||
             audiounit_setup_stream(stm) != CUBEB_OK) {
           LOG("(%p) Second stream reinit failed.", stm);
           return CUBEB_ERROR;
         }
       }
     }
 
     if (vol_rv == CUBEB_OK) {
@@ -863,17 +863,17 @@ audiounit_reinit_stream(cubeb_stream * s
   return CUBEB_OK;
 }
 
 static void
 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
 {
   if (std::atomic_exchange(&stm->reinit_pending, true)) {
     // A reinit task is already pending, nothing more to do.
-    ALOG("(%p) re-init stream task already pending, cancelling request ", stm);
+    ALOG("(%p) re-init stream task already pending, cancelling request", stm);
     return;
   }
 
   // Use a new thread, through the queue, to avoid deadlock when calling
   // Get/SetProperties method from inside notify callback
   dispatch_async(stm->context->serial_queue, ^() {
     if (stm->destroy_pending) {
       ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
@@ -939,17 +939,17 @@ audiounit_property_listener_callback(Aud
           if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
             LOG("It's the default input device, ignore the event");
             stm->switching_device = false;
             return noErr;
           }
         }
         break;
       case kAudioDevicePropertyDataSource: {
-          LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource for id=%d", (unsigned int) i, id);
+          LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", (unsigned int) i, id);
         }
         break;
       default:
         LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
         stm->switching_device = false;
         return noErr;
     }
   }
@@ -1309,19 +1309,19 @@ 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.
+  // When having one 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
@@ -1606,17 +1606,17 @@ audiounit_create_blank_aggregate_device(
                                                           kAudioObjectPropertyScopeGlobal,
                                                           kAudioObjectPropertyElementMaster };
   UInt32 size = 0;
   OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
                                               &address_plugin_bundle_id,
                                               0, NULL,
                                               &size);
   if (r != noErr) {
-    LOG("AudioHardwareGetPropertyInfo/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
+    LOG("AudioObjectGetPropertyDataSize/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
     return CUBEB_ERROR;
   }
 
   AudioValueTranslation translation_value;
   CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
   translation_value.mInputData = &in_bundle_ref;
   translation_value.mInputDataSize = sizeof(in_bundle_ref);
   translation_value.mOutputData = plugin_id;
@@ -1624,17 +1624,17 @@ audiounit_create_blank_aggregate_device(
 
   r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                  &address_plugin_bundle_id,
                                  0,
                                  nullptr,
                                  &size,
                                  &translation_value);
   if (r != noErr) {
-    LOG("AudioHardwareGetProperty/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
+    LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
     return CUBEB_ERROR;
   }
 
   AudioObjectPropertyAddress create_aggregate_device_address = { kAudioPlugInCreateAggregateDevice,
                                                                  kAudioObjectPropertyScopeGlobal,
                                                                  kAudioObjectPropertyElementMaster };
   r = AudioObjectGetPropertyDataSize(*plugin_id,
                                      &create_aggregate_device_address,
@@ -1750,17 +1750,17 @@ audiounit_set_aggregate_sub_device_list(
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
 {
-  assert(aggregate_device_id);
+  assert(aggregate_device_id != kAudioObjectUnknown);
   AudioObjectPropertyAddress master_aggregate_sub_device =  { kAudioAggregateDevicePropertyMasterSubDevice,
                                                               kAudioObjectPropertyScopeGlobal,
                                                               kAudioObjectPropertyElementMaster };
 
   // Master become the 1st output sub device
   AudioDeviceID output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
   const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
   CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
@@ -1781,17 +1781,17 @@ audiounit_set_master_aggregate_device(co
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id)
 {
-  assert(aggregate_device_id);
+  assert(aggregate_device_id != kAudioObjectUnknown);
   AudioObjectPropertyAddress address_owned = { kAudioObjectPropertyOwnedObjects,
                                                kAudioObjectPropertyScopeGlobal,
                                                kAudioObjectPropertyElementMaster };
 
   UInt32 qualifier_data_size = sizeof(AudioObjectID);
   AudioClassID class_id = kAudioSubDeviceClassID;
   void * qualifier_data = &class_id;
   UInt32 size = 0;
@@ -1978,17 +1978,17 @@ audiounit_destroy_aggregate_device(Audio
                                   &size,
                                   aggregate_device_id);
   if (rv != noErr) {
     LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", rv);
     return CUBEB_ERROR;
   }
 
   LOG("Destroyed aggregate device %d", *aggregate_device_id);
-  *aggregate_device_id = 0;
+  *aggregate_device_id = kAudioObjectUnknown;
   return CUBEB_OK;
 }
 
 static int
 audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
 {
   AudioComponentDescription desc;
   AudioComponent comp;
@@ -2067,33 +2067,33 @@ audiounit_create_unit(AudioUnit * unit, 
       (device->flags & DEV_OUTPUT)) {
     return CUBEB_OK;
   }
 
 
   if (device->flags & DEV_INPUT) {
     r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to enable audiounit input scope ");
+      LOG("Failed to enable audiounit input scope");
       return r;
     }
     r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to disable audiounit output scope ");
+      LOG("Failed to disable audiounit output scope");
       return r;
     }
   } else if (device->flags & DEV_OUTPUT) {
     r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to enable audiounit output scope ");
+      LOG("Failed to enable audiounit output scope");
       return r;
     }
     r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to disable audiounit input scope ");
+      LOG("Failed to disable audiounit input scope");
       return r;
     }
   } else {
     assert(false);
   }
 
   rv = AudioUnitSetProperty(*unit,
                             kAudioOutputUnitProperty_CurrentDevice,
@@ -2552,17 +2552,17 @@ audiounit_setup_stream(cubeb_stream * st
 
   device_info in_dev_info = stm->input_device;
   device_info out_dev_info = stm->output_device;
 
   if (has_input(stm) && has_output(stm) &&
       stm->input_device.id != stm->output_device.id) {
     r = audiounit_create_aggregate_device(stm);
     if (r != CUBEB_OK) {
-      stm->aggregate_device_id = 0;
+      stm->aggregate_device_id = kAudioObjectUnknown;
       LOG("(%p) Create aggregate devices failed.", stm);
       // !!!NOTE: It is not necessary to return here. If it does not
       // return it will fallback to the old implementation. The intention
       // is to investigate how often it fails. I plan to remove
       // it after a couple of weeks.
       return r;
     } else {
       in_dev_info.id = out_dev_info.id = stm->aggregate_device_id;
@@ -2583,26 +2583,26 @@ audiounit_setup_stream(cubeb_stream * st
     r = audiounit_create_unit(&stm->output_unit, &out_dev_info);
     if (r != CUBEB_OK) {
       LOG("(%p) AudioUnit creation for output failed.", stm);
       return r;
     }
   }
 
   /* Latency cannot change if another stream is operating in parallel. In this case
-  * latecy is set to the other stream value. */
+   * latency is set to the other stream value. */
   if (audiounit_active_streams(stm->context) > 1) {
     LOG("(%p) More than one active stream, use global latency.", stm);
     stm->latency_frames = stm->context->global_latency_frames;
   } else {
     /* Silently clamp the latency down to the platform default, because we
-    * synthetize the clock from the callbacks, and we want the clock to update
-    * often. */
+     * synthetize the clock from the callbacks, and we want the clock to update
+     * often. */
     stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
-    assert(stm->latency_frames); // Ungly error check
+    assert(stm->latency_frames); // Ugly error check
     audiounit_set_global_latency(stm->context, stm->latency_frames);
   }
 
   /* Configure I/O stream */
   if (has_input(stm)) {
     r = audiounit_configure_input(stm);
     if (r != CUBEB_OK) {
       LOG("(%p) Configure audiounit input failed.", stm);
@@ -2835,19 +2835,19 @@ audiounit_close_stream(cubeb_stream *stm
     AudioUnitUninitialize(stm->output_unit);
     AudioComponentInstanceDispose(stm->output_unit);
     stm->output_unit = nullptr;
   }
 
   stm->resampler.reset();
   stm->mixer.reset();
 
-  if (stm->aggregate_device_id) {
+  if (stm->aggregate_device_id != kAudioObjectUnknown) {
     audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
-    stm->aggregate_device_id = 0;
+    stm->aggregate_device_id = kAudioObjectUnknown;
   }
 }
 
 static void
 audiounit_stream_destroy_internal(cubeb_stream *stm)
 {
   stm->context->mutex.assert_current_thread_owns();
 
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -52,22 +52,29 @@
     LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, __LINE__);\
   }                                                   \
 } while(0)
 #else
 #define TIMESTAMP(...)
 #endif
 
 #define ANDROID_VERSION_GINGERBREAD_MR1 10
+#define ANDROID_VERSION_JELLY_BEAN 18
 #define ANDROID_VERSION_LOLLIPOP 21
 #define ANDROID_VERSION_MARSHMALLOW 23
+#define ANDROID_VERSION_N_MR1 25
 #endif
 
 #define DEFAULT_SAMPLE_RATE 48000
 #define DEFAULT_NUM_OF_FRAMES 480
+// If the latency requested is above this threshold, this stream is considered
+// intended for playback (vs. real-time). Tell Android it should favor saving
+// power over performance or latency.
+// This is around 100ms at 44100 or 48000
+#define POWERSAVE_LATENCY_FRAMES_THRESHOLD 4000
 
 static struct cubeb_ops const opensl_ops;
 
 struct cubeb {
   struct cubeb_ops const * ops;
   void * lib;
   SLInterfaceID SL_IID_BUFFERQUEUE;
   SLInterfaceID SL_IID_PLAY;
@@ -79,17 +86,17 @@ struct cubeb {
   SLInterfaceID SL_IID_RECORD;
   SLObjectItf engObj;
   SLEngineItf eng;
   SLObjectItf outmixObj;
   output_latency_function * p_output_latency_function;
 };
 
 #define NELEMS(A) (sizeof(A) / sizeof A[0])
-#define NBUFS 4
+#define NBUFS 2
 
 struct cubeb_stream {
   /* Note: Must match cubeb_stream layout in cubeb.c. */
   cubeb * context;
   void * user_ptr;
   /**/
   pthread_mutex_t mutex;
   SLObjectItf playerObj;
@@ -150,20 +157,23 @@ struct cubeb_stream {
   /* Store user callback. */
   cubeb_data_callback data_callback;
   /* Store state callback. */
   cubeb_state_callback state_callback;
 
   cubeb_resampler * resampler;
   unsigned int user_output_rate;
   unsigned int output_configured_rate;
-  unsigned int latency_frames;
+  unsigned int buffer_size_frames;
+  // Audio output latency used in cubeb_stream_get_position().
+  unsigned int output_latency_ms;
   int64_t lastPosition;
   int64_t lastPositionTimeStamp;
   int64_t lastCompensativePosition;
+  int voice;
 };
 
 /* Forward declaration. */
 static int opensl_stop_player(cubeb_stream * stm);
 static int opensl_stop_recorder(cubeb_stream * stm);
 
 static int
 opensl_get_draining(cubeb_stream * stm)
@@ -841,26 +851,27 @@ opensl_configure_capture(cubeb_stream * 
   lDataLocatorIn.deviceType = SL_IODEVICE_AUDIOINPUT;
   lDataLocatorIn.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
   lDataLocatorIn.device = NULL;
 
   SLDataSource lDataSource;
   lDataSource.pLocator = &lDataLocatorIn;
   lDataSource.pFormat = NULL;
 
-  const SLuint32 lSoundRecorderIIDCount = 2;
   const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD,
-                                               stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
-  const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
+                                               stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                               stm->context->SL_IID_ANDROIDCONFIGURATION };
+
+  const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
   // create the audio recorder abstract object
   SLresult res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
                                                            &stm->recorderObj,
                                                            &lDataSource,
                                                            &lDataSink,
-                                                           lSoundRecorderIIDCount,
+                                                           NELEMS(lSoundRecorderIIDs),
                                                            lSoundRecorderIIDs,
                                                            lSoundRecorderReqs);
   // Sample rate not supported. Try again with default sample rate!
   if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
     if (stm->output_enabled && stm->output_configured_rate != 0) {
       // Set the same with the player. Since there is no
       // api for input device this is a safe choice.
       stm->input_device_rate = stm->output_configured_rate;
@@ -869,26 +880,56 @@ opensl_configure_capture(cubeb_stream * 
       // The default rate expected to be supported from all android devices.
       stm->input_device_rate = DEFAULT_SAMPLE_RATE;
     }
     lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
     res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
                                                     &stm->recorderObj,
                                                     &lDataSource,
                                                     &lDataSink,
-                                                    lSoundRecorderIIDCount,
+                                                    NELEMS(lSoundRecorderIIDs),
                                                     lSoundRecorderIIDs,
                                                     lSoundRecorderReqs);
 
     if (res != SL_RESULT_SUCCESS) {
       LOG("Failed to create recorder. Error code: %lu", res);
       return CUBEB_ERROR;
     }
   }
 
+
+  if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
+    SLAndroidConfigurationItf recorderConfig;
+    res = (*stm->recorderObj)
+              ->GetInterface(stm->recorderObj,
+                             stm->context->SL_IID_ANDROIDCONFIGURATION,
+                             &recorderConfig);
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to get the android configuration interface for recorder. Error "
+          "code: %lu",
+          res);
+      return CUBEB_ERROR;
+    }
+
+    // Voice recognition is the lowest latency, according to the docs. Camcorder
+    // uses a microphone that is in the same direction as the camera.
+    SLint32 streamType = stm->voice ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
+                                    : SL_ANDROID_RECORDING_PRESET_CAMCORDER;
+
+    res = (*recorderConfig)
+              ->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
+                                 &streamType, sizeof(SLint32));
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set the android configuration to VOICE for the recorder. "
+          "Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+  }
   // realize the audio recorder
   res = (*stm->recorderObj)->Realize(stm->recorderObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to realize recorder. Error code: %lu", res);
     return CUBEB_ERROR;
   }
   // get the record interface
   res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
@@ -932,17 +973,17 @@ opensl_configure_capture(cubeb_stream * 
                                                          stm);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to register recorder buffer queue callback. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
   // Calculate length of input buffer according to requested latency
   stm->input_frame_size = params->channels * sizeof(int16_t);
-  stm->input_buffer_length = (stm->input_frame_size * stm->latency_frames);
+  stm->input_buffer_length = (stm->input_frame_size * stm->buffer_size_frames);
 
   // Calculate the capacity of input array
   stm->input_array_capacity = NBUFS;
   if (stm->output_enabled) {
     // Full duplex, update capacity to hold 1 sec of data
     stm->input_array_capacity = 1 * stm->input_device_rate / stm->input_buffer_length;
   }
   // Allocate input array
@@ -1043,37 +1084,116 @@ opensl_configure_playback(cubeb_stream *
 
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to create audio player. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
   stm->output_configured_rate = preferred_sampling_rate;
   stm->bytespersec = stm->output_configured_rate * stm->framesize;
-  stm->queuebuf_len = stm->framesize * stm->latency_frames;
+  stm->queuebuf_len = stm->framesize * stm->buffer_size_frames;
 
   // Calculate the capacity of input array
   stm->queuebuf_capacity = NBUFS;
   if (stm->output_enabled) {
     // Full duplex, update capacity to hold 1 sec of data
     stm->queuebuf_capacity = 1 * stm->output_configured_rate / stm->queuebuf_len;
   }
   // Allocate input array
   stm->queuebuf = (void**)calloc(1, sizeof(void*) * stm->queuebuf_capacity);
   for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) {
     stm->queuebuf[i] = calloc(1, stm->queuebuf_len);
     assert(stm->queuebuf[i]);
   }
 
+  SLAndroidConfigurationItf playerConfig = NULL;
+
+  if (get_android_version() >= ANDROID_VERSION_N_MR1) {
+    res = (*stm->playerObj)
+              ->GetInterface(stm->playerObj,
+                             stm->context->SL_IID_ANDROIDCONFIGURATION,
+                             &playerConfig);
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to get Android configuration interface. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+
+    SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
+    if (stm->voice) {
+      streamType = SL_ANDROID_STREAM_VOICE;
+    }
+    res = (*playerConfig)->SetConfiguration(playerConfig,
+                                            SL_ANDROID_KEY_STREAM_TYPE,
+                                            &streamType,
+                                            sizeof(streamType));
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set Android configuration to %d Error code: %lu",
+          streamType, res);
+    }
+
+    SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_LATENCY;
+    if (stm->buffer_size_frames > POWERSAVE_LATENCY_FRAMES_THRESHOLD) {
+      performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
+    }
+
+    res = (*playerConfig)->SetConfiguration(playerConfig,
+                                            SL_ANDROID_KEY_PERFORMANCE_MODE,
+                                            &performanceMode,
+                                            sizeof(performanceMode));
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set Android performance mode to %d Error code: %lu. This is"
+          " not fatal", performanceMode, res);
+    }
+  }
+
   res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to realize player object. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
+  // There are two ways of getting the audio output latency:
+  // - a configuration value, only available on some devices (notably devices
+  // running FireOS)
+  // - A Java method, that we call using JNI.
+  //
+  // The first method is prefered, if available, because it can account for more
+  // latency causes, and is more precise.
+
+  // Latency has to be queried after the realization of the interface, when
+  // using SL_IID_ANDROIDCONFIGURATION.
+  SLuint32 audioLatency = 0;
+  SLuint32 paramSize = sizeof(SLuint32);
+  // The reported latency is in milliseconds.
+  if (playerConfig) {
+    res = (*playerConfig)->GetConfiguration(playerConfig,
+                                            (const SLchar *)"androidGetAudioLatency",
+                                            &paramSize,
+                                            &audioLatency);
+    if (res == SL_RESULT_SUCCESS) {
+      LOG("Got playback latency using android configuration extension");
+      stm->output_latency_ms = audioLatency;
+    }
+  }
+  // `playerConfig` is available, but the above failed, or `playerConfig` is not
+  // available. In both cases, we need to acquire the output latency by an other
+  // mean.
+  if ((playerConfig && res != SL_RESULT_SUCCESS) ||
+      !playerConfig) {
+    if (cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
+      LOG("Got playback latency using JNI");
+      stm->output_latency_ms = cubeb_get_output_latency(stm->context->p_output_latency_function);
+    } else {
+      LOG("No alternate latency querying method loaded, A/V sync will be off.");
+      stm->output_latency_ms = 0;
+    }
+  }
+
+  LOG("Audio output latency: %dms", stm->output_latency_ms);
+
   res = (*stm->playerObj)->GetInterface(stm->playerObj,
                                         stm->context->SL_IID_PLAY,
                                         &stm->play);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to get play interface. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
@@ -1143,16 +1263,24 @@ opensl_validate_stream_param(cubeb_strea
   if ((stream_params &&
        (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
     LOG("Loopback is not supported");
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
   return CUBEB_OK;
 }
 
+int has_pref_set(cubeb_stream_params* input_params,
+                 cubeb_stream_params* output_params,
+                 cubeb_stream_prefs pref)
+{
+  return (input_params && input_params->prefs & pref) ||
+         (output_params && output_params->prefs & pref);
+}
+
 static int
 opensl_stream_init(cubeb * ctx, 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,
@@ -1180,46 +1308,50 @@ opensl_stream_init(cubeb * ctx, cubeb_st
 
   stm = calloc(1, sizeof(*stm));
   assert(stm);
 
   stm->context = ctx;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
-  stm->latency_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
+  stm->buffer_size_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
   stm->input_enabled = (input_stream_params) ? 1 : 0;
   stm->output_enabled = (output_stream_params) ? 1 : 0;
   stm->shutdown = 1;
+  stm->voice = has_pref_set(input_stream_params, output_stream_params, CUBEB_STREAM_PREF_VOICE);
+
+  LOG("cubeb stream prefs: voice: %s", stm->voice ? "true" : "false");
+
 
 #ifdef DEBUG
   pthread_mutexattr_t attr;
   pthread_mutexattr_init(&attr);
   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
   r = pthread_mutex_init(&stm->mutex, &attr);
 #else
   r = pthread_mutex_init(&stm->mutex, NULL);
 #endif
   assert(r == 0);
 
   if (output_stream_params) {
     LOG("Playback params: Rate %d, channels %d, format %d, latency in frames %d.",
         output_stream_params->rate, output_stream_params->channels,
-        output_stream_params->format, stm->latency_frames);
+        output_stream_params->format, stm->buffer_size_frames);
     r = opensl_configure_playback(stm, output_stream_params);
     if (r != CUBEB_OK) {
       opensl_stream_destroy(stm);
       return r;
     }
   }
 
   if (input_stream_params) {
     LOG("Capture params: Rate %d, channels %d, format %d, latency in frames %d.",
         input_stream_params->rate, input_stream_params->channels,
-        input_stream_params->format, stm->latency_frames);
+        input_stream_params->format, stm->buffer_size_frames);
     r = opensl_configure_capture(stm, input_stream_params);
     if (r != CUBEB_OK) {
       opensl_stream_destroy(stm);
       return r;
     }
   }
 
   /* Configure resampler*/
@@ -1447,51 +1579,47 @@ opensl_stream_destroy(cubeb_stream * stm
 
 static int
 opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   SLmillisecond msec;
   uint32_t compensation_msec = 0;
   SLresult res;
 
-  if (!cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
-    return CUBEB_ERROR_NOT_SUPPORTED;
-  }
-
   res = (*stm->play)->GetPosition(stm->play, &msec);
   if (res != SL_RESULT_SUCCESS)
     return CUBEB_ERROR;
 
   struct timespec t;
   clock_gettime(CLOCK_MONOTONIC, &t);
   if(stm->lastPosition == msec) {
     compensation_msec =
       (t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000;
   } else {
     stm->lastPositionTimeStamp = t.tv_sec*1000000000LL + t.tv_nsec;
     stm->lastPosition = msec;
   }
 
   uint64_t samplerate = stm->user_output_rate;
-  uint32_t mixer_latency = cubeb_get_output_latency(stm->context->p_output_latency_function);
+  uint32_t output_latency = stm->output_latency_ms;
 
   pthread_mutex_lock(&stm->mutex);
   int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate;
   pthread_mutex_unlock(&stm->mutex);
   assert(maximum_position >= 0);
 
-  if (msec > mixer_latency) {
+  if (msec > output_latency) {
     int64_t unadjusted_position;
     if (stm->lastCompensativePosition > msec + compensation_msec) {
       // Over compensation, use lastCompensativePosition.
       unadjusted_position =
-        samplerate * (stm->lastCompensativePosition - mixer_latency) / 1000;
+        samplerate * (stm->lastCompensativePosition - output_latency) / 1000;
     } else {
       unadjusted_position =
-        samplerate * (msec - mixer_latency + compensation_msec) / 1000;
+        samplerate * (msec - output_latency + compensation_msec) / 1000;
       stm->lastCompensativePosition = msec + compensation_msec;
     }
     *position = unadjusted_position < maximum_position ?
       unadjusted_position : maximum_position;
   } else {
     *position = 0;
   }
   return CUBEB_OK;
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -136,16 +136,17 @@ private:
 };
 
 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);
+ERole pref_to_role(cubeb_stream_prefs param);
 static char const * wstr_to_utf8(wchar_t const * str);
 static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
 
 }
 
 class wasapi_collection_notification_client;
 class monitor_device_notifications;
 
@@ -186,16 +187,18 @@ struct cubeb_stream {
      samplerate/channel layout, as WASAPI does not resample nor upmix
      itself. */
   cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
   cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
   /* Stream parameters. This is what the client requested,
    * and what will be presented in the callback. */
   cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
   cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  /* A MMDevice role for this stream: either communication or console here. */
+  ERole role;
   /* The input and output device, or NULL for default. */
   std::unique_ptr<const wchar_t[]> input_device;
   std::unique_ptr<const wchar_t[]> output_device;
   /* The latency initially requested for this stream, in frames. */
   unsigned latency = 0;
   cubeb_state_callback state_callback = nullptr;
   cubeb_data_callback data_callback = nullptr;
   wasapi_refill_callback refill_callback = nullptr;
@@ -560,31 +563,32 @@ public:
       *ppvInterface = (IMMNotificationClient*)this;
     } else {
       *ppvInterface = NULL;
       return E_NOINTERFACE;
     }
     return S_OK;
   }
 
-  wasapi_endpoint_notification_client(HANDLE event)
+  wasapi_endpoint_notification_client(HANDLE event, ERole role)
     : ref_count(1)
     , reconfigure_event(event)
+    , role(role)
   { }
 
   virtual ~wasapi_endpoint_notification_client()
   { }
 
   HRESULT STDMETHODCALLTYPE
   OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
   {
     LOG("endpoint: Audio device default changed.");
 
     /* we only support a single stream type for now. */
-    if (flow != eRender && role != eConsole) {
+    if (flow != eRender && role != this->role) {
       return S_OK;
     }
 
     BOOL ok = SetEvent(reconfigure_event);
     if (!ok) {
       LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError());
     }
 
@@ -617,16 +621,17 @@ public:
   {
     //Audio device property value changed.
     return S_OK;
   }
 private:
   /* refcount for this instance, necessary to implement MSCOM semantics. */
   LONG ref_count;
   HANDLE reconfigure_event;
+  ERole role;
 };
 
 namespace {
 
 char const *
 intern_device_id(cubeb * ctx, wchar_t const * id)
 {
   XASSERT(id);
@@ -1251,17 +1256,17 @@ HRESULT register_notification_client(cub
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(stm->device_enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
     return hr;
   }
 
-  stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event));
+  stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event, stm->role));
 
   hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client.get());
   if (FAILED(hr)) {
     LOG("Could not register endpoint notification callback: %lx", hr);
     stm->notification_client = nullptr;
     stm->device_enumerator = nullptr;
   }
 
@@ -1346,27 +1351,27 @@ HRESULT unregister_collection_notificati
 
   context->collection_notification_client = nullptr;
   context->device_collection_enumerator = nullptr;
 
   context->monitor_notifications.reset();
   return hr;
 }
 
-HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction)
+HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction, ERole role)
 {
   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);
     return hr;
   }
-  hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device.receive());
+  hr = enumerator->GetDefaultAudioEndpoint(direction, role, device.receive());
   if (FAILED(hr)) {
     LOG("Could not get default audio endpoint: %lx", hr);
     return hr;
   }
 
   return ERROR_SUCCESS;
 }
 
@@ -1442,21 +1447,21 @@ stream_set_volume(cubeb_stream * stm, fl
 
 extern "C" {
 int wasapi_init(cubeb ** context, char const * context_name)
 {
   /* 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;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
   if (FAILED(hr)) {
     XASSERT(hr != CO_E_NOTINITIALIZED);
     LOG("It wasn't able to find a default rendering device: %lx", hr);
-    hr = get_default_endpoint(device, eCapture);
+    hr = get_default_endpoint(device, eCapture, eConsole);
     if (FAILED(hr)) {
       LOG("It wasn't able to find a default capture device: %lx", hr);
       return CUBEB_ERROR;
     }
   }
 
   cubeb * ctx = new cubeb();
 
@@ -1545,17 +1550,17 @@ char const * wasapi_get_backend_id(cubeb
 }
 
 int
 wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
 {
   XASSERT(ctx && max_channels);
 
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
@@ -1577,18 +1582,20 @@ wasapi_get_max_channel_count(cubeb * ctx
 
 int
 wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) {
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
+  ERole role = pref_to_role(params.prefs);
+
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, role);
   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,
@@ -1618,17 +1625,17 @@ wasapi_get_min_latency(cubeb * ctx, cube
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
@@ -1750,17 +1757,17 @@ int setup_wasapi_stream_one_side(cubeb_s
       if (FAILED(hr)) {
         LOG("Could not get %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
         return CUBEB_ERROR;
       }
     } else {
       // If caller has requested loopback but not specified a device, look for
       // the default render device. Otherwise look for the default device
       // appropriate to the direction.
-      hr = get_default_endpoint(device, is_loopback ? eRender : direction);
+      hr = get_default_endpoint(device, is_loopback ? eRender : direction, pref_to_role(stream_params->prefs));
       if (FAILED(hr)) {
         if (is_loopback) {
           LOG("Could not get default render endpoint for loopback, error: %lx\n", hr);
         } else {
           LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
         }
         return CUBEB_ERROR;
       }
@@ -2052,16 +2059,26 @@ int setup_wasapi_stream(cubeb_stream * s
     // 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;
 }
 
+ERole
+pref_to_role(cubeb_stream_prefs prefs)
+{
+  if (prefs & CUBEB_STREAM_PREF_VOICE) {
+    return eCommunications;
+  }
+
+  return eConsole;
+}
+
 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,
@@ -2077,16 +2094,24 @@ wasapi_stream_init(cubeb * context, cube
   }
 
   std::unique_ptr<cubeb_stream, decltype(&wasapi_stream_destroy)> stm(new cubeb_stream(), wasapi_stream_destroy);
 
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
+
+  if (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_VOICE ||
+      stm->input_stream_params.prefs & CUBEB_STREAM_PREF_VOICE) {
+    stm->role = eCommunications;
+  } else {
+    stm->role = eConsole;
+  }
+
   if (input_stream_params) {
     stm->input_stream_params = *input_stream_params;
     stm->input_device = utf8_to_wstr(reinterpret_cast<char const *>(input_device));
   }
   if (output_stream_params) {
     stm->output_stream_params = *output_stream_params;
     stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
   }