summaryrefslogtreecommitdiff
path: root/indra/newview/llvoicewebrtc.cpp
diff options
context:
space:
mode:
authorRoxie Linden <roxie@lindenlab.com>2023-11-30 13:14:07 -0800
committerRoxie Linden <roxie@lindenlab.com>2024-02-08 18:34:01 -0800
commitd302d89891475d1431099deac99bc52a2c3046a1 (patch)
tree96ed5451a17b2791c9111501a7345d0b10789a31 /indra/newview/llvoicewebrtc.cpp
parentbce9e50cd38b8a22d4f20d950a686668d217592d (diff)
Refactor/clean-up WebRTC voice to handle multiple voice streams
This is useful for cross-region voice, quick voice switching, etc.
Diffstat (limited to 'indra/newview/llvoicewebrtc.cpp')
-rw-r--r--indra/newview/llvoicewebrtc.cpp2574
1 files changed, 857 insertions, 1717 deletions
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 &regionID, 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