Bug 694484 - OpenSL backend for libcubeb, r=kinetik

This commit is contained in:
Michael Wu 2012-09-04 17:45:08 -03:00
parent f36e3d9c21
commit 38a2fa1577
5 changed files with 308 additions and 1 deletions

View File

@ -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*)

View File

@ -298,7 +298,11 @@ static int PrefChanged(const char* aPref, void* aClosure)
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) {

View File

@ -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 = \

View File

@ -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;
}

View File

@ -409,6 +409,12 @@ OS_LIBS += \
-lsensorservice \
-ldbus \
$(NULL)
ifdef MOZ_CUBEB
OS_LIBS += \
-lOpenSLES \
$(NULL)
endif
endif
EXTRA_DEPS += \