Bug 1339816 - Part 2: Uplift cubeb audiounit drift fix. r=padenot a=gchang
authorAlex Chronopoulos <achronop@gmail.com>
Fri, 24 Feb 2017 11:37:51 +0200
changeset 359437 1c202d2bb24197c9f31d96998477f485acc85be4
parent 359436 2d1a7803246bcd64518df53db07108b9da97d848
child 359438 ccc8d62ca46027052502908a22834588b82dee84
push id10800
push userachronop@gmail.com
push dateFri, 24 Feb 2017 09:35:56 +0000
treeherdermozilla-aurora@1c202d2bb241 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot, gchang
bugs1339816
milestone53.0a2
Bug 1339816 - Part 2: Uplift cubeb audiounit drift fix. r=padenot a=gchang
media/libcubeb/audiounit-drift-fix.patch
media/libcubeb/gtest/test_resampler.cpp
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_resampler.cpp
media/libcubeb/src/cubeb_resampler_internal.h
media/libcubeb/update.sh
media/libcubeb/wasapi-drift-fix-passthrough-resampler.patch
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/audiounit-drift-fix.patch
@@ -0,0 +1,510 @@
+# HG changeset patch
+# User Alex Chronopoulos <achronop@gmail.com>
+# Parent  d1c742cd2af9d3da481d227b4675279902259751
+Bug 1339816 - Uplift cubeb audiounit drift fix. r?padenot
+
+diff --git a/media/libcubeb/gtest/test_resampler.cpp b/media/libcubeb/gtest/test_resampler.cpp
+--- a/media/libcubeb/gtest/test_resampler.cpp
++++ b/media/libcubeb/gtest/test_resampler.cpp
+@@ -712,17 +712,18 @@ TEST(cubeb, resampler_passthrough_duplex
+   seq_idx = seq(input_buffer_prebuffer, input_channels, seq_idx,
+                 prebuffer_frames);
+ 
+   long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
+                                   output_buffer, BUF_BASE_SIZE);
+ 
+   output_seq_idx += BUF_BASE_SIZE;
+ 
+-  ASSERT_EQ(prebuffer_frames, static_cast<long>(ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels));
++  // prebuffer_frames will hold the frames used by the resampler.
++  ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
+   ASSERT_EQ(got, BUF_BASE_SIZE);
+ 
+   for (uint32_t i = 0; i < 300; i++) {
+     long int frames = BUF_BASE_SIZE;
+     // Simulate that sometimes, we don't have the input callback on time
+     if (i != 0 && (i % 100) == 0) {
+       long zero = 0;
+       got = cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
+diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp
+--- a/media/libcubeb/src/cubeb_audiounit.cpp
++++ b/media/libcubeb/src/cubeb_audiounit.cpp
+@@ -41,16 +41,26 @@ typedef UInt32  AudioFormatFlags;
+ #define AU_IN_BUS     1
+ 
+ #define PRINT_ERROR_CODE(str, r) do {                           \
+     LOG("System call failed: %s (rv: %d)", str, (int) r);       \
+   } while(0)
+ 
+ const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
+ 
++#ifdef ALOGV
++#undef ALOGV
++#endif
++#define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);})
++
++#ifdef ALOG
++#undef ALOG
++#endif
++#define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);})
++
+ /* Testing empirically, some headsets report a minimal latency that is very
+  * low, but this does not work in practice. Lie and say the minimum is 256
+  * frames. */
+ const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
+ const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
+ 
+ void audiounit_stream_stop_internal(cubeb_stream * stm);
+ void audiounit_stream_start_internal(cubeb_stream * stm);
+@@ -156,35 +166,34 @@ struct cubeb_stream {
+   Float64 output_hw_rate = 0;
+   /* Expected I/O thread interleave,
+    * calculated from I/O hw rate. */
+   int expected_output_callbacks_in_a_row = 0;
+   owned_critical_section mutex;
+   /* Hold the input samples in every
+    * input callback iteration */
+   std::unique_ptr<auto_array_wrapper> input_linear_buffer;
++  // After the resampling some input data remains stored inside
++  // the resampler. This number is used in order to calculate
++  // the number of extra silence frames in input.
++  std::atomic<uint32_t> available_input_frames{ 0 };
+   /* Frames on input buffer */
+   std::atomic<uint32_t> input_buffer_frames{ 0 };
+   /* Frame counters */
+   std::atomic<uint64_t> frames_played{ 0 };
+   uint64_t frames_queued = 0;
+   std::atomic<int64_t> frames_read{ 0 };
+-  std::atomic<bool> shutdown{ false };
++  std::atomic<bool> shutdown{ true };
+   std::atomic<bool> draining{ false };
+   /* Latency requested by the user. */
+   uint32_t latency_frames = 0;
+   std::atomic<uint64_t> current_latency_frames{ 0 };
+   uint64_t hw_latency_frames = UINT64_MAX;
+   std::atomic<float> panning{ 0 };
+   std::unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
+-  /* This is the number of output callback we got in a row. This is usually one,
+-   * but can be two when the input and output rate are different, and more when
+-   * a device has been plugged or unplugged, as there can be some time before
+-   * the device is ready. */
+-  std::atomic<int> output_callback_in_a_row{ 0 };
+   /* This is true if a device change callback is currently running.  */
+   std::atomic<bool> switching_device{ false };
+   std::atomic<bool> buffer_size_change_state{ false };
+ };
+ 
+ bool has_input(cubeb_stream * stm)
+ {
+   return stm->input_stream_params.rate != 0;
+@@ -276,26 +285,28 @@ audiounit_render_input(cubeb_stream * st
+     PRINT_ERROR_CODE("AudioUnitRender", r);
+     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);
+ 
+-  LOGV("(%p) input:  buffers %u, size %u, channels %u, frames %d.",
++  /* Advance input frame counter. */
++  assert(input_frames > 0);
++  stm->frames_read += input_frames;
++  stm->available_input_frames += input_frames;
++
++  ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %d.",
+        stm,
+        (unsigned int) input_buffer_list.mNumberBuffers,
+        (unsigned int) input_buffer_list.mBuffers[0].mDataByteSize,
+        (unsigned int) input_buffer_list.mBuffers[0].mNumberChannels,
+-       (unsigned int) input_frames);
+-
+-  /* Advance input frame counter. */
+-  assert(input_frames > 0);
+-  stm->frames_read += input_frames;
++       (unsigned int) input_frames,
++        stm->available_input_frames.load());
+ 
+   return noErr;
+ }
+ 
+ static OSStatus
+ audiounit_input_callback(void * user_ptr,
+                          AudioUnitRenderActionFlags * flags,
+                          AudioTimeStamp const * tstamp,
+@@ -304,37 +315,27 @@ audiounit_input_callback(void * user_ptr
+                          AudioBufferList * /* bufs */)
+ {
+   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
+ 
+   assert(stm->input_unit != NULL);
+   assert(AU_IN_BUS == bus);
+ 
+   if (stm->shutdown) {
+-    LOG("(%p) input shutdown", stm);
++    ALOG("(%p) input shutdown", stm);
+     return noErr;
+   }
+ 
+-  // This happens when we're finally getting a new input callback after having
+-  // switched device, we can clear the input buffer now, only keeping the data
+-  // we just got.
+-  if (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row) {
+-    stm->input_linear_buffer->pop(
+-        stm->input_linear_buffer->length() -
+-        input_frames * stm->input_stream_params.channels);
+-  }
+-
+   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) {
+-    stm->output_callback_in_a_row = 0;
+     return noErr;
+   }
+ 
+   /* Input only. Call the user callback through resampler.
+      Resampler will deliver input buffer in the correct rate. */
+   assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
+   long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
+   long outframes = cubeb_resampler_fill(stm->resampler.get(),
+@@ -348,64 +349,60 @@ audiounit_input_callback(void * user_ptr
+   if (outframes < 0 || (UInt32) outframes != input_frames) {
+     stm->shutdown = true;
+     return noErr;
+   }
+ 
+   return noErr;
+ }
+ 
++static uint32_t
++minimum_resampling_input_frames(cubeb_stream *stm)
++{
++  return ceilf(stm->input_hw_rate / stm->output_hw_rate * stm->input_buffer_frames);
++}
++
+ static bool
+ is_extra_input_needed(cubeb_stream * stm)
+ {
+   /* If the output callback came first and this is a duplex stream, we need to
+     * fill in some additional silence in the resampler.
+     * Otherwise, if we had more than expected callbacks in a row, or we're currently
+     * switching, we add some silence as well to compensate for the fact that
+     * we're lacking some input data. */
+-
+-  /* If resampling is taking place after every output callback
+-   * the input buffer expected to be empty.  Any frame left over
+-   * from resampling is stored inside the resampler available to
+-   * be used in next iteration as needed.
+-   * BUT when noop_resampler is operating we have left over
+-   * frames since it does not store anything internally. */
+   return stm->frames_read == 0 ||
+-         (stm->input_linear_buffer->length() == 0 &&
+-         (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row ||
+-         stm->switching_device));
++         stm->available_input_frames.load() < minimum_resampling_input_frames(stm);
+ }
+ 
+ static OSStatus
+ audiounit_output_callback(void * user_ptr,
+                           AudioUnitRenderActionFlags * /* flags */,
+                           AudioTimeStamp const * tstamp,
+                           UInt32 bus,
+                           UInt32 output_frames,
+                           AudioBufferList * outBufferList)
+ {
+   assert(AU_OUT_BUS == bus);
+   assert(outBufferList->mNumberBuffers == 1);
+ 
+   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
+ 
+-  stm->output_callback_in_a_row++;
++  ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %d.",
++        stm,
++        (unsigned int) outBufferList->mNumberBuffers,
++        (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
++        (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
++        (unsigned int) output_frames,
++        stm->available_input_frames.load());
+ 
+-  LOGV("(%p) output: buffers %u, size %u, channels %u, frames %u.",
+-       stm,
+-       (unsigned int) outBufferList->mNumberBuffers,
+-       (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
+-       (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
+-       (unsigned int) output_frames);
+-
+-  long input_frames = 0;
++  long input_frames = 0, input_frames_before_fill = 0;
+   void * output_buffer = NULL, * input_buffer = NULL;
+ 
+   if (stm->shutdown) {
+-    LOG("(%p) output shutdown.", stm);
++    ALOG("(%p) output shutdown.", stm);
+     audiounit_make_silent(&outBufferList->mBuffers[0]);
+     return noErr;
+   }
+ 
+   stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
+   if (stm->draining) {
+     OSStatus r = AudioOutputUnitStop(stm->output_unit);
+     assert(r == 0);
+@@ -417,37 +414,44 @@ audiounit_output_callback(void * user_pt
+     audiounit_make_silent(&outBufferList->mBuffers[0]);
+     return noErr;
+   }
+   /* Get output buffer. */
+   output_buffer = outBufferList->mBuffers[0].mData;
+   /* If Full duplex get also input buffer */
+   if (stm->input_unit != NULL) {
+     if (is_extra_input_needed(stm)) {
+-      uint32_t min_input_frames_required = ceilf(stm->input_hw_rate / stm->output_hw_rate *
+-                                                                      stm->input_buffer_frames);
+-      stm->input_linear_buffer->push_silence(min_input_frames_required * stm->input_desc.mChannelsPerFrame);
+-      LOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
+-          stm->switching_device ? "Device switching," : "Drop out,", min_input_frames_required);
++      uint32_t min_input_frames = minimum_resampling_input_frames(stm);
++      stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame);
++      stm->available_input_frames += min_input_frames;
++
++      ALOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
++           stm->switching_device ? "Device switching," : "Drop out,", min_input_frames);
+     }
+-    // The input buffer
+     input_buffer = stm->input_linear_buffer->data();
+-    // Number of input frames in the buffer
++    // Number of input frames in the buffer. It will change to actually used frames
++    // inside fill
+     input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
++    // Number of input frames pushed inside resampler.
++    input_frames_before_fill = input_frames;
+   }
+ 
+   /* Call user callback through resampler. */
+   long outframes = cubeb_resampler_fill(stm->resampler.get(),
+                                         input_buffer,
+                                         input_buffer ? &input_frames : NULL,
+                                         output_buffer,
+                                         output_frames);
+ 
+   if (input_buffer) {
+-    stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame);
++    // Decrease counter by the number of frames used by resampler
++    stm->available_input_frames -= input_frames;
++    assert(stm->available_input_frames.load() >= 0);
++    // Pop from the buffer the frames pushed to the resampler.
++    stm->input_linear_buffer->pop(input_frames_before_fill * stm->input_desc.mChannelsPerFrame);
+   }
+ 
+   if (outframes < 0) {
+     stm->shutdown = true;
+     return noErr;
+   }
+ 
+   size_t outbpf = stm->output_desc.mBytesPerFrame;
+@@ -613,16 +617,19 @@ audiounit_property_listener_callback(Aud
+           }
+           // Allow restart to choose the new default. Event register only for input.
+           stm->input_device = 0;
+         }
+         break;
+       case kAudioDevicePropertyDataSource:
+         LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource", (unsigned int) i);
+         break;
++      default:
++        LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
++        return noErr;
+     }
+   }
+ 
+   for (UInt32 i = 0; i < address_count; i++) {
+     switch(addresses[i].mSelector) {
+     case kAudioHardwarePropertyDefaultOutputDevice:
+     case kAudioHardwarePropertyDefaultInputDevice:
+     case kAudioDevicePropertyDeviceIsAlive:
+@@ -789,16 +796,26 @@ audiounit_uninstall_device_changed_callb
+       return CUBEB_ERROR;
+     }
+ 
+     r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
+         kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+     if (r != noErr) {
+       return CUBEB_ERROR;
+     }
++
++    /* Event to notify when the input is going away. */
++    AudioDeviceID dev = stm->input_device ? stm->input_device :
++                        audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
++    r = audiounit_remove_listener(stm, dev, kAudioDevicePropertyDeviceIsAlive,
++                                  kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
++    if (r != noErr) {
++      PRINT_ERROR_CODE("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive", r);
++      return CUBEB_ERROR;
++    }
+   }
+   return CUBEB_OK;
+ }
+ 
+ /* Get the acceptable buffer size (in frames) that this device can work with. */
+ static int
+ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
+ {
+@@ -1106,28 +1123,18 @@ static int
+ audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
+ {
+   uint32_t size = capacity * stream->input_buffer_frames * stream->input_desc.mChannelsPerFrame;
+   if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
+     stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size));
+   } else {
+     stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size));
+   }
+-
+   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 uint32_t
+ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
+ {
+   // For the 1st stream set anything within safe min-max
+   assert(stm->context->active_streams > 0);
+diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp
+--- a/media/libcubeb/src/cubeb_resampler.cpp
++++ b/media/libcubeb/src/cubeb_resampler.cpp
+@@ -67,16 +67,17 @@ long passthrough_resampler<T>::fill(void
+                                frames_to_samples(*input_frames_count));
+   }
+ 
+   long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
+                           output_buffer, output_frames);
+ 
+   if (input_buffer) {
+     internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
++    *input_frames_count = output_frames;
+   }
+ 
+   return rv;
+ }
+ 
+ template<typename T, typename InputProcessor, typename OutputProcessor>
+ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+   ::cubeb_resampler_speex(InputProcessor * input_processor,
+@@ -174,17 +175,17 @@ cubeb_resampler_speex<T, InputProcessor,
+ 
+   /* The input data, after eventual resampling. This is passed to the callback. */
+   T * resampled_input = nullptr;
+   uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
+ 
+   /* process the input, and present exactly `output_frames_needed` in the
+   * callback. */
+   input_processor->input(input_buffer, *input_frames_count);
+-  resampled_input = input_processor->output(resampled_frame_count);
++  resampled_input = input_processor->output(resampled_frame_count, (size_t*)input_frames_count);
+ 
+   long got = data_callback(stream, user_ptr,
+                            resampled_input, nullptr, resampled_frame_count);
+ 
+   /* Return the number of initial input frames or part of it.
+   * Since output_frames_needed == 0 in input scenario, the only
+   * available number outside resampler is the initial number of frames. */
+   return (*input_frames_count) * (got / resampled_frame_count);
+@@ -221,17 +222,17 @@ cubeb_resampler_speex<T, InputProcessor,
+   out_unprocessed =
+     output_processor->input_buffer(output_frames_before_processing);
+ 
+   if (in_buffer) {
+     /* process the input, and present exactly `output_frames_needed` in the
+     * callback. */
+     input_processor->input(in_buffer, *input_frames_count);
+     resampled_input =
+-      input_processor->output(output_frames_before_processing);
++      input_processor->output(output_frames_before_processing, (size_t*)input_frames_count);
+   } else {
+     resampled_input = nullptr;
+   }
+ 
+   got = data_callback(stream, user_ptr,
+                       resampled_input, out_unprocessed,
+                       output_frames_before_processing);
+ 
+diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h
+--- a/media/libcubeb/src/cubeb_resampler_internal.h
++++ b/media/libcubeb/src/cubeb_resampler_internal.h
+@@ -217,33 +217,34 @@ public:
+   size_t output_for_input(uint32_t input_frames)
+   {
+     return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
+                          / resampling_ratio);
+   }
+ 
+   /** Returns a buffer containing exactly `output_frame_count` resampled frames.
+     * The consumer should not hold onto the pointer. */
+-  T * output(size_t output_frame_count)
++  T * output(size_t output_frame_count, size_t * input_frames_used)
+   {
+     if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
+       resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
+     }
+ 
+     uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
+     uint32_t out_len = output_frame_count;
+ 
+     speex_resample(resampling_in_buffer.data(), &in_len,
+                    resampling_out_buffer.data(), &out_len);
+ 
+     assert(out_len == output_frame_count);
+ 
+     /* This shifts back any unresampled samples to the beginning of the input
+        buffer. */
+     resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
++    *input_frames_used = in_len;
+ 
+     return resampling_out_buffer.data();
+   }
+ 
+   /** Get the latency of the resampler, in output frames. */
+   uint32_t latency() const
+   {
+     /* The documentation of the resampler talks about "samples" here, but it
+@@ -371,26 +372,27 @@ public:
+   void input(T * buffer, uint32_t frame_count)
+   {
+     delay_input_buffer.push(buffer, frames_to_samples(frame_count));
+   }
+   /** Pop some frames from the internal buffer, into a internal output buffer.
+    * @parameter frames_needed the number of frames to be returned.
+    * @return a buffer containing the delayed frames. The consumer should not
+    * hold onto the pointer. */
+-  T * output(uint32_t frames_needed)
++  T * output(uint32_t frames_needed, size_t * input_frames_used)
+   {
+     if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {
+       delay_output_buffer.reserve(frames_to_samples(frames_needed));
+     }
+ 
+     delay_output_buffer.clear();
+     delay_output_buffer.push(delay_input_buffer.data(),
+                              frames_to_samples(frames_needed));
+     delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed));
++    *input_frames_used = frames_needed;
+ 
+     return delay_output_buffer.data();
+   }
+   /** Get a pointer to the first writable location in the input buffer>
+    * @parameter frames_needed the number of frames the user needs to write into
+    * the buffer.
+    * @returns a pointer to a location in the input buffer where #frames_needed
+    * can be writen. */
--- a/media/libcubeb/gtest/test_resampler.cpp
+++ b/media/libcubeb/gtest/test_resampler.cpp
@@ -712,17 +712,18 @@ TEST(cubeb, resampler_passthrough_duplex
   seq_idx = seq(input_buffer_prebuffer, input_channels, seq_idx,
                 prebuffer_frames);
 
   long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
                                   output_buffer, BUF_BASE_SIZE);
 
   output_seq_idx += BUF_BASE_SIZE;
 
