diff options
| author | Roxie Linden <roxie@lindenlab.com> | 2024-03-09 16:48:44 -0800 | 
|---|---|---|
| committer | Roxie Linden <roxie@lindenlab.com> | 2024-03-09 16:48:44 -0800 | 
| commit | fd2d5c9c6c97254be7126171dc4472d3d5451ef8 (patch) | |
| tree | f860ea0ff61edc557408d9589a2b76edd59b770f | |
| parent | 42c7a335f840acf1b927b1ed6c8e06a6f00534d0 (diff) | |
Code cleanup and commenting
| -rw-r--r-- | indra/newview/llvoiceclient.h | 2 | ||||
| -rw-r--r-- | indra/newview/llvoicevivox.cpp | 7 | ||||
| -rw-r--r-- | indra/newview/llvoicevivox.h | 6 | ||||
| -rw-r--r-- | indra/newview/llvoicewebrtc.cpp | 2446 | ||||
| -rw-r--r-- | indra/newview/llvoicewebrtc.h | 776 | 
5 files changed, 1395 insertions, 1842 deletions
| diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 4c6cf92276..79bca23fc4 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -464,8 +464,6 @@ public:  	F32 getUserVolume(const LLUUID& id);  	///////////////////////////// -	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled. -													  // Use this to determine whether to show a "no speech" icon in the menu bar.  	void getParticipantList(std::set<LLUUID> &participants);  	bool isParticipant(const LLUUID& speaker_id); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 529abff01b..1454342083 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -295,7 +295,6 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :  	mTuningSpeakerVolumeDirty(true),  	mDevicesListUpdated(false), -	mAreaVoiceDisabled(false),  	mAudioSession(), // TBD - should be NULL  	mAudioSessionChanged(false),  	mNextAudioSession(), @@ -4927,7 +4926,6 @@ bool LLVivoxVoiceClient::setSpatialChannel(const LLSD& channelInfo)      mProcessChannels = true;  	mSpatialSessionURI = channelInfo["channel_uri"].asString();      mSpatialSessionCredentials = channelInfo["channel_credentials"].asString(); -	mAreaVoiceDisabled = mSpatialSessionURI.empty();  	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << mSpatialSessionURI << "\"" << LL_ENDL; @@ -5657,11 +5655,6 @@ std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id)  	return result;  } -BOOL LLVivoxVoiceClient::getAreaVoiceDisabled() -{ -	return mAreaVoiceDisabled; -} -  void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)  {  //	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index a141ac9465..14f6422ebb 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -511,11 +511,6 @@ protected:  	// MBW -- XXX -- Not sure how to get this data out of the TVC  	BOOL getUsingPTT(const LLUUID& id);  	std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable) - -	///////////////////////////// -	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled. -										// Use this to determine whether to show a "no speech" icon in the menu bar. -		  	/////////////////////////////  	// Recording controls @@ -711,7 +706,6 @@ private:  	std::string mMainSessionGroupHandle; // handle of the "main" session group.  	std::string mChannelName;			// Name of the channel to be looked up  -	bool mAreaVoiceDisabled;      sessionStatePtr_t mAudioSession;		// Session state for the current audio session  	bool mAudioSessionChanged;			// set to true when the above pointer gets changed, so observers can be notified. diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index fe4c4129c5..6e0cd32249 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -40,10 +40,10 @@  # include "expat/expat.h"  #endif  #include "llcallbacklist.h" -#include "llviewernetwork.h"		// for gGridChoice +#include "llviewernetwork.h"        // for gGridChoice  #include "llbase64.h"  #include "llviewercontrol.h" -#include "llappviewer.h"	// for gDisconnected, gDisableVoice +#include "llappviewer.h"    // for gDisconnected, gDisableVoice  #include "llprocess.h"  // Viewer includes @@ -78,12 +78,6 @@  #include "json/reader.h"  #include "json/writer.h" -#define USE_SESSION_GROUPS 0 -#define VX_NULL_POSITION -2147483648.0 /*The Silence*/ - -extern LLMenuBarGL* gMenuBarView; -extern void handle_voice_morphing_subscribe(); -  const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc";  namespace { @@ -92,51 +86,19 @@ namespace {      const F32 VOLUME_SCALE_WEBRTC = 0.01f;      const F32 LEVEL_SCALE_WEBRTC  = 0.008f; -    const F32 SPEAKING_TIMEOUT = 1.f;      const F32 SPEAKING_AUDIO_LEVEL = 0.40; -    static const std::string VISIBLE_VOICE_SERVER_TYPE = "WebRTC"; +    static const std::string REPORTED_VOICE_SERVER_TYPE = "Secondlife WebRTC Gateway";      // Don't send positional updates more frequently than this:      const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - -    // Timeout for connection to WebRTC  -    const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; -    const F32 CONNECT_DNS_TIMEOUT = 5.0f; - -    const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; -    const S32 PROVISION_WAIT_TIMEOUT_SEC = 5;      // Cosine of a "trivially" small angle      const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f);      const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); -    // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() -    // which is treated as normal. The is the number of frames to wait for a channel join before giving up.  This was changed  -    // from the original count of 50 for two reason.  Modern PCs have higher frame rates and sometimes the SLVoice process  -    // backs up processing join requests.  There is a log statement that records when channel joins take longer than 100 frames. -    const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500; - -    // How often to check for expired voice fonts in seconds -    const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; -    // Time of day at which WebRTC expires voice font subscriptions. -    // Used to replace the time portion of received expiry timestamps. -    static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; - -    // Maximum length of capture buffer recordings in seconds. -    const F32 CAPTURE_BUFFER_MAX_TIME = 10.f;  }  // namespace -float LLWebRTCVoiceClient::getAudioLevel() -{ -    if (mIsInTuningMode) -    { -        return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; -    } -    else -    { -        return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; -    } -} +  /////////////////////////////////////////////////////////////////////////////////////////////// @@ -233,83 +195,41 @@ LLSD LLVoiceWebRTCStats::read()  ///////////////////////////////////////////////////////////////////////////////////////////////  bool LLWebRTCVoiceClient::sShuttingDown = false; -bool LLWebRTCVoiceClient::sConnected = false; -LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr;  LLWebRTCVoiceClient::LLWebRTCVoiceClient() : -    mRelogRequested(false), -    mSpatialJoiningNum(0), - +    mHidden(false),      mTuningMode(false),      mTuningMicGain(0.0), -    mTuningSpeakerVolume(50),  // Set to 50 so the user can hear himself when he sets his mic volume -    mTuningSpeakerVolumeDirty(true), +    mTuningSpeakerVolume(50),  // Set to 50 so the user can hear themselves when he sets his mic volume      mDevicesListUpdated(false), -    mAreaVoiceDisabled(false), -    mSession(),  // TBD - should be NULL -    mNextSession(), - -    mCurrentParcelLocalID(0), - -    mBuddyListMapPopulated(false), -    mBlockRulesListReceived(false), -    mAutoAcceptRulesListReceived(false), -      mSpatialCoordsDirty(false), -    mIsInitialized(false),      mMuteMic(false), -    mMuteMicDirty(false), -    mFriendsListDirty(true),      mEarLocation(0), -    mSpeakerVolumeDirty(true),      mMicGain(0.0),      mVoiceEnabled(false),      mProcessChannels(false), -    mShutdownComplete(true), -    mPlayRequestCount(0), -      mAvatarNameCacheConnection(),      mIsInTuningMode(false), -    mIsJoiningSession(false), -    mIsWaitingForFonts(false), -    mIsLoggingIn(false),      mIsProcessingChannels(false),      mIsCoroutineActive(false),      mWebRTCPump("WebRTCClientPump"),      mWebRTCDeviceInterface(nullptr)  {      sShuttingDown = false; -    sConnected = false; -    sPump = nullptr; -	mSpeakerVolume = 0.0; +    mSpeakerVolume = 0.0; -	mVoiceVersion.serverVersion = ""; -	mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; +    mVoiceVersion.serverVersion = ""; +    mVoiceVersion.voiceServerType = REPORTED_VOICE_SERVER_TYPE;      mVoiceVersion.internalVoiceServerType = WEBRTC_VOICE_SERVER_TYPE;      mVoiceVersion.minorVersion = 0;      mVoiceVersion.majorVersion = 2;      mVoiceVersion.mBuildVersion = ""; -	 -#if LL_DARWIN || LL_LINUX -		// HACK: THIS DOES NOT BELONG HERE -		// When the WebRTC daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. -		// This should cause us to ignore SIGPIPE and handle the error through proper channels. -		// This should really be set up elsewhere.  Where should it go? -		signal(SIGPIPE, SIG_IGN); -		 -		// Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. -		// Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that. -		signal(SIGCHLD, SIG_IGN); -#endif - - -	gIdleCallbacks.addFunction(idle, this);  }  //--------------------------------------------------- @@ -328,10 +248,6 @@ LLWebRTCVoiceClient::~LLWebRTCVoiceClient()  void LLWebRTCVoiceClient::init(LLPumpIO* pump)  {      // constructor will set up LLVoiceClient::getInstance() -    sPump = pump; - -    //     LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", -    //         boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance()));      llwebrtc::init();      mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); @@ -345,12 +261,10 @@ void LLWebRTCVoiceClient::terminate()          return;      } -    mRelogRequested = false;      mVoiceEnabled = false;      llwebrtc::terminate();      sShuttingDown = true; -    sPump = NULL;  }  //--------------------------------------------------- @@ -361,7 +275,7 @@ void LLWebRTCVoiceClient::cleanUp()      mSession.reset();      mNeighboringRegions.clear();      sessionState::for_each(boost::bind(predShutdownSession, _1)); -    LL_DEBUGS("Voice") << "exiting" << LL_ENDL; +    LL_DEBUGS("Voice") << "Exiting" << LL_ENDL;  }  //--------------------------------------------------- @@ -386,177 +300,96 @@ void LLWebRTCVoiceClient::updateSettings()      setMicGain(mic_level);  } +// Observers +void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver *observer) +{  +    mParticipantObservers.insert(observer); +} -///////////////////////////// -// session control messages +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver *observer) +{  +    mParticipantObservers.erase(observer); +} -void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, const LLUUID& regionID) +void LLWebRTCVoiceClient::notifyParticipantObservers()  { -    if (gAgent.getRegion()->getRegionID() == regionID) +    for (observer_set_t::iterator it = mParticipantObservers.begin(); it != mParticipantObservers.end();)      { -        if (mNextSession && mNextSession->mChannelID == channelID) -        { -            LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); -        } -        else if (mSession && mSession->mChannelID == channelID) -        { -            LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); -        } +        LLVoiceClientParticipantObserver *observer = *it; +        observer->onParticipantsChanged(); +        // In case onParticipantsChanged() deleted an entry. +        it = mParticipantObservers.upper_bound(observer);      }  } -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID) -{ -    if (gAgent.getRegion()->getRegionID() == regionID) -    { -        if (mNextSession && mNextSession->mChannelID == channelID) -        { -            if (mSession) -            { -                mSession->shutdownAllConnections(); -            } -            mSession = mNextSession; -            mNextSession.reset(); -            mSession->addParticipant(gAgentID); -        } -         -        if (mSession && mSession->mChannelID == channelID) -        { -            LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); -            if (!mSession->mNotifyOnFirstJoin) -            { -                LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); -            } -        } -    } +void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver *observer) +{  +    mStatusObservers.insert(observer);  } -void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) -{ -    if (gAgent.getRegion()->getRegionID() == regionID) -    { -        if (mSession && mSession->mChannelID == channelID) -        { -            //LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); -        } -    } +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver *observer) +{  +    mStatusObservers.erase(observer);  } -void LLWebRTCVoiceClient::idle(void* user_data) +void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)  { -} +    LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" +                       << " mSession=" << mSession << LL_ENDL; -//========================================================================= -// the following are methods to support the coroutine implementation of the  -// voice connection and processing.  They should only be called in the context  -// of a coroutine. -//  -//  +    LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) << ", session channelInfo " +                       << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL; -void LLWebRTCVoiceClient::sessionState::processSessionStates() -{ +    mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; -    auto iter = mSessions.begin(); -    while (iter != mSessions.end()) +    LLSD channelInfo = getAudioSessionChannelInfo(); +    for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end();)      { -        if (!iter->second->processConnectionStates() && iter->second->mShuttingDown) -        { -            iter = mSessions.erase(iter); -        } -        else -        { -            iter++; -        } +        LLVoiceClientStatusObserver *observer = *it; +        observer->onChange(status, channelInfo, inSpatialChannel()); +        // In case onError() deleted an entry. +        it = mStatusObservers.upper_bound(observer);      } -} -bool LLWebRTCVoiceClient::sessionState::processConnectionStates() -{ -    std::list<connectionPtr_t>::iterator iter = mWebRTCConnections.begin(); -    while (iter != mWebRTCConnections.end()) +    // skipped to avoid speak button blinking +    if (status != LLVoiceClientStatusObserver::STATUS_JOINING &&  +        status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL && +        status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED)      { -        if (!iter->get()->connectionStateMachine()) -        { -            iter = mWebRTCConnections.erase(iter); -        } -        else +        bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + +        gAgent.setVoiceConnected(voice_status); + +        if (voice_status)          { -            ++iter; +            LLFirstUse::speak(true);          }      } -    return !mWebRTCConnections.empty();  } - -LLWebRTCVoiceClient::estateSessionState::estateSessionState() -{ -    mHangupOnLastLeave = false; -    mNotifyOnFirstJoin = false; -    mChannelID       = "Estate"; -    LLUUID region_id = gAgent.getRegion()->getRegionID(); - -    mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, INVALID_PARCEL_ID, "Estate")); -} - -LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) -{ -    mHangupOnLastLeave = false; -    mNotifyOnFirstJoin = false; -    LLUUID region_id = gAgent.getRegion()->getRegionID(); -    mChannelID       = channelID; -    mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); -} - -LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, -                                                          const std::string& credentials, -                                                          bool notify_on_first_join, -                                                          bool hangup_on_last_leave) : -    mCredentials(credentials) -{ -    mHangupOnLastLeave = hangup_on_last_leave; -    mNotifyOnFirstJoin = notify_on_first_join; -    LLUUID region_id = gAgent.getRegion()->getRegionID(); -    mChannelID = channelID; -    mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); +void LLWebRTCVoiceClient::addObserver(LLFriendObserver *observer) +{   } -bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() -{ - -    if (!mShuttingDown) -    { -        // Estate voice requires connection to neighboring regions. -        std::set<LLUUID> neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); - -        for (auto &connection : mWebRTCConnections) -        { -            boost::shared_ptr<LLVoiceWebRTCSpatialConnection> spatialConnection = -                boost::static_pointer_cast<LLVoiceWebRTCSpatialConnection>(connection); - -            LLUUID regionID = spatialConnection.get()->getRegionID(); - -            if (neighbor_ids.find(regionID) == neighbor_ids.end()) -            { -                // shut down connections to neighbors that are too far away. -                spatialConnection.get()->shutDown(); -            } -            neighbor_ids.erase(regionID); -        } - -        // add new connections for new neighbors -        for (auto &neighbor : neighbor_ids) -        { -            connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); - -            mWebRTCConnections.push_back(connection); -            connection->setMicGain(mMicGain); -            connection->setMuteMic(mMuted); -            connection->setSpeakerVolume(mSpeakerVolume); -        } -    } -    return LLWebRTCVoiceClient::sessionState::processConnectionStates(); +void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) +{   } +//--------------------------------------------------- +// Primary voice loop. +// This voice loop is called every 100ms plus the time it +// takes to process the various functions called in the loop +// The loop does the following: +// * gates whether we do channel processing depending on +//   whether we're running a WebRTC voice channel or  +//   one from another voice provider. +// * If in spatial voice, it determines whether we've changed +//   parcels, whether region/parcel voice settings have changed, +//   etc. and manages whether the voice channel needs to change. +// * calls the state machines for the sessions to negotiate  +//   connection to various voice channels. +// * Sends updates to the voice server when this agent's +//   voice levels, or positions have changed.  void LLWebRTCVoiceClient::voiceConnectionCoro()  {      LL_DEBUGS("Voice") << "starting" << LL_ENDL; @@ -567,6 +400,13 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()          LLMuteList::getInstance()->addObserver(this);          while (!sShuttingDown)          { +            // TODO: Doing some measurement and calculation here, +            // we could reduce the timeout to take into account the +            // time spent on the previous loop to have the loop +            // cycle at exactly 100ms, instead of 100ms + loop +            // execution time. +            // Could help with voice updates making for smoother +            // voice when we're busy.              llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);              bool voiceEnabled = mVoiceEnabled; @@ -575,7 +415,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()                  continue;              } -    		LLViewerRegion *regionp = gAgent.getRegion(); +            LLViewerRegion *regionp = gAgent.getRegion();              if (!regionp)              {                  continue; @@ -590,14 +430,14 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()              }              else if (inSpatialChannel())              { +                bool useEstateVoice = true;                  // add session for region or parcel voice.                  if (!regionp || regionp->getRegionID().isNull())                  { +                    // no region, no voice.                      continue;                  } -                updatePosition(); -                  voiceEnabled = voiceEnabled && regionp->isVoiceEnabled();                  if (voiceEnabled) @@ -613,24 +453,18 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()                          }                          else if (!parcel->getParcelFlagUseEstateVoiceChannel())                          { -                            S32 parcel_local_id = parcel->getLocalID(); -                            std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); +                            // use the parcel-specific voice channel. +                            S32         parcel_local_id = parcel->getLocalID(); +                            std::string channelID       = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); +                            useEstateVoice = false;                              if (!inOrJoiningChannel(channelID))                              {                                  startParcelSession(channelID, parcel_local_id);                              }                          } -                        else  -                        { -                            // parcel using estate voice -                            if (!inEstateChannel()) -                            { -                                startEstateSession(); -                            } -                        }                      } -                    else +                    if (useEstateVoice && !inEstateChannel())                      {                          // estate voice                          if (!inEstateChannel()) @@ -644,6 +478,12 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()                      // voice is disabled, so leave and disable PTT                      leaveChannel(true);                  } +                else +                { +                    // we're in spatial voice, and voice is enabled, so determine positions in order +                    // to send position updates. +                    updatePosition(); +                }              }              sessionState::processSessionStates(); @@ -674,37 +514,68 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()      cleanUp();  } +// For spatial, determine which neighboring regions to connect to +// for cross-region voice. +void LLWebRTCVoiceClient::updateNeighboringRegions() +{ +    static const std::vector<LLVector3d> neighbors {LLVector3d(0.0f, 1.0f, 0.0f),  LLVector3d(0.707f, 0.707f, 0.0f), +                                                    LLVector3d(1.0f, 0.0f, 0.0f),  LLVector3d(0.707f, -0.707f, 0.0f), +                                                    LLVector3d(0.0f, -1.0f, 0.0f), LLVector3d(-0.707f, -0.707f, 0.0f), +                                                    LLVector3d(-1.0f, 0.0f, 0.0f), LLVector3d(-0.707f, 0.707f, 0.0f)}; + +    // Estate voice requires connection to neighboring regions. +    mNeighboringRegions.clear(); -//========================================================================= +    mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); + +    // base off of speaker position as it'll move more slowly than camera position. +    // Once we have hysteresis, we may be able to track off of speaker and camera position at 50m +    // TODO: Add hysteresis so we don't flip-flop connections to neighbors +    LLVector3d speaker_pos = LLWebRTCVoiceClient::getInstance()->getSpeakerPosition(); +    for (auto &neighbor_pos : neighbors) +    { +        // include every region within 100m (2*MAX_AUDIO_DIST) to deal witht he fact that the camera +        // can stray 50m away from the avatar. +        LLViewerRegion *neighbor = LLWorld::instance().getRegionFromPosGlobal(speaker_pos + 2 * MAX_AUDIO_DIST * neighbor_pos); +        if (neighbor && !neighbor->getRegionID().isNull()) +        { +            mNeighboringRegions.insert(neighbor->getRegionID()); +        } +    } +} +//========================================================================= +// shut down the current audio session to make room for the next one.  void LLWebRTCVoiceClient::leaveAudioSession()  { -	if(mSession) -	{ -		LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; +    if(mSession) +    { +        LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL;          mSession->shutdownAllConnections(); -	} -	else -	{ -		LL_WARNS("Voice") << "called with no active session" << LL_ENDL; -	} +    } +    else +    { +        LL_WARNS("Voice") << "called with no active session" << LL_ENDL; +    }  } +//========================================================================= +// Device Management  void LLWebRTCVoiceClient::clearCaptureDevices()  { -	LL_DEBUGS("Voice") << "called" << LL_ENDL; -	mCaptureDevices.clear(); +    LL_DEBUGS("Voice") << "called" << LL_ENDL; +    mCaptureDevices.clear();  }  void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device)  { -	LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; +    LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL;      mCaptureDevices.push_back(device);  }  LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices()  { -	return mCaptureDevices; +    return mCaptureDevices;  }  void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) @@ -713,7 +584,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name)  }  void LLWebRTCVoiceClient::setDevicesListUpdated(bool state)  { -	mDevicesListUpdated = state; +    mDevicesListUpdated = state;  }  void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, @@ -758,21 +629,21 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi  }  void LLWebRTCVoiceClient::clearRenderDevices() -{	 -	LL_DEBUGS("Voice") << "called" << LL_ENDL; -	mRenderDevices.clear(); +{     +    LL_DEBUGS("Voice") << "called" << LL_ENDL; +    mRenderDevices.clear();  }  void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device)  { -	LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; +    LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL;      mRenderDevices.push_back(device);  }  LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices()  { -	return mRenderDevices; +    return mRenderDevices;  }  void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) @@ -791,7 +662,7 @@ void LLWebRTCVoiceClient::tuningStart()  void LLWebRTCVoiceClient::tuningStop()  {  -	if (mIsInTuningMode) +    if (mIsInTuningMode)      {          mWebRTCDeviceInterface->setTuningMode(false);          mIsInTuningMode = false; @@ -809,15 +680,25 @@ void LLWebRTCVoiceClient::tuningSetMicVolume(float volume)  }  void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{	 +{     -	if (volume != mTuningSpeakerVolume) -	{ -        mTuningSpeakerVolume      = volume; -		mTuningSpeakerVolumeDirty = true; -	} +    if (volume != mTuningSpeakerVolume) +    { +        mTuningSpeakerVolume = volume; +    }  } +float LLWebRTCVoiceClient::getAudioLevel() +{ +    if (mIsInTuningMode) +    { +        return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; +    } +    else +    { +        return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; +    } +}  float LLWebRTCVoiceClient::tuningGetEnergy(void)  { @@ -826,52 +707,257 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void)  bool LLWebRTCVoiceClient::deviceSettingsAvailable()  { -	bool result = true; -	 -	if(mRenderDevices.empty() || mCaptureDevices.empty()) -		result = false; -	 -	return result; +    bool result = true; +     +    if(mRenderDevices.empty() || mCaptureDevices.empty()) +        result = false; +     +    return result;  }  bool LLWebRTCVoiceClient::deviceSettingsUpdated()  {      bool updated = mDevicesListUpdated; -	mDevicesListUpdated = false; -	return updated;		 +    mDevicesListUpdated = false; +    return updated;          }  void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList)  { -	if(clearCurrentList) -	{ -		clearCaptureDevices(); -		clearRenderDevices(); -	} +    if(clearCurrentList) +    { +        clearCaptureDevices(); +        clearRenderDevices(); +    }      mWebRTCDeviceInterface->refreshDevices();  } -void LLWebRTCVoiceClient::giveUp() -{ -	// All has failed.  Clean up and stop trying. -    LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; -	cleanUp(); -}  void LLWebRTCVoiceClient::setHidden(bool hidden)  {      mHidden = hidden; -    if (mHidden && inSpatialChannel()) +    if (inSpatialChannel())      { -        // get out of the channel entirely  -        leaveAudioSession(); +        if (mHidden) +        { +            // get out of the channel entirely +            leaveAudioSession(); +        } +        else +        { +            updatePosition(); +            sendPositionUpdate(true); +        }      } -    else +} + +///////////////////////////// +// session control messages. +// +// these are called by the sessions to report +// status for a given channel.  By filtering +// on channel and region, these functions +// can send various notifications to +// other parts of the viewer, as well as  +// managing housekeeping + +// A connection to a channel was successfully established, +// so shut down the current session and move on to the next +// if one is available. +// if the current session is the one that was established, +// notify the observers. +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID, const LLUUID ®ionID) +{ +    if (gAgent.getRegion()->getRegionID() == regionID)      { -        sendPositionUpdate(true); +        if (mNextSession && mNextSession->mChannelID == channelID) +        { +            if (mSession) +            { +                mSession->shutdownAllConnections(); +            } +            mSession = mNextSession; +            mNextSession.reset(); + +            // Add ourselves as a participant. +            mSession->addParticipant(gAgentID); +        } + +        // The current session was established. +        if (mSession && mSession->mChannelID == channelID) +        { +            LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + +            // only set status to joined if asked to.  This will happen in the case where we're not +            // doing an ad-hoc based p2p session. Those sessions expect a STATUS_JOINED when the peer +            // has, in fact, joined, which we detect elsewhere. +            if (!mSession->mNotifyOnFirstJoin) +            { +                LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); +            } +        }      }  } +void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) +{ +    if (gAgent.getRegion()->getRegionID() == regionID) +    { +        if (mSession && mSession->mChannelID == channelID) +        { +            LL_DEBUGS("Voice") << "Main WebRTC Connection Shut Down." << LL_ENDL; +        } +    } +} +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string &channelID, const LLUUID ®ionID) +{ +    LL_DEBUGS("Voice") << "A connection failed.  channel:" << channelID << LL_ENDL; +    if (gAgent.getRegion()->getRegionID() == regionID) +    { +        if (mNextSession && mNextSession->mChannelID == channelID) +        { +            LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); +        } +        else if (mSession && mSession->mChannelID == channelID) +        { +            LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); +        } +    } +} + +// ----------------------------------------------------------- +// positional functionality. +void LLWebRTCVoiceClient::setEarLocation(S32 loc) +{ +    if (mEarLocation != loc) +    { +        LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; + +        mEarLocation        = loc; +        mSpatialCoordsDirty = true; +    } +} + +void LLWebRTCVoiceClient::updatePosition(void) +{ +    LLViewerRegion *region = gAgent.getRegion(); +    if (region && isAgentAvatarValid()) +    { +        // get the avatar position. +        LLVector3d   avatar_pos  = gAgentAvatarp->getPositionGlobal(); +        LLQuaternion avatar_qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); + +        avatar_pos += LLVector3d(0.f, 0.f, 1.f);  // bump it up to head height + +        LLVector3d   earPosition; +        LLQuaternion earRot; +        switch (mEarLocation) +        { +            case earLocCamera: +            default: +                earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); +                earRot      = LLViewerCamera::getInstance()->getQuaternion(); +                break; + +            case earLocAvatar: +                earPosition = mAvatarPosition; +                earRot      = mAvatarRot; +                break; + +            case earLocMixed: +                earPosition = mAvatarPosition; +                earRot      = LLViewerCamera::getInstance()->getQuaternion(); +                break; +        } +        setListenerPosition(earPosition,      // position +                            LLVector3::zero,  // velocity +                            earRot);          // rotation matrix + +        setAvatarPosition(avatar_pos,       // position +                          LLVector3::zero,  // velocity +                          avatar_qrot);     // rotation matrix + +        enforceTether(); + +        updateNeighboringRegions(); +    } +} + +void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) +{ +    mListenerRequestedPosition = position; + +    if (mListenerVelocity != velocity) +    { +        mListenerVelocity   = velocity; +        mSpatialCoordsDirty = true; +    } + +    if (mListenerRot != rot) +    { +        mListenerRot        = rot; +        mSpatialCoordsDirty = true; +    } +} + +void LLWebRTCVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) +{ +    if (dist_vec_squared(mAvatarPosition, position) > 0.01) +    { +        mAvatarPosition     = position; +        mSpatialCoordsDirty = true; +    } + +    if (mAvatarVelocity != velocity) +    { +        mAvatarVelocity     = velocity; +        mSpatialCoordsDirty = true; +    } + +    // If the two rotations are not exactly equal test their dot product +    // to get the cos of the angle between them. +    // If it is too small, don't update. +    F32 rot_cos_diff = llabs(dot(mAvatarRot, rot)); +    if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS)) +    { +        mAvatarRot          = rot; +        mSpatialCoordsDirty = true; +    } +} + +// The listener (camera) must be within 50m of the +// avatar.  Enforce it on the client. +// This will also be enforced on the voice server +// based on position sent from the simulator to the +// voice server. +void LLWebRTCVoiceClient::enforceTether() +{ +    LLVector3d tethered = mListenerRequestedPosition; + +    // constrain 'tethered' to within 50m of mAvatarPosition. +    { +        LLVector3d camera_offset   = mListenerRequestedPosition - mAvatarPosition; +        F32        camera_distance = (F32) camera_offset.magVec(); +        if (camera_distance > MAX_AUDIO_DIST) +        { +            tethered = mAvatarPosition + (MAX_AUDIO_DIST / camera_distance) * camera_offset; +        } +    } + +    if (dist_vec_squared(mListenerPosition, tethered) > 0.01) +    { +        mListenerPosition   = tethered; +        mSpatialCoordsDirty = true; +    } +} + +// We send our position via a WebRTC data channel to the WebRTC +// server for fine-grained, low latency updates.  On the server, +// these updates will be 'tethered' to the actual position of the avatar. +// Those updates are higher latency, however. +// This mechanism gives low latency spatial updates and server-enforced +// prevention of 'evesdropping' by sending camera updates beyond the +// standard 50m  void LLWebRTCVoiceClient::sendPositionUpdate(bool force)  {      Json::FastWriter writer; @@ -908,6 +994,9 @@ void LLWebRTCVoiceClient::sendPositionUpdate(bool force)      }  } +// Update our own volume on our participant, so it'll show up +// in the UI.  This is done on all sessions, so switching +// sessions retains consistent volume levels.  void LLWebRTCVoiceClient::updateOwnVolume() {       F32 audio_level = 0.0;      if (!mMuteMic && !mTuningMode) @@ -918,359 +1007,196 @@ void LLWebRTCVoiceClient::updateOwnVolume() {      sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level));   } -void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level) -{ -    participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); -    if (participant) -    { -        participant->mLevel      = audio_level; -        participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; -    } -} +//////////////////////////////////// +// Managing list of participants -void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data) -{ -    if (session->isSpatial() && !spatial_data.empty()) -    { -        session->sendData(spatial_data); -    } -} +// Provider-level participant management -void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) +BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id)  { -    for (auto& connection : mWebRTCConnections) -    { -        connection->sendData(data); -    } +    // WebRTC participants are always SL avatars. +    return TRUE;  } -void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) +void LLWebRTCVoiceClient::getParticipantList(std::set<LLUUID> &participants)  { -    mMuted = muted; -    for (auto& connection : mWebRTCConnections) +    if (mSession)      { -        connection->setMuteMic(muted); +        for (participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin();  +            iter != mSession->mParticipantsByUUID.end(); +            iter++) +        { +            participants.insert(iter->first); +        }      }  } -void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) +bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id)  { -    mMicGain = gain; -    for (auto& connection : mWebRTCConnections) +    if (mSession)      { -        connection->setMicGain(gain); +        return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end());      } +    return false;  } -void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) +// protected provider-level participant management. +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string &channelID, const LLUUID &id)  { -    mSpeakerVolume = volume; -    for (auto& connection : mWebRTCConnections) +    participantStatePtr_t result; +    LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); + +    if (session)      { -        connection->setSpeakerVolume(volume); +        result = session->findParticipantByID(id);      } + +    return result;  } -void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volume) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string &channelID, const LLUUID &id)  { -    if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) -    { -        return; -    } -    for (auto& connection : mWebRTCConnections) +    participantStatePtr_t result; +    LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); +    if (session)      { -        connection->setUserVolume(id, volume); +        result = session->addParticipant(id); +        if (session->mNotifyOnFirstJoin && (id != gAgentID)) +        { +            notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); +        }      } +    return result;  } -void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID& id, bool mute) +void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id)  { -    if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) -    { -        return; -    } -    for (auto& connection : mWebRTCConnections) +    participantStatePtr_t result; +    LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); +    if (session)      { -        connection->setUserMute(id, mute); +        participantStatePtr_t participant = session->findParticipantByID(id); +        if (participant) +        { +            session->removeParticipant(participant); +            if (session->mHangupOnLastLeave && (id != gAgentID) && (session->mParticipantsByUUID.size() <= 1)) +            { +                notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); +            } +        }      }  } -void LLWebRTCVoiceClient::sendLocalAudioUpdates() -{ -} - -void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) -{ -	if(session) -	{		 -		if(session == mSession) -		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the current session)" << LL_ENDL; -		} -		else if(session == mNextSession) -		{ -            LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the next session)" << LL_ENDL; -		} -		else -		{ -			// We don't have a reason to keep tracking this session, so just delete it. -            LL_DEBUGS("Voice") << "deleting session " << session->mChannelID << LL_ENDL; -			deleteSession(session); -		}	 -	} -	else -	{ -//		LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; -	} -} - -void LLWebRTCVoiceClient::muteListChanged() -{ -	// The user's mute list has been updated.  Go through the current participant list and sync it with the mute list. -	if(mSession) -	{ -		participantMap::iterator iter = mSession->mParticipantsByURI.begin(); -		 -		for(; iter != mSession->mParticipantsByURI.end(); iter++) -		{ -			participantStatePtr_t p(iter->second); -			 -			// Check to see if this participant is on the mute list already -			if(p->updateMuteState()) -				mSession->mVolumeDirty = true; -		} -	} -} - -///////////////////////////// -// Managing list of participants +//  participantState level participant management  LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) :  -	 mURI(agent_id.asString()), +     mURI(agent_id.asString()),       mAvatarID(agent_id), -	 mPTT(false),  -	 mIsSpeaking(false),  -	 mIsModeratorMuted(false),  -	 mLastSpokeTimestamp(0.f),  -	 mLevel(0.f),  -	 mVolume(LLVoiceClient::VOLUME_DEFAULT),  -	 mUserVolume(0), -	 mOnMuteList(false),  -	 mVolumeSet(false), -	 mVolumeDirty(false),  -	 mAvatarIDValid(false), -	 mIsSelf(false) +     mIsSpeaking(false),  +     mIsModeratorMuted(false),  +     mLevel(0.f),  +     mVolume(LLVoiceClient::VOLUME_DEFAULT),  +     mOnMuteList(false)  {  }  LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id)  {      participantStatePtr_t result; -	 +          participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); - -	if (iter != mParticipantsByUUID.end()) -	{ -		result = iter->second; -	} -		 -	if(!result) -	{ -		// participant isn't already in one list or the other. -		result.reset(new participantState(agent_id)); -        mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); +    if (iter != mParticipantsByUUID.end()) +    { +        result = iter->second; +    } +         +    if(!result) +    { +        // participant isn't already in one list or the other. +        result.reset(new participantState(agent_id));          mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); - -		result->mAvatarIDValid = true;          result->mAvatarID      = agent_id; -		 -        if(result->updateMuteState()) -        { -	        mMuteDirty = true; -        } -		if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) -		{ -			result->mVolumeDirty = true; -			mVolumeDirty = true; -		} +        LLWebRTCVoiceClient::getInstance()->lookupName(agent_id); +         +        result->updateMuteState(); + +        LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume);          if (!LLWebRTCVoiceClient::sShuttingDown)          {              LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers();          } -		 -		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; -	} -	 -	return result; +         +        LL_DEBUGS("Voice") << "Participant \"" << result->mURI << "\" added." << LL_ENDL; +    } +     +    return result;  }  bool LLWebRTCVoiceClient::participantState::updateMuteState()  { -	bool result = false; - -	bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); -	if(mOnMuteList != isMuted) -	{ -	    mOnMuteList = isMuted; -	    mVolumeDirty = true; -	    result = true; -	} -	return result; -} - -bool LLWebRTCVoiceClient::participantState::isAvatar() -{ -	return mAvatarIDValid; -} - -void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant) -{ -	if(participant) -	{ -		participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); -		participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); -		 -		LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; -		 -		if(iter == mParticipantsByURI.end()) -		{ -			LL_WARNS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; -		} -		else if(iter2 == mParticipantsByUUID.end()) -		{ -			LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; -		} -		else if(iter->second != iter2->second) -		{ -			LL_WARNS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; -		} -		else -		{ -			mParticipantsByURI.erase(iter); -			mParticipantsByUUID.erase(iter2); -            if (!LLWebRTCVoiceClient::sShuttingDown) -            { -                LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); -            } -		} -	} -} - -void LLWebRTCVoiceClient::sessionState::removeAllParticipants() -{ -	LL_DEBUGS("Voice") << "called" << LL_ENDL; +    bool result = false; -	while(!mParticipantsByURI.empty()) -	{ -		removeParticipant(mParticipantsByURI.begin()->second); -	} -	 -	if(!mParticipantsByUUID.empty()) -	{ -		LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; -	} -} - - -void LLWebRTCVoiceClient::getParticipantList(std::set<LLUUID> &participants) -{ -	if(mSession) -	{ -		for(participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); -			iter != mSession->mParticipantsByUUID.end();  -			iter++) -		{ -			participants.insert(iter->first); -		} -	} -} - -bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) -{ -  if(mSession) +    bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); +    if(mOnMuteList != isMuted)      { -      return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end()); +        mOnMuteList = isMuted; +        result = true;      } -  return false; +    return result;  } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipant(const std::string &uri) -{ -    participantStatePtr_t result; -	 -	participantMap::iterator iter = mParticipantsByURI.find(uri); -	if(iter != mParticipantsByURI.end()) -	{ -		result = iter->second; -	} -		 -	return result; -} +// session-level participant management  LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipantByID(const LLUUID& id)  {      participantStatePtr_t result; -	participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); +    participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); -	if(iter != mParticipantsByUUID.end()) -	{ -		result = iter->second; -	} +    if(iter != mParticipantsByUUID.end()) +    { +        result = iter->second; +    } -	return result; +    return result;  } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string& channelID, const LLUUID& id) +void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant)  { -    participantStatePtr_t result; -    auto session = sessionState::matchSessionByChannelID(channelID); -	 -	if (session) -	{ -        result = session->findParticipantByID(id); -	} -	 -	return result; -} - -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string& channelID, const LLUUID &id) -{  -	participantStatePtr_t result; -    auto session = sessionState::matchSessionByChannelID(channelID); -    if (session) +    if (participant)      { -        result = session->addParticipant(id); -        if (session->mNotifyOnFirstJoin && (id != gAgentID)) +        participantUUIDMap::iterator iter = mParticipantsByUUID.find(participant->mAvatarID); + +        LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; + +        if (iter == mParticipantsByUUID.end())          { -            notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); +            LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; +        } +        else +        { +            mParticipantsByUUID.erase(iter); +            if (!LLWebRTCVoiceClient::sShuttingDown) +            { +                LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); +            }          }      } -    return result;  } -void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id) +void LLWebRTCVoiceClient::sessionState::removeAllParticipants()  { -    participantStatePtr_t result; -    auto session = sessionState::matchSessionByChannelID(channelID); -    if (session) +    LL_DEBUGS("Voice") << "called" << LL_ENDL; + +    while (!mParticipantsByUUID.empty())      { -        participantStatePtr_t participant = session->findParticipantByID(id); -        if (participant) -        { -            session->removeParticipant(participant); -            if (session->mHangupOnLastLeave && -                (id != gAgentID) && -                (session->mParticipantsByURI.size() <= 1)) -            { -                notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); -            } -        } +        removeParticipant(mParticipantsByUUID.begin()->second);      }  } +// Initiated the various types of sessions.  bool LLWebRTCVoiceClient::startEstateSession()  {      leaveChannel(false); @@ -1291,75 +1217,40 @@ bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool notify      LL_WARNS("Voice") << "Start AdHoc Session " << channelInfo << LL_ENDL;      std::string channelID = channelInfo["channel_uri"];      std::string credentials = channelInfo["channel_credentials"]; -    mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID,  -                                                                                   credentials, -                                                                                   notify_on_first_join, -                                                                                   hangup_on_last_leave))); +    mNextSession = addSession(channelID, +                              sessionState::ptr_t(new adhocSessionState(channelID,  +                                                                        credentials, +                                                                        notify_on_first_join, +                                                                        hangup_on_last_leave)));      return true;  }  bool LLWebRTCVoiceClient::isVoiceWorking() const  { - -    //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) -    // Condition with joining spatial num was added to take into account possible problems with connection to voice -    // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. -    return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && mIsProcessingChannels; -//  return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); -} - -// Returns true if the indicated participant in the current audio session is really an SL avatar. -// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. -BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) -{ -	BOOL result = TRUE;  -	return result; +    return mIsProcessingChannels;  }  // Returns true if calling back the session URI after the session has closed is possible. -// Currently this will be false only for PSTN P2P calls.		 +// Currently this will be false only for PSTN P2P calls.          BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)  { -	BOOL result = TRUE;       sessionStatePtr_t session(findP2PSession(session_id)); -	 -	if(session != NULL) -	{ -		result = session->isCallBackPossible(); -	} -	 -	return result; -} - -// Returns true if the session can accept text IM's. -// Currently this will be false only for PSTN P2P calls. -BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) -{ -	bool result = TRUE; -    sessionStatePtr_t session(findP2PSession(session_id)); -	 -	if(session != NULL) -	{ -		result = session->isTextIMPossible(); -	} -	 -	return result; +    return session && session->isCallbackPossible() ? TRUE : FALSE;  } +// Channel Management  void LLWebRTCVoiceClient::leaveNonSpatialChannel()  { -    LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; +    LL_DEBUGS("Voice") << "Request to leave non-spatial channel." << LL_ENDL; -	// Make sure we don't rejoin the current session.	 -    sessionStatePtr_t oldNextSession(mNextSession); -	mNextSession.reset(); -	 -	// Most likely this will still be the current session at this point, but check it anyway. -	reapSession(oldNextSession); -	 -	leaveChannel(true); +    // make sure we're not simply rejoining the current session +    deleteSession(mNextSession); +     +    leaveChannel(true);  } +// determine whether we're processing channels, or whether +// another voice provider is.  void LLWebRTCVoiceClient::processChannels(bool process)  {       mProcessChannels = process; @@ -1367,81 +1258,7 @@ void LLWebRTCVoiceClient::processChannels(bool process)  bool LLWebRTCVoiceClient::inProximalChannel()  { -	return inSpatialChannel(); -} - -std::string LLWebRTCVoiceClient::nameFromID(const LLUUID &uuid) -{ -	std::string result; -	 -	if (uuid.isNull()) { -		//WebRTC, the uuid emtpy look for the mURIString and return that instead. -		//result.assign(uuid.mURIStringName); -		LLStringUtil::replaceChar(result, '_', ' '); -		return result; -	} -	// Prepending this apparently prevents conflicts with reserved names inside the WebRTC code. -	result = "x"; -	 -	// Base64 encode and replace the pieces of base64 that are less compatible  -	// with e-mail local-parts. -	// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" -	result += LLBase64::encode(uuid.mData, UUID_BYTES); -	LLStringUtil::replaceChar(result, '+', '-'); -	LLStringUtil::replaceChar(result, '/', '_'); -	 -	// If you need to transform a GUID to this form on the Mac OS X command line, this will do so: -	// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') -	 -	// The reverse transform can be done with: -	// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p -	 -	return result; -} - -bool LLWebRTCVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) -{ -	bool result = false; -	 -	// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.WebRTC.com" -	// If it is, convert to a bare name before doing the transform. -	std::string name; -	 -	// Doesn't look like a SIP URI, assume it's an actual name. -	if(name.empty()) -		name = inName; - -	// This will only work if the name is of the proper form. -	// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: -	// "xFnPP04IpREWNkuw1cOXlhw==" -	 -	if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) -	{ -		// The name appears to have the right form. - -		// Reverse the transforms done by nameFromID -		std::string temp = name; -		LLStringUtil::replaceChar(temp, '-', '+'); -		LLStringUtil::replaceChar(temp, '_', '/'); - -		U8 rawuuid[UUID_BYTES + 1];  -		int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); -		if(len == UUID_BYTES) -		{ -			// The decode succeeded.  Stuff the bits into the result's UUID -			memcpy(uuid.mData, rawuuid, UUID_BYTES); -			result = true; -		} -	}  -	 -	if(!result) -	{ -		// WebRTC:  not a standard account name, just copy the URI name mURIString field -		// and hope for the best.  bpj -		uuid.setNull();  // WebRTC, set the uuid field to nulls -	} -	 -	return result; +    return inSpatialChannel();  }  bool LLWebRTCVoiceClient::inOrJoiningChannel(const std::string& channelID) @@ -1456,20 +1273,22 @@ bool LLWebRTCVoiceClient::inEstateChannel()  bool LLWebRTCVoiceClient::inSpatialChannel()  { -	bool result = true; -	 +    bool result = true; +          if (mNextSession)       {          result = mNextSession->isSpatial();      } -	else if(mSession) +    else if(mSession)      { -		result = mSession->isSpatial(); +        result = mSession->isSpatial();      } -	return result; +    return result;  } +// retrieves information used to negotiate p2p, adhoc, and group +// channels  LLSD LLWebRTCVoiceClient::getAudioSessionChannelInfo()  {      LLSD result; @@ -1483,167 +1302,9 @@ LLSD LLWebRTCVoiceClient::getAudioSessionChannelInfo()      return result;  } -///////////////////////////// -// Sending updates of current state - -void LLWebRTCVoiceClient::enforceTether() -{ -	LLVector3d tethered	= mListenerRequestedPosition; - -	// constrain 'tethered' to within 50m of mAvatarPosition. -	{ -		LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition; -		F32 camera_distance = (F32)camera_offset.magVec(); -		if(camera_distance > MAX_AUDIO_DIST) -		{ -			tethered = mAvatarPosition +  -				(MAX_AUDIO_DIST / camera_distance) * camera_offset; -		} -	} -	 -	if(dist_vec_squared(mListenerPosition, tethered) > 0.01) -	{ -        mListenerPosition   = tethered; -		mSpatialCoordsDirty = true; -	} -} - -void LLWebRTCVoiceClient::updateNeighboringRegions() -{ -    static const std::vector<LLVector3d> neighbors {LLVector3d(0.0f, 1.0f, 0.0f),  LLVector3d(0.707f, 0.707f, 0.0f), -                                                    LLVector3d(1.0f, 0.0f, 0.0f),  LLVector3d(0.707f, -0.707f, 0.0f), -                                                    LLVector3d(0.0f, -1.0f, 0.0f), LLVector3d(-0.707f, -0.707f, 0.0f), -                                                    LLVector3d(-1.0f, 0.0f, 0.0f), LLVector3d(-0.707f, 0.707f, 0.0f)}; - -    // Estate voice requires connection to neighboring regions. -    mNeighboringRegions.clear(); - -    mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); - -    // base off of speaker position as it'll move more slowly than camera position. -    // Once we have hysteresis, we may be able to track off of speaker and camera position at 50m -    // TODO: Add hysteresis so we don't flip-flop connections to neighbors -    LLVector3d speaker_pos = LLWebRTCVoiceClient::getInstance()->getSpeakerPosition(); -    for (auto &neighbor_pos : neighbors) -    { -        // include every region within 100m (2*MAX_AUDIO_DIST) to deal witht he fact that the camera -        // can stray 50m away from the avatar. -        LLViewerRegion *neighbor = LLWorld::instance().getRegionFromPosGlobal(speaker_pos + 2 * MAX_AUDIO_DIST * neighbor_pos); -        if (neighbor && !neighbor->getRegionID().isNull()) -        { -            mNeighboringRegions.insert(neighbor->getRegionID()); -        } -    } -} - -void LLWebRTCVoiceClient::updatePosition(void) -{ -	LLViewerRegion *region = gAgent.getRegion(); -	if(region && isAgentAvatarValid()) -	{ -        LLVector3d avatar_pos = gAgentAvatarp->getPositionGlobal(); -        LLQuaternion avatar_qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); - -		avatar_pos += LLVector3d(0.f, 0.f, 1.f); // bump it up to head height -		 -		// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... -		// They're currently always set to zero. -        LLVector3d   earPosition; -        LLQuaternion earRot; -        switch (mEarLocation) -        { -            case earLocCamera: -            default: -                earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); -                earRot      = LLViewerCamera::getInstance()->getQuaternion(); -                break; - -            case earLocAvatar: -                earPosition = mAvatarPosition; -                earRot      = mAvatarRot; -                break; - -            case earLocMixed: -                earPosition = mAvatarPosition; -                earRot      = LLViewerCamera::getInstance()->getQuaternion(); -                break; -        }		 -		setListenerPosition(earPosition,      // position -				            LLVector3::zero,  // velocity -                            earRot);          // rotation matrix -		 -		setAvatarPosition( -			avatar_pos,			// position -			LLVector3::zero, 	// velocity -			avatar_qrot);		// rotation matrix - -        enforceTether(); - -        updateNeighboringRegions(); -	} -} - -void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) -{ - -	mListenerRequestedPosition = position; -	 -	if(mListenerVelocity != velocity) -	{ -		mListenerVelocity = velocity; -		mSpatialCoordsDirty = true; -	} -	 -	if(mListenerRot != rot) -	{ -		mListenerRot = rot; -		mSpatialCoordsDirty = true; -	} -} - -void LLWebRTCVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) -{ -	if(dist_vec_squared(mAvatarPosition, position) > 0.01) -	{ -		mAvatarPosition = position; -		mSpatialCoordsDirty = true; -	} -	 -	if(mAvatarVelocity != velocity) -	{ -		mAvatarVelocity = velocity; -		mSpatialCoordsDirty = true; -	} -	 -    // If the two rotations are not exactly equal test their dot product -    // to get the cos of the angle between them. -    // If it is too small, don't update. -    F32 rot_cos_diff = llabs(dot(mAvatarRot, rot)); -    if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS)) -	{    -		mAvatarRot = rot; -		mSpatialCoordsDirty = true; -	} -} - -bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) -{ -	bool result = false; -	 -	if(region) -	{ -		name = region->getName(); -	} -	 -	if(!name.empty()) -		result = true; -	 -	return result; -} -  void LLWebRTCVoiceClient::leaveChannel(bool stopTalking)  { -   if (mSession) +    if (mSession)      {          deleteSession(mSession);      } @@ -1678,20 +1339,24 @@ bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo)  bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2)  {      return (channelInfo1["voice_server_type"] == WEBRTC_VOICE_SERVER_TYPE) && -        (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) &&  -        (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]); +           (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) &&  +           (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]);  } + +//---------------------------------------------- +// Audio muting, volume, gain, etc. + +// we're muting the mic, so tell each session such  void LLWebRTCVoiceClient::setMuteMic(bool muted)  { -      mMuteMic = muted;      sessionState::for_each(boost::bind(predSetMuteMic, _1, muted));  }  void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool muted)  { -    participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); +    participantStatePtr_t participant = session->findParticipantByID(gAgentID);      if (participant)      {          participant->mLevel = 0.0; @@ -1699,28 +1364,34 @@ void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionState      session->setMuteMic(muted);  } -void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +void LLWebRTCVoiceClient::setVoiceVolume(F32 volume)  { -    session->setSpeakerVolume(volume); +    if (volume != mSpeakerVolume) +    { +        { +            mSpeakerVolume      = volume; +        } +        sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); +    }  } -void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain) +void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume)  { -    session->setMicGain(gain); +    session->setSpeakerVolume(volume);  } -void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, -                                          const LLUUID &id, -                                          bool mute) +void LLWebRTCVoiceClient::setMicGain(F32 gain)  { -    session->setUserMute(id, mute); +    if (gain != mMicGain) +    { +        mMicGain = gain; +        sessionState::for_each(boost::bind(predSetMicGain, _1, gain)); +    }  } -void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, -                                          const LLUUID &id, -                                          F32 volume) +void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain)  { -    session->setUserVolume(id, volume); +    session->setMicGain(gain);  }  void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -1731,18 +1402,18 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)          << " coro "<< (mIsCoroutineActive ? "active" : "inactive")          << LL_ENDL; -	if (enabled != mVoiceEnabled) -	{ -		// TODO: Refactor this so we don't call into LLVoiceChannel, but simply -		// use the status observer -		mVoiceEnabled = enabled; -		LLVoiceClientStatusObserver::EStatusType status; -		 -		if (enabled) -		{ +    if (enabled != mVoiceEnabled) +    { +        // TODO: Refactor this so we don't call into LLVoiceChannel, but simply +        // use the status observer +        mVoiceEnabled = enabled; +        LLVoiceClientStatusObserver::EStatusType status; +         +        if (enabled) +        {              LL_DEBUGS("Voice") << "enabling" << LL_ENDL; -			LLVoiceChannel::getCurrentVoiceChannel()->activate(); -			status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; +            LLVoiceChannel::getCurrentVoiceChannel()->activate(); +            status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;              mSpatialCoordsDirty = true;              updatePosition();              if (!mIsCoroutineActive) @@ -1754,122 +1425,92 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)              {                  LL_DEBUGS("Voice") << "coro should be active.. not launching" << LL_ENDL;              } -		} -		else -		{ -			// Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. -			LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); -			gAgent.setVoiceConnected(false); -			status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; +        } +        else +        { +            // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. +            LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); +            gAgent.setVoiceConnected(false); +            status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;              cleanUp(); -		} +        } -		notifyStatusObservers(status); -	} +        notifyStatusObservers(status); +    }      else      {          LL_DEBUGS("Voice") << " no-op" << LL_ENDL;      }  } -void LLWebRTCVoiceClient::setEarLocation(S32 loc) -{ -	if(mEarLocation != loc) -	{ -		LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; -		 -		mEarLocation = loc; -		mSpatialCoordsDirty = true; -	} -} - -void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) -{ -	if (volume != mSpeakerVolume) -	{ -        { -            mSpeakerVolume      = volume; -            mSpeakerVolumeDirty = true; -        } -        sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); -	} -} - -void LLWebRTCVoiceClient::setMicGain(F32 gain) -{	 -	if (gain != mMicGain) -    { -        mMicGain = gain; -        sessionState::for_each(boost::bind(predSetMicGain, _1, gain)); -    } -}  /////////////////////////////  // Accessors for data related to nearby speakers  BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id)  { -	BOOL result = FALSE; +    BOOL result = FALSE;      if (!mSession)      {          return FALSE;      } -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		// I'm not sure what the semantics of this should be. -		// For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. -		result = TRUE; -	} -	 -	return result; +    participantStatePtr_t participant(mSession->findParticipantByID(id)); +    if(participant) +    { +        // I'm not sure what the semantics of this should be. +        // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. +        result = TRUE; +    } +     +    return result;  }  std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id)  { -	std::string result; +    std::string result;      if (!mSession)      {          return result;      } -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		result = participant->mDisplayName; -	} -	 -	return result; +    participantStatePtr_t participant(mSession->findParticipantByID(id)); +    if(participant) +    { +        result = participant->mDisplayName; +    } +     +    return result;  }  BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id)  { -	BOOL result = FALSE; +    BOOL result = FALSE;      if (!mSession)      {          return result;      } -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		result = participant->mIsSpeaking; -	} -	 -	return result; +    participantStatePtr_t participant(mSession->findParticipantByID(id)); +    if(participant) +    { +        result = participant->mIsSpeaking; +    } +     +    return result;  } +// TODO: Need to pull muted status from the webrtc server  BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id)  { -	BOOL result = FALSE; +    BOOL result = FALSE;      if (!mSession)      {          return result;      } -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		result = participant->mIsModeratorMuted; -	} -	 -	return result; +    participantStatePtr_t participant(mSession->findParticipantByID(id)); +    if(participant) +    { +        result = participant->mIsModeratorMuted; +    } +     +    return result;  }  F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) @@ -1879,46 +1520,28 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id)      {          return result;      } -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if (participant) -	{ +    participantStatePtr_t participant(mSession->findParticipantByID(id)); +    if (participant) +    {          if (participant->mIsSpeaking)          {              result = participant->mLevel;          } -	} -	return result; -} - -BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) -{ -	BOOL result = FALSE; -    if (!mSession) -    { -        return result;      } -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		// I'm not sure what the semantics of this should be. -		// Does "using PTT" mean they're configured with a push-to-talk button? -		// For now, we know there's no PTT mechanism in place, so nobody is using it. -	} -	 -	return result; +    return result;  }  BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id)  { -	BOOL result = FALSE; -	 -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		result = participant->mOnMuteList; -	} +    BOOL result = FALSE; +     +    participantStatePtr_t participant(mSession->findParticipantByID(id)); +    if(participant) +    { +        result = participant->mOnMuteList; +    } -	return result; +    return result;  }  // External accessors. @@ -1926,58 +1549,46 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id)  {      // Minimum volume will be returned for users with voice disabled      F32 result = LLVoiceClient::VOLUME_MIN; -	 -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); +     +    participantStatePtr_t participant(mSession->findParticipantByID(id));      if(participant) -	{ -		result = participant->mVolume; - -		// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. -		// LL_DEBUGS("Voice") << "mVolume = " << result <<  " for " << id << LL_ENDL; -	} +    { +        result = participant->mVolume; +    } -	return result; +    return result;  }  void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume)  {      F32 clamped_volume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); -	if(mSession) -	{ -        participantStatePtr_t participant(mSession->findParticipant(id.asString())); -		if (participant && !participant->mIsSelf) -		{ -			if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) -			{ -				// Store this volume setting for future sessions if it has been -				// changed from the default -				LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); -			} -			else -			{ -				// Remove stored volume setting if it is returned to the default -				LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); -			} - -			participant->mVolume = clamped_volume; -			participant->mVolumeDirty = true; -			mSession->mVolumeDirty = true; -		} -	} +    if(mSession) +    { +        participantStatePtr_t participant(mSession->findParticipantByID(id)); +        if (participant && (participant->mAvatarID != gAgentID)) +        { +            if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) +            { +                // Store this volume setting for future sessions if it has been +                // changed from the default +                LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); +            } +            else +            { +                // Remove stored volume setting if it is returned to the default +                LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); +            } + +            participant->mVolume = clamped_volume; +        } +    }      sessionState::for_each(boost::bind(predSetUserVolume, _1, id, clamped_volume));  } -std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) +// set volume level (gain level) for another user. +void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID &id, F32 volume)  { -	std::string result; - -    participantStatePtr_t participant(mSession->findParticipant(id.asString())); -	if(participant) -	{ -		result = participant->mGroupID; -	} -	 -	return result; +    session->setUserVolume(id, volume);  }  //////////////////////// @@ -1997,51 +1608,119 @@ void LLWebRTCVoiceClient::onChangeDetailed(const LLMute& mute)      }  } - -BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() +void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID &id, bool mute)  { -	return mAreaVoiceDisabled; +    session->setUserMute(id, mute);  }  //------------------------------------------------------------------------ +// Sessions +  std::map<std::string, LLWebRTCVoiceClient::sessionState::ptr_t> LLWebRTCVoiceClient::sessionState::mSessions; -LLWebRTCVoiceClient::sessionState::sessionState() : -    mErrorStatusCode(0), -    mVolumeDirty(false), -    mMuteDirty(false), +LLWebRTCVoiceClient::sessionState::sessionState() :  +    mHangupOnLastLeave(false), +    mNotifyOnFirstJoin(false), +    mMicGain(1.0), +    mMuted(false), +    mSpeakerVolume(1.0),      mShuttingDown(false)  {  } +// ------------------------------------------------------------------ +// Predicates, for calls to all sessions -/*static*/ -void LLWebRTCVoiceClient::sessionState::addSession( -    const std::string & channelID, -    LLWebRTCVoiceClient::sessionState::ptr_t& session) +void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level)  { -    mSessions[channelID] = session; +    participantStatePtr_t participant = session->findParticipantByID(gAgentID); +    if (participant) +    { +        participant->mLevel = audio_level; +        // TODO: Add VAD for our own voice. +        participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; +    }  } -LLWebRTCVoiceClient::sessionState::~sessionState() +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string &spatial_data)  { -    LL_INFOS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL; +    if (session->isSpatial() && !spatial_data.empty()) +    { +        session->sendData(spatial_data); +    } +} -	removeAllParticipants(); +void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) +{ +    for (auto &connection : mWebRTCConnections) +    { +        connection->sendData(data); +    } +} + +void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) +{ +    mMuted = muted; +    for (auto &connection : mWebRTCConnections) +    { +        connection->setMuteMic(muted); +    } +} + +void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) +{ +    mMicGain = gain; +    for (auto &connection : mWebRTCConnections) +    { +        connection->setMicGain(gain); +    } +} + +void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) +{ +    mSpeakerVolume = volume; +    for (auto &connection : mWebRTCConnections) +    { +        connection->setSpeakerVolume(volume); +    } +} + +void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID &id, F32 volume) +{ +    if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) +    { +        return; +    } +    for (auto &connection : mWebRTCConnections) +    { +        connection->setUserVolume(id, volume); +    }  } -bool LLWebRTCVoiceClient::sessionState::isCallBackPossible() +void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID &id, bool mute) +{ +    if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) +    { +        return; +    } +    for (auto &connection : mWebRTCConnections) +    { +        connection->setUserMute(id, mute); +    } +} +/*static*/ +void LLWebRTCVoiceClient::sessionState::addSession( +    const std::string & channelID, +    LLWebRTCVoiceClient::sessionState::ptr_t& session)  { -	// This may change to be explicitly specified by WebRTC in the future... -	// Currently, only PSTN P2P calls cannot be returned. -	// Conveniently, this is also the only case where we synthesize a caller UUID. -	return false; +    mSessions[channelID] = session;  } -bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() +LLWebRTCVoiceClient::sessionState::~sessionState()  { -	// This may change to be explicitly specified by WebRTC in the future... -	return false; +    LL_DEBUGS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL; + +    removeAllParticipants();  }  /*static*/ @@ -2079,20 +1758,6 @@ void LLWebRTCVoiceClient::sessionState::reapEmptySessions()      }  } -bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) -{ -    ptr_t aLock(a.lock()); - -    return aLock ? (aLock->mChannelID == LLUUID(uri)) : false; -} - -bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId) -{ -    ptr_t aLock(a.lock()); - -    return aLock ? ((aLock->mCallerID == participantId) || (aLock->mIMSessionID == participantId)) : false; -} -  /*static*/  void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pair<std::string, LLWebRTCVoiceClient::sessionState::wptr_t> &a, sessionFunc_t func)  { @@ -2106,6 +1771,31 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pair<std::s      }  } +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session) +{ +    sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); +    if (!existingSession) +    { +        // No existing session found. + +        LL_DEBUGS("Voice") << "adding new session with channel: " << channel_id << LL_ENDL; +        session->setMuteMic(mMuteMic); +        session->setMicGain(mMicGain); +        session->setSpeakerVolume(mSpeakerVolume); + +        sessionState::addSession(channel_id, session); +        return session; +    } +    else +    { +        // Found an existing session +        LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; +        existingSession->revive(); + +        return existingSession; +    } +} +  LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const LLUUID &agent_id)  {      sessionStatePtr_t result = sessionState::matchSessionByChannelID(agent_id.asString()); @@ -2113,12 +1803,11 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const      {          return result;      } -	 +          result.reset(); -	return result; +    return result;  } -  void LLWebRTCVoiceClient::sessionState::shutdownAllConnections()  {      mShuttingDown = true; @@ -2128,218 +1817,172 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections()      }  } +// in case we drop into a session (spatial, etc.) right after +// telling the session to shut down, revive it so it reconnects.  void LLWebRTCVoiceClient::sessionState::revive()  {      mShuttingDown = false;  } +//========================================================================= +// the following are methods to support the coroutine implementation of the +// voice connection and processing.  They should only be called in the context +// of a coroutine. +// +// -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session) +void LLWebRTCVoiceClient::sessionState::processSessionStates()  { -    sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); -	if (!existingSession) -	{ -		// No existing session found. -		 -		LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; -        session->setMuteMic(mMuteMic); -        session->setMicGain(mMicGain); -        session->setSpeakerVolume(mSpeakerVolume); - -		if (LLVoiceClient::instance().getVoiceEffectEnabled()) -		{ -            session->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); -		} - -        sessionState::addSession(channel_id, session); -        return session; -	} -	else -	{ -		// Found an existing session -		LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; -        existingSession->revive(); - -        return existingSession; -	}	 +    auto iter = mSessions.begin(); +    while (iter != mSessions.end()) +    { +        if (!iter->second->processConnectionStates() && iter->second->mShuttingDown) +        { +            // if the connections associated with a session are gone, +            // and this session is shutting down, remove it. +            iter = mSessions.erase(iter); +        } +        else +        { +            iter++; +        } +    }  } -void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) -{  -    session->shutdownAllConnections(); +// process the states on each connection associated with a session. +bool LLWebRTCVoiceClient::sessionState::processConnectionStates() +{ +    std::list<connectionPtr_t>::iterator iter = mWebRTCConnections.begin(); +    while (iter != mWebRTCConnections.end()) +    { +        if (!iter->get()->connectionStateMachine()) +        { +            // if the state machine returns false, the connection is shut down +            // so delete it. +            iter = mWebRTCConnections.erase(iter); +        } +        else +        { +            ++iter; +        } +    } +    return !mWebRTCConnections.empty();  } -void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) +// processing of spatial voice connection states requires special handling. +// as neighboring regions need to be started up or shut down depending +// on our location. +bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates()  { -	// At this point, the session should be unhooked from all lists and all state should be consistent. -    session->shutdownAllConnections(); -	// If this is the current audio session, clean up the pointer which will soon be dangling. -    bool deleteAudioSession = mSession == session; -    bool deleteNextAudioSession = mNextSession == session; -    if (deleteAudioSession) -	{ -		mSession.reset(); -	} +    if (!mShuttingDown) +    { +        // Estate voice requires connection to neighboring regions. +        std::set<LLUUID> neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); -	// ditto for the next audio session -    if (deleteNextAudioSession) -	{ -		mNextSession.reset(); -	} -} +        for (auto &connection : mWebRTCConnections) +        { +            boost::shared_ptr<LLVoiceWebRTCSpatialConnection> spatialConnection = +                boost::static_pointer_cast<LLVoiceWebRTCSpatialConnection>(connection); -void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) -{ -	mParticipantObservers.insert(observer); -} +            LLUUID regionID = spatialConnection.get()->getRegionID(); -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) -{ -	mParticipantObservers.erase(observer); -} +            if (neighbor_ids.find(regionID) == neighbor_ids.end()) +            { +                // shut down connections to neighbors that are too far away. +                spatialConnection.get()->shutDown(); +            } +            neighbor_ids.erase(regionID); +        } -void LLWebRTCVoiceClient::notifyParticipantObservers() -{ -	for (observer_set_t::iterator it = mParticipantObservers.begin(); -		it != mParticipantObservers.end(); -		) -	{ -		LLVoiceClientParticipantObserver* observer = *it; -		observer->onParticipantsChanged(); -		// In case onParticipantsChanged() deleted an entry. -		it = mParticipantObservers.upper_bound(observer); -	} -} +        // add new connections for new neighbors +        for (auto &neighbor : neighbor_ids) +        { +            connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); -void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) -{ -	mStatusObservers.insert(observer); +            mWebRTCConnections.push_back(connection); +            connection->setMicGain(mMicGain); +            connection->setMuteMic(mMuted); +            connection->setSpeakerVolume(mSpeakerVolume); +        } +    } +    return LLWebRTCVoiceClient::sessionState::processConnectionStates();  } -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) -{ -	mStatusObservers.erase(observer); -} +// Various session state constructors. -void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) +LLWebRTCVoiceClient::estateSessionState::estateSessionState()  { -    LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" -                       << " mSession=" << mSession -                       << LL_ENDL; -	if(mSession) -	{ -		if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) -		{ -			switch(mSession->mErrorStatusCode) -			{ -				case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break; -				case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break; -				case 20715: -					//invalid channel, we may be using a set of poorly cached -					//info -					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; -					break; -				case 1009: -					//invalid username and password -					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; -					break; -			} - -			// Reset the error code to make sure it won't be reused later by accident. -			mSession->mErrorStatusCode = 0; -		} -		else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) -		{ -			switch(mSession->mErrorStatusCode) -			{ -				case HTTP_NOT_FOUND:	// NOT_FOUND -				// *TODO: Should this be 503? -				case 480:	// TEMPORARILY_UNAVAILABLE -				case HTTP_REQUEST_TIME_OUT:	// REQUEST_TIMEOUT -					// call failed because other user was not available -					// treat this as an error case -					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - -					// Reset the error code to make sure it won't be reused later by accident. -					mSession->mErrorStatusCode = 0; -				break; -			} -		} -	} -		 -	LL_DEBUGS("Voice")  -		<< " " << LLVoiceClientStatusObserver::status2string(status)   -		<< ", session channelInfo " << getAudioSessionChannelInfo()  -		<< ", proximal is " << inSpatialChannel() -        << LL_ENDL; - -    mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; - -    LLSD channelInfo = getAudioSessionChannelInfo(); -	for (status_observer_set_t::iterator it = mStatusObservers.begin(); -		it != mStatusObservers.end(); -		) -	{ -		LLVoiceClientStatusObserver* observer = *it; -		observer->onChange(status, channelInfo, inSpatialChannel()); -		// In case onError() deleted an entry. -		it = mStatusObservers.upper_bound(observer); -	} - -	// skipped to avoid speak button blinking -	if (   status != LLVoiceClientStatusObserver::STATUS_JOINING -		&& status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL -		&& status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) -	{ -		bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - -		gAgent.setVoiceConnected(voice_status); +    mHangupOnLastLeave = false; +    mNotifyOnFirstJoin = false; +    mChannelID         = "Estate"; +    LLUUID region_id   = gAgent.getRegion()->getRegionID(); -		if (voice_status) -		{ -			LLFirstUse::speak(true); -		} -	} +    mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, INVALID_PARCEL_ID, "Estate"));  } -void LLWebRTCVoiceClient::addObserver(LLFriendObserver* observer) +LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id)  { -	mFriendObservers.insert(observer); +    mHangupOnLastLeave = false; +    mNotifyOnFirstJoin = false; +    LLUUID region_id   = gAgent.getRegion()->getRegionID(); +    mChannelID         = channelID; +    mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID));  } -void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, +                                                          const std::string &credentials, +                                                          bool notify_on_first_join, +                                                          bool hangup_on_last_leave) : +    mCredentials(credentials)  { -	mFriendObservers.erase(observer); +    mHangupOnLastLeave = hangup_on_last_leave; +    mNotifyOnFirstJoin = notify_on_first_join; +    LLUUID region_id   = gAgent.getRegion()->getRegionID(); +    mChannelID         = channelID; +    mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials));  } -void LLWebRTCVoiceClient::notifyFriendObservers() +void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +{  +    session->shutdownAllConnections(); +} + +void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session)  { -	for (friend_observer_set_t::iterator it = mFriendObservers.begin(); -		it != mFriendObservers.end(); -		) -	{ -		LLFriendObserver* observer = *it; -		it++; -		// The only friend-related thing we notify on is online/offline transitions. -		observer->changed(LLFriendObserver::ONLINE); -	} +    // At this point, the session should be unhooked from all lists and all state should be consistent. +    session->shutdownAllConnections(); +    // If this is the current audio session, clean up the pointer which will soon be dangling. +    bool deleteAudioSession = mSession == session; +    bool deleteNextAudioSession = mNextSession == session; +    if (deleteAudioSession) +    { +        mSession.reset(); +    } + +    // ditto for the next audio session +    if (deleteNextAudioSession) +    { +        mNextSession.reset(); +    }  } + +// Name resolution  void LLWebRTCVoiceClient::lookupName(const LLUUID &id)  { -	if (mAvatarNameCacheConnection.connected()) -	{ -		mAvatarNameCacheConnection.disconnect(); -	} -	mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); +    if (mAvatarNameCacheConnection.connected()) +    { +        mAvatarNameCacheConnection.disconnect(); +    } +    mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2));  }  void LLWebRTCVoiceClient::onAvatarNameCache(const LLUUID& agent_id, -										   const LLAvatarName& av_name) +                                           const LLAvatarName& av_name)  { -	mAvatarNameCacheConnection.disconnect(); -	std::string display_name = av_name.getDisplayName(); -	avatarNameResolved(agent_id, display_name); +    mAvatarNameCacheConnection.disconnect(); +    std::string display_name = av_name.getDisplayName(); +    avatarNameResolved(agent_id, display_name);  }  void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) @@ -2348,16 +1991,10 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se      if (participant)      {          // Found -- fill in the name +        participant->mDisplayName = name;          // and post a "participants updated" message to listeners later.          LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers();      } - -    // Check whether this is a p2p session whose caller name just resolved -    if (session->mCallerID == id) -    { -        // this session's "caller ID" just resolved.  Fill in the name. -        session->mName = name; -    }  }  void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) @@ -2365,13 +2002,20 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string      sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name));  } - -std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } +// Leftover from vivox PTSN +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) +{  +    return id.asString(); +}  /////////////////////////////  // LLVoiceWebRTCConnection - +// These connections manage state transitions, negotiating webrtc connections, +// and other such things for a single connection to a Secondlife WebRTC server. +// Multiple of these connections may be active at once, in the case of +// cross-region voice, or when a new connection is being created before the old  +// has a chance to shut down.  LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID) :      mWebRTCAudioInterface(nullptr),      mWebRTCDataInterface(nullptr), @@ -2405,9 +2049,18 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection()      }  } + +// ICE (Interactive Connectivity Establishment) +// When WebRTC tries to negotiate a connection to the Secondlife WebRTC Server, +// the negotiation will result in a few updates about the best path +// to which to connect. +// The Secondlife servers are configured for ICE trickling, where, after a session is partially +// negotiated, updates about the best connectivity paths may trickle in.  These need to be +// sent to the Secondlife WebRTC server via the simulator so that both sides have a clear +// view of the network environment.  void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state)  { -    LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; +    LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL;      switch (state)      { @@ -2475,39 +2128,11 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS      mOutstandingRequests--;  } -void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) -{ -    LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; -    LLMutexLock lock(&mVoiceStateMutex); -    mChannelSDP = sdp; -    if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START) -    { -        mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION; -    } -} - -void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) -{ -    LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; -    mWebRTCAudioInterface = audio_interface; -    setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); -} - -void LLVoiceWebRTCConnection::OnRenegotiationNeeded() -{ -    LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; -    if (!mShutDown) -    { -        setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); -    } -} - -void LLVoiceWebRTCConnection::OnPeerShutDown() -{ -    setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); -    mOutstandingRequests--; -} +// Ice candidates may be streamed in before or after the SDP offer is available (see below) +// This function determines whether candidates are available to send to the Secondlife WebRTC +// server via the simulator.  If so, and there are no more candidates, this code +// will make the cap call to the server sending up the ICE candidates.   void LLVoiceWebRTCConnection::processIceUpdates()  {      if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) @@ -2541,11 +2166,11 @@ void LLVoiceWebRTCConnection::processIceUpdates()                      LLSD candidates = LLSD::emptyArray();                      for (auto &ice_candidate : mIceCandidates)                      { -                            LLSD body_candidate; -                            body_candidate["sdpMid"]        = ice_candidate.sdp_mid; -                            body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; -                            body_candidate["candidate"]     = ice_candidate.candidate; -                            candidates.append(body_candidate); +                        LLSD body_candidate; +                        body_candidate["sdpMid"]        = ice_candidate.sdp_mid; +                        body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; +                        body_candidate["candidate"]     = ice_candidate.candidate; +                        candidates.append(body_candidate);                      }                      body["candidates"] = candidates;                      mIceCandidates.clear(); @@ -2559,7 +2184,7 @@ void LLVoiceWebRTCConnection::processIceUpdates()                      mIceCompleted               = false;                  } -                body["viewer_session"] = mViewerSession; +                body["viewer_session"]    = mViewerSession;                  body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE;                  LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -2581,6 +2206,48 @@ void LLVoiceWebRTCConnection::processIceUpdates()  } +// An 'Offer' comes in the form of a SDP (Session Description Protocol) +// which contains all sorts of info about the session, from network paths +// to the type of session (audio, video) to characteristics (the encoder type.) +// This SDP also serves as the 'ticket' to the server, security-wise. +// The Offer is retrieved from the WebRTC library on the client, +// and is passed to the simulator via a CAP, which then passes  +// it on to the Secondlife WebRTC server. + +void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) +{ +    LL_DEBUGS("Voice") << "On Offer Available." << LL_ENDL; +    LLMutexLock lock(&mVoiceStateMutex); +    mChannelSDP = sdp; +    if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START) +    { +        mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION; +    } +} + +// Notifications from the webrtc library. +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +{ +    LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL; +    mWebRTCAudioInterface = audio_interface; +    setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); +} + +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() +{ +    LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; +    if (!mShutDown) +    { +        setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); +    } +} + +void LLVoiceWebRTCConnection::OnPeerShutDown() +{ +    setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); +    mOutstandingRequests--;  // shut down is an async call which is handled on a webrtc thread. +} +  void LLVoiceWebRTCConnection::setMuteMic(bool muted)  {      mMuted = muted; @@ -2631,6 +2298,8 @@ void LLVoiceWebRTCConnection::setUserMute(const LLUUID& id, bool mute)  } +// Send data to the Secondlife WebRTC server via the webrtc +// data channel.  void LLVoiceWebRTCConnection::sendData(const std::string &data)  {      if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) @@ -2639,9 +2308,11 @@ void LLVoiceWebRTCConnection::sendData(const std::string &data)      }  } +// Tell the simulator that we're shutting down a voice connection. +// The simulator will pass this on to the Secondlife WebRTC server.  bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait)  { -    LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; +    LL_DEBUGS("Voice") << "Disconnecting voice." << LL_ENDL;      if (mWebRTCDataInterface)      {          mWebRTCDataInterface->unsetDataObserver(this); @@ -2672,7 +2343,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait)      LLSD body;      body["logout"]         = TRUE;      body["viewer_session"] = mViewerSession; -    body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; +    body["voice_server_type"] = REPORTED_VOICE_SERVER_TYPE;      LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(          url, @@ -2715,6 +2386,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url      }      if (retries >= 0)      { +        // retry a few times.          LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(              url,              LLCore::HttpRequest::DEFAULT_POLICY_ID, @@ -2736,6 +2408,55 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url  } +// Tell the simulator to tell the Secondlife WebRTC server that we want a voice +// connection. The SDP is sent up as part of this, and the simulator will respond +// with an 'answer' which is in the form of another SDP.  The webrtc library +// will use the offer and answer to negotiate the session. +bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() +{ +    LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + +    LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL; +    if (!regionp || !regionp->capabilitiesReceived()) +    { +        LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; +        return false; +    } + +    std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); +    if (url.empty()) +    { +        return false; +    } + +    LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + +    LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); +    LLSD body; +    LLSD jsep; +    jsep["type"] = "offer"; +    { +        LLMutexLock lock(&mVoiceStateMutex); +        jsep["sdp"] = mChannelSDP; +    } +    body["jsep"] = jsep; +    if (mParcelLocalID != INVALID_PARCEL_ID) +    { +        body["parcel_local_id"] = mParcelLocalID; +    } + +    body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + +    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +        url, +        LLCore::HttpRequest::DEFAULT_POLICY_ID, +        body, +        boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), +        boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); +    mOutstandingRequests++; +    return true; +} +  void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result)  {      mOutstandingRequests--; @@ -2764,6 +2485,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result      mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP);  } +  void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result)  {      if (LLWebRTCVoiceClient::isShuttingDown()) @@ -2787,6 +2509,9 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i      mOutstandingRequests--;  } + +// Primary state machine for negotiating a single voice connection to the  +// Secondlife WebRTC server.  bool LLVoiceWebRTCConnection::connectionStateMachine()  {      processIceUpdates(); @@ -2803,12 +2528,16 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()              mTrickling    = false;              mIceCompleted = false;              setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START); +            // tell the webrtc library that we want a connection.  The library will +            // respond with an offer on a separate thread, which will cause +            // the session state to change.              if (!mWebRTCPeerConnection->initializeConnection())              {                  setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);              }              break;          } +          case VOICE_STATE_WAIT_FOR_SESSION_START:          {              if (mShutDown) @@ -2817,12 +2546,16 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()              }              break;          } +          case VOICE_STATE_REQUEST_CONNECTION:              if (mShutDown)              {                  setVoiceConnectionState(VOICE_STATE_DISCONNECT);                  break;              } +            // Ask the sim to ask the Secondlife WebRTC server for a connection to  +            // a given voice channel.  On completion, we'll move on to the +            // VOICE_STATE_SESSION_ESTABLISHED via a callback on a webrtc thread.              if (!requestVoiceConnection())              {                  setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); @@ -2832,6 +2565,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()                  setVoiceConnectionState(VOICE_STATE_CONNECTION_WAIT);              }              break; +          case VOICE_STATE_CONNECTION_WAIT:              if (mShutDown)              { @@ -2846,6 +2580,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()                  setVoiceConnectionState(VOICE_STATE_DISCONNECT);                  break;              } +            // update the peer connection with the various characteristics of +            // this connection.              mWebRTCAudioInterface->setMute(mMuted);              mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume);              mWebRTCAudioInterface->setSendVolume(mMicGain); @@ -2853,6 +2589,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()              setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL);              break;          } +          case VOICE_STATE_WAIT_FOR_DATA_CHANNEL:          {              if (mShutDown) @@ -2860,15 +2597,17 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()                  setVoiceConnectionState(VOICE_STATE_DISCONNECT);                  break;              } -            if (mWebRTCDataInterface) +            if (mWebRTCDataInterface) // the interface will be set when the session is negotiated.              { -                sendJoin(); +                sendJoin();  // tell the Secondlife WebRTC server that we're here via the data channel.                  setVoiceConnectionState(VOICE_STATE_SESSION_UP);              }              break;          } +          case VOICE_STATE_SESSION_UP:          { +            // we'll stay here as long as the session remains up.              if (mShutDown)              {                  setVoiceConnectionState(VOICE_STATE_DISCONNECT); @@ -2877,6 +2616,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()          }          case VOICE_STATE_SESSION_RETRY: +            // something went wrong, so notify that the connection has failed.              LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID);              setVoiceConnectionState(VOICE_STATE_DISCONNECT);              break; @@ -2887,6 +2627,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()          case VOICE_STATE_WAIT_FOR_EXIT:              break; +          case VOICE_STATE_SESSION_EXIT:          {              { @@ -2897,6 +2638,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()                  }                  else                  { +                    // if we still have outstanding http or webrtc calls, wait for them to +                    // complete so we don't delete objects while they still may be used.                      if (mOutstandingRequests <= 0)                      {                          LLWebRTCVoiceClient::getInstance()->OnConnectionShutDown(mChannelID, mRegionID); @@ -2916,6 +2659,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()      return true;  } +// Data has been received on the webrtc data channel  void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binary)  {      // incoming data will be a json structure (if it's not binary.)  We may pack @@ -2926,8 +2670,9 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar      // integer indices into an agentid list, populated on join commands.  For size.      // Each key will point to a json object with keys identifying what's updated.      // 'p'  - audio source power (level/volume) (int8 as int) -    // 'j'  - join - object of join data (TBD) (true for now) +    // 'j'  - object of join data (currently only a boolean 'p' marking a primary participant)      // 'l'  - boolean, always true if exists. +    // 'v'  - boolean - voice activity has been detected.      if (binary)      { @@ -2959,11 +2704,19 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar              LLWebRTCVoiceClient::participantStatePtr_t participant =                  LLWebRTCVoiceClient::getInstance()->findParticipantByID(mChannelID, agent_id);              bool joined  = false; -            bool primary = false; +            bool primary = false;  // we ignore any 'joins' reported about participants +                                   // that come from voice servers that aren't their primary +                                   // voice server.  This will happen with cross-region voice +                                   // where a participant on a neighboring region may be +                                   // connected to multiple servers.  We don't want to  +                                   // add new identical participants from all of those servers.              if (voice_data[participant_id].isMember("j"))              { +                // a new participant has announced that they're joining.                  joined  = true;                  primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + +                // track incoming participants that are muted so we can mute their connections (or set their volume)                  bool isMuted = LLMuteList::getInstance()->isMuted(agent_id, LLMute::flagVoiceChat);                  if (isMuted)                  { @@ -2981,10 +2734,12 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar              {                  participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id);              } +              if (participant)              {                  if (voice_data[participant_id].get("l", Json::Value(false)).asBool())                  { +                    // an existing participant is leaving.                      if (agent_id != gAgentID)                      {                          LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); @@ -2992,6 +2747,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar                  }                  else                  { +                    // we got a 'power' update.                      F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128;                      // convert to decibles                      participant->mLevel = level; @@ -3003,6 +2759,9 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar                  }              }          } + +        // tell the simulator to set the mute and volume data for this +        // participant, if there are any updates.          Json::FastWriter writer;          Json::Value      root     = Json::objectValue;          if (mute.size() > 0) @@ -3030,6 +2789,12 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface      }  } +// tell the Secondlife WebRTC server that +// we're joining and whether we're +// joining a server associated with the +// the region we currently occupy or not (primary) +// The WebRTC voice server will pass this info +// to peers.  void LLVoiceWebRTCConnection::sendJoin()  {      Json::FastWriter writer; @@ -3048,7 +2813,9 @@ void LLVoiceWebRTCConnection::sendJoin()  /////////////////////////////  // WebRTC Spatial Connection -LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : +LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, +                                                               S32 parcelLocalID, +                                                               const std::string &channelID) :      LLVoiceWebRTCConnection(regionID, channelID),      mParcelLocalID(parcelLocalID)  { @@ -3066,52 +2833,6 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection()      mWebRTCPeerConnection->unsetSignalingObserver(this);  } - -bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() -{ -    LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); - -    LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; -    if (!regionp || !regionp->capabilitiesReceived()) -    { -        LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; -        return false; -    } - -    std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); -    if (url.empty()) -    { -        return false; -    } - -    LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; - -    LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); -    LLSD body; -    LLSD jsep; -    jsep["type"] = "offer"; -    { -        LLMutexLock lock(&mVoiceStateMutex); -        jsep["sdp"] = mChannelSDP; -    } -    body["jsep"] = jsep; -    if (mParcelLocalID != INVALID_PARCEL_ID) -    { -        body["parcel_local_id"] = mParcelLocalID; -    } - -    body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; - -    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( -        url, -        LLCore::HttpRequest::DEFAULT_POLICY_ID, -        body, -        boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), -        boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); -    mOutstandingRequests++; -    return true; -} -  void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted)  {      mMuted = muted; @@ -3124,7 +2845,9 @@ void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted)          }          else          { -            // always mute to regions the agent isn't on, to prevent echo. +            // Always mute this agent with respect to neighboring regions. +            // Peers don't want to hear this agent from multiple regions +            // as that'll echo.              mWebRTCAudioInterface->setMute(true);          }      } @@ -3153,12 +2876,15 @@ LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection()      mWebRTCPeerConnection->unsetSignalingObserver(this);  } - +// Add-hoc connections require a different channel type +// as they go to a different set of Secondlife WebRTC servers. +// They also require credentials for the given channels. +// So, we have a separate requestVoiceConnection call.  bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection()  {      LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); -    LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; +    LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL;      if (!regionp || !regionp->capabilitiesReceived())      {          LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; @@ -3171,8 +2897,6 @@ bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection()          return false;      } -    LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; -      LLVoiceWebRTCStats::getInstance()->provisionAttemptStart();      LLSD body;      LLSD jsep; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 46ca4e4eda..e6f01fd181 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -59,175 +59,137 @@ typedef boost::shared_ptr<LLVoiceWebRTCConnection> connectionPtr_t;  extern const std::string WEBRTC_VOICE_SERVER_TYPE; -class LLWebRTCVoiceClient :	public LLSingleton<LLWebRTCVoiceClient>, -							virtual public LLVoiceModuleInterface, -							virtual public LLVoiceEffectInterface, +class LLWebRTCVoiceClient : public LLSingleton<LLWebRTCVoiceClient>, +                            virtual public LLVoiceModuleInterface,                              public llwebrtc::LLWebRTCDevicesObserver,                              public LLMuteListObserver  {      LLSINGLETON_C11(LLWebRTCVoiceClient); -	LOG_CLASS(LLWebRTCVoiceClient); -	virtual ~LLWebRTCVoiceClient(); +    LOG_CLASS(LLWebRTCVoiceClient); +    virtual ~LLWebRTCVoiceClient();  public: -	/// @name LLVoiceModuleInterface virtual implementations -	///  @see LLVoiceModuleInterface -	//@{ -	void init(LLPumpIO *pump) override;	// Call this once at application startup (creates connector) -	void terminate() override;	// Call this to clean up during shutdown - -	static bool isShuttingDown() { return sShuttingDown; } -	 -	const LLVoiceVersionInfo& getVersion() override; -	 -	void updateSettings() override; // call after loading settings and whenever they change - -	// Returns true if WebRTC has successfully logged in and is not in error state	 -	bool isVoiceWorking() const override; - -	std::string sipURIFromID(const LLUUID &id) override; - -	///////////////////// -	/// @name Tuning -	//@{ -	void tuningStart() override; -	void tuningStop() override; -	bool inTuningMode() override; -	 -	void tuningSetMicVolume(float volume) override; -	void tuningSetSpeakerVolume(float volume) override; -	float tuningGetEnergy(void) override; -	//@} -	 -	///////////////////// -	/// @name Devices -	//@{ -	// This returns true when it's safe to bring up the "device settings" dialog in the prefs. -	bool deviceSettingsAvailable() override; -	bool deviceSettingsUpdated() override;  //return if the list has been updated and never fetched,  only to be called from the voicepanel. -	 -	// Requery the WebRTC daemon for the current list of input/output devices. -	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed -	// (use this if you want to know when it's done). -	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. -	void refreshDeviceLists(bool clearCurrentList = true) override; -	 -	void setCaptureDevice(const std::string& name) override; -	void setRenderDevice(const std::string& name) override; -	 -	LLVoiceDeviceList& getCaptureDevices() override; -	LLVoiceDeviceList& getRenderDevices() override; -	//@}	 -	 -	void getParticipantList(std::set<LLUUID> &participants) override; -	bool isParticipant(const LLUUID& speaker_id) override; - -	// Send a text message to the specified user, initiating the session if necessary. -	// virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; - -	// Returns true if calling back the session URI after the session has closed is possible. -	// Currently this will be false only for PSTN P2P calls.		 -	// NOTE: this will return true if the session can't be found.  -	BOOL isSessionCallBackPossible(const LLUUID &session_id) override; -	 -	// Returns true if the session can accepte text IM's. -	// Currently this will be false only for PSTN P2P calls. -	// NOTE: this will return true if the session can't be found.  -	BOOL isSessionTextIMPossible(const LLUUID &session_id) override; -	 -	 -	//////////////////////////// -	/// @name Channel stuff -	//@{ -	// returns true iff the user is currently in a proximal (local spatial) channel. -	// Note that gestures should only fire if this returns true. -	bool inProximalChannel() override; -	 -	void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override -	{ +    /// @name LLVoiceModuleInterface virtual implementations +    ///  @see LLVoiceModuleInterface +    //@{ +    void init(LLPumpIO *pump) override;    // Call this once at application startup (creates connector) +    void terminate() override;    // Call this to clean up during shutdown + +    static bool isShuttingDown() { return sShuttingDown; } +     +    const LLVoiceVersionInfo& getVersion() override; +     +    void updateSettings() override; // call after loading settings and whenever they change + +    // Returns true if WebRTC has successfully logged in and is not in error state     +    bool isVoiceWorking() const override; + +    std::string sipURIFromID(const LLUUID &id) override; + +    ///////////////////// +    /// @name Tuning +    //@{ +    void tuningStart() override; +    void tuningStop() override; +    bool inTuningMode() override; +     +    void tuningSetMicVolume(float volume) override; +    void tuningSetSpeakerVolume(float volume) override; +    float tuningGetEnergy(void) override; +    //@} +     +    ///////////////////// +    /// @name Devices +    //@{ +    // This returns true when it's safe to bring up the "device settings" dialog in the prefs. +    bool deviceSettingsAvailable() override; +    bool deviceSettingsUpdated() override;  //return if the list has been updated and never fetched,  only to be called from the voicepanel. +     +    // Requery the WebRTC daemon for the current list of input/output devices. +    // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed +    // (use this if you want to know when it's done). +    // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. +    void refreshDeviceLists(bool clearCurrentList = true) override; +     +    void setCaptureDevice(const std::string& name) override; +    void setRenderDevice(const std::string& name) override; +     +    LLVoiceDeviceList& getCaptureDevices() override; +    LLVoiceDeviceList& getRenderDevices() override; +    //@}     +     +    void getParticipantList(std::set<LLUUID> &participants) override; +    bool isParticipant(const LLUUID& speaker_id) override; + +    // Send a text message to the specified user, initiating the session if necessary. +    // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; + +    // Returns true if calling back the session URI after the session has closed is possible. +    // Currently this will be false only for PSTN P2P calls.         +    // NOTE: this will return true if the session can't be found.  +    BOOL isSessionCallBackPossible(const LLUUID &session_id) override; +     +    // WebRTC doesn't preclude text im +    BOOL isSessionTextIMPossible(const LLUUID &session_id) override { return TRUE; } +     +    //////////////////////////// +    /// @name Channel stuff +    //@{ +    // returns true iff the user is currently in a proximal (local spatial) channel. +    // Note that gestures should only fire if this returns true. +    bool inProximalChannel() override; +     +    void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override +    {          startAdHocSession(channelInfo, notify_on_first_join, hangup_on_last_leave); -	} -	 -	bool setSpatialChannel(const LLSD &channelInfo) override  -	{ +    } +     +    bool setSpatialChannel(const LLSD &channelInfo) override  +    {          processChannels(true); -		          return true; -	} -	 -	void leaveNonSpatialChannel() override; +    } +     +    void leaveNonSpatialChannel() override; -	void processChannels(bool process) override; +    void processChannels(bool process) override; -	void leaveChannel(bool stopTalking); +    void leaveChannel(bool stopTalking); -	bool isCurrentChannel(const LLSD &channelInfo) override; -	bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; -	//@} +    bool isCurrentChannel(const LLSD &channelInfo) override; +    bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; +    //@} -	LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; } +    LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; }      LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override { return nullptr; } -	 -	///////////////////////// -	/// @name Volume/gain -	//@{ -	void setVoiceVolume(F32 volume) override; -	void setMicGain(F32 volume) override; -	//@} -	 -	///////////////////////// -	/// @name enable disable voice and features -	//@{ -	void setVoiceEnabled(bool enabled) override; -	void setMuteMic(bool muted) override;		// Set the mute state of the local mic. -	//@} -		 -	////////////////////////// -	/// @name nearby speaker accessors -	//@{ -	BOOL getVoiceEnabled(const LLUUID& id) override;		// true if we've received data for this avatar -	std::string getDisplayName(const LLUUID& id) override; -	BOOL isParticipantAvatar(const LLUUID &id) override; -	BOOL getIsSpeaking(const LLUUID& id) override; -	BOOL getIsModeratorMuted(const LLUUID& id) override; -	F32 getCurrentPower(const LLUUID& id) override;		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... -	BOOL getOnMuteList(const LLUUID& id) override; -	F32 getUserVolume(const LLUUID& id) override; -	void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) -	//@} - -	////////////////////////// -    /// @name Effect Accessors +     +    ///////////////////////// +    /// @name Volume/gain      //@{ -    bool         setVoiceEffect(const LLUUID &id) override { return false; } -    const LLUUID getVoiceEffect() override { return LLUUID(); } -    LLSD         getVoiceEffectProperties(const LLUUID &id) override { return LLSD(); } - -    void                       refreshVoiceEffectLists(bool clear_lists) override {}; -    const voice_effect_list_t &getVoiceEffectList() const override { return mVoiceEffectList; } -    const voice_effect_list_t &getVoiceEffectTemplateList() const override { return mVoiceEffectList; } - -	voice_effect_list_t mVoiceEffectList; +    void setVoiceVolume(F32 volume) override; +    void setMicGain(F32 volume) override;      //@} - -    ////////////////////////////// -    /// @name Status notification +     +    ///////////////////////// +    /// @name enable disable voice and features      //@{ -    void addObserver(LLVoiceEffectObserver *observer) override {} -    void removeObserver(LLVoiceEffectObserver *observer) override {} +    void setVoiceEnabled(bool enabled) override; +    void setMuteMic(bool muted) override;        // Set the mute state of the local mic.      //@} - -	////////////////////////////// -    /// @name Preview buffer +         +    ////////////////////////// +    /// @name nearby speaker accessors      //@{ -    void enablePreviewBuffer(bool enable) override {} -    void recordPreviewBuffer() override {} -    void playPreviewBuffer(const LLUUID &effect_id = LLUUID::null) override {} -    void stopPreviewBuffer() override {} - -    bool isPreviewRecording() override { return false;  } -    bool isPreviewPlaying() override { return false; } +    BOOL getVoiceEnabled(const LLUUID& id) override;        // true if we've received data for this avatar +    std::string getDisplayName(const LLUUID& id) override; +    BOOL isParticipantAvatar(const LLUUID &id) override; +    BOOL getIsSpeaking(const LLUUID& id) override; +    BOOL getIsModeratorMuted(const LLUUID& id) override; +    F32 getCurrentPower(const LLUUID& id) override;        // "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... +    BOOL getOnMuteList(const LLUUID& id) override; +    F32 getUserVolume(const LLUUID& id) override; +    void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal)      //@}      ////////////////// @@ -237,76 +199,66 @@ public:      void onChangeDetailed(const LLMute& ) override;      //@} -	// authorize the user +    // authorize the user      void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; -	void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); +    void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID);      void OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID);      void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID);      void sendPositionUpdate(bool force);      void updateOwnVolume(); -	 -	////////////////////////////// -	/// @name Status notification -	//@{ -	void addObserver(LLVoiceClientStatusObserver* observer) override; -	void removeObserver(LLVoiceClientStatusObserver* observer) override; -	void addObserver(LLFriendObserver* observer) override; -	void removeObserver(LLFriendObserver* observer) override; -	void addObserver(LLVoiceClientParticipantObserver* observer) override; -	void removeObserver(LLVoiceClientParticipantObserver* observer) override; -	//@} - -	////////////////////////////// +     +    ////////////////////////////// +    /// @name Status notification +    //@{ +    void addObserver(LLVoiceClientStatusObserver* observer) override; +    void removeObserver(LLVoiceClientStatusObserver* observer) override; +    void addObserver(LLFriendObserver* observer) override; +    void removeObserver(LLFriendObserver* observer) override; +    void addObserver(LLVoiceClientParticipantObserver* observer) override; +    void removeObserver(LLVoiceClientParticipantObserver* observer) override; +    //@} + +    //////////////////////////////      /// @name Devices change notification -	//  LLWebRTCDevicesObserver +    //  LLWebRTCDevicesObserver      //@{      void OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices,                            const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override;      //@} -	struct participantState -	{ -	public: -		participantState(const LLUUID& agent_id); -		 -	    bool updateMuteState();	// true if mute state has changed -		bool isAvatar(); -		 -		std::string mURI; -		LLUUID mAvatarID; -		std::string mDisplayName; -		LLFrameTimer mSpeakingTimeout; -		F32	mLastSpokeTimestamp; -		F32 mLevel; -		F32 mVolume; -		std::string mGroupID; -		int mUserVolume; -		bool mPTT; -		bool mIsSpeaking; -		bool mIsModeratorMuted; -		bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted) -		bool mVolumeSet;		// true if incoming volume messages should not change the volume -		bool mVolumeDirty;		// true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) -		bool mAvatarIDValid; -		bool mIsSelf; -	}; +    struct participantState +    { +    public: +        participantState(const LLUUID& agent_id); +         +        bool updateMuteState();    // true if mute state has changed +        bool isAvatar(); +         +        std::string mURI; +        LLUUID mAvatarID; +        std::string mDisplayName; +        LLFrameTimer mSpeakingTimeout; +        F32 mLevel; // the current audio level of the participant +        F32 mVolume; // the gain applied to the participant +        bool mIsSpeaking; +        bool mIsModeratorMuted; +        bool mOnMuteList;        // true if this avatar is on the user's mute list (and should be muted) +    };      typedef boost::shared_ptr<participantState> participantStatePtr_t; -    typedef boost::weak_ptr<participantState> participantStateWptr_t; -    participantStatePtr_t                       findParticipantByID(const std::string &channelID, const LLUUID &id); -    participantStatePtr_t                       addParticipantByID(const std::string& channelID, const LLUUID &id); -    void                                        removeParticipantByID(const std::string& channelID, const LLUUID &id); +    participantStatePtr_t findParticipantByID(const std::string &channelID, const LLUUID &id); +    participantStatePtr_t addParticipantByID(const std::string& channelID, const LLUUID &id); +    void                  removeParticipantByID(const std::string& channelID, const LLUUID &id);    protected: -    typedef std::map<const std::string, participantStatePtr_t> participantMap;      typedef std::map<const LLUUID, participantStatePtr_t> participantUUIDMap; -	 -	class sessionState -	{ +     +    class sessionState +    {      public:          typedef boost::shared_ptr<sessionState> ptr_t;          typedef boost::weak_ptr<sessionState> wptr_t; @@ -314,28 +266,24 @@ public:          typedef boost::function<void(const ptr_t &)> sessionFunc_t;          static void addSession(const std::string &channelID, ptr_t& session); -		virtual ~sessionState(); -		 +        virtual ~sessionState(); +                  participantStatePtr_t addParticipant(const LLUUID& agent_id);          void removeParticipant(const participantStatePtr_t &participant); -		void removeAllParticipants(); +        void removeAllParticipants(); -        participantStatePtr_t findParticipant(const std::string &uri);          participantStatePtr_t findParticipantByID(const LLUUID& id);          static ptr_t matchSessionByChannelID(const std::string& channel_id); -		void shutdownAllConnections(); +        void shutdownAllConnections();          void revive(); -		bool isCallBackPossible(); -		bool isTextIMPossible(); - -		static void processSessionStates(); +        static void processSessionStates();          virtual bool processConnectionStates(); -		virtual void sendData(const std::string &data); +        virtual void sendData(const std::string &data);          void setMuteMic(bool muted);          void setMicGain(F32 volume); @@ -343,80 +291,61 @@ public:          void setUserVolume(const LLUUID& id, F32 volume);          void setUserMute(const LLUUID& id, bool mute); -		 +                  static void for_each(sessionFunc_t func); -		static void reapEmptySessions(); +        static void reapEmptySessions(); -		bool isEmpty() { return mWebRTCConnections.empty(); } +        bool isEmpty() { return mWebRTCConnections.empty(); } -		virtual bool isSpatial() = 0; +        virtual bool isSpatial() = 0;          virtual bool isEstate()  = 0; +        virtual bool isCallbackPossible() = 0; -		std::string mHandle; -		std::string mGroupHandle; -		std::string mChannelID; -		std::string mAlias; -		std::string mName; -		std::string mErrorStatusString; -		std::queue<std::string> mTextMsgQueue; +        std::string mHandle; +        std::string mChannelID; +        std::string mName; -        bool        mMuted;          // this session is muted. -        F32         mMicGain;        // gain for this session. -        F32         mSpeakerVolume;  // volume for this session. -		 -		LLUUID		mIMSessionID; -		LLUUID		mCallerID; -		int			mErrorStatusCode; - -		bool		mIncoming; -		bool		mVoiceActive; -		bool		mReconnect;	// Whether we should try to reconnect to this session if it's dropped -        bool        mShuttingDown; +        bool    mMuted;          // this session is muted. +        F32     mMicGain;        // gain for this session. +        F32     mSpeakerVolume;  // volume for this session. -		// Set to true when the volume/mute state of someone in the participant list changes. -		// The code will have to walk the list to find the changed participant(s). -		bool		mVolumeDirty; -		bool		mMuteDirty; - -		participantMap mParticipantsByURI; -		participantUUIDMap mParticipantsByUUID; +        bool        mShuttingDown; -		LLUUID		mVoiceFontID; +        participantUUIDMap mParticipantsByUUID;          static bool hasSession(const std::string &sessionID)  -		{ return mSessions.find(sessionID) != mSessions.end(); } +        { return mSessions.find(sessionID) != mSessions.end(); } -       bool mHangupOnLastLeave; -       bool mNotifyOnFirstJoin; +       bool mHangupOnLastLeave;  // notify observers after the session becomes empty. +       bool mNotifyOnFirstJoin;  // notify observers when the first peer joins.      protected:          sessionState(); -	    std::list<connectionPtr_t> mWebRTCConnections; +        std::list<connectionPtr_t> mWebRTCConnections;      private:          static std::map<std::string, ptr_t> mSessions;  // canonical list of outstanding sessions. -        static void for_eachPredicate(const std::pair<std::string, LLWebRTCVoiceClient::sessionState::wptr_t> &a, sessionFunc_t func); - -        static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); -        static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); - -	}; +        static void for_eachPredicate(const std::pair<std::string, +                                      LLWebRTCVoiceClient::sessionState::wptr_t> &a, +                                      sessionFunc_t func); +    };      typedef boost::shared_ptr<sessionState> sessionStatePtr_t;      typedef std::map<std::string, sessionStatePtr_t> sessionMap; -	class estateSessionState : public sessionState -	{ +    class estateSessionState : public sessionState +    {        public:          estateSessionState();          bool processConnectionStates() override; -		bool isSpatial() override { return true; } +        bool isSpatial() override { return true; }          bool isEstate() override { return true; } -	}; +        bool isCallbackPossible() override { return false; } +    };      class parcelSessionState : public sessionState      { @@ -425,31 +354,34 @@ public:          bool isSpatial() override { return true; }          bool isEstate() override { return false; } +        bool isCallbackPossible() override { return false; }      };      class adhocSessionState : public sessionState      {      public:          adhocSessionState(const std::string &channelID, -			const std::string& credentials, -			bool notify_on_first_join, -			bool hangup_on_last_leave); +            const std::string& credentials, +            bool notify_on_first_join, +            bool hangup_on_last_leave);          bool isSpatial() override { return false; }          bool isEstate() override { return false; } -		// don't send spatial data to adhoc sessions. -		void sendData(const std::string &data) override { } +        // only p2p-type adhoc sessions allow callback +        bool isCallbackPossible() override { return mNotifyOnFirstJoin && mHangupOnLastLeave; } -	protected: +        // don't send spatial data to adhoc sessions. +        void sendData(const std::string &data) override { } + +    protected:          std::string mCredentials;      }; -	 -	/////////////////////////////////////////////////////// -	// Private Member Functions -	////////////////////////////////////////////////////// +    /////////////////////////////////////////////////////// +    // Private Member Functions +    //////////////////////////////////////////////////////      static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data);      static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); @@ -460,236 +392,148 @@ public:      static void predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, bool mute);      static void predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, F32 volume); -	////////////////////////////// -	/// @name TVC/Server management and communication -	//@{ -	 -	// Call this if we're just giving up on voice (can't provision an account, etc.).  It will clean up and go away. -	void giveUp();	 -	 -//	void requestVoiceAccountProvision(S32 retries = 3); -	 -	 -	//@} - -	//---------------------------------- -	// devices -	void clearCaptureDevices(); -	void addCaptureDevice(const LLVoiceDevice& device); +    //---------------------------------- +    // devices +    void clearCaptureDevices(); +    void addCaptureDevice(const LLVoiceDevice& device);      void clearRenderDevices(); -	void addRenderDevice(const LLVoiceDevice& device);	 -	void setDevicesListUpdated(bool state); -	void buildSetAudioDevices(std::ostringstream &stream); -	 -	// local audio updates, mic mute, speaker mute, mic volume and speaker volumes -	void sendLocalAudioUpdates(); - -	///////////////////////////// -	// Event handlers - -	void muteListChanged(); - -	///////////////////////////// -	// Sending updates of current state -	void updatePosition(void); -	void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); -	void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); -	bool channelFromRegion(LLViewerRegion *region, std::string &name); - -	LLVector3d getListenerPosition() { return mListenerPosition; } -    LLVector3d getSpeakerPosition() { return mAvatarPosition; } +    void addRenderDevice(const LLVoiceDevice& device);     +    void setDevicesListUpdated(bool state); -	void setEarLocation(S32 loc); +    ///////////////////////////// +    // Sending updates of current state +    void updatePosition(void); +    void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); +    void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); -	 -	///////////////////////////// -	// Accessors for data related to nearby speakers +    LLVector3d getListenerPosition() { return mListenerPosition; } +    LLVector3d getSpeakerPosition() { return mAvatarPosition; } -	// MBW -- XXX -- Not sure how to get this data out of the TVC -	BOOL getUsingPTT(const LLUUID& id); -	std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable) +    void setEarLocation(S32 loc); -	///////////////////////////// -	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled. -										// Use this to determine whether to show a "no speech" icon in the menu bar. -    void              sessionEstablished(const LLUUID& region_id); +    ///////////////////////////// +    // Accessors for data related to nearby speakers + +    /////////////////////////////      sessionStatePtr_t findP2PSession(const LLUUID &agent_id); -	 +          sessionStatePtr_t addSession(const std::string &channel_id, sessionState::ptr_t session);      void deleteSession(const sessionStatePtr_t &session); - -	// This is called in several places where the session _may_ need to be deleted. -	// It contains logic for whether to delete the session or keep it around. -    void reapSession(const sessionStatePtr_t &session); -	 -	// Does the actual work to get out of the audio session -	void leaveAudioSession(); -	 -	friend class LLWebRTCVoiceClientCapResponder; -	 -	 -	void lookupName(const LLUUID &id); -	void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); -	void avatarNameResolved(const LLUUID &id, const std::string &name); +     +    // Does the actual work to get out of the audio session +    void leaveAudioSession(); +     +    friend class LLWebRTCVoiceClientCapResponder; +     +     +    void lookupName(const LLUUID &id); +    void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +    void avatarNameResolved(const LLUUID &id, const std::string &name);      static void predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name); -	boost::signals2::connection mAvatarNameCacheConnection; - -	///////////////////////////// -	// Voice fonts - -	void addVoiceFont(const S32 id, -					  const std::string &name, -					  const std::string &description, -					  const LLDate &expiration_date, -					  bool  has_expired, -					  const S32 font_type, -					  const S32 font_status, -					  const bool template_font = false); -	void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); -	void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString);  +    boost::signals2::connection mAvatarNameCacheConnection;  private: -	float getAudioLevel(); -     -	LLVoiceVersionInfo mVoiceVersion; +    // helper function to retrieve the audio level +    // Used in multiple places. +    float getAudioLevel();      // Coroutine support methods      //---      void voiceConnectionCoro(); -    void voiceConnectionStateMachine(); -      //---      /// Clean up objects created during a voice session. -	void cleanUp(); - -	bool mRelogRequested; -	// Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). -	// The larger it is the greater is possibility there is a problem with connection to voice server. -	// Introduced while fixing EXT-4313. -	int mSpatialJoiningNum; -	 -	static void idle(void *user_data); -			 -	bool mTuningMode; -	F32 mTuningMicGain; -	int mTuningSpeakerVolume; -	bool mTuningSpeakerVolumeDirty; -	bool mDevicesListUpdated;			// set to true when the device list has been updated -										// and false when the panelvoicedevicesettings has queried for an update status. -	std::string mSpatialSessionCredentials; - -	std::string mMainSessionGroupHandle; // handle of the "main" session group. -	 -	bool mAreaVoiceDisabled; +    void cleanUp(); +             +    bool mTuningMode; +    F32 mTuningMicGain; +    int mTuningSpeakerVolume; +    bool mDevicesListUpdated;            // set to true when the device list has been updated +                                        // and false when the panelvoicedevicesettings has queried for an update status. +    std::string mSpatialSessionCredentials; + +    std::string mMainSessionGroupHandle; // handle of the "main" session group. +          sessionStatePtr_t mSession;    // Session state for the current session -    sessionStatePtr_t mNextSession;	// Session state for the session we're trying to join - -	S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings -	std::string mCurrentRegionName;		// Used to detect parcel boundary crossings -		 -	bool mBuddyListMapPopulated; -	bool mBlockRulesListReceived; -	bool mAutoAcceptRulesListReceived; -	 -	llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; +    sessionStatePtr_t mNextSession;    // Session state for the session we're trying to join -	LLVoiceDeviceList mCaptureDevices; -	LLVoiceDeviceList mRenderDevices; +    llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; -	bool mIsInitialized; -	bool mShutdownComplete; +    LLVoiceDeviceList mCaptureDevices; +    LLVoiceDeviceList mRenderDevices; -	bool startEstateSession(); +    bool startEstateSession();      bool startParcelSession(const std::string& channelID, S32 parcelID);      bool startAdHocSession(const LLSD &channelInfo, bool notify_on_first_join, bool hangup_on_last_leave); -	 -	std::string nameFromID(const LLUUID &id); -	bool IDFromName(const std::string name, LLUUID &uuid); -	bool inSpatialChannel(); +    bool inSpatialChannel();      bool inOrJoiningChannel(const std::string &channelID);      bool inEstateChannel(); -	LLSD getAudioSessionChannelInfo(); -			 +    LLSD getAudioSessionChannelInfo(); +                  void setHidden(bool hidden) override; //virtual -	void enforceTether(); +    void enforceTether(); -	void updateNeighboringRegions(); +    void updateNeighboringRegions();      std::set<LLUUID> getNeighboringRegions() { return mNeighboringRegions; } -	 -	bool		mSpatialCoordsDirty; -	 -	LLVector3d	mListenerPosition; -	LLVector3d	mListenerRequestedPosition; -	LLVector3	mListenerVelocity; -	LLQuaternion mListenerRot; - -	LLVector3d	mAvatarPosition; -	LLVector3	mAvatarVelocity; -	LLQuaternion mAvatarRot; - -	std::set<LLUUID> mNeighboringRegions; // includes current region -	 -	bool		mMuteMic; -	bool		mMuteMicDirty; -    bool        mHidden;       //Set to true during teleport to hide the agent's position. -			 -	// Set to true when the friends list is known to have changed. -	bool		mFriendsListDirty; -	 -	enum -	{ -		earLocCamera = 0,		// ear at camera -		earLocAvatar,			// ear at avatar -		earLocMixed				// ear at avatar location/camera direction -	}; -	 -	S32			mEarLocation;   -	 -	bool		mSpeakerVolumeDirty; -	float		mSpeakerVolume; - -	F32			mMicGain; -	 -	bool		mVoiceEnabled; -    bool        mProcessChannels; -	 -	BOOL		mLipSyncEnabled; - -	typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t; -	observer_set_t mParticipantObservers; - -	void notifyParticipantObservers(); - -	typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t; -	status_observer_set_t mStatusObservers; -	 -	void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); - -	typedef std::set<LLFriendObserver*> friend_observer_set_t; -	friend_observer_set_t mFriendObservers; -	void notifyFriendObservers(); - -	S32     mPlayRequestCount; + +    LLVoiceVersionInfo mVoiceVersion; +     +    bool         mSpatialCoordsDirty; +     +    LLVector3d   mListenerPosition; +    LLVector3d   mListenerRequestedPosition; +    LLVector3    mListenerVelocity; +    LLQuaternion mListenerRot; + +    LLVector3d   mAvatarPosition; +    LLVector3    mAvatarVelocity; +    LLQuaternion mAvatarRot; + +    std::set<LLUUID> mNeighboringRegions; // includes current region +     +    bool         mMuteMic; +    bool         mHidden;       //Set to true during teleport to hide the agent's position. +     +    enum +    { +        earLocCamera = 0,        // ear at camera +        earLocAvatar,            // ear at avatar +        earLocMixed              // ear at avatar location/camera direction +    }; +     +    S32   mEarLocation;   +  +    float mSpeakerVolume; + +    F32   mMicGain; +     +    bool  mVoiceEnabled; +    bool  mProcessChannels; + +    typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t; +    observer_set_t mParticipantObservers; + +    void notifyParticipantObservers(); + +    typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t; +    status_observer_set_t mStatusObservers; +     +    void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); +      bool    mIsInTuningMode; -    bool    mIsJoiningSession; -    bool    mIsWaitingForFonts; -    bool    mIsLoggingIn;      bool    mIsProcessingChannels;      bool    mIsCoroutineActive;      // These variables can last longer than WebRTC in coroutines so we need them as static      static bool sShuttingDown; -    static bool sConnected; -    static LLPumpIO* sPump;      LLEventMailDrop mWebRTCPump;  }; @@ -728,15 +572,15 @@ class LLVoiceWebRTCStats : public LLSingleton<LLVoiceWebRTCStats>  };  class LLVoiceWebRTCConnection :  -	public llwebrtc::LLWebRTCSignalingObserver, -	public llwebrtc::LLWebRTCDataObserver +    public llwebrtc::LLWebRTCSignalingObserver, +    public llwebrtc::LLWebRTCDataObserver  {    public:      LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID);      virtual ~LLVoiceWebRTCConnection() = 0; -	////////////////////////////// +    //////////////////////////////      /// @name Signaling notification      //  LLWebRTCSignalingObserver      //@{ @@ -759,7 +603,7 @@ class LLVoiceWebRTCConnection :      void sendJoin();      void sendData(const std::string &data); -	virtual void processIceUpdates(); +    virtual void processIceUpdates();      virtual void onIceUpdateComplete(bool ice_completed, const LLSD &result);      virtual void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); @@ -772,9 +616,9 @@ class LLVoiceWebRTCConnection :      bool connectionStateMachine(); -	virtual bool isSpatial() = 0; +    virtual bool isSpatial() = 0; -	LLUUID getRegionID() { return mRegionID; } +    LLUUID getRegionID() { return mRegionID; }      void shutDown()      { @@ -840,16 +684,16 @@ class LLVoiceWebRTCConnection :      void OnVoiceDisconnectionRequestSuccess(const LLSD &body);      void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); -	LLUUID mRegionID; +    LLUUID mRegionID;      LLUUID mViewerSession;      std::string mChannelID;      std::string mChannelSDP;      std::string mRemoteChannelSDP; -	bool mMuted; -	F32  mMicGain; -	F32  mSpeakerVolume; +    bool mMuted; +    F32  mMicGain; +    F32  mSpeakerVolume;      bool mShutDown;      S32  mOutstandingRequests; @@ -865,7 +709,7 @@ class LLVoiceWebRTCConnection :  class LLVoiceWebRTCSpatialConnection : -	public LLVoiceWebRTCConnection +    public LLVoiceWebRTCConnection  {    public:      LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); @@ -874,12 +718,12 @@ class LLVoiceWebRTCSpatialConnection :      void setMuteMic(bool muted) override; -	bool isSpatial() override { return true; } +    bool isSpatial() override { return true; }  protected: -	bool requestVoiceConnection() override; +    bool requestVoiceConnection() override;      S32    mParcelLocalID;  }; @@ -891,12 +735,12 @@ class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection      virtual ~LLVoiceWebRTCAdHocConnection(); -	bool isSpatial() override { return false; } +    bool isSpatial() override { return false; }    protected:      bool requestVoiceConnection() override; -	std::string mCredentials; +    std::string mCredentials;  };  #define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__); | 
