Loading media/libmediaplayerservice/MediaPlayerService.cpp +131 −37 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -1358,6 +1360,7 @@ MediaPlayerService::AudioOutput::~AudioOutput() delete mCallbackData; } //static void MediaPlayerService::AudioOutput::setMinBufferCount() { char value[PROPERTY_VALUE_MAX]; Loading @@ -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) { Loading @@ -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, Loading Loading @@ -1535,6 +1556,7 @@ status_t MediaPlayerService::AudioOutput::open( } } Mutex::Autolock lock(mLock); mCallback = cb; mCallbackCookie = cookie; Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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))); Loading @@ -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; Loading @@ -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(); } Loading @@ -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); Loading @@ -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(); } Loading @@ -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(); } Loading @@ -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) { Loading @@ -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; Loading @@ -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; } Loading @@ -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); Loading @@ -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); Loading @@ -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; Loading Loading @@ -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(); } Loading media/libmediaplayerservice/MediaPlayerService.h +24 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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); }; Loading Loading
media/libmediaplayerservice/MediaPlayerService.cpp +131 −37 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -1358,6 +1360,7 @@ MediaPlayerService::AudioOutput::~AudioOutput() delete mCallbackData; } //static void MediaPlayerService::AudioOutput::setMinBufferCount() { char value[PROPERTY_VALUE_MAX]; Loading @@ -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) { Loading @@ -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, Loading Loading @@ -1535,6 +1556,7 @@ status_t MediaPlayerService::AudioOutput::open( } } Mutex::Autolock lock(mLock); mCallback = cb; mCallbackCookie = cookie; Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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))); Loading @@ -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; Loading @@ -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(); } Loading @@ -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); Loading @@ -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(); } Loading @@ -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(); } Loading @@ -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) { Loading @@ -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; Loading @@ -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; } Loading @@ -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); Loading @@ -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); Loading @@ -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; Loading Loading @@ -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(); } Loading
media/libmediaplayerservice/MediaPlayerService.h +24 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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); }; Loading