Bug 1285541 - Update cubeb to revision 2a5fd74b. r=padenot,achronop
authorPaul Adenot <paul@paul.cx>
Wed, 20 Jul 2016 15:02:29 +0200
changeset 306359 3027825ee1fdf541b4060523cb5613f9b85f1147
parent 306358 43682f11a9cc57ef1076a29f5d5c8119de7cbfaf
child 306360 32050e8124afbca3b4bfc4fca30059abb9029229
push id30484
push usercbook@mozilla.com
push dateMon, 25 Jul 2016 13:51:04 +0000
treeherdermozilla-central@e23f2ec25e96 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot, achronop
bugs1285541
milestone50.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 1285541 - Update cubeb to revision 2a5fd74b. r=padenot,achronop Patches by padenot and achronop. MozReview-Commit-ID: G00OFgHdKne
CLOBBER
media/libcubeb/README_MOZILLA
media/libcubeb/include/cubeb.h
media/libcubeb/src/cubeb.c
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiotrack.c
media/libcubeb/src/cubeb_audiounit.c
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_jack.cpp
media/libcubeb/src/cubeb_opensl.c
media/libcubeb/src/cubeb_osx_run_loop.h
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_resampler_internal.h
media/libcubeb/src/cubeb_ring_array.h
media/libcubeb/src/cubeb_sndio.c
media/libcubeb/src/cubeb_wasapi.cpp
media/libcubeb/src/cubeb_winmm.c
media/libcubeb/tests/test_audio.cpp
media/libcubeb/tests/test_duplex.cpp
media/libcubeb/tests/test_latency.cpp
media/libcubeb/tests/test_record.cpp
media/libcubeb/tests/test_sanity.cpp
media/libcubeb/tests/test_tone.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1287946 - clobber due to generated SDK headers changing (bug 1182840)
+Bug 1285541 - Clobber needed because this patch renames a file.
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was b8aebef33a096640d6cbd7a6fa568bccef1b339c.
+The git commit ID used was 2a5fd74b1122ff4e4c445c1e092932be1273646e.
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -27,20 +27,20 @@ extern "C" {
     to the speakers, with minimal latency and the proper sample-rate for the
     platform.
 
     @code
     cubeb * app_ctx;
     cubeb_init(&app_ctx, "Example Application");
     int rv;
     int rate;
-    int latency_ms;
+    int latency_frames;
     uint64_t ts;
 
-    rv = cubeb_get_min_latency(app_ctx, output_params, &latency_ms);
+    rv = cubeb_get_min_latency(app_ctx, output_params, &latency_frames);
     if (rv != CUBEB_OK) {
       fprintf(stderr, "Could not get minimum latency");
       return rv;
     }
 
     rv = cubeb_get_preferred_sample_rate(app_ctx, output_params, &rate);
     if (rv != CUBEB_OK) {
       fprintf(stderr, "Could not get preferred sample-rate");
@@ -56,17 +56,17 @@ extern "C" {
     output_params.format = CUBEB_SAMPLE_FLOAT32NE;
     output_params.rate = rate;
     output_params.channels = 1;
 
     cubeb_stream * stm;
     rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
                            NULL, input_params,
                            NULL, output_params,
-                           latency_ms,
+                           latency_frames,
                            data_cb, state_cb,
                            NULL);
     if (rv != CUBEB_OK) {
       fprintf(stderr, "Could not open the stream");
       return rv;
     }
 
     rv = cubeb_stream_start(stm);
@@ -279,18 +279,18 @@ typedef struct {
 
   cubeb_device_fmt format;    /**< Sample format supported. */
   cubeb_device_fmt default_format; /**< The default sample format for this device. */
   unsigned int max_channels;  /**< Channels. */
   unsigned int default_rate;  /**< Default/Preferred sample rate. */
   unsigned int max_rate;      /**< Maximum sample rate supported. */
   unsigned int min_rate;      /**< Minimum sample rate supported. */
 
-  unsigned int latency_lo_ms; /**< Lowest possible latency in milliseconds. */
-  unsigned int latency_hi_ms; /**< Higest possible latency in milliseconds. */
+  unsigned int latency_lo; /**< Lowest possible latency in frames. */
+  unsigned int latency_hi; /**< Higest possible latency in frames. */
 } cubeb_device_info;
 
 /** Device collection. */
 typedef struct {
   uint32_t count;                 /**< Device count in collection. */
   cubeb_device_info * device[1];   /**< Array of pointers to device info. */
 } cubeb_device_collection;
 
@@ -358,29 +358,30 @@ char const * cubeb_get_backend_id(cubeb 
     @param context A pointer to the cubeb context.
     @param max_channels The maximum number of channels.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED
     @retval CUBEB_ERROR */
 int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
 
-/** Get the minimal latency value, in milliseconds, that is guaranteed to work
+/** Get the minimal latency value, in frames, that is guaranteed to work
     when creating a stream for the specified sample rate. This is platform,
     hardware and backend dependant.
     @param context A pointer to the cubeb context.
     @param params On some backends, the minimum achievable latency depends on
                   the characteristics of the stream.
-    @param latency_ms The latency value, in ms, to pass to cubeb_stream_init.
+    @param latency_frames The latency value, in frames, to pass to
+                          cubeb_stream_init.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED */
 int cubeb_get_min_latency(cubeb * context,
                           cubeb_stream_params params,
-                          uint32_t * latency_ms);
+                          uint32_t * latency_frames);
 
 /** Get the preferred sample rate for this backend: this is hardware and
     platform dependant, 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 */
@@ -399,35 +400,35 @@ void cubeb_destroy(cubeb * context);
     @param input_device Device for the input side of the stream. If NULL the
                         default input device is used.
     @param input_stream_params Parameters for the input side of the stream, or
                                NULL if this stream is output only.
     @param output_device Device for the output side of the stream. If NULL the
                          default output device is used.
     @param output_stream_params Parameters for the output side of the stream, or
                                 NULL if this stream is input only.
-    @param latency Approximate stream latency in milliseconds.  Valid range
-                   is [1, 2000].
+    @param latency Stream latency in frames.  Valid range
+                   is [1, 96000].
     @param data_callback Will be called to preroll data before playback is
                          started by cubeb_stream_start.
     @param state_callback A pointer to a state callback.
     @param user_ptr A pointer that will be passed to the callbacks. This pointer
                     must outlive the life time of the stream.
     @retval CUBEB_OK
     @retval CUBEB_ERROR
     @retval CUBEB_ERROR_INVALID_FORMAT
     @retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
 int cubeb_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,
+                      unsigned int latency_frames,
                       cubeb_data_callback data_callback,
                       cubeb_state_callback state_callback,
                       void * user_ptr);
 
 /** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
     stream.
     @param stream The stream to destroy. */
 void cubeb_stream_destroy(cubeb_stream * stream);
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -102,17 +102,17 @@ validate_stream_params(cubeb_stream_para
   return CUBEB_ERROR_INVALID_FORMAT;
 }
 
 
 
 int
 validate_latency(int latency)
 {
-  if (latency < 1 || latency > 2000) {
+  if (latency < 1 || latency > 96000) {
     return CUBEB_ERROR_INVALID_PARAMETER;
   }
   return CUBEB_OK;
 }
 
 int
 cubeb_init(cubeb ** context, char const * context_name)
 {
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -769,24 +769,26 @@ alsa_destroy(cubeb * ctx)
 static void alsa_stream_destroy(cubeb_stream * stm);
 
 static int
 alsa_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,
+                 unsigned int latency_frames,
                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                  void * user_ptr)
 {
   cubeb_stream * stm;
   int r;
   snd_pcm_format_t format;
   snd_pcm_uframes_t period_size;
+  int latency_us = 0;
+
 
   assert(ctx && stream);
 
   if (input_stream_params) {
     /* Capture support not yet implemented. */
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
@@ -840,26 +842,29 @@ alsa_stream_init(cubeb * ctx, cubeb_stre
   if (r < 0) {
     alsa_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   r = snd_pcm_nonblock(stm->pcm, 1);
   assert(r == 0);
 
+  latency_us = latency_frames * 1e6 / stm->params.rate;
+
   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
      possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
      Only resort to this hack if the handle_underrun workaround failed. */
   if (!ctx->local_config && ctx->is_pa) {
-    latency = latency < 500 ? 500 : latency;
+    const int min_latency = 5e5;
+    latency_us = latency_us < min_latency ? min_latency: latency_us;
   }
 
   r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                          stm->params.channels, stm->params.rate, 1,
-                         latency * 1000);
+                         latency_us);
   if (r < 0) {
     alsa_stream_destroy(stm);
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size);
   assert(r == 0);
 
@@ -994,21 +999,21 @@ alsa_get_preferred_sample_rate(cubeb * c
   }
 
   snd_pcm_close(pcm);
 
   return CUBEB_OK;
 }
 
 static int
-alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
-  /* This is found to be an acceptable minimum, even on a super low-end
+  /* 40ms is found to be an acceptable minimum, even on a super low-end
    * machine. */
-  *latency_ms = 40;
+  *latency_frames = 40 * params.rate / 1000;
 
   return CUBEB_OK;
 }
 
 static int
 alsa_stream_start(cubeb_stream * stm)
 {
   cubeb * ctx;
--- a/media/libcubeb/src/cubeb_audiotrack.c
+++ b/media/libcubeb/src/cubeb_audiotrack.c
@@ -245,19 +245,16 @@ audiotrack_get_min_latency(cubeb * ctx, 
    * audiotrack_stream_init), so this value is not going to be used. */
   int r;
 
   r = audiotrack_get_min_frame_count(ctx, &params, (int *)latency_ms);
   if (r != CUBEB_OK) {
     return CUBEB_ERROR;
   }
 
-  /* Convert to milliseconds. */
-  *latency_ms = *latency_ms * 1000 / params.rate;
-
   return CUBEB_OK;
 }
 
 static int
 audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   status_t r;
 
rename from media/libcubeb/src/cubeb_audiounit.c
rename to media/libcubeb/src/cubeb_audiounit.cpp
--- a/media/libcubeb/src/cubeb_audiounit.c
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -22,16 +22,18 @@
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 #include "cubeb_panner.h"
 #if !TARGET_OS_IPHONE
 #include "cubeb_osx_run_loop.h"
 #endif
 #include "cubeb_resampler.h"
 #include "cubeb_ring_array.h"
+#include "cubeb_utils.h"
+#include <algorithm>
 
 #if !defined(kCFCoreFoundationVersionNumber10_7)
 /* From CoreFoundation CFBase.h */
 #define kCFCoreFoundationVersionNumber10_7 635.00
 #endif
 
 #if !TARGET_OS_IPHONE && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
 #define AudioComponent Component
@@ -60,31 +62,96 @@ typedef UInt32  AudioFormatFlags;
 #ifdef LOGGING_ENABLED
 #define LOG(...) do {                           \
     fprintf(stderr, __VA_ARGS__);               \
   } while(0)
 #else
 #define LOG(...)
 #endif
 
-static struct cubeb_ops const audiounit_ops;
+
+/* Testing empirically, some headsets report a minimal latency that is very
+ * low, but this does not work in practice. Lie and say the minimum is 256
+ * frames. */
+const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
+
+extern cubeb_ops const audiounit_ops;
 
 struct cubeb {
-  struct cubeb_ops const * ops;
+  cubeb_ops const * ops;
   pthread_mutex_t mutex;
   int active_streams;
   int limit_streams;
   cubeb_device_collection_changed_callback collection_changed_callback;
   void * collection_changed_user_ptr;
   /* Differentiate input from output devices. */
   cubeb_device_type collection_changed_devtype;
   uint32_t devtype_device_count;
   AudioObjectID * devtype_device_array;
 };
 
+class auto_array_wrapper
+{
+public:
+  explicit auto_array_wrapper(auto_array<float> * ar)
+  : float_ar(ar)
+  , short_ar(nullptr)
+  {assert((float_ar && !short_ar) || (!float_ar && short_ar));}
+
+  explicit auto_array_wrapper(auto_array<short> * ar)
+  : float_ar(nullptr)
+  , short_ar(ar)
+  {assert((float_ar && !short_ar) || (!float_ar && short_ar));}
+
+  ~auto_array_wrapper() {
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    delete float_ar;
+    delete short_ar;
+  }
+
+  void push(void * elements, size_t length){
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    if (float_ar)
+      return float_ar->push(static_cast<float*>(elements), length);
+    return short_ar->push(static_cast<short*>(elements), length);
+  }
+
+  size_t length() const {
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    if (float_ar)
+      return float_ar->length();
+    return short_ar->length();
+  }
+
+  void push_silence(size_t length) {
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    if (float_ar)
+      return float_ar->push_silence(length);
+    return short_ar->push_silence(length);
+  }
+
+  bool pop(void * elements, size_t length) {
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    if (float_ar)
+      return float_ar->pop(static_cast<float*>(elements), length);
+    return short_ar->pop(static_cast<short*>(elements), length);
+  }
+
+  void * data() const {
+    assert((float_ar && !short_ar) || (!float_ar && short_ar));
+    if (float_ar)
+      return float_ar->data();
+    return short_ar->data();
+  }
+
+private:
+  auto_array<float> * float_ar;
+  auto_array<short> * short_ar;
+};
+
 struct cubeb_stream {
   cubeb * context;
   cubeb_data_callback data_callback;
   cubeb_state_callback state_callback;
   cubeb_device_changed_callback device_changed_callback;
   /* User pointer of data_callback */
   void * user_ptr;
   /* Format descriptions */
@@ -93,17 +160,17 @@ struct cubeb_stream {
   /* I/O AudioUnits */
   AudioUnit input_unit;
   AudioUnit output_unit;
   /* Sample rate of input device*/
   Float64 input_hw_rate;
   pthread_mutex_t mutex;
   /* Hold the input samples in every
    * input callback iteration */
-  ring_array input_buffer_array;
+  auto_array_wrapper * input_linear_buffer;
   /* Frames on input buffer */
   uint32_t input_buffer_frames;
   /* Frame counters */
   uint64_t frames_played;
   uint64_t frames_queued;
   uint64_t frames_read;
   int shutdown;
   int draining;
@@ -153,89 +220,106 @@ audiotimestamp_to_latency(AudioTimeStamp
 
 static void
 audiounit_make_silent(AudioBuffer * ioData)
 {
   memset(ioData->mData, 0, ioData->mDataByteSize);
 }
 
 static OSStatus
-audiounit_input_callback(void * user_ptr,
-                         AudioUnitRenderActionFlags * flags,
-                         AudioTimeStamp const * tstamp,
-                         UInt32 bus,
-                         UInt32 input_frames,
-                         AudioBufferList * bufs)
+audiounit_render_input(cubeb_stream * stm,
+                       AudioUnitRenderActionFlags * flags,
+                       AudioTimeStamp const * tstamp,
+                       UInt32 bus,
+                       UInt32 input_frames)
 {
-  cubeb_stream * stm = user_ptr;
-  long outframes, frames;
-
-  pthread_mutex_lock(&stm->mutex);
-
-  assert(stm->input_unit != NULL);
-  assert(AU_IN_BUS == bus);
-
-  if (stm->shutdown) {
-    pthread_mutex_unlock(&stm->mutex);
-    return noErr;
-  }
-
-  /* Get next store buffer from ring array */
-  AudioBuffer * input_buffer = ring_array_get_free_buffer(&stm->input_buffer_array);
-  if (input_buffer == NULL) {
-    LOG("input: Ring array is full drop one buffer\n");
-    ring_array_get_data_buffer(&stm->input_buffer_array);
-
-    input_buffer = ring_array_get_free_buffer(&stm->input_buffer_array);
-    assert(input_buffer);
-  }
-
+  /* Create the AudioBufferList to store input. */
   AudioBufferList input_buffer_list;
-  input_buffer_list.mBuffers[0] = *input_buffer;
+  input_buffer_list.mBuffers[0].mDataByteSize =
+      stm->input_desc.mBytesPerFrame * stm->input_buffer_frames;
+  input_buffer_list.mBuffers[0].mData = nullptr;
+  input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
   input_buffer_list.mNumberBuffers = 1;
 
   /* Render input samples */
   OSStatus r = AudioUnitRender(stm->input_unit,
                                flags,
                                tstamp,
                                bus,
                                input_frames,
                                &input_buffer_list);
+
   if (r != noErr) {
-    LOG("Input AudioUnitRender failed with error=%d", r);
-    audiounit_make_silent(input_buffer);
+    LOG("Input AudioUnitRender failed with error=%d\n", r);
+    audiounit_make_silent(&input_buffer_list.mBuffers[0]);
     return r;
   }
 
+  /* Copy input data in linear buffer. */
+  stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
+                                 input_frames * stm->input_desc.mChannelsPerFrame);
+
   LOG("- input:  buffers %d, size %d, channels %d, frames %d\n",
       input_buffer_list.mNumberBuffers,
       input_buffer_list.mBuffers[0].mDataByteSize,
       input_buffer_list.mBuffers[0].mNumberChannels,
       input_frames);
 
+  /* Advance input frame counter. */
   assert(input_frames > 0);
   stm->frames_read += input_frames;
 
+  return noErr;
+}
+
+static OSStatus
+audiounit_input_callback(void * user_ptr,
+                         AudioUnitRenderActionFlags * flags,
+                         AudioTimeStamp const * tstamp,
+                         UInt32 bus,
+                         UInt32 input_frames,
+                         AudioBufferList * bufs)
+{
+  cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
+  long outframes, frames;
+
+  pthread_mutex_lock(&stm->mutex);
+
+  assert(stm->input_unit != NULL);
+  assert(AU_IN_BUS == bus);
+
+  if (stm->shutdown) {
+    LOG("- input shutdown\n");
+    pthread_mutex_unlock(&stm->mutex);
+    return noErr;
+  }
+
+  OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
+  if (r != noErr) {
+    return r;
+  }
+
   // Full Duplex. We'll call data_callback in the AudioUnit output callback.
   if (stm->output_unit != NULL) {
     // User callback will be called by output callback
     pthread_mutex_unlock(&stm->mutex);
     return noErr;
   }
 
   /* Input only. Call the user callback through resampler.
      Resampler will deliver input buffer in the correct rate. */
   frames = input_frames;
-  input_buffer = ring_array_get_data_buffer(&stm->input_buffer_array);
-  assert(input_buffer && "fetch buffer is null in the input");
+  assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
   outframes = cubeb_resampler_fill(stm->resampler,
-                                   input_buffer->mData,
+                                   stm->input_linear_buffer->data(),
                                    &frames,
                                    NULL,
                                    0);
+  // Reset input buffer
+  stm->input_linear_buffer->pop(nullptr, frames * stm->input_desc.mChannelsPerFrame);
 
   if (outframes < 0 || outframes != input_frames) {
     stm->shutdown = 1;
     pthread_mutex_unlock(&stm->mutex);
     return noErr;
   }
 
   pthread_mutex_unlock(&stm->mutex);
@@ -248,28 +332,29 @@ audiounit_output_callback(void * user_pt
                           AudioTimeStamp const * tstamp,
                           UInt32 bus,
                           UInt32 output_frames,
                           AudioBufferList * outBufferList)
 {
   assert(AU_OUT_BUS == bus);
   assert(outBufferList->mNumberBuffers == 1);
 
-  cubeb_stream * stm = user_ptr;
+  cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
 
   LOG("- output(%p): buffers %d, size %d, channels %d, frames %d\n", stm,
       outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
       outBufferList->mBuffers[0].mNumberChannels, output_frames);
 
   long outframes = 0, input_frames = 0;
   void * output_buffer = NULL, * input_buffer = NULL;
 
   pthread_mutex_lock(&stm->mutex);
 
   if (stm->shutdown) {
+    LOG("- output shutdown\n");
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     pthread_mutex_unlock(&stm->mutex);
     return noErr;
   }
 
   stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
   if (stm->draining) {
     OSStatus r = AudioOutputUnitStop(stm->output_unit);
@@ -281,55 +366,43 @@ audiounit_output_callback(void * user_pt
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
     pthread_mutex_unlock(&stm->mutex);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
   /* Get output buffer. */
   output_buffer = outBufferList->mBuffers[0].mData;
   /* If Full duplex get also input buffer */
-  AudioBuffer * input_aud_buf = NULL;
   if (stm->input_unit != NULL) {
     /* Output callback came first */
     if (stm->frames_read == 0) {
       LOG("Output callback came first send silent.\n");
-      audiounit_make_silent(&outBufferList->mBuffers[0]);
-      pthread_mutex_unlock(&stm->mutex);
-      return noErr;
+      stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
+                                            stm->input_desc.mChannelsPerFrame);
     }
     /* Input samples stored previously in input callback. */
-    input_aud_buf = ring_array_get_data_buffer(&stm->input_buffer_array);
-    if (input_aud_buf == NULL) {
-      LOG("Requested more output than input. "
-             "This is either a hole or we are after a stream stop and input thread stopped before output\n");
-      /* Provide silent input. Other than that we could provide silent output and exit without
-       * calling user callback. I do not prefer it because the user loose the control of
-       * the output. Also resampler loose frame counting and produce less frame than
-       * expected at some point in the future breaking an assert. */
-
-      /* Avoid here to allocate new memory since we are inside callback. Use the existing
-       * allocated buffers since the ring array is empty and the buffer is not used. */
-      input_aud_buf = ring_array_get_dummy_buffer(&stm->input_buffer_array);
-      audiounit_make_silent(input_aud_buf);
+    if (stm->input_linear_buffer->length() == 0) {
+      /* Do nothing, there should be enough pre-buffered data to consume. */
+      LOG("Input hole. Requested more input than ouput.\n");
     }
-    input_buffer = input_aud_buf->mData;
-    input_frames = stm->input_buffer_frames;
-    assert(stm->frames_read > 0);
+    // The input buffer
+    input_buffer = stm->input_linear_buffer->data();
+    // Number of input frames in the buffer
+    input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
   }
 
   /* Call user callback through resampler. */
   outframes = cubeb_resampler_fill(stm->resampler,
                                    input_buffer,
                                    input_buffer ? &input_frames : NULL,
                                    output_buffer,
                                    output_frames);
 
-  /* Cleanup the input buffer to make sure that we have fresh data. */
   if (input_buffer) {
-    audiounit_make_silent(input_aud_buf);
+    stm->input_linear_buffer->pop(nullptr, input_frames * stm->input_desc.mChannelsPerFrame);
   }
 
   if (outframes < 0) {
     stm->shutdown = 1;
     pthread_mutex_unlock(&stm->mutex);
     return noErr;
   }
 
@@ -353,26 +426,28 @@ audiounit_output_callback(void * user_pt
       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;
 }
 
-/*static*/ int
+extern "C" {
+int
 audiounit_init(cubeb ** context, char const * context_name)
 {
   cubeb * ctx;
   int r;
 
   *context = NULL;
 
-  ctx = calloc(1, sizeof(*ctx));
+  ctx = new cubeb;
   assert(ctx);
+  PodZero(ctx, 1);
 
   ctx->ops = &audiounit_ops;
 
   r = pthread_mutex_init(&ctx->mutex, NULL);
   assert(r == 0);
 
   ctx->active_streams = 0;
 
@@ -380,16 +455,17 @@ audiounit_init(cubeb ** context, char co
 #if !TARGET_OS_IPHONE
   cubeb_set_coreaudio_notification_runloop();
 #endif
 
   *context = ctx;
 
   return CUBEB_OK;
 }
+}
 
 static char const *
 audiounit_get_backend_id(cubeb * ctx)
 {
   return "audiounit";
 }
 
 #if !TARGET_OS_IPHONE
@@ -695,28 +771,29 @@ audiounit_get_max_channel_count(cubeb * 
   }
 
   *max_channels = stream_format.mChannelsPerFrame;
 #endif
   return CUBEB_OK;
 }
 
 static int
-audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
 #if TARGET_OS_IPHONE
   //TODO: [[AVAudioSession sharedInstance] inputLatency]
   return CUBEB_ERROR_NOT_SUPPORTED;
 #else
   AudioValueRange latency_range;
   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
     return CUBEB_ERROR;
   }
 
-  *latency_ms = (latency_range.mMinimum * 1000 + params.rate - 1) / params.rate;
+  *latency_frames = std::max<uint32_t>(latency_range.mMinimum,
+                                       SAFE_MIN_LATENCY_FRAMES);
 #endif
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
@@ -745,17 +822,17 @@ audiounit_get_preferred_sample_rate(cube
                                  NULL,
                                  &size,
                                  &fsamplerate);
 
   if (r != noErr) {
     return CUBEB_ERROR;
   }
 
-  *rate = (uint32_t)fsamplerate;
+  *rate = static_cast<uint32_t>(fsamplerate);
 #endif
   return CUBEB_OK;
 }
 
 static OSStatus audiounit_remove_device_listener(cubeb * context);
 
 static void
 audiounit_destroy(cubeb * ctx)
@@ -768,17 +845,17 @@ audiounit_destroy(cubeb * ctx)
     pthread_mutex_lock(&ctx->mutex);
     audiounit_remove_device_listener(ctx);
     pthread_mutex_unlock(&ctx->mutex);
   }
 
   int r = pthread_mutex_destroy(&ctx->mutex);
   assert(r == 0);
 
-  free(ctx);
+  delete ctx;
 }
 
 static void audiounit_stream_destroy(cubeb_stream * stm);
 
 static int
 audio_stream_desc_init(AudioStreamBasicDescription * ss,
                        const cubeb_stream_params * stream_params)
 {
@@ -857,56 +934,77 @@ audiounit_create_unit(AudioUnit * unit,
         is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32)) != noErr) {
     return CUBEB_ERROR;
   }
 
   if (device == NULL) {
     devid = audiounit_get_default_device_id(is_input ? CUBEB_DEVICE_TYPE_INPUT
                                                      : CUBEB_DEVICE_TYPE_OUTPUT);
   } else {
-    devid = (AudioDeviceID)device;
+    devid = reinterpret_cast<intptr_t>(device);
   }
   int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
                                  kAudioUnitScope_Global,
                                  is_input ? AU_IN_BUS : AU_OUT_BUS,
                                  &devid, sizeof(AudioDeviceID));
   if (err != noErr) {
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 static int
-audiounit_init_input_buffer_array(cubeb_stream * stream, uint32_t capacity)
+audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
 {
-  int r = ring_array_init(&stream->input_buffer_array,
-                          capacity,
-                          stream->input_desc.mBytesPerFrame,
-                          stream->input_desc.mChannelsPerFrame,
-                          stream->input_buffer_frames);
+  if (stream->input_desc.mFormatFlags == kAudioFormatFlagIsSignedInteger) {
+    stream->input_linear_buffer = new auto_array_wrapper(
+        new auto_array<short>(capacity *
+                              stream->input_buffer_frames *
+                              stream->input_desc.mChannelsPerFrame) );
+  } else {
+    stream->input_linear_buffer = new auto_array_wrapper(
+        new auto_array<float>(capacity *
+                              stream->input_buffer_frames *
+                              stream->input_desc.mChannelsPerFrame) );
+  }
 
-  return r;
+  if (!stream->input_linear_buffer) {
+    return CUBEB_ERROR;
+  }
+
+  assert(stream->input_linear_buffer->length() == 0);
+
+  // Pre-buffer silence if needed
+  if (capacity != 1) {
+    size_t silence_size = stream->input_buffer_frames *
+                          stream->input_desc.mChannelsPerFrame;
+    stream->input_linear_buffer->push_silence(silence_size);
+
+    assert(stream->input_linear_buffer->length() == silence_size);
+  }
+
+  return CUBEB_OK;
 }
 
 static void
-audiounit_destroy_input_buffer_array(cubeb_stream * stream)
+audiounit_destroy_input_linear_buffer(cubeb_stream * stream)
 {
-  ring_array_destroy(&stream->input_buffer_array);
+  delete stream->input_linear_buffer;
 }
 
 static int
 audiounit_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,
+                      unsigned int latency_frames,
                       cubeb_data_callback data_callback,
                       cubeb_state_callback state_callback,
                       void * user_ptr)
 {
   cubeb_stream * stm;
   AudioUnit input_unit;
   AudioUnit output_unit;
   int r;
@@ -940,18 +1038,19 @@ audiounit_stream_init(cubeb * context,
                               output_stream_params,
                               output_device);
     if (r != CUBEB_OK) {
       LOG("Create output stream failed\n");
       return r;
     }
   }
 
-  stm = calloc(1, sizeof(cubeb_stream));
+  stm = new cubeb_stream;
   assert(stm);
+  PodZero(stm, 1);
 
   /* These could be different in the future if we have both
    * full-duplex stream and different devices for input vs output. */
   stm->input_unit  = (input_stream_params != NULL) ? input_unit : NULL;
   stm->output_unit = (output_stream_params != NULL) ? output_unit : NULL;
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
@@ -963,39 +1062,46 @@ audiounit_stream_init(cubeb * context,
   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
   r = pthread_mutex_init(&stm->mutex, &attr);
   assert(r == 0);
   pthread_mutexattr_destroy(&attr);
 
   /* Init data members where necessary */
   stm->hw_latency_frames = UINT64_MAX;
 
-  /* Setup Input Stream! */
-  if (input_stream_params != NULL) {
-    size = sizeof(UInt32);
-    if (AudioUnitGetProperty(stm->input_unit,
-                             kAudioDevicePropertyBufferFrameSize,
-                             kAudioUnitScope_Input,
-                             AU_IN_BUS,
-                             &stm->input_buffer_frames,
-                             &size) != 0) {
+  /* 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. */
+
+  UInt32 default_frame_count;
+  size = sizeof(default_frame_count);
+  if (stm->output_unit) {
+    if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
+          kAudioUnitScope_Output, 0, &default_frame_count, &size) != 0) {
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
-
-    if (AudioUnitSetProperty(stm->input_unit,
-                             kAudioDevicePropertyBufferFrameSize,
-                             kAudioUnitScope_Output,
-                             AU_IN_BUS,
-                             &stm->input_buffer_frames,
-                             size) != 0) {
+  } else {
+    if (AudioUnitGetProperty(stm->input_unit, kAudioDevicePropertyBufferFrameSize,
+          kAudioUnitScope_Input, 0, &default_frame_count, &size) != 0) {
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
+  }
 
+  LOG("Default buffer size: %u frames\n", default_frame_count);
+
+  latency_frames = std::max(std::min<uint32_t>(latency_frames,
+                                               default_frame_count),
+                            SAFE_MIN_LATENCY_FRAMES);
+
+  LOG("Clamped buffer size: %u frames\n", latency_frames);
+
+  /* Setup Input Stream! */
+  if (input_stream_params != NULL) {
     /* Get input device sample rate. */
     AudioStreamBasicDescription input_hw_desc;
     size = sizeof(AudioStreamBasicDescription);
     if (AudioUnitGetProperty(stm->input_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Input,
                              AU_IN_BUS,
                              &input_hw_desc,
@@ -1007,16 +1113,33 @@ audiounit_stream_init(cubeb * context,
 
     /* Set format description according to the input params. */
     r = audio_stream_desc_init(&stm->input_desc, input_stream_params);
     if (r != CUBEB_OK) {
       audiounit_stream_destroy(stm);
       return r;
     }
 
+    // Use latency to calculate buffer size
+    if (stm->input_hw_rate == input_stream_params->rate) {
+      stm->input_buffer_frames = latency_frames;
+    } else {
+      stm->input_buffer_frames = (latency_frames * stm->input_hw_rate) / input_stream_params->rate;
+    }
+    LOG("Calculated input number of frames %u for latency %u\n", stm->input_buffer_frames, latency_frames);
+    if (AudioUnitSetProperty(stm->input_unit,
+                             kAudioDevicePropertyBufferFrameSize,
+                             kAudioUnitScope_Output,
+                             AU_IN_BUS,
+                             &stm->input_buffer_frames,
+                             sizeof(UInt32)) != 0) {
+      audiounit_stream_destroy(stm);
+      return CUBEB_ERROR;
+    }
+
     AudioStreamBasicDescription src_desc = stm->input_desc;
     /* Input AudioUnit must be configured with device's sample rate.
        we will resample inside input callback. */
     src_desc.mSampleRate = stm->input_hw_rate;
 
     if (AudioUnitSetProperty(stm->input_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
@@ -1039,17 +1162,17 @@ audiounit_stream_init(cubeb * context,
     }
 
     // Input only capacity
     unsigned int array_capacity = 1;
     if (output_stream_params) {
       // Full-duplex increase capacity
       array_capacity = 8;
     }
-    if (audiounit_init_input_buffer_array(stm, array_capacity) != CUBEB_OK) {
+    if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     assert(stm->input_unit != NULL);
     aurcbs_in.inputProc = audiounit_input_callback;
     aurcbs_in.inputProcRefCon = stm;
     if (AudioUnitSetProperty(stm->input_unit,
@@ -1067,26 +1190,61 @@ audiounit_stream_init(cubeb * context,
   /* Setup Output Stream! */
   if (output_stream_params != NULL) {
     r = audio_stream_desc_init(&stm->output_desc, output_stream_params);
     if (r != CUBEB_OK) {
       audiounit_stream_destroy(stm);
       return r;
     }
 
+    /* Get output device sample rate. */
+    AudioStreamBasicDescription output_hw_desc;
+    size = sizeof(AudioStreamBasicDescription);
+    memset(&output_hw_desc, 0, size);
+    if (AudioUnitGetProperty(stm->output_unit,
+                             kAudioUnitProperty_StreamFormat,
+                             kAudioUnitScope_Output,
+                             AU_OUT_BUS,
+                             &output_hw_desc,
+                             &size) != 0) {
+      audiounit_stream_destroy(stm);
+      return CUBEB_ERROR;
+    }
+
     if (AudioUnitSetProperty(stm->output_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Input,
                              AU_OUT_BUS,
                              &stm->output_desc,
                              sizeof(AudioStreamBasicDescription)) != 0) {
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
+    // Use latency to set number of frames in out buffer. Use the
+    // device sampling rate, internal resampler of audiounit will
+    // calculate the expected number of frames.
+    // Use latency to calculate buffer size
+    uint32_t output_buffer_frames = 0;
+    if (output_hw_desc.mSampleRate == output_stream_params->rate) {
+      output_buffer_frames = latency_frames;
+    } else {
+      output_buffer_frames = (latency_frames * output_hw_desc.mSampleRate) / output_stream_params->rate;
+    }
+    LOG("Calculated output number of frames %u for latency %u\n", output_buffer_frames, latency_frames);
+    if (AudioUnitSetProperty(stm->output_unit,
+                             kAudioDevicePropertyBufferFrameSize,
+                             kAudioUnitScope_Input,
+                             AU_OUT_BUS,
+                             &output_buffer_frames,
+                             sizeof(output_buffer_frames)) != 0) {
+      audiounit_stream_destroy(stm);
+      return CUBEB_ERROR;
+    }
+
     assert(stm->output_unit != NULL);
     aurcbs_out.inputProc = audiounit_output_callback;
     aurcbs_out.inputProcRefCon = stm;
     if (AudioUnitSetProperty(stm->output_unit,
                              kAudioUnitProperty_SetRenderCallback,
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_out,
@@ -1095,17 +1253,17 @@ audiounit_stream_init(cubeb * context,
       return CUBEB_ERROR;
     }
     LOG("Output audiounit init successfully.\n");
   }
 
   // Setting the latency doesn't work well for USB headsets (eg. plantronics).
   // Keep the default latency for now.
 #if 0
-  buffer_size = latency / 1000.0 * ss.mSampleRate;
+  buffer_size = latency;
 
   /* Get the range of latency this particular device can work with, and clamp
    * the requested latency to this acceptable range. */
 #if !TARGET_OS_IPHONE
   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
     audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
@@ -1198,17 +1356,17 @@ audiounit_stream_destroy(cubeb_stream * 
   stm->shutdown = 1;
 
   if (stm->input_unit != NULL) {
     AudioOutputUnitStop(stm->input_unit);
     AudioUnitUninitialize(stm->input_unit);
     AudioComponentInstanceDispose(stm->input_unit);
   }
 
-  audiounit_destroy_input_buffer_array(stm);
+  audiounit_destroy_input_linear_buffer(stm);
 
   if (stm->output_unit != NULL) {
     AudioOutputUnitStop(stm->output_unit);
     AudioUnitUninitialize(stm->output_unit);
     AudioComponentInstanceDispose(stm->output_unit);
   }
 
   cubeb_resampler_destroy(stm->resampler);
@@ -1220,17 +1378,17 @@ audiounit_stream_destroy(cubeb_stream * 
   int r = pthread_mutex_destroy(&stm->mutex);
   assert(r == 0);
 
   pthread_mutex_lock(&stm->context->mutex);
   assert(stm->context->active_streams >= 1);
   stm->context->active_streams -= 1;
   pthread_mutex_unlock(&stm->context->mutex);
 
-  free(stm);
+  delete stm;
 }
 
 static int
 audiounit_stream_start(cubeb_stream * stm)
 {
   pthread_mutex_lock(&stm->context->mutex);
   stm->shutdown = 0;
   stm->draining = 0;
@@ -1342,17 +1500,17 @@ audiounit_stream_get_latency(cubeb_strea
                                    &device_safety_offset);
     if (r != noErr) {
       pthread_mutex_unlock(&stm->mutex);
       return CUBEB_ERROR;
     }
 
     /* This part is fixed and depend on the stream parameter and the hardware. */
     stm->hw_latency_frames =
-      (uint32_t)(unit_latency_sec * stm->output_desc.mSampleRate)
+      static_cast<uint32_t>(unit_latency_sec * stm->output_desc.mSampleRate)
       + device_latency_frames
       + device_safety_offset;
   }
 
   *latency = stm->hw_latency_frames + stm->current_latency_frames;
   pthread_mutex_unlock(&stm->mutex);
 
   return CUBEB_OK;
@@ -1414,37 +1572,33 @@ int audiounit_stream_get_current_device(
   };
 
   *device = NULL;
 
   if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
     return CUBEB_ERROR;
   }
 
-  *device = malloc(sizeof(cubeb_device));
+  *device = new cubeb_device;
   if (!*device) {
     return CUBEB_ERROR;
   }
+  PodZero(*device, 1);
 
   size = sizeof(UInt32);
   /* This fails with some USB headset, so simply return an empty string. */
   r = AudioObjectGetPropertyData(output_device_id,
                                  &datasource_address,
                                  0, NULL, &size, &data);
   if (r != noErr) {
     size = 0;
     data = 0;
   }
 
-  (*device)->output_name = malloc(size + 1);
-  if (!(*device)->output_name) {
-    return CUBEB_ERROR;
-  }
-
-  (*device)->output_name = malloc(size + 1);
+  (*device)->output_name = new char[size + 1];
   if (!(*device)->output_name) {
     return CUBEB_ERROR;
   }
 
   // Turn the four chars packed into a uint32 into a string
   strdata[0] = (char)(data >> 24);
   strdata[1] = (char)(data >> 16);
   strdata[2] = (char)(data >> 8);
@@ -1460,17 +1614,17 @@ int audiounit_stream_get_current_device(
   size = sizeof(UInt32);
   r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data);
   if (r != noErr) {
     LOG("Error when getting device !\n");
     size = 0;
     data = 0;
   }
 
-  (*device)->input_name = malloc(size + 1);
+  (*device)->input_name = new char[size + 1];
   if (!(*device)->input_name) {
     return CUBEB_ERROR;
   }
 
   // Turn the four chars packed into a uint32 into a string
   strdata[0] = (char)(data >> 24);
   strdata[1] = (char)(data >> 16);
   strdata[2] = (char)(data >> 8);
@@ -1481,19 +1635,19 @@ int audiounit_stream_get_current_device(
 
   return CUBEB_OK;
 #endif
 }
 
 int audiounit_stream_device_destroy(cubeb_stream * stream,
                                     cubeb_device * device)
 {
-  free(device->output_name);
-  free(device->input_name);
-  free(device);
+  delete [] device->output_name;
+  delete [] device->input_name;
+  delete device;
   return CUBEB_OK;
 }
 
 int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
                                                       cubeb_device_changed_callback  device_changed_callback)
 {
   /* Note: second register without unregister first causes 'nope' error.
    * Current implementation requires unregister before register a new cb. */
@@ -1523,27 +1677,27 @@ audiounit_get_devices(AudioObjectID ** d
                                      kAudioObjectPropertyScopeGlobal,
                                      kAudioObjectPropertyElementMaster };
 
   ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size);
   if (ret != noErr) {
     return ret;
   }
 
-  *count = (uint32_t)(size / sizeof(AudioObjectID));
+  *count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
   if (size >= sizeof(AudioObjectID)) {
     if (*devices != NULL) {
-      free(*devices);
+      delete [] (*devices);
     }
-    *devices = malloc(size);
-    memset(*devices, 0, size);
+    *devices = new AudioObjectID[*count];
+    PodZero(*devices, *count);
 
     ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices);
     if (ret != noErr) {
-      free(*devices);
+      delete [] (*devices);
       *devices = NULL;
     }
   } else {
     *devices = NULL;
     ret = -1;
   }
 
   return ret;
@@ -1555,37 +1709,37 @@ audiounit_strref_to_cstr_utf8(CFStringRe
   CFIndex len, size;
   char * ret;
   if (strref == NULL) {
     return NULL;
   }
 
   len = CFStringGetLength(strref);
   size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
-  ret = malloc(size);
+  ret = new char[size];
 
   if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
-    free(ret);
+    delete [] ret;
     ret = NULL;
   }
 
   return ret;
 }
 
 static uint32_t
 audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
 {
   AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
   UInt32 size = 0;
   uint32_t i, ret = 0;
 
   adr.mSelector = kAudioDevicePropertyStreamConfiguration;
 
   if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
-    AudioBufferList * list = alloca(size);
+    AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
     if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
       for (i = 0; i < list->mNumberBuffers; i++)
         ret += list->mBuffers[i].mNumberChannels;
     }
   }
 
   return ret;
 }
@@ -1606,30 +1760,30 @@ audiounit_get_available_samplerate(Audio
   }
 
   adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
   UInt32 size = 0;
   AudioValueRange range;
   if (AudioObjectHasProperty(devid, &adr) &&
       AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
     uint32_t i, count = size / sizeof(AudioValueRange);
-    AudioValueRange * ranges = malloc(size);
+    AudioValueRange * ranges = new AudioValueRange[count];
     range.mMinimum = 9999999999.0;
     range.mMaximum = 0.0;
     if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) {
       for (i = 0; i < count; i++) {
         if (ranges[i].mMaximum > range.mMaximum)
           range.mMaximum = ranges[i].mMaximum;
         if (ranges[i].mMinimum < range.mMinimum)
           range.mMinimum = ranges[i].mMinimum;
       }
     }
-    free(ranges);
-    *max = (uint32_t)range.mMaximum;
-    *min = (uint32_t)range.mMinimum;
+    delete [] ranges;
+    *max = static_cast<uint32_t>(range.mMaximum);
+    *min = static_cast<uint32_t>(range.mMinimum);
   } else {
     *min = *max = 0;
   }
 
 }
 
 static UInt32
 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope)
@@ -1678,17 +1832,18 @@ audiounit_create_device_from_hwdev(Audio
     return NULL;
   }
 
   ch = audiounit_get_channel_count(devid, adr.mScope);
   if (ch == 0) {
     return NULL;
   }
 
-  ret = calloc(1, sizeof(cubeb_device_info));
+  ret = new cubeb_device_info;
+  PodZero(ret, 1);
 
   size = sizeof(CFStringRef);
   adr.mSelector = kAudioDevicePropertyDeviceUID;
   if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
     ret->device_id = audiounit_strref_to_cstr_utf8(str);
     ret->devid = (cubeb_devid)(size_t)devid;
     ret->group_id = strdup(ret->device_id);
     CFRelease(str);
@@ -1728,32 +1883,32 @@ audiounit_create_device_from_hwdev(Audio
   }
 
   ret->type = type;
   ret->state = CUBEB_DEVICE_STATE_ENABLED;
   ret->preferred = (devid == audiounit_get_default_device_id(type)) ?
     CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
 
   ret->max_channels = ch;
-  ret->format = CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
+  ret->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
   /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
   ret->default_format = CUBEB_DEVICE_FMT_F32NE;
   audiounit_get_available_samplerate(devid, adr.mScope,
       &ret->min_rate, &ret->max_rate, &ret->default_rate);
 
   latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
 
   adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
   size = sizeof(AudioValueRange);
   if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) {
-    ret->latency_lo_ms = ((latency + range.mMinimum) * 1000) / ret->default_rate;
-    ret->latency_hi_ms = ((latency + range.mMaximum) * 1000) / ret->default_rate;
+    ret->latency_lo = latency + range.mMinimum;
+    ret->latency_hi = latency + range.mMaximum;
   } else {
-    ret->latency_lo_ms = 10;  /* Default to  10ms */
-    ret->latency_hi_ms = 100; /* Default to 100ms */
+    ret->latency_lo = 10 * ret->default_rate / 1000;  /* Default to  10ms */
+    ret->latency_hi = 100 * ret->default_rate / 1000; /* Default to 100ms */
   }
 
   return ret;
 }
 
 static int
 audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
                             cubeb_device_collection ** collection)
@@ -1761,18 +1916,18 @@ audiounit_enumerate_devices(cubeb * cont
   AudioObjectID * hwdevs = NULL;
   uint32_t i, hwdevcount = 0;
   OSStatus err;
 
   if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) {
     return CUBEB_ERROR;
   }
 
-  *collection = malloc(sizeof(cubeb_device_collection) +
-      sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0));
+  *collection = static_cast<cubeb_device_collection *>(malloc(sizeof(cubeb_device_collection) +
+      sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)));
   (*collection)->count = 0;
 
   if (hwdevcount > 0) {
     cubeb_device_info * cur;
 
     if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
       for (i = 0; i < hwdevcount; i++) {
         if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT)) != NULL)
@@ -1783,17 +1938,17 @@ audiounit_enumerate_devices(cubeb * cont
     if (type & CUBEB_DEVICE_TYPE_INPUT) {
       for (i = 0; i < hwdevcount; i++) {
         if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_INPUT)) != NULL)
           (*collection)->device[(*collection)->count++] = cur;
       }
     }
   }
 
-  free(hwdevs);
+  delete [] hwdevs;
 
   return CUBEB_OK;
 }
 
 /* qsort compare method. */
 int compare_devid(const void * a, const void * b)
 {
   return (*(AudioObjectID*)a - *(AudioObjectID*)b);
@@ -1820,17 +1975,17 @@ audiounit_get_devices_of_type(cubeb_devi
   if (ret != noErr) {
     return 0;
   }
   /* Expected sorted but did not find anything in the docs. */
   qsort(devices, count, sizeof(AudioObjectID), compare_devid);
 
   if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
     if (devid_array) {
-      *devid_array = calloc(count, sizeof(AudioObjectID));
+      *devid_array = new AudioObjectID[count];
       assert(*devid_array);
       memcpy(*devid_array, &devices, count * sizeof(AudioObjectID));
     }
     return count;
   }
 
   AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) ?
                                          kAudioDevicePropertyScopeInput :
