diff options
| -rw-r--r-- | indra/llwebrtc/llwebrtc.cpp | 79 | ||||
| -rw-r--r-- | indra/llwebrtc/llwebrtc.h | 8 | ||||
| -rw-r--r-- | indra/llwebrtc/llwebrtc_impl.h | 7 | ||||
| -rw-r--r-- | indra/newview/llvoicewebrtc.cpp | 2574 | ||||
| -rw-r--r-- | indra/newview/llvoicewebrtc.h | 398 | 
5 files changed, 1099 insertions, 1967 deletions
| diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index bf1e04c2f2..b2f5e0212e 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -216,6 +216,7 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id)      mWorkerThread->PostTask(                              [this, id]()                              { +                                int16_t tuningRecordingDevice = 0;                                  int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices();                                  for (int16_t i = 0; i < captureDeviceCount; i++)                                  { @@ -225,18 +226,32 @@ void LLWebRTCImpl::setCaptureDevice(const std::string &id)                                      if (id == guid || id == "Default")  // first one in list is default                                      {                                          RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; -                                        mRecordingDevice = i; +                                        tuningRecordingDevice = i;                                          break;                                      }                                  }                                  mTuningDeviceModule->StopRecording(); -                                mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); +                                mTuningDeviceModule->SetRecordingDevice(tuningRecordingDevice);                                  mTuningDeviceModule->InitMicrophone();                                  mTuningDeviceModule->InitRecording();                                  mTuningDeviceModule->StartRecording();                                  bool was_peer_recording = false;                                  if (mPeerDeviceModule)                                  { +                                    int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); +                                    for (int16_t i = 0; i < captureDeviceCount; i++) +                                    { +                                        char name[webrtc::kAdmMaxDeviceNameSize]; +                                        char guid[webrtc::kAdmMaxGuidSize]; +                                        mTuningDeviceModule->RecordingDeviceName(i, name, guid); +                                        if (id == guid || id == "Default")  // first one in list is default +                                        { +                                            RTC_LOG(LS_INFO) +                                                << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; +                                            mRecordingDevice = i; +                                            break; +                                        } +                                    }                                      was_peer_recording = mPeerDeviceModule->Recording();                                      if (was_peer_recording)                                      { @@ -259,6 +274,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)                              [this, id]()                              {                                  int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); +                                int16_t tuningPlayoutDevice = 0;                                  for (int16_t i = 0; i < renderDeviceCount; i++)                                  {                                      char name[webrtc::kAdmMaxDeviceNameSize]; @@ -267,7 +283,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)                                      if (id == guid || id == "Default")                                      {                                          RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; -                                        mPlayoutDevice = i; +                                        tuningPlayoutDevice = i;                                          break;                                      }                                  } @@ -277,30 +293,40 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)                                  {                                      mTuningDeviceModule->StopPlayout();                                  } -                                bool was_peer_mute = false;                                  if (mPeerDeviceModule)                                  { -                                    mPeerDeviceModule->SpeakerMute(&was_peer_mute); -                                    if (!was_peer_mute) -                                    { -                                        mPeerDeviceModule->SetSpeakerMute(true); -                                    } +                                    mPeerDeviceModule->SetSpeakerMute(true);                                  } -                                mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); +                                mTuningDeviceModule->SetPlayoutDevice(tuningPlayoutDevice);                                  mTuningDeviceModule->InitSpeaker();                                  mTuningDeviceModule->InitPlayout();                                  if (was_tuning_playing)                                  {                                      mTuningDeviceModule->StartPlayout();                                  } +                                renderDeviceCount = mPeerDeviceModule->PlayoutDevices(); +                                  if (mPeerDeviceModule)                                  { +                                    for (int16_t i = 0; i < renderDeviceCount; i++) +                                    { +                                        char name[webrtc::kAdmMaxDeviceNameSize]; +                                        char guid[webrtc::kAdmMaxGuidSize]; +                                        mPeerDeviceModule->PlayoutDeviceName(i, name, guid); +                                        if (id == guid || id == "Default") +                                        { +                                            RTC_LOG(LS_INFO) +                                                << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; +                                            mPlayoutDevice = i; +                                            break; +                                        } +                                    }                                      mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice);                                      mPeerDeviceModule->InitSpeaker();                                      mPeerDeviceModule->InitPlayout();                                      mPeerDeviceModule->StartPlayout(); -                                    mPeerDeviceModule->SetSpeakerMute(was_peer_mute); +                                    mPeerDeviceModule->SetSpeakerMute(false);                                  }                                  mTuningDeviceModule->SetSpeakerMute(false); @@ -376,6 +402,8 @@ float LLWebRTCImpl::getPeerAudioLevel() { return 20 * mPeerAudioDeviceObserver->  void LLWebRTCImpl::setSpeakerVolume(float volume) { mPeerDeviceModule->SetSpeakerVolume( (uint32_t)(volume * VOLUME_SCALE_WEBRTC));}  void LLWebRTCImpl::setMicrophoneVolume(float volume) { mPeerDeviceModule->SetMicrophoneVolume((uint32_t)(volume * VOLUME_SCALE_WEBRTC));} +void LLWebRTCImpl::setMute(bool mute) { mPeerDeviceModule->SetMicrophoneMute(mute); } +  //  // Helpers  // @@ -566,9 +594,12 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)      mWebRTCImpl->PostSignalingTask(                                 [this, sdp]()                                 { -                                   RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); -                                   mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), -                                                                         rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>(this)); +                                   if (mPeerConnection) +                                   { +                                       RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state(); +                                       mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp), +                                                                             rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>(this)); +                                   }                                                    });  } @@ -844,17 +875,6 @@ void LLWebRTCPeerConnectionImpl::OnSetLocalDescriptionComplete(webrtc::RTCError  } -void LLWebRTCPeerConnectionImpl::setAudioObserver(LLWebRTCAudioObserver *observer) { mAudioObserverList.emplace_back(observer); } - -void LLWebRTCPeerConnectionImpl::unsetAudioObserver(LLWebRTCAudioObserver *observer) -{ -    std::vector<LLWebRTCAudioObserver *>::iterator it = std::find(mAudioObserverList.begin(), mAudioObserverList.end(), observer); -    if (it != mAudioObserverList.end()) -    { -        mAudioObserverList.erase(it); -    } -} -  //  // DataChannelObserver implementation  // @@ -897,9 +917,12 @@ void LLWebRTCPeerConnectionImpl::OnMessage(const webrtc::DataBuffer& buffer)  void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary)  { -    rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length()); -    webrtc::DataBuffer buffer(cowBuffer, binary); -    mDataChannel->Send(buffer); +    if (mDataChannel) +    { +        rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length()); +        webrtc::DataBuffer     buffer(cowBuffer, binary); +        mDataChannel->Send(buffer); +    }  }  void LLWebRTCPeerConnectionImpl::setDataObserver(LLWebRTCDataObserver* observer) { mDataObserverList.emplace_back(observer); } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 753fe6a983..ed80fa5648 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -88,19 +88,13 @@ class LLWebRTCDeviceInterface      virtual void setSpeakerVolume(float volume) = 0;  // volume between 0.0 and 1.0      virtual void setMicrophoneVolume(float volume) = 0;  // volume between 0.0 and 1.0 +    virtual void setMute(bool mute) = 0;      virtual float getPeerAudioLevel() = 0;  }; -class LLWebRTCAudioObserver -{ -  public: -}; -  class LLWebRTCAudioInterface  {    public: -    virtual void setAudioObserver(LLWebRTCAudioObserver *observer)  = 0; -    virtual void unsetAudioObserver(LLWebRTCAudioObserver *observer) = 0;      virtual void setMute(bool mute) = 0;  }; diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index eb439b5a46..76e29c63fb 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -118,7 +118,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface      float getPeerAudioLevel() override;      void setSpeakerVolume(float volume) override; // range 0.0-1.0 -    void setMicrophoneVolume(float volume) override; // range 0.0-1.0     +    void setMicrophoneVolume(float volume) override; // range 0.0-1.0   +    void setMute(bool mute) override;      //      // Helpers @@ -227,8 +228,6 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection,      //      // LLWebRTCAudioInterface      // -    void setAudioObserver(LLWebRTCAudioObserver *observer) override; -    void unsetAudioObserver(LLWebRTCAudioObserver *observer) override;      void setMute(bool mute) override;      // @@ -292,8 +291,6 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection,      bool                                                       mAnswerReceived;      rtc::scoped_refptr<webrtc::PeerConnectionInterface>        mPeerConnection; -     -    std::vector<LLWebRTCAudioObserver *>                       mAudioObserverList;      std::vector<LLWebRTCDataObserver *>                        mDataObserverList;      rtc::scoped_refptr<webrtc::DataChannelInterface>           mDataChannel; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 93efd2526d..31be3daed3 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -51,6 +51,7 @@  #include "llagent.h"  #include "llcachename.h"  #include "llimview.h" // for LLIMMgr +#include "llworld.h"  #include "llparcel.h"  #include "llviewerparcelmgr.h"  #include "llfirstuse.h" @@ -230,7 +231,6 @@ LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr;  LLWebRTCVoiceClient::LLWebRTCVoiceClient() :      mSessionTerminateRequested(false),      mRelogRequested(false), -    mTerminateDaemon(false),      mSpatialJoiningNum(0),      mTuningMode(false), @@ -247,11 +247,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() :      mNextAudioSession(),      mCurrentParcelLocalID(0), -    mConnectorEstablished(false), -    mAccountLoggedIn(false), -    mNumberOfAliases(0), -    mCommandCookie(0), -    mLoginRetryCount(0),      mBuddyListMapPopulated(false),      mBlockRulesListReceived(false), @@ -271,7 +266,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() :      mMicVolumeDirty(true),      mVoiceEnabled(false), -    mWriteInProgress(false),      mLipSyncEnabled(false), @@ -280,17 +274,13 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() :      mAvatarNameCacheConnection(),      mIsInTuningMode(false), -    mIsInChannel(false),      mIsJoiningSession(false),      mIsWaitingForFonts(false),      mIsLoggingIn(false), -    mIsLoggedIn(false),      mIsProcessingChannels(false),      mIsCoroutineActive(false),      mWebRTCPump("WebRTCClientPump"), -    mWebRTCDeviceInterface(nullptr), -    mWebRTCPeerConnection(nullptr), -    mWebRTCAudioInterface(nullptr) +    mWebRTCDeviceInterface(nullptr)  {      sShuttingDown = false;      sConnected = false; @@ -339,15 +329,12 @@ void LLWebRTCVoiceClient::init(LLPumpIO *pump)  	// constructor will set up LLVoiceClient::getInstance()  	sPump = pump; -//     LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", -//         boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); +//     LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", +//         boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance()));      llwebrtc::init();  	mWebRTCDeviceInterface = llwebrtc::getDeviceInterface();      mWebRTCDeviceInterface->setDevicesObserver(this); - -    mWebRTCPeerConnection = llwebrtc::newPeerConnection(); -    mWebRTCPeerConnection->setSignalingObserver(this);  }  void LLWebRTCVoiceClient::terminate() @@ -371,7 +358,7 @@ void LLWebRTCVoiceClient::cleanUp()  {      LL_DEBUGS("Voice") << LL_ENDL; -	deleteAllSessions(); +	sessionState::deleteAllSessions();      LL_DEBUGS("Voice") << "exiting" << LL_ENDL;  } @@ -401,1039 +388,143 @@ void LLWebRTCVoiceClient::updateSettings()  /////////////////////////////  // session control messages -void LLWebRTCVoiceClient::connectorCreate() -{ -} - -void LLWebRTCVoiceClient::connectorShutdown() +void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID)  { - -	mShutdownComplete = true; +    session->OnConnectionEstablished(channelID);  } -void LLWebRTCVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID)  { - -	mAccountDisplayName = user_id; - -	LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - -	mAccountName = nameFromID(agentID); +    session->OnConnectionFailure(channelID);  } -void LLWebRTCVoiceClient::setLoginInfo( -	const std::string& account_name, -	const std::string& password, -	const std::string& channel_sdp) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID)  { -	mRemoteChannelSDP = channel_sdp; -    mWebRTCPeerConnection->AnswerAvailable(channel_sdp); - -	if(mAccountLoggedIn) -	{ -		// Already logged in. -		LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; -		 -		// Don't process another login. -		return; -	} -	else if ( account_name != mAccountName ) -	{ -		LL_WARNS("Voice") << "Mismatched account name! " << account_name -                          << " instead of " << mAccountName << LL_ENDL; -	} -	else -	{ -		mAccountPassword = password; -	} +    sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID));  } -void LLWebRTCVoiceClient::idle(void* user_data) +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID)  { -} - -//========================================================================= -// 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. -//  -//  - -typedef enum e_voice_control_coro_state -{ -    VOICE_STATE_ERROR = -1, -    VOICE_STATE_TP_WAIT = 0, // entry point -    VOICE_STATE_START_DAEMON, -    VOICE_STATE_PROVISION_ACCOUNT, -    VOICE_STATE_SESSION_PROVISION_WAIT, -    VOICE_STATE_START_SESSION, -	VOICE_STATE_WAIT_FOR_SESSION_START, -    VOICE_STATE_SESSION_RETRY, -    VOICE_STATE_SESSION_ESTABLISHED, -    VOICE_STATE_WAIT_FOR_CHANNEL, -    VOICE_STATE_DISCONNECT, -    VOICE_STATE_WAIT_FOR_EXIT, -} EVoiceControlCoroState; - -void LLWebRTCVoiceClient::voiceControlCoro() -{ -    int state = 0; -    try +    if (mNextAudioSession && mNextAudioSession->mChannelID == channelID)      { -        // state is passed as a reference instead of being -        // a member due to unresolved issues with coroutine -        // surviving longer than LLWebRTCVoiceClient -        voiceControlStateMachine(); -    } -    catch (const LLCoros::Stop&) -    { -        LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL; -    } -    catch (const LLContinueError&) -    { -        LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient"); -    } -    catch (...) -    { -        // Ideally for Windows need to log SEH exception instead or to set SEH -        // handlers but bugsplat shows local variables for windows, which should -        // be enough -        LL_WARNS("Voice") << "voiceControlStateMachine crashed in state " << state << LL_ENDL; -        throw; -    } -} - -void LLWebRTCVoiceClient::voiceControlStateMachine() -{ -    if (sShuttingDown) -    { -        return; -    } - -    LL_DEBUGS("Voice") << "starting" << LL_ENDL; -    mIsCoroutineActive = true; -    LLCoros::set_consuming(true); - -    U32 retry = 0; -    U32 provisionWaitTimeout = 0; - -    setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - -    do -    { -        if (sShuttingDown) -        { -            // WebRTC singleton performed the exit, logged out, -            // cleaned sockets, gateway and no longer cares -            // about state of coroutine, so just stop -            return; -        } - -		processIceUpdates(); - -        switch (getVoiceControlState()) -        { -            case VOICE_STATE_TP_WAIT: -                // starting point for voice -                if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) -                { -                    LL_DEBUGS("Voice") << "Suspending voiceControlCoro() momentarily for teleport. Tuning: " << mTuningMode -                                       << ". Relog: " << mRelogRequested << LL_ENDL; -                    llcoro::suspendUntilTimeout(1.0); -                } -                else -                { -                    LLMutexLock lock(&mVoiceStateMutex); - -                    mTrickling = false; -                    mIceCompleted = false; -                    setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); -                } -                break; - -            case VOICE_STATE_START_SESSION: -                if (establishVoiceConnection() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) -                { -                    setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_SESSION_START, VOICE_STATE_SESSION_RETRY); -                } -                else -                { -                    setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); -                } -                break; - -            case VOICE_STATE_WAIT_FOR_SESSION_START: -            { -                llcoro::suspendUntilTimeout(1.0); -                std::string channel_sdp; -                { -                    LLMutexLock lock(&mVoiceStateMutex); -                    if (mVoiceControlState == VOICE_STATE_SESSION_RETRY) -                    { -                        break; -                    } -                    if (!mChannelSDP.empty()) -                    { -                        mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; -                    } -                } -                break; -            } -            case VOICE_STATE_PROVISION_ACCOUNT: -                if (!provisionVoiceAccount()) -                { -                    setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); -                } -                else -                { -                    provisionWaitTimeout = 0; -                    setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); -                } -                break; -            case VOICE_STATE_SESSION_PROVISION_WAIT: -                llcoro::suspendUntilTimeout(1.0); -                if (provisionWaitTimeout++ > PROVISION_WAIT_TIMEOUT_SEC) -				{ -                    setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); -				} -                break; - -            case VOICE_STATE_SESSION_RETRY: -                giveUp();  // cleans sockets and session -                if (mRelogRequested) -                { -                    // We failed to connect, give it a bit time before retrying. -                    retry++; -                    F32 full_delay    = llmin(2.f * (F32) retry, 10.f); -                    F32 current_delay = 0.f; -                    LL_INFOS("Voice") << "Voice failed to establish session after " << retry << " tries. Will attempt to reconnect in " -                                      << full_delay << " seconds" << LL_ENDL; -                    while (current_delay < full_delay && !sShuttingDown) -                    { -                        // Assuming that a second has passed is not accurate, -                        // but we don't need accurancy here, just to make sure -                        // that some time passed and not to outlive voice itself -                        current_delay++; -                        llcoro::suspendUntilTimeout(1.f); -                    } -                } -                setVoiceControlStateUnless(VOICE_STATE_DISCONNECT); -                break; - -            case VOICE_STATE_SESSION_ESTABLISHED: -            { -                retry = 0; -                if (mTuningMode) -                { -                    performMicTuning(); -                } -                sessionEstablished(); -                setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); -            } -            break; - -            case VOICE_STATE_WAIT_FOR_CHANNEL: -            { -                if ((!waitForChannel()) || !mVoiceEnabled)  // todo: split into more states like login/fonts -                { -                    setVoiceControlStateUnless(VOICE_STATE_DISCONNECT, VOICE_STATE_SESSION_RETRY); -                } -                // on true, it's a retry, so let the state stand. -            } -            break; - -            case VOICE_STATE_DISCONNECT: -                LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; -                breakVoiceConnection(true); -                retry = 0;  // Connected without issues -                setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); -                break; - -            case VOICE_STATE_WAIT_FOR_EXIT: -                if (mVoiceEnabled) -                { -                    LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; -                    setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); -                } -                else -                { -                    llcoro::suspendUntilTimeout(1.0); -                } -                break; - -            default: -            { -                LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; -                break; -            } -        } -    } while (true); -} - -bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) -{ -    if (!sShuttingDown && mVoiceEnabled) -    { -        LL_WARNS("Voice") << "SLVoice terminated " << ll_stream_notation_sd(data) << LL_ENDL; -        terminateAudioSession(false); -        closeSocket(); -        cleanUp(); -        LLVoiceClient::getInstance()->setUserPTTState(false); -        gAgent.setVoiceConnected(false); -        mRelogRequested = true; -    } -    return false; -} - -bool LLWebRTCVoiceClient::provisionVoiceAccount() -{ -    LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; - -    while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) -    { -        LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; -        // *TODO* Pump a message for wake up. -        llcoro::suspend(); -    } - -    if (sShuttingDown) -    { -        return false; -    } - -    std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); - -    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; - -    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( -							      url,	 -                                  LLCore::HttpRequest::DEFAULT_POLICY_ID, -                                  body, -                                  boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisioned, this, _1), -								  boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure, this, url, 3, body, _1)); -    return true; -} - -void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) -{ -    LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); -    std::string channelSDP; -    if (result.has("jsep") &&  -		result["jsep"].has("type") &&  -		result["jsep"]["type"] == "answer" &&  -		result["jsep"].has("sdp")) -    { -        channelSDP = result["jsep"]["sdp"].asString(); +        mAudioSession = mNextAudioSession; +        mNextAudioSession.reset();      } -    std::string voiceAccountServerUri; -    std::string voiceUserName = gAgent.getID().asString(); -    std::string voicePassword = "";  // no password for now. - -    LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" -                       << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " -                       << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; - -    setLoginInfo(voiceUserName, voicePassword, channelSDP); - -    // switch to the default region channel. -    switchChannel(gAgent.getRegion()->getRegionID().asString()); +    sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID));  } -void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) +void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::string& channelID)  { -    if (sShuttingDown) -	{ -		return; -	} -    if (retries >= 0) -    { - -		LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( -									  url, -                                      LLCore::HttpRequest::DEFAULT_POLICY_ID, -                                      body, -                                      boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisioned, this, _1), -									  boost::bind(&LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure, this, url, retries - 1, body, _1)); -    } -    else +    if (channelID == mPrimaryConnectionID)      { -        LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; +        LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);      }  } -bool LLWebRTCVoiceClient::establishVoiceConnection() +void LLWebRTCVoiceClient::sessionState::OnConnectionFailure(const std::string &channelID)  { -    LL_INFOS("Voice") << "Ice Gathering voice account." << LL_ENDL; -    while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) -    { -        LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; -        // *TODO* Pump a message for wake up. -        llcoro::suspend(); -        return false; -    } - -    if (!mVoiceEnabled && mIsInitialized) -    { -        LL_WARNS("Voice") << "cannot establish connection; enabled "<<mVoiceEnabled<<" initialized "<<mIsInitialized<<LL_ENDL; -        return false; -    } - -    if (sShuttingDown) +    if (channelID == mPrimaryConnectionID)      { -        return false; +        LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);      } -    return mWebRTCPeerConnection->initializeConnection();  } -bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) +void LLWebRTCVoiceClient::idle(void* user_data)  { - -	LL_INFOS("Voice") << "Breaking voice account." << LL_ENDL; - -    while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) -    { -		LL_DEBUGS("Voice") << "no capabilities for voice breaking; waiting " << LL_ENDL; -		// *TODO* Pump a message for wake up. -		llcoro::suspend(); -    } - -    if (sShuttingDown) -    { -		return false; -    } - -    std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); - -    LL_DEBUGS("Voice") << "region ready for voice break; url=" << url << LL_ENDL; - -    LL_DEBUGS("Voice") << "sending ProvisionVoiceAccountRequest (breaking) (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - -    LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); -    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy)); -    LLCore::HttpRequest::ptr_t                  httpRequest(new LLCore::HttpRequest); - -    LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); -    LLSD body; -    body["logout"] = TRUE; -    httpAdapter->postAndSuspend(httpRequest, url, body); -    mWebRTCPeerConnection->shutdownConnection(); -    return true;  } -bool LLWebRTCVoiceClient::loginToWebRTC() -{ -    mRelogRequested = false; -    mIsLoggedIn = true; -    notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - -    // Set the initial state of mic mute, local speaker volume, etc. -    sendLocalAudioUpdates(); -    mIsLoggingIn = false; - -    return true; -} +//========================================================================= +// 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. +//  +//  -void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) +void LLWebRTCVoiceClient::predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t& session)  { -    if (mIsLoggedIn) -    { -        mAccountPassword.clear(); -        breakVoiceConnection(wait); -        // Ensure that we'll re-request provisioning before logging in again -        mIsLoggedIn = false; -    } +    session->processSessionStates();  } -bool LLWebRTCVoiceClient::requestParcelVoiceInfo() +void LLWebRTCVoiceClient::sessionState::processSessionStates()  { -    //_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL; - -    LLViewerRegion * region = gAgent.getRegion(); -    if (region == NULL || !region->capabilitiesReceived()) -    { -        LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; -        return false; -    } - -    // grab the cap. -    std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); -    if (url.empty()) -    { -        // Region dosn't have the cap. Stop probing. -        LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; -        return false; -    } -  -    // update the parcel -    checkParcelChanged(true); - -    LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - -    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); -    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t -        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy)); -    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - -    LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD()); - -    if (sShuttingDown) -    { -        return false; -    } - -    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; -    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - -    if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) -    { -        // if a terminate request has been received, -        // bail and go to the stateSessionTerminated -        // state.  If the cap request is still pending, -        // the responder will check to see if we've moved -        // to a new session and won't change any state. -        LL_DEBUGS("Voice") << "terminate requested " << mSessionTerminateRequested -                           << " enabled " << mVoiceEnabled -                           << " initialized " << mIsInitialized -                           << LL_ENDL; -        terminateAudioSession(true); -        return false; -    } - -    if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))) -    { -        if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) -        { -            LL_WARNS("Voice") << "Session terminated." << LL_ENDL; -        } - -        LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL; -        sessionTerminate(); -        return false; -    } - -    std::string uri; -    std::string credentials; - -    LL_WARNS("Voice") << "Got voice credentials" << result << LL_ENDL; - -    if (result.has("voice_credentials")) -    { -        LLSD voice_credentials = result["voice_credentials"]; -        if (voice_credentials.has("channel_uri")) -        { -            LL_DEBUGS("Voice") << "got voice channel uri" << LL_ENDL; -            uri = voice_credentials["channel_uri"].asString(); -        } -        else -        { -            LL_WARNS("Voice") << "No voice channel uri" << LL_ENDL; -        } -         -        if (voice_credentials.has("channel_credentials")) -        { -            LL_DEBUGS("Voice") << "got voice channel credentials" << LL_ENDL; -            credentials = -                voice_credentials["channel_credentials"].asString(); -        } -        else -        { -            LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel(); -            if (channel != NULL) -            { -                if (channel->getSessionName().empty() && channel->getSessionID().isNull()) -                { -                    if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) -                    { -                        LL_WARNS("Voice") << "No channel credentials for default channel" << LL_ENDL; -                    } -                } -                else -                { -                    LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL; -                } -            } -        } -    } -    else +    std::map<std::string, connectionPtr_t>::iterator iter; +    for (iter = mWebRTCConnections.begin(); iter != mWebRTCConnections.end();)      { -        if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) +        if (!iter->second->connectionStateMachine())          { -            LL_WARNS("Voice") << "No voice credentials" << LL_ENDL; +            iter = mWebRTCConnections.erase(iter);          }          else          { -            LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL; +            ++iter;          }      } - -    // set the spatial channel.  If no voice credentials or uri are  -    // available, then we simply drop out of voice spatially. -    return !setSpatialChannel(uri, "");  } -bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) +void LLWebRTCVoiceClient::voiceConnectionCoro()  { -    mIsJoiningSession = true; - -    sessionStatePtr_t oldSession = mAudioSession; - -    LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL; - -    mAudioSession = nextSession; -    mAudioSessionChanged = true; -    if (!mAudioSession || !mAudioSession->mReconnect) -    { -        mNextAudioSession.reset(); -    } - -    notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); - -    llcoro::suspend(); - -    if (sShuttingDown) -    { -        return false; -    } - -    LLSD result; - -    if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) -    { -        // Notify observers to let them know there is problem with voice -        notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); -        LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL; -    } -     -    // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for -    // example for p2p many times while waiting for response, so it can't be used to detect errors -    if (mAudioSession && mAudioSession->mIsSpatial) -    { -        mSpatialJoiningNum++; -    } - -    if (!mVoiceEnabled && mIsInitialized) -    { -        LL_DEBUGS("Voice") << "Voice no longer enabled. Exiting" -                           << " enabled " << mVoiceEnabled -                           << " initialized " << mIsInitialized -                           << LL_ENDL; -        mIsJoiningSession = false; -        // User bailed out during connect -- jump straight to teardown. -        terminateAudioSession(true); -        notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); -        return false; -    } -    else if (mSessionTerminateRequested) -    { -        LL_DEBUGS("Voice") << "Terminate requested" << LL_ENDL; -        if (mAudioSession && !mAudioSession->mHandle.empty()) -        { -            // Only allow direct exits from this state in p2p calls (for cancelling an invite). -            // Terminating a half-connected session on other types of calls seems to break something in the WebRTC gateway. -            if (mAudioSession->mIsP2P) -            { -                terminateAudioSession(true); -                mIsJoiningSession = false; -                notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); -                return false; -            } -        } -    } - -    LLSD timeoutResult(LLSDMap("session", "timeout")); - -    // We are about to start a whole new session.  Anything that MIGHT still be in our  -    // maildrop is going to be stale and cause us much wailing and gnashing of teeth.   -    // Just flush it all out and start new. -    mWebRTCPump.discard(); - -	// add 'self' participant. -    addParticipantByID(gAgent.getID()); -    // tell peers that this participant has joined. - -    if (mWebRTCDataInterface) -    { -        Json::FastWriter writer; -        Json::Value      root = getPositionAndVolumeUpdateJson(true); -        root["j"]             = true; -        std::string json_data = writer.write(root); -        mWebRTCDataInterface->sendData(json_data, false); -    } - -    notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - -    return true; -} - -bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) -{ - -    if (mAudioSession) +    LL_DEBUGS("Voice") << "starting" << LL_ENDL; +    mIsCoroutineActive = true; +    LLCoros::set_consuming(true); +    try      { -        LL_INFOS("Voice") << "terminateAudioSession(" << wait << ") Terminating current voice session " << mAudioSession->mHandle << LL_ENDL; - -        if (mIsLoggedIn) +        while (!sShuttingDown)          { -            if (!mAudioSession->mHandle.empty()) +            // add session for region or parcel voice. +            LLViewerRegion *regionp = gAgent.getRegion(); +            if (!regionp)              { -                if (wait) -                { -                    LLSD result; -                    do -                    { -                        LLSD timeoutResult(LLSDMap("session", "timeout")); - -                        result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - -                        if (sShuttingDown) -                        { -                            return false; -                        } - -                        LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; -                        if (result.has("session")) -                        { -                            if (result.has("handle")) -                            { -                                if (result["handle"] != mAudioSession->mHandle) -                                { -                                    LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; -                                    continue; -                                } -                            } - -                            std::string message = result["session"].asString(); -                            if (message == "removed" || message == "timeout") -                                break; -                        } -                    } while (true); - -                } +                llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); +                continue;              } -            else +            if (regionp->getRegionID().isNull())              { -                LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; +                llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); +                continue;              } -        } -        else -        { -            LL_WARNS("Voice") << "Session " << mAudioSession->mHandle << " already terminated by logout." << LL_ENDL; -        } - -        sessionStatePtr_t oldSession = mAudioSession; - -        mAudioSession.reset(); -        // We just notified status observers about this change.  Don't do it again. -        mAudioSessionChanged = false; - -        // The old session may now need to be deleted. -        reapSession(oldSession); -    } -    else -    { -        LL_WARNS("Voice") << "terminateAudioSession(" << wait << ") with NULL mAudioSession" << LL_ENDL; -    } - -    notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - -    // Always reset the terminate request flag when we get here. -    // Some slower PCs have a race condition where they can switch to an incoming  P2P call faster than the state machine leaves  -    // the region chat. -    mSessionTerminateRequested = false; - -    bool status=((mVoiceEnabled || !mIsInitialized) && !mRelogRequested  && !sShuttingDown); -    LL_DEBUGS("Voice") << "exiting" -                       << " VoiceEnabled " << mVoiceEnabled -                       << " IsInitialized " << mIsInitialized -                       << " RelogRequested " << mRelogRequested -                       << " ShuttingDown " << (sShuttingDown ? "TRUE" : "FALSE") -                       << " returning " << status -                       << LL_ENDL; -    return status; -} - - -typedef enum e_voice_wait_for_channel_state -{ -    VOICE_CHANNEL_STATE_LOGIN = 0, // entry point -    VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, -    VOICE_CHANNEL_STATE_PROCESS_CHANNEL, -    VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, -    VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK -} EVoiceWaitForChannelState; - -bool LLWebRTCVoiceClient::waitForChannel() -{ -    LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL; - -    EVoiceWaitForChannelState state = VOICE_CHANNEL_STATE_LOGIN; - -    do  -    { -        if (sShuttingDown) -        { -            // terminate() forcefully disconects voice, no need for cleanup -            return false; -        } - -		if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY)  -		{ -            mIsProcessingChannels = false; -            return true; -		} - -		processIceUpdates(); -        switch (state) -        { -        case VOICE_CHANNEL_STATE_LOGIN: -            if (!loginToWebRTC()) +            if (!mAudioSession || mAudioSession->mIsSpatial)              { -                return false; -            } -            state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; -            break; +                // check to see if parcel changed. +                std::string channelID = regionp->getRegionID().asString(); -        case VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING: -            mIsProcessingChannels = true; -            llcoro::suspend(); -            state = VOICE_CHANNEL_STATE_PROCESS_CHANNEL; -            break; - -        case VOICE_CHANNEL_STATE_PROCESS_CHANNEL: -            if (mTuningMode) -            { -                performMicTuning(); -            } -            else if (checkParcelChanged() || (mNextAudioSession == NULL)) -            { -                // the parcel is changed, or we have no pending audio sessions, -                // so try to request the parcel voice info -                // if we have the cap, we move to the appropriate state -                requestParcelVoiceInfo(); //suspends for http reply -            } -            else if (sessionNeedsRelog(mNextAudioSession)) -            { -                LL_INFOS("Voice") << "Session requesting reprovision and login." << LL_ENDL; -                requestRelog(); -                break; -            } -            else if (mNextAudioSession) -            { -                sessionStatePtr_t joinSession = mNextAudioSession; -                mNextAudioSession.reset(); -                if (!runSession(joinSession)) //suspends +                LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); +                S32       parcel_local_id = INVALID_PARCEL_ID; +                if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID && !parcel->getParcelFlagUseEstateVoiceChannel())                  { -                    LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL; -                    break; +                    parcel_local_id = parcel->getLocalID(); +                    channelID += "-" + std::to_string(parcel->getLocalID());                  } -                else +                if (!mAudioSession || channelID != mAudioSession->mChannelID)                  { -                    LL_DEBUGS("Voice") -                        << "runSession returned true to inner loop" -                        << " RelogRequested=" << mRelogRequested -                        << " VoiceEnabled=" << mVoiceEnabled -                        << LL_ENDL; +                    setSpatialChannel(channelID, "", parcel_local_id);                  }              } - -            state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY; -            break; - -        case VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY: -            if (!mNextAudioSession) -            { -                llcoro::suspendUntilTimeout(1.0); -            } -            state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK; -            break; - -        case VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK: -            if (mVoiceEnabled && !mRelogRequested) -            { -                state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; -                break; -            } -            else -            { -                mIsProcessingChannels = false; -                LL_DEBUGS("Voice") -                    << "leaving inner waitForChannel loop" -                    << " RelogRequested=" << mRelogRequested -                    << " VoiceEnabled=" << mVoiceEnabled -                    << LL_ENDL; -                return !sShuttingDown; -            } +            updatePosition(); +            sessionState::for_each(boost::bind(predProcessSessionStates, _1)); +            sendPositionAndVolumeUpdate(true); +            updateOwnVolume(); +            llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);          } -    } while (true); -} - -bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) -{ -    LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL; - -    bool joined_session = addAndJoinSession(session); - -    if (sShuttingDown) -    { -        return false;      } - -    if (!joined_session) +    catch (const LLCoros::Stop&)      { -        notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - -        if (mSessionTerminateRequested) -        { -            LL_DEBUGS("Voice") << "runSession terminate requested " << LL_ENDL; -            terminateAudioSession(true); -        } -        // if a relog has been requested then addAndJoineSession  -        // failed in a spectacular way and we need to back out. -        // If this is not the case then we were simply trying to -        // make a call and the other party rejected it.   -        return !mRelogRequested; +        LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL;      } - -    notifyParticipantObservers(); - -    LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); - -    mIsInChannel = true; -    mMuteMicDirty = true; - -    while (!sShuttingDown -           && mVoiceEnabled -           && !mSessionTerminateRequested -           && !mTuningMode) +    catch (const LLContinueError&)      { - -        if (sShuttingDown) -        { -            return false; -        } -        if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY)  -		{ -			break;  -		} - -        if (mSessionTerminateRequested) -        { -            break; -        } - -        if (mAudioSession && mAudioSession->mParticipantsChanged) -        { -            mAudioSession->mParticipantsChanged = false; -            notifyParticipantObservers(); -        } -         -        if (!inSpatialChannel()) -        { -            // When in a non-spatial channel, never send positional updates. -            mSpatialCoordsDirty = false; -        } -        else -        { -            updatePosition(); - -            if (checkParcelChanged()) -            { -                // *RIDER: I think I can just return here if the parcel has changed  -                // and grab the new voice channel from the outside loop. -                //  -                // if the parcel has changed, attempted to request the -                // cap for the parcel voice info.  If we can't request it -                // then we don't have the cap URL so we do nothing and will -                // recheck next time around -                if (requestParcelVoiceInfo()) // suspends -                {   // The parcel voice URI has changed.. break out and reconnect. -                    break; -                } - -                if (sShuttingDown) -                { -                    return false; -                } -            } -            // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) -            enforceTether(); -        } -        sendPositionAndVolumeUpdate(); - -        // send any requests to adjust mic and speaker settings if they have changed -        sendLocalAudioUpdates(); - -        mIsInitialized = true; -        LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, UPDATE_THROTTLE_SECONDS, timeoutEvent); - -        if (sShuttingDown) -        { -            return false; -        } - -        if (!result.has("timeout")) // logging the timeout event spams the log -        { -            LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; -        } -        if (result.has("session")) -        {    -            if (result.has("handle")) -            { -                if (!mAudioSession) -                { -                    LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while session is not initiated." << LL_ENDL; -                    continue; -                } -                if (result["handle"] != mAudioSession->mHandle) -                { -                    LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; -                    continue; -                } -            } - -            std::string message = result["session"]; - -            if (message == "removed") -            { -                LL_DEBUGS("Voice") << "session removed" << LL_ENDL; -                notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); -                break; -            } -        } -        else if (result.has("login")) -        { -            std::string message = result["login"]; -            if (message == "account_logout") -            { -                LL_DEBUGS("Voice") << "logged out" << LL_ENDL; -                mIsLoggedIn = false; -                mRelogRequested = true; -                break; -            } -        } +        LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient");      } - -    if (sShuttingDown) +    catch (...)      { -        return false; +        // Ideally for Windows need to log SEH exception instead or to set SEH +        // handlers but bugsplat shows local variables for windows, which should +        // be enough +        LL_WARNS("Voice") << "voiceConnectionStateMachine crashed" << LL_ENDL; +        throw;      } -    mIsInChannel = false; -    LL_DEBUGS("Voice") << "terminating at end of runSession" << LL_ENDL; -    terminateAudioSession(true); - -    return true; +    cleanUp();  }  bool LLWebRTCVoiceClient::performMicTuning() @@ -1448,21 +539,6 @@ bool LLWebRTCVoiceClient::performMicTuning()  //========================================================================= -void LLWebRTCVoiceClient::closeSocket(void) -{ -	mSocket.reset(); -    sConnected = false; -	mConnectorEstablished = false; -	mAccountLoggedIn = false; -} - -void LLWebRTCVoiceClient::logout() -{ -	// Ensure that we'll re-request provisioning before logging in again -	mAccountPassword.clear(); -    mAccountLoggedIn = false; -} -  void LLWebRTCVoiceClient::sessionTerminate()  {  	mSessionTerminateRequested = true; @@ -1479,12 +555,7 @@ void LLWebRTCVoiceClient::leaveAudioSession()  {  	if(mAudioSession)  	{ -		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - -		if(mAudioSession->mHandle.empty()) -		{ -			LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	 -		} +		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mChannelID << LL_ENDL;  	}  	else  	{ @@ -1642,19 +713,10 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList)      mWebRTCDeviceInterface->refreshDevices();  } -void LLWebRTCVoiceClient::daemonDied() -{ -	// The daemon died, so the connection is gone.  Reset everything and start over. -	LL_WARNS("Voice") << "Connection to WebRTC daemon lost.  Resetting state."<< LL_ENDL; - -	//TODO: Try to relaunch the daemon -} -  void LLWebRTCVoiceClient::giveUp()  {  	// All has failed.  Clean up and stop trying.      LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; -	closeSocket();  	cleanUp();  } @@ -1669,16 +731,29 @@ void LLWebRTCVoiceClient::setHidden(bool hidden)      }      else      { -        sendPositionAndVolumeUpdate(); +        sendPositionAndVolumeUpdate(true);      }  } -Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) +void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force)  { -    Json::Value root = Json::objectValue; +    Json::FastWriter writer; +    std::string      spatial_data; +    std::string      volume_data; + +    F32 audio_level = 0.0; +    uint32_t uint_audio_level = 0.0; + +    if (!mMuteMic) +    { +        audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); +        uint_audio_level = (uint32_t) (audio_level * 128); -    if ((mSpatialCoordsDirty || force) && inSpatialChannel()) +    } + +    if (mSpatialCoordsDirty || force)      { +        Json::Value   spatial = Json::objectValue;          LLVector3d earPosition;          LLQuaternion  earRot;          switch (mEarLocation) @@ -1700,343 +775,89 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force)                  break;          } -        root["sp"]      = Json::objectValue; -        root["sp"]["x"] = (int) (mAvatarPosition[0] * 100); -        root["sp"]["y"] = (int) (mAvatarPosition[1] * 100); -        root["sp"]["z"] = (int) (mAvatarPosition[2] * 100); -        root["sh"]      = Json::objectValue; -        root["sh"]["x"] = (int) (mAvatarRot[0] * 100); -        root["sh"]["y"] = (int) (mAvatarRot[1] * 100); -        root["sh"]["z"] = (int) (mAvatarRot[2] * 100); -        root["sh"]["w"] = (int) (mAvatarRot[3] * 100); - -        root["lp"]      = Json::objectValue; -        root["lp"]["x"] = (int) (earPosition[0] * 100); -        root["lp"]["y"] = (int) (earPosition[1] * 100); -        root["lp"]["z"] = (int) (earPosition[2] * 100); -        root["lh"]      = Json::objectValue; -        root["lh"]["x"] = (int) (earRot[0] * 100); -        root["lh"]["y"] = (int) (earRot[1] * 100); -        root["lh"]["z"] = (int) (earRot[2] * 100); -        root["lh"]["w"] = (int) (earRot[3] * 100); +        spatial["sp"]   = Json::objectValue; +        spatial["sp"]["x"] = (int) (mAvatarPosition[0] * 100); +        spatial["sp"]["y"] = (int) (mAvatarPosition[1] * 100); +        spatial["sp"]["z"] = (int) (mAvatarPosition[2] * 100); +        spatial["sh"]      = Json::objectValue; +        spatial["sh"]["x"] = (int) (mAvatarRot[0] * 100); +        spatial["sh"]["y"] = (int) (mAvatarRot[1] * 100); +        spatial["sh"]["z"] = (int) (mAvatarRot[2] * 100); +        spatial["sh"]["w"] = (int) (mAvatarRot[3] * 100); + +        spatial["lp"]   = Json::objectValue; +        spatial["lp"]["x"] = (int) (earPosition[0] * 100); +        spatial["lp"]["y"] = (int) (earPosition[1] * 100); +        spatial["lp"]["z"] = (int) (earPosition[2] * 100); +        spatial["lh"]      = Json::objectValue; +        spatial["lh"]["x"] = (int) (earRot[0] * 100); +        spatial["lh"]["y"] = (int) (earRot[1] * 100); +        spatial["lh"]["z"] = (int) (earRot[2] * 100); +        spatial["lh"]["w"] = (int) (earRot[3] * 100);          mSpatialCoordsDirty = false; -    } - -    F32 audio_level = 0.0; - -    if (!mMuteMic) -    { -        audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); -    } -    uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); -    if (force || (uint_audio_level != mAudioLevel)) -    { -        root["p"]                         = uint_audio_level; -        mAudioLevel                       = uint_audio_level; -        participantStatePtr_t participant = findParticipantByID(gAgentID); -        if (participant) +        if (force || (uint_audio_level != mAudioLevel))          { -            participant->mPower      = audio_level; -            participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; +            spatial["p"] = uint_audio_level;          } +        spatial_data = writer.write(spatial);      } -    return root; -} - -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate() -{	 - - -    if (mWebRTCDataInterface && mWebRTCAudioInterface) +    if (force || (uint_audio_level != mAudioLevel))      { -        Json::Value root = getPositionAndVolumeUpdateJson(false); -         -        if (root.size() > 0) -        { -             -            Json::FastWriter writer; -            std::string json_data = writer.write(root); -             -            mWebRTCDataInterface->sendData(json_data, false); -        } +        Json::Value volume    = Json::objectValue; +        volume["p"]           = uint_audio_level; +        volume_data           = writer.write(volume);      } -	 -	 -	if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) -	{ -		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - -		mAudioSession->mVolumeDirty = false; -		mAudioSession->mMuteDirty = false; -		 -		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) -		{ -			participantStatePtr_t p(iter->second); -			 -			if(p->mVolumeDirty) -			{ -				// Can't set volume/mute for yourself -				if(!p->mIsSelf) -				{ -					// scale from the range 0.0-1.0 to WebRTC volume in the range 0-100 -					S32 volume = ll_round(p->mVolume / VOLUME_SCALE_WEBRTC); -					bool mute = p->mOnMuteList; -					 -					if(mute) -					{ -						// SetParticipantMuteForMe doesn't work in p2p sessions. -						// If we want the user to be muted, set their volume to 0 as well. -						// This isn't perfect, but it will at least reduce their volume to a minimum. -						volume = 0; -						// Mark the current volume level as set to prevent incoming events -						// changing it to 0, so that we can return to it when unmuting. -						p->mVolumeSet = true; -					} -					 -					if(volume == 0) -					{ -						mute = true; -					} - -					LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; -				} -				 -				p->mVolumeDirty = false; -			} -		} -	} -} +    mAudioLevel = uint_audio_level; -void LLWebRTCVoiceClient::sendLocalAudioUpdates() -{ +    sessionState::for_each(boost::bind(predSendData, _1, spatial_data, volume_data));  } -///////////////////////////// -// WebRTC Signaling Handlers -void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) -{ -    LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - -	switch (state) +void LLWebRTCVoiceClient::updateOwnVolume() {  +    F32 audio_level = 0.0; +    if (!mMuteMic)      { -        case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: -        { -            LLMutexLock lock(&mVoiceStateMutex); -            mIceCompleted = true; -            break; -        } -        case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: -        { -            LLMutexLock lock(&mVoiceStateMutex); -            mIceCompleted = false; -        } -        default: -            break; +        audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel();      } -} -void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) -{ -    LLMutexLock lock(&mVoiceStateMutex); -	mIceCandidates.push_back(candidate);  +    sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level));   } -void LLWebRTCVoiceClient::processIceUpdates() +void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level)  { -    LL_INFOS("Voice") << "Ice Gathering voice account." << LL_ENDL; -    while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) -    { -        LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; -        // *TODO* Pump a message for wake up. -        llcoro::suspend(); -    } - -    if (sShuttingDown) -    { -        return; -    } - -    std::string url = gAgent.getRegionCapability("VoiceSignalingRequest"); - -    LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; - -    LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); -    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy)); -    LLCore::HttpRequest::ptr_t                  httpRequest(new LLCore::HttpRequest); -    LLCore::HttpOptions::ptr_t                  httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - -	bool iceCompleted = false; -    LLSD body; +    participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); +    if (participant)      { -        LLMutexLock lock(&mVoiceStateMutex); - -		if (!mTrickling) -        { -            if (mIceCandidates.size()) -            { -                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); -                } -                body["candidates"] = candidates; -                mIceCandidates.clear(); -            } -            else if (mIceCompleted) -            { -                LLSD body_candidate; -                body_candidate["completed"] = true; -                body["candidate"]           = body_candidate; -                iceCompleted                = mIceCompleted; -                mIceCompleted               = false; -            } -            else -            { -                return; -            } -            LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( -                url, -                LLCore::HttpRequest::DEFAULT_POLICY_ID, -                body, -                boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, iceCompleted, _1), -                boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); -            mTrickling = true; -        } +        participant->mPower      = audio_level; +        participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL;      }  } -void LLWebRTCVoiceClient::onIceUpdateComplete(bool ice_completed, const LLSD& result) -{ mTrickling = false; } - -void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result) +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data)  { -    if (sShuttingDown) -    { -        return; -    } -    LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); -    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy)); - -	if (retries >= 0) +    if (session->mIsSpatial && !spatial_data.empty())      { -        LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying.  " << result << LL_ENDL; -        LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, -                                      LLCore::HttpRequest::DEFAULT_POLICY_ID, -                                      body, -                                      boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, ice_completed, _1), -									  boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); +        session->sendData(spatial_data);      } -    else  -	{ -        LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection.  " << result << LL_ENDL; -        setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); -        mTrickling = false;  -	} -} - -void LLWebRTCVoiceClient::OnOfferAvailable(const std::string &sdp)  -{ -    LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; -    LLMutexLock lock(&mVoiceStateMutex); -    mChannelSDP = sdp; -} - -void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * audio_interface) -{ -    LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; -    mWebRTCAudioInterface = audio_interface; -    mWebRTCAudioInterface->setAudioObserver(this); -    float speaker_volume  = 0; +    else if (!volume_data.empty())      { -        LLMutexLock lock(&mVoiceStateMutex); -        speaker_volume = mSpeakerVolume; +        session->sendData(volume_data);      } -	mWebRTCDeviceInterface->setSpeakerVolume(mSpeakerVolume); -    setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY);  } -void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) +void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data)  { -    // incoming data will be a json structure (if it's not binary.)  We may pack -    // binary for size reasons.  Most of the keys in the json objects are -    // single or double characters for size reasons. -    // The primary element is: -    // An object where each key is an agent id.  (in the future, we may allow -    // 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) -    // 'l'  - boolean, always true if exists. - -    if (binary) +    for (auto& connection : mWebRTCConnections)      { -        LL_WARNS("Voice") << "Binary data received from data channel." << LL_ENDL; -        return; -    } - -    Json::Reader reader; -    Json::Value  voice_data; -    if (reader.parse(data, voice_data, false))  // don't collect comments -    { -        if (!voice_data.isObject()) -        { -            LL_WARNS("Voice") << "Expected object from data channel:" << data << LL_ENDL; -            return; -        } -        bool new_participant = false;  -        for (auto &participant_id : voice_data.getMemberNames()) -        { -            LLUUID agent_id(participant_id); -            if (agent_id.isNull()) -            { -                LL_WARNS("Voice") << "Bad participant ID from data channel (" << participant_id << "):" << data << LL_ENDL; -                continue; -            } -            participantStatePtr_t participant = findParticipantByID(agent_id); -            bool joined = voice_data[participant_id].get("j", Json::Value(false)).asBool(); -            new_participant |= joined; -            if (!participant && joined) -            { -                participant = addParticipantByID(agent_id); -            } -			if (participant) -			{ -                if(voice_data[participant_id].get("l", Json::Value(false)).asBool()) -                { -                    removeParticipantByID(agent_id); -                } -                F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; -				// convert to decibles -                participant->mPower = energyRMS; -                /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we -				   can use it at some point when we actually process frames. */ -                participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL;  -			} -        } +        connection.second->sendData(data);      }  } -void LLWebRTCVoiceClient::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) +void LLWebRTCVoiceClient::sendLocalAudioUpdates()  { -    mWebRTCDataInterface = data_interface; -    mWebRTCDataInterface->setDataObserver(this);  } -void LLWebRTCVoiceClient::OnRenegotiationNeeded() -{ -    LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; -    mRelogRequested = TRUE; -    mIsProcessingChannels = FALSE; -    notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); -    setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); -} -  void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)  {  	LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; @@ -2054,7 +875,7 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)  	// This is the session we're joining.  	if(mIsJoiningSession)  	{ -        LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) +        LLSD WebRTCevent(LLSDMap("channel", session->mChannelID)                  ("session", "joined"));          mWebRTCPump.post(WebRTCevent); @@ -2062,7 +883,7 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)  		if(!session->mIsChannel)  		{  			// this is a p2p session.  Make sure the other end is added as a participant. -            participantStatePtr_t participant(session->addParticipant(LLUUID(session->mSIPURI))); +            participantStatePtr_t participant(session->addParticipant(LLUUID(session->mChannelID)));  			if(participant)  			{  				if(participant->mAvatarIDValid) @@ -2076,8 +897,7 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)  				}  				// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? -				LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName  -						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; +				LL_INFOS("Voice") << "added caller as participant  (" << participant->mAvatarID << ")"<< LL_ENDL;  			}  		}  	} @@ -2086,20 +906,19 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)  void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session)  {  	if(session) -	{ -		 +	{		  		if(session == mAudioSession)  		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; +			LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the current session)" << LL_ENDL;  		}  		else if(session == mNextAudioSession)  		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; +            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->mSIPURI << LL_ENDL; +            LL_DEBUGS("Voice") << "deleting session " << session->mChannelID << LL_ENDL;  			deleteSession(session);  		}	  	} @@ -2109,35 +928,12 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session)  	}  } -// Returns true if the session seems to indicate we've moved to a region on a different voice server -bool LLWebRTCVoiceClient::sessionNeedsRelog(const sessionStatePtr_t &session) -{ -	bool result = false; -	 -	if(session) -	{ -		// Only make this check for spatial channels (so it won't happen for group or p2p calls) -		if(session->mIsSpatial) -		{	 -			std::string::size_type atsign; -			 -			atsign = session->mSIPURI.find("@"); -			 -			if(atsign != std::string::npos) -			{ -				std::string urihost = session->mSIPURI.substr(atsign + 1); -			} -		} -	} -	 -	return result; -}  void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session)  {      if (mAudioSession == session)      { -        LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) +        LLSD WebRTCevent(LLSDMap("channel", session->mChannelID)              ("session", "removed"));          mWebRTCPump.post(WebRTCevent); @@ -2293,17 +1089,20 @@ void LLWebRTCVoiceClient::sessionState::removeAllParticipants()  /*static*/  void LLWebRTCVoiceClient::sessionState::VerifySessions()  { -    std::set<wptr_t>::iterator it = mSession.begin(); -    while (it != mSession.end()) +    return; +    /* +    std::map<std::string, wptr_t>::iterator it = mSessions.begin(); +    while (it != mSessions.end())      { -        if ((*it).expired()) +        if ((*it).second.expired())          {              LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; -            it = mSession.erase(it); +            it = mSessions.erase(it);          }          else              ++it;      } +    */  } @@ -2336,16 +1135,6 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi  	participantMap::iterator iter = mParticipantsByURI.find(uri); -	if(iter == mParticipantsByURI.end()) -	{ -		if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) -		{ -			// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. -			// Look up the other URI -			iter = mParticipantsByURI.find(mSIPURI); -		} -	} -  	if(iter != mParticipantsByURI.end())  	{  		result = iter->second; @@ -2367,37 +1156,40 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi  	return result;  } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const LLUUID& id) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string& channelID, const LLUUID& id)  {      participantStatePtr_t result; +    auto& session = sessionState::matchSessionByChannelID(channelID); -	if(mAudioSession) +	if (session)  	{ -		result = mAudioSession->findParticipantByID(id); +        result = session->findParticipantByID(id);  	}  	return result;  } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const LLUUID &id) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string& channelID, const LLUUID &id)  {   	participantStatePtr_t result; -    if (mAudioSession) +    auto& session = sessionState::matchSessionByChannelID(channelID); +    if (session)      { -        result = mAudioSession->addParticipant(id); +        result = session->addParticipant(id);      }      return result;  } -void LLWebRTCVoiceClient::removeParticipantByID(const LLUUID &id) +void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id)  {      participantStatePtr_t result; -    if (mAudioSession) +    auto& session = sessionState::matchSessionByChannelID(channelID); +    if (session)      { -        participantStatePtr_t participant = mAudioSession->findParticipantByID(id); +        participantStatePtr_t participant = session->findParticipantByID(id);          if (participant)          { -            mAudioSession->removeParticipant(participant); +            session->removeParticipant(participant);          }      }  } @@ -2437,28 +1229,29 @@ bool LLWebRTCVoiceClient::checkParcelChanged(bool update)  }  bool LLWebRTCVoiceClient::switchChannel( -	std::string uri, +	const std::string channelID,  	bool spatial,  	bool no_reconnect,  	bool is_p2p, -	std::string hash) +	std::string hash, +    S32 parcel_local_id)  { -	bool needsSwitch = !mIsInChannel; -	 -    if (mIsInChannel) +	bool needsSwitch = false; + +    if (mAudioSession)      {          if (mSessionTerminateRequested)          {              // If a terminate has been requested, we need to compare against where the URI we're already headed to.              if(mNextAudioSession)              { -                if(mNextAudioSession->mSIPURI != uri) +                if (mNextAudioSession->mChannelID != channelID)                      needsSwitch = true;              }              else              {                  // mNextAudioSession is null -- this probably means we're on our way back to spatial. -                if(!uri.empty()) +                if (!channelID.empty())                  {                      // We do want to process a switch in this case.                      needsSwitch = true; @@ -2470,14 +1263,14 @@ bool LLWebRTCVoiceClient::switchChannel(              // Otherwise, compare against the URI we're in now.              if(mAudioSession)              { -                if(mAudioSession->mSIPURI != uri) +                if (mAudioSession->mChannelID != channelID)                  {                      needsSwitch = true;                  }              }              else              { -                if(!uri.empty()) +                if (!channelID.empty())                  {                      // mAudioSession is null -- it's not clear what case would cause this.                      // For now, log it as a warning and see if it ever crops up. @@ -2487,10 +1280,24 @@ bool LLWebRTCVoiceClient::switchChannel(              }          }      } +    else +    { +        if (!mNextAudioSession || mNextAudioSession->mChannelID != channelID) +        { +            needsSwitch = true; +        } +    }      if(needsSwitch)  	{ -		if(uri.empty()) +        if (mAudioSession) +        { +            // If we're already in a channel, or if we're joining one, terminate +            // so we can rejoin with the new session data. +            sessionTerminate(); +            mAudioSession->shutdownAllConnections(); +        } +        if (channelID.empty())  		{  			// Leave any channel we may be in  			LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; @@ -2511,20 +1318,13 @@ bool LLWebRTCVoiceClient::switchChannel(  		}  		else  		{ -			LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; +			LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; -			mNextAudioSession = addSession(uri); +			mNextAudioSession             = addSession(channelID, parcel_local_id);  			mNextAudioSession->mIsSpatial = spatial;  			mNextAudioSession->mReconnect = !no_reconnect;  			mNextAudioSession->mIsP2P = is_p2p;  		} -		 -        if (mIsInChannel) -		{ -			// If we're already in a channel, or if we're joining one, terminate -			// so we can rejoin with the new session data. -			sessionTerminate(); -		}  	}      return needsSwitch; @@ -2534,7 +1334,7 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session)  {  	mNextAudioSession = session; -    if (mIsInChannel) +    if (mAudioSession)      {          // If we're already in a channel, or if we're joining one, terminate          // so we can rejoin with the new session data. @@ -2550,14 +1350,13 @@ void LLWebRTCVoiceClient::setNonSpatialChannel(  }  bool LLWebRTCVoiceClient::setSpatialChannel( -	const std::string &uri, const std::string &credentials) +	const std::string &uri, const std::string &credentials, S32 parcel_local_id)  { -	mSpatialSessionURI = uri; -	mAreaVoiceDisabled = mSpatialSessionURI.empty(); +    mAreaVoiceDisabled = uri.empty();  	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; -	if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) +	if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))  	{  		// User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.  		LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; @@ -2565,31 +1364,29 @@ bool LLWebRTCVoiceClient::setSpatialChannel(  	}  	else  	{ -		return switchChannel(mSpatialSessionURI, true, false, false); +        return switchChannel(uri, true, false, false, "", parcel_local_id);  	}  }  void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) -{ -	switchChannel(uuid.asString(), false, true, true); +{  +    switchChannel(uuid.asString(), false, true, true);  } - -  void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid)  {  } -bool LLWebRTCVoiceClient::isValidChannel(std::string &sessionHandle) +bool LLWebRTCVoiceClient::isValidChannel(std::string &channelID)  { -  return(findSession(sessionHandle) != NULL); +  return(findP2PSession(LLUUID(channelID)) != NULL);  } -bool LLWebRTCVoiceClient::answerInvite(std::string &sessionHandle) +bool LLWebRTCVoiceClient::answerInvite(std::string &channelID)  {  	// this is only ever used to answer incoming p2p call invites. -    sessionStatePtr_t session(findSession(sessionHandle)); +    sessionStatePtr_t session(findP2PSession(LLUUID(channelID)));  	if(session)  	{  		session->mIsSpatial = false; @@ -2618,21 +1415,6 @@ bool LLWebRTCVoiceClient::isVoiceWorking() const  BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id)  {  	BOOL result = TRUE;  -    sessionStatePtr_t session(findSession(id)); -	 -	if(!session) -	{ -		// Didn't find a matching session -- check the current audio session for a matching participant -		if(mAudioSession) -		{ -            participantStatePtr_t participant(findParticipantByID(id)); -			if(participant) -			{ -				result = participant->isAvatar(); -			} -		} -	} -	  	return result;  } @@ -2641,7 +1423,7 @@ BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id)  BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)  {  	BOOL result = TRUE;  -    sessionStatePtr_t session(findSession(session_id)); +    sessionStatePtr_t session(findP2PSession(session_id));  	if(session != NULL)  	{ @@ -2656,7 +1438,7 @@ BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)  BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)  {  	bool result = TRUE; -    sessionStatePtr_t session(findSession(session_id)); +    sessionStatePtr_t session(findP2PSession(session_id));  	if(session != NULL)  	{ @@ -2689,26 +1471,12 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel()  std::string LLWebRTCVoiceClient::getCurrentChannel()  { -	std::string result; -	 -    if (mIsInChannel && !mSessionTerminateRequested) -	{ -		result = getAudioSessionURI(); -	} -	 -	return result; +	return getAudioSessionURI();  }  bool LLWebRTCVoiceClient::inProximalChannel()  { -	bool result = false; -	 -    if (mIsInChannel && !mSessionTerminateRequested) -	{ -		result = inSpatialChannel(); -	} -	 -	return result; +	return inSpatialChannel();  }  std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) @@ -2817,22 +1585,11 @@ std::string LLWebRTCVoiceClient::getAudioSessionURI()  	std::string result;  	if(mAudioSession) -		result = mAudioSession->mSIPURI; -		 -	return result; -} - -std::string LLWebRTCVoiceClient::getAudioSessionHandle() -{ -	std::string result; -	 -	if(mAudioSession) -		result = mAudioSession->mHandle; +		result = mAudioSession->mChannelID;  	return result;  } -  /////////////////////////////  // Sending updates of current state @@ -2877,7 +1634,7 @@ void LLWebRTCVoiceClient::updatePosition(void)  		LLWebRTCVoiceClient::getInstance()->setCameraPosition(  															 pos,				// position  															 LLVector3::zero, 	// velocity -                                                            LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix +                                                             LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix  		// Send the current avatar position to the voice code          qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); @@ -2891,6 +1648,8 @@ void LLWebRTCVoiceClient::updatePosition(void)  															 pos,				// position  															 LLVector3::zero, 	// velocity  															 qrot);				// rotation matrix + +        enforceTether();  	}  } @@ -2953,7 +1712,7 @@ bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string  void LLWebRTCVoiceClient::leaveChannel(void)  { -    if (mIsInChannel) +    if (mAudioSession || mNextAudioSession)  	{  		LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL;  		mChannelName.clear(); @@ -2963,16 +1722,22 @@ void LLWebRTCVoiceClient::leaveChannel(void)  void LLWebRTCVoiceClient::setMuteMic(bool muted)  { -    participantStatePtr_t participant = findParticipantByID(gAgentID); + +    if (mWebRTCDeviceInterface) +    { +        mWebRTCDeviceInterface->setMute(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());      if (participant) -	{ +    {          participant->mPower = 0.0; -	} -    if (mWebRTCAudioInterface) -	{ -        mWebRTCAudioInterface->setMute(muted); -	} -    mMuteMic = muted; +    }  }  void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -2998,8 +1763,8 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)              if (!mIsCoroutineActive)              { -                LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", -                    boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); +                LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", +                    boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance()));              }              else              { @@ -3064,7 +1829,6 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume)  	if (volume != mSpeakerVolume)  	{          { -            LLMutexLock lock(&mVoiceStateMutex);              int         min_volume = 0.0;              if ((volume == min_volume) || (mSpeakerVolume == min_volume))              { @@ -3074,7 +1838,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume)              mSpeakerVolume      = volume;              mSpeakerVolumeDirty = true;          } -        if (mWebRTCAudioInterface) +        if (mWebRTCDeviceInterface)          {              mWebRTCDeviceInterface->setSpeakerVolume(volume);          } @@ -3097,7 +1861,11 @@ void LLWebRTCVoiceClient::setMicGain(F32 volume)  BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id)  {  	BOOL result = FALSE; -    participantStatePtr_t participant(findParticipantByID(id)); +    if (!mAudioSession) +    { +        return FALSE; +    } +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		// I'm not sure what the semantics of this should be. @@ -3111,7 +1879,11 @@ BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id)  std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id)  {  	std::string result; -    participantStatePtr_t participant(findParticipantByID(id)); +    if (!mAudioSession) +    { +        return result; +    } +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		result = participant->mDisplayName; @@ -3125,8 +1897,11 @@ std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id)  BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id)  {  	BOOL result = FALSE; - -    participantStatePtr_t participant(findParticipantByID(id)); +    if (!mAudioSession) +    { +        return result; +    } +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		result = participant->mIsSpeaking; @@ -3138,8 +1913,11 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id)  BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id)  {  	BOOL result = FALSE; - -    participantStatePtr_t participant(findParticipantByID(id)); +    if (!mAudioSession) +    { +        return result; +    } +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		result = participant->mIsModeratorMuted; @@ -3151,7 +1929,11 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id)  F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id)  {      F32 result = 0; -	participantStatePtr_t participant(findParticipantByID(id)); +    if (!mAudioSession) +    { +        return result; +    } +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if (participant)  	{  		result = participant->mPower * 2.0; @@ -3162,8 +1944,11 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id)  BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id)  {  	BOOL result = FALSE; - -    participantStatePtr_t participant(findParticipantByID(id)); +    if (!mAudioSession) +    { +        return result; +    } +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		// I'm not sure what the semantics of this should be. @@ -3178,7 +1963,7 @@ BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id)  {  	BOOL result = FALSE; -    participantStatePtr_t participant(findParticipantByID(id)); +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		result = participant->mOnMuteList; @@ -3193,7 +1978,7 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id)      // Minimum volume will be returned for users with voice disabled      F32 result = LLVoiceClient::VOLUME_MIN; -    participantStatePtr_t participant(findParticipantByID(id)); +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));      if(participant)  	{  		result = participant->mVolume; @@ -3209,7 +1994,7 @@ void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume)  {  	if(mAudioSession)  	{ -        participantStatePtr_t participant(findParticipantByID(id)); +        participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  		if (participant && !participant->mIsSelf)  		{  			if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) @@ -3235,7 +2020,7 @@ std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id)  {  	std::string result; -    participantStatePtr_t participant(findParticipantByID(id)); +    participantStatePtr_t participant(mAudioSession->findParticipant(id.asString()));  	if(participant)  	{  		result = participant->mGroupID; @@ -3250,12 +2035,11 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled()  }  //------------------------------------------------------------------------ -std::set<LLWebRTCVoiceClient::sessionState::wptr_t> LLWebRTCVoiceClient::sessionState::mSession; +std::map<std::string, LLWebRTCVoiceClient::sessionState::ptr_t> LLWebRTCVoiceClient::sessionState::mSessions;  LLWebRTCVoiceClient::sessionState::sessionState() :      mErrorStatusCode(0), -    mMediaStreamState(streamStateUnknown),      mIsChannel(false),      mIsSpatial(false),      mIsP2P(false), @@ -3269,23 +2053,26 @@ LLWebRTCVoiceClient::sessionState::sessionState() :  }  /*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession() +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession(const std::string& channelID, S32 parcelLocalID)  { -    sessionState::ptr_t ptr(new sessionState()); +    LLUUID  region_id = gAgent.getRegion()->getRegionID();     -    std::pair<std::set<wptr_t>::iterator, bool>  result = mSession.insert(ptr); +    sessionState::ptr_t session(new sessionState()); +    session->mChannelID = channelID; +    session->mWebRTCConnections[channelID] = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); +    session->mPrimaryConnectionID          = channelID; -    if (result.second) -        ptr->mMyIterator = result.first; +    // add agent as participant +    session->addParticipant(gAgentID); -    return ptr; +    mSessions[channelID] = session; + +    return session;  }  LLWebRTCVoiceClient::sessionState::~sessionState()  { -    LL_INFOS("Voice") << "Destroying session handle=" << mHandle << " SIP=" << mSIPURI << LL_ENDL; -    if (mMyIterator != mSession.end()) -        mSession.erase(mMyIterator); +    LL_INFOS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL;  	removeAllParticipants();  } @@ -3304,78 +2091,49 @@ bool LLWebRTCVoiceClient::sessionState::isTextIMPossible()  	return false;  } - -/*static*/  -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) -{ -    sessionStatePtr_t result; - -    // *TODO: My kingdom for a lambda! -    std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByHandle, _1, handle)); - -    if (it != mSession.end()) -        result = (*it).lock(); - -    return result; -} - -/*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByURI(const std::string &uri) -{ -    sessionStatePtr_t result; - -    // *TODO: My kingdom for a lambda! -    std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testBySIPOrAlterateURI, _1, uri)); - -    if (it != mSession.end()) -        result = (*it).lock(); - -    return result; -} -  /*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByParticipant(const LLUUID &participant_id) +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByChannelID(const std::string& channel_id)  {      sessionStatePtr_t result;      // *TODO: My kingdom for a lambda! -    std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); - -    if (it != mSession.end()) -        result = (*it).lock(); - +    std::map<std::string, ptr_t>::iterator it = mSessions.find(channel_id); +    if (it != mSessions.end()) +    { +        result = (*it).second; +    }      return result;  }  void LLWebRTCVoiceClient::sessionState::for_each(sessionFunc_t func)  { -    std::for_each(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func)); +    std::for_each(mSessions.begin(), mSessions.end(), boost::bind(for_eachPredicate, _1, func));  } -// simple test predicates.   -// *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features. -bool LLWebRTCVoiceClient::sessionState::testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle) +void LLWebRTCVoiceClient::sessionState::reapEmptySessions()  { -    ptr_t aLock(a.lock()); - -    return aLock ? aLock->mHandle == handle : false; +    std::map<std::string, ptr_t>::iterator iter; +    for (iter = mSessions.begin(); iter != mSessions.end();) +    { +        if (!iter->second->isEmpty()) +        { +            iter = mSessions.erase(iter); +        } +        else +        { +            ++iter; +        } +    }  } -bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) -{ -    ptr_t aLock(a.lock()); - -    return aLock ? (aLock->mSIPURI == uri) : false; -} -bool LLWebRTCVoiceClient::sessionState::testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) +bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri)  {      ptr_t aLock(a.lock()); -    return aLock ? ((aLock->mSIPURI == uri) || (aLock->mAlternateSIPURI == uri)) : false; +    return aLock ? (aLock->mChannelID == LLUUID(uri)) : false;  } -  bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId)  {      ptr_t aLock(a.lock()); @@ -3384,9 +2142,9 @@ bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient  }  /*static*/ -void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceClient::sessionState::wptr_t &a, sessionFunc_t func) +void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pair<std::string, LLWebRTCVoiceClient::sessionState::wptr_t> &a, sessionFunc_t func)  { -    ptr_t aLock(a.lock()); +    ptr_t aLock(a.second.lock());      if (aLock)          func(aLock); @@ -3396,101 +2154,63 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceCli      }  } -void LLWebRTCVoiceClient::sessionEstablished() -{ -    mWebRTCAudioInterface->setMute(mMuteMic); -	addSession(gAgent.getRegion()->getRegionID().asString());  -} - -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const std::string &handle) +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const LLUUID &agent_id)  { -    sessionStatePtr_t result; -	sessionMap::iterator iter = mSessionsByHandle.find(handle); -	if(iter != mSessionsByHandle.end()) -	{ -		result = iter->second; -	} +    sessionStatePtr_t result = sessionState::matchSessionByChannelID(agent_id.asString()); +    if (result && result->mIsP2P) +    { +        return result; +    } +    result.reset();  	return result;  } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const LLUUID &participant_id) -{ -    sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); -	 -	return result; + + +void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() +{  +    for (auto &&connection : mWebRTCConnections) +    { +        connection.second->shutDown(); +    }  } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &uri, const std::string &handle) + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string& channel_id, S32 parcel_local_id)  {      sessionStatePtr_t result; -	 -	if(handle.empty()) -	{ -        // No handle supplied. -        // Check whether there's already a session with this URI -        result = sessionState::matchSessionByURI(uri); -	} -	else // (!handle.empty()) -	{ -		// Check for an existing session with this handle -		sessionMap::iterator iter = mSessionsByHandle.find(handle); -		 -		if(iter != mSessionsByHandle.end()) -		{ -			result = iter->second; -		} -	} + +    // Check whether there's already a session with this URI +    result = sessionState::matchSessionByChannelID(channel_id);  	if(!result)  	{  		// No existing session found. -		LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL; -        result = sessionState::createSession(); -		result->mSIPURI = uri; -		result->mHandle = handle; +		LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; +        result = sessionState::createSession(channel_id, parcel_local_id);  		if (LLVoiceClient::instance().getVoiceEffectEnabled())  		{  			result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault();  		} - -		if(!result->mHandle.empty()) -		{ -            // *TODO: Rider: This concerns me.  There is a path (via switchChannel) where  -            // we do not track the session.  In theory this means that we could end up with  -            // a mAuidoSession that does not match the session tracked in mSessionsByHandle -			mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); -		}  	}  	else  	{  		// Found an existing session -		if(uri != result->mSIPURI) +		if (channel_id != result->mChannelID)  		{  			// TODO: Should this be an internal error? -			LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; -			setSessionURI(result, uri); -		} +			LL_DEBUGS("Voice") << "changing uri from " << result->mChannelID << " to " << channel_id << LL_ENDL; -		if(handle != result->mHandle) -		{ -			if(handle.empty()) -			{ -				// There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. -				LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; -			} -			else -			{ -				// TODO: Should this be an internal error? -				LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; -				setSessionHandle(result, handle); -			} +            result->mChannelID = channel_id; + +            verifySessionState();  		} -		LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; +		LL_DEBUGS("Voice") << "returning existing session: CHANNEL " << channel_id << LL_ENDL;  	}  	verifySessionState(); @@ -3498,90 +2218,11 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std  	return result;  } -void LLWebRTCVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) -{ -    if (session) -    { -        if (!session->mHandle.empty()) -        { -            sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); -            if (iter != mSessionsByHandle.end()) -            { -                mSessionsByHandle.erase(iter); -            } -        } -        else -        { -            LL_WARNS("Voice") << "Session has empty handle!" << LL_ENDL; -        } -    } -    else -    { -        LL_WARNS("Voice") << "Attempt to clear NULL session!" << LL_ENDL; -    } - -} - -void LLWebRTCVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle) -{ -	// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. -	 -	if(!session->mHandle.empty()) -	{ -		// Remove session from the map if it should have been there. -		sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); -		if(iter != mSessionsByHandle.end()) -		{ -			if(iter->second != session) -			{ -				LL_WARNS("Voice") << "Internal error: session mismatch! Session may have been duplicated. Removing version in map." << LL_ENDL; -			} - -			mSessionsByHandle.erase(iter); -		} -		else -		{ -            LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL; -		} -	} -			 -	session->mHandle = handle; - -	if(!handle.empty()) -	{ -		mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session)); -	} - -	verifySessionState(); -} - -void LLWebRTCVoiceClient::setSessionURI(const sessionStatePtr_t &session, const std::string &uri) -{ -	// There used to be a map of session URIs to sessions, which made this complex.... -	session->mSIPURI = uri; - -	verifySessionState(); -} -  void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session)  { -	// Remove the session from the handle map -	if(!session->mHandle.empty()) -	{ -		sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); -		if(iter != mSessionsByHandle.end()) -		{ -			if(iter->second != session) -			{ -				LL_WARNS("Voice") << "Internal error: session mismatch, removing session in map." << LL_ENDL; -			} -			mSessionsByHandle.erase(iter); -		} -	} -  	// At this point, the session should be unhooked from all lists and all state should be consistent.  	verifySessionState(); - +    session->shutdownAllConnections();  	// If this is the current audio session, clean up the pointer which will soon be dangling.  	if(mAudioSession == session)  	{ @@ -3594,24 +2235,15 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session)  	{  		mNextAudioSession.reset();  	} -  } -void LLWebRTCVoiceClient::deleteAllSessions() +void LLWebRTCVoiceClient::sessionState::deleteAllSessions()  { -	LL_DEBUGS("Voice") << LL_ENDL; - -    while (!mSessionsByHandle.empty()) -	{ -        const sessionStatePtr_t session = mSessionsByHandle.begin()->second; -        deleteSession(session); -	} -	 +    mSessions.clear();  }  void LLWebRTCVoiceClient::verifySessionState(void)  { -    LL_DEBUGS("Voice") << "Sessions in handle map=" << mSessionsByHandle.size() << LL_ENDL;      sessionState::VerifySessions();  } @@ -3710,7 +2342,10 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt  		// In case onError() deleted an entry.  		it = mStatusObservers.upper_bound(observer);  	} +    mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; +    { +    }  	// skipped to avoid speak button blinking  	if (   status != LLVoiceClientStatusObserver::STATUS_JOINING  		&& status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL @@ -3773,7 +2408,6 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se      if (participant)      {          // Found -- fill in the name -        participant->mAccountName = name;          // and post a "participants updated" message to listeners later.          session->mParticipantsChanged = true;      } @@ -3795,26 +2429,532 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string  std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } -LLWebRTCSecurity::LLWebRTCSecurity() +///////////////////////////// +// WebRTC Signaling Handlers + +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string& channelID) : +    mWebRTCPeerConnection(nullptr), +    mWebRTCAudioInterface(nullptr), +    mWebRTCDataInterface(nullptr), +    mIceCompleted(false), +    mTrickling(false), +    mVoiceConnectionState(VOICE_STATE_START_SESSION), +    mChannelID(channelID), +    mRegionID(regionID), +    mParcelLocalID(parcelLocalID), +    mShutDown(false)  { -    // This size is an arbitrary choice; WebRTC does not care -    // Use a multiple of three so that there is no '=' padding in the base64 (purely an esthetic choice) -    #define WebRTC_TOKEN_BYTES 9 -    U8  random_value[WebRTC_TOKEN_BYTES]; +    mWebRTCPeerConnection = llwebrtc::newPeerConnection(); +    mWebRTCPeerConnection->setSignalingObserver(this); +} -    for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection()  +{ +    if (LLWebRTCVoiceClient::isShuttingDown())      { -        random_value[b] = ll_rand() & 0xff; +        // peer connection and observers will be cleaned up +        // by llwebrtc::terminate() on shutdown. +        return;      } -    mConnectorHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); +    mWebRTCPeerConnection->unsetSignalingObserver(this); +    llwebrtc::freePeerConnection(mWebRTCPeerConnection); +    mWebRTCPeerConnection = nullptr; +} + +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +{ +    LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; + +    switch (state) +    { +        case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: +        { +            LLMutexLock lock(&mVoiceStateMutex); +            mIceCompleted = true; +            break; +        } +        case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: +        { +            LLMutexLock lock(&mVoiceStateMutex); +            mIceCompleted = false; +        } +        default: +            break; +    } +} + +void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +{ +    LLMutexLock lock(&mVoiceStateMutex); +    mIceCandidates.push_back(candidate); +} + +void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +{ +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    mTrickling = false; +} + +void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) +{ +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); +    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy)); + +    if (retries >= 0) +    { +        LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying.  " << result << LL_ENDL; +        LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +            url, +            LLCore::HttpRequest::DEFAULT_POLICY_ID, +            body, +            boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), +            boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); +    } +    else +    { +        LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection.  " << result << LL_ENDL; +        setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); +        mTrickling = false; +    } +} + +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::OnDataReceived(const std::string &data, bool binary) +{ +    // incoming data will be a json structure (if it's not binary.)  We may pack +    // binary for size reasons.  Most of the keys in the json objects are +    // single or double characters for size reasons. +    // The primary element is: +    // An object where each key is an agent id.  (in the future, we may allow +    // 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) +    // 'l'  - boolean, always true if exists. + +    if (binary) +    { +        LL_WARNS("Voice") << "Binary data received from data channel." << LL_ENDL; +        return; +    } + +    Json::Reader reader; +    Json::Value  voice_data; +    if (reader.parse(data, voice_data, false))  // don't collect comments +    { +        if (!voice_data.isObject()) +        { +            LL_WARNS("Voice") << "Expected object from data channel:" << data << LL_ENDL; +            return; +        } +        bool new_participant = false; +        for (auto &participant_id : voice_data.getMemberNames()) +        { +            LLUUID agent_id(participant_id); +            if (agent_id.isNull()) +            { +                LL_WARNS("Voice") << "Bad participant ID from data channel (" << participant_id << "):" << data << LL_ENDL; +                continue; +            } + +            LLWebRTCVoiceClient::participantStatePtr_t participant = LLWebRTCVoiceClient::getInstance()->findParticipantByID(mChannelID, agent_id); +            bool                  joined      = voice_data[participant_id].get("j", Json::Value(false)).asBool(); +            new_participant |= joined; +            if (!participant && joined) +            { +                participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); +            } +            if (participant) +            { +                if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) +                { +                    LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); +                } +                else +                { +                    F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; +                    // convert to decibles +                    participant->mPower = energyRMS; +                    /* WebRTC appears to have deprecated VAD, but it's still in the Audio Processing Module so maybe we +                       can use it at some point when we actually process frames. */ +                    participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; +                } +            } +        } +    } +} + +void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) +{ +    if (data_interface) +    { +        mWebRTCDataInterface = data_interface; +        mWebRTCDataInterface->setDataObserver(this); +        Json::FastWriter writer; +        Json::Value      root = Json::objectValue; +        root["j"]             = true; +        std::string json_data = writer.write(root); +        mWebRTCDataInterface->sendData(json_data, false); +    } +} + +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() +{ +    LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; +    setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); +} + +void LLVoiceWebRTCConnection::processIceUpdates() +{ +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); +    if (!regionp || !regionp->capabilitiesReceived()) +    { +        LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; +        return; +    } + +    std::string url = regionp->getCapability("VoiceSignalingRequest"); +    if (url.empty()) +    { +        return; +    } + +    LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; + +    LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); +    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy)); +    LLCore::HttpRequest::ptr_t                  httpRequest(new LLCore::HttpRequest); +    LLCore::HttpOptions::ptr_t                  httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + +    bool iceCompleted = false; +    LLSD body; +    { +        if (!mTrickling) +        { +            if (mIceCandidates.size()) +            { +                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); +                } +                body["candidates"] = candidates; +                mIceCandidates.clear(); +            } +            else if (mIceCompleted) +            { +                LLSD body_candidate; +                body_candidate["completed"] = true; +                body["candidate"]           = body_candidate; +                iceCompleted                = mIceCompleted; +                mIceCompleted               = false; +            } +            else +            { +                return; +            } +            LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +                url, +                LLCore::HttpRequest::DEFAULT_POLICY_ID, +                body, +                boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), +                boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); +            mTrickling = true; +        } +    } +} + +bool LLVoiceWebRTCConnection::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; +    } + +    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +        url, +        LLCore::HttpRequest::DEFAULT_POLICY_ID, +        body, +        boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), +        boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); +    return true; +} + +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +{ +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); + +    if (result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) +    { +        mRemoteChannelSDP = result["jsep"]["sdp"].asString(); +    } +    std::string voiceAccountServerUri; +    std::string voiceUserName = gAgent.getID().asString(); +    std::string voicePassword = "";  // no password for now. + +    LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" +                       << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " +                       << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << mRemoteChannelSDP << LL_ENDL; + +    mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); +} + +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +{ +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    if (retries >= 0) +    { +        LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +            url, +            LLCore::HttpRequest::DEFAULT_POLICY_ID, +            body, +            boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), +            boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); +    } +    else +    { +        LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; +        setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);        +    } +} + +bool LLVoiceWebRTCConnection::connectionStateMachine() +{ +    U32 retry                = 0; -    for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) +    processIceUpdates(); + +    switch (getVoiceConnectionState()) +    { +        case VOICE_STATE_START_SESSION: +        { +            mTrickling    = false; +            mIceCompleted = false; +            setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START); +            if (!mWebRTCPeerConnection->initializeConnection()) +            { +                setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); +            } +            break; +        } +        case VOICE_STATE_WAIT_FOR_SESSION_START: +        { +            break; +        } +        case VOICE_STATE_REQUEST_CONNECTION: +            if (!requestVoiceConnection()) +            { +                setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); +            } +            else +            { +                setVoiceConnectionState(VOICE_STATE_CONNECTION_WAIT); +            } +            break; +        case VOICE_STATE_CONNECTION_WAIT: +            break; + +        case VOICE_STATE_SESSION_ESTABLISHED: +        { +            LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID); +            setVoiceConnectionState(VOICE_STATE_SESSION_UP); +        } +        break; +        case VOICE_STATE_SESSION_UP: +        { +            if (mShutDown) +            { +                setVoiceConnectionState(VOICE_STATE_DISCONNECT); +            } +            break; +        } + +        case VOICE_STATE_SESSION_RETRY: +            LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID); +            setVoiceConnectionState(VOICE_STATE_DISCONNECT); +            break; +        break; + +        case VOICE_STATE_DISCONNECT: +            if (breakVoiceConnection(true)) +            { +                setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); +            } +            else +            { +                setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); +            } +            retry = 0;  // Connected without issues +            break; + +        case VOICE_STATE_WAIT_FOR_EXIT: +            break; +        case VOICE_STATE_SESSION_EXIT: +        { +            { +                LLMutexLock lock(&mVoiceStateMutex); +                if (!mShutDown)  +                { +                    mVoiceConnectionState = VOICE_STATE_START_SESSION; +                } +                else +                { +                    return false; +                } +            } +            break; +        } + +        default: +        { +            LL_WARNS("Voice") << "Unknown voice control state " << getVoiceConnectionState() << LL_ENDL; +            return false; +        } +    } +    return true; +} + + +void LLVoiceWebRTCConnection::sendData(const std::string& data) {  +    if (mWebRTCDataInterface) +    { +        mWebRTCDataInterface->sendData(data, false); +    } +} + +bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) +{ +    LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; +    if (mWebRTCDataInterface) +    { +        mWebRTCDataInterface->unsetDataObserver(this); +        mWebRTCDataInterface = nullptr; +    } +    mWebRTCAudioInterface = nullptr; +    if (mWebRTCPeerConnection) +    { +        mWebRTCPeerConnection->shutdownConnection(); +    } +    LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); +    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())      { -        random_value[b] = ll_rand() & 0xff; +        return false;      } -    mAccountHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); + +    LL_DEBUGS("Voice") << "region ready for voice break; url=" << url << LL_ENDL; + +    LLCore::HttpRequest::policy_t               httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); +    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy)); +    LLCore::HttpRequest::ptr_t                  httpRequest(new LLCore::HttpRequest); + +    LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); +    LLSD body; +    body["logout"] = TRUE; + +    LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +        url, +        LLCore::HttpRequest::DEFAULT_POLICY_ID, +        body, +        boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), +        boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); +    return true;  } -LLWebRTCSecurity::~LLWebRTCSecurity() +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result)  { +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    setVoiceConnectionState(VOICE_STATE_SESSION_EXIT);  } + +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +{ +    if (LLWebRTCVoiceClient::isShuttingDown()) +    { +        return; +    } +    if (retries >= 0) +    { +        LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( +            url, +            LLCore::HttpRequest::DEFAULT_POLICY_ID, +            body, +            boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), +            boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); +    } +    setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); +}
\ No newline at end of file diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ee7edb3030..456681ed25 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -39,6 +39,7 @@ class LLWebRTCProtocolParser;  #include "llcallingcard.h"   // for LLFriendObserver  #include "lleventcoro.h"  #include "llcoros.h" +#include "llparcel.h"  #include <queue>  #include "json/reader.h" @@ -53,14 +54,13 @@ class LLWebRTCProtocolParser;  #include <llwebrtc.h>  class LLAvatarName; +class LLVoiceWebRTCConnection; +typedef boost::shared_ptr<LLVoiceWebRTCConnection> connectionPtr_t;  class LLWebRTCVoiceClient :	public LLSingleton<LLWebRTCVoiceClient>,  							virtual public LLVoiceModuleInterface,  							virtual public LLVoiceEffectInterface, -                            public llwebrtc::LLWebRTCDevicesObserver, -                            public llwebrtc::LLWebRTCSignalingObserver, -                            public llwebrtc::LLWebRTCAudioObserver, -                            public llwebrtc::LLWebRTCDataObserver +                            public llwebrtc::LLWebRTCDevicesObserver  {      LLSINGLETON_C11(LLWebRTCVoiceClient);  	LOG_CLASS(LLWebRTCVoiceClient); @@ -72,6 +72,8 @@ public:  	//@{  	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; @@ -145,8 +147,12 @@ public:  	void setNonSpatialChannel(const std::string &uri,  									  const std::string &credentials) override; -	bool setSpatialChannel(const std::string &uri, -								   const std::string &credentials) override; +	bool setSpatialChannel(const std::string &uri, const std::string &credentials) override  +	{ +        return setSpatialChannel(uri, credentials, INVALID_PARCEL_ID); +	} + +    bool setSpatialChannel(const std::string &uri, const std::string &credentials, S32 localParcelID);  	void leaveNonSpatialChannel() override; @@ -163,9 +169,9 @@ public:  	//@{  	// start a voice channel with the specified user  	void callUser(const LLUUID &uuid) override; -	bool isValidChannel(std::string &channelHandle) override; -	bool answerInvite(std::string &channelHandle) override; -	void declineInvite(std::string &channelHandle) override; +	bool isValidChannel(std::string &channelID) override; +    bool answerInvite(std::string &channelID) override; +    void declineInvite(std::string &channelID) override;  	//@}  	///////////////////////// @@ -232,10 +238,14 @@ public:      bool isPreviewPlaying() override { return false; }      //@} -	  	// authorize the user -	void userAuthorized(const std::string& user_id, -								const LLUUID &agentID) override; +    void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; + + +	void OnConnectionEstablished(const std::string& channelID); +    void OnConnectionFailure(const std::string &channelID); +    void sendPositionAndVolumeUpdate(bool force); +    void updateOwnVolume();  	//////////////////////////////  	/// @name Status notification @@ -247,10 +257,6 @@ public:  	void addObserver(LLVoiceClientParticipantObserver* observer) override;  	void removeObserver(LLVoiceClientParticipantObserver* observer) override;  	//@} -	 -	//@} - -  	//////////////////////////////      /// @name Devices change notification @@ -260,64 +266,17 @@ public:                            const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override;      //@} -	////////////////////////////// -    /// @name Signaling notification -    //  LLWebRTCSignalingObserver -    //@{ -    void OnIceGatheringState(IceGatheringState state) override; -    void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; -    void OnOfferAvailable(const std::string &sdp) override; -    void OnRenegotiationNeeded() override; -    void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; -    //@} - -	////////////////////////////// -    /// @name Signaling notification -    //  LLWebRTCAudioObserver -    //@{ -    //@} -     -    ///////////////////////// -    /// @name Data Notification -    /// LLWebRTCDataObserver -    //@{ -    void OnDataReceived(const std::string& data, bool binary) override; -    void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; -    //@} - -	void processIceUpdates(); -    void onIceUpdateComplete(bool ice_completed, const LLSD& result); -    void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); - - -	//@} - -protected: -	////////////////////// -	// WebRTC Specific definitions	 - -	 -	enum streamState -	{ -		streamStateUnknown = 0, -		streamStateIdle = 1, -		streamStateConnected = 2, -		streamStateRinging = 3, -		streamStateConnecting = 6,  // same as WebRTC session_media_connecting enum -		streamStateDisconnecting = 7,  //Same as WebRTC session_media_disconnecting enum -	};	  	struct participantState  	{  	public:  		participantState(const LLUUID& agent_id); -	        bool updateMuteState();	// true if mute state has changed +	    bool updateMuteState();	// true if mute state has changed  		bool isAvatar();  		std::string mURI;  		LLUUID mAvatarID; -		std::string mAccountName;  		std::string mDisplayName;  		LLFrameTimer mSpeakingTimeout;  		F32	mLastSpokeTimestamp; @@ -337,6 +296,12 @@ protected:      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); + +  protected: +      typedef std::map<const std::string, participantStatePtr_t> participantMap;      typedef std::map<const LLUUID, participantStatePtr_t> participantUUIDMap; @@ -348,7 +313,7 @@ protected:          typedef boost::function<void(const ptr_t &)> sessionFunc_t; -        static ptr_t createSession(); +        static ptr_t createSession(const std::string& channelID, S32 parcel_local_id);  		~sessionState();          participantStatePtr_t addParticipant(const LLUUID& agent_id); @@ -358,28 +323,37 @@ protected:          participantStatePtr_t findParticipant(const std::string &uri);          participantStatePtr_t findParticipantByID(const LLUUID& id); -        static ptr_t matchSessionByHandle(const std::string &handle); -        static ptr_t matchSessionByURI(const std::string &uri); -        static ptr_t matchSessionByParticipant(const LLUUID &participant_id); +        static ptr_t matchSessionByChannelID(const std::string& channel_id); + +		void shutdownAllConnections();  		bool isCallBackPossible();  		bool isTextIMPossible(); + +        void processSessionStates(); + +		void OnConnectionEstablished(const std::string &channelID); +        void OnConnectionFailure(const std::string &channelID); + +		void sendData(const std::string &data);          static void for_each(sessionFunc_t func); +		static void reapEmptySessions(); + +		bool isEmpty() { return mWebRTCConnections.empty(); } +  		std::string mHandle;  		std::string mGroupHandle; -		std::string mSIPURI; +		std::string mChannelID;  		std::string mAlias;  		std::string mName; -		std::string mAlternateSIPURI;  		std::string mErrorStatusString;  		std::queue<std::string> mTextMsgQueue;  		LLUUID		mIMSessionID;  		LLUUID		mCallerID;  		int			mErrorStatusCode; -		int			mMediaStreamState;  		bool		mIsChannel;	// True for both group and spatial channels (false for p2p, PSTN)  		bool		mIsSpatial;	// True for spatial channels  		bool		mIsP2P; @@ -399,18 +373,20 @@ protected:  		LLUUID		mVoiceFontID;          static void VerifySessions(); +        static void deleteAllSessions();      private: + +		std::map<std::string, connectionPtr_t> mWebRTCConnections; +        std::string   					       mPrimaryConnectionID; +          sessionState(); -        static std::set<wptr_t> mSession;   // canonical list of outstanding sessions. -        std::set<wptr_t>::iterator  mMyIterator;    // used for delete +        static std::map<std::string, ptr_t> mSessions;  // canonical list of outstanding sessions. -        static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); +        static void for_eachPredicate(const std::pair<std::string, LLWebRTCVoiceClient::sessionState::wptr_t> &a, sessionFunc_t func); -        static bool testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle);          static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); -        static bool testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri);          static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId);  	}; @@ -422,28 +398,20 @@ protected:  	// Private Member Functions  	////////////////////////////////////////////////////// - - +    static void predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t &session); +    static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); +    static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); +    static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); +    static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); +    static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute);  	//////////////////////////////  	/// @name TVC/Server management and communication  	//@{ -	// Call this if the connection to the daemon terminates unexpectedly.  It will attempt to reset everything and relaunch. -	void daemonDied();  	// 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 connectorCreate(); -	void connectorShutdown();	 -	void closeSocket(void);	 -	  //	void requestVoiceAccountProvision(S32 retries = 3); -	void setLoginInfo( -			   const std::string& account_name, -			   const std::string& password, -			   const std::string& channel_sdp); -	void logout();	  	//@} @@ -486,30 +454,12 @@ protected:  	/////////////////////////////  	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. -	 -    participantStatePtr_t findParticipantByID(const LLUUID& id); -    participantStatePtr_t addParticipantByID(const LLUUID &id);	 -    void removeParticipantByID(const LLUUID &id); -#if 0 -	//////////////////////////////////////// -	// voice sessions. -    typedef std::set<sessionStatePtr_t> sessionSet; -			 -	typedef sessionSet::iterator sessionIterator; -	sessionIterator sessionsBegin(void); -	sessionIterator sessionsEnd(void); -#endif -    void              sessionEstablished(); -    sessionStatePtr_t findSession(const std::string &handle); -    sessionStatePtr_t findSession(const LLUUID &participant_id); -	 -    sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); -    void clearSessionHandle(const sessionStatePtr_t &session); -    void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle); -    void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); +    void              sessionEstablished(const LLUUID& region_id); +    sessionStatePtr_t findP2PSession(const LLUUID &agent_id); +	 +    sessionStatePtr_t addSession(const std::string& channel_id, S32 parcel_local_id);      void deleteSession(const sessionStatePtr_t &session); -	void deleteAllSessions(void);  	void verifySessionState(void); @@ -518,11 +468,7 @@ protected:  	// 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); -	 -	// Returns true if the session seems to indicate we've moved to a region on a different voice server -    bool sessionNeedsRelog(const sessionStatePtr_t &session); -	 +    void reapSession(const sessionStatePtr_t &session);	  	//////////////////////////////////////  	// buddy list stuff, needed for SLIM later @@ -583,41 +529,9 @@ private:      // Coroutine support methods      //--- -    void voiceControlCoro(); -    void voiceControlStateMachine(); - -    int  mVoiceControlState; -    LLMutex mVoiceStateMutex; -	void setVoiceControlStateUnless(int new_voice_control_state, int unless=-1) -	{  -		LLMutexLock lock(&mVoiceStateMutex); -		if (mVoiceControlState != unless) -		{ -            mVoiceControlState = new_voice_control_state; -		}  -	} -    int  getVoiceControlState() -	{  -		LLMutexLock lock(&mVoiceStateMutex); -		return mVoiceControlState;  -	} - -    bool callbackEndDaemon(const LLSD& data); -    bool provisionVoiceAccount(); -    void OnVoiceAccountProvisioned(const LLSD& body); -    void OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result); -    bool establishVoiceConnection(); -    bool breakVoiceConnection(bool wait); -    bool loginToWebRTC(); -    void logoutOfWebRTC(bool wait); - -    bool requestParcelVoiceInfo(); +    void voiceConnectionCoro(); -    bool addAndJoinSession(const sessionStatePtr_t &nextSession); -    bool terminateAudioSession(bool wait); - -    bool waitForChannel(); -    bool runSession(const sessionStatePtr_t &session); +    void voiceConnectionStateMachine();      bool performMicTuning();      //--- @@ -632,72 +546,38 @@ private:  	int mSpatialJoiningNum;  	static void idle(void *user_data); -	 -	LLHost mDaemonHost; -	LLSocket::ptr_t mSocket; -	 -	// We should kill the voice daemon in case of connection alert  -	bool mTerminateDaemon; -	 -	std::string mAccountName; -	std::string mAccountPassword; -    std::string mChannelSDP; -    std::string mRemoteChannelSDP; -	std::string mAccountDisplayName; -  	bool mTuningMode;  	float mTuningEnergy; -	std::string mTuningAudioFile;  	int mTuningMicVolume;  	bool mTuningMicVolumeDirty;  	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 mSpatialSessionURI;  	std::string mSpatialSessionCredentials;  	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 +    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.      sessionStatePtr_t mNextAudioSession;	// Session state for the audio session we're trying to join  	S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings  	std::string mCurrentRegionName;		// Used to detect parcel boundary crossings -	 -    bool mConnectorEstablished; // set by "Create Connector" response -    bool mAccountLoggedIn;		// set by login message		 -	int  mNumberOfAliases; -	U32  mCommandCookie; -	 -	int mLoginRetryCount; -	 -	sessionMap mSessionsByHandle;				// Active sessions, indexed by session handle.  Sessions which are being initiated may not be in this map. -#if 0 -	sessionSet mSessions;						// All sessions, not indexed.  This is the canonical session list. -#endif -	 +		  	bool mBuddyListMapPopulated;  	bool mBlockRulesListReceived;  	bool mAutoAcceptRulesListReceived;  	buddyListMap mBuddyListMap;  	llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; -    llwebrtc::LLWebRTCPeerConnection  *mWebRTCPeerConnection; -    llwebrtc::LLWebRTCAudioInterface  *mWebRTCAudioInterface; -    llwebrtc::LLWebRTCDataInterface   *mWebRTCDataInterface;  	LLVoiceDeviceList mCaptureDevices;  	LLVoiceDeviceList mRenderDevices; -    std::vector<llwebrtc::LLWebRTCIceCandidate> mIceCandidates; -    bool                                        mIceCompleted; -    bool                                        mTrickling;  	uint32_t mAudioLevel; @@ -705,7 +585,12 @@ private:  	bool mShutdownComplete;  	bool checkParcelChanged(bool update = false); -	bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); +    bool switchChannel(const std::string channelID, +                       bool         spatial      = true, +                       bool         no_reconnect = false, +                       bool         is_p2p       = false, +                       std::string  hash         = "", +		               S32          parcel_local_id = INVALID_PARCEL_ID);      void joinSession(const sessionStatePtr_t &session);  	std::string nameFromAvatar(LLVOAvatar *avatar); @@ -716,11 +601,8 @@ private:  	bool inSpatialChannel(void);  	std::string getAudioSessionURI(); -	std::string getAudioSessionHandle();      void setHidden(bool hidden) override; //virtual -    Json::Value getPositionAndVolumeUpdateJson(bool force); -	void sendPositionAndVolumeUpdate();  	void enforceTether(void); @@ -759,9 +641,6 @@ private:  	bool		mMicVolumeDirty;  	bool		mVoiceEnabled; -	bool		mWriteInProgress; -	std::string mWriteString; -	size_t		mWriteOffset;  	BOOL		mLipSyncEnabled; @@ -781,15 +660,13 @@ private:  	S32     mPlayRequestCount;      bool    mIsInTuningMode; -    bool    mIsInChannel;      bool    mIsJoiningSession;      bool    mIsWaitingForFonts;      bool    mIsLoggingIn; -    bool    mIsLoggedIn;      bool    mIsProcessingChannels;      bool    mIsCoroutineActive; -    // This variables can last longer than WebRTC in coroutines so we need them as static +    // These variables can last longer than WebRTC in coroutines so we need them as static      static bool sShuttingDown;      static bool sConnected;      static LLPumpIO* sPump; @@ -797,19 +674,6 @@ private:      LLEventMailDrop mWebRTCPump;  }; -class LLWebRTCSecurity :	public LLSingleton<LLWebRTCSecurity> -{ -    LLSINGLETON(LLWebRTCSecurity); -    virtual ~LLWebRTCSecurity(); - -  public: -    std::string     connectorHandle() { return mConnectorHandle; }; -    std::string     accountHandle()    { return mAccountHandle;    }; - -  private: -    std::string     mConnectorHandle; -    std::string     mAccountHandle; -};  class LLVoiceWebRTCStats : public LLSingleton<LLVoiceWebRTCStats>  { @@ -843,5 +707,119 @@ class LLVoiceWebRTCStats : public LLSingleton<LLVoiceWebRTCStats>      LLSD read();  }; +class LLVoiceWebRTCConnection :  +	public llwebrtc::LLWebRTCSignalingObserver,  +	public llwebrtc::LLWebRTCDataObserver +{ +  public: +    LLVoiceWebRTCConnection(const LLUUID& regionID, S32 parcelLocalID, const std::string& channelID); + +    virtual ~LLVoiceWebRTCConnection(); + +	////////////////////////////// +    /// @name Signaling notification +    //  LLWebRTCSignalingObserver +    //@{ +    void OnIceGatheringState(IceGatheringState state) override; +    void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; +    void OnOfferAvailable(const std::string &sdp) override; +    void OnRenegotiationNeeded() override; +    void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; +    //@} + +    ///////////////////////// +    /// @name Data Notification +    /// LLWebRTCDataObserver +    //@{ +    void OnDataReceived(const std::string &data, bool binary) override; +    void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; +    //@} + +	void processIceUpdates(); +    void onIceUpdateComplete(bool ice_completed, const LLSD &result); +    void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); + +    bool requestVoiceConnection(); +    void OnVoiceConnectionRequestSuccess(const LLSD &body); +    void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + +	bool connectionStateMachine(); + +	void sendData(const std::string &data); + +	void shutDown() +	{  +		LLMutexLock lock(&mVoiceStateMutex); +		mShutDown = true; +	} + +protected: +    typedef enum e_voice_connection_state +    { +        VOICE_STATE_ERROR   = 0x0, +		VOICE_STATE_START_SESSION = 0x1, +		VOICE_STATE_WAIT_FOR_SESSION_START = 0x2, +		VOICE_STATE_REQUEST_CONNECTION = 0x4, +		VOICE_STATE_CONNECTION_WAIT = 0x8, +        VOICE_STATE_SESSION_ESTABLISHED = 0x10, +        VOICE_STATE_SESSION_UP = 0x20, +        VOICE_STATE_SESSION_RETRY = 0x40, +        VOICE_STATE_DISCONNECT = 0x80, +        VOICE_STATE_WAIT_FOR_EXIT = 0x100, +		VOICE_STATE_SESSION_EXIT = 0x200, +		VOICE_STATE_SESSION_STOPPING = 0x3c0, +		VOICE_STATE_SESSION_WAITING = 0x10a +    } EVoiceConnectionState; + +    EVoiceConnectionState mVoiceConnectionState; +    LLMutex mVoiceStateMutex; +    void setVoiceConnectionState(EVoiceConnectionState new_voice_connection_state) +    { +        LLMutexLock lock(&mVoiceStateMutex); + +		if (new_voice_connection_state & VOICE_STATE_SESSION_STOPPING) +		{ +			// the new state is shutdown or restart. +            mVoiceConnectionState = new_voice_connection_state; +            return; +		} +        if (mVoiceConnectionState & VOICE_STATE_SESSION_STOPPING) +		{ +			// we're currently shutting down or restarting, so ignore any +			// state changes. +            return; +		} + +        mVoiceConnectionState = new_voice_connection_state; +    } +    EVoiceConnectionState getVoiceConnectionState() +    { +        LLMutexLock lock(&mVoiceStateMutex); +        return mVoiceConnectionState; +    } + +    bool breakVoiceConnection(bool wait); +    void OnVoiceDisconnectionRequestSuccess(const LLSD &body); +    void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + +    std::string mChannelSDP; +    std::string mRemoteChannelSDP; + +    std::string mChannelID; +    LLUUID mRegionID; +    S32    mParcelLocalID; + +    bool   mShutDown; + +    std::vector<llwebrtc::LLWebRTCIceCandidate> mIceCandidates; +    bool                                        mIceCompleted; +    bool                                        mTrickling; + +    llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; +    llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; +    llwebrtc::LLWebRTCDataInterface  *mWebRTCDataInterface; +}; + +  #endif //LL_WebRTC_VOICE_CLIENT_H | 
