diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 9867d06cbd..c69e361d29 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -358,7 +358,9 @@ void LLAudioEngine_FMODEX::allocateListener(void) void LLAudioEngine_FMODEX::shutdown() { - stopInternetStream(); + // Safe stop and release for FMOD Ex music streams + //stopInternetStream(); + // LL_INFOS() << "About to LLAudioEngine::shutdown()" << LL_ENDL; LLAudioEngine::shutdown(); diff --git a/indra/llaudio/llstreamingaudio.h b/indra/llaudio/llstreamingaudio.h index aa3fbe93e5..befca39628 100755 --- a/indra/llaudio/llstreamingaudio.h +++ b/indra/llaudio/llstreamingaudio.h @@ -45,6 +45,9 @@ class LLStreamingAudioInterface virtual void setGain(F32 vol) = 0; virtual F32 getGain() = 0; virtual std::string getURL() = 0; + // Safe stop and release for FMOD Ex music streams + virtual bool delayedRelease(bool force = false){return false;} + // virtual bool supportsAdjustableBufferSizes(){return false;} virtual void setBufferSizes(U32 streambuffertime, U32 decodebuffertime){}; diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index cc942e636a..dff80578bc 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -52,6 +52,9 @@ public: LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url); FMOD::Channel* startStream(); bool stopStream(); // Returns true if the stream was successfully stopped. + // Safe stop and release for FMOD Ex music streams + void forceReleaseStream(); // This should rarely if ever be called. + // bool ready(); const std::string& getURL() { return mInternetStreamURL; } @@ -126,22 +129,31 @@ void LLStreamingAudio_FMODEX::start(const std::string& url) void LLStreamingAudio_FMODEX::update() { - // Kill dead internet streams, if possible - std::list::iterator iter; - for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + // Safe stop and release for FMOD Ex music streams + //// Kill dead internet streams, if possible + //std::list::iterator iter; + //for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + //{ + // LLAudioStreamManagerFMODEX *streamp = *iter; + // if (streamp->stopStream()) + // { + // LL_INFOS() << "Closed dead stream" << LL_ENDL; + // delete streamp; + // mDeadStreams.erase(iter++); + // } + // else + // { + // iter++; + // } + //} + + // Doesn't matter if this returns true or false here. Calling it is what kills delayed release streams + // if safe to do so. Returning true or false only matters when called from llappviewer during shutdown + if (delayedRelease()) { - LLAudioStreamManagerFMODEX *streamp = *iter; - if (streamp->stopStream()) - { - LL_INFOS() << "Closed dead stream" << LL_ENDL; - delete streamp; - mDeadStreams.erase(iter++); - } - else - { - iter++; - } + // Do nothing. } + // // Don't do anything if there are no streams playing if (!mCurrentInternetStreamp) @@ -305,8 +317,11 @@ void LLStreamingAudio_FMODEX::stop() { if (mFMODInternetStreamChannelp) { - mFMODInternetStreamChannelp->setPaused(true); - mFMODInternetStreamChannelp->setPriority(0); + // Safe stop and release for FMOD Ex music streams + //mFMODInternetStreamChannelp->setPaused(true); + //mFMODInternetStreamChannelp->setPriority(0); + mFMODInternetStreamChannelp->stop(); + // mFMODInternetStreamChannelp = NULL; } @@ -315,18 +330,60 @@ void LLStreamingAudio_FMODEX::stop() LL_INFOS() << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL; if (mCurrentInternetStreamp->stopStream()) { + // Safe stop and release for FMOD Ex music streams + LL_INFOS() << "Successfully released internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL; + // delete mCurrentInternetStreamp; } else { - LL_WARNS() << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL; - mDeadStreams.push_back(mCurrentInternetStreamp); + // Safe stop and release for FMOD Ex music streams + //LL_WARNS() << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL; + //mDeadStreams.push_back(mCurrentInternetStreamp); + LL_INFOS() << "Delaying release of internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL; + mDelayedReleaseStreams.push_back(mCurrentInternetStreamp); + // } mCurrentInternetStreamp = NULL; //mURL.clear(); } } +// Safe stop and release for FMOD Ex music streams +bool LLStreamingAudio_FMODEX::delayedRelease(bool force) +{ + bool streams = false; + std::list::iterator iter; + for (iter = mDelayedReleaseStreams.begin(); iter != mDelayedReleaseStreams.end();) + { + streams = true; + LLAudioStreamManagerFMODEX *streamp = *iter; + + if (!force) + { + if (streamp->stopStream()) + { + LL_INFOS() << "Successfully released internet stream: " << streamp->getURL() << LL_ENDL; + delete streamp; + mDelayedReleaseStreams.erase(iter++); + } + else + { + iter++; + } + } + else + { + streamp->forceReleaseStream(); + LL_INFOS() << "Force released internet stream: " << streamp->getURL() << LL_ENDL; + delete streamp; + mDelayedReleaseStreams.erase(iter++); + } + } + return streams; +} +// + void LLStreamingAudio_FMODEX::pause(int pauseopt) { if (pauseopt < 0) @@ -457,16 +514,25 @@ bool LLAudioStreamManagerFMODEX::stopStream() { if (mInternetStream) { - - - bool close = true; + // Safe stop and release for FMOD Ex music streams + mSystem->update(); + + //bool close = true; + bool close = false; switch (getOpenState()) { - case FMOD_OPENSTATE_CONNECTING: - close = false; + //case FMOD_OPENSTATE_CONNECTING: + case FMOD_OPENSTATE_READY: + //close = false; + close = true; + break; + case FMOD_OPENSTATE_ERROR: + close = true; break; default: - close = true; + //close = true; + close = false; + // } if (close) @@ -487,6 +553,18 @@ bool LLAudioStreamManagerFMODEX::stopStream() } } +// Safe stop and release for FMOD Ex music streams +void LLAudioStreamManagerFMODEX::forceReleaseStream() +{ + if (mInternetStream) + { + mInternetStream->release(); + mStreamChannel = NULL; + mInternetStream = NULL; + } +} +// + FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) { FMOD_OPENSTATE state; diff --git a/indra/llaudio/llstreamingaudio_fmodex.h b/indra/llaudio/llstreamingaudio_fmodex.h index 56a1a2f6f2..69d5f0baee 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.h +++ b/indra/llaudio/llstreamingaudio_fmodex.h @@ -55,6 +55,9 @@ class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface /*virtual*/ void setGain(F32 vol); /*virtual*/ F32 getGain(); /*virtual*/ std::string getURL(); + // Safe stop and release for FMOD Ex music streams + /*virtual*/ bool delayedRelease(bool force = false); + // /*virtual*/ bool supportsAdjustableBufferSizes(){return true;} /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); @@ -68,7 +71,10 @@ private: LLAudioStreamManagerFMODEX *mCurrentInternetStreamp; FMOD::Channel *mFMODInternetStreamChannelp; - std::list mDeadStreams; + // Safe stop and release for FMOD Ex music streams + //std::list mDeadStreams; + std::list mDelayedReleaseStreams; + // std::string mURL; F32 mGain; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 71438238d0..5e447686bc 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2135,11 +2135,40 @@ bool LLAppViewer::cleanup() // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); - delete sai; - gAudiop->setStreamingAudioImpl(NULL); + // Safe stop and release for FMOD Ex music streams + if (sai) + { + sai->stop(); + + LLTimer streamReleaseTimer; + streamReleaseTimer.start(); + + // Kill delayed release streams if any. Most streams will release almost instantly + // but there is a timeout in place just in case. + while (sai->delayedRelease() && streamReleaseTimer.getElapsedTimeF32() < 5.0f); + { + // Do nothing while waiting for delayedRelease to kill the streams. + } + streamReleaseTimer.stop(); + + // We should rarely if ever enter this loop, but we will if there are streams that + // were not released before the 5 second timeout above. Show no mercy and call release + // on them right away. + while (sai->delayedRelease(true)); + { + // Do nothing again while delayedRelease force kills the streams. + } + } + + //delete sai; + //gAudiop->setStreamingAudioImpl(NULL); // shut down the audio subsystem - gAudiop->shutdown(); + gAudiop->shutdown(); + + delete sai; + gAudiop->setStreamingAudioImpl(NULL); + // delete gAudiop; gAudiop = NULL;