-  ASSERT_EQ(prebuffer_frames, static_cast<long>(ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels));
+  // prebuffer_frames will hold the frames used by the resampler.
+  ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
   ASSERT_EQ(got, BUF_BASE_SIZE);
 
   for (uint32_t i = 0; i < 300; i++) {
     long int frames = BUF_BASE_SIZE;
     // Simulate that sometimes, we don't have the input callback on time
     if (i != 0 && (i % 100) == 0) {
       long zero = 0;
       got = cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -41,16 +41,26 @@ typedef UInt32  AudioFormatFlags;
 #define AU_IN_BUS     1
 
 #define PRINT_ERROR_CODE(str, r) do {                           \
     LOG("System call failed: %s (rv: %d)", str, (int) r);       \
   } while(0)
 
 const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
 
+#ifdef ALOGV
+#undef ALOGV
+#endif
+#define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);})
+
+#ifdef ALOG
+#undef ALOG
+#endif
+#define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);})
+
 /* Testing empirically, some headsets report a minimal latency that is very
  * low, but this does not work in practice. Lie and say the minimum is 256
  * frames. */
 const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
 
 void audiounit_stream_stop_internal(cubeb_stream * stm);
 void audiounit_stream_start_internal(cubeb_stream * stm);
@@ -156,35 +166,34 @@ struct cubeb_stream {
   Float64 output_hw_rate = 0;
   /* Expected I/O thread interleave,
    * calculated from I/O hw rate. */
   int expected_output_callbacks_in_a_row = 0;
   owned_critical_section mutex;
   /* Hold the input samples in every
    * input callback iteration */
   std::unique_ptr<auto_array_wrapper> input_linear_buffer;
+  // After the resampling some input data remains stored inside
+  // the resampler. This number is used in order to calculate
+  // the number of extra silence frames in input.
+  std::atomic<uint32_t> available_input_frames{ 0 };
   /* Frames on input buffer */
   std::atomic<uint32_t> input_buffer_frames{ 0 };
   /* Frame counters */
   std::atomic<uint64_t> frames_played{ 0 };
   uint64_t frames_queued = 0;
   std::atomic<int64_t> frames_read{ 0 };
-  std::atomic<bool> shutdown{ false };
+  std::atomic<bool> shutdown{ true };
   std::atomic<bool> draining{ false };
   /* Latency requested by the user. */
   uint32_t latency_frames = 0;
   std::atomic<uint64_t> current_latency_frames{ 0 };
   uint64_t hw_latency_frames = UINT64_MAX;
   std::atomic<float> panning{ 0 };
   std::unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
-  /* This is the number of output callback we got in a row. This is usually one,
-   * but can be two when the input and output rate are different, and more when
-   * a device has been plugged or unplugged, as there can be some time before
-   * the device is ready. */
-  std::atomic<int> output_callback_in_a_row{ 0 };
   /* This is true if a device change callback is currently running.  */
   std::atomic<bool> switching_device{ false };
   std::atomic<bool> buffer_size_change_state{ false };
 };
 
 bool has_input(cubeb_stream * stm)
 {
   return stm->input_stream_params.rate != 0;
@@ -276,26 +285,28 @@ audiounit_render_input(cubeb_stream * st
     PRINT_ERROR_CODE("AudioUnitRender", r);
     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);
 
-  LOGV("(%p) input:  buffers %u, size %u, channels %u, frames %d.",
+  /* Advance input frame counter. */
+  assert(input_frames > 0);
+  stm->frames_read += input_frames;
+  stm->available_input_frames += input_frames;
+
+  ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %d.",
        stm,
        (unsigned int) input_buffer_list.mNumberBuffers,
        (unsigned int) input_buffer_list.mBuffers[0].mDataByteSize,
        (unsigned int) input_buffer_list.mBuffers[0].mNumberChannels,
-       (unsigned int) input_frames);
-
-  /* Advance input frame counter. */
-  assert(input_frames > 0);
-  stm->frames_read += input_frames;
+       (unsigned int) input_frames,
+        stm->available_input_frames.load());
 
   return noErr;
 }
 
 static OSStatus
 audiounit_input_callback(void * user_ptr,
                          AudioUnitRenderActionFlags * flags,
                          AudioTimeStamp const * tstamp,
@@ -304,37 +315,27 @@ audiounit_input_callback(void * user_ptr
                          AudioBufferList * /* bufs */)
 {
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
 
   assert(stm->input_unit != NULL);
   assert(AU_IN_BUS == bus);
 
   if (stm->shutdown) {
-    LOG("(%p) input shutdown", stm);
+    ALOG("(%p) input shutdown", stm);
     return noErr;
   }
 
-  // This happens when we're finally getting a new input callback after having
-  // switched device, we can clear the input buffer now, only keeping the data
-  // we just got.
-  if (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row) {
-    stm->input_linear_buffer->pop(
-        stm->input_linear_buffer->length() -
-        input_frames * stm->input_stream_params.channels);
-  }
-
   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) {
-    stm->output_callback_in_a_row = 0;
     return noErr;
   }
 
   /* Input only. Call the user callback through resampler.
      Resampler will deliver input buffer in the correct rate. */
   assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
   long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
   long outframes = cubeb_resampler_fill(stm->resampler.get(),
@@ -348,64 +349,60 @@ audiounit_input_callback(void * user_ptr
   if (outframes < 0 || (UInt32) outframes != input_frames) {
     stm->shutdown = true;
     return noErr;
   }
 
   return noErr;
 }
 
+static uint32_t
+minimum_resampling_input_frames(cubeb_stream *stm)
+{
+  return ceilf(stm->input_hw_rate / stm->output_hw_rate * stm->input_buffer_frames);
+}
+
 static bool
 is_extra_input_needed(cubeb_stream * stm)
 {
   /* If the output callback came first and this is a duplex stream, we need to
     * fill in some additional silence in the resampler.
     * Otherwise, if we had more than expected callbacks in a row, or we're currently
     * switching, we add some silence as well to compensate for the fact that
     * we're lacking some input data. */
-
-  /* If resampling is taking place after every output callback
-   * the input buffer expected to be empty.  Any frame left over
-   * from resampling is stored inside the resampler available to
-   * be used in next iteration as needed.
-   * BUT when noop_resampler is operating we have left over
-   * frames since it does not store anything internally. */
   return stm->frames_read == 0 ||
-         (stm->input_linear_buffer->length() == 0 &&
-         (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row ||
-         stm->switching_device));
+         stm->available_input_frames.load() < minimum_resampling_input_frames(stm);
 }
 
 static OSStatus
 audiounit_output_callback(void * user_ptr,
                           AudioUnitRenderActionFlags * /* flags */,
                           AudioTimeStamp const * tstamp,
                           UInt32 bus,
                           UInt32 output_frames,
                           AudioBufferList * outBufferList)
 {
   assert(AU_OUT_BUS == bus);
   assert(outBufferList->mNumberBuffers == 1);
 
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
 
-  stm->output_callback_in_a_row++;
+  ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %d.",
+        stm,
+        (unsigned int) outBufferList->mNumberBuffers,
+        (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
+        (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
+        (unsigned int) output_frames,
+        stm->available_input_frames.load());
 
-  LOGV("(%p) output: buffers %u, size %u, channels %u, frames %u.",
-       stm,
-       (unsigned int) outBufferList->mNumberBuffers,
-       (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
-       (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
-       (unsigned int) output_frames);
-
-  long input_frames = 0;
+  long input_frames = 0, input_frames_before_fill = 0;
   void * output_buffer = NULL, * input_buffer = NULL;
 
   if (stm->shutdown) {
-    LOG("(%p) output shutdown.", stm);
+    ALOG("(%p) output shutdown.", stm);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
 
   stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
   if (stm->draining) {
     OSStatus r = AudioOutputUnitStop(stm->output_unit);
     assert(r == 0);
@@ -417,37 +414,44 @@ audiounit_output_callback(void * user_pt
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
   /* Get output buffer. */
   output_buffer = outBufferList->mBuffers[0].mData;
   /* If Full duplex get also input buffer */
   if (stm->input_unit != NULL) {
     if (is_extra_input_needed(stm)) {
-      uint32_t min_input_frames_required = ceilf(stm->input_hw_rate / stm->output_hw_rate *
-                                                                      stm->input_buffer_frames);
-      stm->input_linear_buffer->push_silence(min_input_frames_required * stm->input_desc.mChannelsPerFrame);
-      LOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
-          stm->switching_device ? "Device switching," : "Drop out,", min_input_frames_required);
+      uint32_t min_input_frames = minimum_resampling_input_frames(stm);
+      stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame);
+      stm->available_input_frames += min_input_frames;
+
+      ALOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
+           stm->switching_device ? "Device switching," : "Drop out,", min_input_frames);
     }
-    // The input buffer
     input_buffer = stm->input_linear_buffer->data();
-    // Number of input frames in the buffer
+    // Number of input frames in the buffer. It will change to actually used frames
+    // inside fill
     input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
+    // Number of input frames pushed inside resampler.
+    input_frames_before_fill = input_frames;
   }
 
   /* Call user callback through resampler. */
   long outframes = cubeb_resampler_fill(stm->resampler.get(),
                                         input_buffer,
                                         input_buffer ? &input_frames : NULL,
                                         output_buffer,
                                         output_frames);
 
   if (input_buffer) {
-    stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame);
+    // Decrease counter by the number of frames used by resampler
+    stm->available_input_frames -= input_frames;
+    assert(stm->available_input_frames.load() >= 0);
+    // Pop from the buffer the frames pushed to the resampler.
+    stm->input_linear_buffer->pop(input_frames_before_fill * stm->input_desc.mChannelsPerFrame);
   }
 
   if (outframes < 0) {
     stm->shutdown = true;
     return noErr;
   }
 
   size_t outbpf = stm->output_desc.mBytesPerFrame;
@@ -613,16 +617,19 @@ audiounit_property_listener_callback(Aud
           }
           // Allow restart to choose the new default. Event register only for input.
           stm->input_device = 0;
         }
         break;
       case kAudioDevicePropertyDataSource:
         LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource", (unsigned int) i);
         break;
+      default:
+        LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
+        return noErr;
     }
   }
 
   for (UInt32 i = 0; i < address_count; i++) {
     switch(addresses[i].mSelector) {
     case kAudioHardwarePropertyDefaultOutputDevice:
     case kAudioHardwarePropertyDefaultInputDevice:
     case kAudioDevicePropertyDeviceIsAlive:
@@ -789,16 +796,26 @@ audiounit_uninstall_device_changed_callb
       return CUBEB_ERROR;
     }
 
     r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
         kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
     if (r != noErr) {
       return CUBEB_ERROR;
     }
+
+    /* Event to notify when the input is going away. */
+    AudioDeviceID dev = stm->input_device ? stm->input_device :
+                        audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
+    r = audiounit_remove_listener(stm, dev, kAudioDevicePropertyDeviceIsAlive,
+                                  kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive", r);
+      return CUBEB_ERROR;
+    }
   }
   return CUBEB_OK;
 }
 
 /* Get the acceptable buffer size (in frames) that this device can work with. */
 static int
 audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
 {
@@ -1106,28 +1123,18 @@ static int
 audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
 {
   uint32_t size = capacity * stream->input_buffer_frames * stream->input_desc.mChannelsPerFrame;
   if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
     stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size));
   } else {
     stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size));
   }
