blob: 6d066843fbedb1d1bda4f182dc3646792095bae2 [file] [log] [blame]
/*
* 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);
}