Bug 694484 - OpenSL backend for libcubeb, r=kinetik
authorMichael Wu <mwu@mozilla.com>
Tue, 04 Sep 2012 17:45:08 -0300
changeset 104223 8eee25223a3ed4bab80927eb920a211773fa1927
parent 104222 206e0ad324c9701e88ce5efcf8bea4246356da84
child 104224 f867845a9956e604bd64e819b0dd14ccd6204705
push id23407
push userryanvm@gmail.com
push dateWed, 05 Sep 2012 01:45:23 +0000
treeherdermozilla-central@6705e131aeaa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs694484
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 694484 - OpenSL backend for libcubeb, r=kinetik
configure.in
content/media/nsAudioStream.cpp
media/libcubeb/src/Makefile.in
media/libcubeb/src/cubeb_opensl.c
toolkit/library/Makefile.in
--- a/configure.in
+++ b/configure.in
@@ -186,17 +186,17 @@ if test -n "$gonkdir" ; then
     arm)
         ARCH_DIR=arch-arm
         ;;
     i?86)
         ARCH_DIR=arch-x86
         ;;
     esac
 
-    CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera"
+    CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera -I$gonkdir/system/media/wilhelm/include"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS $STLPORT_CPPFLAGS"
     dnl Add -llog by default, since we use it all over the place.
     LIBS="$LIBS -llog $STLPORT_LIBS"
 
     LDFLAGS="-mandroid -L$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib -Wl,-rpath-link=$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib --sysroot=$gonkdir/out/target/product/$GONK_PRODUCT/obj/ $LDFLAGS"
 
     dnl prevent cross compile section from using these flags as host flags
@@ -5444,16 +5444,19 @@ fi
 
 if test -n "$MOZ_SPEEX_RESAMPLER"; then
     AC_DEFINE(MOZ_SPEEX_RESAMPLER)
 fi
 
 if test -n "$MOZ_CUBEB"; then
     case "$target" in
     *-android*|*-linuxandroid*)
+        if test -n "$gonkdir"; then
+            AC_DEFINE(MOZ_CUBEB)
+        fi
         dnl No Android implementation of libcubeb yet.
         ;;
     *-linux*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
     *-mingw*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -293,17 +293,21 @@ static int PrefChanged(const char* aPref
     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     if (value.IsEmpty()) {
       gVolumeScale = 1.0;
     } else {
       NS_ConvertUTF16toUTF8 utf8(value);
       gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nullptr));
     }
   } else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
+#ifdef MOZ_WIDGET_GONK
+    bool value = Preferences::GetBool(aPref, false);
+#else
     bool value = Preferences::GetBool(aPref, true);
