Bug 1290425 - Update cubeb to revision 6278ef2f73. r=padenot
authorAlex Chronopoulos <achronop@gmail.com>
Fri, 29 Jul 2016 13:40:52 +0200
changeset 332342 87ebc9fdddeed6194f2dbe55ebaba09f7f0e7f3a
parent 332341 0220622b0a711db67a58946be4d01197d544bebb
child 332343 00970bf2151cd5ff60b08798063ae1d802c559d2
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1290425
milestone50.0a1
Bug 1290425 - Update cubeb to revision 6278ef2f73. r=padenot
media/libcubeb/README_MOZILLA
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiounit.cpp
media/libcubeb/src/cubeb_sndio.c
media/libcubeb/src/cubeb_utils.h
media/libcubeb/src/cubeb_utils_unix.h
media/libcubeb/src/cubeb_utils_win.h
media/libcubeb/src/cubeb_wasapi.cpp
media/libcubeb/tests/test_sanity.cpp
media/libcubeb/update.sh
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 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 2a5fd74b1122ff4e4c445c1e092932be1273646e.
+The git commit ID used was fec2a837862fd0feaee1bf3f34bbe9754186b7ac.
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -256,17 +256,17 @@ alsa_refill_stream(cubeb_stream * stm)
   void * p;
   int draining;
 
   draining = 0;
 
   pthread_mutex_lock(&stm->mutex);
 
   avail = snd_pcm_avail_update(stm->pcm);
-  if (avail == -EPIPE) {
+  if (avail < 0) {
     snd_pcm_recover(stm->pcm, avail, 1);
     avail = snd_pcm_avail_update(stm->pcm);
   }
 
   /* Failed to recover from an xrun, this stream must be broken. */
   if (avail < 0) {
     pthread_mutex_unlock(&stm->mutex);
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
@@ -275,36 +275,32 @@ alsa_refill_stream(cubeb_stream * stm)
 
   /* This should never happen. */
   if ((unsigned int) avail > stm->buffer_size) {
     avail = stm->buffer_size;
   }
 
   /* poll(2) claims this stream is active, so there should be some space
      available to write.  If avail is still zero here, the stream must be in
-     a funky state, so recover and try again. */
+     a funky state, bail and wait for another wakeup. */
   if (avail == 0) {
-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
-    avail = snd_pcm_avail_update(stm->pcm);
-    if (avail <= 0) {
-      pthread_mutex_unlock(&stm->mutex);
-      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
-      return ERROR;
-    }
+    pthread_mutex_unlock(&stm->mutex);
+    return RUNNING;
   }
 
   p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
   assert(p);
 
   pthread_mutex_unlock(&stm->mutex);
   got = stm->data_callback(stm, stm->user_ptr, NULL, p, avail);
   pthread_mutex_lock(&stm->mutex);
   if (got < 0) {
     pthread_mutex_unlock(&stm->mutex);
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+    free(p);
     return ERROR;
   }
   if (got > 0) {
     snd_pcm_sframes_t wrote;
 
     if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
       float * b = (float *) p;
       for (uint32_t i = 0; i < got * stm->params.channels; i++) {
@@ -312,17 +308,17 @@ alsa_refill_stream(cubeb_stream * stm)
       }
     } else {
       short * b = (short *) p;
       for (uint32_t i = 0; i < got * stm->params.channels; i++) {
         b[i] *= stm->volume;
       }
     }
     wrote = snd_pcm_writei(stm->pcm, p, got);
-    if (wrote == -EPIPE) {
+    if (wrote < 0) {
       snd_pcm_recover(stm->pcm, wrote, 1);
       wrote = snd_pcm_writei(stm->pcm, p, got);
     }
     assert(wrote >= 0 && wrote == got);
     stm->write_position += wrote;
     gettimeofday(&stm->last_activity, NULL);
   }
   if (got != avail) {
@@ -384,16 +380,18 @@ alsa_run(cubeb * ctx)
       if (ctx->shutdown) {
         pthread_mutex_unlock(&ctx->mutex);
         return -1;
       }
     }
 
     for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
       stm = ctx->streams[i];
+      /* We can't use snd_pcm_poll_descriptors_revents here because of
+         https://github.com/kinetiknz/cubeb/issues/135. */
       if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
         alsa_set_stream_state(stm, PROCESSING);
         pthread_mutex_unlock(&ctx->mutex);
         state = alsa_refill_stream(stm);
         pthread_mutex_lock(&ctx->mutex);
         alsa_set_stream_state(stm, state);
       }
     }
@@ -966,17 +964,17 @@ alsa_get_preferred_sample_rate(cubeb * c
   int r, dir;
   snd_pcm_t * pcm;
   snd_pcm_hw_params_t * hw_params;
 
   snd_pcm_hw_params_alloca(&hw_params);
 
   /* get a pcm, disabling resampling, so we get a rate the
    * hardware/dmix/pulse/etc. supports. */
-  r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+  r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
   if (r < 0) {
     return CUBEB_ERROR;
   }
 
   r = snd_pcm_hw_params_any(pcm, hw_params);
   if (r < 0) {
     snd_pcm_close(pcm);
     return CUBEB_ERROR;
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -8,16 +8,17 @@
 
 #include <TargetConditionals.h>
 #include <assert.h>
 #include <mach/mach_time.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <AudioUnit/AudioUnit.h>
 #if !TARGET_OS_IPHONE
+#include <AvailabilityMacros.h>
 #include <CoreAudio/AudioHardware.h>
 #include <CoreAudio/HostTime.h>
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 #include <CoreAudio/CoreAudioTypes.h>
 #include <AudioToolbox/AudioToolbox.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
@@ -38,17 +39,17 @@
 #if !TARGET_OS_IPHONE && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
 #define AudioComponent Component
 #define AudioComponentDescription ComponentDescription
 #define AudioComponentFindNext FindNextComponent
 #define AudioComponentInstanceNew OpenAComponent
 #define AudioComponentInstanceDispose CloseComponent
 #endif
 
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
 typedef UInt32  AudioFormatFlags;
 #endif
 
 #define CUBEB_STREAM_MAX 8
 
 #define AU_OUT_BUS    0
 #define AU_IN_BUS     1
 
@@ -57,32 +58,41 @@ typedef UInt32  AudioFormatFlags;
 #else
 #define CUBEB_AUDIOUNIT_SUBTYPE kAudioUnitSubType_HALOutput
 #endif
 
 //#define LOGGING_ENABLED
 #ifdef LOGGING_ENABLED
 #define LOG(...) do {                           \
     fprintf(stderr, __VA_ARGS__);               \
+    fprintf(stderr, "(line: %d)\n", __LINE__);  \
   } while(0)
 #else
 #define LOG(...)
 #endif
 
+#ifdef LOGGING_ENABLED
+#define PRINT_ERROR_CODE(str, r) do {                                \
+    fprintf(stderr, "Error %s (line: %d) (%d)\n", str, __LINE__, r); \
+  } while (0)
+#else
+#define PRINT_ERROR_CODE(str, r)
+#endif
 
 /* 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;
 
 extern cubeb_ops const audiounit_ops;
 
 struct cubeb {
   cubeb_ops const * ops;
-  pthread_mutex_t mutex;
+  owned_critical_section 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;
@@ -157,17 +167,17 @@ struct cubeb_stream {
   /* Format descriptions */
   AudioStreamBasicDescription input_desc;
   AudioStreamBasicDescription output_desc;
   /* I/O AudioUnits */
   AudioUnit input_unit;
   AudioUnit output_unit;
   /* Sample rate of input device*/
   Float64 input_hw_rate;