@@ -1842,17 +1997,17 @@ audiounit_get_devices_of_type(cubeb_devi
     /* For device in the given scope channel must be > 0. */
     if (audiounit_get_channel_count(devices[i], scope) > 0) {
       devices_in_scope[dev_count] = devices[i];
       ++dev_count;
     }
   }
 
   if (devid_array && dev_count > 0) {
-    *devid_array = calloc(dev_count, sizeof(AudioObjectID));
+    *devid_array = static_cast<AudioObjectID *>(calloc(dev_count, sizeof(AudioObjectID)));
     assert(*devid_array);
     memcpy(*devid_array, &devices_in_scope, dev_count * sizeof(AudioObjectID));
   }
   return dev_count;
 }
 
 static uint32_t
 audiounit_equal_arrays(AudioObjectID * left, AudioObjectID * right, uint32_t size)
@@ -1867,41 +2022,41 @@ audiounit_equal_arrays(AudioObjectID * l
 }
 
 static OSStatus
 audiounit_collection_changed_callback(AudioObjectID inObjectID,
                                       UInt32 inNumberAddresses,
                                       const AudioObjectPropertyAddress * inAddresses,
                                       void * inClientData)
 {
-  cubeb * context = inClientData;
+  cubeb * context = static_cast<cubeb *>(inClientData);
   pthread_mutex_lock(&context->mutex);
   if (context->collection_changed_callback == NULL) {
     /* Listener removed while waiting in mutex, abort. */
     pthread_mutex_unlock(&context->mutex);
     return noErr;
   }
 
   /* Differentiate input from output changes. */
   if (context->collection_changed_devtype == CUBEB_DEVICE_TYPE_INPUT ||
       context->collection_changed_devtype == CUBEB_DEVICE_TYPE_OUTPUT) {
     AudioObjectID * devices = NULL;
     uint32_t new_number_of_devices = audiounit_get_devices_of_type(context->collection_changed_devtype, &devices);
     /* When count is the same examine the devid for the case of coalescing. */
     if (context->devtype_device_count == new_number_of_devices &&
         audiounit_equal_arrays(devices, context->devtype_device_array, new_number_of_devices)) {
       /* Device changed for the other scope, ignore. */
-      free(devices);
+      delete [] devices;
       pthread_mutex_unlock(&context->mutex);
       return noErr;
     }
     /* Device on desired scope changed, reset counter and array. */
     context->devtype_device_count = new_number_of_devices;
     /* Free the old array before replace. */
-    free(context->devtype_device_array);
+    delete [] context->devtype_device_array;
     context->devtype_device_array = devices;
   }
 
   context->collection_changed_callback(context, context->collection_changed_user_ptr);
   pthread_mutex_unlock(&context->mutex);
   return noErr;
 }
 
