From 5fcdbfdd9e543abca3f5606b9f62306ff3c9099e Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 18 Jul 2012 21:04:21 -0500 Subject: [PATCH] FMODEx diagnostics. SHFMODExStreamBufferSize added. Determines stream buffer size in ms. (stream restart required) SHFMODExDecodeBufferSize added. Determines decode buffer size in ms. (stream restart required) Streams will mute themselves if they are starving, until they are free of starvation for 5 full seconds. Streams that fail to accumulate any buffer progress while starving for 10 full updates will be stopped. Stream buffer progress(buffer percent) is llinfos spewed every update. (temporary) Doubled default stream buffer size Increased default decode buffer size to 1000ms (from 400) Temporarily using FMOD::Memory_Initialize to display raw stream/decode buffer sizes via llinfos. Added llwarns messages for SigmaTel hardware or bad audio acceleration configuration. --- indra/llaudio/llaudioengine_fmodex.cpp | 28 +++++++++ indra/llaudio/llstreamingaudio.h | 3 + indra/llaudio/llstreamingaudio_fmodex.cpp | 68 ++++++++++++++++++++-- indra/llaudio/llstreamingaudio_fmodex.h | 7 +++ indra/newview/app_settings/settings_sh.xml | 22 +++++++ indra/newview/llviewerparcelmedia.cpp | 4 ++ 6 files changed, 126 insertions(+), 6 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 2b2bd19d5..83ef4c5b4 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -95,6 +95,28 @@ inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) return true; } +void* __stdcall decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + if(type & FMOD_MEMORY_STREAM_DECODE) + { + llinfos << "Decode buffer size: " << size << llendl; + } + else if(type & FMOD_MEMORY_STREAM_FILE) + { + llinfos << "Strean buffer size: " << size << llendl; + } + return new char[size]; +} +void* __stdcall decode_realloc(void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + memset(ptr,0,size); + return ptr; +} +void __stdcall decode_dealloc(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + delete[] ptr; +} + bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) { @@ -108,6 +130,10 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL; + result = FMOD::Memory_Initialize(NULL, NULL, &decode_alloc, &decode_realloc, &decode_dealloc, FMOD_MEMORY_STREAM_DECODE | FMOD_MEMORY_STREAM_FILE); + if(Check_FMOD_Error(result, "FMOD::Memory_Initialize")) + return false; + result = FMOD::System_Create(&mSystem); if(Check_FMOD_Error(result, "FMOD::System_Create")) return false; @@ -156,12 +182,14 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) */ result = mSystem->setDSPBufferSize(1024, 10); Check_FMOD_Error(result, "FMOD::System::setDSPBufferSize"); + llwarns << "Windows audio acceleration is disabled. This may introduce latency issues." << llendl; } result = mSystem->getDriverInfo(0, name, 256, 0); Check_FMOD_Error(result, "FMOD::System::getDriverInfo"); if (strstr(name, "SigmaTel")) { + llwarns << "SigmaTel device detected. This may introduce audio quality issues." << llendl; /* Sigmatel sound devices crackle for some reason if the format is PCM 16bit. PCM floating point output seems to solve it. diff --git a/indra/llaudio/llstreamingaudio.h b/indra/llaudio/llstreamingaudio.h index 0e81cc530..3fb9ad4e3 100644 --- a/indra/llaudio/llstreamingaudio.h +++ b/indra/llaudio/llstreamingaudio.h @@ -58,6 +58,9 @@ class LLStreamingAudioInterface virtual const LLSD *getMetaData() = 0; virtual bool supportsWaveData() = 0; virtual bool getWaveData(float* arr, S32 count, S32 stride = 1) = 0; + + virtual bool supportsAdjustableBufferSizes(){return false;} + virtual void setBufferSizes(U32 streambuffertime, U32 decodebuffertime){}; }; #endif // LL_STREAMINGAUDIO_H diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index 6d97bcc49..c57f7d59b 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -50,7 +50,7 @@ public: const std::string& getURL() { return mInternetStreamURL; } - FMOD_OPENSTATE getOpenState(); + FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL); protected: FMOD::System* mSystem; FMOD::Channel* mStreamChannel; @@ -70,11 +70,13 @@ LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) : mCurrentInternetStreamp(NULL), mFMODInternetStreamChannelp(NULL), mGain(1.0f), - mMetaData(NULL) + mMetaData(NULL), + mStarvedProgress(0), + mStarvedNoProgressFrames(0) { // Number of milliseconds of audio to buffer for the audio card. // Must be larger than the usual Second Life frame stutter time. - const U32 buffer_seconds = 5; //sec + const U32 buffer_seconds = 10; //sec const U32 estimated_bitrate = 128; //kbit/sec mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); @@ -145,7 +147,10 @@ void LLStreamingAudio_FMODEX::update() return; } - FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(); + unsigned int progress; + bool starving; + bool diskbusy; + FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy); if (open_state == FMOD_OPENSTATE_READY) { @@ -158,6 +163,7 @@ void LLStreamingAudio_FMODEX::update() // Reset volume to previously set volume setGain(getGain()); mFMODInternetStreamChannelp->setPaused(false); + mLastStarved.stop(); } } else if(open_state == FMOD_OPENSTATE_ERROR) @@ -168,6 +174,7 @@ void LLStreamingAudio_FMODEX::update() if(mFMODInternetStreamChannelp) { + llinfos << "progress = " << progress << llendl; if(!mMetaData) mMetaData = new LLSD; @@ -237,12 +244,46 @@ void LLStreamingAudio_FMODEX::update() } } } + if(starving) + { + if(!mLastStarved.getStarted()) + { + llinfos << "Stream starvation detected! Muting stream audio until it clears." << llendl; + llinfos << " (diskbusy="<setMute(true); + mStarvedProgress = progress; + mStarvedNoProgressFrames = 0; + } + else if(mStarvedProgress == progress) + { + if(++mStarvedNoProgressFrames >= 10) + { + //we got 10 consecutive updates of 0 progress made on the stream buffer. It probably stalled. + llinfos << "Stream unable to recover from starvation. Halting." << llendl; + stop(); + return; + } + } + else + { + mStarvedNoProgressFrames = 0; + mStarvedProgress = progress; + } + mLastStarved.start(); + } + else if(mLastStarved.getStarted() && mLastStarved.getElapsedTimeF32() > 5.f) + { + mLastStarved.stop(); + mFMODInternetStreamChannelp->setMute(false); + } } } } void LLStreamingAudio_FMODEX::stop() { + mLastStarved.stop(); if(mMetaData) { delete mMetaData; @@ -341,6 +382,11 @@ void LLStreamingAudio_FMODEX::setGain(F32 vol) if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) return false; + bool muted=false; + mFMODInternetStreamChannelp->getMute(&muted); + if(muted) + return false; + static std::vector local_array(count); //Have to have an extra buffer to mix channels. Bleh. if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink. local_array.resize(count); @@ -442,9 +488,19 @@ bool LLAudioStreamManagerFMODEX::stopStream() } } -FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState() +FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) { FMOD_OPENSTATE state; - mInternetStream->getOpenState(&state,NULL,NULL,NULL); + mInternetStream->getOpenState(&state,percentbuffered,starving,diskbusy); return state; } + +void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) +{ + mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES); + FMOD_ADVANCEDSETTINGS settings; + memset(&settings,0,sizeof(settings)); + settings.cbsize=sizeof(settings); + settings.defaultDecodeBufferSize = decodebuffertime;//ms + mSystem->setAdvancedSettings(&settings); +} \ No newline at end of file diff --git a/indra/llaudio/llstreamingaudio_fmodex.h b/indra/llaudio/llstreamingaudio_fmodex.h index 064b266e6..46c0ea553 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.h +++ b/indra/llaudio/llstreamingaudio_fmodex.h @@ -37,6 +37,7 @@ #include "stdtypes.h" // from llcommon #include "llstreamingaudio.h" +#include "lltimer.h" //Stubs class LLAudioStreamManagerFMODEX; @@ -66,6 +67,8 @@ class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. /*virtual*/ bool supportsWaveData(){return true;} /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1); + /*virtual*/ bool supportsAdjustableBufferSizes(){return true;} + /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); private: FMOD::System *mSystem; @@ -76,6 +79,10 @@ private: std::string mURL; F32 mGain; + LLTimer mLastStarved; + unsigned int mStarvedProgress; + unsigned int mStarvedNoProgressFrames; + LLSD *mMetaData; }; diff --git a/indra/newview/app_settings/settings_sh.xml b/indra/newview/app_settings/settings_sh.xml index 803f7a12e..feae4cb77 100644 --- a/indra/newview/app_settings/settings_sh.xml +++ b/indra/newview/app_settings/settings_sh.xml @@ -40,6 +40,28 @@ Boolean Value 0 + + SHFMODExStreamBufferSize + + Comment + Sets the streaming buffer size (in milliseconds) + Persist + 1 + Type + U32 + Value + 7000 + + SHFMODExDecodeBufferSize + + Comment + Sets the streaming decode buffer size (in milliseconds) + Persist + 1 + Type + U32 + Value + 1000 SHAllowScriptCommands diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 9e7df265e..1349429ee 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -54,6 +54,7 @@ #include "llaudioengine.h" #include "lloverlaybar.h" #include "slfloatermediafilter.h" +#include "llstreamingaudio.h" // Static Variables @@ -661,6 +662,9 @@ void LLViewerParcelMedia::playStreamingMusic(LLParcel* parcel, bool filter) else if (gAudiop) { LLStringUtil::trim(music_url); + LLStreamingAudioInterface *stream = gAudiop->getStreamingAudioImpl(); + if(stream && stream->supportsAdjustableBufferSizes()) + stream->setBufferSizes(gSavedSettings.getU32("SHFMODExStreamBufferSize"),gSavedSettings.getU32("SHFMODExDecodeBufferSize")); gAudiop->startInternetStream(music_url); if (music_url.empty()) {