-  pthread_mutex_t mutex;
+  owned_critical_section mutex;
   /* Hold the input samples in every
    * input callback iteration */
   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;
@@ -229,40 +239,40 @@ audiounit_render_input(cubeb_stream * st
                        AudioUnitRenderActionFlags * flags,
                        AudioTimeStamp const * tstamp,
                        UInt32 bus,
                        UInt32 input_frames)
 {
   /* Create the AudioBufferList to store input. */
   AudioBufferList input_buffer_list;
   input_buffer_list.mBuffers[0].mDataByteSize =
-      stm->input_desc.mBytesPerFrame * stm->input_buffer_frames;
+      stm->input_desc.mBytesPerFrame * input_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\n", r);
+    PRINT_ERROR_CODE("AudioUnitRender", 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",
+  LOG("- input:  buffers %d, size %d, channels %d, frames %d.",
       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;
@@ -276,36 +286,34 @@ audiounit_input_callback(void * user_ptr
                          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);
+  auto_lock 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);
+    LOG("- input shutdown");
     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;
   assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
   outframes = cubeb_resampler_fill(stm->resampler,
@@ -313,80 +321,76 @@ audiounit_input_callback(void * user_ptr
                                    &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);
   return noErr;
 }
 
 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);
 
-  LOG("- output(%p): buffers %d, size %d, channels %d, frames %d\n", stm,
+  LOG("- output(%p): buffers %d, size %d, channels %d, frames %d.", 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);
+  auto_lock lock(stm->mutex);
 
   if (stm->shutdown) {
-    LOG("- output shutdown\n");
+    LOG("- output shutdown.");
     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);
     assert(r == 0);
     if (stm->input_unit) {
       r = AudioOutputUnitStop(stm->input_unit);
       assert(r == 0);
     }
     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 */
   if (stm->input_unit != NULL) {
     /* Output callback came first */
     if (stm->frames_read == 0) {
-      LOG("Output callback came first send silent.\n");
+      LOG("Output callback came first send silent.");
       stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
                                             stm->input_desc.mChannelsPerFrame);
     }
     /* Input samples stored previously in input callback. */
     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");
+      LOG("Input hole. Requested more input than ouput.");
     }
     // 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. */
@@ -397,28 +401,26 @@ audiounit_output_callback(void * user_pt
                                    output_frames);
 
   if (input_buffer) {
     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;
   }
 
   size_t outbpf = stm->output_desc.mBytesPerFrame;
   stm->draining = outframes < output_frames;
   stm->frames_played = stm->frames_queued;
   stm->frames_queued += outframes;
 
   AudioFormatFlags outaff = stm->output_desc.mFormatFlags;
   float panning = (stm->output_desc.mChannelsPerFrame == 2) ? stm->panning : 0.0f;
-  pthread_mutex_unlock(&stm->mutex);
 
   /* Post process output samples. */
   if (stm->draining) {
     /* Clear missing frames (silence) */
     memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf);
   }
   /* Pan stereo. */
   if (panning != 0.0f) {
@@ -431,28 +433,26 @@ audiounit_output_callback(void * user_pt
   return noErr;
 }
 
 extern "C" {
 int
 audiounit_init(cubeb ** context, char const * context_name)
 {
   cubeb * ctx;
-  int r;
 
   *context = NULL;
 
   ctx = new cubeb;
   assert(ctx);
   PodZero(ctx, 1);
 
   ctx->ops = &audiounit_ops;
 
-  r = pthread_mutex_init(&ctx->mutex, NULL);
-  assert(r == 0);
+  ctx->mutex = owned_critical_section();
 
   ctx->active_streams = 0;
 
   ctx->limit_streams = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7;
 #if !TARGET_OS_IPHONE
   cubeb_set_coreaudio_notification_runloop();
 #endif
 
@@ -484,16 +484,17 @@ audiounit_get_output_device_id(AudioDevi
 
   r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                  &output_device_address,
                                  0,
                                  NULL,
                                  &size,
                                  device_id);
   if (r != noErr) {
+    PRINT_ERROR_CODE("output_device_id", r);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_get_input_device_id(AudioDeviceID * device_id)
@@ -528,23 +529,23 @@ audiounit_property_listener_callback(Aud
 {
   cubeb_stream * stm = (cubeb_stream*) user;
 
   for (UInt32 i = 0; i < address_count; i++) {
     switch(addresses[i].mSelector) {
     case kAudioHardwarePropertyDefaultOutputDevice:
     case kAudioHardwarePropertyDefaultInputDevice:
       /* fall through */
-    case kAudioDevicePropertyDataSource:
-      pthread_mutex_lock(&stm->mutex);
-      if (stm->device_changed_callback) {
-        stm->device_changed_callback(stm->user_ptr);
+    case kAudioDevicePropertyDataSource: {
+        auto_lock lock(stm->mutex);
+        if (stm->device_changed_callback) {
+          stm->device_changed_callback(stm->user_ptr);
+        }
+        break;
       }
-      pthread_mutex_unlock(&stm->mutex);
-      break;
     }
   }
 
   return noErr;
 }
 
 OSStatus
 audiounit_add_listener(cubeb_stream * stm, AudioDeviceID id, AudioObjectPropertySelector selector,
@@ -687,29 +688,31 @@ audiounit_get_acceptable_latency_range(A
   AudioDeviceID output_device_id;
   AudioObjectPropertyAddress output_device_buffer_size_range = {
     kAudioDevicePropertyBufferFrameSizeRange,
     kAudioDevicePropertyScopeOutput,
     kAudioObjectPropertyElementMaster
   };
 
   if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
+    LOG("Could not get default output device id.");
     return CUBEB_ERROR;
   }
 
   /* Get the buffer size range this device supports */
   size = sizeof(*latency_range);
 
   r = AudioObjectGetPropertyData(output_device_id,
                                  &output_device_buffer_size_range,
                                  0,
                                  NULL,
                                  &size,
                                  latency_range);
   if (r != noErr) {
+    PRINT_ERROR_CODE("AudioObjectGetPropertyData/buffer size range", r);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 #endif /* !TARGET_OS_IPHONE */
 
 static AudioObjectID
@@ -762,16 +765,17 @@ audiounit_get_max_channel_count(cubeb * 
 
   r = AudioObjectGetPropertyData(output_device_id,
                                  &stream_format_address,
                                  0,
                                  NULL,
                                  &size,
                                  &stream_format);
   if (r != noErr) {
+    PRINT_ERROR_CODE("AudioObjectPropertyAddress/StreamFormat", r);
     return CUBEB_ERROR;
   }
 
   *max_channels = stream_format.mChannelsPerFrame;
 #endif
   return CUBEB_OK;
 }
 
@@ -779,16 +783,17 @@ static int
 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) {
+    LOG("Could not get acceptable latency range.");
     return CUBEB_ERROR;
   }
 
   *latency_frames = std::max<uint32_t>(latency_range.mMinimum,
                                        SAFE_MIN_LATENCY_FRAMES);
 #endif
 
   return CUBEB_OK;
@@ -837,24 +842,20 @@ static OSStatus audiounit_remove_device_
 static void
 audiounit_destroy(cubeb * ctx)
 {
   // Disabling this assert for bug 1083664 -- we seem to leak a stream
   // assert(ctx->active_streams == 0);
 
   /* Unregister the callback if necessary. */
   if(ctx->collection_changed_callback) {
-    pthread_mutex_lock(&ctx->mutex);
+    auto_lock lock(ctx->mutex);
     audiounit_remove_device_listener(ctx);
-    pthread_mutex_unlock(&ctx->mutex);
   }
 
-  int r = pthread_mutex_destroy(&ctx->mutex);
-  assert(r == 0);
-
   delete ctx;
 }
 
 static void audiounit_stream_destroy(cubeb_stream * stm);
 
 static int
 audio_stream_desc_init(AudioStreamBasicDescription * ss,
                        const cubeb_stream_params * stream_params)
@@ -901,56 +902,65 @@ audiounit_create_unit(AudioUnit * unit,
                       bool is_input,
                       const cubeb_stream_params * stream_params,
                       cubeb_devid device)
 {
   AudioComponentDescription desc;
   AudioComponent comp;
   UInt32 enable;
   AudioDeviceID devid;
+  OSStatus rv;
 
   desc.componentType = kAudioUnitType_Output;
   desc.componentSubType = CUBEB_AUDIOUNIT_SUBTYPE;
   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
   desc.componentFlags = 0;
   desc.componentFlagsMask = 0;
   comp = AudioComponentFindNext(NULL, &desc);
   if (comp == NULL) {
+    LOG("Could not find matching audio hardware.");
     return CUBEB_ERROR;
   }
 
-  if (AudioComponentInstanceNew(comp, unit) != 0) {
+  rv = AudioComponentInstanceNew(comp, unit);
+  if (rv != noErr) {
+    PRINT_ERROR_CODE("AudioComponentInstanceNew", rv);
     return CUBEB_ERROR;
   }
 
   enable = 1;
-  if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
-        is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
-        is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32)) != noErr) {
+  rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+           is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
+           is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32));
+  if (rv != noErr) {
+    PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
     return CUBEB_ERROR;
   }
 
   enable = 0;
-  if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
-        is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
-        is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32)) != noErr) {
+  rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+            is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
+            is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32));
+  if (rv != noErr) {
+    PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
     return CUBEB_ERROR;
   }
 
   if (device == NULL) {
     devid = audiounit_get_default_device_id(is_input ? CUBEB_DEVICE_TYPE_INPUT
                                                      : CUBEB_DEVICE_TYPE_OUTPUT);
   } else {
     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) {
+    PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice", rv);
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
@@ -986,16 +996,81 @@ audiounit_init_input_linear_buffer(cubeb
 }
 
 static void
 audiounit_destroy_input_linear_buffer(cubeb_stream * stream)
 {
   delete stream->input_linear_buffer;
 }
 
+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);
+  if (stm->context->active_streams == 1) {
+    return std::max(std::min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
+                    SAFE_MIN_LATENCY_FRAMES);
+  }
+
+  // If more than one stream operates in parallel
+  // allow only lower values of latency
+  int r;
+  UInt32 output_buffer_size = 0;
+  UInt32 size = sizeof(output_buffer_size);
+  if (stm->output_unit) {
+    r = AudioUnitGetProperty(stm->output_unit,
+                            kAudioDevicePropertyBufferFrameSize,
+                            kAudioUnitScope_Output,
+                            AU_OUT_BUS,
+                            &output_buffer_size,
+                            &size);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
+      return 0;
+    }
+
+    output_buffer_size = std::max(std::min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
+                                  SAFE_MIN_LATENCY_FRAMES);
+  }
+
+  UInt32 input_buffer_size = 0;
+  if (stm->input_unit) {
+    r = AudioUnitGetProperty(stm->input_unit,
+                            kAudioDevicePropertyBufferFrameSize,
+                            kAudioUnitScope_Input,
+                            AU_IN_BUS,
+                            &input_buffer_size,
+                            &size);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
+      return 0;
+    }
+
+    input_buffer_size = std::max(std::min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
+                                 SAFE_MIN_LATENCY_FRAMES);
+  }
+
+  // Every following active streams can only set smaller latency
+  UInt32 upper_latency_limit = 0;
+  if (input_buffer_size != 0 && output_buffer_size != 0) {
+    upper_latency_limit = std::min<uint32_t>(input_buffer_size, output_buffer_size);
+  } else if (input_buffer_size != 0) {
+    upper_latency_limit = input_buffer_size;
+  } else if (output_buffer_size != 0) {
+    upper_latency_limit = output_buffer_size;
+  } else {
+    upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
+  }
+
+  return std::max(std::min<uint32_t>(latency_frames, upper_latency_limit),
+                  SAFE_MIN_LATENCY_FRAMES);
+}
+
 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,