@@ -1952,22 +2107,22 @@ audiounit_remove_device_listener(cubeb *
 
   /* Note: unregister a non registered cb is not a problem, not checking. */
   OSStatus ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
                                                    &devAddr,
                                                    audiounit_collection_changed_callback,
                                                    context);
   if (ret == noErr) {
     /* Reset all values. */
-    context->collection_changed_devtype = 0;
+    context->collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN;
     context->collection_changed_callback = NULL;
     context->collection_changed_user_ptr = NULL;
     context->devtype_device_count = 0;
     if (context->devtype_device_array) {
-      free(context->devtype_device_array);
+      delete [] context->devtype_device_array;
       context->devtype_device_array = NULL;
     }
   }
   return ret;
 }
 
 int audiounit_register_device_collection_changed(cubeb * context,
                                                  cubeb_device_type devtype,
@@ -1982,29 +2137,29 @@ int audiounit_register_device_collection
                                         user_ptr);
   } else {
     ret = audiounit_remove_device_listener(context);
   }
   pthread_mutex_unlock(&context->mutex);
   return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
 }
 
-static struct 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,
-  .enumerate_devices = audiounit_enumerate_devices,
-  .destroy = audiounit_destroy,
-  .stream_init = audiounit_stream_init,
-  .stream_destroy = audiounit_stream_destroy,
-  .stream_start = audiounit_stream_start,
-  .stream_stop = audiounit_stream_stop,
-  .stream_get_position = audiounit_stream_get_position,
-  .stream_get_latency = audiounit_stream_get_latency,
-  .stream_set_volume = audiounit_stream_set_volume,
-  .stream_set_panning = audiounit_stream_set_panning,
-  .stream_get_current_device = audiounit_stream_get_current_device,
-  .stream_device_destroy = audiounit_stream_device_destroy,
-  .stream_register_device_changed_callback = audiounit_stream_register_device_changed_callback,
-  .register_device_collection_changed = audiounit_register_device_collection_changed
+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,
+  /*.enumerate_devices =*/ audiounit_enumerate_devices,
+  /*.destroy =*/ audiounit_destroy,
+  /*.stream_init =*/ audiounit_stream_init,
+  /*.stream_destroy =*/ audiounit_stream_destroy,
+  /*.stream_start =*/ audiounit_stream_start,
+  /*.stream_stop =*/ audiounit_stream_stop,
+  /*.stream_get_position =*/ audiounit_stream_get_position,
+  /*.stream_get_latency =*/ audiounit_stream_get_latency,
+  /*.stream_set_volume =*/ audiounit_stream_set_volume,
+  /*.stream_set_panning =*/ audiounit_stream_set_panning,
+  /*.stream_get_current_device =*/ audiounit_stream_get_current_device,
+  /*.stream_device_destroy =*/ audiounit_stream_device_destroy,
+  /*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback,
+  /*.register_device_collection_changed =*/ audiounit_register_device_collection_changed
 };
