diff --git a/configure.in b/configure.in index 0141c2de5453..91c236f9d309 100644 --- a/configure.in +++ b/configure.in @@ -191,7 +191,7 @@ if test -n "$gonkdir" ; then ;; 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. @@ -5449,6 +5449,9 @@ 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*) diff --git a/content/media/nsAudioStream.cpp b/content/media/nsAudioStream.cpp index 3320bf676a35..bab777b020fd 100644 --- a/content/media/nsAudioStream.cpp +++ b/content/media/nsAudioStream.cpp @@ -298,7 +298,11 @@ static int PrefChanged(const char* aPref, void* aClosure) gVolumeScale = NS_MAX(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) { diff --git a/media/libcubeb/src/Makefile.in b/media/libcubeb/src/Makefile.in index c3ab02239ed8..96834da1c67e 100644 --- a/media/libcubeb/src/Makefile.in +++ b/media/libcubeb/src/Makefile.in @@ -23,6 +23,11 @@ CSRCS = \ 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 = \ diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c new file mode 100644 index 000000000000..8e492041e8bd --- /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 +#include +#include + +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; +} + diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index 465a8ec98315..ecbc59149e55 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -409,6 +409,12 @@ OS_LIBS += \ -lsensorservice \ -ldbus \ $(NULL) + +ifdef MOZ_CUBEB +OS_LIBS += \ + -lOpenSLES \ + $(NULL) +endif endif EXTRA_DEPS += \