Bug 1283850 - Uplift cubeb to revision b8aebef. r=padenot,kinetik,achronop
authorPaul Adenot <paul@paul.cx>
Fri, 01 Jul 2016 15:43:16 +0200
changeset 383220 6bf1de063f92f982cc1e1ef075cadb9eb17ab9af
parent 383219 733e6ac3ee54688ebb80acea0aa23820c7527ec2
child 383221 00f81b0e199303e9eeef69c6f5dbb25713d925b7
push id21963
push userdmitchell@mozilla.com
push dateFri, 01 Jul 2016 19:54:18 +0000
reviewerspadenot, kinetik, achronop
bugs1283850
milestone50.0a1
Bug 1283850 - Uplift cubeb to revision b8aebef. r=padenot,kinetik,achronop MozReview-Commit-ID: IjlWPClDuFg
media/libcubeb/README_MOZILLA
media/libcubeb/include/cubeb.h
media/libcubeb/src/cubeb_alsa.c
media/libcubeb/src/cubeb_audiounit.c
media/libcubeb/src/cubeb_resampler_internal.h
media/libcubeb/src/cubeb_wasapi.cpp
--- 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 9a1d6ccd2a0d65bfb985743b927398bcbeb05ad6.
+The git commit ID used was b8aebef33a096640d6cbd7a6fa568bccef1b339c.
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -14,54 +14,96 @@ extern "C" {
 #endif
 
 /** @mainpage
 
     @section intro Introduction
 
     This is the documentation for the <tt>libcubeb</tt> C API.
     <tt>libcubeb</tt> is a callback-based audio API library allowing the
-    authoring of portable multiplatform audio playback.
+    authoring of portable multiplatform audio playback and recording.
 
     @section example Example code
 
+    This example shows how to create a duplex stream that pipes the microphone
+    to the speakers, with minimal latency and the proper sample-rate for the
+    platform.
+
     @code
     cubeb * app_ctx;
     cubeb_init(&app_ctx, "Example Application");
+    int rv;
+    int rate;
+    int latency_ms;
+    uint64_t ts;
 
-    cubeb_stream_params params;
-    params.format = CUBEB_SAMPLE_S16NE;
-    params.rate = 48000;
-    params.channels = 2;
+    rv = cubeb_get_min_latency(app_ctx, output_params, &latency_ms);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not get minimum latency");
+      return rv;
+    }
 
-    unsigned int latency_ms = 250;
+    rv = cubeb_get_preferred_sample_rate(app_ctx, output_params, &rate);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not get preferred sample-rate");
+      return rv;
+    }
+
+    cubeb_stream_params output_params;
+    output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+    output_params.rate = rate;
+    output_params.channels = 2;
+
+    cubeb_stream_params input_params;
+    output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+    output_params.rate = rate;
+    output_params.channels = 1;
 
     cubeb_stream * stm;
-    cubeb_stream_init(app_ctx, &stm, "Example Stream 1", params,
-                      latency_ms, data_cb, state_cb, NULL);
+    rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
+                           NULL, input_params,
+                           NULL, output_params,
+                           latency_ms,
+                           data_cb, state_cb,
+                           NULL);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not open the stream");
+      return rv;
+    }
 
-    cubeb_stream_start(stm);
+    rv = cubeb_stream_start(stm);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not start the stream");
+      return rv;
+    }
     for (;;) {
       cubeb_stream_get_position(stm, &ts);
       printf("time=%llu\n", ts);
       sleep(1);
     }
-    cubeb_stream_stop(stm);
+    rv = cubeb_stream_stop(stm);
+    if (rv != CUBEB_OK) {
+      fprintf(stderr, "Could not stop the stream");
+      return rv;
+    }
 
     cubeb_stream_destroy(stm);
     cubeb_destroy(app_ctx);
     @endcode
 
     @code
-    long data_cb(cubeb_stream * stm, void * user, void * buffer, long nframes)
+    long data_cb(cubeb_stream * stm, void * user,
+                 void * input_buffer, void * output_buffer, long nframes)
     {
-      short * buf = buffer;
+      float * in  = input_buffer;
+      float * out = output_buffer;
+
       for (i = 0; i < nframes; ++i) {
-        for (c = 0; c < params.channels; ++c) {
-          buf[i][c] = 0;
+        for (c = 0; c < 2; ++c) {
+          buf[i][c] = in[i];
         }
       }
       return nframes;
     }
     @endcode
 
     @code
     void state_cb(cubeb_stream * stm, void * user, cubeb_state state)
@@ -96,16 +138,20 @@ typedef enum {
   /**< Native endian 16-bit signed PCM. */
   CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16LE,
   /**< Native endian 32-bit IEEE floating point PCM. */
   CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32LE
 #endif
 } cubeb_sample_format;
 
 #if defined(__ANDROID__)