--- a/media/libcubeb/src/cubeb_jack.cpp
+++ b/media/libcubeb/src/cubeb_jack.cpp
@@ -80,34 +80,34 @@ float_to_s16ne(int16_t * dst, float * sr
 }
 
 extern "C"
 {
 /*static*/ int jack_init (cubeb ** context, char const * context_name);
 }
 static char const * cbjack_get_backend_id(cubeb * context);
 static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
-static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms);
-static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms);
+static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames);
+static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
 static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
 static void cbjack_destroy(cubeb * context);
 static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch);
 static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes);
 static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes);
 static int cbjack_stream_device_destroy(cubeb_stream * stream,
                                         cubeb_device * device);
 static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
 static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
                                     cubeb_device_collection ** collection);
 static int cbjack_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,
+                              unsigned int latency_frames,
                               cubeb_data_callback data_callback,
                               cubeb_state_callback state_callback,
                               void * user_ptr);
 static void cbjack_stream_destroy(cubeb_stream * stream);
 static int cbjack_stream_start(cubeb_stream * stream);
 static int cbjack_stream_stop(cubeb_stream * stream);
 static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
 static int cbjack_stream_set_volume(cubeb_stream * stm, float volume);
@@ -302,20 +302,18 @@ cbjack_graph_order_callback(void * arg)
       if (port_latency > max_latency)
           max_latency = port_latency;
     }
     /* Cap minimum latency to 128 frames */
     if (max_latency < 128)
       max_latency = 128;
   }
 
