diff options
author | Andy Hung <hunga@google.com> | 2015-07-10 20:36:56 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-07-10 20:36:57 +0000 |
commit | b6b19ca71e41276190fcd97351a0d19fae75bd34 (patch) | |
tree | 948ddc8e77d15457e4fd4eba6ba5a042f86cefee /media | |
parent | a56d1303bc7505ca7ea17251218185557528d6bb (diff) | |
parent | d1c74340c9346e2bfd061e20fba9bf34c22d77db (diff) | |
download | frameworks_av-b6b19ca71e41276190fcd97351a0d19fae75bd34.zip frameworks_av-b6b19ca71e41276190fcd97351a0d19fae75bd34.tar.gz frameworks_av-b6b19ca71e41276190fcd97351a0d19fae75bd34.tar.bz2 |
Merge "libmediaplayerservice: Serialize access to AudioOutput" into mnc-dev
Diffstat (limited to 'media')
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 168 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 32 |
2 files changed, 155 insertions, 45 deletions
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index ae869d6..0ecfb1e 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -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) { - if (mCallbackData != NULL) { - mCallbackData->beginTrackSwitch(); + + // 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; } - 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; + if (mNextOutput != NULL && mNextOutput != this) { + if (mCallbackData != NULL) { + // 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; + mNextOutput->mRecycledTrack = mTrack; + 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(); } diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 7527506..9e6ca52 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -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); }; |