@@ -1010,40 +1085,41 @@ audiounit_stream_init(cubeb * context,
   int r;
   AURenderCallbackStruct aurcbs_in;
   AURenderCallbackStruct aurcbs_out;
   UInt32 size;
 
   assert(context);
   *stream = NULL;
 
-  pthread_mutex_lock(&context->mutex);
-  if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
-    pthread_mutex_unlock(&context->mutex);
-    return CUBEB_ERROR;
+  {
+    auto_lock lock(context->mutex);
+    if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
+      LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
+      return CUBEB_ERROR;
+    }
+    context->active_streams += 1;
   }
-  context->active_streams += 1;
-  pthread_mutex_unlock(&context->mutex);
 
   if (input_stream_params != NULL) {
     r = audiounit_create_unit(&input_unit, true,
                               input_stream_params,
                               input_device);
     if (r != CUBEB_OK) {
-      LOG("Create input stream failed\n");
+      LOG("AudioUnit creation for input failed.");
       return r;
     }
   }
 
   if (output_stream_params != NULL) {
     r = audiounit_create_unit(&output_unit, false,
                               output_stream_params,
                               output_device);
     if (r != CUBEB_OK) {
-      LOG("Create output stream failed\n");
+      LOG("AudioUnit creation for output failed.");
       return r;
     }
   }
 
   stm = new cubeb_stream;
   assert(stm);
   PodZero(stm, 1);
 