-  if (cbjack_get_preferred_sample_rate(ctx, &rate) == CUBEB_ERROR)
-    ctx->jack_latency = (max_latency * 1000) / 48000;
-  else
-    ctx->jack_latency = (max_latency * 1000) / rate;
+  ctx->jack_latency = max_latency;
+
   return 0;
 }
 
 static int
 cbjack_process(jack_nframes_t nframes, void * arg)
 {
   cubeb * ctx = (cubeb *)arg;
   int t_jack_xruns = ctx->jack_xruns;
@@ -700,17 +698,17 @@ context_alloc_stream(cubeb * context, ch
 }
 
 static int
 cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                    cubeb_devid input_device,
                    cubeb_stream_params * input_stream_params,
                    cubeb_devid output_device,
                    cubeb_stream_params * output_stream_params,
-                   unsigned int latency,
+                   unsigned int latency_frames,
                    cubeb_data_callback data_callback,
                    cubeb_state_callback state_callback,
                    void * user_ptr)
 {
   int stream_actual_rate = 0;
   int jack_rate = api_jack_get_sample_rate(context->jack_client);
 
   if (output_stream_params
@@ -994,18 +992,18 @@ cbjack_enumerate_devices(cubeb * context
     context->devinfo[i]->state = CUBEB_DEVICE_STATE_ENABLED;
     context->devinfo[i]->preferred = CUBEB_DEVICE_PREF_ALL;
     context->devinfo[i]->format = CUBEB_DEVICE_FMT_F32NE;
     context->devinfo[i]->default_format = CUBEB_DEVICE_FMT_F32NE;
     context->devinfo[i]->max_channels = MAX_CHANNELS;
     context->devinfo[i]->min_rate = rate;
     context->devinfo[i]->max_rate = rate;
     context->devinfo[i]->default_rate = rate;
-    context->devinfo[i]->latency_lo_ms = 1;
-    context->devinfo[i]->latency_hi_ms = 10;
+    context->devinfo[i]->latency_lo = 0;
+    context->devinfo[i]->latency_hi = 0;
     i++;
   }
 
   if (type & CUBEB_DEVICE_TYPE_INPUT) {
     context->devinfo[i] = (cubeb_device_info *)malloc(sizeof(cubeb_device_info));
     context->devinfo[i]->device_id = strdup(j_in);
     context->devinfo[i]->devid = context->devinfo[i]->device_id;
     context->devinfo[i]->friendly_name = strdup(j_in);
@@ -1015,18 +1013,18 @@ cbjack_enumerate_devices(cubeb * context
     context->devinfo[i]->state = CUBEB_DEVICE_STATE_ENABLED;
     context->devinfo[i]->preferred = CUBEB_DEVICE_PREF_ALL;
     context->devinfo[i]->format = CUBEB_DEVICE_FMT_F32NE;
     context->devinfo[i]->default_format = CUBEB_DEVICE_FMT_F32NE;
     context->devinfo[i]->max_channels = MAX_CHANNELS;
     context->devinfo[i]->min_rate = rate;
     context->devinfo[i]->max_rate = rate;
     context->devinfo[i]->default_rate = rate;
-    context->devinfo[i]->latency_lo_ms = 1;
-    context->devinfo[i]->latency_hi_ms = 10;
+    context->devinfo[i]->latency_lo = 0;
+    context->devinfo[i]->latency_hi = 0;
     i++;
   }
 
   *collection = (cubeb_device_collection *)
                  malloc(sizeof(cubeb_device_collection) +
                         i * sizeof(cubeb_device_info *));
 
   (*collection)->count = i;
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -420,17 +420,17 @@ opensl_get_preferred_sample_rate(cubeb *
   if (*rate == 0) {
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 static int
-opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
    * We don't want to deal with JNI here (and we don't have Java on b2g anyways),
    * so we just dlopen the library and get the two symbols we need. */
 
   int r;
   void * libmedia;
   size_t (*get_primary_output_frame_count)(void);
@@ -470,17 +470,17 @@ opensl_get_min_latency(cubeb * ctx, cube
     if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) {
       return CUBEB_ERROR;
     }
   }
 
   /* To get a fast track in Android's mixer, we need to be at the native
    * samplerate, which is device dependant. Some devices might be able to
    * resample when playing a fast track, but it's pretty rare. */
-  *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000);
+  *latency_frames = NBUFS * primary_buffer_size;
 
   dlclose(libmedia);
 
   return CUBEB_OK;
 }
 
 static void
 opensl_destroy(cubeb * ctx)
@@ -497,36 +497,31 @@ opensl_destroy(cubeb * ctx)
 static void opensl_stream_destroy(cubeb_stream * stm);
 
 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,
+                   unsigned int latency_frames,
                    cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                    void * user_ptr)
 {
   cubeb_stream * stm;
 
   assert(ctx);
   assert(!input_stream_params && "not supported");
   if (input_device || output_device) {
     /* Device selection not yet implemented. */
     return CUBEB_ERROR_DEVICE_UNAVAILABLE;
   }
 
   *stream = NULL;
 
-  if (output_stream_params->channels < 1 || output_stream_params->channels > 32 ||
-      latency < 1 || latency > 2000) {
-    return CUBEB_ERROR_INVALID_FORMAT;
-  }
-
   SLDataFormat_PCM format;
 
   format.formatType = SL_DATAFORMAT_PCM;
   format.numChannels = output_stream_params->channels;
   // samplesPerSec is in milliHertz
   format.samplesPerSec = output_stream_params->rate * 1000;
   format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
   format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
@@ -549,17 +544,17 @@ opensl_stream_init(cubeb * ctx, cubeb_st
   assert(stm);
 
   stm->context = ctx;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
 
   stm->inputrate = output_stream_params->rate;
-  stm->latency = latency;
+  stm->latency = latency_frames;
   stm->stream_type = output_stream_params->stream_type;
   stm->framesize = output_stream_params->channels * sizeof(int16_t);
   stm->lastPosition = -1;
   stm->lastPositionTimeStamp = 0;
   stm->lastCompensativePosition = -1;
 
   int r = pthread_mutex_init(&stm->mutex, NULL);
   assert(r == 0);
@@ -589,21 +584,21 @@ opensl_stream_init(cubeb * ctx, cubeb_st
 #endif
   assert(NELEMS(ids) == NELEMS(req));
 
   uint32_t preferred_sampling_rate = stm->inputrate;
 #if defined(__ANDROID__)
   if (get_android_version() >= ANDROID_VERSION_MARSHMALLOW) {
     // Reset preferred samping rate to trigger fallback to native sampling rate.
     preferred_sampling_rate = 0;
-    if (opensl_get_min_latency(ctx, *output_stream_params, &latency) != CUBEB_OK) {
+    if (opensl_get_min_latency(ctx, *output_stream_params, &latency_frames) != CUBEB_OK) {
       // Default to AudioFlinger's advertised fast track latency of 10ms.
-      latency = 10;
+      latency_frames = 440;
     }
-    stm->latency = latency;
+    stm->latency = latency_frames;
   }
 #endif
 
   SLresult res = SL_RESULT_CONTENT_UNSUPPORTED;
   if (preferred_sampling_rate) {
     res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj, &source,
                                          &sink, NELEMS(ids), ids, req);
   }
@@ -622,17 +617,17 @@ opensl_stream_init(cubeb * ctx, cubeb_st
 
   if (res != SL_RESULT_SUCCESS) {
     opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   stm->outputrate = preferred_sampling_rate;
   stm->bytespersec = stm->outputrate * stm->framesize;
-  stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
+  stm->queuebuf_len = stm->framesize * latency_frames / NBUFS;
   // round up to the next multiple of stm->framesize, if needed.
   if (stm->queuebuf_len % stm->framesize) {
     stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
   }
 
   cubeb_stream_params params = *output_stream_params;
   params.rate = preferred_sampling_rate;
 
--- a/media/libcubeb/src/cubeb_osx_run_loop.h
+++ b/media/libcubeb/src/cubeb_osx_run_loop.h
@@ -6,9 +6,17 @@
  */
 
 /* On OSX 10.6 and after, the notification callbacks from the audio hardware are
  * called on the main thread. Setting the kAudioHardwarePropertyRunLoop property
  * to null tells the OSX to use a separate thread for that.
  *
  * This has to be called only once per process, so it is in a separate header
  * for easy integration in other code bases.  */
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
 void cubeb_set_coreaudio_notification_runloop();
+
+#if defined(__cplusplus)
+}
+#endif
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -577,20 +577,20 @@ pulse_get_preferred_sample_rate(cubeb * 
   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
   *rate = ctx->default_sink_info->sample_spec.rate;
 
   return CUBEB_OK;
 }
 
 static int
-pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   // According to PulseAudio developers, this is a safe minimum.
-  *latency_ms = 25;
+  *latency_frames = 25 * params.rate / 1000;
 
   return CUBEB_OK;
 }
 
 static void
 pulse_context_destroy(cubeb * ctx)
 {
   pa_operation * o;
@@ -665,22 +665,22 @@ create_pa_stream(cubeb_stream * stm,
   ss.rate = stream_params->rate;
   ss.channels = stream_params->channels;
 
   *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
   return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK;
 }
 
 static pa_buffer_attr
-set_buffering_attribute(unsigned int latency, pa_sample_spec * sample_spec)
+set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec)
 {
   pa_buffer_attr battr;
   battr.maxlength = -1;
   battr.prebuf    = -1;
-  battr.tlength   = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, sample_spec);
+  battr.tlength   = latency_frames * WRAP(pa_frame_size)(sample_spec);
   battr.minreq    = battr.tlength / 4;
   battr.fragsize  = battr.minreq;
 
   LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",
       battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
 
   return battr;
 }
@@ -688,17 +688,17 @@ set_buffering_attribute(unsigned int lat
 static int
 pulse_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,
+                  unsigned int latency_frames,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback,
                   void * user_ptr)
 {
   cubeb_stream * stm;
   pa_buffer_attr battr;
   int r;
 
@@ -729,17 +729,17 @@ pulse_stream_init(cubeb * context,
       return r;
     }
 
     stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
 
     WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
     WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm);
 
-    battr = set_buffering_attribute(latency, &stm->output_sample_spec);
+    battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
     WRAP(pa_stream_connect_playback)(stm->output_stream,
                                      output_device,
                                      &battr,
                                      PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
                                      PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
                                      NULL, NULL);
   }
 
@@ -752,17 +752,17 @@ pulse_stream_init(cubeb * context,
       return r;
     }
 
     stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
 
     WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
     WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
 
-    battr = set_buffering_attribute(latency, &stm->input_sample_spec);
+    battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
     WRAP(pa_stream_connect_record)(stm->input_stream,
                                    input_device,
                                    &battr,
                                    PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
                                    PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
   }
 
   r = wait_until_stream_ready(stm);
@@ -1051,18 +1051,18 @@ pulse_sink_info_cb(pa_context * context,
 
   devinfo->format = CUBEB_DEVICE_FMT_ALL;
   devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
   devinfo->max_channels = info->channel_map.channels;
   devinfo->min_rate = 1;
   devinfo->max_rate = PA_RATE_MAX;
   devinfo->default_rate = info->sample_spec.rate;
 
-  devinfo->latency_lo_ms = 25;
-  devinfo->latency_hi_ms = 400;
+  devinfo->latency_lo = 0;
+  devinfo->latency_hi = 0;
 
   pulse_ensure_dev_list_data_list_size (list_data);
   list_data->devinfo[list_data->count++] = devinfo;
 
   WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
 }
 
 static cubeb_device_state
@@ -1111,18 +1111,18 @@ pulse_source_info_cb(pa_context * contex
 
   devinfo->format = CUBEB_DEVICE_FMT_ALL;
   devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
   devinfo->max_channels = info->channel_map.channels;
   devinfo->min_rate = 1;
   devinfo->max_rate = PA_RATE_MAX;
   devinfo->default_rate = info->sample_spec.rate;
 
-  devinfo->latency_lo_ms = 1;
-  devinfo->latency_hi_ms = 10;
+  devinfo->latency_lo = 0;
+  devinfo->latency_hi = 0;
 
   pulse_ensure_dev_list_data_list_size (list_data);
   list_data->devinfo[list_data->count++] = devinfo;
 
   WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
 }
 
 static void
--- a/media/libcubeb/src/cubeb_resampler_internal.h
+++ b/media/libcubeb/src/cubeb_resampler_internal.h
@@ -258,17 +258,17 @@ public:
   }
 
   /** Returns the number of frames to pass in the input of the resampler to have
    * exactly `output_frame_count` resampled frames. This can return a number
    * slightly bigger than what is strictly necessary, but it guaranteed that the
    * number of output frames will be exactly equal. */
   uint32_t input_needed_for_output(uint32_t output_frame_count)
   {
-    return (uint32_t)ceilf((output_frame_count - samples_to_frames(resampling_in_buffer.length()))
+    return (uint32_t)ceilf((output_frame_count - samples_to_frames(resampling_out_buffer.length()))
                           * resampling_ratio);
 
   }
 
   /** Returns a pointer to the input buffer, that contains empty space for at
    * least `frame_count` elements. This is useful so that consumer can directly
    * write into the input buffer of the resampler. The pointer returned is
    * adjusted so that leftover data are not overwritten.
@@ -327,17 +327,17 @@ private:
   /** Storage for the input frames, to be resampled. Also contains
    * any unresampled frames after resampling. */
   auto_array<T> resampling_in_buffer;
   /* Storage for the resampled frames, to be passed back to the caller. */
   auto_array<T> resampling_out_buffer;
   /** Additional latency inserted into the pipeline for synchronisation. */
   uint32_t additional_latency;
   /** When `input_buffer` is called, this allows tracking the number of samples
-      that where in the buffer. */
+      that were in the buffer. */
   uint32_t leftover_samples;
 };
 
 /** This class allows delaying an audio stream by `frames` frames. */
 template<typename T>
 class delay_line : public processor {
 public:
   /** Constructor
--- a/media/libcubeb/src/cubeb_ring_array.h
+++ b/media/libcubeb/src/cubeb_ring_array.h
@@ -3,19 +3,17 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 
 #ifndef CUBEB_RING_ARRAY_H
 #define CUBEB_RING_ARRAY_H
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "cubeb_utils.h"
 
 /** Ring array of pointers is used to hold buffers. In case that
     asynchronous producer/consumer callbacks do not arrive in a
     repeated order the ring array stores the buffers and fetch
     them in the correct order. */
 
 typedef struct {
   AudioBuffer * buffer_array;   /**< Array that hold pointers of the allocated space for the buffers. */
@@ -29,20 +27,21 @@ single_audiobuffer_init(AudioBuffer * bu
                         uint32_t bytesPerFrame,
                         uint32_t channelsPerFrame,
                         uint32_t frames)
 {
   assert(buffer);
   assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
 
   size_t size = bytesPerFrame * frames;
-  buffer->mData = calloc(1, size);
+  buffer->mData = operator new(size);
   if (buffer->mData == NULL) {
     return CUBEB_ERROR;
   }
+  PodZero(static_cast<char*>(buffer->mData), size);
 
   buffer->mNumberChannels = channelsPerFrame;
   buffer->mDataByteSize = size;
 
   return CUBEB_OK;
 }
 
 /** Initialize the ring array.
@@ -59,17 +58,18 @@ ring_array_init(ring_array * ra,
   if (capacity == 0 || bytesPerFrame == 0 ||
       channelsPerFrame == 0 || framesPerBuffer == 0) {
     return CUBEB_ERROR_INVALID_PARAMETER;
   }
   ra->capacity = capacity;
   ra->tail = 0;
   ra->count = 0;
 
-  ra->buffer_array = calloc(ra->capacity, sizeof(AudioBuffer));
+  ra->buffer_array = new AudioBuffer[ra->capacity];
+  PodZero(ra->buffer_array, ra->capacity);
   if (ra->buffer_array == NULL) {
     return CUBEB_ERROR;
   }
 
   for (unsigned int i = 0; i < ra->capacity; ++i) {
     if (single_audiobuffer_init(&ra->buffer_array[i],
                                 bytesPerFrame,
                                 channelsPerFrame,
@@ -87,36 +87,36 @@ void
 ring_array_destroy(ring_array * ra)
 {
   assert(ra);
   if (ra->buffer_array == NULL){
     return;
   }
   for (unsigned int i = 0; i < ra->capacity; ++i) {
     if (ra->buffer_array[i].mData) {
-      free(ra->buffer_array[i].mData);
+      operator delete(ra->buffer_array[i].mData);
     }
   }
-  free(ra->buffer_array);
+  delete [] ra->buffer_array;
 }
 
 /** Get the allocated buffer to be stored with fresh data.
     @param ra The ring_array pointer.
     @retval Pointer of the allocated space to be stored with fresh data or NULL if full. */
 AudioBuffer *
 ring_array_get_free_buffer(ring_array * ra)
 {
   assert(ra && ra->buffer_array);
   assert(ra->buffer_array[0].mData != NULL);
   if (ra->count == ra->capacity) {
     return NULL;
   }
 
   assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail);
-  void * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
+  AudioBuffer * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
 
   ++ra->count;
   assert(ra->count <= ra->capacity);
 
   return ret;
 }
 
 /** Get the next available buffer with data.
@@ -126,17 +126,17 @@ AudioBuffer *
 ring_array_get_data_buffer(ring_array * ra)
 {
   assert(ra && ra->buffer_array);
   assert(ra->buffer_array[0].mData != NULL);
 
   if (ra->count == 0) {
     return NULL;
   }
-  void * ret = &ra->buffer_array[ra->tail];
+  AudioBuffer * ret = &ra->buffer_array[ra->tail];
 
   ra->tail = (ra->tail + 1) % ra->capacity;
   assert(ra->tail < ra->capacity);
 
   assert(ra->count > 0);
   --ra->count;
 
   return ret;
@@ -151,13 +151,9 @@ ring_array_get_dummy_buffer(ring_array *
   assert(ra && ra->buffer_array);
   assert(ra->capacity > 0);
   if (ra->count > 0) {
     return NULL;
   }
   return &ra->buffer_array[0];
 }
 
-#if defined(__cplusplus)
-}
-#endif
-
 #endif //CUBEB_RING_ARRAY_H
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -172,17 +172,17 @@ sndio_destroy(cubeb *context)
 static int
 sndio_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,
+                  unsigned int latency_frames,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback,
                   void *user_ptr)
 {
   cubeb_stream *s;
   struct sio_par wpar, rpar;
   DPR("sndio_stream_init(%s)\n", stream_name);
   size_t size;
@@ -217,17 +217,17 @@ sndio_stream_init(cubeb * context,
     wpar.le = SIO_LE_NATIVE;
     break;
   default:
     DPR("sndio_stream_init() unsupported format\n");
     return CUBEB_ERROR_INVALID_FORMAT;
   }
   wpar.rate = output_stream_params->rate;
   wpar.pchan = output_stream_params->channels;
-  wpar.appbufsz = latency * wpar.rate / 1000;
+  wpar.appbufsz = latency_frames;
   if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
     sio_close(s->hdl);
     free(s);
     DPR("sndio_stream_init(), sio_setpar() failed\n");
     return CUBEB_ERROR;
   }
   if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
       rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
@@ -285,17 +285,17 @@ sndio_get_preferred_sample_rate(cubeb * 
 
   return CUBEB_OK;
 }
 
 static int
 sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
 {
   // XXX Not yet implemented.
-  *latency_ms = 40;
+  *latency = 2048;
 
   return CUBEB_OK;
 }
 
 static void
 sndio_stream_destroy(cubeb_stream *s)
 {
   DPR("sndio_stream_destroy()\n");
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -57,33 +57,16 @@ DEFINE_PROPERTYKEY(PKEY_Device_InstanceI
 #else
 #define LOG(...)
 #endif
 
 #define ARRAY_LENGTH(array_)                    \
   (sizeof(array_) / sizeof(array_[0]))
 
 namespace {
-uint32_t
-ms_to_hns(uint32_t ms)
-{
-  return ms * 10000;
-}
-
-uint32_t
-hns_to_ms(REFERENCE_TIME hns)
-{
-  return static_cast<uint32_t>(hns / 10000);
-}
-
-double
-hns_to_s(REFERENCE_TIME hns)
-{
-  return static_cast<double>(hns) / 10000000;
-}
 
 void
 SafeRelease(HANDLE handle)
 {
   if (handle) {
     CloseHandle(handle);
   }
 }
@@ -235,17 +218,17 @@ struct cubeb_stream
   cubeb_stream_params output_mix_params;
   /* Stream parameters. This is what the client requested,
    * and what will be presented in the callback. */
   cubeb_stream_params input_stream_params;
   cubeb_stream_params output_stream_params;
   /* The input and output device, or NULL for default. */
   cubeb_devid input_device;
   cubeb_devid output_device;
-  /* The latency initially requested for this stream. */
+  /* The latency initially requested for this stream, in frames. */
   unsigned latency;
   cubeb_state_callback state_callback;
   cubeb_data_callback data_callback;
   wasapi_refill_callback refill_callback;
   void * user_ptr;
   /* Lifetime considerations:
      - client, render_client, audio_clock and audio_stream_volume are interface
        pointer to the IAudioClient.
@@ -433,16 +416,60 @@ bool should_downmix(cubeb_stream_params 
   return mixer.channels < stream.channels;
 }
 
 double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream_params & mixer)
 {
   return double(stream.rate) / mixer.rate;
 }
 
+
+uint32_t
+get_rate(cubeb_stream * stm)
+{
+  return has_input(stm) ? stm->input_stream_params.rate
+                        : stm->output_stream_params.rate;
+}
+
+uint32_t
+ms_to_hns(uint32_t ms)
+{
+  return ms * 10000;
+}
+
+uint32_t
+hns_to_ms(REFERENCE_TIME hns)
+{
+  return static_cast<uint32_t>(hns / 10000);
+}
+
+double
+hns_to_s(REFERENCE_TIME hns)
+{
+  return static_cast<double>(hns) / 10000000;
+}
+
+uint32_t
+hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
+{
+  return hns_to_ms(hns * get_rate(stm)) / 1000;
+}
+
+uint32_t
+hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
+{
+  return hns_to_ms(hns * rate) / 1000;
+}
+
+REFERENCE_TIME
+frames_to_hns(cubeb_stream * stm, uint32_t frames)
+{
+   return frames * 1000 / get_rate(stm);
+}
+
 /* Upmix function, copies a mono channel into L and R */
 template<typename T>
 void
 mono_to_stereo(T * in, long insamples, T * out, int32_t out_channels)
 {
   for (int i = 0, j = 0; i < insamples; ++i, j += out_channels) {
     out[j] = out[j + 1] = in[i];
   }
@@ -1239,17 +1266,17 @@ wasapi_get_max_channel_count(cubeb * ctx
 
   CoTaskMemFree(mix_format);
   SafeRelease(client);
 
   return CUBEB_OK;
 }
 
 int
-wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   HRESULT hr;
   IAudioClient * client;
   REFERENCE_TIME default_period;
   auto_com com;
   if (!com.ok()) {
     return CUBEB_ERROR;
   }
@@ -1282,17 +1309,18 @@ wasapi_get_min_latency(cubeb * ctx, cube
     return CUBEB_ERROR;
   }
 
   LOG("default device period: %lld\n", default_period);
 
   /* According to the docs, the best latency we can achieve is by synchronizing
      the stream and the engine.
      http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
-  *latency_ms = hns_to_ms(default_period);
+
+  *latency_frames = hns_to_frames(params.rate, default_period);
 
   SafeRelease(client);
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
@@ -1471,17 +1499,17 @@ int setup_wasapi_stream_one_side(cubeb_s
   mix_params->channels = mix_format->nChannels;
   LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]\n",
       stream_params->format, stream_params->rate, stream_params->channels,
       mix_params->format, mix_params->rate, mix_params->channels);
 
   hr = (*audio_client)->Initialize(AUDCLNT_SHAREMODE_SHARED,
                                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
                                    AUDCLNT_STREAMFLAGS_NOPERSIST,
-                                   ms_to_hns(stm->latency),
+                                   frames_to_hns(stm, stm->latency),
                                    0,
                                    mix_format,
                                    NULL);
   if (FAILED(hr)) {
     LOG("Unable to initialize audio client for %s: %x.\n", DIRECTION_NAME, hr);
     return CUBEB_ERROR;
   }
 
@@ -1637,27 +1665,27 @@ int setup_wasapi_stream(cubeb_stream * s
 
 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, cubeb_data_callback data_callback,
+                   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);
+  XASSERT(context && stream && (input_stream_params || output_stream_params));
 
   if (output_stream_params && output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE ||
       input_stream_params && input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) {
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
 
@@ -1671,17 +1699,18 @@ wasapi_stream_init(cubeb * context, cube
   if (input_stream_params) {
     stm->input_stream_params = *input_stream_params;
     stm->input_device = input_device;
   }
   if (output_stream_params) {
     stm->output_stream_params = *output_stream_params;
     stm->output_device = output_device;
   }
-  stm->latency = latency;
+
+  stm->latency = latency_frames;
   stm->volume = 1.0;
 
   stm->stream_reset_lock = new owned_critical_section();
 
   stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
   if (!stm->reconfigure_event) {
     LOG("Can't create the reconfigure event, error: %x\n", GetLastError());
     wasapi_stream_destroy(stm);
@@ -1729,18 +1758,16 @@ wasapi_stream_init(cubeb * context, cube
 }
 
 void close_wasapi_stream(cubeb_stream * stm)
 {
   XASSERT(stm);
 
   stm->stream_reset_lock->assert_current_thread_owns();
 
-  XASSERT(stm->output_client || stm->input_client);
-
   SafeRelease(stm->output_client);
   stm->output_client = NULL;
   SafeRelease(stm->input_client);
   stm->capture_client = NULL;
 
   SafeRelease(stm->render_client);
   stm->render_client = NULL;
 
@@ -1942,18 +1969,17 @@ int wasapi_stream_get_latency(cubeb_stre
     return CUBEB_ERROR;
   }
 
   REFERENCE_TIME latency_hns;
   HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
-  double latency_s = hns_to_s(latency_hns);
-  *latency = static_cast<uint32_t>(latency_s * stm->output_stream_params.rate);
+  *latency = hns_to_frames(stm, latency_hns);
 
   return CUBEB_OK;
 }
 
 int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
 {
   auto_lock lock(stm->stream_reset_lock);
 
@@ -2136,21 +2162,21 @@ wasapi_create_device(IMMDeviceEnumerator
         ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec;
         ret->max_channels = wfx->nChannels;
       }
     }
   }
 
   if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) &&
       SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
-    ret->latency_lo_ms = hns_to_ms(min_period);
-    ret->latency_hi_ms = hns_to_ms(def_period);
+    ret->latency_lo = hns_to_frames(ret->default_rate, min_period);
+    ret->latency_hi = hns_to_frames(ret->default_rate, def_period);
   } else {
-    ret->latency_lo_ms = 0;
-    ret->latency_hi_ms = 0;
+    ret->latency_lo = 0;
+    ret->latency_hi = 0;
   }
   SafeRelease(client);
 
 done:
   SafeRelease(devnode);
   SafeRelease(endpoint);
   SafeRelease(propstore);
   if (device_id != NULL)
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -84,17 +84,17 @@ static struct cubeb_ops const winmm_ops;
 struct cubeb {
   struct cubeb_ops const * ops;
   HANDLE event;
   HANDLE thread;
   int shutdown;
   PSLIST_HEADER work;
   CRITICAL_SECTION lock;
   unsigned int active_streams;
-  unsigned int minimum_latency;
+  unsigned int minimum_latency_ms;
 };
 
 struct cubeb_stream {
   cubeb * context;
   cubeb_stream_params params;
   cubeb_data_callback data_callback;
   cubeb_state_callback state_callback;
   void * user_ptr;
@@ -331,17 +331,17 @@ winmm_init(cubeb ** context, char const 
     return CUBEB_ERROR;
   }
 
   SetThreadPriority(ctx->thread, THREAD_PRIORITY_TIME_CRITICAL);
 
   InitializeCriticalSection(&ctx->lock);
   ctx->active_streams = 0;
 
-  ctx->minimum_latency = calculate_minimum_latency();
+  ctx->minimum_latency_ms = calculate_minimum_latency();
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
 static char const *
 winmm_get_backend_id(cubeb * ctx)
@@ -379,17 +379,17 @@ winmm_destroy(cubeb * ctx)
 static void winmm_stream_destroy(cubeb_stream * stm);
 
 static int
 winmm_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,
+                  unsigned int latency_frames,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback,
                   void * user_ptr)
 {
   MMRESULT r;
   WAVEFORMATEXTENSIBLE wfx;
   cubeb_stream * stm;
   int i;
@@ -462,21 +462,23 @@ winmm_stream_init(cubeb * context, cubeb
 
   stm->params = *output_stream_params;
 
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
   stm->written = 0;
 
-  if (latency < context->minimum_latency) {
-    latency = context->minimum_latency;
+  uint32_t latency_ms = latency_frames * 1000 / output_stream_params->rate;
+
+  if (latency_ms < context->minimum_latency_ms) {
+    latency_ms = context->minimum_latency_ms;
   }
 
-  bufsz = (size_t) (stm->params.rate / 1000.0 * latency * bytes_per_frame(stm->params) / NBUFS);
+  bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
   if (bufsz % bytes_per_frame(stm->params) != 0) {
     bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
   }
   XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
 
   stm->buffer_size = bufsz;
 
   InitializeCriticalSection(&stm->lock);
@@ -595,17 +597,17 @@ winmm_get_max_channel_count(cubeb * ctx,
 
   return CUBEB_OK;
 }
 
 static int
 winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
 {
   // 100ms minimum, if we are not in a bizarre configuration.
-  *latency = ctx->minimum_latency;
+  *latency = ctx->minimum_latency_ms * params.rate / 1000;
 
   return CUBEB_OK;
 }
 
 static int
 winmm_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   WAVEOUTCAPS woc;
@@ -849,18 +851,18 @@ winmm_create_device_from_outcaps2(LPWAVE
   ret->preferred = winmm_query_preferred_out_device(devid);
 
   ret->max_channels = caps->wChannels;
   winmm_calculate_device_rate(ret, caps->dwFormats);
   winmm_query_supported_formats(devid, caps->dwFormats,
       &ret->format, &ret->default_format);
 
   /* Hardcoed latency estimates... */
-  ret->latency_lo_ms = 100;
-  ret->latency_hi_ms = 200;
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
 
   return ret;
 }
 
 static cubeb_device_info *
 winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid)
 {
   cubeb_device_info * ret;
@@ -880,18 +882,18 @@ winmm_create_device_from_outcaps(LPWAVEO
   ret->preferred = winmm_query_preferred_out_device(devid);
 
   ret->max_channels = caps->wChannels;
   winmm_calculate_device_rate(ret, caps->dwFormats);
   winmm_query_supported_formats(devid, caps->dwFormats,
       &ret->format, &ret->default_format);
 
   /* Hardcoed latency estimates... */
-  ret->latency_lo_ms = 100;
-  ret->latency_hi_ms = 200;
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
 
   return ret;
 }
 
 static cubeb_device_pref
 winmm_query_preferred_in_device(UINT devid)
 {
   DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
@@ -930,18 +932,18 @@ winmm_create_device_from_incaps2(LPWAVEI
   ret->preferred = winmm_query_preferred_in_device(devid);
 
   ret->max_channels = caps->wChannels;
   winmm_calculate_device_rate(ret, caps->dwFormats);
   winmm_query_supported_formats(devid, caps->dwFormats,
       &ret->format, &ret->default_format);
 
   /* Hardcoed latency estimates... */
-  ret->latency_lo_ms = 100;
-  ret->latency_hi_ms = 200;
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
 
   return ret;
 }
 
 static cubeb_device_info *
 winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid)
 {
   cubeb_device_info * ret;
@@ -961,18 +963,18 @@ winmm_create_device_from_incaps(LPWAVEIN
   ret->preferred = winmm_query_preferred_in_device(devid);
 
   ret->max_channels = caps->wChannels;
   winmm_calculate_device_rate(ret, caps->dwFormats);
   winmm_query_supported_formats(devid, caps->dwFormats,
       &ret->format, &ret->default_format);
 
   /* Hardcoed latency estimates... */
-  ret->latency_lo_ms = 100;
-  ret->latency_hi_ms = 200;
+  ret->latency_lo = 100 * ret->default_rate / 1000;
+  ret->latency_hi = 200 * ret->default_rate / 1000;
 
   return ret;
 }
 
 static int
 winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
                         cubeb_device_collection ** collection)
 {
--- a/media/libcubeb/tests/test_audio.cpp
+++ b/media/libcubeb/tests/test_audio.cpp
@@ -156,17 +156,17 @@ int run_test(int num_channels, int sampl
 
   synth = synth_create(params.channels, params.rate);
   if (synth == NULL) {
     fprintf(stderr, "Out of memory\n");
     goto cleanup;
   }
 
   r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
-                        100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
+                        4096, is_float ? data_cb_float : data_cb_short, state_cb, synth);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
     goto cleanup;
   }
 
   cubeb_stream_start(stream);
   delay(200);
   cubeb_stream_stop(stream);
@@ -208,17 +208,18 @@ int run_panning_volume_test(int is_float
 
   synth = synth_create(params.channels, params.rate);
   if (synth == NULL) {
     fprintf(stderr, "Out of memory\n");
     goto cleanup;
   }
 
   r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
-                        100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
+                        4096, is_float ? data_cb_float : data_cb_short,
+                        state_cb, synth);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
     goto cleanup;
   }
 
   fprintf(stderr, "Testing: volume\n");
   for(int i=0;i <= 4; ++i)
   {
--- a/media/libcubeb/tests/test_duplex.cpp
+++ b/media/libcubeb/tests/test_duplex.cpp
@@ -96,17 +96,17 @@ int main(int argc, char *argv[])
 #endif
 
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params input_params;
   cubeb_stream_params output_params;
   int r;
   user_state stream_state = { false };
-  uint32_t latency_ms = 0;
+  uint32_t latency_frames = 0;
 
   r = cubeb_init(&ctx, "Cubeb duplex example");
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     return r;
   }
 
   /* This test needs an available input device, skip it if this host does not
@@ -118,26 +118,26 @@ int main(int argc, char *argv[])
   /* typical user-case: mono input, stereo output, low latency. */
   input_params.format = STREAM_FORMAT;
   input_params.rate = 48000;
   input_params.channels = 1;
   output_params.format = STREAM_FORMAT;
   output_params.rate = 48000;
   output_params.channels = 2;
 
