Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6b19ca7 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "libmediaplayerservice: Serialize access to AudioOutput" into mnc-dev

parents a56d1303 d1c74340
Loading
Loading
Loading
Loading
+131 −37
Original line number Diff line number Diff line
@@ -1335,21 +1335,23 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid,
      mCallbackCookie(NULL),
      mCallbackData(NULL),
      mBytesWritten(0),
      mStreamType(AUDIO_STREAM_MUSIC),
      mAttributes(attr),
      mLeftVolume(1.0),
      mRightVolume(1.0),
      mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
      mSampleRateHz(0),
      mMsecsPerFrame(0),
      mFrameSize(0),
      mSessionId(sessionId),
      mUid(uid),
      mPid(pid),
      mFlags(AUDIO_OUTPUT_FLAG_NONE) {
      mSendLevel(0.0),
      mAuxEffectId(0),
      mFlags(AUDIO_OUTPUT_FLAG_NONE)
{
    ALOGV("AudioOutput(%d)", sessionId);
    mStreamType = AUDIO_STREAM_MUSIC;
    mLeftVolume = 1.0;
    mRightVolume = 1.0;
    mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
    mSampleRateHz = 0;
    mMsecsPerFrame = 0;
    mAuxEffectId = 0;
    mSendLevel = 0.0;
    setMinBufferCount();
    mAttributes = attr;
}

MediaPlayerService::AudioOutput::~AudioOutput()
@@ -1358,6 +1360,7 @@ MediaPlayerService::AudioOutput::~AudioOutput()
    delete mCallbackData;
}

//static
void MediaPlayerService::AudioOutput::setMinBufferCount()
{
    char value[PROPERTY_VALUE_MAX];
@@ -1367,92 +1370,105 @@ void MediaPlayerService::AudioOutput::setMinBufferCount()
    }
}

// static
bool MediaPlayerService::AudioOutput::isOnEmulator()
{
    setMinBufferCount();
    setMinBufferCount(); // benign race wrt other threads
    return mIsOnEmulator;
}

// static
int MediaPlayerService::AudioOutput::getMinBufferCount()
{
    setMinBufferCount();
    setMinBufferCount(); // benign race wrt other threads
    return mMinBufferCount;
}

ssize_t MediaPlayerService::AudioOutput::bufferSize() const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->frameCount() * frameSize();
    return mTrack->frameCount() * mFrameSize;
}

ssize_t MediaPlayerService::AudioOutput::frameCount() const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->frameCount();
}

ssize_t MediaPlayerService::AudioOutput::channelCount() const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->channelCount();
}

ssize_t MediaPlayerService::AudioOutput::frameSize() const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->frameSize();
    return mFrameSize;
}

uint32_t MediaPlayerService::AudioOutput::latency () const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return 0;
    return mTrack->latency();
}

float MediaPlayerService::AudioOutput::msecsPerFrame() const
{
    Mutex::Autolock lock(mLock);
    return mMsecsPerFrame;
}

status_t MediaPlayerService::AudioOutput::getPosition(uint32_t *position) const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->getPosition(position);
}

status_t MediaPlayerService::AudioOutput::getTimestamp(AudioTimestamp &ts) const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->getTimestamp(ts);
}

status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    *frameswritten = mBytesWritten / frameSize();
    *frameswritten = mBytesWritten / mFrameSize;
    return OK;
}

status_t MediaPlayerService::AudioOutput::setParameters(const String8& keyValuePairs)
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return NO_INIT;
    return mTrack->setParameters(keyValuePairs);
}

String8  MediaPlayerService::AudioOutput::getParameters(const String8& keys)
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return String8::empty();
    return mTrack->getParameters(keys);
}

void MediaPlayerService::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
    Mutex::Autolock lock(mLock);
    mAttributes = attributes;
}