@@ -1051,117 +1127,94 @@ audiounit_stream_init(cubeb * context,
    * 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;
   stm->user_ptr = user_ptr;
   stm->device_changed_callback = NULL;
-
-  pthread_mutexattr_t attr;
-  pthread_mutexattr_init(&attr);
-  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-  r = pthread_mutex_init(&stm->mutex, &attr);
-  assert(r == 0);
-  pthread_mutexattr_destroy(&attr);
+  stm->mutex = owned_critical_section();
 
   /* Init data members where necessary */
   stm->hw_latency_frames = UINT64_MAX;
 
   /* 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;
-    }
-  } 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);
+  latency_frames = audiounit_clamp_latency(stm, latency_frames);
+  assert(latency_frames > 0);
 
   /* 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,
-                             &size) != 0) {
+    r = AudioUnitGetProperty(stm->input_unit,
+                            kAudioUnitProperty_StreamFormat,
+                            kAudioUnitScope_Input,
+                            AU_IN_BUS,
+                            &input_hw_desc,
+                            &size);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
     stm->input_hw_rate = input_hw_desc.mSampleRate;
 
     /* Set format description according to the input params. */
     r = audio_stream_desc_init(&stm->input_desc, input_stream_params);
     if (r != CUBEB_OK) {
+      LOG("Setting format description for input failed.");
       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,
+    // Use latency to set buffer size
+    stm->input_buffer_frames = latency_frames;
+    LOG("Input buffer frame count %u.", stm->input_buffer_frames);
+    r = AudioUnitSetProperty(stm->input_unit,
                              kAudioDevicePropertyBufferFrameSize,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &stm->input_buffer_frames,
-                             sizeof(UInt32)) != 0) {
+                             sizeof(UInt32));
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
       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,
+    r = AudioUnitSetProperty(stm->input_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &src_desc,
-                             sizeof(AudioStreamBasicDescription)) != 0) {
+                             sizeof(AudioStreamBasicDescription));
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     /* Frames per buffer in the input callback. */
-    if (AudioUnitSetProperty(stm->input_unit,
+    r = AudioUnitSetProperty(stm->input_unit,
                              kAudioUnitProperty_MaximumFramesPerSlice,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &stm->input_buffer_frames,
-                             sizeof(UInt32))) {
+                             sizeof(UInt32));
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
     // Input only capacity
     unsigned int array_capacity = 1;
     if (output_stream_params) {
       // Full-duplex increase capacity
@@ -1170,94 +1223,98 @@ audiounit_stream_init(cubeb * context,
     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,
+
+    r = AudioUnitSetProperty(stm->input_unit,
                              kAudioOutputUnitProperty_SetInputCallback,
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_in,
-                             sizeof(aurcbs_in)) != 0) {
+                             sizeof(aurcbs_in));
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
-    LOG("Input audiounit init successfully.\n");
+    LOG("Input audiounit init successfully.");
   }
 
   /* Setup Output Stream! */
   if (output_stream_params != NULL) {
     r = audio_stream_desc_init(&stm->output_desc, output_stream_params);
     if (r != CUBEB_OK) {
+      LOG("Could not initialize the audio stream description.");
       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,
+    r = AudioUnitGetProperty(stm->output_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              AU_OUT_BUS,
                              &output_hw_desc,
-                             &size) != 0) {
+                             &size);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
       audiounit_stream_destroy(stm);
       return CUBEB_ERROR;
     }
 
-    if (AudioUnitSetProperty(stm->output_unit,
+    r = AudioUnitSetProperty(stm->output_unit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Input,
                              AU_OUT_BUS,
                              &stm->output_desc,
-                             sizeof(AudioStreamBasicDescription)) != 0) {
+                             sizeof(AudioStreamBasicDescription));
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
       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,
+    uint32_t output_buffer_frames = latency_frames;
+    LOG("Output buffer frame count %u.", output_buffer_frames);
+    r = AudioUnitSetProperty(stm->output_unit,
                              kAudioDevicePropertyBufferFrameSize,
                              kAudioUnitScope_Input,
                              AU_OUT_BUS,
                              &output_buffer_frames,
-                             sizeof(output_buffer_frames)) != 0) {
+                             sizeof(output_buffer_frames));
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
       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,
+    r = AudioUnitSetProperty(stm->output_unit,
                              kAudioUnitProperty_SetRenderCallback,
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_out,
-                             sizeof(aurcbs_out)) != 0) {
+                             sizeof(aurcbs_out));
+    if (r != noErr) {
       audiounit_stream_destroy(stm);
+      PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
       return CUBEB_ERROR;
     }
