/*
* 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.
*/
/* Engine implementation */
#include <endian.h>
#include "sles_allinclusive.h"
/* Utility functions */
static SLresult initializeBufferQueueMembers(CAudioPlayer *ap) {
// inline allocation of circular mArray, up to a typical max
if (BUFFER_HEADER_TYPICAL >= ap->mBufferQueue.mNumBuffers) {
ap->mBufferQueue.mArray = ap->mBufferQueue.mTypical;
} else {
// Avoid possible integer overflow during multiplication; this arbitrary
// maximum is big enough to not interfere with real applications, but
// small enough to not overflow.
if (ap->mBufferQueue.mNumBuffers >= 256) {
return SL_RESULT_MEMORY_FAILURE;
}
ap->mBufferQueue.mArray = (BufferHeader *)
malloc((ap->mBufferQueue.mNumBuffers + 1) * sizeof(BufferHeader));
if (NULL == ap->mBufferQueue.mArray) {
return SL_RESULT_MEMORY_FAILURE;
}
}
ap->mBufferQueue.mFront = ap->mBufferQueue.mArray;
ap->mBufferQueue.mRear = ap->mBufferQueue.mArray;
return SL_RESULT_SUCCESS;
}
#ifdef ANDROID
static SLresult initializeAndroidBufferQueueMembers(CAudioPlayer *ap) {
// Avoid possible integer overflow during multiplication; this arbitrary
// maximum is big enough to not interfere with real applications, but
// small enough to not overflow.
if (ap->mAndroidBufferQueue.mNumBuffers >= 256) {
return SL_RESULT_MEMORY_FAILURE;
}
ap->mAndroidBufferQueue.mBufferArray = (AdvancedBufferHeader *)
malloc( (ap->mAndroidBufferQueue.mNumBuffers + 1) * sizeof(AdvancedBufferHeader));
if (NULL == ap->mAndroidBufferQueue.mBufferArray) {
return SL_RESULT_MEMORY_FAILURE;
} else {
// initialize ABQ buffer type
// assert below has been checked in android_audioPlayer_checkSourceSink
assert(SL_DATAFORMAT_MIME == ap->mDataSource.mFormat.mFormatType);
switch (ap->mDataSource.mFormat.mMIME.containerType) {
case SL_CONTAINERTYPE_MPEG_TS:
ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
break;
case SL_CONTAINERTYPE_AAC:
case SL_CONTAINERTYPE_RAW: {
const char* mime = (char*)ap->mDataSource.mFormat.mMIME.mimeType;
if ((mime != NULL) && !(strcasecmp(mime, (const char *)SL_ANDROID_MIME_AACADTS) &&
strcasecmp(mime, ANDROID_MIME_AACADTS_ANDROID_FRAMEWORK))) {
ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeAacadts;
} else {
ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
SL_LOGE("CreateAudioPlayer: Invalid buffer type in Android Buffer Queue");
return SL_RESULT_CONTENT_UNSUPPORTED;
}
} break;
default:
ap->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
SL_LOGE("CreateAudioPlayer: Invalid buffer type in Android Buffer Queue");
return SL_RESULT_CONTENT_UNSUPPORTED;
}
ap->mAndroidBufferQueue.mFront = ap->mAndroidBufferQueue.mBufferArray;
ap->mAndroidBufferQueue.mRear = ap->mAndroidBufferQueue.mBufferArray;
}
return SL_RESULT_SUCCESS;
}
#endif
static SLresult IEngine_CreateLEDDevice(SLEngineItf self, SLObjectItf *pDevice, SLuint32 deviceID,
SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_OPTIONAL
if ((NULL == pDevice) || (SL_DEFAULTDEVICEID_LED != deviceID)) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pDevice = NULL;
unsigned exposedMask;
const ClassTable *pCLEDDevice_class = objectIDtoClass(SL_OBJECTID_LEDDEVICE);
if (NULL == pCLEDDevice_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCLEDDevice_class, numInterfaces, pInterfaceIds,
pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
CLEDDevice *thiz = (CLEDDevice *) construct(pCLEDDevice_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
thiz->mDeviceID = deviceID;
IObject_Publish(&thiz->mObject);
// return the new LED object
*pDevice = &thiz->mObject.mItf;
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateVibraDevice(SLEngineItf self, SLObjectItf *pDevice, SLuint32 deviceID,
SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_OPTIONAL
if ((NULL == pDevice) || (SL_DEFAULTDEVICEID_VIBRA != deviceID)) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pDevice = NULL;
unsigned exposedMask;
const ClassTable *pCVibraDevice_class = objectIDtoClass(SL_OBJECTID_VIBRADEVICE);
if (NULL == pCVibraDevice_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCVibraDevice_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
CVibraDevice *thiz = (CVibraDevice *) construct(pCVibraDevice_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
thiz->mDeviceID = deviceID;
IObject_Publish(&thiz->mObject);
// return the new vibra object
*pDevice = &thiz->mObject.mItf;
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateAudioPlayer(SLEngineItf self, SLObjectItf *pPlayer,
SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
if (NULL == pPlayer) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pPlayer = NULL;
unsigned exposedMask, requiredMask;
const ClassTable *pCAudioPlayer_class = objectIDtoClass(SL_OBJECTID_AUDIOPLAYER);
assert(NULL != pCAudioPlayer_class);
result = checkInterfaces(pCAudioPlayer_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, &requiredMask);
if (SL_RESULT_SUCCESS == result) {
// Construct our new AudioPlayer instance
CAudioPlayer *thiz = (CAudioPlayer *) construct(pCAudioPlayer_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
do {
// Initialize private fields not associated with an interface
// Default data source in case of failure in checkDataSource
thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSource.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// Default data sink in case of failure in checkDataSink
thiz->mDataSink.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSink.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// Default is no per-channel mute or solo
thiz->mMuteMask = 0;
thiz->mSoloMask = 0;
// Will be set soon for PCM buffer queues, or later by platform-specific code
// during Realize or Prefetch
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
// More default values, in case destructor needs to be called early
thiz->mDirectLevel = 0; // no attenuation
#ifdef USE_OUTPUTMIXEXT
thiz->mTrack = NULL;
thiz->mGains[0] = 1.0f;
thiz->mGains[1] = 1.0f;
thiz->mDestroyRequested = SL_BOOLEAN_FALSE;
#endif
#ifdef USE_SNDFILE
thiz->mSndFile.mPathname = NULL;
thiz->mSndFile.mSNDFILE = NULL;
memset(&thiz->mSndFile.mSfInfo, 0, sizeof(SF_INFO));
memset(&thiz->mSndFile.mMutex, 0, sizeof(pthread_mutex_t));
thiz->mSndFile.mEOF = SL_BOOLEAN_FALSE;
thiz->mSndFile.mWhich = 0;
memset(thiz->mSndFile.mBuffer, 0, sizeof(thiz->mSndFile.mBuffer));
#endif
#ifdef ANDROID
// placement new (explicit constructor)
// FIXME unnecessary once those fields are encapsulated in one class, rather
// than a structure
(void) new (&thiz->mAudioTrack) android::sp<android::AudioTrack>();
(void) new (&thiz->mCallbackProtector)
android::sp<android::CallbackProtector>();
(void) new (&thiz->mAuxEffect) android::sp<android::AudioEffect>();
(void) new (&thiz->mAPlayer) android::sp<android::GenericPlayer>();
// Android-specific POD fields are initialized in android_audioPlayer_create,
// and assume calloc or memset 0 during allocation
#endif
// Check the source and sink parameters against generic constraints,
// and make a local copy of all parameters in case other application threads
// change memory concurrently.
result = checkDataSource("pAudioSrc", pAudioSrc, &thiz->mDataSource,
DATALOCATOR_MASK_URI | DATALOCATOR_MASK_ADDRESS |
DATALOCATOR_MASK_BUFFERQUEUE
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDFD | DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE
| DATALOCATOR_MASK_ANDROIDBUFFERQUEUE
#endif
, DATAFORMAT_MASK_MIME | DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX);
if (SL_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pAudioSnk", pAudioSnk, &thiz->mDataSink,
DATALOCATOR_MASK_OUTPUTMIX // for playback
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE // for decode to a BQ
| DATALOCATOR_MASK_BUFFERQUEUE // for decode to a BQ
#endif
, DATAFORMAT_MASK_NULL
#ifdef ANDROID
| DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX // for decode to PCM
#endif
);
if (SL_RESULT_SUCCESS != result) {
break;
}
// It would be unsafe to ever refer to the application pointers again
pAudioSrc = NULL;
pAudioSnk = NULL;
// Check that the requested interfaces are compatible with data source and sink
result = checkSourceSinkVsInterfacesCompatibility(&thiz->mDataSource,
&thiz->mDataSink, pCAudioPlayer_class, requiredMask);
if (SL_RESULT_SUCCESS != result) {
break;
}
// copy the buffer queue count from source locator (for playback) / from the
// sink locator (for decode on ANDROID build) to the buffer queue interface
// we have already range-checked the value down to a smaller width
SLuint16 nbBuffers = 0;
bool usesAdvancedBufferHeaders = false;
bool usesSimpleBufferQueue = false;
// creating an AudioPlayer which decodes AAC ADTS buffers to a PCM buffer queue
// will cause usesAdvancedBufferHeaders and usesSimpleBufferQueue to be true
switch (thiz->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
usesSimpleBufferQueue = true;
nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mBufferQueue.numBuffers;
assert(SL_DATAFORMAT_PCM == thiz->mDataSource.mFormat.mFormatType
|| SL_ANDROID_DATAFORMAT_PCM_EX
== thiz->mDataSource.mFormat.mFormatType);
thiz->mNumChannels = thiz->mDataSource.mFormat.mPCM.numChannels;
thiz->mSampleRateMilliHz = thiz->mDataSource.mFormat.mPCM.samplesPerSec;
break;
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
usesAdvancedBufferHeaders = true;
nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;
thiz->mAndroidBufferQueue.mNumBuffers = nbBuffers;
break;
#endif
default:
nbBuffers = 0;
break;
}
#ifdef ANDROID
switch (thiz->mDataSink.mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
usesSimpleBufferQueue = true;
nbBuffers = thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
assert(SL_DATAFORMAT_PCM == thiz->mDataSink.mFormat.mFormatType
|| SL_ANDROID_DATAFORMAT_PCM_EX
== thiz->mDataSink.mFormat.mFormatType);
// FIXME The values specified by the app are meaningless. We get the
// real values from the decoder. But the data sink checks currently require
// that the app specify these useless values. Needs doc/fix.
// Instead use the "unknown" values, as needed by prepare completion.
// thiz->mNumChannels = thiz->mDataSink.mFormat.mPCM.numChannels;
// thiz->mSampleRateMilliHz = thiz->mDataSink.mFormat.mPCM.samplesPerSec;
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
break;
default:
// leave nbBuffers unchanged
break;
}
#endif
thiz->mBufferQueue.mNumBuffers = nbBuffers;
// check the audio source and sink parameters against platform support
#ifdef ANDROID
result = android_audioPlayer_checkSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
break;
}
#endif
#ifdef USE_SNDFILE
result = SndFile_checkAudioPlayerSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
break;
}
#endif
#ifdef USE_OUTPUTMIXEXT
result = IOutputMixExt_checkAudioPlayerSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
break;
}
#endif
// Allocate memory for buffer queue
if (usesAdvancedBufferHeaders) {
#ifdef ANDROID
// locator is SL_DATALOCATOR_ANDROIDBUFFERQUEUE
result = initializeAndroidBufferQueueMembers(thiz);
#else
assert(false);
#endif
}
if (usesSimpleBufferQueue) {
// locator is SL_DATALOCATOR_BUFFERQUEUE
// or SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
result = initializeBufferQueueMembers(thiz);
}
// used to store the data source of our audio player
thiz->mDynamicSource.mDataSource = &thiz->mDataSource.u.mSource;
// platform-specific initialization
#ifdef ANDROID
android_audioPlayer_create(thiz);
#endif
} while (0);
if (SL_RESULT_SUCCESS != result) {
IObject_Destroy(&thiz->mObject.mItf);
} else {
IObject_Publish(&thiz->mObject);
// return the new audio player object
*pPlayer = &thiz->mObject.mItf;
}
}
}
}
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateAudioRecorder(SLEngineItf self, SLObjectItf *pRecorder,
SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if (USE_PROFILES & USE_PROFILES_OPTIONAL) || defined(ANDROID)
if (NULL == pRecorder) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pRecorder = NULL;
unsigned exposedMask;
const ClassTable *pCAudioRecorder_class = objectIDtoClass(SL_OBJECTID_AUDIORECORDER);
if (NULL == pCAudioRecorder_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCAudioRecorder_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
// Construct our new AudioRecorder instance
CAudioRecorder *thiz = (CAudioRecorder *) construct(pCAudioRecorder_class, exposedMask,
self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
do {
// Initialize fields not associated with any interface
// Default data source in case of failure in checkDataSource
thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSource.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// Default data sink in case of failure in checkDataSink
thiz->mDataSink.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSink.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// These fields are set to real values by
// android_audioRecorder_checkSourceSink. Note that the data sink is
// always PCM buffer queue, so we know the channel count and sample rate early.
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
#ifdef ANDROID
// placement new (explicit constructor)
// FIXME unnecessary once those fields are encapsulated in one class, rather
// than a structure
(void) new (&thiz->mAudioRecord) android::sp<android::AudioRecord>();
(void) new (&thiz->mCallbackProtector)
android::sp<android::CallbackProtector>();
thiz->mRecordSource = AUDIO_SOURCE_DEFAULT;
#endif
// Check the source and sink parameters, and make a local copy of all parameters
result = checkDataSource("pAudioSrc", pAudioSrc, &thiz->mDataSource,
DATALOCATOR_MASK_IODEVICE, DATAFORMAT_MASK_NULL);
if (SL_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pAudioSnk", pAudioSnk, &thiz->mDataSink,
DATALOCATOR_MASK_URI
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE
#endif
, DATAFORMAT_MASK_MIME | DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX
);
if (SL_RESULT_SUCCESS != result) {
break;
}
// It would be unsafe to ever refer to the application pointers again
pAudioSrc = NULL;
pAudioSnk = NULL;
// check the audio source and sink parameters against platform support
#ifdef ANDROID
result = android_audioRecorder_checkSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("Cannot create AudioRecorder: invalid source or sink");
break;
}
#endif
#ifdef ANDROID
// Allocate memory for buffer queue
SLuint32 locatorType = thiz->mDataSink.mLocator.mLocatorType;
if (locatorType == SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE) {
thiz->mBufferQueue.mNumBuffers =
thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
// inline allocation of circular Buffer Queue mArray, up to a typical max
if (BUFFER_HEADER_TYPICAL >= thiz->mBufferQueue.mNumBuffers) {
thiz->mBufferQueue.mArray = thiz->mBufferQueue.mTypical;
} else {
// Avoid possible integer overflow during multiplication; this arbitrary
// maximum is big enough to not interfere with real applications, but
// small enough to not overflow.
if (thiz->mBufferQueue.mNumBuffers >= 256) {
result = SL_RESULT_MEMORY_FAILURE;
break;
}
thiz->mBufferQueue.mArray = (BufferHeader *) malloc((thiz->mBufferQueue.
mNumBuffers + 1) * sizeof(BufferHeader));
if (NULL == thiz->mBufferQueue.mArray) {
result = SL_RESULT_MEMORY_FAILURE;
break;
}
}
thiz->mBufferQueue.mFront = thiz->mBufferQueue.mArray;
thiz->mBufferQueue.mRear = thiz->mBufferQueue.mArray;
}
#endif
// platform-specific initialization
#ifdef ANDROID
android_audioRecorder_create(thiz);
#endif
} while (0);
if (SL_RESULT_SUCCESS != result) {
IObject_Destroy(&thiz->mObject.mItf);
} else {
IObject_Publish(&thiz->mObject);
// return the new audio recorder object
*pRecorder = &thiz->mObject.mItf;
}
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateMidiPlayer(SLEngineItf self, SLObjectItf *pPlayer,
SLDataSource *pMIDISrc, SLDataSource *pBankSrc, SLDataSink *pAudioOutput,
SLDataSink *pVibra, SLDataSink *pLEDArray, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & (USE_PROFILES_GAME | USE_PROFILES_PHONE)
if ((NULL == pPlayer) || (NULL == pMIDISrc) || (NULL == pAudioOutput)) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pPlayer = NULL;
unsigned exposedMask;
const ClassTable *pCMidiPlayer_class = objectIDtoClass(SL_OBJECTID_MIDIPLAYER);
if (NULL == pCMidiPlayer_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCMidiPlayer_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
CMidiPlayer *thiz = (CMidiPlayer *) construct(pCMidiPlayer_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
#if 0
"pMIDISrc", pMIDISrc, URI | MIDIBUFFERQUEUE, NONE
"pBankSrc", pBanksrc, NULL | URI | ADDRESS, NULL
"pAudioOutput", pAudioOutput, OUTPUTMIX, NULL
"pVibra", pVibra, NULL | IODEVICE, NULL
"pLEDArray", pLEDArray, NULL | IODEVICE, NULL
#endif
// a fake value - why not use value from IPlay_init? what does CT check for?
thiz->mPlay.mDuration = 0;
IObject_Publish(&thiz->mObject);
// return the new MIDI player object
*pPlayer = &thiz->mObject.mItf;
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateListener(SLEngineItf self, SLObjectItf *pListener,
SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_GAME
if (NULL == pListener) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pListener = NULL;
unsigned exposedMask;
const ClassTable *pCListener_class = objectIDtoClass(SL_OBJECTID_LISTENER);
if (NULL == pCListener_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCListener_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
CListener *thiz = (CListener *) construct(pCListener_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
IObject_Publish(&thiz->mObject);
// return the new 3D listener object
*pListener = &thiz->mObject.mItf;
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_Create3DGroup(SLEngineItf self, SLObjectItf *pGroup, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_GAME
if (NULL == pGroup) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pGroup = NULL;
unsigned exposedMask;
const ClassTable *pC3DGroup_class = objectIDtoClass(SL_OBJECTID_3DGROUP);
if (NULL == pC3DGroup_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pC3DGroup_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
C3DGroup *thiz = (C3DGroup *) construct(pC3DGroup_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
thiz->mMemberMask = 0;
IObject_Publish(&thiz->mObject);
// return the new 3D group object
*pGroup = &thiz->mObject.mItf;
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateOutputMix(SLEngineItf self, SLObjectItf *pMix, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
if (NULL == pMix) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pMix = NULL;
unsigned exposedMask;
const ClassTable *pCOutputMix_class = objectIDtoClass(SL_OBJECTID_OUTPUTMIX);
assert(NULL != pCOutputMix_class);
result = checkInterfaces(pCOutputMix_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
if (SL_RESULT_SUCCESS == result) {
COutputMix *thiz = (COutputMix *) construct(pCOutputMix_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
#ifdef ANDROID
android_outputMix_create(thiz);
#endif
#ifdef USE_SDL
IEngine *thisEngine = &thiz->mObject.mEngine->mEngine;
interface_lock_exclusive(thisEngine);
bool unpause = false;
if (NULL == thisEngine->mOutputMix) {
thisEngine->mOutputMix = thiz;
unpause = true;
}
interface_unlock_exclusive(thisEngine);
#endif
IObject_Publish(&thiz->mObject);
#ifdef USE_SDL
if (unpause) {
// Enable SDL_callback to be called periodically by SDL's internal thread
SDL_PauseAudio(0);
}
#endif
// return the new output mix object
*pMix = &thiz->mObject.mItf;
}
}
}
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateMetadataExtractor(SLEngineItf self, SLObjectItf *pMetadataExtractor,
SLDataSource *pDataSource, SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds,
const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & (USE_PROFILES_GAME | USE_PROFILES_MUSIC)
if (NULL == pMetadataExtractor) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pMetadataExtractor = NULL;
unsigned exposedMask;
const ClassTable *pCMetadataExtractor_class =
objectIDtoClass(SL_OBJECTID_METADATAEXTRACTOR);
if (NULL == pCMetadataExtractor_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCMetadataExtractor_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
CMetadataExtractor *thiz = (CMetadataExtractor *)
construct(pCMetadataExtractor_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
#if 0
"pDataSource", pDataSource, NONE, NONE
#endif
IObject_Publish(&thiz->mObject);
// return the new metadata extractor object
*pMetadataExtractor = &thiz->mObject.mItf;
result = SL_RESULT_SUCCESS;
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IEngine_CreateExtensionObject(SLEngineItf self, SLObjectItf *pObject,
void *pParameters, SLuint32 objectID, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
if (NULL == pObject) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pObject = NULL;
result = SL_RESULT_FEATURE_UNSUPPORTED;
}
SL_LEAVE_INTERFACE
}
static SLresult IEngine_QueryNumSupportedInterfaces(SLEngineItf self,
SLuint32 objectID, SLuint32 *pNumSupportedInterfaces)
{
SL_ENTER_INTERFACE
if (NULL == pNumSupportedInterfaces) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
const ClassTable *clazz = objectIDtoClass(objectID);
if (NULL == clazz) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
SLuint32 count = 0;
SLuint32 i;
for (i = 0; i < clazz->mInterfaceCount; ++i) {
switch (clazz->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_INTERFACE;
}
static SLresult IEngine_QuerySupportedInterfaces(SLEngineItf self,
SLuint32 objectID, SLuint32 index, SLInterfaceID *pInterfaceId)
{
SL_ENTER_INTERFACE
if (NULL == pInterfaceId) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pInterfaceId = NULL;
const ClassTable *clazz = objectIDtoClass(objectID);
if (NULL == clazz) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = SL_RESULT_PARAMETER_INVALID; // will be reset later
SLuint32 i;
for (i = 0; i < clazz->mInterfaceCount; ++i) {
switch (clazz->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) {
*pInterfaceId = &SL_IID_array[clazz->mInterfaces[i].mMPH];
result = SL_RESULT_SUCCESS;
break;
}
--index;
}
}
}
SL_LEAVE_INTERFACE
};
static const char * const extensionNames[] = {
#ifdef ANDROID
#define _(n) #n
#define __(n) _(n)
"ANDROID_SDK_LEVEL_" __(PLATFORM_SDK_VERSION),
#undef _
#undef __
#else
"WILHELM_DESKTOP",
#endif
};
static SLresult IEngine_QueryNumSupportedExtensions(SLEngineItf self, SLuint32 *pNumExtensions)
{
SL_ENTER_INTERFACE
if (NULL == pNumExtensions) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pNumExtensions = sizeof(extensionNames) / sizeof(extensionNames[0]);
result = SL_RESULT_SUCCESS;
}
SL_LEAVE_INTERFACE
}
static SLresult IEngine_QuerySupportedExtension(SLEngineItf self,
SLuint32 index, SLchar *pExtensionName, SLint16 *pNameLength)
{
SL_ENTER_INTERFACE
if (NULL == pNameLength) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
size_t actualNameLength;
unsigned numExtensions = sizeof(extensionNames) / sizeof(extensionNames[0]);
if (index >= numExtensions) {
actualNameLength = 0;
result = SL_RESULT_PARAMETER_INVALID;
} else {
const char *extensionName = extensionNames[index];
actualNameLength = strlen(extensionName) + 1;
if (NULL == pExtensionName) {
// application is querying the name length in order to allocate a buffer
result = SL_RESULT_SUCCESS;
} else {
SLint16 availableNameLength = *pNameLength;
if (0 >= availableNameLength) {
// there is not even room for the terminating NUL
result = SL_RESULT_BUFFER_INSUFFICIENT;
} else if (actualNameLength > (size_t) availableNameLength) {
// "no invalid strings are written. That is, the null-terminator always exists"
memcpy(pExtensionName, extensionName, (size_t) availableNameLength - 1);
pExtensionName[(size_t) availableNameLength - 1] = '\0';
result = SL_RESULT_BUFFER_INSUFFICIENT;
} else {
memcpy(pExtensionName, extensionName, actualNameLength);
result = SL_RESULT_SUCCESS;
}
}
}
*pNameLength = actualNameLength;
}
SL_LEAVE_INTERFACE
}
static SLresult IEngine_IsExtensionSupported(SLEngineItf self,
const SLchar *pExtensionName, SLboolean *pSupported)
{
SL_ENTER_INTERFACE
if (NULL == pSupported) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
SLboolean isSupported = SL_BOOLEAN_FALSE;
if (NULL == pExtensionName) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
unsigned numExtensions = sizeof(extensionNames) / sizeof(extensionNames[0]);
unsigned i;
for (i = 0; i < numExtensions; ++i) {
if (!strcmp((const char *) pExtensionName, extensionNames[i])) {
isSupported = SL_BOOLEAN_TRUE;
break;
}
}
result = SL_RESULT_SUCCESS;
}
*pSupported = isSupported;
}
SL_LEAVE_INTERFACE
}
static const struct SLEngineItf_ IEngine_Itf = {
IEngine_CreateLEDDevice,
IEngine_CreateVibraDevice,
IEngine_CreateAudioPlayer,
IEngine_CreateAudioRecorder,
IEngine_CreateMidiPlayer,
IEngine_CreateListener,
IEngine_Create3DGroup,
IEngine_CreateOutputMix,
IEngine_CreateMetadataExtractor,
IEngine_CreateExtensionObject,
IEngine_QueryNumSupportedInterfaces,
IEngine_QuerySupportedInterfaces,
IEngine_QueryNumSupportedExtensions,
IEngine_QuerySupportedExtension,
IEngine_IsExtensionSupported
};
void IEngine_init(void *self)
{
IEngine *thiz = (IEngine *) self;
thiz->mItf = &IEngine_Itf;
// mLossOfControlGlobal is initialized in slCreateEngine
#ifdef USE_SDL
thiz->mOutputMix = NULL;
#endif
thiz->mInstanceCount = 1; // ourself
thiz->mInstanceMask = 0;
thiz->mChangedMask = 0;
unsigned i;
for (i = 0; i < MAX_INSTANCE; ++i) {
thiz->mInstances[i] = NULL;
}
thiz->mShutdown = SL_BOOLEAN_FALSE;
thiz->mShutdownAck = SL_BOOLEAN_FALSE;
#if _BYTE_ORDER == _BIG_ENDIAN
thiz->mNativeEndianness = SL_BYTEORDER_BIGENDIAN;
#else
thiz->mNativeEndianness = SL_BYTEORDER_LITTLEENDIAN;
#endif
}
void IEngine_deinit(void *self)
{
}
// OpenMAX AL Engine
static XAresult IEngine_CreateCameraDevice(XAEngineItf self, XAObjectItf *pDevice,
XAuint32 deviceID, XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
XA_ENTER_INTERFACE
//IXAEngine *thiz = (IXAEngine *) self;
result = SL_RESULT_FEATURE_UNSUPPORTED;
XA_LEAVE_INTERFACE
}
static XAresult IEngine_CreateRadioDevice(XAEngineItf self, XAObjectItf *pDevice,
XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
XA_ENTER_INTERFACE
//IXAEngine *thiz = (IXAEngine *) self;
result = SL_RESULT_FEATURE_UNSUPPORTED;
XA_LEAVE_INTERFACE
}
static XAresult IXAEngine_CreateLEDDevice(XAEngineItf self, XAObjectItf *pDevice, XAuint32 deviceID,
XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
// forward to OpenSL ES
return IEngine_CreateLEDDevice(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
(SLObjectItf *) pDevice, deviceID, numInterfaces, (const SLInterfaceID *) pInterfaceIds,
(const SLboolean *) pInterfaceRequired);
}
static XAresult IXAEngine_CreateVibraDevice(XAEngineItf self, XAObjectItf *pDevice,
XAuint32 deviceID, XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
// forward to OpenSL ES
return IEngine_CreateVibraDevice(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
(SLObjectItf *) pDevice, deviceID, numInterfaces, (const SLInterfaceID *) pInterfaceIds,
(const SLboolean *) pInterfaceRequired);
}
static XAresult IEngine_CreateMediaPlayer(XAEngineItf self, XAObjectItf *pPlayer,
XADataSource *pDataSrc, XADataSource *pBankSrc, XADataSink *pAudioSnk,
XADataSink *pImageVideoSnk, XADataSink *pVibra, XADataSink *pLEDArray,
XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
XA_ENTER_INTERFACE
if (NULL == pPlayer) {
result = XA_RESULT_PARAMETER_INVALID;
} else {
*pPlayer = NULL;
unsigned exposedMask;
const ClassTable *pCMediaPlayer_class = objectIDtoClass(XA_OBJECTID_MEDIAPLAYER);
assert(NULL != pCMediaPlayer_class);
result = checkInterfaces(pCMediaPlayer_class, numInterfaces,
(const SLInterfaceID *) pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
if (XA_RESULT_SUCCESS == result) {
// Construct our new MediaPlayer instance
CMediaPlayer *thiz = (CMediaPlayer *) construct(pCMediaPlayer_class, exposedMask,
&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf);
if (NULL == thiz) {
result = XA_RESULT_MEMORY_FAILURE;
} else {
do {
// Initialize private fields not associated with an interface
// Default data source in case of failure in checkDataSource
thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSource.mFormat.mFormatType = XA_DATAFORMAT_NULL;
// Default andio and image sink in case of failure in checkDataSink
thiz->mAudioSink.mLocator.mLocatorType = XA_DATALOCATOR_NULL;
thiz->mAudioSink.mFormat.mFormatType = XA_DATAFORMAT_NULL;
thiz->mImageVideoSink.mLocator.mLocatorType = XA_DATALOCATOR_NULL;
thiz->mImageVideoSink.mFormat.mFormatType = XA_DATAFORMAT_NULL;
// More default values, in case destructor needs to be called early
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
#ifdef ANDROID
// placement new (explicit constructor)
// FIXME unnecessary once those fields are encapsulated in one class, rather
// than a structure
(void) new (&thiz->mAVPlayer) android::sp<android::GenericPlayer>();
(void) new (&thiz->mCallbackProtector)
android::sp<android::CallbackProtector>();
// Android-specific POD fields are initialized in android_Player_create,
// and assume calloc or memset 0 during allocation
#endif
// Check the source and sink parameters against generic constraints
result = checkDataSource("pDataSrc", (const SLDataSource *) pDataSrc,
&thiz->mDataSource, DATALOCATOR_MASK_URI
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDFD
| DATALOCATOR_MASK_ANDROIDBUFFERQUEUE
#endif
, DATAFORMAT_MASK_MIME);
if (XA_RESULT_SUCCESS != result) {
break;
}
result = checkDataSource("pBankSrc", (const SLDataSource *) pBankSrc,
&thiz->mBankSource, DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_URI |
DATALOCATOR_MASK_ADDRESS, DATAFORMAT_MASK_NULL);
if (XA_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pAudioSnk", (const SLDataSink *) pAudioSnk,
&thiz->mAudioSink, DATALOCATOR_MASK_OUTPUTMIX, DATAFORMAT_MASK_NULL);
if (XA_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pImageVideoSnk", (const SLDataSink *) pImageVideoSnk,
&thiz->mImageVideoSink,
DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_NATIVEDISPLAY,
DATAFORMAT_MASK_NULL);
if (XA_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pVibra", (const SLDataSink *) pVibra, &thiz->mVibraSink,
DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_IODEVICE,
DATAFORMAT_MASK_NULL);
if (XA_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pLEDArray", (const SLDataSink *) pLEDArray,
&thiz->mLEDArraySink, DATALOCATOR_MASK_NULL | DATALOCATOR_MASK_IODEVICE,
DATAFORMAT_MASK_NULL);
if (XA_RESULT_SUCCESS != result) {
break;
}
// Unsafe to ever refer to application pointers again
pDataSrc = NULL;
pBankSrc = NULL;
pAudioSnk = NULL;
pImageVideoSnk = NULL;
pVibra = NULL;
pLEDArray = NULL;
// Check that the requested interfaces are compatible with the data source
// FIXME implement
// check the source and sink parameters against platform support
#ifdef ANDROID
result = android_Player_checkSourceSink(thiz);
if (XA_RESULT_SUCCESS != result) {
break;
}
#endif
#ifdef ANDROID
// AndroidBufferQueue-specific initialization
if (XA_DATALOCATOR_ANDROIDBUFFERQUEUE ==
thiz->mDataSource.mLocator.mLocatorType) {
XAuint16 nbBuffers = (XAuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;
// Avoid possible integer overflow during multiplication; this arbitrary
// maximum is big enough to not interfere with real applications, but
// small enough to not overflow.
if (nbBuffers >= 256) {
result = SL_RESULT_MEMORY_FAILURE;
break;
}
// initialize ABQ buffer type
// assert below has been checked in android_audioPlayer_checkSourceSink
assert(XA_DATAFORMAT_MIME == thiz->mDataSource.mFormat.mFormatType);
if (XA_CONTAINERTYPE_MPEG_TS ==
thiz->mDataSource.mFormat.mMIME.containerType) {
thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeMpeg2Ts;
// Set the container type for the StreamInformation interface
XAMediaContainerInformation *containerInfo =
(XAMediaContainerInformation*)
// always storing container info at index 0, as per spec
&thiz->mStreamInfo.mStreamInfoTable.itemAt(0).containerInfo;
containerInfo->containerType = XA_CONTAINERTYPE_MPEG_TS;
// there are no streams at this stage
containerInfo->numStreams = 0;
} else {
thiz->mAndroidBufferQueue.mBufferType = kAndroidBufferTypeInvalid;
SL_LOGE("Invalid buffer type in Android Buffer Queue");
result = SL_RESULT_CONTENT_UNSUPPORTED;
}
// initialize ABQ memory
thiz->mAndroidBufferQueue.mBufferArray = (AdvancedBufferHeader *)
malloc( (nbBuffers + 1) * sizeof(AdvancedBufferHeader));
if (NULL == thiz->mAndroidBufferQueue.mBufferArray) {
result = SL_RESULT_MEMORY_FAILURE;
break;
} else {
thiz->mAndroidBufferQueue.mFront =
thiz->mAndroidBufferQueue.mBufferArray;
thiz->mAndroidBufferQueue.mRear =
thiz->mAndroidBufferQueue.mBufferArray;
}
thiz->mAndroidBufferQueue.mNumBuffers = nbBuffers;
}
#endif
// used to store the data source of our audio player
thiz->mDynamicSource.mDataSource = &thiz->mDataSource.u.mSource;
// platform-specific initialization
#ifdef ANDROID
android_Player_create(thiz);
#endif
} while (0);
if (XA_RESULT_SUCCESS != result) {
IObject_Destroy(&thiz->mObject.mItf);
} else {
IObject_Publish(&thiz->mObject);
// return the new media player object
*pPlayer = (XAObjectItf) &thiz->mObject.mItf;
}
}
}
}
XA_LEAVE_INTERFACE
}
static XAresult IEngine_CreateMediaRecorder(XAEngineItf self, XAObjectItf *pRecorder,
XADataSource *pAudioSrc, XADataSource *pImageVideoSrc,
XADataSink *pDataSnk, XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
XA_ENTER_INTERFACE
//IXAEngine *thiz = (IXAEngine *) self;
result = SL_RESULT_FEATURE_UNSUPPORTED;
#if 0
"pAudioSrc", pAudioSrc,
"pImageVideoSrc", pImageVideoSrc,
"pDataSink", pDataSnk,
#endif
XA_LEAVE_INTERFACE
}
static XAresult IXAEngine_CreateOutputMix(XAEngineItf self, XAObjectItf *pMix,
XAuint32 numInterfaces, const XAInterfaceID *pInterfaceIds,
const XAboolean *pInterfaceRequired)
{
// forward to OpenSL ES
return IEngine_CreateOutputMix(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
(SLObjectItf *) pMix, numInterfaces, (const SLInterfaceID *) pInterfaceIds,
(const SLboolean *) pInterfaceRequired);
}
static XAresult IXAEngine_CreateMetadataExtractor(XAEngineItf self, XAObjectItf *pMetadataExtractor,
XADataSource *pDataSource, XAuint32 numInterfaces,
const XAInterfaceID *pInterfaceIds, const XAboolean *pInterfaceRequired)
{
// forward to OpenSL ES
return IEngine_CreateMetadataExtractor(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
(SLObjectItf *) pMetadataExtractor, (SLDataSource *) pDataSource, numInterfaces,
(const SLInterfaceID *) pInterfaceIds, (const SLboolean *) pInterfaceRequired);
}
static XAresult IXAEngine_CreateExtensionObject(XAEngineItf self, XAObjectItf *pObject,
void *pParameters, XAuint32 objectID, XAuint32 numInterfaces,
const XAInterfaceID *pInterfaceIds, const XAboolean *pInterfaceRequired)
{
// forward to OpenSL ES
return IEngine_CreateExtensionObject(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
(SLObjectItf *) pObject, pParameters, objectID, numInterfaces,
(const SLInterfaceID *) pInterfaceIds, (const SLboolean *) pInterfaceRequired);
}
static XAresult IEngine_GetImplementationInfo(XAEngineItf self, XAuint32 *pMajor, XAuint32 *pMinor,
XAuint32 *pStep, /* XAuint32 nImplementationTextSize, */ const XAchar *pImplementationText)
{
XA_ENTER_INTERFACE
//IXAEngine *thiz = (IXAEngine *) self;
result = SL_RESULT_FEATURE_UNSUPPORTED;
XA_LEAVE_INTERFACE
}
static XAresult IXAEngine_QuerySupportedProfiles(XAEngineItf self, XAint16 *pProfilesSupported)
{
XA_ENTER_INTERFACE
if (NULL == pProfilesSupported) {
result = XA_RESULT_PARAMETER_INVALID;
} else {
#if 1
*pProfilesSupported = 0;
// the code below was copied from OpenSL ES and needs to be adapted for OpenMAX AL.
#else
// The generic implementation doesn't implement any of the profiles, they shouldn't be
// declared as supported. Also exclude the fake profiles BASE and OPTIONAL.
*pProfilesSupported = USE_PROFILES &
(USE_PROFILES_GAME | USE_PROFILES_MUSIC | USE_PROFILES_PHONE);
#endif
result = XA_RESULT_SUCCESS;
}
XA_LEAVE_INTERFACE
}
static XAresult IXAEngine_QueryNumSupportedInterfaces(XAEngineItf self, XAuint32 objectID,
XAuint32 *pNumSupportedInterfaces)
{
// forward to OpenSL ES
return IEngine_QueryNumSupportedInterfaces(
&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf, objectID,
pNumSupportedInterfaces);
}
static XAresult IXAEngine_QuerySupportedInterfaces(XAEngineItf self, XAuint32 objectID,
XAuint32 index, XAInterfaceID *pInterfaceId)
{
// forward to OpenSL ES
return IEngine_QuerySupportedInterfaces(
&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf, objectID, index,
(SLInterfaceID *) pInterfaceId);
}
static XAresult IXAEngine_QueryNumSupportedExtensions(XAEngineItf self, XAuint32 *pNumExtensions)
{
// forward to OpenSL ES
return IEngine_QueryNumSupportedExtensions(
&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf, pNumExtensions);
}
static XAresult IXAEngine_QuerySupportedExtension(XAEngineItf self, XAuint32 index,
XAchar *pExtensionName, XAint16 *pNameLength)
{
// forward to OpenSL ES
return IEngine_QuerySupportedExtension(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
index, pExtensionName, (SLint16 *) pNameLength);
}
static XAresult IXAEngine_IsExtensionSupported(XAEngineItf self, const XAchar *pExtensionName,
XAboolean *pSupported)
{
// forward to OpenSL ES
return IEngine_IsExtensionSupported(&((CEngine *) ((IXAEngine *) self)->mThis)->mEngine.mItf,
pExtensionName, pSupported);
}
static XAresult IXAEngine_QueryLEDCapabilities(XAEngineItf self, XAuint32 *pIndex,
XAuint32 *pLEDDeviceID, XALEDDescriptor *pDescriptor)
{
// forward to OpenSL ES EngineCapabilities
return (XAresult) IEngineCapabilities_QueryLEDCapabilities(
&((CEngine *) ((IXAEngine *) self)->mThis)->mEngineCapabilities.mItf, pIndex,
pLEDDeviceID, (SLLEDDescriptor *) pDescriptor);
}
static XAresult IXAEngine_QueryVibraCapabilities(XAEngineItf self, XAuint32 *pIndex,
XAuint32 *pVibraDeviceID, XAVibraDescriptor *pDescriptor)
{
// forward to OpenSL ES EngineCapabilities
return (XAresult) IEngineCapabilities_QueryVibraCapabilities(
&((CEngine *) ((IXAEngine *) self)->mThis)->mEngineCapabilities.mItf, pIndex,
pVibraDeviceID, (SLVibraDescriptor *) pDescriptor);
}
// OpenMAX AL engine v-table
static const struct XAEngineItf_ IXAEngine_Itf = {
IEngine_CreateCameraDevice,
IEngine_CreateRadioDevice,
IXAEngine_CreateLEDDevice,
IXAEngine_CreateVibraDevice,
IEngine_CreateMediaPlayer,
IEngine_CreateMediaRecorder,
IXAEngine_CreateOutputMix,
IXAEngine_CreateMetadataExtractor,
IXAEngine_CreateExtensionObject,
IEngine_GetImplementationInfo,
IXAEngine_QuerySupportedProfiles,
IXAEngine_QueryNumSupportedInterfaces,
IXAEngine_QuerySupportedInterfaces,
IXAEngine_QueryNumSupportedExtensions,
IXAEngine_QuerySupportedExtension,
IXAEngine_IsExtensionSupported,
IXAEngine_QueryLEDCapabilities,
IXAEngine_QueryVibraCapabilities
};
void IXAEngine_init(void *self)
{
IXAEngine *thiz = (IXAEngine *) self;
thiz->mItf = &IXAEngine_Itf;
}
void IXAEngine_deinit(void *self)
{
}