+#endif
     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     gUseCubeb = value;
   } else if (strcmp(aPref, PREF_CUBEB_LATENCY) == 0) {
     // Arbitrary default stream latency of 100ms.  The higher this
     // value, the longer stream volume changes will take to become
     // audible.
     uint32_t value = Preferences::GetUint(aPref, 100);
     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
--- a/media/libcubeb/src/Makefile.in
+++ b/media/libcubeb/src/Makefile.in
@@ -18,16 +18,21 @@ endif
 
 ifeq ($(OS_TARGET),WINNT)
 CSRCS           = \
                 cubeb_winmm.c \
                 $(NULL)
 endif
 
 ifeq ($(OS_TARGET),Android)
+ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
+CSRCS         = \
+              cubeb_opensl.c \
+              $(NULL)
+endif
 # No Android implementation of libcubeb yet.
 else ifeq ($(OS_TARGET),Linux)
 CSRCS         = \
               cubeb_alsa.c \
               $(NULL)
 endif
 
 ifeq ($(OS_TARGET),Darwin)
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright © 2012 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+#include "cubeb/cubeb.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <SLES/OpenSLES.h>
+
+struct cubeb {
+  SLObjectItf engObj;
+  SLEngineItf eng;
+};
+
+#define NBUFS 4
+
+struct cubeb_stream {
+  struct cubeb * context;
+  SLObjectItf playerObj;
+  SLPlayItf play;
+  SLBufferQueueItf bufq;
+  SLObjectItf outmixObj;
+  void *queuebuf[NBUFS];
+  int queuebuf_idx;
+  long queuebuf_len;
+  long bytespersec;
+  long framesize;
+
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  void * user_ptr;
+};
+
+static void
+bufferqueue_callback(SLBufferQueueItf caller, struct cubeb_stream *stm)
+{
+  void *buf = stm->queuebuf[stm->queuebuf_idx];
+
+  long written = stm->data_callback(stm, stm->user_ptr,
+                                    buf, stm->queuebuf_len / stm->framesize);
+  if (written <= 0)
+    return;
+
+  (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
+
+  stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
+  // XXX handle error
+}
+
+static void
+play_callback(SLPlayItf caller, struct cubeb_stream *stm, SLuint32 event)
+{
+  if (event & SL_PLAYEVENT_HEADSTALLED)
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+}
+
+int
+cubeb_init(cubeb ** context, char const * context_name)
+{
+  cubeb * ctx;
+
+  *context = NULL;
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
+
+  SLresult res;
+  res = slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL);
+  if (res != SL_RESULT_SUCCESS) {
+    free(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    free(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+char const *
+cubeb_get_backend_id(cubeb * ctx)
+{
+  return "opensl";
+}
+
+void
+cubeb_destroy(cubeb * ctx)
+{
+  (*ctx->engObj)->Destroy(ctx->engObj);
+  free(ctx);
+}
+
+int
+cubeb_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                  cubeb_stream_params stream_params, unsigned int latency,
+                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+                  void * user_ptr)
+{
+  cubeb_stream * stm;
+
+  assert(ctx);
+
+  *stream = NULL;
+
+  if (stream_params.rate < 8000 || stream_params.rate > 48000 ||
+      stream_params.channels < 1 || stream_params.channels > 32 ||
+      latency < 1 || latency > 2000) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  SLDataFormat_PCM format;
+
+  format.formatType = SL_DATAFORMAT_PCM;
+  format.numChannels = stream_params.channels;
+  // samplesPerSec is in milliHertz
+  format.samplesPerSec = stream_params.rate * 1000;
+  format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+  format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
+  format.channelMask = stream_params.channels == 1 ?
+                       SL_SPEAKER_FRONT_CENTER :
+                       SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+
+  switch (stream_params.format) {
+  case CUBEB_SAMPLE_S16LE:
+    format.endianness = SL_BYTEORDER_LITTLEENDIAN;
+    break;
+  case CUBEB_SAMPLE_S16BE:
+    format.endianness = SL_BYTEORDER_BIGENDIAN;
+    break;
+  default:
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  stm = calloc(1, sizeof(*stm));
+  assert(stm);
+
+  stm->context = ctx;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+
+  stm->framesize = stream_params.channels * sizeof(int16_t);
+  stm->bytespersec = stream_params.rate * stm->framesize;
+  stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
+  stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
+  int i;
+  for (i = 0; i < NBUFS; i++) {
+    stm->queuebuf[i] = malloc(stm->queuebuf_len);
+    assert(stm->queuebuf[i]);
+  }
+
+  SLDataLocator_BufferQueue loc_bufq;
+  loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
+  loc_bufq.numBuffers = NBUFS;
+  SLDataSource source;
+  source.pLocator = &loc_bufq;
+  source.pFormat = &format;
+
+  SLresult res;
+  const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
+  const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
+  res = (*ctx->eng)->CreateOutputMix(ctx->eng, &stm->outmixObj, 1, idsom, reqom);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->outmixObj)->Realize(stm->outmixObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  SLDataLocator_OutputMix loc_outmix;
+  loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+  loc_outmix.outputMix = stm->outmixObj;
+  SLDataSink sink;
+  sink.pLocator = &loc_outmix;
+  sink.pFormat = NULL;
+
+  const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
+  const SLboolean req[] = {SL_BOOLEAN_TRUE};
+  res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
+                                       &source, &sink, 1, ids, req);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj, SL_IID_PLAY, &stm->play);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj, SL_IID_BUFFERQUEUE,
+                                    &stm->bufq);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->play)->SetCallbackEventsMask(stm->play, SL_PLAYEVENT_HEADSTALLED);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+
+  return CUBEB_OK;
+}
+
+void
+cubeb_stream_destroy(cubeb_stream * stm)
+{
+  if (stm->playerObj)
+    (*stm->playerObj)->Destroy(stm->playerObj);
+  if (stm->outmixObj)
+    (*stm->outmixObj)->Destroy(stm->outmixObj);
+  free(stm);
+}
+
+int
+cubeb_stream_start(cubeb_stream * stm)
+{
+  SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+  bufferqueue_callback(NULL, stm);
+  return CUBEB_OK;
+}
+
+int
+cubeb_stream_stop(cubeb_stream * stm)
+{
+  SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+  return CUBEB_OK;
+}
+
+int
+cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  SLmillisecond msec;
+  SLresult res = (*stm->play)->GetPosition(stm->play, &msec);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+  *position = (stm->bytespersec * msec) / (1000 * stm->framesize);
+  return CUBEB_OK;
+}
+
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -404,16 +404,22 @@ OS_LIBS += \
   -lutils \
   -lcutils \
   -lsysutils \
   -lcamera_client \
   -lbinder \
   -lsensorservice \
   -ldbus \
   $(NULL)
+
+ifdef MOZ_CUBEB
+OS_LIBS += \
+  -lOpenSLES \
+  $(NULL)
+endif
 endif
 
 EXTRA_DEPS += \
   $(topsrcdir)/intl/unicharutil/util/objs.mk \
   $(topsrcdir)/rdf/util/src/objs.mk \
   $(NULL)
 
 CPPSRCS += \