| /* |
| * 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. |
| */ |
| |
| #include "sles_allinclusive.h" |
| |
| |
| /** \brief Exclusively lock an object */ |
| |
| #ifdef USE_DEBUG |
| void object_lock_exclusive_(IObject *thiz, const char *file, int line) |
| { |
| int ok; |
| ok = pthread_mutex_trylock(&thiz->mMutex); |
| if (0 != ok) { |
| // pthread_mutex_timedlock_np is not available, but wait up to 100 ms |
| static const useconds_t backoffs[] = {1, 10000, 20000, 30000, 40000}; |
| unsigned i = 0; |
| for (;;) { |
| usleep(backoffs[i]); |
| ok = pthread_mutex_trylock(&thiz->mMutex); |
| if (0 == ok) |
| break; |
| if (++i >= (sizeof(backoffs) / sizeof(backoffs[0]))) { |
| SL_LOGW("%s:%d: object %p was locked by %p at %s:%d\n", |
| file, line, thiz, *(void **)&thiz->mOwner, thiz->mFile, thiz->mLine); |
| // attempt one more time; maybe this time we will be successful |
| ok = pthread_mutex_lock(&thiz->mMutex); |
| assert(0 == ok); |
| break; |
| } |
| } |
| } |
| pthread_t zero; |
| memset(&zero, 0, sizeof(pthread_t)); |
| if (0 != memcmp(&zero, &thiz->mOwner, sizeof(pthread_t))) { |
| if (pthread_equal(pthread_self(), thiz->mOwner)) { |
| SL_LOGE("%s:%d: object %p was recursively locked by %p at %s:%d\n", |
| file, line, thiz, *(void **)&thiz->mOwner, thiz->mFile, thiz->mLine); |
| } else { |
| SL_LOGE("%s:%d: object %p was left unlocked in unexpected state by %p at %s:%d\n", |
| file, line, thiz, *(void **)&thiz->mOwner, thiz->mFile, thiz->mLine); |
| } |
| assert(false); |
| } |
| thiz->mOwner = pthread_self(); |
| thiz->mFile = file; |
| thiz->mLine = line; |
| } |
| #else |
| void object_lock_exclusive(IObject *thiz) |
| { |
| int ok; |
| ok = pthread_mutex_lock(&thiz->mMutex); |
| assert(0 == ok); |
| } |
| #endif |
| |
| |
| /** \brief Exclusively unlock an object and do not report any updates */ |
| |
| #ifdef USE_DEBUG |
| void object_unlock_exclusive_(IObject *thiz, const char *file, int line) |
| { |
| assert(pthread_equal(pthread_self(), thiz->mOwner)); |
| assert(NULL != thiz->mFile); |
| assert(0 != thiz->mLine); |
| memset(&thiz->mOwner, 0, sizeof(pthread_t)); |
| thiz->mFile = file; |
| thiz->mLine = line; |
| int ok; |
| ok = pthread_mutex_unlock(&thiz->mMutex); |
| assert(0 == ok); |
| } |
| #else |
| void object_unlock_exclusive(IObject *thiz) |
| { |
| int ok; |
| ok = pthread_mutex_unlock(&thiz->mMutex); |
| assert(0 == ok); |
| } |
| #endif |
| |
| |
| /** \brief Exclusively unlock an object and report updates to the specified bit-mask of |
| * attributes |
| */ |
| |
| #ifdef USE_DEBUG |
| void object_unlock_exclusive_attributes_(IObject *thiz, unsigned attributes, |
| const char *file, int line) |
| #else |
| void object_unlock_exclusive_attributes(IObject *thiz, unsigned attributes) |
| #endif |
| { |
| |
| #ifdef USE_DEBUG |
| assert(pthread_equal(pthread_self(), thiz->mOwner)); |
| assert(NULL != thiz->mFile); |
| assert(0 != thiz->mLine); |
| #endif |
| |
| int ok; |
| SLuint32 objectID = IObjectToObjectID(thiz); |
| CAudioPlayer *ap; |
| |
| // FIXME The endless if statements are getting ugly, should use bit search |
| |
| // Android likes to see certain updates synchronously |
| |
| if (attributes & ATTR_GAIN) { |
| switch (objectID) { |
| case SL_OBJECTID_AUDIOPLAYER: |
| attributes &= ~ATTR_GAIN; // no need to process asynchronously also |
| ap = (CAudioPlayer *) thiz; |
| #ifdef ANDROID |
| android_audioPlayer_volumeUpdate(ap); |
| #else |
| audioPlayerGainUpdate(ap); |
| #endif |
| break; |
| case SL_OBJECTID_OUTPUTMIX: |
| // FIXME update gains on all players attached to this outputmix |
| SL_LOGD("[ FIXME: gain update on an SL_OBJECTID_OUTPUTMIX to be implemented ]"); |
| break; |
| case SL_OBJECTID_MIDIPLAYER: |
| // MIDI |
| SL_LOGD("[ FIXME: gain update on an SL_OBJECTID_MIDIPLAYER to be implemented ]"); |
| break; |
| case XA_OBJECTID_MEDIAPLAYER: { |
| #ifdef ANDROID |
| attributes &= ~ATTR_GAIN; // no need to process asynchronously also |
| CMediaPlayer *mp = (CMediaPlayer *) thiz; |
| android::GenericPlayer* avp = mp->mAVPlayer.get(); |
| if (avp != NULL) { |
| android_Player_volumeUpdate(avp, &mp->mVolume); |
| } |
| #endif |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| if (attributes & ATTR_POSITION) { |
| switch (objectID) { |
| case SL_OBJECTID_AUDIOPLAYER: |
| #ifdef ANDROID |
| ap = (CAudioPlayer *) thiz; |
| attributes &= ~ATTR_POSITION; // no need to process asynchronously also |
| android_audioPlayer_seek(ap, ap->mSeek.mPos); |
| #else |
| //audioPlayerTransportUpdate(ap); |
| #endif |
| break; |
| case SL_OBJECTID_MIDIPLAYER: |
| // MIDI |
| SL_LOGD("[ FIXME: position update on an SL_OBJECTID_MIDIPLAYER to be implemented ]"); |
| break; |
| case XA_OBJECTID_MEDIAPLAYER: { |
| CMediaPlayer *mp = (CMediaPlayer *) thiz; |
| attributes &= ~ATTR_POSITION; // no need to process asynchronously also |
| android_Player_seek(mp, mp->mSeek.mPos); |
| break; |
| } |
| default: { } |
| } |
| } |
| |
| if (attributes & ATTR_TRANSPORT) { |
| switch (objectID) { |
| case SL_OBJECTID_AUDIOPLAYER: |
| #ifdef ANDROID |
| attributes &= ~ATTR_TRANSPORT; // no need to process asynchronously also |
| ap = (CAudioPlayer *) thiz; |
| // FIXME should only call when state changes |
| android_audioPlayer_setPlayState(ap, false /*lockAP*/); |
| // FIXME ditto, but for either eventflags or marker position |
| android_audioPlayer_useEventMask(ap); |
| #else |
| //audioPlayerTransportUpdate(ap); |
| #endif |
| break; |
| case SL_OBJECTID_AUDIORECORDER: |
| #ifdef ANDROID |
| { |
| attributes &= ~ATTR_TRANSPORT; // no need to process asynchronously also |
| CAudioRecorder* ar = (CAudioRecorder *) thiz; |
| android_audioRecorder_useEventMask(ar); |
| } |
| #endif |
| break; |
| case XA_OBJECTID_MEDIAPLAYER: |
| #ifdef ANDROID |
| { |
| attributes &= ~ATTR_TRANSPORT; // no need to process asynchronously also |
| CMediaPlayer *mp = (CMediaPlayer *) thiz; |
| android::GenericPlayer* avp = mp->mAVPlayer.get(); |
| if (avp != NULL) { |
| android_Player_setPlayState(avp, mp->mPlay.mState, &(mp->mAndroidObjState)); |
| } |
| } |
| #endif |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (attributes & ATTR_BQ_ENQUEUE) { |
| // ( buffer queue count is non-empty and play state == PLAYING ) became true |
| if (SL_OBJECTID_AUDIOPLAYER == objectID) { |
| attributes &= ~ATTR_BQ_ENQUEUE; |
| ap = (CAudioPlayer *) thiz; |
| if (SL_PLAYSTATE_PLAYING == ap->mPlay.mState) { |
| #ifdef ANDROID |
| android_audioPlayer_bufferQueue_onRefilled_l(ap); |
| #endif |
| } |
| } |
| #ifndef ANDROID |
| } |
| #else |
| } else if (attributes & ATTR_ABQ_ENQUEUE) { |
| // (Android buffer queue count is non-empty and play state == PLAYING ) became true |
| if (SL_OBJECTID_AUDIOPLAYER == objectID) { |
| attributes &= ~ATTR_ABQ_ENQUEUE; |
| ap = (CAudioPlayer *) thiz; |
| if (SL_PLAYSTATE_PLAYING == ap->mPlay.mState) { |
| android_audioPlayer_androidBufferQueue_onRefilled_l(ap); |
| } |
| } else if (XA_OBJECTID_MEDIAPLAYER == objectID) { |
| attributes &= ~ATTR_ABQ_ENQUEUE; |
| CMediaPlayer* mp = (CMediaPlayer *)thiz; |
| if (SL_PLAYSTATE_PLAYING == mp->mPlay.mState) { |
| android_Player_androidBufferQueue_onRefilled_l(mp); |
| } |
| } |
| } |
| #endif |
| |
| if (attributes) { |
| unsigned oldAttributesMask = thiz->mAttributesMask; |
| thiz->mAttributesMask = oldAttributesMask | attributes; |
| if (oldAttributesMask) |
| attributes = ATTR_NONE; |
| } |
| #ifdef USE_DEBUG |
| memset(&thiz->mOwner, 0, sizeof(pthread_t)); |
| thiz->mFile = file; |
| thiz->mLine = line; |
| #endif |
| ok = pthread_mutex_unlock(&thiz->mMutex); |
| assert(0 == ok); |
| // first update to this interface since previous sync |
| if (attributes) { |
| unsigned id = thiz->mInstanceID; |
| if (0 != id) { |
| --id; |
| assert(MAX_INSTANCE > id); |
| IEngine *thisEngine = &thiz->mEngine->mEngine; |
| interface_lock_exclusive(thisEngine); |
| thisEngine->mChangedMask |= 1 << id; |
| interface_unlock_exclusive(thisEngine); |
| } |
| } |
| } |
| |
| |
| /** \brief Wait on the condition variable associated with the object; see pthread_cond_wait */ |
| |
| #ifdef USE_DEBUG |
| void object_cond_wait_(IObject *thiz, const char *file, int line) |
| { |
| // note that this will unlock the mutex, so we have to clear the owner |
| assert(pthread_equal(pthread_self(), thiz->mOwner)); |
| assert(NULL != thiz->mFile); |
| assert(0 != thiz->mLine); |
| memset(&thiz->mOwner, 0, sizeof(pthread_t)); |
| thiz->mFile = file; |
| thiz->mLine = line; |
| // alas we don't know the new owner's identity |
| int ok; |
| ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex); |
| assert(0 == ok); |
| // restore my ownership |
| thiz->mOwner = pthread_self(); |
| thiz->mFile = file; |
| thiz->mLine = line; |
| } |
| #else |
| void object_cond_wait(IObject *thiz) |
| { |
| int ok; |
| ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex); |
| assert(0 == ok); |
| } |
| #endif |
| |
| |
| /** \brief Signal the condition variable associated with the object; see pthread_cond_signal */ |
| |
| void object_cond_signal(IObject *thiz) |
| { |
| int ok; |
| ok = pthread_cond_signal(&thiz->mCond); |
| assert(0 == ok); |
| } |
| |
| |
| /** \brief Broadcast the condition variable associated with the object; |
| * see pthread_cond_broadcast |
| */ |
| |
| void object_cond_broadcast(IObject *thiz) |
| { |
| int ok; |
| ok = pthread_cond_broadcast(&thiz->mCond); |
| assert(0 == ok); |
| } |