/**
 * Called with a lock on AudioRecorder, and blocks until safe to destroy
 */
void android_audioRecorder_preDestroy(CAudioRecorder* ar) {
    object_unlock_exclusive(&ar->mObject);
    if (ar->mCallbackProtector != 0) {
        ar->mCallbackProtector->requestCbExitAndWait();
    }
    object_lock_exclusive(&ar->mObject);
}
Example #2
0
static SLresult I3DGrouping_Set3DGroup(SL3DGroupingItf self, SLObjectItf group)
{
    SL_ENTER_INTERFACE

    // validate input parameters
    C3DGroup *newGroup = (C3DGroup *) group;
    result = SL_RESULT_SUCCESS;
    if (NULL != newGroup) {
        // check that new group has the correct object ID and is realized, and acquire a strong
        // reference to it. FYI note that a deadlock will occur if application incorrectly
        // specifies group as this audio player
        result = AcquireStrongRef(&newGroup->mObject, SL_OBJECTID_3DGROUP);
        // the new group is left unlocked, but it will be locked again below
    }
    if (SL_RESULT_SUCCESS == result) {
        I3DGrouping *thiz = (I3DGrouping *) self;
        IObject *thisObject = InterfaceToIObject(thiz);
        unsigned id = thisObject->mInstanceID;
        assert(0 != id);        // player object must be published by this point
        --id;
        assert(MAX_INSTANCE > id);
        unsigned mask = 1 << id;
        interface_lock_exclusive(thiz);
        C3DGroup *oldGroup = thiz->mGroup;
        if (newGroup != oldGroup) {
            // remove this object from the old group's set of objects
            if (NULL != oldGroup) {
                IObject *oldGroupObject = &oldGroup->mObject;
                // note that we already have a strong reference to the old group
                object_lock_exclusive(oldGroupObject);
                assert(oldGroup->mMemberMask & mask);
                oldGroup->mMemberMask &= ~mask;
                ReleaseStrongRefAndUnlockExclusive(oldGroupObject);
            }
            // add this object to the new group's set of objects
            if (NULL != newGroup) {
                IObject *newGroupObject = &newGroup->mObject;
                // we already have a strong reference to the new group, but we need to re-lock it
                // so that we always lock objects in the same nesting order to prevent a deadlock
                object_lock_exclusive(newGroupObject);
                assert(!(newGroup->mMemberMask & mask));
                newGroup->mMemberMask |= mask;
                object_unlock_exclusive(newGroupObject);
            }
            thiz->mGroup = newGroup;
        }
        interface_unlock_exclusive(thiz);
    }

    SL_LEAVE_INTERFACE
}
Example #3
0
void ReleaseStrongRefAndUnlockExclusive(IObject *object)
{
#ifdef USE_DEBUG
    assert(pthread_equal(pthread_self(), object->mOwner));
#endif
    assert(0 < object->mStrongRefCount);
    if ((0 == --object->mStrongRefCount) && (SL_OBJECT_STATE_DESTROYING == object->mState)) {
        // FIXME do the destroy here - merge with IDestroy
        // but can't do this until we move Destroy to the sync thread
        // as Destroy is now a blocking operation, and to avoid a race
    } else {
        object_unlock_exclusive(object);
    }
}
//-----------------------------------------------------------------------------
void android_eq_init(audio_session_t sessionId, IEqualizer* ieq) {
    SL_LOGV("android_eq_init on session %d", sessionId);

    if (!android_fx_initEffectObj(sessionId, ieq->mEqEffect, &ieq->mEqDescriptor.type)) {
        SL_LOGE("Equalizer effect initialization failed");
        return;
    }

    // initialize number of bands, band level range, and number of presets
    uint16_t num = 0;
    if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect, EQ_PARAM_NUM_BANDS, 0, &num)) {
        ieq->mNumBands = num;
    }
    int16_t range[2] = {0, 0};
    if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect, EQ_PARAM_LEVEL_RANGE, 0, range)) {
        ieq->mBandLevelRangeMin = range[0];
        ieq->mBandLevelRangeMax = range[1];
    }

    SL_LOGV(" EQ init: num bands = %u, band range=[%d %d]mB", num, range[0], range[1]);

    // FIXME don't store presets names, they can be queried each time they're needed
    // initialize preset number and names, store in IEngine
    uint16_t numPresets = 0;
    if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect,
            EQ_PARAM_GET_NUM_OF_PRESETS, 0, &numPresets)) {
        ieq->mThis->mEngine->mEqNumPresets = numPresets;
        ieq->mNumPresets = numPresets;
    }

    object_lock_exclusive(&ieq->mThis->mEngine->mObject);
    char name[EFFECT_STRING_LEN_MAX];
    if ((0 < numPresets) && (NULL == ieq->mThis->mEngine->mEqPresetNames)) {
        ieq->mThis->mEngine->mEqPresetNames = (char **)new char *[numPresets];
        for(uint32_t i = 0 ; i < numPresets ; i++) {
            if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect,
                    EQ_PARAM_GET_PRESET_NAME, i, name)) {
                ieq->mThis->mEngine->mEqPresetNames[i] = new char[strlen(name) + 1];
                strcpy(ieq->mThis->mEngine->mEqPresetNames[i], name);
                SL_LOGV(" EQ init: presets = %u is %s", i, ieq->mThis->mEngine->mEqPresetNames[i]);
            }
        }
    }
    object_unlock_exclusive(&ieq->mThis->mEngine->mObject);

}
// Called with a lock on MediaPlayer, and blocks until safe to destroy
XAresult android_Player_preDestroy(CMediaPlayer *mp) {
    SL_LOGV("android_Player_preDestroy(%p)", mp);

    // Not yet clear why this order is important, but it reduces detected deadlocks
    object_unlock_exclusive(&mp->mObject);
    if (mp->mCallbackProtector != 0) {
        mp->mCallbackProtector->requestCbExitAndWait();
    }
    object_lock_exclusive(&mp->mObject);

    if (mp->mAVPlayer != 0) {
        mp->mAVPlayer->preDestroy();
    }
    SL_LOGV("android_Player_preDestroy(%p) after mAVPlayer->preDestroy()", mp);

    return XA_RESULT_SUCCESS;
}
Example #6
0
SLresult AcquireStrongRef(IObject *object, SLuint32 expectedObjectID)
{
    if (NULL == object) {
        return SL_RESULT_PARAMETER_INVALID;
    }
    // NTH additional validity checks on address here
    SLresult result;
    object_lock_exclusive(object);
    SLuint32 actualObjectID = IObjectToObjectID(object);
    if (expectedObjectID != actualObjectID) {
        SL_LOGE("object %p has object ID %u but expected %u", object, actualObjectID,
            expectedObjectID);
        result = SL_RESULT_PARAMETER_INVALID;
    } else if (SL_OBJECT_STATE_REALIZED != object->mState) {
        SL_LOGE("object %p with object ID %u is not realized", object, actualObjectID);
        result = SL_RESULT_PRECONDITIONS_VIOLATED;
    } else {
        ++object->mStrongRefCount;
        result = SL_RESULT_SUCCESS;
    }
    object_unlock_exclusive(object);
    return result;
}
//-----------------------------------------------------------------------------
static void player_handleMediaPlayerEventNotifications(int event, int data1, int data2, void* user)
{

    // FIXME This code is derived from similar code in sfplayer_handlePrefetchEvent.  The two
    // versions are quite similar, but still different enough that they need to be separate.
    // At some point they should be re-factored and merged if feasible.
    // As with other OpenMAX AL implementation code, this copy mostly uses SL_ symbols
    // rather than XA_ unless the difference is significant.

    if (NULL == user) {
        return;
    }

    CMediaPlayer* mp = (CMediaPlayer*) user;
    if (!android::CallbackProtector::enterCbIfOk(mp->mCallbackProtector)) {
        // it is not safe to enter the callback (the media player is about to go away)
        return;
    }
    union {
        char c[sizeof(int)];
        int i;
    } u;
    u.i = event;
    SL_LOGV("player_handleMediaPlayerEventNotifications(event='%c%c%c%c' (%d), data1=%d, data2=%d, "
            "user=%p) from AVPlayer", u.c[3], u.c[2], u.c[1], u.c[0], event, data1, data2, user);
    switch(event) {

      case android::GenericPlayer::kEventPrepared: {
        SL_LOGV("Received GenericPlayer::kEventPrepared for CMediaPlayer %p", mp);

        // assume no callback
        slPrefetchCallback callback = NULL;
        void* callbackPContext;
        XAuint32 events;

        object_lock_exclusive(&mp->mObject);

        // mark object as prepared; same state is used for successful or unsuccessful prepare
        assert(mp->mAndroidObjState == ANDROID_PREPARING);
        mp->mAndroidObjState = ANDROID_READY;

        if (PLAYER_SUCCESS == data1) {
            // Most of successful prepare completion for mp->mAVPlayer
            // is handled by GenericPlayer and its subclasses.
        } else {
            // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to
            //  indicate a prefetch error, so we signal it by sending simultaneously two events:
            //  - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0
            //  - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW
            SL_LOGE(ERROR_PLAYER_PREFETCH_d, data1);
            if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
                mp->mPrefetchStatus.mLevel = 0;
                mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
                if (!(~mp->mPrefetchStatus.mCallbackEventsMask &
                        (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
                    callback = mp->mPrefetchStatus.mCallback;
                    callbackPContext = mp->mPrefetchStatus.mContext;
                    events = SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE;
                }
            }
        }

        object_unlock_exclusive(&mp->mObject);

        // callback with no lock held
        if (NULL != callback) {
            (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, events);
        }

        break;
      }

      case android::GenericPlayer::kEventHasVideoSize: {
        SL_LOGV("Received AVPlayer::kEventHasVideoSize (%d,%d) for CMediaPlayer %p",
                data1, data2, mp);

        object_lock_exclusive(&mp->mObject);

        // remove an existing video info entry (here we only have one video stream)
        for(size_t i=0 ; i < mp->mStreamInfo.mStreamInfoTable.size() ; i++) {
            if (XA_DOMAINTYPE_VIDEO == mp->mStreamInfo.mStreamInfoTable.itemAt(i).domain) {
                mp->mStreamInfo.mStreamInfoTable.removeAt(i);
                break;
            }
        }
        // update the stream information with a new video info entry
        StreamInfo streamInfo;
        streamInfo.domain = XA_DOMAINTYPE_VIDEO;
        streamInfo.videoInfo.codecId = 0;// unknown, we don't have that info FIXME
        streamInfo.videoInfo.width = (XAuint32)data1;
        streamInfo.videoInfo.height = (XAuint32)data2;
        streamInfo.videoInfo.bitRate = 0;// unknown, we don't have that info FIXME
        streamInfo.videoInfo.frameRate = 0;
        streamInfo.videoInfo.duration = XA_TIME_UNKNOWN;
        StreamInfo &contInfo = mp->mStreamInfo.mStreamInfoTable.editItemAt(0);
        contInfo.containerInfo.numStreams = 1;
        ssize_t index = mp->mStreamInfo.mStreamInfoTable.add(streamInfo);

        // callback is unconditional; there is no bitmask of enabled events
        xaStreamEventChangeCallback callback = mp->mStreamInfo.mCallback;
        void* callbackPContext = mp->mStreamInfo.mContext;

        object_unlock_exclusive(&mp->mObject);

        // enqueue notification (outside of lock) that the stream information has been updated
        if ((NULL != callback) && (index >= 0)) {
#ifndef USE_ASYNCHRONOUS_STREAMCBEVENT_PROPERTYCHANGE_CALLBACK
            (*callback)(&mp->mStreamInfo.mItf, XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/,
                    1 /*streamIndex, only one stream supported here, 0 is reserved*/,
                    NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/,
                    callbackPContext /*pContext*/);
#else
            SLresult res = EnqueueAsyncCallback_piipp(mp, callback,
                    /*p1*/ &mp->mStreamInfo.mItf,
                    /*i1*/ XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/,
                    /*i2*/ 1 /*streamIndex, only one stream supported here, 0 is reserved*/,
                    /*p2*/ NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/,
                    /*p3*/ callbackPContext /*pContext*/);
            ALOGW_IF(SL_RESULT_SUCCESS != res,
                        "Callback %p(%p, XA_STREAMCBEVENT_PROPERTYCHANGE, 1, NULL, %p) dropped",
                        callback, &mp->mStreamInfo.mItf, callbackPContext);
#endif
        }
        break;
      }

      case android::GenericPlayer::kEventEndOfStream: {
        SL_LOGV("Received AVPlayer::kEventEndOfStream for CMediaPlayer %p", mp);

        object_lock_exclusive(&mp->mObject);
        // should be xaPlayCallback but we're sharing the itf between SL and AL
        slPlayCallback playCallback = NULL;
        void * playContext = NULL;
        // XAPlayItf callback or no callback?
        if (mp->mPlay.mEventFlags & XA_PLAYEVENT_HEADATEND) {
            playCallback = mp->mPlay.mCallback;
            playContext = mp->mPlay.mContext;
        }
        mp->mPlay.mState = XA_PLAYSTATE_PAUSED;
        object_unlock_exclusive(&mp->mObject);

        // enqueue callback with no lock held
        if (NULL != playCallback) {
#ifndef USE_ASYNCHRONOUS_PLAY_CALLBACK
            (*playCallback)(&mp->mPlay.mItf, playContext, XA_PLAYEVENT_HEADATEND);
#else
            SLresult res = EnqueueAsyncCallback_ppi(mp, playCallback, &mp->mPlay.mItf, playContext,
                    XA_PLAYEVENT_HEADATEND);
            ALOGW_IF(SL_RESULT_SUCCESS != res,
                    "Callback %p(%p, %p, SL_PLAYEVENT_HEADATEND) dropped", playCallback,
                    &mp->mPlay.mItf, playContext);
#endif
        }
        break;
      }

      case android::GenericPlayer::kEventChannelCount: {
        SL_LOGV("kEventChannelCount channels = %d", data1);
        object_lock_exclusive(&mp->mObject);
        if (UNKNOWN_NUMCHANNELS == mp->mNumChannels && UNKNOWN_NUMCHANNELS != data1) {
            mp->mNumChannels = data1;
            android_Player_volumeUpdate(mp);
        }
        object_unlock_exclusive(&mp->mObject);
      }
      break;

      case android::GenericPlayer::kEventPrefetchFillLevelUpdate: {
        SL_LOGV("kEventPrefetchFillLevelUpdate");
        if (!IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
            break;
        }
        slPrefetchCallback callback = NULL;
        void* callbackPContext = NULL;

        // SLPrefetchStatusItf callback or no callback?
        interface_lock_exclusive(&mp->mPrefetchStatus);
        if (mp->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
            callback = mp->mPrefetchStatus.mCallback;
            callbackPContext = mp->mPrefetchStatus.mContext;
        }
        mp->mPrefetchStatus.mLevel = (SLpermille)data1;
        interface_unlock_exclusive(&mp->mPrefetchStatus);

        // callback with no lock held
        if (NULL != callback) {
            (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext,
                    SL_PREFETCHEVENT_FILLLEVELCHANGE);
        }
      }
      break;

      case android::GenericPlayer::kEventPrefetchStatusChange: {
        SL_LOGV("kEventPrefetchStatusChange");
        if (!IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
            break;
        }
        slPrefetchCallback callback = NULL;
        void* callbackPContext = NULL;

        // SLPrefetchStatusItf callback or no callback?
        object_lock_exclusive(&mp->mObject);
        if (mp->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_STATUSCHANGE) {
            callback = mp->mPrefetchStatus.mCallback;
            callbackPContext = mp->mPrefetchStatus.mContext;
        }
        if (data1 >= android::kStatusIntermediate) {
            mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_SUFFICIENTDATA;
        } else if (data1 < android::kStatusIntermediate) {
            mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
        }
        object_unlock_exclusive(&mp->mObject);

        // callback with no lock held
        if (NULL != callback) {
            (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, SL_PREFETCHEVENT_STATUSCHANGE);
        }
      }
      break;

      case android::GenericPlayer::kEventPlay: {
        SL_LOGV("kEventPlay");

        interface_lock_shared(&mp->mPlay);
        slPlayCallback callback = mp->mPlay.mCallback;
        void* callbackPContext = mp->mPlay.mContext;
        interface_unlock_shared(&mp->mPlay);

        if (NULL != callback) {
            (*callback)(&mp->mPlay.mItf, callbackPContext, (SLuint32) data1); // SL_PLAYEVENT_HEAD*
        }
      }
      break;

      case android::GenericPlayer::kEventErrorAfterPrepare: {
        SL_LOGV("kEventErrorAfterPrepare");

        // assume no callback
        slPrefetchCallback callback = NULL;
        void* callbackPContext = NULL;

        object_lock_exclusive(&mp->mObject);
        if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
            mp->mPrefetchStatus.mLevel = 0;
            mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
            if (!(~mp->mPrefetchStatus.mCallbackEventsMask &
                    (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
                callback = mp->mPrefetchStatus.mCallback;
                callbackPContext = mp->mPrefetchStatus.mContext;
            }
        }
        object_unlock_exclusive(&mp->mObject);

        // FIXME there's interesting information in data1, but no API to convey it to client
        SL_LOGE("Error after prepare: %d", data1);

        // callback with no lock held
        if (NULL != callback) {
            (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext,
                    SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
        }

      }
      break;

      default: {
        SL_LOGE("Received unknown event %d, data %d from AVPlayer", event, data1);
      }
    }

    mp->mCallbackProtector->exitCb();
}
static void HandleAdd(void *self, int MPH)
{

    // validate input parameters
    IDynamicInterfaceManagement *this = (IDynamicInterfaceManagement *) self;
    assert(NULL != this);
    IObject *thisObject = InterfaceToIObject(this);
    assert(NULL != thisObject);
    assert(0 <= MPH && MPH < MPH_MAX);
    const ClassTable *class__ = thisObject->mClass;
    assert(NULL != class__);
    int index = class__->mMPH_to_index[MPH];
    assert(0 <= index && index < (int) class__->mInterfaceCount);
    SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
    SLresult result;

    // check interface state
    object_lock_exclusive(thisObject);
    SLuint8 state = *interfaceStateP;
    switch (state) {

    case INTERFACE_ADDING_1:    // normal case
        {
        // change state to indicate we are now adding the interface
        *interfaceStateP = INTERFACE_ADDING_2;
        object_unlock_exclusive(thisObject);

        // this section runs with mutex unlocked
        const struct iid_vtable *x = &class__->mInterfaces[index];
        size_t offset = x->mOffset;
        void *thisItf = (char *) thisObject + offset;
        BoolHook expose = MPH_init_table[MPH].mExpose;
        // call the optional expose hook
        if ((NULL == expose) || (*expose)(thisItf)) {
            result = SL_RESULT_SUCCESS;
        } else {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        }

        // re-lock mutex to update state
        object_lock_exclusive(thisObject);
        assert(INTERFACE_ADDING_2 == *interfaceStateP);
        if (SL_RESULT_SUCCESS == result) {
            ((size_t *) thisItf)[0] ^= ~0;
            state = INTERFACE_ADDED;
        } else {
            state = INTERFACE_INITIALIZED;
        }
        }
        break;

    case INTERFACE_ADDING_1A:   // operation was aborted while on work queue
        result = SL_RESULT_OPERATION_ABORTED;
        state = INTERFACE_INITIALIZED;
        break;

    default:                    // impossible
        assert(SL_BOOLEAN_FALSE);
        result = SL_RESULT_INTERNAL_ERROR;
        break;

    }

    // mutex is locked, update state
    *interfaceStateP = state;

    // Make a copy of these, so we can call the callback with mutex unlocked
    slDynamicInterfaceManagementCallback callback = this->mCallback;
    void *context = this->mContext;
    object_unlock_exclusive(thisObject);

    // Note that the mutex is unlocked during the callback
    if (NULL != callback) {
        const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID
        (*callback)(&this->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid);
    }

}