/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* OpenSL ES private and global functions not associated with an interface or class */
#include "sles_allinclusive.h"
/** \brief Return true if the specified interface exists and has been initialized for this object.
* Returns false if the class does not support this kind of interface, or the class supports the
* interface but this particular object has not had the interface exposed at object creation time
* or by DynamicInterface::AddInterface. Note that the return value is not affected by whether
* the application has requested access to the interface with Object::GetInterface. Assumes on
* entry that the object is locked for either shared or exclusive access.
*/
bool IsInterfaceInitialized(IObject *this, unsigned MPH)
{
assert(NULL != this);
assert( /* (MPH_MIN <= MPH) && */ (MPH < (unsigned) MPH_MAX));
const ClassTable *class__ = this->mClass;
assert(NULL != class__);
int index;
if (0 > (index = class__->mMPH_to_index[MPH])) {
return false;
}
assert(MAX_INDEX >= class__->mInterfaceCount);
assert(class__->mInterfaceCount > (unsigned) index);
switch (this->mInterfaceStates[index]) {
case INTERFACE_EXPOSED:
case INTERFACE_ADDED:
return true;
default:
return false;
}
}
/** \brief Map an IObject to it's "object ID" (which is really a class ID) */
SLuint32 IObjectToObjectID(IObject *this)
{
assert(NULL != this);
return this->mClass->mObjectID;
}
/** \brief Acquire a strong reference to an object.
* Check that object has the specified "object ID" (which is really a class ID) and is in the
* realized state. If so, then acquire a strong reference to it and return true.
* Otherwise return false.
*/
SLresult AcquireStrongRef(IObject *object, SLuint32 expectedObjectID)
{
if (NULL == object) {
return SL_RESULT_PARAMETER_INVALID;
}
// NTH additional validity checks on address here
SLresult result;
object_lock_exclusive(object);
SLuint32 actualObjectID = IObjectToObjectID(object);
if (expectedObjectID != actualObjectID) {
SL_LOGE("object %p has object ID %lu but expected %lu", object, actualObjectID,
expectedObjectID);
result = SL_RESULT_PARAMETER_INVALID;
} else if (SL_OBJECT_STATE_REALIZED != object->mState) {
SL_LOGE("object %p with object ID %lu is not realized", object, actualObjectID);
result = SL_RESULT_PRECONDITIONS_VIOLATED;
} else {
++object->mStrongRefCount;
result = SL_RESULT_SUCCESS;
}
object_unlock_exclusive(object);
return result;
}
/** \brief Release a strong reference to an object.
* Entry condition: the object is locked.
* Exit condition: the object is unlocked.
* Finishes the destroy if needed.
*/
void ReleaseStrongRefAndUnlockExclusive(IObject *object)
{
#ifdef USE_DEBUG
assert(pthread_equal(pthread_self(), object->mOwner));
#endif
assert(0 < object->mStrongRefCount);
if ((0 == --object->mStrongRefCount) && (SL_OBJECT_STATE_DESTROYING == object->mState)) {
// FIXME do the destroy here - merge with IDestroy
// but can't do this until we move Destroy to the sync thread
// as Destroy is now a blocking operation, and to avoid a race
} else {
object_unlock_exclusive(object);
}
}
/** \brief Release a strong reference to an object.
* Entry condition: the object is unlocked.
* Exit condition: the object is unlocked.
* Finishes the destroy if needed.
*/
void ReleaseStrongRef(IObject *object)
{
assert(NULL != object);
object_lock_exclusive(object);
ReleaseStrongRefAndUnlockExclusive(object);
}
/** \brief Convert POSIX pthread error code to OpenSL ES result code */
SLresult err_to_result(int err)
{
if (EAGAIN == err || ENOMEM == err) {
return SL_RESULT_RESOURCE_ERROR;
}
if (0 != err) {
return SL_RESULT_INTERNAL_ERROR;
}
return SL_RESULT_SUCCESS;
}
/** \brief Check the interface IDs passed into a Create operation */
SLresult checkInterfaces(const ClassTable *class__, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired, unsigned *pExposedMask)
{
assert(NULL != class__ && NULL != pExposedMask);
// Initially no interfaces are exposed
unsigned exposedMask = 0;
const struct iid_vtable *interfaces = class__->mInterfaces;
SLuint32 interfaceCount = class__->mInterfaceCount;
SLuint32 i;
// Expose all implicit interfaces
for (i = 0; i < interfaceCount; ++i) {
switch (interfaces[i].mInterface) {
case INTERFACE_IMPLICIT:
case INTERFACE_IMPLICIT_PREREALIZE:
// there must be an initialization hook present
if (NULL != MPH_init_table[interfaces[i].mMPH].mInit) {
exposedMask |= 1 << i;
}
break;
case INTERFACE_EXPLICIT:
case INTERFACE_DYNAMIC:
case INTERFACE_UNAVAILABLE:
case INTERFACE_EXPLICIT_PREREALIZE:
break;
default:
assert(false);
break;
}
}
if (0 < numInterfaces) {
if (NULL == pInterfaceIds || NULL == pInterfaceRequired) {
return SL_RESULT_PARAMETER_INVALID;
}
bool anyRequiredButUnsupported = false;
// Loop for each requested interface
for (i = 0; i < numInterfaces; ++i) {
SLInterfaceID iid = pInterfaceIds[i];
if (NULL == iid) {
return SL_RESULT_PARAMETER_INVALID;
}
int MPH, index;
if ((0 > (MPH = IID_to_MPH(iid))) ||
// there must be an initialization hook present
(NULL == MPH_init_table[MPH].mInit) ||
(0 > (index = class__->mMPH_to_index[MPH])) ||
(INTERFACE_UNAVAILABLE == interfaces[index].mInterface)) {
// Here if interface was not found, or is not available for this object type
if (pInterfaceRequired[i]) {
// Application said it required the interface, so give up
SL_LOGE("class %s interface %lu required but unavailable MPH=%d",
class__->mName, i, MPH);
anyRequiredButUnsupported = true;
}
// Application said it didn't really need the interface, so ignore with warning
SL_LOGW("class %s interface %lu requested but unavailable MPH=%d",
class__->mName, i, MPH);
continue;
}
// The requested interface was both found and available, so expose it
exposedMask |= (1 << index);
// Note that we ignore duplicate requests, including equal and aliased IDs
}
if (anyRequiredButUnsupported) {
return SL_RESULT_FEATURE_UNSUPPORTED;
}
}
*pExposedMask = exposedMask;
return SL_RESULT_SUCCESS;
}
/** \brief Helper shared by decoder and encoder */
SLresult GetCodecCapabilities(SLuint32 codecId, SLuint32 *pIndex,
SLAudioCodecDescriptor *pDescriptor, const CodecDescriptor *codecDescriptors)
{
if (NULL == pIndex) {
return SL_RESULT_PARAMETER_INVALID;
}
const CodecDescriptor *cd = codecDescriptors;
SLuint32 index;
if (NULL == pDescriptor) {
for (index = 0 ; NULL != cd->mDescriptor; ++cd) {
if (cd->mCodecID == codecId) {
++index;
}
}
*pIndex = index;
return SL_RESULT_SUCCESS;
}
index = *pIndex;
for ( ; NULL != cd->mDescriptor; ++cd) {
if (cd->mCodecID == codecId) {
if (0 == index) {
*pDescriptor = *cd->mDescriptor;
#if 0 // Temporary workaround for Khronos bug 6331
if (0 < pDescriptor->numSampleRatesSupported) {
// The malloc is not in the 1.0.1 specification
SLmilliHertz *temp = (SLmilliHertz *) malloc(sizeof(SLmilliHertz) *
pDescriptor->numSampleRatesSupported);
assert(NULL != temp);
memcpy(temp, pDescriptor->pSampleRatesSupported, sizeof(SLmilliHertz) *
pDescriptor->numSampleRatesSupported);
pDescriptor->pSampleRatesSupported = temp;
} else {
pDescriptor->pSampleRatesSupported = NULL;
}
#endif
return SL_RESULT_SUCCESS;
}
--index;
}
}
return SL_RESULT_PARAMETER_INVALID;
}
/** \brief Check a data locator and make local deep copy */
static SLresult checkDataLocator(void *pLocator, DataLocator *pDataLocator)
{
if (NULL == pLocator) {
pDataLocator->mLocatorType = SL_DATALOCATOR_NULL;
return SL_RESULT_SUCCESS;
}
SLresult result;
SLuint32 locatorType = *(SLuint32 *)pLocator;
switch (locatorType) {
case SL_DATALOCATOR_ADDRESS:
pDataLocator->mAddress = *(SLDataLocator_Address *)pLocator;
// if length is greater than zero, then the address must be non-NULL
if ((0 < pDataLocator->mAddress.length) && (NULL == pDataLocator->mAddress.pAddress)) {
SL_LOGE("pAddress is NULL");
return SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
// This is an alias that is _not_ converted; the rest of the code must check for both locator
// types. That's because it is only an alias for audio players, not audio recorder objects
// so we have to remember the distinction.
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
pDataLocator->mBufferQueue = *(SLDataLocator_BufferQueue *)pLocator;
// number of buffers must be specified, there is no default value, and must not be excessive
if (!((1 <= pDataLocator->mBufferQueue.numBuffers) &&
(pDataLocator->mBufferQueue.numBuffers <= 255))) {
SL_LOGE("numBuffers=%u", (unsigned) pDataLocator->mBufferQueue.numBuffers);
return SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_DATALOCATOR_IODEVICE:
{
pDataLocator->mIODevice = *(SLDataLocator_IODevice *)pLocator;
SLuint32 deviceType = pDataLocator->mIODevice.deviceType;
SLObjectItf device = pDataLocator->mIODevice.device;
if (NULL != device) {
pDataLocator->mIODevice.deviceID = 0;
SLuint32 expectedObjectID;
switch (deviceType) {
case SL_IODEVICE_LEDARRAY:
expectedObjectID = SL_OBJECTID_LEDDEVICE;
break;
case SL_IODEVICE_VIBRA:
expectedObjectID = SL_OBJECTID_VIBRADEVICE;
break;
// audio input and audio output cannot be specified via objects
case SL_IODEVICE_AUDIOINPUT:
// worse yet, an SL_IODEVICE enum constant for audio output does not exist yet
// case SL_IODEVICE_AUDIOOUTPUT:
default:
SL_LOGE("invalid deviceType %lu", deviceType);
pDataLocator->mIODevice.device = NULL;
return SL_RESULT_PARAMETER_INVALID;
}
// check that device has the correct object ID and is realized,
// and acquire a strong reference to it
result = AcquireStrongRef((IObject *) device, expectedObjectID);
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("locator type is IODEVICE, but device field %p has wrong object ID or is " \
"not realized", device);
pDataLocator->mIODevice.device = NULL;
return result;
}
} else {
SLuint32 deviceID = pDataLocator->mIODevice.deviceID;
switch (deviceType) {
case SL_IODEVICE_LEDARRAY:
if (SL_DEFAULTDEVICEID_LED != deviceID) {
SL_LOGE("invalid LED deviceID %lu", deviceID);
return SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_IODEVICE_VIBRA:
if (SL_DEFAULTDEVICEID_VIBRA != deviceID) {
SL_LOGE("invalid vibra deviceID %lu", deviceID);
return SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_IODEVICE_AUDIOINPUT:
if (SL_DEFAULTDEVICEID_AUDIOINPUT != deviceID) {
SL_LOGE("invalid audio input deviceID %lu", deviceID);
return SL_RESULT_PARAMETER_INVALID;
}
break;
default:
SL_LOGE("invalid deviceType %lu", deviceType);
return SL_RESULT_PARAMETER_INVALID;
}
}
}
break;
case SL_DATALOCATOR_MIDIBUFFERQUEUE:
pDataLocator->mMIDIBufferQueue = *(SLDataLocator_MIDIBufferQueue *)pLocator;
if (0 == pDataLocator->mMIDIBufferQueue.tpqn) {
pDataLocator->mMIDIBufferQueue.tpqn = 192;
}
// number of buffers must be specified, there is no default value, and must not be excessive
if (!((1 <= pDataLocator->mMIDIBufferQueue.numBuffers) &&
(pDataLocator->mMIDIBufferQueue.numBuffers <= 255))) {
SL_LOGE("invalid MIDI buffer queue");
return SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_DATALOCATOR_OUTPUTMIX:
pDataLocator->mOutputMix = *(SLDataLocator_OutputMix *)pLocator;
// check that output mix object has the correct object ID and is realized,
// and acquire a strong reference to it
result = AcquireStrongRef((IObject *) pDataLocator->mOutputMix.outputMix,
SL_OBJECTID_OUTPUTMIX);
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("locatorType is SL_DATALOCATOR_OUTPUTMIX, but outputMix field %p does not " \
"refer to an SL_OBJECTID_OUTPUTMIX or the output mix is not realized", \
pDataLocator->mOutputMix.outputMix);
pDataLocator->mOutputMix.outputMix = NULL;
return result;
}
break;
case SL_DATALOCATOR_URI:
{
pDataLocator->mURI = *(SLDataLocator_URI *)pLocator;
if (NULL == pDataLocator->mURI.URI) {
SL_LOGE("invalid URI");
return SL_RESULT_PARAMETER_INVALID;
}
// NTH verify URI address for validity
size_t len = strlen((const char *) pDataLocator->mURI.URI);
SLchar *myURI = (SLchar *) malloc(len + 1);
if (NULL == myURI) {
pDataLocator->mURI.URI = NULL;
return SL_RESULT_MEMORY_FAILURE;
}
memcpy(myURI, pDataLocator->mURI.URI, len + 1);
// Verify that another thread didn't change the NUL-terminator after we used it
// to determine length of string to copy. It's OK if the string became shorter.
if ('\0' != myURI[len]) {
free(myURI);
pDataLocator->mURI.URI = NULL;
return SL_RESULT_PARAMETER_INVALID;
}
pDataLocator->mURI.URI = myURI;
}
break;
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDFD:
{
pDataLocator->mFD = *(SLDataLocator_AndroidFD *)pLocator;
SL_LOGV("Data locator FD: fd=%ld offset=%lld length=%lld", pDataLocator->mFD.fd,
pDataLocator->mFD.offset, pDataLocator->mFD.length);
// NTH check against process fd limit
if (0 > pDataLocator->mFD.fd) {
return SL_RESULT_PARAMETER_INVALID;
}
}
break;
#endif
default:
SL_LOGE("invalid locatorType %lu", locatorType);
return SL_RESULT_PARAMETER_INVALID;
}
// Verify that another thread didn't change the locatorType field after we used it
// to determine sizeof struct to copy.
if (locatorType != pDataLocator->mLocatorType) {
return SL_RESULT_PARAMETER_INVALID;
}
return SL_RESULT_SUCCESS;
}
/** \brief Free the local deep copy of a data locator */
static void freeDataLocator(DataLocator *pDataLocator)
{
switch (pDataLocator->mLocatorType) {
case SL_DATALOCATOR_URI:
if (NULL != pDataLocator->mURI.URI) {
free(pDataLocator->mURI.URI);
pDataLocator->mURI.URI = NULL;
}
pDataLocator->mURI.URI = NULL;
break;
case SL_DATALOCATOR_IODEVICE:
if (NULL != pDataLocator->mIODevice.device) {
ReleaseStrongRef((IObject *) pDataLocator->mIODevice.device);
pDataLocator->mIODevice.device = NULL;
}
break;
case SL_DATALOCATOR_OUTPUTMIX:
if (NULL != pDataLocator->mOutputMix.outputMix) {
ReleaseStrongRef((IObject *) pDataLocator->mOutputMix.outputMix);
pDataLocator->mOutputMix.outputMix = NULL;
}
break;
default:
break;
}
}
/** \brief Check a data format and make local deep copy */
static SLresult checkDataFormat(void *pFormat, DataFormat *pDataFormat)
{
SLresult result = SL_RESULT_SUCCESS;
if (NULL == pFormat) {
pDataFormat->mFormatType = SL_DATAFORMAT_NULL;
} else {
SLuint32 formatType = *(SLuint32 *)pFormat;
switch (formatType) {
case SL_DATAFORMAT_PCM:
pDataFormat->mPCM = *(SLDataFormat_PCM *)pFormat;
do {
// check the channel count
switch (pDataFormat->mPCM.numChannels) {
case 1: // mono
case 2: // stereo
break;
case 0: // unknown
result = SL_RESULT_PARAMETER_INVALID;
break;
default: // multi-channel
result = SL_RESULT_CONTENT_UNSUPPORTED;
break;
}
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("numChannels=%u", (unsigned) pDataFormat->mPCM.numChannels);
break;
}
// check the sampling rate
switch (pDataFormat->mPCM.samplesPerSec) {
case SL_SAMPLINGRATE_8:
case SL_SAMPLINGRATE_11_025:
case SL_SAMPLINGRATE_12:
case SL_SAMPLINGRATE_16:
case SL_SAMPLINGRATE_22_05:
case SL_SAMPLINGRATE_24:
case SL_SAMPLINGRATE_32:
case SL_SAMPLINGRATE_44_1:
case SL_SAMPLINGRATE_48:
case SL_SAMPLINGRATE_64:
case SL_SAMPLINGRATE_88_2:
case SL_SAMPLINGRATE_96:
case SL_SAMPLINGRATE_192:
break;
case 0:
result = SL_RESULT_PARAMETER_INVALID;
break;
default:
result = SL_RESULT_CONTENT_UNSUPPORTED;
break;
}
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("samplesPerSec=%u", (unsigned) pDataFormat->mPCM.samplesPerSec);
break;
}
// check the sample bit depth
switch (pDataFormat->mPCM.bitsPerSample) {
case SL_PCMSAMPLEFORMAT_FIXED_8:
case SL_PCMSAMPLEFORMAT_FIXED_16:
break;
case SL_PCMSAMPLEFORMAT_FIXED_20:
case SL_PCMSAMPLEFORMAT_FIXED_24:
case SL_PCMSAMPLEFORMAT_FIXED_28:
case SL_PCMSAMPLEFORMAT_FIXED_32:
result = SL_RESULT_CONTENT_UNSUPPORTED;
break;
default:
result = SL_RESULT_PARAMETER_INVALID;
break;
}
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("bitsPerSample=%u", (unsigned) pDataFormat->mPCM.bitsPerSample);
break;
}
// check the container bit depth
if (pDataFormat->mPCM.containerSize < pDataFormat->mPCM.bitsPerSample) {
result = SL_RESULT_PARAMETER_INVALID;
} else if (pDataFormat->mPCM.containerSize != pDataFormat->mPCM.bitsPerSample) {
result = SL_RESULT_CONTENT_UNSUPPORTED;
}
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("containerSize=%u, bitsPerSample=%u",
(unsigned) pDataFormat->mPCM.containerSize,
(unsigned) pDataFormat->mPCM.bitsPerSample);
break;
}
// check the channel mask
switch (pDataFormat->mPCM.channelMask) {
case SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT:
if (2 != pDataFormat->mPCM.numChannels) {
result = SL_RESULT_PARAMETER_INVALID;
}
break;
case SL_SPEAKER_FRONT_LEFT:
case SL_SPEAKER_FRONT_RIGHT:
case SL_SPEAKER_FRONT_CENTER:
if (1 != pDataFormat->mPCM.numChannels) {
result = SL_RESULT_PARAMETER_INVALID;
}
break;
case 0:
pDataFormat->mPCM.channelMask = pDataFormat->mPCM.numChannels == 2 ?
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT : SL_SPEAKER_FRONT_CENTER;
break;
default:
result = SL_RESULT_PARAMETER_INVALID;
break;
}
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("channelMask=0x%lx numChannels=%lu", pDataFormat->mPCM.channelMask,
pDataFormat->mPCM.numChannels);
break;
}
// check the endianness / byte order
switch (pDataFormat->mPCM.endianness) {
case SL_BYTEORDER_LITTLEENDIAN:
case SL_BYTEORDER_BIGENDIAN:
break;
// native is proposed but not yet in spec
default:
result = SL_RESULT_PARAMETER_INVALID;
break;
}
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("endianness=%u", (unsigned) pDataFormat->mPCM.endianness);
break;
}
// here if all checks passed successfully
} while(0);
break;
case SL_DATAFORMAT_MIME:
pDataFormat->mMIME = *(SLDataFormat_MIME *)pFormat;
if (NULL != pDataFormat->mMIME.mimeType) {
// NTH check address for validity
size_t len = strlen((const char *) pDataFormat->mMIME.mimeType);
SLchar *myMIME = (SLchar *) malloc(len + 1);
if (NULL == myMIME) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
memcpy(myMIME, pDataFormat->mMIME.mimeType, len + 1);
// make sure MIME string was not modified asynchronously
if ('\0' != myMIME[len]) {
free(myMIME);
myMIME = NULL;
result = SL_RESULT_PRECONDITIONS_VIOLATED;
}
}
pDataFormat->mMIME.mimeType = myMIME;
}
break;
default:
result = SL_RESULT_PARAMETER_INVALID;
SL_LOGE("formatType=%u", (unsigned) formatType);
break;
}
// make sure format type was not modified asynchronously
if ((SL_RESULT_SUCCESS == result) && (formatType != pDataFormat->mFormatType)) {
result = SL_RESULT_PRECONDITIONS_VIOLATED;
}
}
return result;
}
/** \brief Check interface ID compatibility with respect to a particular data locator format */
SLresult checkSourceFormatVsInterfacesCompatibility(const DataLocatorFormat *pDataLocatorFormat,
const ClassTable *class__, unsigned exposedMask) {
int index;
switch (pDataLocatorFormat->mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
// can't request SLSeekItf if data source is a buffer queue
index = class__->mMPH_to_index[MPH_SEEK];
if (0 <= index) {
if (exposedMask & (1 << index)) {
SL_LOGE("can't request SL_IID_SEEK with a buffer queue data source");
return SL_RESULT_FEATURE_UNSUPPORTED;
}
}
// can't request SLMuteSoloItf if data source is a mono buffer queue
index = class__->mMPH_to_index[MPH_MUTESOLO];
if (0 <= index) {
if ((exposedMask & (1 << index)) &&
(SL_DATAFORMAT_PCM == pDataLocatorFormat->mFormat.mFormatType) &&
(1 == pDataLocatorFormat->mFormat.mPCM.numChannels)) {
SL_LOGE("can't request SL_IID_MUTESOLO with a mono buffer queue data source");
return SL_RESULT_FEATURE_UNSUPPORTED;
}
}
break;
default:
// can't request SLBufferQueueItf or its alias SLAndroidSimpleBufferQueueItf
// if the data source is not a buffer queue
index = class__->mMPH_to_index[MPH_BUFFERQUEUE];
#ifdef ANDROID
assert(index == class__->mMPH_to_index[MPH_ANDROIDSIMPLEBUFFERQUEUE]);
#endif
if (0 <= index) {
if (exposedMask & (1 << index)) {
SL_LOGE("can't request SL_IID_BUFFERQUEUE "
#ifdef ANDROID
"or SL_IID_ANDROIDSIMPLEBUFFERQUEUE "
#endif
"with a non-buffer queue data source");
return SL_RESULT_FEATURE_UNSUPPORTED;
}
}
break;
}
return SL_RESULT_SUCCESS;
}
/** \brief Free the local deep copy of a data format */
static void freeDataFormat(DataFormat *pDataFormat)
{
switch (pDataFormat->mFormatType) {
case SL_DATAFORMAT_MIME:
if (NULL != pDataFormat->mMIME.mimeType) {
free(pDataFormat->mMIME.mimeType);
pDataFormat->mMIME.mimeType = NULL;
}
break;
default:
break;
}
}
/** \brief Check a data source and make local deep copy */
SLresult checkDataSource(const SLDataSource *pDataSrc, DataLocatorFormat *pDataLocatorFormat)
{
if (NULL == pDataSrc) {
SL_LOGE("pDataSrc NULL");
return SL_RESULT_PARAMETER_INVALID;
}
SLDataSource myDataSrc = *pDataSrc;
SLresult result;
result = checkDataLocator(myDataSrc.pLocator, &pDataLocatorFormat->mLocator);
if (SL_RESULT_SUCCESS != result) {
return result;
}
switch (pDataLocatorFormat->mLocator.mLocatorType) {
case SL_DATALOCATOR_URI:
case SL_DATALOCATOR_ADDRESS:
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_MIDIBUFFERQUEUE:
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDFD:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
result = checkDataFormat(myDataSrc.pFormat, &pDataLocatorFormat->mFormat);
if (SL_RESULT_SUCCESS != result) {
freeDataLocator(&pDataLocatorFormat->mLocator);
return result;
}
break;
case SL_DATALOCATOR_NULL:
case SL_DATALOCATOR_OUTPUTMIX:
default:
// invalid but fall through; the invalid locator will be caught later
SL_LOGE("mLocatorType=%u", (unsigned) pDataLocatorFormat->mLocator.mLocatorType);
// keep going
case SL_DATALOCATOR_IODEVICE:
// for these data locator types, ignore the pFormat as it might be uninitialized
pDataLocatorFormat->mFormat.mFormatType = SL_DATAFORMAT_NULL;
break;
}
pDataLocatorFormat->u.mSource.pLocator = &pDataLocatorFormat->mLocator;
pDataLocatorFormat->u.mSource.pFormat = &pDataLocatorFormat->mFormat;
return SL_RESULT_SUCCESS;
}
/** \brief Check a data sink and make local deep copy */
SLresult checkDataSink(const SLDataSink *pDataSink, DataLocatorFormat *pDataLocatorFormat,
SLuint32 objType)
{
if (NULL == pDataSink) {
SL_LOGE("pDataSink NULL");
return SL_RESULT_PARAMETER_INVALID;
}
SLDataSink myDataSink = *pDataSink;
SLresult result;
result = checkDataLocator(myDataSink.pLocator, &pDataLocatorFormat->mLocator);
if (SL_RESULT_SUCCESS != result) {
return result;
}
switch (pDataLocatorFormat->mLocator.mLocatorType) {
case SL_DATALOCATOR_URI:
case SL_DATALOCATOR_ADDRESS:
result = checkDataFormat(myDataSink.pFormat, &pDataLocatorFormat->mFormat);
if (SL_RESULT_SUCCESS != result) {
freeDataLocator(&pDataLocatorFormat->mLocator);
return result;
}
break;
case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
if (SL_OBJECTID_AUDIOPLAYER == objType) {
SL_LOGE("buffer queue can't be used as data sink for audio player");
result = SL_RESULT_PARAMETER_INVALID;
} else if (SL_OBJECTID_AUDIORECORDER == objType) {
#ifdef ANDROID
if (SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE !=
pDataLocatorFormat->mLocator.mLocatorType) {
SL_LOGE("audio recorder source locator must be SL_DATALOCATOR_ANDROIDBUFFERQUEUE");
result = SL_RESULT_PARAMETER_INVALID;
} else {
result = checkDataFormat(myDataSink.pFormat, &pDataLocatorFormat->mFormat);
}
#else
SL_LOGE("mLocatorType=%u", (unsigned) pDataLocatorFormat->mLocator.mLocatorType);
result = SL_RESULT_PARAMETER_INVALID;
#endif
}
if (SL_RESULT_SUCCESS != result) {
freeDataLocator(&pDataLocatorFormat->mLocator);
return result;
}
break;
case SL_DATALOCATOR_NULL:
case SL_DATALOCATOR_MIDIBUFFERQUEUE:
default:
// invalid but fall through; the invalid locator will be caught later
SL_LOGE("mLocatorType=%u", (unsigned) pDataLocatorFormat->mLocator.mLocatorType);
// keep going
case SL_DATALOCATOR_IODEVICE:
case SL_DATALOCATOR_OUTPUTMIX:
// for these data locator types, ignore the pFormat as it might be uninitialized
pDataLocatorFormat->mFormat.mFormatType = SL_DATAFORMAT_NULL;
break;
}
pDataLocatorFormat->u.mSink.pLocator = &pDataLocatorFormat->mLocator;
pDataLocatorFormat->u.mSink.pFormat = &pDataLocatorFormat->mFormat;
return SL_RESULT_SUCCESS;
}
/** \brief Free the local deep copy of a data locator format */
void freeDataLocatorFormat(DataLocatorFormat *dlf)
{
freeDataLocator(&dlf->mLocator);
freeDataFormat(&dlf->mFormat);
}
/* Interface initialization hooks */
extern void
I3DCommit_init(void *),
I3DDoppler_init(void *),
I3DGrouping_init(void *),
I3DLocation_init(void *),
I3DMacroscopic_init(void *),
I3DSource_init(void *),
IAndroidConfiguration_init(void *),
IAndroidEffect_init(void *),
IAndroidEffectCapabilities_init(void *),
IAndroidEffectSend_init(void *),
IAudioDecoderCapabilities_init(void *),
IAudioEncoder_init(void *),
IAudioEncoderCapabilities_init(void *),
IAudioIODeviceCapabilities_init(void *),
IBassBoost_init(void *),
IBufferQueue_init(void *),
IDeviceVolume_init(void *),
IDynamicInterfaceManagement_init(void *),
IDynamicSource_init(void *),
IEffectSend_init(void *),
IEngine_init(void *),
IEngineCapabilities_init(void *),
IEnvironmentalReverb_init(void *),
IEqualizer_init(void *),
ILEDArray_init(void *),
IMIDIMessage_init(void *),
IMIDIMuteSolo_init(void *),
IMIDITempo_init(void *),
IMIDITime_init(void *),
IMetadataExtraction_init(void *),
IMetadataTraversal_init(void *),
IMuteSolo_init(void *),
IObject_init(void *),
IOutputMix_init(void *),
IOutputMixExt_init(void *),
IPitch_init(void *),
IPlay_init(void *),
IPlaybackRate_init(void *),
IPrefetchStatus_init(void *),
IPresetReverb_init(void *),
IRatePitch_init(void *),
IRecord_init(void *),
ISeek_init(void *),
IThreadSync_init(void *),
IVibra_init(void *),
IVirtualizer_init(void *),
IVisualization_init(void *),
IVolume_init(void *);
extern void
I3DGrouping_deinit(void *),
IAndroidEffect_deinit(void *),
IAndroidEffectCapabilities_deinit(void *),
IBassBoost_deinit(void *),
IBufferQueue_deinit(void *),
IEngine_deinit(void *),
IEnvironmentalReverb_deinit(void *),
IEqualizer_deinit(void *),
IObject_deinit(void *),
IPresetReverb_deinit(void *),
IThreadSync_deinit(void *),
IVirtualizer_deinit(void *);
extern bool
IAndroidEffectCapabilities_Expose(void *),
IBassBoost_Expose(void *),
IEnvironmentalReverb_Expose(void *),
IEqualizer_Expose(void *),
IPresetReverb_Expose(void *),
IVirtualizer_Expose(void *);
#if !(USE_PROFILES & USE_PROFILES_MUSIC)
#define IDynamicSource_init NULL
#define IMetadataExtraction_init NULL
#define IMetadataTraversal_init NULL
#define IVisualization_init NULL
#endif
#if !(USE_PROFILES & USE_PROFILES_GAME)
#define I3DCommit_init NULL
#define I3DDoppler_init NULL
#define I3DGrouping_init NULL
#define I3DLocation_init NULL
#define I3DMacroscopic_init NULL
#define I3DSource_init NULL
#define IMIDIMessage_init NULL
#define IMIDIMuteSolo_init NULL
#define IMIDITempo_init NULL
#define IMIDITime_init NULL
#define IPitch_init NULL
#define IRatePitch_init NULL
#define I3DGrouping_deinit NULL
#endif
#if !(USE_PROFILES & USE_PROFILES_BASE)
#define IAudioDecoderCapabilities_init NULL
#define IAudioEncoderCapabilities_init NULL
#define IAudioEncoder_init NULL
#define IAudioIODeviceCapabilities_init NULL
#define IDeviceVolume_init NULL
#define IEngineCapabilities_init NULL
#define IThreadSync_init NULL
#define IThreadSync_deinit NULL
#endif
#if !(USE_PROFILES & USE_PROFILES_OPTIONAL)
#define ILEDArray_init NULL
#define IVibra_init NULL
#endif
#ifndef ANDROID
#define IAndroidConfiguration_init NULL
#define IAndroidEffect_init NULL
#define IAndroidEffectCapabilities_init NULL
#define IAndroidEffectSend_init NULL
#define IAndroidEffect_deinit NULL
#define IAndroidEffectCapabilities_deinit NULL
#define IAndroidEffectCapabilities_Expose NULL
#endif
#ifndef USE_OUTPUTMIXEXT
#define IOutputMixExt_init NULL
#endif
/*static*/ const struct MPH_init MPH_init_table[MPH_MAX] = {
{ /* MPH_3DCOMMIT, */ I3DCommit_init, NULL, NULL, NULL, NULL },
{ /* MPH_3DDOPPLER, */ I3DDoppler_init, NULL, NULL, NULL, NULL },
{ /* MPH_3DGROUPING, */ I3DGrouping_init, NULL, I3DGrouping_deinit, NULL, NULL },
{ /* MPH_3DLOCATION, */ I3DLocation_init, NULL, NULL, NULL, NULL },
{ /* MPH_3DMACROSCOPIC, */ I3DMacroscopic_init, NULL, NULL, NULL, NULL },
{ /* MPH_3DSOURCE, */ I3DSource_init, NULL, NULL, NULL, NULL },
{ /* MPH_AUDIODECODERCAPABILITIES, */ IAudioDecoderCapabilities_init, NULL, NULL, NULL, NULL },
{ /* MPH_AUDIOENCODER, */ IAudioEncoder_init, NULL, NULL, NULL, NULL },
{ /* MPH_AUDIOENCODERCAPABILITIES, */ IAudioEncoderCapabilities_init, NULL, NULL, NULL, NULL },
{ /* MPH_AUDIOIODEVICECAPABILITIES, */ IAudioIODeviceCapabilities_init, NULL, NULL, NULL,
NULL },
{ /* MPH_BASSBOOST, */ IBassBoost_init, NULL, IBassBoost_deinit, IBassBoost_Expose, NULL },
{ /* MPH_BUFFERQUEUE, */ IBufferQueue_init, NULL, IBufferQueue_deinit, NULL, NULL },
{ /* MPH_DEVICEVOLUME, */ IDeviceVolume_init, NULL, NULL, NULL, NULL },
{ /* MPH_DYNAMICINTERFACEMANAGEMENT, */ IDynamicInterfaceManagement_init, NULL, NULL, NULL,
NULL },
{ /* MPH_DYNAMICSOURCE, */ IDynamicSource_init, NULL, NULL, NULL, NULL },
{ /* MPH_EFFECTSEND, */ IEffectSend_init, NULL, NULL, NULL, NULL },
{ /* MPH_ENGINE, */ IEngine_init, NULL, IEngine_deinit, NULL, NULL },
{ /* MPH_ENGINECAPABILITIES, */ IEngineCapabilities_init, NULL, NULL, NULL, NULL },
{ /* MPH_ENVIRONMENTALREVERB, */ IEnvironmentalReverb_init, NULL, IEnvironmentalReverb_deinit,
IEnvironmentalReverb_Expose, NULL },
{ /* MPH_EQUALIZER, */ IEqualizer_init, NULL, IEqualizer_deinit, IEqualizer_Expose, NULL },
{ /* MPH_LED, */ ILEDArray_init, NULL, NULL, NULL, NULL },
{ /* MPH_METADATAEXTRACTION, */ IMetadataExtraction_init, NULL, NULL, NULL, NULL },
{ /* MPH_METADATATRAVERSAL, */ IMetadataTraversal_init, NULL, NULL, NULL, NULL },
{ /* MPH_MIDIMESSAGE, */ IMIDIMessage_init, NULL, NULL, NULL, NULL },
{ /* MPH_MIDITIME, */ IMIDITime_init, NULL, NULL, NULL, NULL },
{ /* MPH_MIDITEMPO, */ IMIDITempo_init, NULL, NULL, NULL, NULL },
{ /* MPH_MIDIMUTESOLO, */ IMIDIMuteSolo_init, NULL, NULL, NULL, NULL },
{ /* MPH_MUTESOLO, */ IMuteSolo_init, NULL, NULL, NULL, NULL },
{ /* MPH_NULL, */ NULL, NULL, NULL, NULL, NULL },
{ /* MPH_OBJECT, */ IObject_init, NULL, IObject_deinit, NULL, NULL },
{ /* MPH_OUTPUTMIX, */ IOutputMix_init, NULL, NULL, NULL, NULL },
{ /* MPH_PITCH, */ IPitch_init, NULL, NULL, NULL, NULL },
{ /* MPH_PLAY, */ IPlay_init, NULL, NULL, NULL, NULL },
{ /* MPH_PLAYBACKRATE, */ IPlaybackRate_init, NULL, NULL, NULL, NULL },
{ /* MPH_PREFETCHSTATUS, */ IPrefetchStatus_init, NULL, NULL, NULL, NULL },
{ /* MPH_PRESETREVERB, */ IPresetReverb_init, NULL, IPresetReverb_deinit,
IPresetReverb_Expose, NULL },
{ /* MPH_RATEPITCH, */ IRatePitch_init, NULL, NULL, NULL, NULL },
{ /* MPH_RECORD, */ IRecord_init, NULL, NULL, NULL, NULL },
{ /* MPH_SEEK, */ ISeek_init, NULL, NULL, NULL, NULL },
{ /* MPH_THREADSYNC, */ IThreadSync_init, NULL, IThreadSync_deinit, NULL, NULL },
{ /* MPH_VIBRA, */ IVibra_init, NULL, NULL, NULL, NULL },
{ /* MPH_VIRTUALIZER, */ IVirtualizer_init, NULL, IVirtualizer_deinit, IVirtualizer_Expose,
NULL },
{ /* MPH_VISUALIZATION, */ IVisualization_init, NULL, NULL, NULL, NULL },
{ /* MPH_VOLUME, */ IVolume_init, NULL, NULL, NULL, NULL },
{ /* MPH_OUTPUTMIXEXT, */ IOutputMixExt_init, NULL, NULL, NULL, NULL },
{ /* MPH_ANDROIDEFFECT */ IAndroidEffect_init, NULL, IAndroidEffect_deinit, NULL, NULL },
{ /* MPH_ANDROIDEFFECTCAPABILITIES */ IAndroidEffectCapabilities_init, NULL,
IAndroidEffectCapabilities_deinit, IAndroidEffectCapabilities_Expose, NULL },
{ /* MPH_ANDROIDEFFECTSEND */ IAndroidEffectSend_init, NULL, NULL, NULL, NULL },
{ /* MPH_ANDROIDCONFIGURATION */ IAndroidConfiguration_init, NULL, NULL, NULL, NULL },
{ /* MPH_ANDROIDSIMPLEBUFFERQUEUE, */ IBufferQueue_init /* alias */, NULL, NULL, NULL, NULL }
};
/** \brief Construct a new instance of the specified class, exposing selected interfaces */
IObject *construct(const ClassTable *class__, unsigned exposedMask, SLEngineItf engine)
{
IObject *this;
// Do not change this to malloc; we depend on the object being memset to zero
this = (IObject *) calloc(1, class__->mSize);
if (NULL != this) {
SL_LOGV("construct %s at %p", class__->mName, this);
unsigned lossOfControlMask = 0;
// a NULL engine means we are constructing the engine
IEngine *thisEngine = (IEngine *) engine;
if (NULL == thisEngine) {
thisEngine = &((CEngine *) this)->mEngine;
} else {
interface_lock_exclusive(thisEngine);
if (MAX_INSTANCE <= thisEngine->mInstanceCount) {
SL_LOGE("Too many objects");
interface_unlock_exclusive(thisEngine);
free(this);
return NULL;
}
// pre-allocate a pending slot, but don't assign bit from mInstanceMask yet
++thisEngine->mInstanceCount;
assert(((unsigned) ~0) != thisEngine->mInstanceMask);
interface_unlock_exclusive(thisEngine);
// const, no lock needed
if (thisEngine->mLossOfControlGlobal) {
lossOfControlMask = ~0;
}
}
this->mLossOfControlMask = lossOfControlMask;
this->mClass = class__;
this->mEngine = thisEngine;
const struct iid_vtable *x = class__->mInterfaces;
SLuint8 *interfaceStateP = this->mInterfaceStates;
SLuint32 index;
for (index = 0; index < class__->mInterfaceCount; ++index, ++x, exposedMask >>= 1) {
SLuint8 state;
// initialize all interfaces with init hooks, even if not exposed
const struct MPH_init *mi = &MPH_init_table[x->mMPH];
VoidHook init = mi->mInit;
if (NULL != init) {
void *self = (char *) this + x->mOffset;
// IObject does not have an mThis, so [1] is not always defined
if (index) {
((IObject **) self)[1] = this;
}
// call the initialization hook
(*init)(self);
// IObject does not require a call to GetInterface
if (index) {
// This trickery invalidates the v-table until GetInterface
((size_t *) self)[0] ^= ~0;
}
// if interface is exposed, also call the optional expose hook
BoolHook expose;
state = (exposedMask & 1) && ((NULL == (expose = mi->mExpose)) || (*expose)(self)) ?
INTERFACE_EXPOSED : INTERFACE_INITIALIZED;
// FIXME log or report to application if an expose hook on a
// required explicit interface fails at creation time
} else {
state = INTERFACE_UNINITIALIZED;
}
*interfaceStateP++ = state;
}
// note that the new object is not yet published; creator must call IObject_Publish
}
return this;
}
/* This implementation supports at most one engine */
static CEngine *theOneTrueEngine = NULL;
static pthread_mutex_t theOneTrueMutex = PTHREAD_MUTEX_INITIALIZER;
/** \brief Called by dlopen when .so is loaded */
__attribute__((constructor)) static void onDlOpen(void)
{
}
/** \brief Called by dlclose when .so is unloaded */
__attribute__((destructor)) static void onDlClose(void)
{
if (NULL != theOneTrueEngine) {
SL_LOGE("Object::Destroy omitted for engine %p", theOneTrueEngine);
}
}
/** \brief Called by IObject::Destroy after engine is destroyed. The parameter refers to the
* previous engine, which is now undefined memory.
*/
void CEngine_Destroyed(CEngine *self)
{
int ok;
ok = pthread_mutex_lock(&theOneTrueMutex);
assert(0 == ok);
assert(self == theOneTrueEngine);
theOneTrueEngine = NULL;
ok = pthread_mutex_unlock(&theOneTrueMutex);
assert(0 == ok);
}
/* Initial global entry points */
/** \brief slCreateEngine Function */
SLresult SLAPIENTRY slCreateEngine(SLObjectItf *pEngine, SLuint32 numOptions,
const SLEngineOption *pEngineOptions, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_GLOBAL
int ok;
ok = pthread_mutex_lock(&theOneTrueMutex);
assert(0 == ok);
do {
#ifdef ANDROID
android::ProcessState::self()->startThreadPool();
#ifndef USE_BACKPORT
android::DataSource::RegisterDefaultSniffers();
#endif
#endif
if (NULL == pEngine) {
result = SL_RESULT_PARAMETER_INVALID;
break;
}
*pEngine = NULL;
if (NULL != theOneTrueEngine) {
SL_LOGE("slCreateEngine while another engine %p is active", theOneTrueEngine);
result = SL_RESULT_RESOURCE_ERROR;
break;
}
if ((0 < numOptions) && (NULL == pEngineOptions)) {
SL_LOGE("numOptions=%lu and pEngineOptions=NULL", numOptions);
result = SL_RESULT_PARAMETER_INVALID;
break;
}
// default values
SLboolean threadSafe = SL_BOOLEAN_TRUE;
SLboolean lossOfControlGlobal = SL_BOOLEAN_FALSE;
// process engine options
SLuint32 i;
const SLEngineOption *option = pEngineOptions;
result = SL_RESULT_SUCCESS;
for (i = 0; i < numOptions; ++i, ++option) {
switch (option->feature) {
case SL_ENGINEOPTION_THREADSAFE:
threadSafe = SL_BOOLEAN_FALSE != (SLboolean) option->data; // normalize
break;
case SL_ENGINEOPTION_LOSSOFCONTROL:
lossOfControlGlobal = SL_BOOLEAN_FALSE != (SLboolean) option->data; // normalize
break;
default:
SL_LOGE("unknown engine option: feature=%lu data=%lu",
option->feature, option->data);
result = SL_RESULT_PARAMETER_INVALID;
break;
}
}
if (SL_RESULT_SUCCESS != result) {
break;
}
unsigned exposedMask;
const ClassTable *pCEngine_class = objectIDtoClass(SL_OBJECTID_ENGINE);
assert(NULL != pCEngine_class);
result = checkInterfaces(pCEngine_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask);
if (SL_RESULT_SUCCESS != result) {
break;
}
CEngine *this = (CEngine *) construct(pCEngine_class, exposedMask, NULL);
if (NULL == this) {
result = SL_RESULT_MEMORY_FAILURE;
break;
}
// initialize fields not associated with an interface
memset(&this->mSyncThread, 0, sizeof(pthread_t));
// initialize fields related to an interface
this->mObject.mLossOfControlMask = lossOfControlGlobal ? ~0 : 0;
this->mEngine.mLossOfControlGlobal = lossOfControlGlobal;
this->mEngineCapabilities.mThreadSafe = threadSafe;
IObject_Publish(&this->mObject);
theOneTrueEngine = this;
// return the new engine object
*pEngine = &this->mObject.mItf;
} while(0);
ok = pthread_mutex_unlock(&theOneTrueMutex);
assert(0 == ok);
SL_LEAVE_GLOBAL
}
/** \brief slQueryNumSupportedEngineInterfaces Function */
SLresult SLAPIENTRY slQueryNumSupportedEngineInterfaces(SLuint32 *pNumSupportedInterfaces)
{
SL_ENTER_GLOBAL
if (NULL == pNumSupportedInterfaces) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
const ClassTable *class__ = objectIDtoClass(SL_OBJECTID_ENGINE);
assert(NULL != class__);
SLuint32 count = 0;
SLuint32 i;
for (i = 0; i < class__->mInterfaceCount; ++i) {
switch (class__->mInterfaces[i].mInterface) {
case INTERFACE_IMPLICIT:
case INTERFACE_IMPLICIT_PREREALIZE:
case INTERFACE_EXPLICIT:
case INTERFACE_EXPLICIT_PREREALIZE:
case INTERFACE_DYNAMIC:
++count;
break;
case INTERFACE_UNAVAILABLE:
break;
default:
assert(false);
break;
}
}
*pNumSupportedInterfaces = count;
result = SL_RESULT_SUCCESS;
}
SL_LEAVE_GLOBAL
}
/** \brief slQuerySupportedEngineInterfaces Function */
SLresult SLAPIENTRY slQuerySupportedEngineInterfaces(SLuint32 index, SLInterfaceID *pInterfaceId)
{
SL_ENTER_GLOBAL
if (NULL == pInterfaceId) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pInterfaceId = NULL;
const ClassTable *class__ = objectIDtoClass(SL_OBJECTID_ENGINE);
assert(NULL != class__);
result = SL_RESULT_PARAMETER_INVALID; // will be reset later
SLuint32 i;
for (i = 0; i < class__->mInterfaceCount; ++i) {
switch (class__->mInterfaces[i].mInterface) {
case INTERFACE_IMPLICIT:
case INTERFACE_IMPLICIT_PREREALIZE:
case INTERFACE_EXPLICIT:
case INTERFACE_EXPLICIT_PREREALIZE:
case INTERFACE_DYNAMIC:
break;
case INTERFACE_UNAVAILABLE:
continue;
default:
assert(false);
break;
}
if (index == 0) {
// The engine has no aliases, but if it did, this would return only the primary
*pInterfaceId = &SL_IID_array[class__->mInterfaces[i].mMPH];
result = SL_RESULT_SUCCESS;
break;
}
--index;
}
}
SL_LEAVE_GLOBAL
}