-
   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 uint32_t
 audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
 {
   // For the 1st stream set anything within safe min-max
   assert(stm->context->active_streams > 0);
--- a/media/libcubeb/src/cubeb_resampler.cpp
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -67,16 +67,17 @@ long passthrough_resampler<T>::fill(void
                                frames_to_samples(*input_frames_count));
   }
 
   long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
                           output_buffer, output_frames);
 
   if (input_buffer) {
     internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
+    *input_frames_count = output_frames;
   }
 
   return rv;
 }
 
 template<typename T, typename InputProcessor, typename OutputProcessor>
 cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
   ::cubeb_resampler_speex(InputProcessor * input_processor,
@@ -174,17 +175,17 @@ cubeb_resampler_speex<T, InputProcessor,
 
   /* The input data, after eventual resampling. This is passed to the callback. */
   T * resampled_input = nullptr;
   uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
 
   /* process the input, and present exactly `output_frames_needed` in the
   * callback. */
   input_processor->input(input_buffer, *input_frames_count);
-  resampled_input = input_processor->output(resampled_frame_count);
+  resampled_input = input_processor->output(resampled_frame_count, (size_t*)input_frames_count);
 
   long got = data_callback(stream, user_ptr,
                            resampled_input, nullptr, resampled_frame_count);
 
   /* Return the number of initial input frames or part of it.
   * Since output_frames_needed == 0 in input scenario, the only
   * available number outside resampler is the initial number of frames. */
   return (*input_frames_count) * (got / resampled_frame_count);
@@ -221,17 +222,17 @@ cubeb_resampler_speex<T, InputProcessor,
   out_unprocessed =
     output_processor->input_buffer(output_frames_before_processing);
 
   if (in_buffer) {
     /* process the input, and present exactly `output_frames_needed` in the
     * callback. */
     input_processor->input(in_buffer, *input_frames_count);
     resampled_input =
-      input_processor->output(output_frames_before_processing);
+      input_processor->output(output_frames_before_processing, (size_t*)input_frames_count);
   } else {
     resampled_input = nullptr;
   }
 
   got = data_callback(stream, user_ptr,
                       resampled_input, out_unprocessed,
                       output_frames_before_processing);
 
--- a/media/libcubeb/src/cubeb_resampler_internal.h
+++ b/media/libcubeb/src/cubeb_resampler_internal.h
@@ -217,33 +217,34 @@ public:
   size_t output_for_input(uint32_t input_frames)
   {
     return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
                          / resampling_ratio);
   }
 
   /** Returns a buffer containing exactly `output_frame_count` resampled frames.
     * The consumer should not hold onto the pointer. */