-  r = cubeb_get_min_latency(ctx, output_params, &latency_ms);
+  r = cubeb_get_min_latency(ctx, output_params, &latency_frames);
 
   if (r != CUBEB_OK) {
     fprintf(stderr, "Could not get minimal latency\n");
     return r;
   }
 
   r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
                         NULL, &input_params, NULL, &output_params,
-                        latency_ms, data_cb, state_cb, &stream_state);
+                        latency_frames, data_cb, state_cb, &stream_state);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb stream\n");
     return r;
   }
 
   cubeb_stream_start(stream);
   delay(500);
   cubeb_stream_stop(stream);
--- a/media/libcubeb/tests/test_latency.cpp
+++ b/media/libcubeb/tests/test_latency.cpp
@@ -16,17 +16,17 @@ int main(int argc, char * argv[])
 #ifdef CUBEB_GECKO_BUILD
   ScopedXPCOM xpcom("test_latency");
 #endif
 
   cubeb * ctx = NULL;
   int r;
   uint32_t max_channels;
   uint32_t preferred_rate;
-  uint32_t latency_ms;
+  uint32_t latency_frames;
 
   LOG("latency_test start");
   r = cubeb_init(&ctx, "Cubeb audio test");
   assert(r == CUBEB_OK && "Cubeb init failed.");
   LOG("cubeb_init ok");
 
   r = cubeb_get_max_channel_count(ctx, &max_channels);
   assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