+/**
+ * This maps to the underlying stream types on supported platforms, e.g.
+ * Android.
+ */
 typedef enum {
     CUBEB_STREAM_TYPE_VOICE_CALL = 0,
     CUBEB_STREAM_TYPE_SYSTEM = 1,
     CUBEB_STREAM_TYPE_RING = 2,
     CUBEB_STREAM_TYPE_MUSIC = 3,
     CUBEB_STREAM_TYPE_ALARM = 4,
     CUBEB_STREAM_TYPE_NOTIFICATION = 5,
     CUBEB_STREAM_TYPE_BLUETOOTH_SCO = 6,
@@ -128,17 +174,17 @@ typedef struct {
                                    #cubeb_sample_format. */
   unsigned int rate;          /**< Requested sample rate.  Valid range is [1000, 192000]. */
   unsigned int channels;      /**< Requested channel count.  Valid range is [1, 8]. */
 #if defined(__ANDROID__)
   cubeb_stream_type stream_type; /**< Used to map Android audio stream types */
 #endif
 } cubeb_stream_params;
 
-/** Output device description */
+/** Audio device description */
 typedef struct {
   char * output_name; /**< The name of the output device */
   char * input_name; /**< The name of the input device */
 } cubeb_device;
 
 /** Stream states signaled via state_callback. */
 typedef enum {
   CUBEB_STATE_STARTED, /**< Stream started. */
@@ -244,137 +290,151 @@ typedef struct {
 
 /** Device collection. */
 typedef struct {
   uint32_t count;                 /**< Device count in collection. */
   cubeb_device_info * device[1];   /**< Array of pointers to device info. */
 } cubeb_device_collection;
 
 /** User supplied data callback.
-    @param stream The stream for which this callback fired
-    @param user_ptr The pointer passed to cubeb_stream_create
+    - Calling other cubeb functions from this callback is unsafe.
+    - The code in the callback should be non-blocking.
+    - Returning less than the number of frames this callback asks for or
+      provides puts the stream in drain mode. This callback will not be called
+      again, and the state callback will be called with CUBEB_STATE_DRAINED when
+      all the frames have been output.
+    @param stream The stream for which this callback fired.
+    @param user_ptr The pointer passed to cubeb_stream_init.
     @param input_buffer A pointer containing the input data, or nullptr
                         if this is an output-only stream.
-    @param output_buffer A pointer containing the output data, or nullptr
-                         if this is an input -only stream.
+    @param output_buffer A pointer to a buffer to be filled with audio samples,
+                         or nullptr if this is an input-only stream.
     @param nframes The number of frames of the two buffer.
-    @retval Number of frames written to the output buffer, which must equal
-            nframes except at end of stream.
+    @retval Number of frames written to the output buffer. If this number is
+            less than nframes, then the stream will start to drain.
     @retval CUBEB_ERROR on error, in which case the data callback will stop
             and the stream will enter a shutdown state. */
 typedef long (* cubeb_data_callback)(cubeb_stream * stream,
                                      void * user_ptr,
                                      const void * input_buffer,
                                      void * output_buffer,
                                      long nframes);
 
 /** User supplied state callback.
-    @param stream
-    @param user_ptr
-    @param state */
+    @param stream The stream for this this callback fired.
+    @param user_ptr The pointer passed to cubeb_stream_init.
+    @param state The new state of the stream. */
 typedef void (* cubeb_state_callback)(cubeb_stream * stream,
                                       void * user_ptr,
                                       cubeb_state state);
 
 /**
  * User supplied callback called when the underlying device changed.
- * @param user */
+ * @param user The pointer passed to cubeb_stream_init. */
 typedef void (* cubeb_device_changed_callback)(void * user_ptr);
 
 /**
  * User supplied callback called when the underlying device collection changed.
- * @param context
- * @param user_ptr */
-typedef void (* cubeb_device_collection_changed_callback)(cubeb * context, void * user_ptr);
+ * @param context A pointer to the cubeb context.
+ * @param user_ptr The pointer passed to cubeb_stream_init. */
+typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
+                                                          void * user_ptr);
 
 /** Initialize an application context.  This will perform any library or
     application scoped initialization.
-    @param context
-    @param context_name
-    @retval CUBEB_OK
-    @retval CUBEB_ERROR */
+    @param context A out param where an opaque pointer to the application
+                   context will be returned.
+    @param context_name A name for the context. Depending on the platform this
+                        can appear in different locations.
+    @retval CUBEB_OK in case of success.
+    @retval CUBEB_ERROR in case of error, for example because the host
+                        has no audio hardware. */
 int cubeb_init(cubeb ** context, char const * context_name);
 
 /** Get a read-only string identifying this context's current backend.
-    @param context
+    @param context A pointer to the cubeb context.
     @retval Read-only string identifying current backend. */
 char const * cubeb_get_backend_id(cubeb * context);
 
 /** Get the maximum possible number of channels.
-    @param context
+    @param context A pointer to the cubeb context.
     @param max_channels The maximum number of channels.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED
     @retval CUBEB_ERROR */
 int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
 
 /** Get the minimal latency value, in milliseconds, that is guaranteed to work
-    when creating a stream for the specified sample rate. This is platform and
-    backend dependant.
-    @param context
+    when creating a stream for the specified sample rate. This is platform,
+    hardware and backend dependant.
+    @param context A pointer to the cubeb context.
     @param params On some backends, the minimum achievable latency depends on
                   the characteristics of the stream.
     @param latency_ms The latency value, in ms, to pass to cubeb_stream_init.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED */
 int cubeb_get_min_latency(cubeb * context,
                           cubeb_stream_params params,
                           uint32_t * latency_ms);
 
 /** Get the preferred sample rate for this backend: this is hardware and
     platform dependant, and can avoid resampling, and/or trigger fastpaths.
-    @param context
+    @param context A pointer to the cubeb context.
     @param rate The samplerate (in Hz) the current configuration prefers.
     @retval CUBEB_OK
     @retval CUBEB_ERROR_INVALID_PARAMETER
     @retval CUBEB_ERROR_NOT_SUPPORTED */
 int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
 
-/** Destroy an application context.
-    @param context */
+/** Destroy an application context. This must be called after all stream have
+ *  been destroyed.
+    @param context A pointer to the cubeb context.*/
 void cubeb_destroy(cubeb * context);
 
 /** Initialize a stream associated with the supplied application context.
-    @param context
-    @param stream
-    @param stream_name
-    @param input_device Device for the input side of the stream. If NULL
+    @param context A pointer to the cubeb context.
+    @param stream An out parameter to be filled with the an opaque pointer to a
+                  cubeb stream.
+    @param stream_name A name for this stream. 
+    @param input_device Device for the input side of the stream. If NULL the
                         default input device is used.
     @param input_stream_params Parameters for the input side of the stream, or
                                NULL if this stream is output only.
-    @param output_device Device for the output side of the stream. If NULL
+    @param output_device Device for the output side of the stream. If NULL the
                          default output device is used.
     @param output_stream_params Parameters for the output side of the stream, or
                                 NULL if this stream is input only.
     @param latency Approximate stream latency in milliseconds.  Valid range
                    is [1, 2000].
     @param data_callback Will be called to preroll data before playback is
                          started by cubeb_stream_start.
-    @param state_callback
-    @param user_ptr
+    @param state_callback A pointer to a state callback.
+    @param user_ptr A pointer that will be passed to the callbacks. This pointer
+                    must outlive the life time of the stream.
     @retval CUBEB_OK
     @retval CUBEB_ERROR
     @retval CUBEB_ERROR_INVALID_FORMAT
     @retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
 int cubeb_stream_init(cubeb * context,
                       cubeb_stream ** stream,
                       char const * stream_name,
                       cubeb_devid input_device,
                       cubeb_stream_params * input_stream_params,
                       cubeb_devid output_device,
                       cubeb_stream_params * output_stream_params,
                       unsigned int latency,
                       cubeb_data_callback data_callback,
                       cubeb_state_callback state_callback,
                       void * user_ptr);
 
-/** Destroy a stream.
-    @param stream */
+/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
+    stream.
+    @param stream The stream to destroy. */
 void cubeb_stream_destroy(cubeb_stream * stream);
 
 /** Start playback.
     @param stream
     @retval CUBEB_OK
     @retval CUBEB_ERROR */
 int cubeb_stream_start(cubeb_stream * stream);
 
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -246,36 +246,25 @@ alsa_set_stream_state(cubeb_stream * stm
   assert(r == 0);
   ctx->rebuild = 1;
   poll_wake(ctx);
 }
 
 static enum stream_state
 alsa_refill_stream(cubeb_stream * stm)
 {
-  int r;
-  unsigned short revents;
   snd_pcm_sframes_t avail;
   long got;
   void * p;
   int draining;
 
   draining = 0;
 
   pthread_mutex_lock(&stm->mutex);
 
-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
-  if (r < 0 || revents != POLLOUT) {
-    /* This should be a stream error; it makes no sense for poll(2) to wake
-       for this stream and then have the stream report that it's not ready.
-       Unfortunately, this does happen, so just bail out and try again. */
-    pthread_mutex_unlock(&stm->mutex);
-    return RUNNING;
-  }
-
   avail = snd_pcm_avail_update(stm->pcm);
   if (avail == -EPIPE) {
     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) {
--- a/media/libcubeb/src/cubeb_audiounit.c
+++ b/media/libcubeb/src/cubeb_audiounit.c
@@ -1183,17 +1183,17 @@ audiounit_stream_init(cubeb * context,
   }
   if (stm->output_unit != NULL &&
       AudioUnitInitialize(stm->output_unit) != 0) {
     audiounit_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
-  LOG("Cubeb stream init successfully.\n");
+  LOG("Cubeb stream (%p) init successful.\n", stm);
   return CUBEB_OK;
 }
 
 static void
 audiounit_stream_destroy(cubeb_stream * stm)
 {
   stm->shutdown = 1;
 
@@ -1226,44 +1226,51 @@ audiounit_stream_destroy(cubeb_stream * 
   pthread_mutex_unlock(&stm->context->mutex);
 
   free(stm);
 }
 
 static int
 audiounit_stream_start(cubeb_stream * stm)
 {
+  pthread_mutex_lock(&stm->context->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);
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_stop(cubeb_stream * stm)
 {
+  pthread_mutex_lock(&stm->context->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);
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   pthread_mutex_lock(&stm->mutex);
   *position = stm->frames_played;
--- a/media/libcubeb/src/cubeb_resampler_internal.h
+++ b/media/libcubeb/src/cubeb_resampler_internal.h
@@ -210,18 +210,18 @@ public:
        buffer. */
     resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
 
     return out_len;
   }
 
   size_t output_for_input(uint32_t input_frames)
   {
-    return size_t(ceilf(input_frames / resampling_ratio)
-                  - resampling_in_buffer.length() / channels);
+    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)
   {
     if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
       resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
@@ -258,18 +258,19 @@ public:
   }
 
   /** Returns the number of frames to pass in the input of the resampler to have
    * exactly `output_frame_count` resampled frames. This can return a number
    * slightly bigger than what is strictly necessary, but it guaranteed that the
    * number of output frames will be exactly equal. */
   uint32_t input_needed_for_output(uint32_t output_frame_count)
   {
-    return uint32_t(ceilf(output_frame_count * resampling_ratio) + 1
-                    - samples_to_frames(resampling_in_buffer.length()));
+    return (uint32_t)ceilf((output_frame_count - samples_to_frames(resampling_in_buffer.length()))
+                          * resampling_ratio);
+
   }
 
   /** Returns a pointer to the input buffer, that contains empty space for at
    * least `frame_count` elements. This is useful so that consumer can directly
    * write into the input buffer of the resampler. The pointer returned is
    * adjusted so that leftover data are not overwritten.
    */
   T * input_buffer(size_t frame_count)
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -1864,18 +1864,16 @@ int wasapi_stream_start(cubeb_stream * s
   return CUBEB_OK;
 }
 
 int wasapi_stream_stop(cubeb_stream * stm)
 {
   XASSERT(stm);
   HRESULT hr;
 
-  XASSERT(stm->output_client || stm->input_client);
-
   {
     auto_lock lock(stm->stream_reset_lock);
 
     if (stm->output_client) {
       hr = stm->output_client->Stop();
       if (FAILED(hr)) {
         LOG("could not stop AudioClient (output)\n");
         return CUBEB_ERROR;