-  T * output(size_t output_frame_count)
+  T * output(size_t output_frame_count, size_t * input_frames_used)
   {
     if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
       resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
     }
 
     uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
     uint32_t out_len = output_frame_count;
 
     speex_resample(resampling_in_buffer.data(), &in_len,
                    resampling_out_buffer.data(), &out_len);
 
     assert(out_len == output_frame_count);
 
     /* This shifts back any unresampled samples to the beginning of the input
        buffer. */
     resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
+    *input_frames_used = in_len;
 
     return resampling_out_buffer.data();
   }
 
   /** Get the latency of the resampler, in output frames. */
   uint32_t latency() const
   {
     /* The documentation of the resampler talks about "samples" here, but it
@@ -371,26 +372,27 @@ public:
   void input(T * buffer, uint32_t frame_count)
   {
     delay_input_buffer.push(buffer, frames_to_samples(frame_count));
   }
   /** Pop some frames from the internal buffer, into a internal output buffer.
    * @parameter frames_needed the number of frames to be returned.
    * @return a buffer containing the delayed frames. The consumer should not
    * hold onto the pointer. */
-  T * output(uint32_t frames_needed)
+  T * output(uint32_t frames_needed, size_t * input_frames_used)
   {
     if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {
       delay_output_buffer.reserve(frames_to_samples(frames_needed));
     }
 
     delay_output_buffer.clear();
     delay_output_buffer.push(delay_input_buffer.data(),
                              frames_to_samples(frames_needed));
     delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed));