void MediaPlayerService::AudioOutput::deleteRecycledTrack()
void MediaPlayerService::AudioOutput::deleteRecycledTrack_l()
{
    ALOGV("deleteRecycledTrack");

    ALOGV("deleteRecycledTrack_l");
    if (mRecycledTrack != 0) {

        if (mCallbackData != NULL) {
@@ -1470,12 +1486,17 @@ void MediaPlayerService::AudioOutput::deleteRecycledTrack()
        // AudioFlinger to drain the track.

        mRecycledTrack.clear();
        close_l();
        delete mCallbackData;
        mCallbackData = NULL;
        close();
    }
}

void MediaPlayerService::AudioOutput::close_l()
{
    mTrack.clear();
}

status_t MediaPlayerService::AudioOutput::open(
        uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
        audio_format_t format, int bufferCount,
@@ -1535,6 +1556,7 @@ status_t MediaPlayerService::AudioOutput::open(
        }
    }

    Mutex::Autolock lock(mLock);
    mCallback = cb;
    mCallbackCookie = cookie;

@@ -1577,7 +1599,7 @@ status_t MediaPlayerService::AudioOutput::open(
    // we must close the previous output before opening a new one
    if (bothOffloaded && !reuse) {
        ALOGV("both offloaded and not recycling");
        deleteRecycledTrack();
        deleteRecycledTrack_l();
    }

    sp<AudioTrack> t;
@@ -1655,7 +1677,7 @@ status_t MediaPlayerService::AudioOutput::open(

        if (reuse) {
            ALOGV("chaining to next output and recycling track");
            close();
            close_l();
            mTrack = mRecycledTrack;
            mRecycledTrack.clear();
            if (mCallbackData != NULL) {
@@ -1669,7 +1691,7 @@ status_t MediaPlayerService::AudioOutput::open(
    // we're not going to reuse the track, unblock and flush it
    // this was done earlier if both tracks are offloaded
    if (!bothOffloaded) {
        deleteRecycledTrack();
        deleteRecycledTrack_l();
    }

    CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
@@ -1681,9 +1703,10 @@ status_t MediaPlayerService::AudioOutput::open(
    mSampleRateHz = sampleRate;
    mFlags = t->getFlags(); // we suggest the flags above, but new AudioTrack() may not grant it.
    mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
    mFrameSize = t->frameSize();
    uint32_t pos;
    if (t->getPosition(&pos) == OK) {
        mBytesWritten = uint64_t(pos) * t->frameSize();
        mBytesWritten = uint64_t(pos) * mFrameSize;
    }
    mTrack = t;

@@ -1704,6 +1727,7 @@ status_t MediaPlayerService::AudioOutput::open(
status_t MediaPlayerService::AudioOutput::start()
{
    ALOGV("start");
    Mutex::Autolock lock(mLock);
    if (mCallbackData != NULL) {
        mCallbackData->endTrackSwitch();
    }
@@ -1716,30 +1740,88 @@ status_t MediaPlayerService::AudioOutput::start()
}

void MediaPlayerService::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) {
    Mutex::Autolock lock(mLock);
    mNextOutput = nextOutput;
}


void MediaPlayerService::AudioOutput::switchToNextOutput() {
    ALOGV("switchToNextOutput");
    if (mNextOutput != NULL) {

    // Try to acquire the callback lock before moving track (without incurring deadlock).
    const unsigned kMaxSwitchTries = 100;
    Mutex::Autolock lock(mLock);
    for (unsigned tries = 0;;) {
        if (mTrack == 0) {
            return;
        }
        if (mNextOutput != NULL && mNextOutput != this) {
            if (mCallbackData != NULL) {
            mCallbackData->beginTrackSwitch();
                // two alternative approaches
#if 1
                CallbackData *callbackData = mCallbackData;
                mLock.unlock();
                // proper acquisition sequence
                callbackData->lock();
                mLock.lock();
                // Caution: it is unlikely that someone deleted our callback or changed our target
                if (callbackData != mCallbackData || mNextOutput == NULL || mNextOutput == this) {
                    // fatal if we are starved out.
                    LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
                            "switchToNextOutput() cannot obtain correct lock sequence");
                    callbackData->unlock();
                    continue;
                }
                callbackData->mSwitching = true; // begin track switch
#else
                // tryBeginTrackSwitch() returns false if the callback has the lock.
                if (!mCallbackData->tryBeginTrackSwitch()) {
                    // fatal if we are starved out.
                    LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
                            "switchToNextOutput() cannot obtain callback lock");
                    mLock.unlock();
                    usleep(5 * 1000 /* usec */); // allow callback to use AudioOutput
                    mLock.lock();
                    continue;
                }
#endif
            }

            Mutex::Autolock nextLock(mNextOutput->mLock);

            // If the next output track is not NULL, then it has been
            // opened already for playback.
            // This is possible even without the next player being started,
            // for example, the next player could be prepared and seeked.
            //
            // Presuming it isn't advisable to force the track over.
             if (mNextOutput->mTrack == NULL) {
                ALOGD("Recycling track for gapless playback");
                delete mNextOutput->mCallbackData;
                mNextOutput->mCallbackData = mCallbackData;
        mCallbackData = NULL;
                mNextOutput->mRecycledTrack = mTrack;
        mTrack.clear();
                mNextOutput->mSampleRateHz = mSampleRateHz;
                mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
                mNextOutput->mBytesWritten = mBytesWritten;
                mNextOutput->mFlags = mFlags;
                mNextOutput->mFrameSize = mFrameSize;
                close_l();
                mCallbackData = NULL;  // destruction handled by mNextOutput
            } else {
                ALOGW("Ignoring gapless playback because next player has already started");
                // remove track in case resource needed for future players.
                if (mCallbackData != NULL) {
                    mCallbackData->endTrackSwitch();  // release lock for callbacks before close.
                }
                close_l();
            }
        }
        break;
    }
}

ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size, bool blocking)
{
    Mutex::Autolock lock(mLock);
    LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");

    //ALOGV("write(%p, %u)", buffer, size);
@@ -1756,6 +1838,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size,
void MediaPlayerService::AudioOutput::stop()
{
    ALOGV("stop");
    Mutex::Autolock lock(mLock);
    mBytesWritten = 0;
    if (mTrack != 0) mTrack->stop();
}
@@ -1763,6 +1846,7 @@ void MediaPlayerService::AudioOutput::stop()
void MediaPlayerService::AudioOutput::flush()
{
    ALOGV("flush");
    Mutex::Autolock lock(mLock);
    mBytesWritten = 0;
    if (mTrack != 0) mTrack->flush();
}
@@ -1770,18 +1854,21 @@ void MediaPlayerService::AudioOutput::flush()
void MediaPlayerService::AudioOutput::pause()
{
    ALOGV("pause");
    Mutex::Autolock lock(mLock);
    if (mTrack != 0) mTrack->pause();
}

void MediaPlayerService::AudioOutput::close()
{
    ALOGV("close");
    mTrack.clear();
    Mutex::Autolock lock(mLock);
    close_l();
}

void MediaPlayerService::AudioOutput::setVolume(float left, float right)
{
    ALOGV("setVolume(%f, %f)", left, right);
    Mutex::Autolock lock(mLock);
    mLeftVolume = left;
    mRightVolume = right;
    if (mTrack != 0) {
@@ -1793,6 +1880,7 @@ status_t MediaPlayerService::AudioOutput::setPlaybackRate(const AudioPlaybackRat
{
    ALOGV("setPlaybackRate(%f %f %d %d)",
                rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) {
        // remember rate so that we can set it when the track is opened
        mPlaybackRate = rate;
@@ -1814,6 +1902,7 @@ status_t MediaPlayerService::AudioOutput::setPlaybackRate(const AudioPlaybackRat
status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
{
    ALOGV("setPlaybackRate");
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) {
        return NO_INIT;
    }
@@ -1824,6 +1913,7 @@ status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rat
status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
{
    ALOGV("setAuxEffectSendLevel(%f)", level);
    Mutex::Autolock lock(mLock);
    mSendLevel = level;
    if (mTrack != 0) {
        return mTrack->setAuxEffectSendLevel(level);
@@ -1834,6 +1924,7 @@ status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId)
{
    ALOGV("attachAuxEffect(%d)", effectId);
    Mutex::Autolock lock(mLock);
    mAuxEffectId = effectId;
    if (mTrack != 0) {
        return mTrack->attachAuxEffect(effectId);
@@ -1846,6 +1937,7 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(
        int event, void *cookie, void *info) {
    //ALOGV("callbackwrapper");
    CallbackData *data = (CallbackData*)cookie;
    // lock to ensure we aren't caught in the middle of a track switch.
    data->lock();
    AudioOutput *me = data->getOutput();
    AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
@@ -1915,11 +2007,13 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(

int MediaPlayerService::AudioOutput::getSessionId() const
{
    Mutex::Autolock lock(mLock);
    return mSessionId;
}

uint32_t MediaPlayerService::AudioOutput::getSampleRate() const
{
    Mutex::Autolock lock(mLock);
    if (mTrack == 0) return 0;
    return mTrack->getSampleRate();
}
+24 −8
Original line number Diff line number Diff line
@@ -132,7 +132,8 @@ class MediaPlayerService : public BnMediaPlayerService
        static void             setMinBufferCount();
        static void             CallbackWrapper(
                int event, void *me, void *info);
               void             deleteRecycledTrack();
               void             deleteRecycledTrack_l();
               void             close_l();

        sp<AudioTrack>          mTrack;
        sp<AudioTrack>          mRecycledTrack;
@@ -148,32 +149,47 @@ class MediaPlayerService : public BnMediaPlayerService
        AudioPlaybackRate       mPlaybackRate;
        uint32_t                mSampleRateHz; // sample rate of the content, as set in open()
        float                   mMsecsPerFrame;
        size_t                  mFrameSize;
        int                     mSessionId;
        int                     mUid;
        int                     mPid;
        float                   mSendLevel;
        int                     mAuxEffectId;
        audio_output_flags_t    mFlags;
        mutable Mutex           mLock;

        // static variables below not protected by mutex
        static bool             mIsOnEmulator;
        static int              mMinBufferCount;  // 12 for emulator; otherwise 4
        audio_output_flags_t    mFlags;

        // CallbackData is what is passed to the AudioTrack as the "user" data.
        // We need to be able to target this to a different Output on the fly,
        // so we can't use the Output itself for this.
        class CallbackData {
            friend AudioOutput;
        public:
            CallbackData(AudioOutput *cookie) {
                mData = cookie;
                mSwitching = false;
            }
            AudioOutput *   getOutput() { return mData;}
            AudioOutput *   getOutput() const { return mData; }
            void            setOutput(AudioOutput* newcookie) { mData = newcookie; }
            // lock/unlock are used by the callback before accessing the payload of this object
            void            lock() { mLock.lock(); }
            void            unlock() { mLock.unlock(); }
            // beginTrackSwitch/endTrackSwitch are used when this object is being handed over
            void            lock() const { mLock.lock(); }
            void            unlock() const { mLock.unlock(); }

            // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
            // to the next sink.
            void            beginTrackSwitch() { mLock.lock(); mSwitching = true; }

            // tryBeginTrackSwitch() returns true only if it obtains the lock.
            bool            tryBeginTrackSwitch() {
                LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
                if (mLock.tryLock() != OK) {
                    return false;
                }
                mSwitching = true;
                return true;
            }
            void            endTrackSwitch() {
                if (mSwitching) {
                    mLock.unlock();
@@ -182,7 +198,7 @@ class MediaPlayerService : public BnMediaPlayerService
            }
        private:
            AudioOutput *   mData;
            mutable Mutex   mLock;
            mutable Mutex   mLock; // a recursive mutex might make this unnecessary.
            bool            mSwitching;
            DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
        };