-    LOG("Output audiounit init successfully.\n");
+    LOG("Output audiounit init successfully.");
   }
 
   // Setting the latency doesn't work well for USB headsets (eg. plantronics).
   // Keep the default latency for now.
 #if 0
   buffer_size = latency;
 
   /* Get the range of latency this particular device can work with, and clamp
@@ -1324,34 +1381,41 @@ audiounit_stream_init(cubeb * context,
   stm->resampler = cubeb_resampler_create(stm,
                                           input_stream_params ? &input_unconverted_params : NULL,
                                           output_stream_params,
                                           target_sample_rate,
                                           stm->data_callback,
                                           stm->user_ptr,
                                           CUBEB_RESAMPLER_QUALITY_DESKTOP);
   if (!stm->resampler) {
-    LOG("Could not create resampler\n");
+    LOG("Could not create resampler.");
     audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
-  if (stm->input_unit != NULL &&
-      AudioUnitInitialize(stm->input_unit) != 0) {
-    audiounit_stream_destroy(stm);
-    return CUBEB_ERROR;
+  if (stm->input_unit != NULL) {
+    r = AudioUnitInitialize(stm->input_unit);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitInitialize/input", r);
+      audiounit_stream_destroy(stm);
+      return CUBEB_ERROR;
+    }
   }
-  if (stm->output_unit != NULL &&
-      AudioUnitInitialize(stm->output_unit) != 0) {
-    audiounit_stream_destroy(stm);
-    return CUBEB_ERROR;
+
+  if (stm->output_unit != NULL) {
+    r = AudioUnitInitialize(stm->output_unit);
+    if (r != noErr) {
+      PRINT_ERROR_CODE("AudioUnitInitialize/output", r);
+      audiounit_stream_destroy(stm);
+      return CUBEB_ERROR;
+    }
   }
 
   *stream = stm;
-  LOG("Cubeb stream (%p) init successful.\n", stm);
+  LOG("Cubeb stream (%p) init successful.", stm);
   return CUBEB_OK;
 }
 
 static void
 audiounit_stream_destroy(cubeb_stream * stm)
 {
   stm->shutdown = 1;
 
@@ -1370,85 +1434,87 @@ audiounit_stream_destroy(cubeb_stream * 
   }
 
   cubeb_resampler_destroy(stm->resampler);
 
 #if !TARGET_OS_IPHONE
   audiounit_uninstall_device_changed_callback(stm);
 #endif
 
-  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);
+  {
+    auto_lock lock(stm->context->mutex);
+    assert(stm->context->active_streams >= 1);
+    stm->context->active_streams -= 1;
+  }
 
   delete stm;
 }
 
 static int
 audiounit_stream_start(cubeb_stream * stm)
 {
-  pthread_mutex_lock(&stm->context->mutex);
-  stm->shutdown = 0;
-  stm->draining = 0;
+  {
+    auto_lock lock(stm->mutex);
+    stm->shutdown = 0;
+    stm->draining = 0;
+  }
+
   OSStatus r;
   if (stm->input_unit != NULL) {
     r = AudioOutputUnitStart(stm->input_unit);
     assert(r == 0);
   }
   if (stm->output_unit != NULL) {
     r = AudioOutputUnitStart(stm->output_unit);
     assert(r == 0);
   }
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
-  LOG("Cubeb stream (%p) started successfully.\n", stm);
-  pthread_mutex_unlock(&stm->context->mutex);
+  LOG("Cubeb stream (%p) started successfully.", stm);
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_stop(cubeb_stream * stm)
 {
-  pthread_mutex_lock(&stm->context->mutex);
-  stm->shutdown = 1;
+  {
+    auto_lock lock(stm->mutex);
+    stm->shutdown = 1;
+  }
+
   OSStatus r;
   if (stm->input_unit != NULL) {
     r = AudioOutputUnitStop(stm->input_unit);
     assert(r == 0);
   }
   if (stm->output_unit != NULL) {
     r = AudioOutputUnitStop(stm->output_unit);
     assert(r == 0);
   }
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
-  LOG("Cubeb stream (%p) stopped successfully.\n", stm);
-  pthread_mutex_unlock(&stm->context->mutex);
+  LOG("Cubeb stream (%p) stopped successfully.", stm);
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
-  pthread_mutex_lock(&stm->mutex);
+  auto_lock lock(stm->mutex);
+
   *position = stm->frames_played;
-  pthread_mutex_unlock(&stm->mutex);
   return CUBEB_OK;
 }
 
 int
 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
 #if TARGET_OS_IPHONE
   //TODO
   return CUBEB_ERROR_NOT_SUPPORTED;
 #else
-  pthread_mutex_lock(&stm->mutex);
+  auto_lock lock(stm->mutex);
   if (stm->hw_latency_frames == UINT64_MAX) {
     UInt32 size;
     uint32_t device_latency_frames, device_safety_offset;
     double unit_latency_sec;
     AudioDeviceID output_device_id;
     OSStatus r;
     AudioObjectPropertyAddress latency_address = {
       kAudioDevicePropertyLatency,
@@ -1458,94 +1524,94 @@ audiounit_stream_get_latency(cubeb_strea
     AudioObjectPropertyAddress safety_offset_address = {
       kAudioDevicePropertySafetyOffset,
       kAudioDevicePropertyScopeOutput,
       kAudioObjectPropertyElementMaster
     };
 
     r = audiounit_get_output_device_id(&output_device_id);
     if (r != noErr) {
-      pthread_mutex_unlock(&stm->mutex);
       return CUBEB_ERROR;
     }
 
     size = sizeof(unit_latency_sec);
     r = AudioUnitGetProperty(stm->output_unit,
                              kAudioUnitProperty_Latency,
                              kAudioUnitScope_Global,
                              0,
                              &unit_latency_sec,
                              &size);
     if (r != noErr) {
-      pthread_mutex_unlock(&stm->mutex);
+      PRINT_ERROR_CODE("AudioUnitGetProperty/kAudioUnitProperty_Latency", r);
       return CUBEB_ERROR;
     }
 
     size = sizeof(device_latency_frames);
     r = AudioObjectGetPropertyData(output_device_id,
                                    &latency_address,
                                    0,
                                    NULL,
                                    &size,
                                    &device_latency_frames);
     if (r != noErr) {
-      pthread_mutex_unlock(&stm->mutex);
+      PRINT_ERROR_CODE("AudioUnitGetPropertyData/latency_frames", r);
       return CUBEB_ERROR;
     }
 
     size = sizeof(device_safety_offset);
     r = AudioObjectGetPropertyData(output_device_id,
                                    &safety_offset_address,
                                    0,
                                    NULL,
                                    &size,
                                    &device_safety_offset);
     if (r != noErr) {
-      pthread_mutex_unlock(&stm->mutex);
+      PRINT_ERROR_CODE("AudioUnitGetPropertyData/safety_offset", r);
       return CUBEB_ERROR;
     }
 
     /* This part is fixed and depend on the stream parameter and the hardware. */
     stm->hw_latency_frames =
       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;
 #endif
 }
 
 int audiounit_stream_set_volume(cubeb_stream * stm, float volume)
 {
   OSStatus r;
 
   r = AudioUnitSetParameter(stm->output_unit,
                             kHALOutputParam_Volume,
                             kAudioUnitScope_Global,
                             0, volume, 0);
 
   if (r != noErr) {
+    PRINT_ERROR_CODE("AudioUnitSetParameter/kHALOutputParam_Volume", r);
     return CUBEB_ERROR;
   }
   return CUBEB_OK;
 }
 
 int audiounit_stream_set_panning(cubeb_stream * stm, float panning)
 {
   if (stm->output_desc.mChannelsPerFrame > 2) {
     return CUBEB_ERROR_INVALID_PARAMETER;
   }
 
-  pthread_mutex_lock(&stm->mutex);
-  stm->panning = panning;
-  pthread_mutex_unlock(&stm->mutex);
+  {
+    auto_lock lock(stm->mutex);
+    stm->panning = panning;
+  }
 
   return CUBEB_OK;
 }
 
 int audiounit_stream_get_current_device(cubeb_stream * stm,
                                         cubeb_device ** const  device)
 {
 #if TARGET_OS_IPHONE
@@ -1609,17 +1675,17 @@ int audiounit_stream_get_current_device(
 
   if (audiounit_get_input_device_id(&input_device_id) != CUBEB_OK) {
     return CUBEB_ERROR;
   }
 
   size = sizeof(UInt32);
   r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data);
   if (r != noErr) {
-    LOG("Error when getting device !\n");
+    LOG("Error when getting device !");
     size = 0;
     data = 0;
   }
 
   (*device)->input_name = new char[size + 1];
   if (!(*device)->input_name) {
     return CUBEB_ERROR;
   }
@@ -1648,27 +1714,27 @@ int audiounit_stream_device_destroy(cube
 
 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. */
   assert(!stream->device_changed_callback);
 
-  pthread_mutex_lock(&stream->mutex);
+  auto_lock lock(stream->mutex);
+
   stream->device_changed_callback = device_changed_callback;
   int r = CUBEB_OK;
 #if !TARGET_OS_IPHONE
   if (device_changed_callback) {
     r = audiounit_install_device_changed_callback(stream);
   } else {
     r = audiounit_uninstall_device_changed_callback(stream);
   }
 #endif
-  pthread_mutex_unlock(&stream->mutex);
 
   return r;
 }
 
 static OSStatus
 audiounit_get_devices(AudioObjectID ** devices, uint32_t * count)
 {
   OSStatus ret;
@@ -2023,45 +2089,43 @@ audiounit_equal_arrays(AudioObjectID * l
 
 static OSStatus
 audiounit_collection_changed_callback(AudioObjectID inObjectID,
                                       UInt32 inNumberAddresses,
                                       const AudioObjectPropertyAddress * inAddresses,
                                       void * inClientData)
 {
   cubeb * context = static_cast<cubeb *>(inClientData);
-  pthread_mutex_lock(&context->mutex);
+  auto_lock 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. */
       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. */
     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;
 }
 
 static OSStatus
 audiounit_add_device_listener(cubeb * context,
                               cubeb_device_type devtype,
                               cubeb_device_collection_changed_callback collection_changed_callback,
                               void * user_ptr)
@@ -2125,25 +2189,24 @@ audiounit_remove_device_listener(cubeb *
 }
 
 int audiounit_register_device_collection_changed(cubeb * context,
                                                  cubeb_device_type devtype,
                                                  cubeb_device_collection_changed_callback collection_changed_callback,
                                                  void * user_ptr)
 {
   OSStatus ret;
-  pthread_mutex_lock(&context->mutex);
+  auto_lock lock(context->mutex);
   if (collection_changed_callback) {
     ret = audiounit_add_device_listener(context, devtype,
                                         collection_changed_callback,
                                         user_ptr);
   } else {
     ret = audiounit_remove_device_listener(context);
   }
-  pthread_mutex_unlock(&context->mutex);
   return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
 }
 
 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,
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -282,20 +282,20 @@ sndio_get_preferred_sample_rate(cubeb * 
 {
   // XXX Not yet implemented.
   *rate = 44100;
 
   return CUBEB_OK;
 }
 
 static int
-sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
+sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   // XXX Not yet implemented.
-  *latency = 2048;
+  *latency_frames = 2048;
 
   return CUBEB_OK;
 }
 
 static void
 sndio_stream_destroy(cubeb_stream *s)
 {
   DPR("sndio_stream_destroy()\n");
--- a/media/libcubeb/src/cubeb_utils.h
+++ b/media/libcubeb/src/cubeb_utils.h
@@ -6,16 +6,21 @@
  */
 
 #if !defined(CUBEB_UTILS)
 #define CUBEB_UTILS
 
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
+#if defined(WIN32)
+#include "cubeb_utils_win.h"
+#else
+#include "cubeb_utils_unix.h"
+#endif
 
 /** Similar to memcpy, but accounts for the size of an element. */
 template<typename T>
 void PodCopy(T * destination, const T * source, size_t count)
 {
   memcpy(destination, source, count * sizeof(T));
 }
 
@@ -184,9 +189,23 @@ private:
   /** The underlying storage */
   T * data_;
   /** The size, in number of elements, of the storage. */
   size_t capacity_;
   /** The number of elements the array contains. */
   size_t length_;
 };
 
+struct auto_lock {
+  auto_lock(owned_critical_section & lock)
+    : lock(lock)
+  {
+    lock.enter();
+  }
+  ~auto_lock()
+  {
+    lock.leave();
+  }
+private:
+  owned_critical_section & lock;
+};
+
 #endif /* CUBEB_UTILS */
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_utils_unix.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(CUBEB_UTILS_UNIX)
+#define CUBEB_UTILS_UNIX
+
+#include <pthread.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* This wraps a critical section to track the owner in debug mode. */
+class owned_critical_section
+{
+public:
+  owned_critical_section()
+  {
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+#ifdef DEBUG
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#else
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+#endif
+
+#ifdef DEBUG
+    int r =
+#endif
+    pthread_mutex_init(&mutex, &attr);
+#ifdef DEBUG
+    assert(r == 0);
+#endif
+
+    pthread_mutexattr_destroy(&attr);
+  }
+
+  ~owned_critical_section()
+  {
+#ifdef DEBUG
+    int r =
+#endif
+    pthread_mutex_destroy(&mutex);
+#ifdef DEBUG
+    assert(r == 0);
+#endif
+  }
+
+  void enter()
+  {
+#ifdef DEBUG
+    int r =
+#endif
+    pthread_mutex_lock(&mutex);
+#ifdef DEBUG
+    assert(r == 0 && "Deadlock");
+#endif
+  }
+
+  void leave()
+  {
+#ifdef DEBUG
+    int r =
+#endif
+    pthread_mutex_unlock(&mutex);
+#ifdef DEBUG
+    assert(r == 0 && "Unlocking unlocked mutex");
+#endif
+  }
+
+  void assert_current_thread_owns()
+  {
+#ifdef DEBUG
+    int r = pthread_mutex_lock(&mutex);
+    assert(r == EDEADLK);
+#endif
+  }
+
+private:
+  pthread_mutex_t mutex;
+};
+
+#endif /* CUBEB_UTILS_UNIX */
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_utils_win.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+
+#if !defined(CUBEB_UTILS_WIN)
+#define CUBEB_UTILS_WIN
+
+#include <windows.h>
+#include "cubeb-internal.h"
+
+/* This wraps a critical section to track the owner in debug mode, adapted from
+   NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
+class owned_critical_section
+{
+public:
+  owned_critical_section()
+#ifdef DEBUG
+    : owner(0)
+#endif
+  {
+    InitializeCriticalSection(&critical_section);
+  }
+
+  ~owned_critical_section()
+  {
+    DeleteCriticalSection(&critical_section);
+  }
+
+  void enter()
+  {
+    EnterCriticalSection(&critical_section);
+#ifdef DEBUG
+    XASSERT(owner != GetCurrentThreadId() && "recursive locking");
+    owner = GetCurrentThreadId();
+#endif
+  }
+
+  void leave()
+  {
+#ifdef DEBUG
+    /* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
+    owner = 0;
+#endif
+    LeaveCriticalSection(&critical_section);
+  }
+
+  /* This is guaranteed to have the good behaviour if it succeeds. The behaviour
+     is undefined otherwise. */
+  void assert_current_thread_owns()
+  {
+#ifdef DEBUG
+    /* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
+    XASSERT(owner == GetCurrentThreadId());
+#endif
+  }
+
+private:
+  CRITICAL_SECTION critical_section;
+#ifdef DEBUG
+  DWORD owner;
+#endif
+};
+
+#endif /* CUBEB_UTILS_WIN */
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -74,83 +74,16 @@ SafeRelease(HANDLE handle)
 template <typename T>
 void SafeRelease(T * ptr)
 {
   if (ptr) {
     ptr->Release();
   }
 }
 
-/* This wraps a critical section to track the owner in debug mode, adapted from
-   NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
-class owned_critical_section
-{
-public:
-  owned_critical_section()
-#ifdef DEBUG
-    : owner(0)
-#endif
-  {
-    InitializeCriticalSection(&critical_section);
-  }
-
-  ~owned_critical_section()
-  {
-    DeleteCriticalSection(&critical_section);
-  }
-
-  void enter()
-  {
-    EnterCriticalSection(&critical_section);
-#ifdef DEBUG
-    XASSERT(owner != GetCurrentThreadId() && "recursive locking");
-    owner = GetCurrentThreadId();
-#endif
-  }
-  
-  void leave()
-  {
-#ifdef DEBUG
-    /* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
-    owner = 0;
-#endif
-    LeaveCriticalSection(&critical_section);
-  }
-
-  /* This is guaranteed to have the good behaviour if it succeeds. The behaviour
-     is undefined otherwise. */
-  void assert_current_thread_owns()
-  {
-#ifdef DEBUG
-    /* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
-    XASSERT(owner == GetCurrentThreadId());
-#endif
-  }
-
-private:
-  CRITICAL_SECTION critical_section;
-#ifdef DEBUG
-  DWORD owner;
-#endif
-};
-
-struct auto_lock {
-  auto_lock(owned_critical_section * lock)
-    : lock(lock)
-  {
-    lock->enter();
-  }
-  ~auto_lock()
-  {
-    lock->leave();
-  }
-private:
-  owned_critical_section * lock;
-};
-
 struct auto_com {
   auto_com() {
     result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
   }
   ~auto_com() {
     if (result == RPC_E_CHANGED_MODE) {
       // This is not an error, COM was not initialized by this function, so it is
       // not necessary to uninit it.
@@ -275,17 +208,17 @@ struct cubeb_stream
    * practice, we read from the input stream in the output callback, so
    * this is not used, but it is necessary to start getting input data. */
   HANDLE input_available_event;
   /* Each cubeb_stream has its own thread. */
   HANDLE thread;
   /* The lock protects all members that are touched by the render thread or
      change during a device reset, including: audio_clock, audio_stream_volume,
      client, frames_written, mix_params, total_frames_written, prev_position. */
-  owned_critical_section * stream_reset_lock;
+  owned_critical_section stream_reset_lock;
   /* Maximum number of frames that can be passed down in a callback. */
   uint32_t input_buffer_frame_count;
   /* Maximum number of frames that can be requested in a callback. */
   uint32_t output_buffer_frame_count;
   /* Resampler instance. Resampling will only happen if necessary. */
   cubeb_resampler * resampler;
   /* A buffer for up/down mixing multi-channel audio. */
   float * mix_buffer;
@@ -1058,17 +991,17 @@ HRESULT get_default_endpoint(IMMDevice *
   SafeRelease(enumerator);
 
   return ERROR_SUCCESS;
 }
 
 double
 current_stream_delay(cubeb_stream * stm)
 {
-  stm->stream_reset_lock->assert_current_thread_owns();
+  stm->stream_reset_lock.assert_current_thread_owns();
 
   /* If the default audio endpoint went away during playback and we weren't
      able to configure a new one, it's possible the caller may call this
      before the error callback has propogated back. */
   if (!stm->audio_clock) {
     return 0;
   }
 
@@ -1092,17 +1025,17 @@ current_stream_delay(cubeb_stream * stm)
   XASSERT(delay >= 0 || stm->draining);
 
   return delay;
 }
 
 int
 stream_set_volume(cubeb_stream * stm, float volume)
 {
-  stm->stream_reset_lock->assert_current_thread_owns();
+  stm->stream_reset_lock.assert_current_thread_owns();
 
   if (!stm->audio_stream_volume) {
     return CUBEB_ERROR;
   }
 
   uint32_t channels;
   HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels);
   if (hr != S_OK) {
@@ -1446,17 +1379,17 @@ int setup_wasapi_stream_one_side(cubeb_s
                                  HANDLE & event,
                                  T ** render_or_capture_client,
                                  cubeb_stream_params * mix_params)
 {
   IMMDevice * device;
   WAVEFORMATEX * mix_format;
   HRESULT hr;
 
-  stm->stream_reset_lock->assert_current_thread_owns();
+  stm->stream_reset_lock.assert_current_thread_owns();
 
   if (devid) {
     std::unique_ptr<const wchar_t> id;
     id.reset(utf8_to_wstr(reinterpret_cast<char*>(devid)));
     hr = get_endpoint(&device, id.get());
     if (FAILED(hr)) {
       LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
       return CUBEB_ERROR;
@@ -1547,17 +1480,17 @@ int setup_wasapi_stream_one_side(cubeb_s
 
 #undef DIRECTION_NAME
 
 int setup_wasapi_stream(cubeb_stream * stm)
 {
   HRESULT hr;
   int rv;
 
-  stm->stream_reset_lock->assert_current_thread_owns();
+  stm->stream_reset_lock.assert_current_thread_owns();
 
   auto_com com;
   if (!com.ok()) {
     return CUBEB_ERROR;
   }
 
   XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
 
@@ -1703,17 +1636,17 @@ wasapi_stream_init(cubeb * context, cube
   if (output_stream_params) {
     stm->output_stream_params = *output_stream_params;
     stm->output_device = output_device;
   }
 
   stm->latency = latency_frames;
   stm->volume = 1.0;
 
-  stm->stream_reset_lock = new owned_critical_section();
+  stm->stream_reset_lock = 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);
     return CUBEB_ERROR;
   }
 
@@ -1756,17 +1689,17 @@ wasapi_stream_init(cubeb * context, cube
 
   return CUBEB_OK;
 }
 
 void close_wasapi_stream(cubeb_stream * stm)
 {
   XASSERT(stm);
 
-  stm->stream_reset_lock->assert_current_thread_owns();
+  stm->stream_reset_lock.assert_current_thread_owns();
 
   SafeRelease(stm->output_client);
   stm->output_client = NULL;
   SafeRelease(stm->input_client);
   stm->capture_client = NULL;
 
   SafeRelease(stm->render_client);
   stm->render_client = NULL;
@@ -1800,18 +1733,16 @@ void wasapi_stream_destroy(cubeb_stream 
   SafeRelease(stm->refill_event);
   SafeRelease(stm->input_available_event);
 
   {
     auto_lock lock(stm->stream_reset_lock);
     close_wasapi_stream(stm);
   }
 
-  delete stm->stream_reset_lock;
-
   free(stm);
 }
 
 enum StreamDirection {
   OUTPUT,
   INPUT
 };
 
--- a/media/libcubeb/tests/test_sanity.cpp
+++ b/media/libcubeb/tests/test_sanity.cpp
@@ -13,20 +13,16 @@
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 #include "common.h"
 #ifdef CUBEB_GECKO_BUILD
 #include "TestHarness.h"
 #endif
 
-#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_RATE 44100
 #define STREAM_LATENCY 100 * STREAM_RATE / 1000
 #define STREAM_CHANNELS 1
 #if (defined(_WIN32) || defined(__WIN32__))
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -20,16 +20,18 @@ cp $1/src/cubeb_panner.cpp src
 cp $1/src/cubeb_panner.h src
 cp $1/src/cubeb_pulse.c src
 cp $1/src/cubeb_resampler.cpp src
 cp $1/src/cubeb_resampler.h src
 cp $1/src/cubeb_resampler_internal.h src
 cp $1/src/cubeb_ring_array.h src
 cp $1/src/cubeb_sndio.c src
 cp $1/src/cubeb_utils.h src
+cp $1/src/cubeb_utils_unix.h src
+cp $1/src/cubeb_utils_win.h src
 cp $1/src/cubeb_wasapi.cpp src
 cp $1/src/cubeb_winmm.c src
 cp $1/test/common.h tests/common.h
 cp $1/test/test_audio.cpp tests/test_audio.cpp
 #cp $1/test/test_devices.c tests/test_devices.cpp
 cp $1/test/test_duplex.cpp tests/test_duplex.cpp
 cp $1/test/test_latency.cpp tests/test_latency.cpp
 cp $1/test/test_record.cpp tests/test_record.cpp