@@ -42,19 +42,19 @@ int main(int argc, char * argv[])
     LOG("cubeb_get_preferred_sample_rate ok");
   }
 
   cubeb_stream_params params = {
     CUBEB_SAMPLE_FLOAT32NE,
     preferred_rate,
     max_channels
   };
-  r = cubeb_get_min_latency(ctx, params, &latency_ms);
+  r = cubeb_get_min_latency(ctx, params, &latency_frames);
   assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
-    assert(latency_ms > 0 && "Invalid minimal latency.");
+    assert(latency_frames > 0 && "Invalid minimal latency.");
     LOG("cubeb_get_min_latency ok");
   }
 
   cubeb_destroy(ctx);
   LOG("cubeb_destroy ok");
   return EXIT_SUCCESS;
 }
--- a/media/libcubeb/tests/test_record.cpp
+++ b/media/libcubeb/tests/test_record.cpp
@@ -101,17 +101,17 @@ int main(int argc, char *argv[])
     return 0;
   }
 
   params.format = STREAM_FORMAT;
   params.rate = SAMPLE_FREQUENCY;
   params.channels = 1;
 
   r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, &params, NULL, nullptr,
-                        250, data_cb, state_cb, &stream_state);
+                        4096, data_cb, state_cb, &stream_state);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb stream\n");
     return r;
   }
 
   cubeb_stream_start(stream);
   delay(500);
   cubeb_stream_stop(stream);