+    *input_frames_used = frames_needed;
 
     return delay_output_buffer.data();
   }
   /** Get a pointer to the first writable location in the input buffer>
    * @parameter frames_needed the number of frames the user needs to write into
    * the buffer.
    * @returns a pointer to a location in the input buffer where #frames_needed
    * can be writen. */
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -57,8 +57,14 @@ if [ -n "$rev" ]; then
     version=$version-dirty
     echo "WARNING: updating from a dirty git repository."
   fi
   sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\}\./$version./" README_MOZILLA
   rm README_MOZILLA.bak
 else
   echo "Remember to update README_MOZILLA with the version details."
 fi
+
+echo "Applying a patch on top of $version"
+patch -p1 < ./wasapi-drift-fix-passthrough-resampler.patch
+
+echo "Applying a patch on top of $version"
+patch -p1 < ./audiounit-drift-fix.patch
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/wasapi-drift-fix-passthrough-resampler.patch
@@ -0,0 +1,548 @@
+# HG changeset patch
+# User Alex Chronopoulos <achronop@gmail.com>
+# Parent  35ef506e005a3bc7f42637debfada1d3e09d7e5a
+Bug 1339816 - Uplift cubeb wasapi drift fix and passthrough resampler. r?padenot
+
+diff --git a/media/libcubeb/gtest/common.h b/media/libcubeb/gtest/common.h
+--- a/media/libcubeb/gtest/common.h
++++ b/media/libcubeb/gtest/common.h
+@@ -11,16 +11,18 @@
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #include <windows.h>
+ #else
+ #include <unistd.h>
+ #endif
+ 
++#include "cubeb/cubeb.h"
++
+ template<typename T, size_t N>
+ constexpr size_t
+ ARRAY_LENGTH(T(&)[N])
+ {
+   return N;
+ }
+ 
+ void delay(unsigned int ms)
+diff --git a/media/libcubeb/gtest/test_resampler.cpp b/media/libcubeb/gtest/test_resampler.cpp
+--- a/media/libcubeb/gtest/test_resampler.cpp
++++ b/media/libcubeb/gtest/test_resampler.cpp
+@@ -3,16 +3,17 @@
+  *
+  * This program is made available under an ISC-style license.  See the
+  * accompanying file LICENSE for details.
+  */
+ #ifndef NOMINMAX
+ #define NOMINMAX
+ #endif // NOMINMAX
+ #include "gtest/gtest.h"
++#include "common.h"
+ #include "cubeb_resampler_internal.h"
+ #include <stdio.h>
+ #include <algorithm>
+ #include <iostream>
+ 
+ /* Windows cmath USE_MATH_DEFINE thing... */
+ const float PI = 3.14159265359f;
+ 
+@@ -386,18 +387,21 @@ void test_resampler_duplex(uint32_t inpu
+     state.output.push(output_buffer.data(), got * state.output_channels);
+   }
+ 
+   dump("input_expected.raw", expected_resampled_input.data(), expected_resampled_input.length());
+   dump("output_expected.raw", expected_resampled_output.data(), expected_resampled_output.length());
+   dump("input.raw", state.input.data(), state.input.length());
+   dump("output.raw", state.output.data(), state.output.length());
+ 
+-  ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
+-  ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
++ // This is disabled because the latency estimation in the resampler code is
++ // slightly off so we can generate expected vectors.
++ // See https://github.com/kinetiknz/cubeb/issues/93
++ // ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
++ // ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
+ 
+   cubeb_resampler_destroy(resampler);
+ }
+ 
+ #define array_size(x) (sizeof(x) / sizeof(x[0]))
+ 
+ TEST(cubeb, resampler_one_way)
+ {
+@@ -411,19 +415,16 @@ TEST(cubeb, resampler_one_way)
+           test_resampler_one_way<float>(channels, sample_rates[source_rate],
+                                         sample_rates[dest_rate], chunk_duration);
+         }
+       }
+     }
+   }
+ }
+ 
+-// This is disabled because the latency estimation in the resampler code is
+-// slightly off so we can generate expected vectors.
+-// See https://github.com/kinetiknz/cubeb/issues/93
+ TEST(cubeb, DISABLED_resampler_duplex)
+ {
+   for (uint32_t input_channels = 1; input_channels <= max_channels; input_channels++) {
+     for (uint32_t output_channels = 1; output_channels <= max_channels; output_channels++) {
+       for (uint32_t source_rate_input = 0; source_rate_input < array_size(sample_rates); source_rate_input++) {
+         for (uint32_t source_rate_output = 0; source_rate_output < array_size(sample_rates); source_rate_output++) {
+           for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
+             for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
+@@ -531,8 +532,219 @@ TEST(cubeb, resampler_drain)
+ 
+   /* If the above is not an infinite loop, the drain was a success, just mark
+    * this test as such. */
+   ASSERT_TRUE(true);
+ 
+   cubeb_resampler_destroy(resampler);
+ }
+ 
++// gtest does not support using ASSERT_EQ and friend in a function that returns
++// a value.
++void check_output(const void * input_buffer, void * output_buffer, long frame_count)
++{
++  ASSERT_EQ(input_buffer, nullptr);
++  ASSERT_EQ(frame_count, 256);
++  ASSERT_TRUE(!!output_buffer);
++}
++
++long cb_passthrough_resampler_output(cubeb_stream * /*stm*/, void * /*user_ptr*/,
++                                     const void * input_buffer,
++                                     void * output_buffer, long frame_count)
++{
++  check_output(input_buffer, output_buffer, frame_count);
++  return frame_count;
++}
++
++TEST(cubeb, resampler_passthrough_output_only)
++{
++  // Test that the passthrough resampler works when there is only an output stream.
++  cubeb_stream_params output_params;
++
++  const size_t output_channels = 2;
++  output_params.channels = output_channels;
++  output_params.rate = 44100;
++  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
++  int target_rate = output_params.rate;
++
++  cubeb_resampler * resampler =
++    cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params,
++                           target_rate, cb_passthrough_resampler_output, nullptr,
++                           CUBEB_RESAMPLER_QUALITY_VOIP);
++
++  float output_buffer[output_channels * 256];
++
++  long got;
++  for (uint32_t i = 0; i < 30; i++) {
++    got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256);
++    ASSERT_EQ(got, 256);
++  }
++}
++
++// gtest does not support using ASSERT_EQ and friend in a function that returns
++// a value.
++void check_input(const void * input_buffer, void * output_buffer, long frame_count)
++{
++  ASSERT_EQ(output_buffer, nullptr);
++  ASSERT_EQ(frame_count, 256);
++  ASSERT_TRUE(!!input_buffer);
++}
++
++long cb_passthrough_resampler_input(cubeb_stream * /*stm*/, void * /*user_ptr*/,
++                                    const void * input_buffer,
++                                    void * output_buffer, long frame_count)
++{
++  check_input(input_buffer, output_buffer, frame_count);
++  return frame_count;
++}
++
++TEST(cubeb, resampler_passthrough_input_only)
++{
++  // Test that the passthrough resampler works when there is only an output stream.
++  cubeb_stream_params input_params;
++
++  const size_t input_channels = 2;
++  input_params.channels = input_channels;
++  input_params.rate = 44100;
++  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
++  int target_rate = input_params.rate;
++
++  cubeb_resampler * resampler =
++    cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, nullptr,
++                           target_rate, cb_passthrough_resampler_input, nullptr,
++                           CUBEB_RESAMPLER_QUALITY_VOIP);
++
++  float input_buffer[input_channels * 256];
++
++  long got;
++  for (uint32_t i = 0; i < 30; i++) {
++    long int frames = 256;
++    got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0);
++    ASSERT_EQ(got, 256);
++  }
++}
++
++template<typename T>
++long seq(T* array, int stride, long start, long count)
++{
++  for(int i = 0; i < count; i++) {
++    for (int j = 0; j < stride; j++) {
++      array[i + j] = static_cast<T>(start + i);
++    }
++  }
++  return start + count;
++}
++
++template<typename T>
++void is_seq(T * array, int stride, long count, long expected_start)
++{
++  uint32_t output_index = 0;
++  for (long i = 0; i < count; i++) {
++    for (int j = 0; j < stride; j++) {
++      ASSERT_EQ(array[output_index + j], expected_start + i);
++    }
++    output_index += stride;
++  }
++}
++
++// gtest does not support using ASSERT_EQ and friend in a function that returns
++// a value.
++template<typename T>
++void check_duplex(const T * input_buffer,
++                  T * output_buffer, long frame_count)
++{
++  ASSERT_EQ(frame_count, 256);
++  // Silence scan-build warning.
++  ASSERT_TRUE(!!output_buffer); assert(output_buffer);
++  ASSERT_TRUE(!!input_buffer); assert(input_buffer);
++
++  int output_index = 0;
++  for (int i = 0; i < frame_count; i++) {
++    // output is two channels, input is one channel, we upmix.
++    output_buffer[output_index] = output_buffer[output_index+1] = input_buffer[i];
++    output_index += 2;
++  }
++}
++
++long cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * /*user_ptr*/,
++                                     const void * input_buffer,
++                                     void * output_buffer, long frame_count)
++{
++  check_duplex<float>(static_cast<const float*>(input_buffer), static_cast<float*>(output_buffer), frame_count);
++  return frame_count;
++}
++
++
++TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
++{
++  // Test that when pre-buffering on resampler creation, we can survive an input
++  // callback being delayed.
++
++  cubeb_stream_params input_params;
++  cubeb_stream_params output_params;
++
++  const int input_channels = 1;
++  const int output_channels = 2;
++
++  input_params.channels = input_channels;
++  input_params.rate = 44100;
++  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
++
++  output_params.channels = output_channels;
++  output_params.rate = input_params.rate;
++  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
++
++  int target_rate = input_params.rate;
++
++  cubeb_resampler * resampler =
++    cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
++                           target_rate, cb_passthrough_resampler_duplex, nullptr,
++                           CUBEB_RESAMPLER_QUALITY_VOIP);
++
++  const long BUF_BASE_SIZE = 256;
++  float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
++  float input_buffer_glitch[input_channels * BUF_BASE_SIZE * 2];
++  float input_buffer_normal[input_channels * BUF_BASE_SIZE];
++  float output_buffer[output_channels * BUF_BASE_SIZE];
++
++  long seq_idx = 0;
++  long output_seq_idx = 0;
++
++  long prebuffer_frames = ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels;
++  seq_idx = seq(input_buffer_prebuffer, input_channels, seq_idx,
++                prebuffer_frames);
++
++  long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
++                                  output_buffer, BUF_BASE_SIZE);
++
++  output_seq_idx += BUF_BASE_SIZE;
++
++  ASSERT_EQ(prebuffer_frames, static_cast<long>(ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels));
++  ASSERT_EQ(got, BUF_BASE_SIZE);
++
++  for (uint32_t i = 0; i < 300; i++) {
++    long int frames = BUF_BASE_SIZE;
++    // Simulate that sometimes, we don't have the input callback on time
++    if (i != 0 && (i % 100) == 0) {
++      long zero = 0;
++      got = cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
++                                 &zero, output_buffer, BUF_BASE_SIZE);
++      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
++      output_seq_idx += BUF_BASE_SIZE;
++    } else if (i != 0 && (i % 100) == 1) {
++      // if this is the case, the on the next iteration, we'll have twice the
++      // amount of input frames
++      seq_idx = seq(input_buffer_glitch, input_channels, seq_idx, BUF_BASE_SIZE * 2);
++      frames = 2 * BUF_BASE_SIZE;
++      got = cubeb_resampler_fill(resampler, input_buffer_glitch, &frames, output_buffer, BUF_BASE_SIZE);
++      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
++      output_seq_idx += BUF_BASE_SIZE;
++    } else {
++       // normal case
++      seq_idx = seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE);
++      long normal_input_frame_count = 256;
++      got = cubeb_resampler_fill(resampler, input_buffer_normal, &normal_input_frame_count, output_buffer, BUF_BASE_SIZE);
++      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
++      output_seq_idx += BUF_BASE_SIZE;
++    }
++    ASSERT_EQ(got, BUF_BASE_SIZE);
++  }
++}
+diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp
+--- a/media/libcubeb/src/cubeb_resampler.cpp
++++ b/media/libcubeb/src/cubeb_resampler.cpp
+@@ -30,39 +30,56 @@ to_speex_quality(cubeb_resampler_quality
+   case CUBEB_RESAMPLER_QUALITY_DESKTOP:
+     return SPEEX_RESAMPLER_QUALITY_DESKTOP;
+   default:
+     assert(false);
+     return 0XFFFFFFFF;
+   }
+ }
+ 
+-long noop_resampler::fill(void * input_buffer, long * input_frames_count,
+-                          void * output_buffer, long output_frames)
++template<typename T>
++passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
++                                                cubeb_data_callback cb,
++                                                void * ptr,
++                                                uint32_t input_channels)
++  : processor(input_channels)
++  , stream(s)
++  , data_callback(cb)
++  , user_ptr(ptr)
++{
++}
++
++template<typename T>
++long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
++                                    void * output_buffer, long output_frames)
+ {
+   if (input_buffer) {
+     assert(input_frames_count);
+   }
+   assert((input_buffer && output_buffer &&
+-         *input_frames_count >= output_frames) ||
+-         (!input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
+-         (!output_buffer && output_frames == 0));
++         *input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
++         (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
++         (input_buffer && !output_buffer && output_frames == 0));
+ 
+-  if (output_buffer == nullptr) {
+-    assert(input_buffer);
+-    output_frames = *input_frames_count;
++  if (input_buffer) {
++    if (!output_buffer) {
++      output_frames = *input_frames_count;
++    }
++    internal_input_buffer.push(static_cast<T*>(input_buffer),
++                               frames_to_samples(*input_frames_count));
+   }
+ 
+-  if (input_buffer && *input_frames_count != output_frames) {
+-    assert(*input_frames_count > output_frames);
+-    *input_frames_count = output_frames;
++  long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
++                          output_buffer, output_frames);
++
++  if (input_buffer) {
++    internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
+   }
+ 
+-  return data_callback(stream, user_ptr,
+-                       input_buffer, output_buffer, output_frames);
++  return rv;
+ }
+ 
+ template<typename T, typename InputProcessor, typename OutputProcessor>
+ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+   ::cubeb_resampler_speex(InputProcessor * input_processor,
+                           OutputProcessor * output_processor,
+                           cubeb_stream * s,
+                           cubeb_data_callback cb,
+diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h
+--- a/media/libcubeb/src/cubeb_resampler_internal.h
++++ b/media/libcubeb/src/cubeb_resampler_internal.h
+@@ -43,41 +43,16 @@ int to_speex_quality(cubeb_resampler_qua
+ 
+ struct cubeb_resampler {
+   virtual long fill(void * input_buffer, long * input_frames_count,
+                     void * output_buffer, long frames_needed) = 0;
+   virtual long latency() = 0;
+   virtual ~cubeb_resampler() {}
+ };
+ 
+-class noop_resampler : public cubeb_resampler {
+-public:
+-  noop_resampler(cubeb_stream * s,
+-                 cubeb_data_callback cb,
+-                 void * ptr)
+-    : stream(s)
+-    , data_callback(cb)
+-    , user_ptr(ptr)
+-  {
+-  }
+-
+-  virtual long fill(void * input_buffer, long * input_frames_count,
+-                    void * output_buffer, long output_frames);
+-
+-  virtual long latency()
+-  {
+-    return 0;
+-  }
+-
+-private:
+-  cubeb_stream * const stream;
+-  const cubeb_data_callback data_callback;
+-  void * const user_ptr;
+-};
+-
+ /** Base class for processors. This is just used to share methods for now. */
+ class processor {
+ public:
+   explicit processor(uint32_t channels)
+     : channels(channels)
+   {}
+ protected:
+   size_t frames_to_samples(size_t frames)
+@@ -88,16 +63,42 @@ protected:
+   {
+     assert(!(samples % channels));
+     return samples / channels;
+   }
+   /** The number of channel of the audio buffers to be resampled. */
+   const uint32_t channels;
+ };
+ 
++template<typename T>
++class passthrough_resampler : public cubeb_resampler
++                            , public processor {
++public:
++  passthrough_resampler(cubeb_stream * s,
++                        cubeb_data_callback cb,
++                        void * ptr,
++                        uint32_t input_channels);
++
++  virtual long fill(void * input_buffer, long * input_frames_count,
++                    void * output_buffer, long output_frames);
++
++  virtual long latency()
++  {
++    return 0;
++  }
++
++private:
++  cubeb_stream * const stream;
++  const cubeb_data_callback data_callback;
++  void * const user_ptr;
++  /* This allows to buffer some input to account for the fact that we buffer
++   * some inputs. */
++  auto_array<T> internal_input_buffer;
++};
++
+ /** Bidirectional resampler, can resample an input and an output stream, or just
+  * an input stream or output stream. In this case a delay is inserted in the
+  * opposite direction to keep the streams synchronized. */
+ template<typename T, typename InputProcessing, typename OutputProcessing>
+ class cubeb_resampler_speex : public cubeb_resampler {
+ public:
+   cubeb_resampler_speex(InputProcessing * input_processor,
+                         OutputProcessing * output_processor,
+@@ -475,17 +476,19 @@ cubeb_resampler_create_internal(cubeb_st
+ 
+   /* All the streams we have have a sample rate that matches the target
+      sample rate, use a no-op resampler, that simply forwards the buffers to the
+      callback. */
+   if (((input_params && input_params->rate == target_rate) &&
+       (output_params && output_params->rate == target_rate)) ||
+       (input_params && !output_params && (input_params->rate == target_rate)) ||
+       (output_params && !input_params && (output_params->rate == target_rate))) {
+-    return new noop_resampler(stream, callback, user_ptr);
++    return new passthrough_resampler<T>(stream, callback,
++                                        user_ptr,
++                                        input_params ? input_params->channels : 0);
+   }
+ 
+   /* Determine if we need to resampler one or both directions, and create the
+      resamplers. */
+   if (output_params && (output_params->rate != target_rate)) {
+     output_resampler.reset(
+         new cubeb_resampler_speex_one_way<T>(output_params->channels,
+                                              target_rate,
+diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp
+--- a/media/libcubeb/src/cubeb_wasapi.cpp
++++ b/media/libcubeb/src/cubeb_wasapi.cpp
+@@ -742,25 +742,16 @@ refill_callback_duplex(cubeb_stream * st
+   }
+ 
+   /* This can only happen when debugging, and having breakpoints set in the
+    * callback in a way that it makes the stream underrun. */
+   if (output_frames == 0) {
+     return true;
+   }
+ 
+-  // When WASAPI has not filled the input buffer yet, send silence.
+-  double output_duration = double(output_frames) / stm->output_mix_params.rate;
+-  double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
+-  if (input_duration < output_duration) {
+-    size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
+-    LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
+-    stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
+-  }
+-
+   LOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
+        stm->linear_input_buffer.length(), output_frames);
+ 
+   refill(stm,
+          stm->linear_input_buffer.data(),
+          stm->linear_input_buffer.length(),
+          output_buffer,
+          output_frames);
+@@ -1646,16 +1637,24 @@ int setup_wasapi_stream(cubeb_stream * s
+                                       stm->input_device.get(),
+                                       eCapture,
+                                       __uuidof(IAudioCaptureClient),
+                                       stm->input_client,
+                                       &stm->input_buffer_frame_count,
+                                       stm->input_available_event,
+                                       stm->capture_client,
+                                       &stm->input_mix_params);
++
++    // We initializing an input stream, buffer ahead two buffers worth of silence.
++    // This delays the input side slightly, but allow to not glitch when no input
++    // is available when calling into the resampler to call the callback: the input
++    // refill event will be set shortly after to compensate for this lack of data.
++    stm->linear_input_buffer.push_silence(stm->input_buffer_frame_count *
++                                          stm->input_stream_params.channels * 2);
++
+     if (rv != CUBEB_OK) {
+       LOG("Failure to open the input side.");
+       return rv;
+     }
+   }
+ 
+   if (has_output(stm)) {
+     LOG("(%p) Setup render: device=%p", stm, stm->output_device.get());