| /* | |
| * 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->mTrackPlayer) android::sp<android::TrackPlayerBase>(); | |
| (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->mCallbackHandle) android::sp<android::AudioRecordCallback>(); | |
| (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) | |
| { | |
| } |