--- a/media/libcubeb/tests/test_sanity.cpp
+++ b/media/libcubeb/tests/test_sanity.cpp
@@ -21,18 +21,18 @@
 #if (defined(_WIN32) || defined(__WIN32__))
 #define __func__ __FUNCTION__
 #endif
 
 #define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0]))
 #define BEGIN_TEST fprintf(stderr, "START %s\n", __func__)
 #define END_TEST fprintf(stderr, "END %s\n", __func__)
 
-#define STREAM_LATENCY 100
 #define STREAM_RATE 44100
+#define STREAM_LATENCY 100 * STREAM_RATE / 1000
 #define STREAM_CHANNELS 1
 #if (defined(_WIN32) || defined(__WIN32__))
 #define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
 #else
 #define STREAM_FORMAT CUBEB_SAMPLE_S16LE
 #endif
 
 static int dummy;
--- a/media/libcubeb/tests/test_tone.cpp
+++ b/media/libcubeb/tests/test_tone.cpp
@@ -123,17 +123,17 @@ int main(int argc, char *argv[])
   user_data = (struct cb_user_data *) malloc(sizeof(*user_data));
   if (user_data == NULL) {
     fprintf(stderr, "Error allocating user data\n");
     return CUBEB_ERROR;
   }
   user_data->position = 0;
 
   r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL, &params,
-                        250, data_cb, state_cb, user_data);
+                        4096, data_cb, state_cb, user_data);
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb stream\n");
     return r;
   }
 
   cubeb_stream_start(stream);
   delay(500);
   cubeb_stream_stop(stream);