| /* |
| * 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 "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; |
| #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>(); |
| #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); |
| |
| 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 // 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); |
| 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); |
| // 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_checkSourceSinkSupport. 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 |
| thiz->mAudioRecord = NULL; |
| 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 |
| ); |
| 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_checkSourceSinkSupport(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; |
| } |
| |
| 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; |
| |
| // (assume calloc or memset 0 during allocation) |
| // placement new |
| #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>(); |
| #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) |
| { |
| } |