From 0ae8bbaf5d85b065354dcc010d08a4a2e759f867 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 6 Sep 2023 15:28:02 -0700 Subject: Checkpoint WebRTC Voice --- indra/newview/llvoicewebrtc.cpp | 7299 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 7299 insertions(+) create mode 100644 indra/newview/llvoicewebrtc.cpp (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp new file mode 100644 index 0000000000..6e68ca7e4f --- /dev/null +++ b/indra/newview/llvoicewebrtc.cpp @@ -0,0 +1,7299 @@ + /** + * @file LLWebRTCVoiceClient.cpp + * @brief Implementation of LLWebRTCVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#include +#include "llvoicewebrtc.h" + +#include "llsdutil.h" + +// Linden library includes +#include "llavatarnamecache.h" +#include "llvoavatarself.h" +#include "llbufferstream.h" +#include "llfile.h" +#include "llmenugl.h" +#ifdef LL_USESYSTEMLIBS +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llcallbacklist.h" +#include "llviewernetwork.h" // for gGridChoice +#include "llbase64.h" +#include "llviewercontrol.h" +#include "llappviewer.h" // for gDisconnected, gDisableVoice +#include "llprocess.h" + +// Viewer includes +#include "llmutelist.h" // to check for muted avatars +#include "llagent.h" +#include "llcachename.h" +#include "llimview.h" // for LLIMMgr +#include "llparcel.h" +#include "llviewerparcelmgr.h" +#include "llfirstuse.h" +#include "llspeakers.h" +#include "lltrans.h" +#include "llrand.h" +#include "llviewerwindow.h" +#include "llviewercamera.h" +#include "llversioninfo.h" + +#include "llviewernetwork.h" +#include "llnotificationsutil.h" + +#include "llcorehttputil.h" +#include "lleventfilter.h" + +#include "stringize.h" + +#include "llwebrtc.h" + +// for base64 decoding +#include "apr_base64.h" + +#define USE_SESSION_GROUPS 0 +#define VX_NULL_POSITION -2147483648.0 /*The Silence*/ + +extern LLMenuBarGL* gMenuBarView; +extern void handle_voice_morphing_subscribe(); + +namespace { + const F32 VOLUME_SCALE_WEBRTC = 0.01f; + + const F32 SPEAKING_TIMEOUT = 1.f; + + static const std::string VOICE_SERVER_TYPE = "WebRTC"; + + // Don't retry connecting to the daemon more frequently than this: + const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; + const int DAEMON_CONNECT_RETRY_MAX = 3; + + // Don't send positional updates more frequently than this: + const F32 UPDATE_THROTTLE_SECONDS = 0.5f; + + // Timeout for connection to WebRTC + const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; + const F32 CONNECT_DNS_TIMEOUT = 5.0f; + const int CONNECT_RETRY_MAX = 3; + + const F32 LOGIN_ATTEMPT_TIMEOUT = 30.0f; + const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; + const int LOGIN_RETRY_MAX = 3; + + const F32 PROVISION_RETRY_TIMEOUT = 2.0; + const int PROVISION_RETRY_MAX = 5; + + // Cosine of a "trivially" small angle + const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); + const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); + + const F32 SESSION_JOIN_TIMEOUT = 30.0f; + + // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() + // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed + // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process + // backs up processing join requests. There is a log statement that records when channel joins take longer than 100 frames. + const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500; + + // How often to check for expired voice fonts in seconds + const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; + // Time of day at which WebRTC expires voice font subscriptions. + // Used to replace the time portion of received expiry timestamps. + static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; + + // Maximum length of capture buffer recordings in seconds. + const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; + + const int ERROR_WebRTC_OBJECT_NOT_FOUND = 1001; + const int ERROR_WebRTC_NOT_LOGGED_IN = 1007; +} + +static int scale_mic_volume(float volume) +{ + // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. + // Map it to WebRTC levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 + return 30 + (int)(volume * 20.0f); +} + +static int scale_speaker_volume(float volume) +{ + // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. + // Map it to WebRTC levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 + return 30 + (int)(volume * 40.0f); + +} + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLWebRTCVoiceClientMuteListObserver : public LLMuteListObserver +{ + /* virtual */ void onChange() { LLWebRTCVoiceClient::getInstance()->muteListChanged();} +}; + + +void LLVoiceWebRTCStats::reset() +{ + mStartTime = -1.0f; + mConnectCycles = 0; + mConnectTime = -1.0f; + mConnectAttempts = 0; + mProvisionTime = -1.0f; + mProvisionAttempts = 0; + mEstablishTime = -1.0f; + mEstablishAttempts = 0; +} + +LLVoiceWebRTCStats::LLVoiceWebRTCStats() +{ + reset(); +} + +LLVoiceWebRTCStats::~LLVoiceWebRTCStats() +{ +} + +void LLVoiceWebRTCStats::connectionAttemptStart() +{ + if (!mConnectAttempts) + { + mStartTime = LLTimer::getTotalTime(); + mConnectCycles++; + } + mConnectAttempts++; +} + +void LLVoiceWebRTCStats::connectionAttemptEnd(bool success) +{ + if ( success ) + { + mConnectTime = (LLTimer::getTotalTime() - mStartTime) / USEC_PER_SEC; + } +} + +void LLVoiceWebRTCStats::provisionAttemptStart() +{ + if (!mProvisionAttempts) + { + mStartTime = LLTimer::getTotalTime(); + } + mProvisionAttempts++; +} + +void LLVoiceWebRTCStats::provisionAttemptEnd(bool success) +{ + if ( success ) + { + mProvisionTime = (LLTimer::getTotalTime() - mStartTime) / USEC_PER_SEC; + } +} + +void LLVoiceWebRTCStats::establishAttemptStart() +{ + if (!mEstablishAttempts) + { + mStartTime = LLTimer::getTotalTime(); + } + mEstablishAttempts++; +} + +void LLVoiceWebRTCStats::establishAttemptEnd(bool success) +{ + if ( success ) + { + mEstablishTime = (LLTimer::getTotalTime() - mStartTime) / USEC_PER_SEC; + } +} + +LLSD LLVoiceWebRTCStats::read() +{ + LLSD stats(LLSD::emptyMap()); + + stats["connect_cycles"] = LLSD::Integer(mConnectCycles); + stats["connect_attempts"] = LLSD::Integer(mConnectAttempts); + stats["connect_time"] = LLSD::Real(mConnectTime); + + stats["provision_attempts"] = LLSD::Integer(mProvisionAttempts); + stats["provision_time"] = LLSD::Real(mProvisionTime); + + stats["establish_attempts"] = LLSD::Integer(mEstablishAttempts); + stats["establish_time"] = LLSD::Real(mEstablishTime); + + return stats; +} + +static LLWebRTCVoiceClientMuteListObserver mutelist_listener; +static bool sMuteListListener_listening = false; + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool LLWebRTCVoiceClient::sShuttingDown = false; +bool LLWebRTCVoiceClient::sConnected = false; +LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; + +LLWebRTCVoiceClient::LLWebRTCVoiceClient() : + mSessionTerminateRequested(false), + mRelogRequested(false), + mTerminateDaemon(false), + mSpatialJoiningNum(0), + + mTuningMode(false), + mTuningEnergy(0.0f), + mTuningMicVolume(0), + mTuningMicVolumeDirty(true), + mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume + mTuningSpeakerVolumeDirty(true), + mDevicesListUpdated(false), + + mAreaVoiceDisabled(false), + mAudioSession(), // TBD - should be NULL + mAudioSessionChanged(false), + mNextAudioSession(), + + mCurrentParcelLocalID(0), + mConnectorEstablished(false), + mAccountLoggedIn(false), + mNumberOfAliases(0), + mCommandCookie(0), + mLoginRetryCount(0), + + mBuddyListMapPopulated(false), + mBlockRulesListReceived(false), + mAutoAcceptRulesListReceived(false), + + mSpatialCoordsDirty(false), + mIsInitialized(false), + + mMuteMic(false), + mMuteMicDirty(false), + mFriendsListDirty(true), + + mEarLocation(0), + mSpeakerVolumeDirty(true), + mSpeakerMuteDirty(true), + mMicVolume(0), + mMicVolumeDirty(true), + + mVoiceEnabled(false), + mWriteInProgress(false), + + mLipSyncEnabled(false), + + mVoiceFontsReceived(false), + mVoiceFontsNew(false), + mVoiceFontListDirty(false), + + mCaptureBufferMode(false), + mCaptureBufferRecording(false), + mCaptureBufferRecorded(false), + mCaptureBufferPlaying(false), + mShutdownComplete(true), + mPlayRequestCount(0), + + mAvatarNameCacheConnection(), + mIsInTuningMode(false), + mIsInChannel(false), + mIsJoiningSession(false), + mIsWaitingForFonts(false), + mIsLoggingIn(false), + mIsLoggedIn(false), + mIsProcessingChannels(false), + mIsCoroutineActive(false), + mWebRTCPump("WebRTCClientPump"), + mWebRTCDeviceInterface(nullptr), + mWebRTCSignalingInterface(nullptr), + mWebRTCAudioInterface(nullptr) +{ + sShuttingDown = false; + sConnected = false; + sPump = nullptr; + + mSpeakerVolume = scale_speaker_volume(0); + + mVoiceVersion.serverVersion = ""; + mVoiceVersion.serverType = VOICE_SERVER_TYPE; + + // gMuteListp isn't set up at this point, so we defer this until later. +// gMuteListp->addObserver(&mutelist_listener); + + +#if LL_DARWIN || LL_LINUX + // HACK: THIS DOES NOT BELONG HERE + // When the WebRTC daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. + // This should cause us to ignore SIGPIPE and handle the error through proper channels. + // This should really be set up elsewhere. Where should it go? + signal(SIGPIPE, SIG_IGN); + + // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. + // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. + signal(SIGCHLD, SIG_IGN); +#endif + + + gIdleCallbacks.addFunction(idle, this); +} + +//--------------------------------------------------- + +LLWebRTCVoiceClient::~LLWebRTCVoiceClient() +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + sShuttingDown = true; +} + +//--------------------------------------------------- + +void LLWebRTCVoiceClient::init(LLPumpIO *pump) +{ + // constructor will set up LLVoiceClient::getInstance() + sPump = pump; + +// LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", +// boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); + llwebrtc::init(); + + mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); + mWebRTCDeviceInterface->setDevicesObserver(this); + + mWebRTCSignalingInterface = llwebrtc::getSignalingInterface(); + mWebRTCSignalingInterface->setSignalingObserver(this); +} + +void LLWebRTCVoiceClient::terminate() +{ + if (sShuttingDown) + { + return; + } + + // needs to be done manually here since we will not get another pass in + // coroutines... that mechanism is long since gone. + if (mIsLoggedIn) + { + logoutOfWebRTC(false); + } + + if(sConnected) + { + breakVoiceConnection(false); + sConnected = false; + } + else + { + mRelogRequested = false; + } + + sShuttingDown = true; + sPump = NULL; +} + +//--------------------------------------------------- + +void LLWebRTCVoiceClient::cleanUp() +{ + LL_DEBUGS("Voice") << LL_ENDL; + + deleteAllSessions(); + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + LL_DEBUGS("Voice") << "exiting" << LL_ENDL; +} + +//--------------------------------------------------- + +const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() +{ + return mVoiceVersion; +} + +//--------------------------------------------------- + +void LLWebRTCVoiceClient::updateSettings() +{ + setVoiceEnabled(voiceEnabled()); + setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + + std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + setCaptureDevice(inputDevice); + std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + setRenderDevice(outputDevice); + F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); + setMicGain(mic_level); + setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); +} + +///////////////////////////// +// utility functions + +bool LLWebRTCVoiceClient::writeString(const std::string &str) +{ + bool result = false; + LL_DEBUGS("LowVoice") << "sending:\n" << str << LL_ENDL; + + if(sConnected) + { + apr_status_t err; + apr_size_t size = (apr_size_t)str.size(); + apr_size_t written = size; + + //MARK: Turn this on to log outgoing XML + // LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; + + // check return code - sockets will fail (broken, etc.) + err = apr_socket_send( + mSocket->getSocket(), + (const char*)str.data(), + &written); + + if(err == 0 && written == size) + { + // Success. + result = true; + } + else if (err == 0 && written != size) { + // Did a short write, log it for now + LL_WARNS("Voice") << ") short write on socket sending data to WebRTC daemon." << "Sent " << written << "bytes instead of " << size <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; + } +} + +void LLWebRTCVoiceClient::idle(void* user_data) +{ +} + +//========================================================================= +// 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_DONE = 0, + VOICE_STATE_TP_WAIT, // 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 + { + // 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; + + mVoiceControlState = 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 (mVoiceControlState) + { + 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 + { + mVoiceControlState = VOICE_STATE_START_SESSION; + } + break; + + case VOICE_STATE_START_SESSION: + if (establishVoiceConnection()) + { + mVoiceControlState = VOICE_STATE_WAIT_FOR_SESSION_START; + } + else + { + mVoiceControlState = VOICE_STATE_SESSION_RETRY; + } + break; + + case VOICE_STATE_WAIT_FOR_SESSION_START: + llcoro::suspendUntilTimeout(1.0); + if (!mChannelSDP.empty()) + { + mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; + } + break; + + case VOICE_STATE_PROVISION_ACCOUNT: + if (!provisionVoiceAccount()) + { + mVoiceControlState = VOICE_STATE_SESSION_RETRY; + } + else + { + mVoiceControlState = VOICE_STATE_SESSION_PROVISION_WAIT; + } + break; + case VOICE_STATE_SESSION_PROVISION_WAIT: + llcoro::suspendUntilTimeout(1.0); + 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(5.f * (F32)retry, 60.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); + } + mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + } + else + { + mVoiceControlState = VOICE_STATE_DONE; + } + break; + + case VOICE_STATE_SESSION_ESTABLISHED: + { + if (mTuningMode) + { + performMicTuning(); + } + + mVoiceControlState = VOICE_STATE_WAIT_FOR_CHANNEL; + } + break; + + case VOICE_STATE_WAIT_FOR_CHANNEL: + waitForChannel(); // todo: split into more states like login/fonts + mVoiceControlState = VOICE_STATE_DISCONNECT; + break; + + case VOICE_STATE_DISCONNECT: + LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; + endAndDisconnectSession(); + retry = 0; // Connected without issues + mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + break; + + case VOICE_STATE_WAIT_FOR_EXIT: + if (mRelogRequested && mVoiceEnabled) + { + LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; + mVoiceControlState = VOICE_STATE_TP_WAIT; + } + else + { + mVoiceControlState = VOICE_STATE_DONE; + } + break; + + case VOICE_STATE_DONE: + break; + } + } while (mVoiceControlState > 0); + + if (sShuttingDown) + { + // LLWebRTCVoiceClient might be already dead + return; + } + + mIsCoroutineActive = false; + LL_INFOS("Voice") << "exiting" << LL_ENDL; +} + +bool LLWebRTCVoiceClient::endAndDisconnectSession() +{ + LL_DEBUGS("Voice") << LL_ENDL; + + breakVoiceConnection(true); + + return 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"; + 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) +{ + mVoiceControlState = VOICE_STATE_SESSION_ESTABLISHED; + 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"]; + } + 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); +} + +void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) +{ + 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 + { + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + } +} + +bool LLWebRTCVoiceClient::establishVoiceConnection() +{ + 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 "<initializeConnection(); +} + +bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) +{ + LL_DEBUGS("Voice") << "( wait=" << corowait << ")" << LL_ENDL; + bool retval(true); + + mShutdownComplete = false; + connectorShutdown(); + + if (corowait) + { + LLSD timeoutResult(LLSDMap("connector", "timeout")); + + LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + + retval = result.has("connector"); + } + else + { + mRelogRequested = false; //stop the control coro + // If we are not doing a corowait then we must sleep until the connector has responded + // otherwise we may very well close the socket too early. +#if LL_WINDOWS + if (!mShutdownComplete) + { + // The situation that brings us here is a call from ::terminate() + // At this point message system is already down so we can't wait for + // the message, yet we need to receive "connector shutdown response". + // Either wait a bit and emulate it or check gMessageSystem for specific message + _sleep(1000); + if (sConnected) + { + sConnected = false; + LLSD WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); + mWebRTCPump.post(WebRTCevent); + } + mShutdownComplete = true; + } +#endif + } + + LL_DEBUGS("Voice") << "closing SLVoice socket" << LL_ENDL; + closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. + cleanUp(); + sConnected = false; + + return retval; +} + +bool LLWebRTCVoiceClient::loginToWebRTC() +{ + + + mRelogRequested = false; + mIsLoggedIn = true; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + + // Set up the mute list observer if it hasn't been set up already. + if ((!sMuteListListener_listening)) + { + LLMuteList::getInstance()->addObserver(&mutelist_listener); + sMuteListListener_listening = true; + } + + // Set the initial state of mic mute, local speaker volume, etc. + sendLocalAudioUpdates(); + mIsLoggingIn = false; + + return true; +} + +void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) +{ + if (mIsLoggedIn) + { + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + + logoutSendMessage(); + + if (wait) + { + LLSD timeoutResult(LLSDMap("logout", "timeout")); + LLSD result; + + do + { + LL_DEBUGS("Voice") + << "waiting for logout response on " + << mWebRTCPump.getName() + << LL_ENDL; + + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + + if (sShuttingDown) + { + break; + } + + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + // Don't get confused by prior queued events -- note that it's + // very important that mWebRTCPump is an LLEventMailDrop, which + // does queue events. + } while (! result["logout"]); + } + else + { + LL_DEBUGS("Voice") << "not waiting for logout" << LL_ENDL; + } + + mIsLoggedIn = false; + } +} + +bool LLWebRTCVoiceClient::requestParcelVoiceInfo() +{ + //_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; + + 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 + { + if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) + { + LL_WARNS("Voice") << "No voice credentials" << LL_ENDL; + } + else + { + LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL; + } + } + + // set the spatial channel. If no voice credentials or uri are + // available, then we simply drop out of voice spatially. + return !setSpatialChannel(uri, credentials); +} + +bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) +{ + 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(); + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + + return true; +} + +bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) +{ + + if (mAudioSession) + { + LL_INFOS("Voice") << "terminateAudioSession(" << wait << ") Terminating current voice session " << mAudioSession->mHandle << LL_ENDL; + + if (mIsLoggedIn) + { + if (!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/WebRTCrecording"); + { + time_t now = time(NULL); + const size_t BUF_SIZE = 64; + char time_str[BUF_SIZE]; /* Flawfinder: ignore */ + + strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + savepath += time_str; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + + 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); + + } + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + } + } + 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_CHECK_EFFECTS, + VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, + VOICE_CHANNEL_STATE_PROCESS_CHANNEL, + VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, + VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK, + VOICE_CHANNEL_STATE_LOGOUT, + VOICE_CHANNEL_STATE_RELOG, + VOICE_CHANNEL_STATE_DONE, +} 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; + } + + processIceUpdates(); + switch (state) + { + case VOICE_CHANNEL_STATE_LOGIN: + if (!loginToWebRTC()) + { + return false; + } + state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; + break; + + 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 (mCaptureBufferMode) + { + recordingAndPlaybackMode(); + } + 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 + { + LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL; + break; + } + else + { + LL_DEBUGS("Voice") + << "runSession returned true to inner loop" + << " RelogRequested=" << mRelogRequested + << " VoiceEnabled=" << mVoiceEnabled + << LL_ENDL; + } + } + + 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; + state = VOICE_CHANNEL_STATE_LOGOUT; + break; + } + + case VOICE_CHANNEL_STATE_LOGOUT: + logoutOfWebRTC(true /*bool wait*/); + if (mRelogRequested) + { + state = VOICE_CHANNEL_STATE_RELOG; + } + else + { + state = VOICE_CHANNEL_STATE_DONE; + } + break; + + case VOICE_CHANNEL_STATE_RELOG: + LL_DEBUGS("Voice") << "Relog Requested, restarting provisioning" << LL_ENDL; + if (!provisionVoiceAccount()) + { + if (sShuttingDown) + { + return false; + } + LL_WARNS("Voice") << "provisioning voice failed; giving up" << LL_ENDL; + giveUp(); + return false; + } + if (mVoiceEnabled && mRelogRequested) + { + state = VOICE_CHANNEL_STATE_LOGIN; + } + else + { + state = VOICE_CHANNEL_STATE_DONE; + } + break; + case VOICE_CHANNEL_STATE_DONE: + LL_DEBUGS("Voice") + << "exiting" + << " RelogRequested=" << mRelogRequested + << " VoiceEnabled=" << mVoiceEnabled + << LL_ENDL; + return !sShuttingDown; + } + } 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) + { + 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; + } + + notifyParticipantObservers(); + notifyVoiceFontObservers(); + + LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); + + mIsInChannel = true; + mMuteMicDirty = true; + + while (!sShuttingDown + && mVoiceEnabled + && !mSessionTerminateRequested + && !mTuningMode) + { + + if (sShuttingDown) + { + return false; + } + + 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(); + + // Do notifications for expiring Voice Fonts. + if (mVoiceFontExpiryTimer.hasExpired()) + { + expireVoiceFonts(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + } + + // 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; + } + } + } + + if (sShuttingDown) + { + return false; + } + + mIsInChannel = false; + LL_DEBUGS("Voice") << "terminating at end of runSession" << LL_ENDL; + terminateAudioSession(true); + + return true; +} + +void LLWebRTCVoiceClient::recordingAndPlaybackMode() +{ + LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL; + + while (true) + { + LLSD command; + do + { + command = llcoro::suspendUntilEventOn(mWebRTCPump); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(command) << LL_ENDL; + } while (!command.has("recplay")); + + if (command["recplay"].asString() == "quit") + { + mCaptureBufferMode = false; + break; + } + else if (command["recplay"].asString() == "record") + { + voiceRecordBuffer(); + } + else if (command["recplay"].asString() == "playback") + { + voicePlaybackBuffer(); + } + } + + LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL; + mCaptureBufferRecording = false; + mCaptureBufferRecorded = false; + mCaptureBufferPlaying = false; + + return; +} + +int LLWebRTCVoiceClient::voiceRecordBuffer() +{ + LLSD timeoutResult(LLSDMap("recplay", "stop")); + + LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; + + LLSD result; + + captureBufferRecordStartSendMessage(); + notifyVoiceFontObservers(); + + do + { + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + } while (!result.has("recplay")); + + mCaptureBufferRecorded = true; + + captureBufferRecordStopSendMessage(); + mCaptureBufferRecording = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + return true; + /*TODO expand return to move directly into play*/ +} + +int LLWebRTCVoiceClient::voicePlaybackBuffer() +{ + LLSD timeoutResult(LLSDMap("recplay", "stop")); + + LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL; + + LLSD result; + + do + { + captureBufferPlayStartSendMessage(mPreviewVoiceFont); + + // Store the voice font being previewed, so that we know to restart if it changes. + mPreviewVoiceFontLast = mPreviewVoiceFont; + + do + { + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + } while (!result.has("recplay")); + + if (result["recplay"] == "playback") + continue; // restart playback... May be a font change. + + break; + } while (true); + + // Stop playing. + captureBufferPlayStopSendMessage(); + mCaptureBufferPlaying = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + return true; +} + + +bool LLWebRTCVoiceClient::performMicTuning() +{ + LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; + + mIsInTuningMode = false; + + //--------------------------------------------------------------------- + return true; +} + +//========================================================================= + +void LLWebRTCVoiceClient::closeSocket(void) +{ + mSocket.reset(); + sConnected = false; + mConnectorEstablished = false; + mAccountLoggedIn = false; +} + +void LLWebRTCVoiceClient::loginSendMessage() +{ + std::ostringstream stream; + + bool autoPostCrashDumps = gSavedSettings.getBOOL("WebRTCAutoPostCrashDumps"); + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << mAccountName << "" + << "" << mAccountPassword << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "VerifyAnswer" + << "false" + << "0" + << "Application" + << "5" + << (autoPostCrashDumps?"true":"") + << "\n\n\n"; + + LL_INFOS("Voice") << "Attempting voice login" << LL_ENDL; + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::logout() +{ + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + + logoutSendMessage(); +} + +void LLWebRTCVoiceClient::logoutSendMessage() +{ + if(mAccountLoggedIn) + { + LL_INFOS("Voice") << "Attempting voice logout" << LL_ENDL; + std::ostringstream stream; + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + mAccountLoggedIn = false; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::sessionGroupCreateSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "Normal" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI + << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" + << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"Session.Create.1\">" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" << session->mSIPURI << ""; + + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~"; + + if(!session->mHash.empty()) + { + stream + << "" << LLURI::escape(session->mHash, allowed_chars) << "" + << "SHA1UserName"; + } + + stream + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << font_index << "" + << "" << mChannelName << "" + << "\n\n\n"; + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) +{ + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::string password; + if(!session->mHash.empty()) + { + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~" + ; + password = LLURI::escape(session->mHash, allowed_chars); + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mSIPURI << "" + << "" << mChannelName << "" + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << font_index << "" + << "" << password << "" + << "SHA1UserName" + << "\n\n\n" + ; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t &session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle + << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" + << LL_ENDL; + + session->mMediaConnectInProgress = true; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.MediaConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "" << font_index << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.TextConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionTerminate() +{ + mSessionTerminateRequested = true; +} + +void LLWebRTCVoiceClient::requestRelog() +{ + mSessionTerminateRequested = true; + mRelogRequested = true; +} + + +void LLWebRTCVoiceClient::leaveAudioSession() +{ + if(mAudioSession) + { + LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; + + if(!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/WebRTCrecording"); + { + time_t now = time(NULL); + const size_t BUF_SIZE = 64; + char time_str[BUF_SIZE]; /* Flawfinder: ignore */ + + strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + savepath += time_str; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + } + sessionTerminate(); +} + +void LLWebRTCVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &session) +{ + std::ostringstream stream; + + sessionGroupTerminateSendMessage(session); + return; + /* + LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); + */ +} + +void LLWebRTCVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session) +{ + std::ostringstream stream; + sessionGroupTerminateSendMessage(session); + return; + /* + LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); + */ + +} + +void LLWebRTCVoiceClient::OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList& render_devices) +{ + clearCaptureDevices(); + for (auto &device : render_devices) + { + LLWebRTCVoiceClient::addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); + } + LLWebRTCVoiceClient::setDevicesListUpdated(true); +} + +void LLWebRTCVoiceClient::clearCaptureDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mCaptureDevices.clear(); +} + +void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) +{ + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + mCaptureDevices.push_back(device); +} + +LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() +{ + return mCaptureDevices; +} + +void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) +{ + bool inTuningMode = mIsInTuningMode; + if (inTuningMode) + { + tuningStop(); + } + mWebRTCDeviceInterface->setCaptureDevice(name); + if (inTuningMode) + { + tuningStart(); + } +} +void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) +{ + mDevicesListUpdated = state; +} + +void LLWebRTCVoiceClient::OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) +{ + clearRenderDevices(); + for (auto &device : render_devices) + { + addRenderDevice(LLVoiceDevice(device.display_name, device.id)); + } + setDevicesListUpdated(true); +} + +void LLWebRTCVoiceClient::clearRenderDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mRenderDevices.clear(); +} + +void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) +{ + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + mRenderDevices.push_back(device); + +} + +LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() +{ + return mRenderDevices; +} + +void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) +{ + mWebRTCDeviceInterface->setRenderDevice(name); +} + +void LLWebRTCVoiceClient::tuningStart() +{ + if (!mIsInTuningMode) + { + mWebRTCDeviceInterface->setTuningMode(true); + mIsInTuningMode = true; + } +} + +void LLWebRTCVoiceClient::tuningStop() +{ + if (mIsInTuningMode) + { + mWebRTCDeviceInterface->setTuningMode(false); + mIsInTuningMode = false; + } +} + +bool LLWebRTCVoiceClient::inTuningMode() +{ + return mIsInTuningMode; +} + +void LLWebRTCVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) +{ + mTuningAudioFile = name; + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "" << (loop?"1":"0") << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::tuningRenderStopSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::tuningCaptureStartSendMessage(int loop) +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "-1" + << "" << loop << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::tuningCaptureStopSendMessage() +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); + + mTuningEnergy = 0.0f; +} + +void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mTuningMicVolume) + { + mTuningMicVolume = scaled_volume; + mTuningMicVolumeDirty = true; + } +} + +void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = scaled_volume; + mTuningSpeakerVolumeDirty = true; + } +} + +float LLWebRTCVoiceClient::tuningGetEnergy(void) +{ + return mWebRTCDeviceInterface->getTuningMicrophoneEnergy(); +} + +bool LLWebRTCVoiceClient::deviceSettingsAvailable() +{ + bool result = true; + + if(mRenderDevices.empty()) + result = false; + + return result; +} +bool LLWebRTCVoiceClient::deviceSettingsUpdated() +{ + bool updated = mDevicesListUpdated; + if (mDevicesListUpdated) + { + // a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel. + mDevicesListUpdated = false; // toggle the setting + } + return updated; +} + +void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } + 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(); +} + +static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) +{ + F32 nat[3], nup[3], nl[3]; // the new at, up, left vectors and the new position and velocity +// F32 nvel[3]; + F64 npos[3]; + + // The original XML command was sent like this: + /* + << "" + << "" << pos[VX] << "" + << "" << pos[VZ] << "" + << "" << pos[VY] << "" + << "" + << "" + << "" << mAvatarVelocity[VX] << "" + << "" << mAvatarVelocity[VZ] << "" + << "" << mAvatarVelocity[VY] << "" + << "" + << "" + << "" << l.mV[VX] << "" + << "" << u.mV[VX] << "" + << "" << a.mV[VX] << "" + << "" + << "" + << "" << l.mV[VZ] << "" + << "" << u.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VY] << "" + << "" << u.mV [VZ] << "" + << "" << a.mV [VY] << "" + << ""; + */ + +#if 1 + // This was the original transform done when building the XML command + nat[0] = left.mV[VX]; + nat[1] = up.mV[VX]; + nat[2] = at.mV[VX]; + + nup[0] = left.mV[VZ]; + nup[1] = up.mV[VY]; + nup[2] = at.mV[VZ]; + + nl[0] = left.mV[VY]; + nl[1] = up.mV[VZ]; + nl[2] = at.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY]; + +// nvel[0] = vel.mV[VX]; +// nvel[1] = vel.mV[VZ]; +// nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + + // This was the original transform done in the SDK + nat[0] = at.mV[2]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * left.mV[2]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = at.mV[0]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[0]; + + npos[2] = pos.mdV[2] * -1.0; + npos[1] = pos.mdV[1]; + npos[0] = pos.mdV[0]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } +#else + // This is the compose of the two transforms (at least, that's what I'm trying for) + nat[0] = at.mV[VX]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * up.mV[VZ]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = left.mV[VX]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY] * -1.0; + + nvel[0] = vel.mV[VX]; + nvel[1] = vel.mV[VZ]; + nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + +#endif +} + +void LLWebRTCVoiceClient::setHidden(bool hidden) +{ + mHidden = hidden; + + if (mHidden && inSpatialChannel()) + { + // get out of the channel entirely + leaveAudioSession(); + } + else + { + sendPositionAndVolumeUpdate(); + } +} + +void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) +{ + std::ostringstream stream; + + if (mSpatialCoordsDirty && inSpatialChannel()) + { + LLVector3 l, u, a, vel; + LLVector3d pos; + + mSpatialCoordsDirty = false; + + // Always send both speaker and listener positions together. + stream << "" + << "" << getAudioSessionHandle() << ""; + + stream << ""; + + LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); + +// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; + l = avatarRot.getLeftRow(); + u = avatarRot.getUpRow(); + a = avatarRot.getFwdRow(); + + pos = mAvatarPosition; + vel = mAvatarVelocity; + + // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. + // The old transform is replicated by this function. + oldSDKTransform(l, u, a, pos, vel); + + if (mHidden) + { + for (int i=0;i<3;++i) + { + pos.mdV[i] = VX_NULL_POSITION; + } + } + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << "" + ; + + stream << ""; + + stream << ""; + + LLVector3d earPosition; + LLVector3 earVelocity; + LLMatrix3 earRot; + + switch(mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earVelocity = mCameraVelocity; + earRot = mCameraRot; + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = avatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = mCameraRot; + break; + } + + l = earRot.getLeftRow(); + u = earRot.getUpRow(); + a = earRot.getFwdRow(); + + pos = earPosition; + vel = earVelocity; + + + oldSDKTransform(l, u, a, pos, vel); + + if (mHidden) + { + for (int i=0;i<3;++i) + { + pos.mdV[i] = VX_NULL_POSITION; + } + } + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << "" + ; + + stream << ""; + + stream << "1"; //do not generate responses for update requests + stream << "\n\n\n"; + } + + 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; + + // SLIM SDK: Send both volume and mute commands. + + // Send a "volume for me" command for the user. + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << volume << "" + << "\n\n\n"; + + if(!mAudioSession->mIsP2P) + { + // Send a "mute for me" command for the user + // Doesn't work in P2P sessions + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << (mute?"1":"0") << "" + << "Audio" + << "\n\n\n"; + } + } + + p->mVolumeDirty = false; + } + } + } + + std::string update(stream.str()); + if(!update.empty()) + { + LL_DEBUGS("VoiceUpdate") << "sending update " << update << LL_ENDL; + writeString(update); + } + +} + +void LLWebRTCVoiceClient::sendLocalAudioUpdates() +{ + // Check all of the dirty states and then send messages to those needing to be changed. + // Tuningmode hands its own mute settings. + std::ostringstream stream; + + if (mMuteMicDirty && !mTuningMode) + { + mMuteMicDirty = false; + + // Send a local mute command. + + LL_INFOS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << (mMuteMic ? "true" : "false") << "" + << "\n\n\n"; + + } + + if (mSpeakerMuteDirty && !mTuningMode) + { + const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false"); + + mSpeakerMuteDirty = false; + + LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << muteval << "" + << "\n\n\n"; + + } + + if (mSpeakerVolumeDirty) + { + mSpeakerVolumeDirty = false; + + LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << mSpeakerVolume << "" + << "\n\n\n"; + + } + + if (mMicVolumeDirty) + { + mMicVolumeDirty = false; + + LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; + + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "" << mMicVolume << "" + << "\n\n\n"; + } + + + if (!stream.str().empty()) + { + writeString(stream.str()); + } +} + +/** + * Because of the recurring voice cutout issues (SL-15072) we are going to try + * to disable the automatic VAD (Voice Activity Detection) and set the associated + * parameters directly. We will expose them via Debug Settings and that should + * let us iterate on a collection of values that work for us. Hopefully! + * + * From the WebRTC Docs: + * + * VadAuto: A flag indicating if the automatic VAD is enabled (1) or disabled (0) + * + * VadHangover: The time (in milliseconds) that it takes + * for the VAD to switch back to silence from speech mode after the last speech + * frame has been detected. + * + * VadNoiseFloor: A dimensionless value between 0 and + * 20000 (default 576) that controls the maximum level at which the noise floor + * may be set at by the VAD's noise tracking. Too low of a value will make noise + * tracking ineffective (A value of 0 disables noise tracking and the VAD then + * relies purely on the sensitivity property). Too high of a value will make + * long speech classifiable as noise. + * + * VadSensitivity: A dimensionless value between 0 and + * 100, indicating the 'sensitivity of the VAD'. Increasing this value corresponds + * to decreasing the sensitivity of the VAD (i.e. '0' is most sensitive, + * while 100 is 'least sensitive') + */ +void LLWebRTCVoiceClient::setupVADParams(unsigned int vad_auto, + unsigned int vad_hangover, + unsigned int vad_noise_floor, + unsigned int vad_sensitivity) +{ + std::ostringstream stream; + + LL_INFOS("Voice") << "Setting the automatic VAD to " + << (vad_auto ? "True" : "False") + << " and discrete values to" + << " VadHangover = " << vad_hangover + << ", VadSensitivity = " << vad_sensitivity + << ", VadNoiseFloor = " << vad_noise_floor + << LL_ENDL; + + // Create a request to set the VAD parameters: + stream << "" + << "" << vad_auto << "" + << "" << vad_hangover << "" + << "" << vad_sensitivity << "" + << "" << vad_noise_floor << "" + << "\n\n\n"; + + if (!stream.str().empty()) + { + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::onVADSettingsChange() +{ + // pick up the VAD variables (one of which was changed) + unsigned int vad_auto = gSavedSettings.getU32("WebRTCVadAuto"); + unsigned int vad_hangover = gSavedSettings.getU32("WebRTCVadHangover"); + unsigned int vad_noise_floor = gSavedSettings.getU32("WebRTCVadNoiseFloor"); + unsigned int vad_sensitivity = gSavedSettings.getU32("WebRTCVadSensitivity"); + + // build a VAD params change request and send it to SLVoice + setupVADParams(vad_auto, vad_hangover, vad_noise_floor, vad_sensitivity); +} + +///////////////////////////// +// WebRTC Signaling Handlers +void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +{ + LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; + + if (state != llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) + { + return; + } + mIceCompleted = true; +} + +void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { + mIceCandidates.push_back(candidate); +} + +void LLWebRTCVoiceClient::processIceUpdates() +{ + 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); + + LLSD body; + if (mIceCandidates.size()) + { + LLSD body; + + 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; + body["candidates"].append(body_candidate); + } + mIceCandidates.clear(); + } + else if (mIceCompleted) + { + LLSD body_candidate; + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + mIceCompleted = false; + } + else + { + return; + } + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, _1)); +} + +void LLWebRTCVoiceClient::onIceUpdateComplete(const LLSD& result) +{ + if (sShuttingDown) + { + return; + } +} + +void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result) +{ + 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) + { + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, _1)); + } + else + { + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::OnOfferAvailable(const std::string &sdp) +{ + LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; + mChannelSDP = sdp; +} + +void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * audio_interface) +{ + LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; + mWebRTCAudioInterface = audio_interface; + audio_interface->setMute(true); +} + +///////////////////////////// +// Response/Event handlers + +void LLWebRTCVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) +{ + LLSD result = LLSD::emptyMap(); + + if(statusCode == 0) + { + // Connector created, move forward. + if (connectorHandle == LLWebRTCSecurity::getInstance()->connectorHandle()) + { + LL_INFOS("Voice") << "Voice connector succeeded, WebRTC SDK version is " << versionID << " connector handle " << connectorHandle << LL_ENDL; + mVoiceVersion.serverVersion = versionID; + mConnectorEstablished = true; + mTerminateDaemon = false; + + result["connector"] = LLSD::Boolean(true); + } + else + { + // This shouldn't happen - we are somehow out of sync with SLVoice + // or possibly there are two things trying to run SLVoice at once + // or someone is trying to hack into it. + LL_WARNS("Voice") << "Connector returned wrong handle " + << "(" << connectorHandle << ")" + << " expected (" << LLWebRTCSecurity::getInstance()->connectorHandle() << ")" + << LL_ENDL; + result["connector"] = LLSD::Boolean(false); + // Give up. + mTerminateDaemon = true; + } + } + else if (statusCode == 10028) // web request timeout prior to login + { + // this is usually fatal, but a long timeout might work + result["connector"] = LLSD::Boolean(false); + result["retry"] = LLSD::Real(CONNECT_ATTEMPT_TIMEOUT); + + LL_WARNS("Voice") << "Voice connection failed" << LL_ENDL; + } + else if (statusCode == 10006) // name resolution failure - a shorter retry may work + { + // some networks have slower DNS, but a short timeout might let it catch up + result["connector"] = LLSD::Boolean(false); + result["retry"] = LLSD::Real(CONNECT_DNS_TIMEOUT); + + LL_WARNS("Voice") << "Voice connection DNS lookup failed" << LL_ENDL; + } + else // unknown failure - give up + { + LL_WARNS("Voice") << "Voice connection failure ("<< statusCode << "): " << statusString << LL_ENDL; + mTerminateDaemon = true; + result["connector"] = LLSD::Boolean(false); + } + + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) +{ + LLSD result = LLSD::emptyMap(); + + LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; + + // Status code of 20200 means "bad password". We may want to special-case that at some point. + + if ( statusCode == HTTP_UNAUTHORIZED ) + { + // Login failure which is probably caused by the delay after a user's password being updated. + LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + result["login"] = LLSD::String("retry"); + } + else if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + result["login"] = LLSD::String("failed"); + } + else + { + // Login succeeded, move forward. + mAccountLoggedIn = true; + mNumberOfAliases = numberOfAliases; + result["login"] = LLSD::String("response_ok"); + } + + mWebRTCPump.post(result); + +} + +void LLWebRTCVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{ + sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId)); + + if(session) + { + session->mCreateInProgress = false; + } + + if(statusCode != 0) + { + LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "failed") + ("reason", LLSD::Integer(statusCode))); + + mWebRTCPump.post(WebRTCevent); + } + else + { + reapSession(session); + } + } + } + else + { + LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "created")); + + mWebRTCPump.post(WebRTCevent); + } +} + +void LLWebRTCVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{ + sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId)); + + if(session) + { + session->mCreateInProgress = false; + } + + if(statusCode != 0) + { + LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "failed")); + + mWebRTCPump.post(WebRTCevent); + } + else + { + reapSession(session); + } + } + } + else + { + LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) + ("session", "added")); + + mWebRTCPump.post(WebRTCevent); + + } +} + +void LLWebRTCVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +{ + sessionStatePtr_t session(findSession(requestId)); + // 1026 is session already has media, somehow mediaconnect was called twice on the same session. + // set the session info to reflect that the user is already connected. + if (statusCode == 1026) + { + session->mVoiceActive = true; + session->mMediaConnectInProgress = false; + session->mMediaStreamState = streamStateConnected; + //session->mTextStreamState = streamStateConnected; + session->mErrorStatusCode = 0; + } + else if (statusCode != 0) + { + LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; + if (session) + { + session->mMediaConnectInProgress = false; + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + } + } + else + { + LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::logoutResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } + LLSD WebRTCevent(LLSDMap("logout", LLSD::Boolean(true))); + + mWebRTCPump.post(WebRTCevent); +} + +void LLWebRTCVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } + + sConnected = false; + mShutdownComplete = true; + + LLSD WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); + + mWebRTCPump.post(WebRTCevent); +} + +void LLWebRTCVoiceClient::sessionAddedEvent( + std::string &uriString, + std::string &alias, + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool isChannel, + bool incoming, + std::string &nameString, + std::string &applicationString) +{ + sessionStatePtr_t session; + + LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; + + session = addSession(uriString, sessionHandle); + if(session) + { + session->mGroupHandle = sessionGroupHandle; + session->mIsChannel = isChannel; + session->mIncoming = incoming; + session->mAlias = alias; + + // Generate a caller UUID -- don't need to do this for channels + if(!session->mIsChannel) + { + if(IDFromName(session->mSIPURI, session->mCallerID)) + { + // Normal URI(base64-encoded UUID) + } + else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) + { + // Wrong URI, but an alias is available. Stash the incoming URI as an alternate + session->mAlternateSIPURI = session->mSIPURI; + + // and generate a proper URI from the ID. + setSessionURI(session, sipURIFromID(session->mCallerID)); + } + else + { + LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; + session->mCallerID.generate(session->mSIPURI); + session->mSynthesizedCallerID = true; + + // Can't look up the name in this case -- we have to extract it from the URI. + std::string namePortion = nameString; + + // Some incoming names may be separated with an underscore instead of a space. Fix this. + LLStringUtil::replaceChar(namePortion, '_', ' '); + + // Act like we just finished resolving the name (this stores it in all the right places) + avatarNameResolved(session->mCallerID, namePortion); + } + + LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; + + if(!session->mSynthesizedCallerID) + { + // If we got here, we don't have a proper name. Initiate a lookup. + lookupName(session->mCallerID); + } + } + } +} + +void LLWebRTCVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) +{ + LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; + +#if USE_SESSION_GROUPS + if(mMainSessionGroupHandle.empty()) + { + // This is the first (i.e. "main") session group. Save its handle. + mMainSessionGroupHandle = sessionGroupHandle; + } + else + { + LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; + } +#endif +} + +void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) + { + sessionStatePtr_t oldSession = mAudioSession; + + mAudioSession = session; + mAudioSessionChanged = true; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + + // This is the session we're joining. + if(mIsJoiningSession) + { + LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) + ("session", "joined")); + + mWebRTCPump.post(WebRTCevent); + + if(!session->mIsChannel) + { + // this is a p2p session. Make sure the other end is added as a participant. + participantStatePtr_t participant(session->addParticipant(session->mSIPURI)); + if(participant) + { + if(participant->mAvatarIDValid) + { + lookupName(participant->mAvatarID); + } + else if(!session->mName.empty()) + { + participant->mDisplayName = session->mName; + avatarNameResolved(participant->mAvatarID, session->mName); + } + + // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? + LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + } + } + } +} + +void LLWebRTCVoiceClient::sessionRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle) +{ + LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; + + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + leftAudioSession(session); + + // This message invalidates the session's handle. Set it to empty. + clearSessionHandle(session); + + // This also means that the session's session group is now empty. + // Terminate the session group so it doesn't leak. + sessionGroupTerminateSendMessage(session); + + // Reset the media state (we now have no info) + session->mMediaStreamState = streamStateUnknown; + //session->mTextStreamState = streamStateUnknown; + + // Conditionally delete the session + reapSession(session); + } + else + { + // Already reaped this session. + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; + } + +} + +void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) +{ + if(session) + { + + if(session->mCreateInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; + } + else if(session->mMediaConnectInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; + } + else if(session == mAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (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; + } + 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; + deleteSession(session); + } + } + else + { +// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; + } +} + +// 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)) + ("session", "removed")); + + mWebRTCPump.post(WebRTCevent); + } +} + +void LLWebRTCVoiceClient::accountLoginStateChangeEvent( + std::string &accountHandle, + int statusCode, + std::string &statusString, + int state) +{ + LLSD levent = LLSD::emptyMap(); + + /* + According to Mike S., status codes for this event are: + login_state_logged_out=0, + login_state_logged_in = 1, + login_state_logging_in = 2, + login_state_logging_out = 3, + login_state_resetting = 4, + login_state_error=100 + */ + + LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; + switch(state) + { + case 1: + levent["login"] = LLSD::String("account_login"); + + mWebRTCPump.post(levent); + break; + case 2: + break; + + case 3: + levent["login"] = LLSD::String("account_loggingOut"); + + mWebRTCPump.post(levent); + break; + + case 4: + break; + + case 100: + LL_WARNS("Voice") << "account state event error" << LL_ENDL; + break; + + case 0: + levent["login"] = LLSD::String("account_logout"); + + mWebRTCPump.post(levent); + break; + + default: + //Used to be a commented out warning + LL_WARNS("Voice") << "unknown account state event: " << state << LL_ENDL; + break; + } +} + +void LLWebRTCVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +{ + LLSD result; + + if (mediaCompletionType == "AuxBufferAudioCapture") + { + mCaptureBufferRecording = false; + result["recplay"] = "end"; + } + else if (mediaCompletionType == "AuxBufferAudioRender") + { + // Ignore all but the last stop event + if (--mPlayRequestCount <= 0) + { + mCaptureBufferPlaying = false; + result["recplay"] = "end"; +// result["recplay"] = "done"; + } + } + else + { + LL_WARNS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + } + + if (!result.isUndefined()) + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::mediaStreamUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + int statusCode, + std::string &statusString, + int state, + bool incoming) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + + LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; + + if(session) + { + // We know about this session + + // Save the state for later use + session->mMediaStreamState = state; + + switch(statusCode) + { + case 0: + case HTTP_OK: + // generic success + // Don't change the saved error code (it may have been set elsewhere) + break; + default: + // save the status code for later + session->mErrorStatusCode = statusCode; + break; + } + + switch(state) + { + case streamStateDisconnecting: + case streamStateIdle: + // Standard "left audio session", WebRTC state 'disconnected' + session->mVoiceActive = false; + session->mMediaConnectInProgress = false; + leftAudioSession(session); + break; + + case streamStateConnected: + session->mVoiceActive = true; + session->mMediaConnectInProgress = false; + joinedAudioSession(session); + case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. + break; + + case streamStateRinging: + if(incoming) + { + // Send the voice chat invite to the GUI layer + // TODO: Question: Should we correlate with the mute list here? + session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); + session->mVoiceInvitePending = true; + if(session->mName.empty()) + { + lookupName(session->mCallerID); + } + else + { + // Act like we just finished resolving the name + avatarNameResolved(session->mCallerID, session->mName); + } + } + break; + + default: + LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; + break; + + } + + } + else + { + // session disconnectintg and disconnected events arriving after we have already left the session. + LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL; + } +} + +void LLWebRTCVoiceClient::participantAddedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString, + std::string &displayNameString, + int participantType) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + participantStatePtr_t participant(session->addParticipant(uriString)); + if(participant) + { + participant->mAccountName = nameString; + + LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + + if(participant->mAvatarIDValid) + { + // Initiate a lookup + lookupName(participant->mAvatarID); + } + else + { + // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. + std::string namePortion = displayNameString; + + if(namePortion.empty()) + { + // Problems with both of the above, fall back to the account name + namePortion = nameString; + } + + // Set the display name (which is a hint to the active speakers window not to do its own lookup) + participant->mDisplayName = namePortion; + avatarNameResolved(participant->mAvatarID, namePortion); + } + } + } +} + +void LLWebRTCVoiceClient::participantRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + participantStatePtr_t participant(session->findParticipant(uriString)); + if(participant) + { + session->removeParticipant(participant); + } + else + { + LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; + } + } + else + { + // a late arriving event on a session we have already left. + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + + +void LLWebRTCVoiceClient::participantUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + bool isModeratorMuted, + bool isSpeaking, + int volume, + F32 energy) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + participantStatePtr_t participant(session->findParticipant(uriString)); + + if(participant) + { + //LL_INFOS("Voice") << "Participant Update for " << participant->mDisplayName << LL_ENDL; + + participant->mIsSpeaking = isSpeaking; + participant->mIsModeratorMuted = isModeratorMuted; + + // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false + if (isSpeaking) + { + participant->mSpeakingTimeout.reset(); + participant->mPower = energy; + } + else + { + participant->mPower = 0.0f; + } + + // Ignore incoming volume level if it has been explicitly set, or there + // is a volume or mute change pending. + if ( !participant->mVolumeSet && !participant->mVolumeDirty) + { + participant->mVolume = (F32)volume * VOLUME_SCALE_WEBRTC; + } + + // *HACK: mantipov: added while working on EXT-3544 + /* + Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE + LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER. + + participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted + Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug. + Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates. + + But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post() + voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager + and event is not fired. + + So, we have to call LLSpeakerMgr::update() here. + */ + LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); + + // ignore session ID of local chat + if (voice_cnl && voice_cnl->getSessionID().notNull()) + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); + if (speaker_manager) + { + speaker_manager->update(true); + + // also initialize voice moderate_mode depend on Agent's participant. See EXT-6937. + // *TODO: remove once a way to request the current voice channel moderation mode is implemented. + if (gAgent.getID() == participant->mAvatarID) + { + speaker_manager->initVoiceModerateMode(); + } + } + } + } + else + { + LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::messageEvent( + std::string &sessionHandle, + std::string &uriString, + std::string &alias, + std::string &messageHeader, + std::string &messageBody, + std::string &applicationString) +{ + LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; +// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; + + LL_INFOS("Voice") << "WebRTC raw message:" << std::endl << messageBody << LL_ENDL; + + if(messageHeader.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos) + { + std::string message; + + { + const std::string startMarker = ", try looking for a instead. + start = messageBody.find(startSpan); + start = messageBody.find(startMarker2, start); + end = messageBody.find(endSpan); + + if(start != std::string::npos) + { + start += startMarker2.size(); + + if(end != std::string::npos) + end -= start; + + message.assign(messageBody, start, end); + } + } + } + +// LL_DEBUGS("Voice") << " raw message = \n" << message << LL_ENDL; + + // strip formatting tags + { + std::string::size_type start; + std::string::size_type end; + + while((start = message.find('<')) != std::string::npos) + { + if((end = message.find('>', start + 1)) != std::string::npos) + { + // Strip out the tag + message.erase(start, (end + 1) - start); + } + else + { + // Avoid an infinite loop + break; + } + } + } + + // Decode ampersand-escaped chars + { + std::string::size_type mark = 0; + + // The text may contain text encoded with <, >, and & + mark = 0; + while((mark = message.find("<", mark)) != std::string::npos) + { + message.replace(mark, 4, "<"); + mark += 1; + } + + mark = 0; + while((mark = message.find(">", mark)) != std::string::npos) + { + message.replace(mark, 4, ">"); + mark += 1; + } + + mark = 0; + while((mark = message.find("&", mark)) != std::string::npos) + { + message.replace(mark, 5, "&"); + mark += 1; + } + } + + // strip leading/trailing whitespace (since we always seem to get a couple newlines) + LLStringUtil::trim(message); + +// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL; + + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + bool is_do_not_disturb = gAgent.isDoNotDisturb(); + bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); + bool is_linden = LLMuteList::isLinden(session->mName); + LLChat chat; + + chat.mMuted = is_muted && !is_linden; + + if(!chat.mMuted) + { + chat.mFromID = session->mCallerID; + chat.mFromName = session->mName; + chat.mSourceType = CHAT_SOURCE_AGENT; + + if(is_do_not_disturb && !is_linden) + { + // TODO: Question: Return do not disturb mode response here? Or maybe when session is started instead? + } + + LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; + LLIMMgr::getInstance()->addMessage(session->mIMSessionID, + session->mCallerID, + session->mName.c_str(), + message.c_str(), + false, + LLStringUtil::null, // default arg + IM_NOTHING_SPECIAL, // default arg + 0, // default arg + LLUUID::null, // default arg + LLVector3::zero); // default arg + } + } + } +} + +void LLWebRTCVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + + if(session) + { + participantStatePtr_t participant(session->findParticipant(uriString)); + if(participant) + { + if (!stricmp(notificationType.c_str(), "Typing")) + { + // Other end started typing + // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). + // It requires some info for the message, which we don't have here. + } + else if (!stricmp(notificationType.c_str(), "NotTyping")) + { + // Other end stopped typing + // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). + // It requires some info for the message, which we don't have here. + } + else + { + LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; + } +} + +void LLWebRTCVoiceClient::voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id) +{ + // We don't generally need to process this. However, one occurence is when we first connect, and so it is the + // earliest opportunity to learn what we're connected to. + if (statusCode) + { + LL_WARNS("Voice") << "VoiceServiceConnectionStateChangedEvent statusCode: " << statusCode << + "statusString: " << statusString << LL_ENDL; + return; + } + if (build_id.empty()) + { + return; + } + mVoiceVersion.mBuildVersion = build_id; +} + +void LLWebRTCVoiceClient::auxAudioPropertiesEvent(F32 energy) +{ + LL_DEBUGS("VoiceEnergy") << "got energy " << energy << LL_ENDL; + mTuningEnergy = energy; +} + +void LLWebRTCVoiceClient::muteListChanged() +{ + // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. + if(mAudioSession) + { + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + { + participantStatePtr_t p(iter->second); + + // Check to see if this participant is on the mute list already + if(p->updateMuteState()) + mAudioSession->mVolumeDirty = true; + } + } +} + +///////////////////////////// +// Managing list of participants +LLWebRTCVoiceClient::participantState::participantState(const std::string &uri) : + mURI(uri), + mPTT(false), + mIsSpeaking(false), + mIsModeratorMuted(false), + mLastSpokeTimestamp(0.f), + mPower(0.f), + mVolume(LLVoiceClient::VOLUME_DEFAULT), + mUserVolume(0), + mOnMuteList(false), + mVolumeSet(false), + mVolumeDirty(false), + mAvatarIDValid(false), + mIsSelf(false) +{ +} + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const std::string &uri) +{ + participantStatePtr_t result; + bool useAlternateURI = false; + + // Note: this is mostly the body of LLWebRTCVoiceClient::sessionState::findParticipant(), but since we need to know if it + // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. + { + 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. + // Use mSIPURI instead, since it will be properly encoded. + iter = mParticipantsByURI.find(mSIPURI); + useAlternateURI = true; + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + } + + if(!result) + { + // participant isn't already in one list or the other. + result.reset(new participantState(useAlternateURI?mSIPURI:uri)); + mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); + mParticipantsChanged = true; + + // Try to do a reverse transform on the URI to get the GUID back. + { + LLUUID id; + if(LLWebRTCVoiceClient::getInstance()->IDFromName(result->mURI, id)) + { + result->mAvatarIDValid = true; + result->mAvatarID = id; + } + else + { + // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. + // This indicates that the ID will not be in the name cache. + result->mAvatarID.generate(uri); + } + } + + if(result->updateMuteState()) + { + mMuteDirty = true; + } + + mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); + + if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) + { + result->mVolumeDirty = true; + mVolumeDirty = true; + } + + LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; + } + + return result; +} + +bool LLWebRTCVoiceClient::participantState::updateMuteState() +{ + bool result = false; + + bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); + if(mOnMuteList != isMuted) + { + mOnMuteList = isMuted; + mVolumeDirty = true; + result = true; + } + return result; +} + +bool LLWebRTCVoiceClient::participantState::isAvatar() +{ + return mAvatarIDValid; +} + +void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant) +{ + if(participant) + { + participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); + participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); + + LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; + + if(iter == mParticipantsByURI.end()) + { + LL_WARNS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; + } + else if(iter2 == mParticipantsByUUID.end()) + { + LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; + } + else if(iter->second != iter2->second) + { + LL_WARNS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; + } + else + { + mParticipantsByURI.erase(iter); + mParticipantsByUUID.erase(iter2); + + mParticipantsChanged = true; + } + } +} + +void LLWebRTCVoiceClient::sessionState::removeAllParticipants() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while(!mParticipantsByURI.empty()) + { + removeParticipant(mParticipantsByURI.begin()->second); + } + + if(!mParticipantsByUUID.empty()) + { + LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; + } +} + +/*static*/ +void LLWebRTCVoiceClient::sessionState::VerifySessions() +{ + std::set::iterator it = mSession.begin(); + while (it != mSession.end()) + { + if ((*it).expired()) + { + LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; + it = mSession.erase(it); + } + else + ++it; + } +} + + +void LLWebRTCVoiceClient::getParticipantList(std::set &participants) +{ + if(mAudioSession) + { + for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); + iter != mAudioSession->mParticipantsByUUID.end(); + iter++) + { + participants.insert(iter->first); + } + } +} + +bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) +{ + if(mAudioSession) + { + return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + } + return false; +} + + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipant(const std::string &uri) +{ + participantStatePtr_t result; + + 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; + } + + return result; +} + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +{ + participantStatePtr_t result; + participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + + if(iter != mParticipantsByUUID.end()) + { + result = iter->second; + } + + return result; +} + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const LLUUID& id) +{ + participantStatePtr_t result; + + if(mAudioSession) + { + result = mAudioSession->findParticipantByID(id); + } + + return result; +} + + + +// Check for parcel boundary crossing +bool LLWebRTCVoiceClient::checkParcelChanged(bool update) +{ + LLViewerRegion *region = gAgent.getRegion(); + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + if(region && parcel) + { + S32 parcelLocalID = parcel->getLocalID(); + std::string regionName = region->getName(); + + // LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; + + // The region name starts out empty and gets filled in later. + // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. + // If either is empty, wait for the next time around. + if(!regionName.empty()) + { + if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) + { + // We have changed parcels. Initiate a parcel channel lookup. + if (update) + { + mCurrentParcelLocalID = parcelLocalID; + mCurrentRegionName = regionName; + } + return true; + } + } + } + return false; +} + +bool LLWebRTCVoiceClient::switchChannel( + std::string uri, + bool spatial, + bool no_reconnect, + bool is_p2p, + std::string hash) +{ + bool needsSwitch = !mIsInChannel; + + if (mIsInChannel) + { + 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) + needsSwitch = true; + } + else + { + // mNextAudioSession is null -- this probably means we're on our way back to spatial. + if(!uri.empty()) + { + // We do want to process a switch in this case. + needsSwitch = true; + } + } + } + else + { + // Otherwise, compare against the URI we're in now. + if(mAudioSession) + { + if(mAudioSession->mSIPURI != uri) + { + needsSwitch = true; + } + } + else + { + if(!uri.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. + LL_WARNS("Voice") << "No current audio session... Forcing switch" << LL_ENDL; + needsSwitch = true; + } + } + } + } + + if(needsSwitch) + { + if(uri.empty()) + { + // Leave any channel we may be in + LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + + sessionStatePtr_t oldSession = mNextAudioSession; + mNextAudioSession.reset(); + + // The old session may now need to be deleted. + reapSession(oldSession); + + // If voice was on, turn it off + if (LLVoiceClient::getInstance()->getUserPTTState()) + { + LLVoiceClient::getInstance()->setUserPTTState(false); + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + } + else + { + LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; + + mNextAudioSession = addSession(uri); + mNextAudioSession->mHash = hash; + 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; +} + +void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) +{ + mNextAudioSession = session; + + 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(); + } +} + +void LLWebRTCVoiceClient::setNonSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + switchChannel(uri, false, false, false, credentials); +} + +bool LLWebRTCVoiceClient::setSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + mSpatialSessionURI = uri; + mSpatialSessionCredentials = credentials; + mAreaVoiceDisabled = mSpatialSessionURI.empty(); + + LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; + + if((mIsInChannel && 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; + return false; + } + else + { + return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + } +} + +void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) +{ + std::string userURI = sipURIFromID(uuid); + + switchChannel(userURI, false, true, true); +} + +#if 0 +// WebRTC text IMs are not in use. +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::startUserIMSession(const LLUUID &uuid) +{ + // Figure out if a session with the user already exists + sessionStatePtr_t session(findSession(uuid)); + if(!session) + { + // No session with user, need to start one. + std::string uri = sipURIFromID(uuid); + session = addSession(uri); + + llassert(session); + if (!session) + return session; + + session->mIsSpatial = false; + session->mReconnect = false; + session->mIsP2P = true; + session->mCallerID = uuid; + } + + if(session->mHandle.empty()) + { + // Session isn't active -- start it up. + sessionCreateSendMessage(session, false, false); + } + else + { + // Session is already active -- start up text. + sessionTextConnectSendMessage(session); + } + + return session; +} +#endif + +void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) +{ +#if 0 + // WebRTC text IMs are not in use. + + // Figure out if a session with the user exists + sessionStatePtr_t session(findSession(uuid)); + if(session) + { + // found the session + if(!session->mHandle.empty()) + { + // sessionTextDisconnectSendMessage(session); // a SLim leftover, not used any more. + } + } + else + { + LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; + } +#endif +} +bool LLWebRTCVoiceClient::isValidChannel(std::string &sessionHandle) +{ + return(findSession(sessionHandle) != NULL); + +} +bool LLWebRTCVoiceClient::answerInvite(std::string &sessionHandle) +{ + // this is only ever used to answer incoming p2p call invites. + + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + session->mIsSpatial = false; + session->mReconnect = false; + session->mIsP2P = true; + + joinSession(session); + return true; + } + + return false; +} + +bool LLWebRTCVoiceClient::isVoiceWorking() const +{ + + //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) + // Condition with joining spatial num was added to take into account possible problems with connection to voice + // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. + return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && mIsProcessingChannels; +// return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); +} + +// Returns true if the indicated participant in the current audio session is really an SL avatar. +// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. +BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) +{ + BOOL result = TRUE; + sessionStatePtr_t session(findSession(id)); + + if(session) + { + // this is a p2p session with the indicated caller, or the session with the specified UUID. + if(session->mSynthesizedCallerID) + result = FALSE; + } + else + { + // 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; +} + +// Returns true if calling back the session URI after the session has closed is possible. +// Currently this will be false only for PSTN P2P calls. +BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) +{ + BOOL result = TRUE; + sessionStatePtr_t session(findSession(session_id)); + + if(session != NULL) + { + result = session->isCallBackPossible(); + } + + return result; +} + +// Returns true if the session can accept text IM's. +// Currently this will be false only for PSTN P2P calls. +BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) +{ + bool result = TRUE; + sessionStatePtr_t session(findSession(session_id)); + + if(session != NULL) + { + result = session->isTextIMPossible(); + } + + return result; +} + + +void LLWebRTCVoiceClient::declineInvite(std::string &sessionHandle) +{ + sessionStatePtr_t session(findSession(sessionHandle)); + if(session) + { + sessionMediaDisconnectSendMessage(session); + } +} + +void LLWebRTCVoiceClient::leaveNonSpatialChannel() +{ + LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; + + // Make sure we don't rejoin the current session. + sessionStatePtr_t oldNextSession(mNextAudioSession); + mNextAudioSession.reset(); + + // Most likely this will still be the current session at this point, but check it anyway. + reapSession(oldNextSession); + + verifySessionState(); + + sessionTerminate(); +} + +std::string LLWebRTCVoiceClient::getCurrentChannel() +{ + std::string result; + + if (mIsInChannel && !mSessionTerminateRequested) + { + result = getAudioSessionURI(); + } + + return result; +} + +bool LLWebRTCVoiceClient::inProximalChannel() +{ + bool result = false; + + if (mIsInChannel && !mSessionTerminateRequested) + { + result = inSpatialChannel(); + } + + return result; +} + +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID &id) +{ + std::string result; + result = "sip:"; + result += nameFromID(id); + result += "@"; + + return result; +} + +std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) +{ + std::string result; + if(avatar) + { + result = nameFromID(avatar->getID()); + } + return result; +} + +std::string LLWebRTCVoiceClient::nameFromID(const LLUUID &uuid) +{ + std::string result; + + if (uuid.isNull()) { + //WebRTC, the uuid emtpy look for the mURIString and return that instead. + //result.assign(uuid.mURIStringName); + LLStringUtil::replaceChar(result, '_', ' '); + return result; + } + // Prepending this apparently prevents conflicts with reserved names inside the WebRTC code. + result = "x"; + + // Base64 encode and replace the pieces of base64 that are less compatible + // with e-mail local-parts. + // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" + result += LLBase64::encode(uuid.mData, UUID_BYTES); + LLStringUtil::replaceChar(result, '+', '-'); + LLStringUtil::replaceChar(result, '/', '_'); + + // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: + // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') + + // The reverse transform can be done with: + // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p + + return result; +} + +bool LLWebRTCVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) +{ + bool result = false; + + // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.WebRTC.com" + // If it is, convert to a bare name before doing the transform. + std::string name; + + // Doesn't look like a SIP URI, assume it's an actual name. + if(name.empty()) + name = inName; + + // This will only work if the name is of the proper form. + // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: + // "xFnPP04IpREWNkuw1cOXlhw==" + + if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) + { + // The name appears to have the right form. + + // Reverse the transforms done by nameFromID + std::string temp = name; + LLStringUtil::replaceChar(temp, '-', '+'); + LLStringUtil::replaceChar(temp, '_', '/'); + + U8 rawuuid[UUID_BYTES + 1]; + int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); + if(len == UUID_BYTES) + { + // The decode succeeded. Stuff the bits into the result's UUID + memcpy(uuid.mData, rawuuid, UUID_BYTES); + result = true; + } + } + + if(!result) + { + // WebRTC: not a standard account name, just copy the URI name mURIString field + // and hope for the best. bpj + uuid.setNull(); // WebRTC, set the uuid field to nulls + } + + return result; +} + +std::string LLWebRTCVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) +{ + return avatar->getFullname(); +} + +bool LLWebRTCVoiceClient::inSpatialChannel(void) +{ + bool result = false; + + if(mAudioSession) + { + result = mAudioSession->mIsSpatial; + } + + return result; +} + +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; + + return result; +} + + +///////////////////////////// +// Sending updates of current state + +void LLWebRTCVoiceClient::enforceTether(void) +{ + LLVector3d tethered = mCameraRequestedPosition; + + // constrain 'tethered' to within 50m of mAvatarPosition. + { + F32 max_dist = 50.0f; + LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; + F32 camera_distance = (F32)camera_offset.magVec(); + if(camera_distance > max_dist) + { + tethered = mAvatarPosition + + (max_dist / camera_distance) * camera_offset; + } + } + + if(dist_vec_squared(mCameraPosition, tethered) > 0.01) + { + mCameraPosition = tethered; + mSpatialCoordsDirty = true; + } +} + +void LLWebRTCVoiceClient::updatePosition(void) +{ + + LLViewerRegion *region = gAgent.getRegion(); + if(region && isAgentAvatarValid()) + { + LLMatrix3 rot; + LLVector3d pos; + LLQuaternion qrot; + + // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... + // They're currently always set to zero. + + // Send the current camera position to the voice code + rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); + pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + + LLWebRTCVoiceClient::getInstance()->setCameraPosition( + pos, // position + LLVector3::zero, // velocity + rot); // rotation matrix + + // Send the current avatar position to the voice code + qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); + pos = gAgentAvatarp->getPositionGlobal(); + + // TODO: Can we get the head offset from outside the LLVOAvatar? + // pos += LLVector3d(mHeadOffset); + pos += LLVector3d(0.f, 0.f, 1.f); + + LLWebRTCVoiceClient::getInstance()->setAvatarPosition( + pos, // position + LLVector3::zero, // velocity + qrot); // rotation matrix + } +} + +void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ + mCameraRequestedPosition = position; + + if(mCameraVelocity != velocity) + { + mCameraVelocity = velocity; + mSpatialCoordsDirty = true; + } + + if(mCameraRot != rot) + { + mCameraRot = rot; + mSpatialCoordsDirty = true; + } +} + +void LLWebRTCVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) +{ + if(dist_vec_squared(mAvatarPosition, position) > 0.01) + { + mAvatarPosition = position; + mSpatialCoordsDirty = true; + } + + if(mAvatarVelocity != velocity) + { + mAvatarVelocity = velocity; + mSpatialCoordsDirty = true; + } + + // If the two rotations are not exactly equal test their dot product + // to get the cos of the angle between them. + // If it is too small, don't update. + F32 rot_cos_diff = llabs(dot(mAvatarRot, rot)); + if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS)) + { + mAvatarRot = rot; + mSpatialCoordsDirty = true; + } +} + +bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) +{ + bool result = false; + + if(region) + { + name = region->getName(); + } + + if(!name.empty()) + result = true; + + return result; +} + +void LLWebRTCVoiceClient::leaveChannel(void) +{ + if (mIsInChannel) + { + LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; + mChannelName.clear(); + sessionTerminate(); + } +} + +void LLWebRTCVoiceClient::setMuteMic(bool muted) +{ + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(muted); + } +} + +void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) +{ + LL_DEBUGS("Voice") + << "( " << (enabled ? "enabled" : "disabled") << " )" + << " was "<< (mVoiceEnabled ? "enabled" : "disabled") + << " coro "<< (mIsCoroutineActive ? "active" : "inactive") + << LL_ENDL; + + if (enabled != mVoiceEnabled) + { + // TODO: Refactor this so we don't call into LLVoiceChannel, but simply + // use the status observer + mVoiceEnabled = enabled; + LLVoiceClientStatusObserver::EStatusType status; + + if (enabled) + { + LL_DEBUGS("Voice") << "enabling" << LL_ENDL; + LLVoiceChannel::getCurrentVoiceChannel()->activate(); + status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; + + if (!mIsCoroutineActive) + { + LLCoros::instance().launch("LLWebRTCVoiceClient::voiceControlCoro", + boost::bind(&LLWebRTCVoiceClient::voiceControlCoro, LLWebRTCVoiceClient::getInstance())); + } + else + { + LL_DEBUGS("Voice") << "coro should be active.. not launching" << LL_ENDL; + } + } + else + { + // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. + LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); + gAgent.setVoiceConnected(false); + status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; + } + + notifyStatusObservers(status); + } + else + { + LL_DEBUGS("Voice") << " no-op" << LL_ENDL; + } +} + +bool LLWebRTCVoiceClient::voiceEnabled() +{ + return gSavedSettings.getBOOL("EnableVoiceChat") && + !gSavedSettings.getBOOL("CmdLineDisableVoice") && + !gNonInteractive; +} + +void LLWebRTCVoiceClient::setLipSyncEnabled(BOOL enabled) +{ + mLipSyncEnabled = enabled; +} + +BOOL LLWebRTCVoiceClient::lipSyncEnabled() +{ + + if ( mVoiceEnabled ) + { + return mLipSyncEnabled; + } + else + { + return FALSE; + } +} + + +void LLWebRTCVoiceClient::setEarLocation(S32 loc) +{ + if(mEarLocation != loc) + { + LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; + + mEarLocation = loc; + mSpatialCoordsDirty = true; + } +} + +void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mSpeakerVolume) + { + int min_volume = scale_speaker_volume(0); + if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) + { + mSpeakerMuteDirty = true; + } + + mSpeakerVolume = scaled_volume; + mSpeakerVolumeDirty = true; + } +} + +void LLWebRTCVoiceClient::setMicGain(F32 volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mMicVolume) + { + mMicVolume = scaled_volume; + mMicVolumeDirty = true; + } +} + +///////////////////////////// +// Accessors for data related to nearby speakers +BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) +{ + BOOL result = FALSE; + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + // I'm not sure what the semantics of this should be. + // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. + result = TRUE; + } + + return result; +} + +std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) +{ + std::string result; + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mDisplayName; + } + + return result; +} + + + +BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) + { + participant->mIsSpeaking = FALSE; + } + result = participant->mIsSpeaking; + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mIsModeratorMuted; + } + + return result; +} + +F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID& id) +{ + F32 result = 0; + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mPower; + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + // I'm not sure what the semantics of this should be. + // Does "using PTT" mean they're configured with a push-to-talk button? + // For now, we know there's no PTT mechanism in place, so nobody is using it. + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) +{ + BOOL result = FALSE; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mOnMuteList; + } + + return result; +} + +// External accessors. +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)); + if(participant) + { + result = participant->mVolume; + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. + // LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL; + } + + return result; +} + +void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) +{ + if(mAudioSession) + { + participantStatePtr_t participant(findParticipantByID(id)); + if (participant && !participant->mIsSelf) + { + if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) + { + // Store this volume setting for future sessions if it has been + // changed from the default + LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); + } + else + { + // Remove stored volume setting if it is returned to the default + LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); + } + + participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); + participant->mVolumeDirty = true; + mAudioSession->mVolumeDirty = true; + } + } +} + +std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) +{ + std::string result; + + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) + { + result = participant->mGroupID; + } + + return result; +} + +BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() +{ + return mAreaVoiceDisabled; +} + +void LLWebRTCVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; + + if(!mMainSessionGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << deltaFramesPerControlFrame << "" + << "" << "" << "" + << "false" + << "" << seconds << "" + << "\n\n\n"; + + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::recordingLoopSave(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Flush" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::recordingStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::filePlaybackStart(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::filePlaybackStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::filePlaybackSetPaused(bool paused) +{ + // TODO: Implement once WebRTC gives me a sample +} + +void LLWebRTCVoiceClient::filePlaybackSetMode(bool vox, float speed) +{ + // TODO: Implement once WebRTC gives me a sample +} + +//------------------------------------------------------------------------ +std::set LLWebRTCVoiceClient::sessionState::mSession; + + +LLWebRTCVoiceClient::sessionState::sessionState() : + mErrorStatusCode(0), + mMediaStreamState(streamStateUnknown), + mCreateInProgress(false), + mMediaConnectInProgress(false), + mVoiceInvitePending(false), + mTextInvitePending(false), + mSynthesizedCallerID(false), + mIsChannel(false), + mIsSpatial(false), + mIsP2P(false), + mIncoming(false), + mVoiceActive(false), + mReconnect(false), + mVolumeDirty(false), + mMuteDirty(false), + mParticipantsChanged(false) +{ +} + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession() +{ + sessionState::ptr_t ptr(new sessionState()); + + std::pair::iterator, bool> result = mSession.insert(ptr); + + if (result.second) + ptr->mMyIterator = result.first; + + return ptr; +} + +LLWebRTCVoiceClient::sessionState::~sessionState() +{ + LL_INFOS("Voice") << "Destroying session handle=" << mHandle << " SIP=" << mSIPURI << LL_ENDL; + if (mMyIterator != mSession.end()) + mSession.erase(mMyIterator); + + removeAllParticipants(); +} + +bool LLWebRTCVoiceClient::sessionState::isCallBackPossible() +{ + // This may change to be explicitly specified by WebRTC in the future... + // Currently, only PSTN P2P calls cannot be returned. + // Conveniently, this is also the only case where we synthesize a caller UUID. + return !mSynthesizedCallerID; +} + +bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() +{ + // This may change to be explicitly specified by WebRTC in the future... + return !mSynthesizedCallerID; +} + + +/*static*/ +LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::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::matchCreatingSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCreatingURI, _1, uri)); + + 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::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) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +void LLWebRTCVoiceClient::sessionState::for_each(sessionFunc_t func) +{ + std::for_each(mSession.begin(), mSession.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) +{ + ptr_t aLock(a.lock()); + + return aLock ? aLock->mHandle == handle : false; +} + +bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); + + return aLock ? (aLock->mCreateInProgress && (aLock->mSIPURI == uri)) : false; +} + +bool LLWebRTCVoiceClient::sessionState::testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mSIPURI == uri) || (aLock->mAlternateSIPURI == uri)) : false; +} + + +bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId) +{ + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mCallerID == participantId) || (aLock->mIMSessionID == participantId)) : false; +} + +/*static*/ +void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceClient::sessionState::wptr_t &a, sessionFunc_t func) +{ + ptr_t aLock(a.lock()); + + if (aLock) + func(aLock); + else + { + LL_WARNS("Voice") << "Stale handle in session map!" << LL_ENDL; + } +} + + + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const std::string &handle) +{ + sessionStatePtr_t result; + sessionMap::iterator iter = mSessionsByHandle.find(handle); + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + + return result; +} + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) +{ + sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); + + return result; +} + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const LLUUID &participant_id) +{ + sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); + + return result; +} + +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &uri, const std::string &handle) +{ + 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; + } + } + + 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; + + 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) + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; + setSessionURI(result, uri); + } + + 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); + } + } + + LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + } + + verifySessionState(); + + 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(); + + // If this is the current audio session, clean up the pointer which will soon be dangling. + if(mAudioSession == session) + { + mAudioSession.reset(); + mAudioSessionChanged = true; + } + + // ditto for the next audio session + if(mNextAudioSession == session) + { + mNextAudioSession.reset(); + } + +} + +void LLWebRTCVoiceClient::deleteAllSessions() +{ + LL_DEBUGS("Voice") << LL_ENDL; + + while (!mSessionsByHandle.empty()) + { + const sessionStatePtr_t session = mSessionsByHandle.begin()->second; + deleteSession(session); + } + +} + +void LLWebRTCVoiceClient::verifySessionState(void) +{ + LL_DEBUGS("Voice") << "Sessions in handle map=" << mSessionsByHandle.size() << LL_ENDL; + sessionState::VerifySessions(); +} + +void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyParticipantObservers() +{ + for (observer_set_t::iterator it = mParticipantObservers.begin(); + it != mParticipantObservers.end(); + ) + { + LLVoiceClientParticipantObserver* observer = *it; + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. + it = mParticipantObservers.upper_bound(observer); + } +} + +void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) +{ + LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" + << " mAudioSession=" << mAudioSession + << LL_ENDL; + + if(mAudioSession) + { + if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) + { + switch(mAudioSession->mErrorStatusCode) + { + case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; + case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; + case 20715: + //invalid channel, we may be using a set of poorly cached + //info + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + case 1009: + //invalid username and password + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + } + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + } + else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + switch(mAudioSession->mErrorStatusCode) + { + case HTTP_NOT_FOUND: // NOT_FOUND + // *TODO: Should this be 503? + case 480: // TEMPORARILY_UNAVAILABLE + case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT + // call failed because other user was not available + // treat this as an error case + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + break; + } + } + } + + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session URI " << getAudioSessionURI() + << ", proximal is " << inSpatialChannel() + << LL_ENDL; + + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); + } + + // skipped to avoid speak button blinking + if ( status != LLVoiceClientStatusObserver::STATUS_JOINING + && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL + && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) + { + bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + + gAgent.setVoiceConnected(voice_status); + + if (voice_status) + { + LLFirstUse::speak(true); + } + } +} + +void LLWebRTCVoiceClient::addObserver(LLFriendObserver* observer) +{ + mFriendObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +{ + mFriendObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyFriendObservers() +{ + for (friend_observer_set_t::iterator it = mFriendObservers.begin(); + it != mFriendObservers.end(); + ) + { + LLFriendObserver* observer = *it; + it++; + // The only friend-related thing we notify on is online/offline transitions. + observer->changed(LLFriendObserver::ONLINE); + } +} + +void LLWebRTCVoiceClient::lookupName(const LLUUID &id) +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); +} + +void LLWebRTCVoiceClient::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + std::string display_name = av_name.getDisplayName(); + avatarNameResolved(agent_id, display_name); +} + +void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) +{ + participantStatePtr_t participant(session->findParticipantByID(id)); + if (participant) + { + // Found -- fill in the name + participant->mAccountName = name; + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; + } + + // Check whether this is a p2p session whose caller name just resolved + if (session->mCallerID == id) + { + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; + if (session->mTextInvitePending) + { + session->mTextInvitePending = false; + + // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. + } + if (session->mVoiceInvitePending) + { + session->mVoiceInvitePending = false; + + LLIMMgr::getInstance()->inviteToSession( + session->mIMSessionID, + session->mName, + session->mCallerID, + session->mName, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + session->mHandle, + session->mSIPURI); + } + + } +} + +void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +{ + sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); +} + +bool LLWebRTCVoiceClient::setVoiceEffect(const LLUUID& id) +{ + if (!mAudioSession) + { + return false; + } + + if (!id.isNull()) + { + if (mVoiceFontMap.empty()) + { + LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; + return false; + } + else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) + { + LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; + return false; + } + } + + // *TODO: Check for expired fonts? + mAudioSession->mVoiceFontID = id; + + // *TODO: Separate voice font defaults for spatial chat and IM? + gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); + + sessionSetVoiceFontSendMessage(mAudioSession); + notifyVoiceFontObservers(); + + return true; +} + +const LLUUID LLWebRTCVoiceClient::getVoiceEffect() +{ + return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; +} + +LLSD LLWebRTCVoiceClient::getVoiceEffectProperties(const LLUUID& id) +{ + LLSD sd; + + voice_font_map_t::iterator iter = mVoiceFontMap.find(id); + if (iter != mVoiceFontMap.end()) + { + sd["template_only"] = false; + } + else + { + // Voice effect is not in the voice font map, see if there is a template + iter = mVoiceFontTemplateMap.find(id); + if (iter == mVoiceFontTemplateMap.end()) + { + LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; + return sd; + } + sd["template_only"] = true; + } + + voiceFontEntry *font = iter->second; + sd["name"] = font->mName; + sd["expiry_date"] = font->mExpirationDate; + sd["is_new"] = font->mIsNew; + + return sd; +} + +LLWebRTCVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : + mID(id), + mFontIndex(0), + mFontType(VOICE_FONT_TYPE_NONE), + mFontStatus(VOICE_FONT_STATUS_NONE), + mIsNew(false) +{ + mExpiryTimer.stop(); + mExpiryWarningTimer.stop(); +} + +LLWebRTCVoiceClient::voiceFontEntry::~voiceFontEntry() +{ +} + +void LLWebRTCVoiceClient::refreshVoiceEffectLists(bool clear_lists) +{ + if (clear_lists) + { + mVoiceFontsReceived = false; + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + } + + accountGetSessionFontsSendMessage(); + accountGetTemplateFontsSendMessage(); +} + +const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectList() const +{ + return mVoiceFontList; +} + +const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectTemplateList() const +{ + return mVoiceFontTemplateList; +} + +void LLWebRTCVoiceClient::addVoiceFont(const S32 font_index, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font) +{ + // WebRTC SessionFontIDs are not guaranteed to remain the same between + // sessions or grids so use a UUID for the name. + + // If received name is not a UUID, fudge one by hashing the name and type. + LLUUID font_id; + if (LLUUID::validate(name)) + { + font_id = LLUUID(name); + } + else + { + font_id.generate(STRINGIZE(font_type << ":" << name)); + } + + voiceFontEntry *font = NULL; + + voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; + voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; + + // Check whether we've seen this font before. + voice_font_map_t::iterator iter = font_map.find(font_id); + bool new_font = (iter == font_map.end()); + + // Override the has_expired flag if we have passed the expiration_date as a double check. + if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) + { + has_expired = true; + } + + if (has_expired) + { + LL_DEBUGS("VoiceFont") << "Expired " << (template_font ? "Template " : "") + << expiration_date.asString() << " " << font_id + << " (" << font_index << ") " << name << LL_ENDL; + + // Remove existing session fonts that have expired since we last saw them. + if (!new_font && !template_font) + { + deleteVoiceFont(font_id); + } + return; + } + + if (new_font) + { + // If it is a new font create a new entry. + font = new voiceFontEntry(font_id); + } + else + { + // Not a new font, update the existing entry + font = iter->second; + } + + if (font) + { + font->mFontIndex = font_index; + // Use the description for the human readable name if available, as the + // "name" may be a UUID. + font->mName = description.empty() ? name : description; + font->mFontType = font_type; + font->mFontStatus = font_status; + + // If the font is new or the expiration date has changed the expiry timers need updating. + if (!template_font && (new_font || font->mExpirationDate != expiration_date)) + { + font->mExpirationDate = expiration_date; + + // Set the expiry timer to trigger a notification when the voice font can no longer be used. + font->mExpiryTimer.start(); + font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); + + // Set the warning timer to some interval before actual expiry. + S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); + if (warning_time != 0) + { + font->mExpiryWarningTimer.start(); + F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); + font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); + } + else + { + // Disable the warning timer. + font->mExpiryWarningTimer.stop(); + } + + // Only flag new session fonts after the first time we have fetched the list. + if (mVoiceFontsReceived) + { + font->mIsNew = true; + mVoiceFontsNew = true; + } + } + + LL_DEBUGS("VoiceFont") << (template_font ? "Template " : "") + << font->mExpirationDate.asString() << " " << font->mID + << " (" << font->mFontIndex << ") " << name << LL_ENDL; + + if (new_font) + { + font_map.insert(voice_font_map_t::value_type(font->mID, font)); + font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); + } + + mVoiceFontListDirty = true; + + // Debugging stuff + + if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) + { + LL_WARNS("VoiceFont") << "Unknown voice font type: " << font_type << LL_ENDL; + } + if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) + { + LL_WARNS("VoiceFont") << "Unknown voice font status: " << font_status << LL_ENDL; + } + } +} + +void LLWebRTCVoiceClient::expireVoiceFonts() +{ + // *TODO: If we are selling voice fonts in packs, there are probably + // going to be a number of fonts with the same expiration time, so would + // be more efficient to just keep a list of expiration times rather + // than checking each font individually. + + bool have_expired = false; + bool will_expire = false; + bool expired_in_use = false; + + LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + voiceFontEntry* voice_font = iter->second; + LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; + LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; + + // Check for expired voice fonts + if (expiry_timer.getStarted() && expiry_timer.hasExpired()) + { + // Check whether it is the active voice font + if (voice_font->mID == current_effect) + { + // Reset to no voice effect. + setVoiceEffect(LLUUID::null); + expired_in_use = true; + } + + LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; + deleteVoiceFont(voice_font->mID); + have_expired = true; + } + + // Check for voice fonts that will expire in less that the warning time + if (warning_timer.getStarted() && warning_timer.hasExpired()) + { + LL_DEBUGS("VoiceFont") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; + will_expire = true; + warning_timer.stop(); + } + } + + LLSD args; + args["URL"] = LLTrans::getString("voice_morphing_url"); + args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); + + // Give a notification if any voice fonts have expired. + if (have_expired) + { + if (expired_in_use) + { + LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); + } + else + { + LLNotificationsUtil::add("VoiceEffectsExpired", args); + } + + // Refresh voice font lists in the UI. + notifyVoiceFontObservers(); + } + + // Give a warning notification if any voice fonts are due to expire. + if (will_expire) + { + S32Seconds seconds(gSavedSettings.getS32("VoiceEffectExpiryWarningTime")); + args["INTERVAL"] = llformat("%d", LLUnit(seconds).value()); + + LLNotificationsUtil::add("VoiceEffectsWillExpire", args); + } +} + +void LLWebRTCVoiceClient::deleteVoiceFont(const LLUUID& id) +{ + // Remove the entry from the voice font list. + voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); + while (list_iter != mVoiceFontList.end()) + { + if (list_iter->second == id) + { + LL_DEBUGS("VoiceFont") << "Removing " << id << " from the voice font list." << LL_ENDL; + list_iter = mVoiceFontList.erase(list_iter); + mVoiceFontListDirty = true; + } + else + { + ++list_iter; + } + } + + // Find the entry in the voice font map and erase its data. + voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); + if (map_iter != mVoiceFontMap.end()) + { + delete map_iter->second; + } + + // Remove the entry from the voice font map. + mVoiceFontMap.erase(map_iter); +} + +void LLWebRTCVoiceClient::deleteAllVoiceFonts() +{ + mVoiceFontList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontMap.clear(); +} + +void LLWebRTCVoiceClient::deleteVoiceFontTemplates() +{ + mVoiceFontTemplateList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontTemplateMap.clear(); +} + +S32 LLWebRTCVoiceClient::getVoiceFontIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); + if (it != mVoiceFontMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_WARNS("VoiceFont") << "Selected voice font " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +S32 LLWebRTCVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); + if (it != mVoiceFontTemplateMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_WARNS("VoiceFont") << "Selected voice font template " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +void LLWebRTCVoiceClient::accountGetSessionFontsSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("VoiceFont") << "Requesting voice font list." << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::accountGetTemplateFontsSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("VoiceFont") << "Requesting voice font template list." << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("VoiceFont") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "" + << "" << session->mHandle << "" + << "" << font_index << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLWebRTCVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + if (mIsWaitingForFonts) + { + // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after + // receiving the last one. + LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true))); + + mWebRTCPump.post(result); + } + notifyVoiceFontObservers(); + mVoiceFontsReceived = true; +} + +void LLWebRTCVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + notifyVoiceFontObservers(); +} +void LLWebRTCVoiceClient::addObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.erase(observer); +} + +// method checks the item in VoiceMorphing menu for appropriate current voice font +bool LLWebRTCVoiceClient::onCheckVoiceEffect(const std::string& voice_effect_name) +{ + LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); + if (NULL != effect_interfacep) + { + const LLUUID& currect_voice_effect_id = effect_interfacep->getVoiceEffect(); + + if (currect_voice_effect_id.isNull()) + { + if (voice_effect_name == "NoVoiceMorphing") + { + return true; + } + } + else + { + const LLSD& voice_effect_props = effect_interfacep->getVoiceEffectProperties(currect_voice_effect_id); + if (voice_effect_props["name"].asString() == voice_effect_name) + { + return true; + } + } + } + + return false; +} + +// method changes voice font for selected VoiceMorphing menu item +void LLWebRTCVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name) +{ + LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); + if (NULL != effect_interfacep) + { + if (voice_effect_name == "NoVoiceMorphing") + { + effect_interfacep->setVoiceEffect(LLUUID()); + return; + } + const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); + if (!effect_list.empty()) + { + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + if (voice_effect_name == it->first) + { + effect_interfacep->setVoiceEffect(it->second); + return; + } + } + } + } +} + +// it updates VoiceMorphing menu items in accordance with purchased properties +void LLWebRTCVoiceClient::updateVoiceMorphingMenu() +{ + if (mVoiceFontListDirty) + { + LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interfacep) + { + const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); + if (!effect_list.empty()) + { + LLMenuGL * voice_morphing_menup = gMenuBarView->findChildMenuByName("VoiceMorphing", TRUE); + + if (NULL != voice_morphing_menup) + { + S32 items = voice_morphing_menup->getItemCount(); + if (items > 0) + { + voice_morphing_menup->erase(1, items - 3, false); + + S32 pos = 1; + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + LLMenuItemCheckGL::Params p; + p.name = it->first; + p.label = it->first; + p.on_check.function(boost::bind(&LLWebRTCVoiceClient::onCheckVoiceEffect, this, it->first)); + p.on_click.function(boost::bind(&LLWebRTCVoiceClient::onClickVoiceEffect, this, it->first)); + LLMenuItemCheckGL * voice_effect_itemp = LLUICtrlFactory::create(p); + voice_morphing_menup->insert(pos++, voice_effect_itemp, false); + } + + voice_morphing_menup->needsArrange(); + } + } + } + } + } +} +void LLWebRTCVoiceClient::notifyVoiceFontObservers() +{ + LL_DEBUGS("VoiceFont") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; + + updateVoiceMorphingMenu(); + + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end();) + { + LLVoiceEffectObserver* observer = *it; + observer->onVoiceEffectChanged(mVoiceFontListDirty); + // In case onVoiceEffectChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } + mVoiceFontListDirty = false; + + // If new Voice Fonts have been added notify the user. + if (mVoiceFontsNew) + { + if (mVoiceFontsReceived) + { + LLNotificationsUtil::add("VoiceEffectsNew"); + } + mVoiceFontsNew = false; + } +} + +void LLWebRTCVoiceClient::enablePreviewBuffer(bool enable) +{ + LLSD result; + mCaptureBufferMode = enable; + + if (enable) + result["recplay"] = "start"; + else + result["recplay"] = "quit"; + + mWebRTCPump.post(result); + + if(mCaptureBufferMode && mIsInChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } +} + +void LLWebRTCVoiceClient::recordPreviewBuffer() +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + mCaptureBufferRecording = true; + + LLSD result(LLSDMap("recplay", "record")); + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::playPreviewBuffer(const LLUUID& effect_id) +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + if (!mCaptureBufferRecorded) + { + // Can't play until we have something recorded! + mCaptureBufferPlaying = false; + return; + } + + mPreviewVoiceFont = effect_id; + mCaptureBufferPlaying = true; + + LLSD result(LLSDMap("recplay", "playback")); + mWebRTCPump.post(result); +} + +void LLWebRTCVoiceClient::stopPreviewBuffer() +{ + mCaptureBufferRecording = false; + mCaptureBufferPlaying = false; + + LLSD result(LLSDMap("recplay", "quit")); + mWebRTCPump.post(result); +} + +bool LLWebRTCVoiceClient::isPreviewRecording() +{ + return (mCaptureBufferMode && mCaptureBufferRecording); +} + +bool LLWebRTCVoiceClient::isPreviewPlaying() +{ + return (mCaptureBufferMode && mCaptureBufferPlaying); +} + +void LLWebRTCVoiceClient::captureBufferRecordStartSendMessage() +{ if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; + + // Start capture + stream + << "" + << "" + << "\n\n\n"; + + // Unmute the mic + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "false" + << "\n\n\n"; + + // Dirty the mute mic state so that it will get reset when we finishing previewing + mMuteMicDirty = true; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::captureBufferRecordStopSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; + + // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. + stream << "" + << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" + << "true" + << "\n\n\n"; + + // Stop capture + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) +{ + if(mAccountLoggedIn) + { + // Track how may play requests are sent, so we know how many stop events to + // expect before play actually stops. + ++mPlayRequestCount; + + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; + + S32 font_index = getVoiceFontTemplateIndex(voice_font_id); + LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" << font_index << "" + << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() +{ + if(mAccountLoggedIn) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; + + stream + << "" + << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +LLWebRTCProtocolParser::LLWebRTCProtocolParser() +{ + parser = XML_ParserCreate(NULL); + + reset(); +} + +void LLWebRTCProtocolParser::reset() +{ + responseDepth = 0; + ignoringTags = false; + accumulateText = false; + energy = 0.f; + hasText = false; + hasAudio = false; + hasVideo = false; + terminated = false; + ignoreDepth = 0; + isChannel = false; + incoming = false; + enabled = false; + isEvent = false; + isLocallyMuted = false; + isModeratorMuted = false; + isSpeaking = false; + participantType = 0; + returnCode = -1; + state = 0; + statusCode = 0; + volume = 0; + textBuffer.clear(); + alias.clear(); + numberOfAliases = 0; + applicationString.clear(); +} + +//virtual +LLWebRTCProtocolParser::~LLWebRTCProtocolParser() +{ + if (parser) + XML_ParserFree(parser); +} + +static LLTrace::BlockTimerStatHandle FTM_WebRTC_PROCESS("WebRTC Process"); + +// virtual +LLIOPipe::EStatus LLWebRTCProtocolParser::process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump) +{ + LL_RECORD_BLOCK_TIME(FTM_WebRTC_PROCESS); + LLBufferStream istr(channels, buffer.get()); + std::ostringstream ostr; + while (istr.good()) + { + char buf[1024]; + istr.read(buf, sizeof(buf)); + mInput.append(buf, istr.gcount()); + } + + // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. + int start = 0; + int delim; + while((delim = mInput.find("\n\n\n", start)) != std::string::npos) + { + + // Reset internal state of the LLWebRTCProtocolParser (no effect on the expat parser) + reset(); + + XML_ParserReset(parser, NULL); + XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); + XML_SetCharacterDataHandler(parser, ExpatCharHandler); + XML_SetUserData(parser, this); + XML_Parse(parser, mInput.data() + start, delim - start, false); + + LL_DEBUGS("WebRTCProtocolParser") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; + start = delim + 3; + } + + if(start != 0) + mInput = mInput.substr(start); + + LL_DEBUGS("WebRTCProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; + + if(!LLWebRTCVoiceClient::sConnected) + { + // If voice has been disabled, we just want to close the socket. This does so. + LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; + return STATUS_STOP; + } + + return STATUS_OK; +} + +void XMLCALL LLWebRTCProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) +{ + if (data) + { + LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; + object->StartTag(el, attr); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLWebRTCProtocolParser::ExpatEndTag(void *data, const char *el) +{ + if (data) + { + LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; + object->EndTag(el); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLWebRTCProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) +{ + if (data) + { + LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; + object->CharData(s, len); + } +} + +// -------------------------------------------------------------------------------- + + +void LLWebRTCProtocolParser::StartTag(const char *tag, const char **attr) +{ + // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags + textBuffer.clear(); + // only accumulate text if we're not ignoring tags. + accumulateText = !ignoringTags; + + if (responseDepth == 0) + { + isEvent = !stricmp("Event", tag); + + if (!stricmp("Response", tag) || isEvent) + { + // Grab the attributes + while (*attr) + { + const char *key = *attr++; + const char *value = *attr++; + + if (!stricmp("requestId", key)) + { + requestId = value; + } + else if (!stricmp("action", key)) + { + actionString = value; + } + else if (!stricmp("type", key)) + { + eventTypeString = value; + } + } + } + LL_DEBUGS("WebRTCProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + } + else + { + if (ignoringTags) + { + LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + else + { + LL_DEBUGS("WebRTCProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + + // Ignore the InputXml stuff so we don't get confused + if (!stricmp("InputXml", tag)) + { + ignoringTags = true; + ignoreDepth = responseDepth; + accumulateText = false; + + LL_DEBUGS("WebRTCProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; + } + else if (!stricmp("CaptureDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->clearCaptureDevices(); + } + else if (!stricmp("RenderDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->clearRenderDevices(); + } + else if (!stricmp("CaptureDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("RenderDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("TemplateFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType.clear(); + } + } + } + responseDepth++; +} + +// -------------------------------------------------------------------------------- + +void LLWebRTCProtocolParser::EndTag(const char *tag) +{ + const std::string& string = textBuffer; + + responseDepth--; + + if (ignoringTags) + { + if (ignoreDepth == responseDepth) + { + LL_DEBUGS("WebRTCProtocolParser") << "end of ignore" << LL_ENDL; + ignoringTags = false; + } + else + { + LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + } + + if (!ignoringTags) + { + LL_DEBUGS("WebRTCProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + + // Closing a tag. Finalize the text we've accumulated and reset + if (!stricmp("ReturnCode", tag)) + returnCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("SessionHandle", tag)) + sessionHandle = string; + else if (!stricmp("SessionGroupHandle", tag)) + sessionGroupHandle = string; + else if (!stricmp("StatusCode", tag)) + statusCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("StatusString", tag)) + statusString = string; + else if (!stricmp("ParticipantURI", tag)) + uriString = string; + else if (!stricmp("Volume", tag)) + volume = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Energy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("IsModeratorMuted", tag)) + isModeratorMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("IsSpeaking", tag)) + isSpeaking = !stricmp(string.c_str(), "true"); + else if (!stricmp("Alias", tag)) + alias = string; + else if (!stricmp("NumberOfAliases", tag)) + numberOfAliases = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Application", tag)) + applicationString = string; + else if (!stricmp("ConnectorHandle", tag)) + connectorHandle = string; + else if (!stricmp("VersionID", tag)) + versionID = string; + else if (!stricmp("Version", tag)) + mBuildID = string; + else if (!stricmp("AccountHandle", tag)) + accountHandle = string; + else if (!stricmp("State", tag)) + state = strtol(string.c_str(), NULL, 10); + else if (!stricmp("URI", tag)) + uriString = string; + else if (!stricmp("IsChannel", tag)) + isChannel = !stricmp(string.c_str(), "true"); + else if (!stricmp("Incoming", tag)) + incoming = !stricmp(string.c_str(), "true"); + else if (!stricmp("Enabled", tag)) + enabled = !stricmp(string.c_str(), "true"); + else if (!stricmp("Name", tag)) + nameString = string; + else if (!stricmp("AudioMedia", tag)) + audioMediaString = string; + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("DisplayName", tag)) + displayNameString = string; + else if (!stricmp("Device", tag)) + deviceString = string; + else if (!stricmp("AccountName", tag)) + nameString = string; + else if (!stricmp("ParticipantType", tag)) + participantType = strtol(string.c_str(), NULL, 10); + else if (!stricmp("IsLocallyMuted", tag)) + isLocallyMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("MicEnergy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("ChannelURI", tag)) + uriString = string; + else if (!stricmp("BuddyURI", tag)) + uriString = string; + else if (!stricmp("Presence", tag)) + statusString = string; + else if (!stricmp("CaptureDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); + } + else if (!stricmp("RenderDevices", tag)) + { + LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); + } + else if (!stricmp("CaptureDevice", tag)) + { + LLWebRTCVoiceClient::getInstance()->addCaptureDevice(LLVoiceDevice(displayNameString, deviceString)); + } + else if (!stricmp("RenderDevice", tag)) + { + LLWebRTCVoiceClient::getInstance()->addRenderDevice(LLVoiceDevice(displayNameString, deviceString)); + } + else if (!stricmp("BlockMask", tag)) + blockMask = string; + else if (!stricmp("PresenceOnly", tag)) + presenceOnly = string; + else if (!stricmp("AutoAcceptMask", tag)) + autoAcceptMask = string; + else if (!stricmp("AutoAddAsBuddy", tag)) + autoAddAsBuddy = string; + else if (!stricmp("MessageHeader", tag)) + messageHeader = string; + else if (!stricmp("MessageBody", tag)) + messageBody = string; + else if (!stricmp("NotificationType", tag)) + notificationType = string; + else if (!stricmp("HasText", tag)) + hasText = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasAudio", tag)) + hasAudio = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasVideo", tag)) + hasVideo = !stricmp(string.c_str(), "true"); + else if (!stricmp("Terminated", tag)) + terminated = !stricmp(string.c_str(), "true"); + else if (!stricmp("SubscriptionHandle", tag)) + subscriptionHandle = string; + else if (!stricmp("SubscriptionType", tag)) + subscriptionType = string; + else if (!stricmp("SessionFont", tag)) + { + LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); + } + else if (!stricmp("TemplateFont", tag)) + { + LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); + } + else if (!stricmp("ID", tag)) + { + id = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Description", tag)) + { + descriptionString = string; + } + else if (!stricmp("ExpirationDate", tag)) + { + expirationDate = expiryTimeStampToLLDate(string); + } + else if (!stricmp("Expired", tag)) + { + hasExpired = !stricmp(string.c_str(), "1"); + } + else if (!stricmp("Type", tag)) + { + fontType = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Status", tag)) + { + fontStatus = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType = string;; + } + + textBuffer.clear(); + accumulateText= false; + + if (responseDepth == 0) + { + // We finished all of the XML, process the data + processResponse(tag); + } + } +} + +// -------------------------------------------------------------------------------- + +void LLWebRTCProtocolParser::CharData(const char *buffer, int length) +{ + /* + This method is called for anything that isn't a tag, which can be text you + want that lies between tags, and a lot of stuff you don't want like file formatting + (tabs, spaces, CR/LF, etc). + + Only copy text if we are in accumulate mode... + */ + if (accumulateText) + textBuffer.append(buffer, length); +} + +// -------------------------------------------------------------------------------- + +LLDate LLWebRTCProtocolParser::expiryTimeStampToLLDate(const std::string& WebRTC_ts) +{ + // *HACK: WebRTC reports the time incorrectly. LLDate also only parses a + // subset of valid ISO 8601 dates (only handles Z, not offsets). + // So just use the date portion and fix the time here. + std::string time_stamp = WebRTC_ts.substr(0, 10); + time_stamp += VOICE_FONT_EXPIRY_TIME; + + LL_DEBUGS("WebRTCProtocolParser") << "WebRTC timestamp " << WebRTC_ts << " modified to: " << time_stamp << LL_ENDL; + + return LLDate(time_stamp); +} + +// -------------------------------------------------------------------------------- + +void LLWebRTCProtocolParser::processResponse(std::string tag) +{ + LL_DEBUGS("WebRTCProtocolParser") << tag << LL_ENDL; + + // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs. + // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", + // so I believe this will give correct behavior. + + if(returnCode == 0) + statusCode = 0; + + if (isEvent) + { + const char *eventTypeCstr = eventTypeString.c_str(); + LL_DEBUGS("LowVoice") << eventTypeCstr << LL_ENDL; + + if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) + { + // These happen so often that logging them is pretty useless. + LL_DEBUGS("LowVoice") << "Updated Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << isModeratorMuted << ", " << isSpeaking << ", " << volume << ", " << energy << LL_ENDL; + LLWebRTCVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); + } + else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) + { + LLWebRTCVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); + } + else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-1408789@bhr.WebRTC.com + true + false + + + */ + LLWebRTCVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) + { + LLWebRTCVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent")) + { + //nothng useful to process for this event, but we should not WARN that we have received it. + } + else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) + { + LLWebRTCVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + 200 + OK + 2 + false + + */ + LLWebRTCVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); + } + else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) + { + /* + + + AuxBufferAudioCapture + + */ + LLWebRTCVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); + } + else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.WebRTC.com + xI5auBZ60SJWIk606-1JGRQ== + + 0 + + */ + LL_DEBUGS("LowVoice") << "Added Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << ", " << displayNameString << ", " << participantType << LL_ENDL; + LLWebRTCVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); + } + else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.WebRTC.com + xtx7YNV-3SGiG7rA1fo5Ndw== + + */ + LL_DEBUGS("LowVoice") << "Removed params:" << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << LL_ENDL; + + LLWebRTCVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); + } + else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) + { + // These are really spammy in tuning mode + LLWebRTCVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); + } + else if (!stricmp(eventTypeCstr, "MessageEvent")) + { + //TODO: This probably is not received any more, it was used to support SLim clients + LLWebRTCVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) + { + //TODO: This probably is not received any more, it was used to support SLim clients + LLWebRTCVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); + } + else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-9@bhd.WebRTC.com + 0 + 50 + 1 + 0 + 000 + 0 + + */ + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) + { + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) + { + LLWebRTCVoiceClient::getInstance()->voiceServiceConnectionStateChangedEvent(statusCode, statusString, mBuildID); + } + else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent")) + { + /* + + RenderDeviceChanged< / EventType> + + Speakers(Turtle Beach P11 Headset)< / Device> + Speakers(Turtle Beach P11 Headset)< / DisplayName> + SpecificDevice< / Type> + < / RelevantDevice> + < / Event> + */ + // an audio device was removed or added, fetch and update the local list of audio devices. + } + else + { + LL_WARNS("WebRTCProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; + } + } + else + { + const char *actionCstr = actionString.c_str(); + LL_DEBUGS("LowVoice") << actionCstr << LL_ENDL; + + if (!stricmp(actionCstr, "Session.Set3DPosition.1")) + { + // We don't need to process these + } + else if (!stricmp(actionCstr, "Connector.Create.1")) + { + LLWebRTCVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); + } + else if (!stricmp(actionCstr, "Account.Login.1")) + { + LLWebRTCVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); + } + else if (!stricmp(actionCstr, "Session.Create.1")) + { + LLWebRTCVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) + { + LLWebRTCVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "Session.Connect.1")) + { + LLWebRTCVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.Logout.1")) + { + LLWebRTCVoiceClient::getInstance()->logoutResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) + { + LLWebRTCVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) + { + LLWebRTCVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) + { + LLWebRTCVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Aux.SetVadProperties.1")) + { + // both values of statusCode (old and more recent) indicate valid requests + if (statusCode != 0 && statusCode != 200) + { + LL_WARNS("Voice") << "Aux.SetVadProperties.1 request failed: " + << "statusCode: " << statusCode + << " and " + << "statusString: " << statusString + << LL_ENDL; + } + } + /* + else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) + { + LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) + { + + } + else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) + { + + } + */ + } +} + +LLWebRTCSecurity::LLWebRTCSecurity() +{ + // 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]; + + for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) + { + random_value[b] = ll_rand() & 0xff; + } + mConnectorHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); + + for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) + { + random_value[b] = ll_rand() & 0xff; + } + mAccountHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); +} + +LLWebRTCSecurity::~LLWebRTCSecurity() +{ +} -- cgit v1.2.3 From 8fa09570ec91a656e55f88de882fc81fe39f35fa Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 9 Sep 2023 22:19:22 -0700 Subject: Updates to build on mac. --- indra/newview/llvoicewebrtc.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 6e68ca7e4f..5fcfc969b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -87,31 +87,19 @@ namespace { static const std::string VOICE_SERVER_TYPE = "WebRTC"; - // Don't retry connecting to the daemon more frequently than this: - const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; - const int DAEMON_CONNECT_RETRY_MAX = 3; - // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.5f; // Timeout for connection to WebRTC const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; const F32 CONNECT_DNS_TIMEOUT = 5.0f; - const int CONNECT_RETRY_MAX = 3; - const F32 LOGIN_ATTEMPT_TIMEOUT = 30.0f; const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; - const int LOGIN_RETRY_MAX = 3; - - const F32 PROVISION_RETRY_TIMEOUT = 2.0; - const int PROVISION_RETRY_MAX = 5; - + // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); - const F32 SESSION_JOIN_TIMEOUT = 30.0f; - // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process @@ -126,9 +114,6 @@ namespace { // Maximum length of capture buffer recordings in seconds. const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; - - const int ERROR_WebRTC_OBJECT_NOT_FOUND = 1001; - const int ERROR_WebRTC_NOT_LOGGED_IN = 1007; } static int scale_mic_volume(float volume) @@ -829,7 +814,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) { - channelSDP = result["jsep"]["sdp"]; + channelSDP = result["jsep"]["sdp"].asString(); } std::string voiceAccountServerUri; std::string voiceUserName = gAgent.getID().asString(); @@ -1468,6 +1453,8 @@ bool LLWebRTCVoiceClient::waitForChannel() << " VoiceEnabled=" << mVoiceEnabled << LL_ENDL; return !sShuttingDown; + case VOICE_CHANNEL_STATE_CHECK_EFFECTS: + break; } } while (true); } -- cgit v1.2.3 From fa313bab6348117f03b30dc4a5813b5058df57f7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 12 Sep 2023 15:17:08 -0700 Subject: do some thread safety to prevent webrtc threads from conflicting with viewer threads. --- indra/newview/llvoicewebrtc.cpp | 129 +++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 48 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 5fcfc969b5..d79b11f1da 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -603,7 +603,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() U32 retry = 0; - mVoiceControlState = VOICE_STATE_TP_WAIT; + setVoiceControlState(VOICE_STATE_TP_WAIT); do { @@ -617,7 +617,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() processIceUpdates(); - switch (mVoiceControlState) + switch (getVoiceControlState()) { case VOICE_STATE_TP_WAIT: // starting point for voice @@ -628,37 +628,44 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { - mVoiceControlState = VOICE_STATE_START_SESSION; + setVoiceControlState(VOICE_STATE_START_SESSION); } break; case VOICE_STATE_START_SESSION: if (establishVoiceConnection()) { - mVoiceControlState = VOICE_STATE_WAIT_FOR_SESSION_START; + setVoiceControlState(VOICE_STATE_WAIT_FOR_SESSION_START); } else { - mVoiceControlState = VOICE_STATE_SESSION_RETRY; + setVoiceControlState(VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_WAIT_FOR_SESSION_START: - llcoro::suspendUntilTimeout(1.0); - if (!mChannelSDP.empty()) { - mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; + llcoro::suspendUntilTimeout(1.0); + std::string channel_sdp; + { + LLMutexLock lock(&mVoiceStateMutex); + channel_sdp = mChannelSDP; + } + if (!channel_sdp.empty() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) + { + setVoiceControlState(VOICE_STATE_PROVISION_ACCOUNT); + } + break; } - break; - case VOICE_STATE_PROVISION_ACCOUNT: - if (!provisionVoiceAccount()) + // getVoiceControlState() can change while provisionVoiceAccount is happening + if (!provisionVoiceAccount() && getVoiceControlState() == VOICE_STATE_SESSION_RETRY) { - mVoiceControlState = VOICE_STATE_SESSION_RETRY; + setVoiceControlState(VOICE_STATE_SESSION_RETRY); } else { - mVoiceControlState = VOICE_STATE_SESSION_PROVISION_WAIT; + setVoiceControlState(VOICE_STATE_SESSION_PROVISION_WAIT); } break; case VOICE_STATE_SESSION_PROVISION_WAIT: @@ -685,11 +692,11 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() current_delay++; llcoro::suspendUntilTimeout(1.f); } - mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); } else { - mVoiceControlState = VOICE_STATE_DONE; + setVoiceControlState(VOICE_STATE_DONE); } break; @@ -700,38 +707,43 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() performMicTuning(); } - mVoiceControlState = VOICE_STATE_WAIT_FOR_CHANNEL; + setVoiceControlState(VOICE_STATE_WAIT_FOR_CHANNEL); } break; case VOICE_STATE_WAIT_FOR_CHANNEL: - waitForChannel(); // todo: split into more states like login/fonts - mVoiceControlState = VOICE_STATE_DISCONNECT; + { + if (!waitForChannel()) // todo: split into more states like login/fonts + { + setVoiceControlState(VOICE_STATE_DISCONNECT); + } + // 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; endAndDisconnectSession(); retry = 0; // Connected without issues - mVoiceControlState = VOICE_STATE_WAIT_FOR_EXIT; + setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); break; case VOICE_STATE_WAIT_FOR_EXIT: if (mRelogRequested && mVoiceEnabled) { LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - mVoiceControlState = VOICE_STATE_TP_WAIT; + setVoiceControlState(VOICE_STATE_TP_WAIT); } else { - mVoiceControlState = VOICE_STATE_DONE; + setVoiceControlState(VOICE_STATE_DONE); } break; case VOICE_STATE_DONE: break; } - } while (mVoiceControlState > 0); + } while (getVoiceControlState() > 0); if (sShuttingDown) { @@ -792,7 +804,10 @@ bool LLWebRTCVoiceClient::provisionVoiceAccount() LLSD body; LLSD jsep; jsep["type"] = "offer"; - jsep["sdp"] = mChannelSDP; + { + LLMutexLock lock(&mVoiceStateMutex); + jsep["sdp"] = mChannelSDP; + } body["jsep"] = jsep; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( @@ -806,7 +821,6 @@ bool LLWebRTCVoiceClient::provisionVoiceAccount() void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) { - mVoiceControlState = VOICE_STATE_SESSION_ESTABLISHED; LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); std::string channelSDP; if (result.has("jsep") && @@ -825,6 +839,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; setLoginInfo(voiceUserName, voicePassword, channelSDP); + setVoiceControlState(VOICE_STATE_SESSION_ESTABLISHED); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -1326,6 +1341,11 @@ bool LLWebRTCVoiceClient::waitForChannel() return false; } + if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) + { + return true; + } + processIceUpdates(); switch (state) { @@ -2759,14 +2779,16 @@ void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserve { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - if (state != llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) - { - return; + if (state == llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) + { + LLMutexLock lock(&mVoiceStateMutex); + mIceCompleted = true; } - mIceCompleted = true; } -void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { +void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +{ + LLMutexLock lock(&mVoiceStateMutex); mIceCandidates.push_back(candidate); } @@ -2795,31 +2817,35 @@ void LLWebRTCVoiceClient::processIceUpdates() LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); LLSD body; - if (mIceCandidates.size()) { - LLSD body; + LLMutexLock lock(&mVoiceStateMutex); - for (auto &ice_candidate : mIceCandidates) + if (mIceCandidates.size()) + { + LLSD body; + + 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; + body["candidates"].append(body_candidate); + } + mIceCandidates.clear(); + } + else if (mIceCompleted) { LLSD body_candidate; - body_candidate["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - body["candidates"].append(body_candidate); + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + mIceCompleted = false; + } + else + { + return; } - mIceCandidates.clear(); - } - else if (mIceCompleted) - { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - mIceCompleted = false; } - else - { - return; - } LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, @@ -2862,6 +2888,7 @@ void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD bo void LLWebRTCVoiceClient::OnOfferAvailable(const std::string &sdp) { LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; + LLMutexLock lock(&mVoiceStateMutex); mChannelSDP = sdp; } @@ -2872,6 +2899,12 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * audio_interface->setMute(true); } +void LLWebRTCVoiceClient::OnRenegotiationNeeded() +{ + LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; + setVoiceControlState(VOICE_STATE_SESSION_RETRY); +} + ///////////////////////////// // Response/Event handlers -- cgit v1.2.3 From f99cfd84aa017a6bd76ff5b48d35f6afa3167f8b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 11:51:36 -0700 Subject: Add some thread safety --- indra/newview/llvoicewebrtc.cpp | 48 +++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 23 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d79b11f1da..516bab1914 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -603,7 +603,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() U32 retry = 0; - setVoiceControlState(VOICE_STATE_TP_WAIT); + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT, VOICE_STATE_SESSION_RETRY); do { @@ -628,18 +628,18 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { - setVoiceControlState(VOICE_STATE_START_SESSION); + setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_START_SESSION: - if (establishVoiceConnection()) + if (establishVoiceConnection() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) { - setVoiceControlState(VOICE_STATE_WAIT_FOR_SESSION_START); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_SESSION_START, VOICE_STATE_SESSION_RETRY); } else { - setVoiceControlState(VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } break; @@ -649,23 +649,25 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() std::string channel_sdp; { LLMutexLock lock(&mVoiceStateMutex); - channel_sdp = mChannelSDP; - } - if (!channel_sdp.empty() && getVoiceControlState() != VOICE_STATE_SESSION_RETRY) - { - setVoiceControlState(VOICE_STATE_PROVISION_ACCOUNT); + if (mVoiceControlState == VOICE_STATE_SESSION_RETRY) + { + break; + } + if (!mChannelSDP.empty()) + { + mVoiceControlState = VOICE_STATE_PROVISION_ACCOUNT; + } } break; } case VOICE_STATE_PROVISION_ACCOUNT: - // getVoiceControlState() can change while provisionVoiceAccount is happening - if (!provisionVoiceAccount() && getVoiceControlState() == VOICE_STATE_SESSION_RETRY) + if (!provisionVoiceAccount()) { - setVoiceControlState(VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } else { - setVoiceControlState(VOICE_STATE_SESSION_PROVISION_WAIT); + setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); } break; case VOICE_STATE_SESSION_PROVISION_WAIT: @@ -692,11 +694,11 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() current_delay++; llcoro::suspendUntilTimeout(1.f); } - setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); } else { - setVoiceControlState(VOICE_STATE_DONE); + setVoiceControlStateUnless(VOICE_STATE_DONE); } break; @@ -707,7 +709,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() performMicTuning(); } - setVoiceControlState(VOICE_STATE_WAIT_FOR_CHANNEL); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); } break; @@ -715,7 +717,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() { if (!waitForChannel()) // todo: split into more states like login/fonts { - setVoiceControlState(VOICE_STATE_DISCONNECT); + setVoiceControlStateUnless(VOICE_STATE_DISCONNECT, VOICE_STATE_SESSION_RETRY); } // on true, it's a retry, so let the state stand. } @@ -725,18 +727,18 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() LL_DEBUGS("Voice") << "lost channel RelogRequested=" << mRelogRequested << LL_ENDL; endAndDisconnectSession(); retry = 0; // Connected without issues - setVoiceControlState(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); break; case VOICE_STATE_WAIT_FOR_EXIT: if (mRelogRequested && mVoiceEnabled) { LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - setVoiceControlState(VOICE_STATE_TP_WAIT); + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); } else { - setVoiceControlState(VOICE_STATE_DONE); + setVoiceControlStateUnless(VOICE_STATE_DONE); } break; @@ -839,7 +841,7 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; setLoginInfo(voiceUserName, voicePassword, channelSDP); - setVoiceControlState(VOICE_STATE_SESSION_ESTABLISHED); + setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -2902,7 +2904,7 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; - setVoiceControlState(VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } ///////////////////////////// -- cgit v1.2.3 From a2435aa1d07a972a8f3dc8dccfc5fdaf16c4a466 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 14:49:42 -0700 Subject: Hook up speaker volume. --- indra/newview/llvoicewebrtc.cpp | 797 ++-------------------------------------- 1 file changed, 25 insertions(+), 772 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 516bab1914..23033c5fee 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -123,14 +123,6 @@ static int scale_mic_volume(float volume) return 30 + (int)(volume * 20.0f); } -static int scale_speaker_volume(float volume) -{ - // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. - // Map it to WebRTC levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 - return 30 + (int)(volume * 40.0f); - -} - /////////////////////////////////////////////////////////////////////////////////////////////// @@ -317,7 +309,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : sConnected = false; sPump = nullptr; - mSpeakerVolume = scale_speaker_volume(0); + mSpeakerVolume = 0.0; mVoiceVersion.serverVersion = ""; mVoiceVersion.serverType = VOICE_SERVER_TYPE; @@ -2220,12 +2212,11 @@ void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{ - int scaled_volume = scale_speaker_volume(volume); +{ - if(scaled_volume != mTuningSpeakerVolume) + if (volume != mTuningSpeakerVolume) { - mTuningSpeakerVolume = scaled_volume; + mTuningSpeakerVolume = volume; mTuningSpeakerVolumeDirty = true; } } @@ -2663,7 +2654,7 @@ void LLWebRTCVoiceClient::sendLocalAudioUpdates() if (mSpeakerMuteDirty && !mTuningMode) { - const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false"); + const char *muteval = ((mSpeakerVolume <= 0.0) ? "true" : "false"); mSpeakerMuteDirty = false; @@ -2899,6 +2890,11 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; audio_interface->setMute(true); + { + LLMutexLock lock(&mVoiceStateMutex); + + audio_interface->setSpeakerVolume(mSpeakerVolume); + } } void LLWebRTCVoiceClient::OnRenegotiationNeeded() @@ -4901,18 +4897,23 @@ void LLWebRTCVoiceClient::setEarLocation(S32 loc) void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) { - int scaled_volume = scale_speaker_volume(volume); - - if(scaled_volume != mSpeakerVolume) + if (volume != mSpeakerVolume) { - int min_volume = scale_speaker_volume(0); - if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) - { - mSpeakerMuteDirty = true; - } + { + LLMutexLock lock(&mVoiceStateMutex); + int min_volume = 0.0; + if ((volume == min_volume) || (mSpeakerVolume == min_volume)) + { + mSpeakerMuteDirty = true; + } - mSpeakerVolume = scaled_volume; - mSpeakerVolumeDirty = true; + mSpeakerVolume = volume; + mSpeakerVolumeDirty = true; + } + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setSpeakerVolume(volume); + } } } @@ -6546,755 +6547,7 @@ void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() } } -LLWebRTCProtocolParser::LLWebRTCProtocolParser() -{ - parser = XML_ParserCreate(NULL); - - reset(); -} - -void LLWebRTCProtocolParser::reset() -{ - responseDepth = 0; - ignoringTags = false; - accumulateText = false; - energy = 0.f; - hasText = false; - hasAudio = false; - hasVideo = false; - terminated = false; - ignoreDepth = 0; - isChannel = false; - incoming = false; - enabled = false; - isEvent = false; - isLocallyMuted = false; - isModeratorMuted = false; - isSpeaking = false; - participantType = 0; - returnCode = -1; - state = 0; - statusCode = 0; - volume = 0; - textBuffer.clear(); - alias.clear(); - numberOfAliases = 0; - applicationString.clear(); -} - -//virtual -LLWebRTCProtocolParser::~LLWebRTCProtocolParser() -{ - if (parser) - XML_ParserFree(parser); -} - -static LLTrace::BlockTimerStatHandle FTM_WebRTC_PROCESS("WebRTC Process"); - -// virtual -LLIOPipe::EStatus LLWebRTCProtocolParser::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_WebRTC_PROCESS); - LLBufferStream istr(channels, buffer.get()); - std::ostringstream ostr; - while (istr.good()) - { - char buf[1024]; - istr.read(buf, sizeof(buf)); - mInput.append(buf, istr.gcount()); - } - - // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. - int start = 0; - int delim; - while((delim = mInput.find("\n\n\n", start)) != std::string::npos) - { - - // Reset internal state of the LLWebRTCProtocolParser (no effect on the expat parser) - reset(); - - XML_ParserReset(parser, NULL); - XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); - XML_SetCharacterDataHandler(parser, ExpatCharHandler); - XML_SetUserData(parser, this); - XML_Parse(parser, mInput.data() + start, delim - start, false); - - LL_DEBUGS("WebRTCProtocolParser") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; - start = delim + 3; - } - - if(start != 0) - mInput = mInput.substr(start); - - LL_DEBUGS("WebRTCProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; - - if(!LLWebRTCVoiceClient::sConnected) - { - // If voice has been disabled, we just want to close the socket. This does so. - LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; - return STATUS_STOP; - } - - return STATUS_OK; -} - -void XMLCALL LLWebRTCProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) -{ - if (data) - { - LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; - object->StartTag(el, attr); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLWebRTCProtocolParser::ExpatEndTag(void *data, const char *el) -{ - if (data) - { - LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; - object->EndTag(el); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLWebRTCProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) -{ - if (data) - { - LLWebRTCProtocolParser *object = (LLWebRTCProtocolParser*)data; - object->CharData(s, len); - } -} - -// -------------------------------------------------------------------------------- - - -void LLWebRTCProtocolParser::StartTag(const char *tag, const char **attr) -{ - // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags - textBuffer.clear(); - // only accumulate text if we're not ignoring tags. - accumulateText = !ignoringTags; - - if (responseDepth == 0) - { - isEvent = !stricmp("Event", tag); - - if (!stricmp("Response", tag) || isEvent) - { - // Grab the attributes - while (*attr) - { - const char *key = *attr++; - const char *value = *attr++; - - if (!stricmp("requestId", key)) - { - requestId = value; - } - else if (!stricmp("action", key)) - { - actionString = value; - } - else if (!stricmp("type", key)) - { - eventTypeString = value; - } - } - } - LL_DEBUGS("WebRTCProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - } - else - { - if (ignoringTags) - { - LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - else - { - LL_DEBUGS("WebRTCProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - - // Ignore the InputXml stuff so we don't get confused - if (!stricmp("InputXml", tag)) - { - ignoringTags = true; - ignoreDepth = responseDepth; - accumulateText = false; - - LL_DEBUGS("WebRTCProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; - } - else if (!stricmp("CaptureDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->clearCaptureDevices(); - } - else if (!stricmp("RenderDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->clearRenderDevices(); - } - else if (!stricmp("CaptureDevice", tag)) - { - deviceString.clear(); - } - else if (!stricmp("RenderDevice", tag)) - { - deviceString.clear(); - } - else if (!stricmp("SessionFont", tag)) - { - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; - } - else if (!stricmp("TemplateFont", tag)) - { - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; - } - else if (!stricmp("MediaCompletionType", tag)) - { - mediaCompletionType.clear(); - } - } - } - responseDepth++; -} - -// -------------------------------------------------------------------------------- - -void LLWebRTCProtocolParser::EndTag(const char *tag) -{ - const std::string& string = textBuffer; - - responseDepth--; - - if (ignoringTags) - { - if (ignoreDepth == responseDepth) - { - LL_DEBUGS("WebRTCProtocolParser") << "end of ignore" << LL_ENDL; - ignoringTags = false; - } - else - { - LL_DEBUGS("WebRTCProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - } - - if (!ignoringTags) - { - LL_DEBUGS("WebRTCProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - - // Closing a tag. Finalize the text we've accumulated and reset - if (!stricmp("ReturnCode", tag)) - returnCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("SessionHandle", tag)) - sessionHandle = string; - else if (!stricmp("SessionGroupHandle", tag)) - sessionGroupHandle = string; - else if (!stricmp("StatusCode", tag)) - statusCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("StatusString", tag)) - statusString = string; - else if (!stricmp("ParticipantURI", tag)) - uriString = string; - else if (!stricmp("Volume", tag)) - volume = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Energy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("IsModeratorMuted", tag)) - isModeratorMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("IsSpeaking", tag)) - isSpeaking = !stricmp(string.c_str(), "true"); - else if (!stricmp("Alias", tag)) - alias = string; - else if (!stricmp("NumberOfAliases", tag)) - numberOfAliases = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Application", tag)) - applicationString = string; - else if (!stricmp("ConnectorHandle", tag)) - connectorHandle = string; - else if (!stricmp("VersionID", tag)) - versionID = string; - else if (!stricmp("Version", tag)) - mBuildID = string; - else if (!stricmp("AccountHandle", tag)) - accountHandle = string; - else if (!stricmp("State", tag)) - state = strtol(string.c_str(), NULL, 10); - else if (!stricmp("URI", tag)) - uriString = string; - else if (!stricmp("IsChannel", tag)) - isChannel = !stricmp(string.c_str(), "true"); - else if (!stricmp("Incoming", tag)) - incoming = !stricmp(string.c_str(), "true"); - else if (!stricmp("Enabled", tag)) - enabled = !stricmp(string.c_str(), "true"); - else if (!stricmp("Name", tag)) - nameString = string; - else if (!stricmp("AudioMedia", tag)) - audioMediaString = string; - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("DisplayName", tag)) - displayNameString = string; - else if (!stricmp("Device", tag)) - deviceString = string; - else if (!stricmp("AccountName", tag)) - nameString = string; - else if (!stricmp("ParticipantType", tag)) - participantType = strtol(string.c_str(), NULL, 10); - else if (!stricmp("IsLocallyMuted", tag)) - isLocallyMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("MicEnergy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("ChannelURI", tag)) - uriString = string; - else if (!stricmp("BuddyURI", tag)) - uriString = string; - else if (!stricmp("Presence", tag)) - statusString = string; - else if (!stricmp("CaptureDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); - } - else if (!stricmp("RenderDevices", tag)) - { - LLWebRTCVoiceClient::getInstance()->setDevicesListUpdated(true); - } - else if (!stricmp("CaptureDevice", tag)) - { - LLWebRTCVoiceClient::getInstance()->addCaptureDevice(LLVoiceDevice(displayNameString, deviceString)); - } - else if (!stricmp("RenderDevice", tag)) - { - LLWebRTCVoiceClient::getInstance()->addRenderDevice(LLVoiceDevice(displayNameString, deviceString)); - } - else if (!stricmp("BlockMask", tag)) - blockMask = string; - else if (!stricmp("PresenceOnly", tag)) - presenceOnly = string; - else if (!stricmp("AutoAcceptMask", tag)) - autoAcceptMask = string; - else if (!stricmp("AutoAddAsBuddy", tag)) - autoAddAsBuddy = string; - else if (!stricmp("MessageHeader", tag)) - messageHeader = string; - else if (!stricmp("MessageBody", tag)) - messageBody = string; - else if (!stricmp("NotificationType", tag)) - notificationType = string; - else if (!stricmp("HasText", tag)) - hasText = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasAudio", tag)) - hasAudio = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasVideo", tag)) - hasVideo = !stricmp(string.c_str(), "true"); - else if (!stricmp("Terminated", tag)) - terminated = !stricmp(string.c_str(), "true"); - else if (!stricmp("SubscriptionHandle", tag)) - subscriptionHandle = string; - else if (!stricmp("SubscriptionType", tag)) - subscriptionType = string; - else if (!stricmp("SessionFont", tag)) - { - LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); - } - else if (!stricmp("TemplateFont", tag)) - { - LLWebRTCVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); - } - else if (!stricmp("ID", tag)) - { - id = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("Description", tag)) - { - descriptionString = string; - } - else if (!stricmp("ExpirationDate", tag)) - { - expirationDate = expiryTimeStampToLLDate(string); - } - else if (!stricmp("Expired", tag)) - { - hasExpired = !stricmp(string.c_str(), "1"); - } - else if (!stricmp("Type", tag)) - { - fontType = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("Status", tag)) - { - fontStatus = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("MediaCompletionType", tag)) - { - mediaCompletionType = string;; - } - - textBuffer.clear(); - accumulateText= false; - - if (responseDepth == 0) - { - // We finished all of the XML, process the data - processResponse(tag); - } - } -} - -// -------------------------------------------------------------------------------- - -void LLWebRTCProtocolParser::CharData(const char *buffer, int length) -{ - /* - This method is called for anything that isn't a tag, which can be text you - want that lies between tags, and a lot of stuff you don't want like file formatting - (tabs, spaces, CR/LF, etc). - - Only copy text if we are in accumulate mode... - */ - if (accumulateText) - textBuffer.append(buffer, length); -} - -// -------------------------------------------------------------------------------- - -LLDate LLWebRTCProtocolParser::expiryTimeStampToLLDate(const std::string& WebRTC_ts) -{ - // *HACK: WebRTC reports the time incorrectly. LLDate also only parses a - // subset of valid ISO 8601 dates (only handles Z, not offsets). - // So just use the date portion and fix the time here. - std::string time_stamp = WebRTC_ts.substr(0, 10); - time_stamp += VOICE_FONT_EXPIRY_TIME; - - LL_DEBUGS("WebRTCProtocolParser") << "WebRTC timestamp " << WebRTC_ts << " modified to: " << time_stamp << LL_ENDL; - - return LLDate(time_stamp); -} - -// -------------------------------------------------------------------------------- - -void LLWebRTCProtocolParser::processResponse(std::string tag) -{ - LL_DEBUGS("WebRTCProtocolParser") << tag << LL_ENDL; - - // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs. - // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", - // so I believe this will give correct behavior. - - if(returnCode == 0) - statusCode = 0; - - if (isEvent) - { - const char *eventTypeCstr = eventTypeString.c_str(); - LL_DEBUGS("LowVoice") << eventTypeCstr << LL_ENDL; - - if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) - { - // These happen so often that logging them is pretty useless. - LL_DEBUGS("LowVoice") << "Updated Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << isModeratorMuted << ", " << isSpeaking << ", " << volume << ", " << energy << LL_ENDL; - LLWebRTCVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); - } - else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) - { - LLWebRTCVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); - } - else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-1408789@bhr.WebRTC.com - true - false - - - */ - LLWebRTCVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) - { - LLWebRTCVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent")) - { - //nothng useful to process for this event, but we should not WARN that we have received it. - } - else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) - { - LLWebRTCVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - 200 - OK - 2 - false - - */ - LLWebRTCVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } - else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) - { - /* - - - AuxBufferAudioCapture - - */ - LLWebRTCVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); - } - else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.WebRTC.com - xI5auBZ60SJWIk606-1JGRQ== - - 0 - - */ - LL_DEBUGS("LowVoice") << "Added Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << ", " << displayNameString << ", " << participantType << LL_ENDL; - LLWebRTCVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); - } - else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.WebRTC.com - xtx7YNV-3SGiG7rA1fo5Ndw== - - */ - LL_DEBUGS("LowVoice") << "Removed params:" << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << LL_ENDL; - - LLWebRTCVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); - } - else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) - { - // These are really spammy in tuning mode - LLWebRTCVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); - } - else if (!stricmp(eventTypeCstr, "MessageEvent")) - { - //TODO: This probably is not received any more, it was used to support SLim clients - LLWebRTCVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) - { - //TODO: This probably is not received any more, it was used to support SLim clients - LLWebRTCVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); - } - else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-9@bhd.WebRTC.com - 0 - 50 - 1 - 0 - 000 - 0 - - */ - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) - { - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) - { - LLWebRTCVoiceClient::getInstance()->voiceServiceConnectionStateChangedEvent(statusCode, statusString, mBuildID); - } - else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent")) - { - /* - - RenderDeviceChanged< / EventType> - - Speakers(Turtle Beach P11 Headset)< / Device> - Speakers(Turtle Beach P11 Headset)< / DisplayName> - SpecificDevice< / Type> - < / RelevantDevice> - < / Event> - */ - // an audio device was removed or added, fetch and update the local list of audio devices. - } - else - { - LL_WARNS("WebRTCProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; - } - } - else - { - const char *actionCstr = actionString.c_str(); - LL_DEBUGS("LowVoice") << actionCstr << LL_ENDL; - if (!stricmp(actionCstr, "Session.Set3DPosition.1")) - { - // We don't need to process these - } - else if (!stricmp(actionCstr, "Connector.Create.1")) - { - LLWebRTCVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); - } - else if (!stricmp(actionCstr, "Account.Login.1")) - { - LLWebRTCVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); - } - else if (!stricmp(actionCstr, "Session.Create.1")) - { - LLWebRTCVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) - { - LLWebRTCVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "Session.Connect.1")) - { - LLWebRTCVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.Logout.1")) - { - LLWebRTCVoiceClient::getInstance()->logoutResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) - { - LLWebRTCVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) - { - LLWebRTCVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) - { - LLWebRTCVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Aux.SetVadProperties.1")) - { - // both values of statusCode (old and more recent) indicate valid requests - if (statusCode != 0 && statusCode != 200) - { - LL_WARNS("Voice") << "Aux.SetVadProperties.1 request failed: " - << "statusCode: " << statusCode - << " and " - << "statusString: " << statusString - << LL_ENDL; - } - } - /* - else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) - { - LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) - { - - } - else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) - { - - } - */ - } -} LLWebRTCSecurity::LLWebRTCSecurity() { -- cgit v1.2.3 From e554bf05293e34a5b8af7933127dff76c537f275 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 21:45:42 -0700 Subject: Fix connection failed logic to do a renegotiate. Also, remove some dead code. --- indra/newview/llvoicewebrtc.cpp | 64 ----------------------------------------- 1 file changed, 64 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 23033c5fee..bbe54018e8 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2633,70 +2633,6 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) void LLWebRTCVoiceClient::sendLocalAudioUpdates() { - // Check all of the dirty states and then send messages to those needing to be changed. - // Tuningmode hands its own mute settings. - std::ostringstream stream; - - if (mMuteMicDirty && !mTuningMode) - { - mMuteMicDirty = false; - - // Send a local mute command. - - LL_INFOS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << (mMuteMic ? "true" : "false") << "" - << "\n\n\n"; - - } - - if (mSpeakerMuteDirty && !mTuningMode) - { - const char *muteval = ((mSpeakerVolume <= 0.0) ? "true" : "false"); - - mSpeakerMuteDirty = false; - - LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << muteval << "" - << "\n\n\n"; - - } - - if (mSpeakerVolumeDirty) - { - mSpeakerVolumeDirty = false; - - LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << mSpeakerVolume << "" - << "\n\n\n"; - - } - - if (mMicVolumeDirty) - { - mMicVolumeDirty = false; - - LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; - - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << mMicVolume << "" - << "\n\n\n"; - } - - - if (!stream.str().empty()) - { - writeString(stream.str()); - } } /** -- cgit v1.2.3 From f519506e0455748794792ab59e4865ad2ae87ab3 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 22:49:39 -0700 Subject: fix some retry logic and speaker volume logic --- indra/newview/llvoicewebrtc.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index bbe54018e8..d66435e2bc 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -833,7 +833,6 @@ void LLWebRTCVoiceClient::OnVoiceAccountProvisioned(const LLSD& result) << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << channelSDP << LL_ENDL; setLoginInfo(voiceUserName, voicePassword, channelSDP); - setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -1518,6 +1517,10 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) { return false; } + if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) + { + break; + } if (mSessionTerminateRequested) { @@ -2825,17 +2828,20 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; + float speaker_volume = 0; audio_interface->setMute(true); { LLMutexLock lock(&mVoiceStateMutex); - - audio_interface->setSpeakerVolume(mSpeakerVolume); + speaker_volume = mSpeakerVolume; } + audio_interface->setSpeakerVolume(mSpeakerVolume); + setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; + mRelogRequested = TRUE; setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } -- cgit v1.2.3 From 0afb330cf19c0f988fd6688d3bc4266a3fd6624e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 20:48:33 -0700 Subject: Remove bad session from janus when negotation fails and is retried. --- indra/newview/llvoicewebrtc.cpp | 361 +++++++++++++++------------------------- 1 file changed, 134 insertions(+), 227 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d66435e2bc..bd4eff8bcf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -595,7 +595,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() U32 retry = 0; - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT, VOICE_STATE_SESSION_RETRY); + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); do { @@ -611,90 +611,89 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() 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 - { - setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); - } - break; + 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 + { + 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_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; - } + 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); + } + break; } - else - { - setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); - } - break; - case VOICE_STATE_SESSION_PROVISION_WAIT: - llcoro::suspendUntilTimeout(1.0); - break; - + case VOICE_STATE_PROVISION_ACCOUNT: + if (!provisionVoiceAccount()) + { + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + } + else + { + setVoiceControlStateUnless(VOICE_STATE_SESSION_PROVISION_WAIT, VOICE_STATE_SESSION_RETRY); + } + break; + case VOICE_STATE_SESSION_PROVISION_WAIT: + llcoro::suspendUntilTimeout(1.0); + 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(5.f * (F32)retry, 60.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) + case VOICE_STATE_SESSION_RETRY: + giveUp(); // cleans sockets and session + if (mRelogRequested) { - // 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); + // We failed to connect, give it a bit time before retrying. + retry++; + F32 full_delay = llmin(5.f * (F32) retry, 60.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_WAIT_FOR_EXIT); } - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_DONE); - } - break; + else + { + setVoiceControlStateUnless(VOICE_STATE_DONE); + } + break; - case VOICE_STATE_SESSION_ESTABLISHED: + case VOICE_STATE_SESSION_ESTABLISHED: { if (mTuningMode) { @@ -705,39 +704,52 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } break; - case VOICE_STATE_WAIT_FOR_CHANNEL: + case VOICE_STATE_WAIT_FOR_CHANNEL: { - if (!waitForChannel()) // todo: split into more states like login/fonts + 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. + // 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; - endAndDisconnectSession(); - retry = 0; // Connected without issues - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - 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 (mRelogRequested && mVoiceEnabled) - { - LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - } - else + case VOICE_STATE_WAIT_FOR_EXIT: + if (mRelogRequested && mVoiceEnabled) + { + LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); + } + else + { + setVoiceControlStateUnless(VOICE_STATE_DONE); + } + break; + + case VOICE_STATE_DONE: + if (mVoiceEnabled) + { + setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); + } + else + { + llcoro::suspendUntilTimeout(1.0); + } + break; + default: { - setVoiceControlStateUnless(VOICE_STATE_DONE); + LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; + break; } - break; - - case VOICE_STATE_DONE: - break; } - } while (getVoiceControlState() > 0); + } while (true); if (sShuttingDown) { @@ -749,15 +761,6 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() LL_INFOS("Voice") << "exiting" << LL_ENDL; } -bool LLWebRTCVoiceClient::endAndDisconnectSession() -{ - LL_DEBUGS("Voice") << LL_ENDL; - - breakVoiceConnection(true); - - return true; -} - bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) { if (!sShuttingDown && mVoiceEnabled) @@ -883,57 +886,40 @@ bool LLWebRTCVoiceClient::establishVoiceConnection() bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) { - LL_DEBUGS("Voice") << "( wait=" << corowait << ")" << LL_ENDL; - bool retval(true); - mShutdownComplete = false; - connectorShutdown(); + LL_INFOS("Voice") << "Breaking voice account." << LL_ENDL; - if (corowait) + while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) { - LLSD timeoutResult(LLSDMap("connector", "timeout")); - - LLSD result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - - retval = result.has("connector"); + LL_DEBUGS("Voice") << "no capabilities for voice breaking; waiting " << LL_ENDL; + // *TODO* Pump a message for wake up. + llcoro::suspend(); } - else + + if (sShuttingDown) { - mRelogRequested = false; //stop the control coro - // If we are not doing a corowait then we must sleep until the connector has responded - // otherwise we may very well close the socket too early. -#if LL_WINDOWS - if (!mShutdownComplete) - { - // The situation that brings us here is a call from ::terminate() - // At this point message system is already down so we can't wait for - // the message, yet we need to receive "connector shutdown response". - // Either wait a bit and emulate it or check gMessageSystem for specific message - _sleep(1000); - if (sConnected) - { - sConnected = false; - LLSD WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); - mWebRTCPump.post(WebRTCevent); - } - mShutdownComplete = true; - } -#endif + return false; } - LL_DEBUGS("Voice") << "closing SLVoice socket" << LL_ENDL; - closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. - cleanUp(); - sConnected = false; + std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); + + LL_DEBUGS("Voice") << "region ready for voice break; url=" << url << LL_ENDL; - return retval; + 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); + return true; } bool LLWebRTCVoiceClient::loginToWebRTC() { - - mRelogRequested = false; mIsLoggedIn = true; notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); @@ -956,41 +942,9 @@ void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) { if (mIsLoggedIn) { - // Ensure that we'll re-request provisioning before logging in again mAccountPassword.clear(); - - logoutSendMessage(); - - if (wait) - { - LLSD timeoutResult(LLSDMap("logout", "timeout")); - LLSD result; - - do - { - LL_DEBUGS("Voice") - << "waiting for logout response on " - << mWebRTCPump.getName() - << LL_ENDL; - - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - - if (sShuttingDown) - { - break; - } - - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - // Don't get confused by prior queued events -- note that it's - // very important that mWebRTCPump is an LLEventMailDrop, which - // does queue events. - } while (! result["logout"]); - } - else - { - LL_DEBUGS("Voice") << "not waiting for logout" << LL_ENDL; - } - + breakVoiceConnection(wait); + // Ensure that we'll re-request provisioning before logging in again mIsLoggedIn = false; } } @@ -1314,10 +1268,7 @@ typedef enum e_voice_wait_for_channel_state VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, VOICE_CHANNEL_STATE_PROCESS_CHANNEL, VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, - VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK, - VOICE_CHANNEL_STATE_LOGOUT, - VOICE_CHANNEL_STATE_RELOG, - VOICE_CHANNEL_STATE_DONE, + VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK } EVoiceWaitForChannelState; bool LLWebRTCVoiceClient::waitForChannel() @@ -1422,52 +1373,8 @@ bool LLWebRTCVoiceClient::waitForChannel() << " RelogRequested=" << mRelogRequested << " VoiceEnabled=" << mVoiceEnabled << LL_ENDL; - state = VOICE_CHANNEL_STATE_LOGOUT; - break; + return !sShuttingDown; } - - case VOICE_CHANNEL_STATE_LOGOUT: - logoutOfWebRTC(true /*bool wait*/); - if (mRelogRequested) - { - state = VOICE_CHANNEL_STATE_RELOG; - } - else - { - state = VOICE_CHANNEL_STATE_DONE; - } - break; - - case VOICE_CHANNEL_STATE_RELOG: - LL_DEBUGS("Voice") << "Relog Requested, restarting provisioning" << LL_ENDL; - if (!provisionVoiceAccount()) - { - if (sShuttingDown) - { - return false; - } - LL_WARNS("Voice") << "provisioning voice failed; giving up" << LL_ENDL; - giveUp(); - return false; - } - if (mVoiceEnabled && mRelogRequested) - { - state = VOICE_CHANNEL_STATE_LOGIN; - } - else - { - state = VOICE_CHANNEL_STATE_DONE; - } - break; - case VOICE_CHANNEL_STATE_DONE: - LL_DEBUGS("Voice") - << "exiting" - << " RelogRequested=" << mRelogRequested - << " VoiceEnabled=" << mVoiceEnabled - << LL_ENDL; - return !sShuttingDown; - case VOICE_CHANNEL_STATE_CHECK_EFFECTS: - break; } } while (true); } -- cgit v1.2.3 From ecbdda73de99bda09089114b5833ba11f55652ee Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 21:38:40 -0700 Subject: fix some shutdown logic in webrtc code --- indra/newview/llvoicewebrtc.cpp | 49 +++++------------------------------------ 1 file changed, 6 insertions(+), 43 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index bd4eff8bcf..92f107bb7c 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -369,23 +369,10 @@ void LLWebRTCVoiceClient::terminate() { return; } - - // needs to be done manually here since we will not get another pass in - // coroutines... that mechanism is long since gone. - if (mIsLoggedIn) - { - logoutOfWebRTC(false); - } - if(sConnected) - { - breakVoiceConnection(false); - sConnected = false; - } - else - { - mRelogRequested = false; - } + mRelogRequested = false; + mVoiceEnabled = false; + llwebrtc::init(); sShuttingDown = true; sPump = NULL; @@ -540,8 +527,7 @@ void LLWebRTCVoiceClient::idle(void* user_data) typedef enum e_voice_control_coro_state { VOICE_STATE_ERROR = -1, - VOICE_STATE_DONE = 0, - VOICE_STATE_TP_WAIT, // entry point + VOICE_STATE_TP_WAIT = 0, // entry point VOICE_STATE_START_DAEMON, VOICE_STATE_PROVISION_ACCOUNT, VOICE_STATE_SESSION_PROVISION_WAIT, @@ -685,12 +671,8 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() current_delay++; llcoro::suspendUntilTimeout(1.f); } - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); - } - else - { - setVoiceControlStateUnless(VOICE_STATE_DONE); } + setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); break; case VOICE_STATE_SESSION_ESTABLISHED: @@ -729,20 +711,10 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { - setVoiceControlStateUnless(VOICE_STATE_DONE); + llcoro::suspendUntilTimeout(1.0); } break; - case VOICE_STATE_DONE: - if (mVoiceEnabled) - { - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - } - else - { - llcoro::suspendUntilTimeout(1.0); - } - break; default: { LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; @@ -750,15 +722,6 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } } } while (true); - - if (sShuttingDown) - { - // LLWebRTCVoiceClient might be already dead - return; - } - - mIsCoroutineActive = false; - LL_INFOS("Voice") << "exiting" << LL_ENDL; } bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) -- cgit v1.2.3 From 112b9e0a965e116b62e754cbeea197e86119aa20 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 22:48:55 -0700 Subject: go ahead and restart voice negotiation when voice is re-enabled --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 92f107bb7c..93fdf8be34 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -704,7 +704,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() break; case VOICE_STATE_WAIT_FOR_EXIT: - if (mRelogRequested && mVoiceEnabled) + if (mVoiceEnabled) { LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); -- cgit v1.2.3 From 78c9b620432257cc636cd9f1ea4d7d1ed48c436c Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 14 Sep 2023 23:13:47 -0700 Subject: fix newer xcode build warning --- indra/newview/llvoicewebrtc.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 93fdf8be34..66bed5f0b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1227,7 +1227,6 @@ bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) typedef enum e_voice_wait_for_channel_state { VOICE_CHANNEL_STATE_LOGIN = 0, // entry point - VOICE_CHANNEL_STATE_CHECK_EFFECTS, VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING, VOICE_CHANNEL_STATE_PROCESS_CHANNEL, VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY, -- cgit v1.2.3 From fd8119c550bea19bbcf26e5426f259010f54c43e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Sep 2023 10:14:29 -0700 Subject: add datachannel support --- indra/newview/llvoicewebrtc.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 66bed5f0b5..9013de67f5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -361,6 +361,9 @@ void LLWebRTCVoiceClient::init(LLPumpIO *pump) mWebRTCSignalingInterface = llwebrtc::getSignalingInterface(); mWebRTCSignalingInterface->setSignalingObserver(this); + + mWebRTCDataInterface = llwebrtc::getDataInterface(); + mWebRTCDataInterface->setDataObserver(this); } void LLWebRTCVoiceClient::terminate() @@ -2707,6 +2710,12 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } +void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) +{ + +} + + void LLWebRTCVoiceClient::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; -- cgit v1.2.3 From f1f1bccad299f6cb70cb9f439872a5f702e8f972 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 21 Sep 2023 15:28:58 -0700 Subject: Stream audio levels to and from viewers via DataChannels --- indra/newview/llvoicewebrtc.cpp | 444 +++++++++++----------------------------- 1 file changed, 120 insertions(+), 324 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9013de67f5..da27ff7320 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -74,6 +74,9 @@ // for base64 decoding #include "apr_base64.h" +#include "json/reader.h" +#include "json/writer.h" + #define USE_SESSION_GROUPS 0 #define VX_NULL_POSITION -2147483648.0 /*The Silence*/ @@ -88,7 +91,7 @@ namespace { static const std::string VOICE_SERVER_TYPE = "WebRTC"; // Don't send positional updates more frequently than this: - const F32 UPDATE_THROTTLE_SECONDS = 0.5f; + const F32 UPDATE_THROTTLE_SECONDS = 0.1f; // Timeout for connection to WebRTC const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; @@ -375,7 +378,7 @@ void LLWebRTCVoiceClient::terminate() mRelogRequested = false; mVoiceEnabled = false; - llwebrtc::init(); + llwebrtc::terminate(); sShuttingDown = true; sPump = NULL; @@ -684,7 +687,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() { performMicTuning(); } - + sessionEstablished(); setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); } break; @@ -1116,6 +1119,9 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession // Just flush it all out and start new. mWebRTCPump.discard(); + // add 'self' participant. + addParticipantByID(gAgent.getID()); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); return true; @@ -1922,16 +1928,6 @@ void LLWebRTCVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePt } -void LLWebRTCVoiceClient::OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList& render_devices) -{ - clearCaptureDevices(); - for (auto &device : render_devices) - { - LLWebRTCVoiceClient::addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); - } - LLWebRTCVoiceClient::setDevicesListUpdated(true); -} - void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; @@ -1967,13 +1963,19 @@ void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) mDevicesListUpdated = state; } -void LLWebRTCVoiceClient::OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) +void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) { clearRenderDevices(); for (auto &device : render_devices) { addRenderDevice(LLVoiceDevice(device.display_name, device.id)); } + clearCaptureDevices(); + for (auto &device : capture_devices) + { + addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); + } setDevicesListUpdated(true); } @@ -2105,7 +2107,7 @@ bool LLWebRTCVoiceClient::deviceSettingsAvailable() { bool result = true; - if(mRenderDevices.empty()) + if(mRenderDevices.empty() || mCaptureDevices.empty()) result = false; return result; @@ -2113,11 +2115,7 @@ bool LLWebRTCVoiceClient::deviceSettingsAvailable() bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; - if (mDevicesListUpdated) - { - // a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel. - mDevicesListUpdated = false; // toggle the setting - } + mDevicesListUpdated = false; return updated; } @@ -2285,20 +2283,12 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) { - std::ostringstream stream; - if (mSpatialCoordsDirty && inSpatialChannel()) { LLVector3 l, u, a, vel; LLVector3d pos; mSpatialCoordsDirty = false; - - // Always send both speaker and listener positions together. - stream << "" - << "" << getAudioSessionHandle() << ""; - - stream << ""; LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); @@ -2321,38 +2311,6 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) pos.mdV[i] = VX_NULL_POSITION; } } - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << "" - ; - - stream << ""; - - stream << ""; LLVector3d earPosition; LLVector3 earVelocity; @@ -2397,40 +2355,13 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) pos.mdV[i] = VX_NULL_POSITION; } } - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << "" - ; - - stream << ""; + } - stream << "1"; //do not generate responses for update requests - stream << "\n\n\n"; - } + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->requestAudioLevel(); + } + if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) { @@ -2469,41 +2400,12 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) } LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; - - // SLIM SDK: Send both volume and mute commands. - - // Send a "volume for me" command for the user. - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << volume << "" - << "\n\n\n"; - - if(!mAudioSession->mIsP2P) - { - // Send a "mute for me" command for the user - // Doesn't work in P2P sessions - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << (mute?"1":"0") << "" - << "Audio" - << "\n\n\n"; - } } p->mVolumeDirty = false; } } } - - std::string update(stream.str()); - if(!update.empty()) - { - LL_DEBUGS("VoiceUpdate") << "sending update " << update << LL_ENDL; - writeString(update); - } - } void LLWebRTCVoiceClient::sendLocalAudioUpdates() @@ -2700,6 +2602,7 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; + mWebRTCAudioInterface->setAudioObserver(this); float speaker_volume = 0; audio_interface->setMute(true); { @@ -2710,9 +2613,68 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } +void LLWebRTCVoiceClient::OnAudioLevel(float level) +{ + if (mWebRTCDataInterface) + { + Json::FastWriter writer; + Json::Value root; + root["p"] = (UINT32) (level * 256); + std::string json_data = writer.write(root); + + mWebRTCDataInterface->sendData(json_data, false); + } +} + void LLWebRTCVoiceClient::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; + } + for (auto &participant_id : voice_data.getMemberNames()) + { + std::string foo = participant_id; + LL_WARNS("Voice") << "Participant ID (" << participant_id << "):" << data << LL_ENDL; + + 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); + if (participant) + { + participant->mPower = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + /* 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 > 0.05; + } + } + } } @@ -2994,7 +2956,7 @@ void LLWebRTCVoiceClient::sessionAddedEvent( session->mAlternateSIPURI = session->mSIPURI; // and generate a proper URI from the ID. - setSessionURI(session, sipURIFromID(session->mCallerID)); + setSessionURI(session, session->mCallerID.asString()); } else { @@ -3065,7 +3027,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(session->mSIPURI)); + participantStatePtr_t participant(session->addParticipant(LLUUID(session->mSIPURI))); if(participant) { if(participant->mAvatarIDValid) @@ -3365,7 +3327,7 @@ void LLWebRTCVoiceClient::participantAddedEvent( sessionStatePtr_t session(findSession(sessionHandle)); if(session) { - participantStatePtr_t participant(session->addParticipant(uriString)); + participantStatePtr_t participant(session->addParticipant(LLUUID(uriString))); if(participant) { participant->mAccountName = nameString; @@ -3424,92 +3386,6 @@ void LLWebRTCVoiceClient::participantRemovedEvent( } } - -void LLWebRTCVoiceClient::participantUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - bool isModeratorMuted, - bool isSpeaking, - int volume, - F32 energy) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - participantStatePtr_t participant(session->findParticipant(uriString)); - - if(participant) - { - //LL_INFOS("Voice") << "Participant Update for " << participant->mDisplayName << LL_ENDL; - - participant->mIsSpeaking = isSpeaking; - participant->mIsModeratorMuted = isModeratorMuted; - - // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false - if (isSpeaking) - { - participant->mSpeakingTimeout.reset(); - participant->mPower = energy; - } - else - { - participant->mPower = 0.0f; - } - - // Ignore incoming volume level if it has been explicitly set, or there - // is a volume or mute change pending. - if ( !participant->mVolumeSet && !participant->mVolumeDirty) - { - participant->mVolume = (F32)volume * VOLUME_SCALE_WEBRTC; - } - - // *HACK: mantipov: added while working on EXT-3544 - /* - Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE - LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER. - - participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted - Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug. - Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates. - - But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post() - voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager - and event is not fired. - - So, we have to call LLSpeakerMgr::update() here. - */ - LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); - - // ignore session ID of local chat - if (voice_cnl && voice_cnl->getSessionID().notNull()) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); - if (speaker_manager) - { - speaker_manager->update(true); - - // also initialize voice moderate_mode depend on Agent's participant. See EXT-6937. - // *TODO: remove once a way to request the current voice channel moderation mode is implemented. - if (gAgent.getID() == participant->mAvatarID) - { - speaker_manager->initVoiceModerateMode(); - } - } - } - } - else - { - LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - void LLWebRTCVoiceClient::messageEvent( std::string &sessionHandle, std::string &uriString, @@ -3743,8 +3619,9 @@ void LLWebRTCVoiceClient::muteListChanged() ///////////////////////////// // Managing list of participants -LLWebRTCVoiceClient::participantState::participantState(const std::string &uri) : - mURI(uri), +LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : + mURI(agent_id.asString()), + mAvatarID(agent_id), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), @@ -3760,55 +3637,27 @@ LLWebRTCVoiceClient::participantState::participantState(const std::string &uri) { } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const std::string &uri) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) { participantStatePtr_t result; - bool useAlternateURI = false; - // Note: this is mostly the body of LLWebRTCVoiceClient::sessionState::findParticipant(), but since we need to know if it - // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. - { - participantMap::iterator iter = mParticipantsByURI.find(uri); + participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); - 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. - // Use mSIPURI instead, since it will be properly encoded. - iter = mParticipantsByURI.find(mSIPURI); - useAlternateURI = true; - } - } - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } + if (iter != mParticipantsByUUID.end()) + { + result = iter->second; } if(!result) { // participant isn't already in one list or the other. - result.reset(new participantState(useAlternateURI?mSIPURI:uri)); - mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); + result.reset(new participantState(agent_id)); + mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); mParticipantsChanged = true; - - // Try to do a reverse transform on the URI to get the GUID back. - { - LLUUID id; - if(LLWebRTCVoiceClient::getInstance()->IDFromName(result->mURI, id)) - { - result->mAvatarIDValid = true; - result->mAvatarID = id; - } - else - { - // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This indicates that the ID will not be in the name cache. - result->mAvatarID.generate(uri); - } - } + + result->mAvatarIDValid = true; + result->mAvatarID = agent_id; if(result->updateMuteState()) { @@ -3983,6 +3832,16 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantB return result; } +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const LLUUID &id) +{ + participantStatePtr_t result; + if (mAudioSession) + { + result = mAudioSession->addParticipant(id); + } + return result; +} + // Check for parcel boundary crossing @@ -4156,68 +4015,14 @@ bool LLWebRTCVoiceClient::setSpatialChannel( void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) { - std::string userURI = sipURIFromID(uuid); - - switchChannel(userURI, false, true, true); + switchChannel(uuid.asString(), false, true, true); } -#if 0 -// WebRTC text IMs are not in use. -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::startUserIMSession(const LLUUID &uuid) -{ - // Figure out if a session with the user already exists - sessionStatePtr_t session(findSession(uuid)); - if(!session) - { - // No session with user, need to start one. - std::string uri = sipURIFromID(uuid); - session = addSession(uri); - - llassert(session); - if (!session) - return session; - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; - session->mCallerID = uuid; - } - - if(session->mHandle.empty()) - { - // Session isn't active -- start it up. - sessionCreateSendMessage(session, false, false); - } - else - { - // Session is already active -- start up text. - sessionTextConnectSendMessage(session); - } - - return session; -} -#endif void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) { -#if 0 - // WebRTC text IMs are not in use. - - // Figure out if a session with the user exists - sessionStatePtr_t session(findSession(uuid)); - if(session) - { - // found the session - if(!session->mHandle.empty()) - { - // sessionTextDisconnectSendMessage(session); // a SLim leftover, not used any more. - } - } - else - { - LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; - } -#endif + } bool LLWebRTCVoiceClient::isValidChannel(std::string &sessionHandle) { @@ -4361,16 +4166,6 @@ bool LLWebRTCVoiceClient::inProximalChannel() return result; } -std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID &id) -{ - std::string result; - result = "sip:"; - result += nameFromID(id); - result += "@"; - - return result; -} - std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) { std::string result; @@ -4785,10 +4580,6 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) participantStatePtr_t participant(findParticipantByID(id)); if(participant) { - if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) - { - participant->mIsSpeaking = FALSE; - } result = participant->mIsSpeaking; } @@ -4814,7 +4605,8 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID& id) participantStatePtr_t participant(findParticipantByID(id)); if(participant) { - result = participant->mPower; + LL_WARNS("Voice") << "Power:" << participant->mPower << LL_ENDL; + result = participant->mPower*4; } return result; @@ -5178,7 +4970,10 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceCli } } - +void LLWebRTCVoiceClient::sessionEstablished() +{ + addSession(gAgent.getRegion()->getRegionID().asString()); +} LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const std::string &handle) { @@ -6367,6 +6162,7 @@ void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() } } +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } LLWebRTCSecurity::LLWebRTCSecurity() -- cgit v1.2.3 From 3f1fa296696086efe618bdbd3c6f636b6e1163b1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 21 Sep 2023 22:33:57 -0700 Subject: deal with add/remove of participants more effectively. --- indra/newview/llvoicewebrtc.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index da27ff7320..a942eb8288 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2619,7 +2619,7 @@ void LLWebRTCVoiceClient::OnAudioLevel(float level) { Json::FastWriter writer; Json::Value root; - root["p"] = (UINT32) (level * 256); + root["p"] = (uint32_t) (level * 256); std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); @@ -2656,9 +2656,6 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) } for (auto &participant_id : voice_data.getMemberNames()) { - std::string foo = participant_id; - LL_WARNS("Voice") << "Participant ID (" << participant_id << "):" << data << LL_ENDL; - LLUUID agent_id(participant_id); if (agent_id.isNull()) { @@ -2666,8 +2663,16 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) continue; } participantStatePtr_t participant = findParticipantByID(agent_id); + if (!participant && voice_data[participant_id].get("j", Json::Value(false)).asBool()) + { + participant = addParticipantByID(agent_id); + } if (participant) { + if(voice_data[participant_id].get("l", Json::Value(false)).asBool()) + { + removeParticipantByID(agent_id); + } participant->mPower = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; /* 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. */ @@ -3842,6 +3847,18 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy return result; } +void LLWebRTCVoiceClient::removeParticipantByID(const LLUUID &id) +{ + participantStatePtr_t result; + if (mAudioSession) + { + participantStatePtr_t participant = mAudioSession->findParticipantByID(id); + if (participant) + { + mAudioSession->removeParticipant(participant); + } + } +} // Check for parcel boundary crossing -- cgit v1.2.3 From 64ec3cb19ffeeb25d7d235e60629c9b4890986f1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 21 Sep 2023 23:23:49 -0700 Subject: send a message to the server when we're ready for data channel data --- indra/newview/llvoicewebrtc.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a942eb8288..b621f5ee92 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2682,6 +2682,16 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) } } +void LLWebRTCVoiceClient::OnDataChannelReady() +{ + // send a join + Json::FastWriter writer; + Json::Value root; + root["j"] = true; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); +} + void LLWebRTCVoiceClient::OnRenegotiationNeeded() { -- cgit v1.2.3 From cf72241f97627c59b5d5ab414caf086bffd47dc1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 10:47:48 -0700 Subject: Fix shutdown crash issue. --- indra/newview/llvoicewebrtc.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b621f5ee92..e6877063b5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2100,7 +2100,7 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return mWebRTCDeviceInterface->getTuningMicrophoneEnergy(); + return mWebRTCDeviceInterface->getAudioLevel(); } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -2357,9 +2357,14 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) } } - if (mWebRTCAudioInterface) + if (mWebRTCDataInterface && mWebRTCAudioInterface) { - mWebRTCAudioInterface->requestAudioLevel(); + Json::FastWriter writer; + Json::Value root; + root["p"] = (uint32_t) ((F32)mWebRTCDeviceInterface->getAudioLevel() * 256); + std::string json_data = writer.write(root); + + mWebRTCDataInterface->sendData(json_data, false); } @@ -2613,19 +2618,6 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } -void LLWebRTCVoiceClient::OnAudioLevel(float level) -{ - if (mWebRTCDataInterface) - { - Json::FastWriter writer; - Json::Value root; - root["p"] = (uint32_t) (level * 256); - std::string json_data = writer.write(root); - - mWebRTCDataInterface->sendData(json_data, false); - } -} - void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) { // incoming data will be a json structure (if it's not binary.) We may pack -- cgit v1.2.3 From 1794898563b4b14a46bd385f202b002fee144100 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 11:22:41 -0700 Subject: only send volume updates when volume has changed. --- indra/newview/llvoicewebrtc.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index e6877063b5..3942f4171d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2359,12 +2359,17 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) if (mWebRTCDataInterface && mWebRTCAudioInterface) { - Json::FastWriter writer; - Json::Value root; - root["p"] = (uint32_t) ((F32)mWebRTCDeviceInterface->getAudioLevel() * 256); - std::string json_data = writer.write(root); + uint32_t audio_level = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); + if (audio_level != mAudioLevel) + { + Json::FastWriter writer; + Json::Value root; + root["p"] = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); + std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + mWebRTCDataInterface->sendData(json_data, false); + mAudioLevel = audio_level; + } } -- cgit v1.2.3 From 2ad058aa38485ffc48910b3bfe9679bdd9d692b3 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 12:40:16 -0700 Subject: join notification was going out before session was created. --- indra/newview/llvoicewebrtc.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3942f4171d..3073a58183 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1121,6 +1121,13 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession // add 'self' participant. addParticipantByID(gAgent.getID()); + // tell peers that this participant has joined. + + Json::FastWriter writer; + Json::Value root; + root["j"] = true; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); @@ -2681,12 +2688,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) void LLWebRTCVoiceClient::OnDataChannelReady() { - // send a join - Json::FastWriter writer; - Json::Value root; - root["j"] = true; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + } -- cgit v1.2.3 From 78228d4dc423478c153aeeab0d92acf9ce5de78b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 22 Sep 2023 16:55:13 -0700 Subject: Smooth voice power level reporting. --- indra/newview/llvoicewebrtc.cpp | 52 +++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3073a58183..c22246d841 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -87,6 +87,7 @@ namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; const F32 SPEAKING_TIMEOUT = 1.f; + const F32 SPEAKING_AUDIO_LEVEL = 0.05; static const std::string VOICE_SERVER_TYPE = "WebRTC"; @@ -2366,16 +2367,28 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) if (mWebRTCDataInterface && mWebRTCAudioInterface) { - uint32_t audio_level = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); - if (audio_level != mAudioLevel) + F32 audio_level = 0.0; + + if (!mMuteMic) + { + audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); + } + uint32_t uint_audio_level = (uint32_t) (audio_level * 256); + if (uint_audio_level != mAudioLevel) { Json::FastWriter writer; - Json::Value root; - root["p"] = (uint32_t) ((F32) mWebRTCDeviceInterface->getAudioLevel() * 256); + Json::Value root; + root["p"] = uint_audio_level; std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); - mAudioLevel = audio_level; + mAudioLevel = uint_audio_level; + participantStatePtr_t participant = findParticipantByID(gAgentID); + if (participant) + { + participant->mPower = audio_level; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + } } } @@ -2677,10 +2690,12 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) { removeParticipantByID(agent_id); } - participant->mPower = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + // 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 > 0.05; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } } } @@ -4446,10 +4461,16 @@ void LLWebRTCVoiceClient::leaveChannel(void) void LLWebRTCVoiceClient::setMuteMic(bool muted) { + participantStatePtr_t participant = findParticipantByID(gAgentID); + if (participant) + { + participant->mPower = 0.0; + } if (mWebRTCAudioInterface) { mWebRTCAudioInterface->setMute(muted); } + mMuteMic = muted; } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -4625,16 +4646,17 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) return result; } -F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID& id) -{ - F32 result = 0; - participantStatePtr_t participant(findParticipantByID(id)); - if(participant) +F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) +{ + F32 result = 0; + if (!mMuteMic) { - LL_WARNS("Voice") << "Power:" << participant->mPower << LL_ENDL; - result = participant->mPower*4; + participantStatePtr_t participant(findParticipantByID(id)); + if (participant) + { + result = participant->mPower * 4; + } } - return result; } -- cgit v1.2.3 From 025e9dcc4d898a7a66c55040903f5d1f9a6d4d7f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 25 Sep 2023 20:03:18 -0700 Subject: send position and rotation data to voice server. --- indra/newview/llvoicewebrtc.cpp | 248 +++++++--------------------------------- 1 file changed, 43 insertions(+), 205 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c22246d841..4c687b5807 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -746,7 +746,6 @@ bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) return false; } - bool LLWebRTCVoiceClient::provisionVoiceAccount() { LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; @@ -2153,127 +2152,6 @@ void LLWebRTCVoiceClient::giveUp() cleanUp(); } -static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) -{ - F32 nat[3], nup[3], nl[3]; // the new at, up, left vectors and the new position and velocity -// F32 nvel[3]; - F64 npos[3]; - - // The original XML command was sent like this: - /* - << "" - << "" << pos[VX] << "" - << "" << pos[VZ] << "" - << "" << pos[VY] << "" - << "" - << "" - << "" << mAvatarVelocity[VX] << "" - << "" << mAvatarVelocity[VZ] << "" - << "" << mAvatarVelocity[VY] << "" - << "" - << "" - << "" << l.mV[VX] << "" - << "" << u.mV[VX] << "" - << "" << a.mV[VX] << "" - << "" - << "" - << "" << l.mV[VZ] << "" - << "" << u.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VY] << "" - << "" << u.mV [VZ] << "" - << "" << a.mV [VY] << "" - << ""; - */ - -#if 1 - // This was the original transform done when building the XML command - nat[0] = left.mV[VX]; - nat[1] = up.mV[VX]; - nat[2] = at.mV[VX]; - - nup[0] = left.mV[VZ]; - nup[1] = up.mV[VY]; - nup[2] = at.mV[VZ]; - - nl[0] = left.mV[VY]; - nl[1] = up.mV[VZ]; - nl[2] = at.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY]; - -// nvel[0] = vel.mV[VX]; -// nvel[1] = vel.mV[VZ]; -// nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - - // This was the original transform done in the SDK - nat[0] = at.mV[2]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * left.mV[2]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = at.mV[0]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[0]; - - npos[2] = pos.mdV[2] * -1.0; - npos[1] = pos.mdV[1]; - npos[0] = pos.mdV[0]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } -#else - // This is the compose of the two transforms (at least, that's what I'm trying for) - nat[0] = at.mV[VX]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * up.mV[VZ]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = left.mV[VX]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY] * -1.0; - - nvel[0] = vel.mV[VX]; - nvel[1] = vel.mV[VZ]; - nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - -#endif -} - void LLWebRTCVoiceClient::setHidden(bool hidden) { mHidden = hidden; @@ -2291,82 +2169,39 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) { - if (mSpatialCoordsDirty && inSpatialChannel()) - { - LLVector3 l, u, a, vel; - LLVector3d pos; - - mSpatialCoordsDirty = false; - LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); -// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; - l = avatarRot.getLeftRow(); - u = avatarRot.getUpRow(); - a = avatarRot.getFwdRow(); - - pos = mAvatarPosition; - vel = mAvatarVelocity; - - // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. - // The old transform is replicated by this function. - oldSDKTransform(l, u, a, pos, vel); + if (mWebRTCDataInterface && mWebRTCAudioInterface) + { + Json::Value root = Json::objectValue; - if (mHidden) + if (mSpatialCoordsDirty && inSpatialChannel()) { - for (int i=0;i<3;++i) - { - pos.mdV[i] = VX_NULL_POSITION; - } - } - - LLVector3d earPosition; - LLVector3 earVelocity; - LLMatrix3 earRot; - - switch(mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earVelocity = mCameraVelocity; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = avatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = mCameraRot; - 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)(mCameraPosition[0]*100); + root["lp"]["y"] = (int)(mCameraPosition[1]*100); + root["lp"]["z"] = (int)(mCameraPosition[2]*100); + root["lh"] = Json::objectValue; + root["lh"]["x"] = (int)(mCameraRot[0]*100); + root["lh"]["y"] = (int)(mCameraRot[1]*100); + root["lh"]["z"] = (int)(mCameraRot[2]*100); + root["lh"]["w"] = (int)(mCameraRot[3]*100); - l = earRot.getLeftRow(); - u = earRot.getUpRow(); - a = earRot.getFwdRow(); - - pos = earPosition; - vel = earVelocity; - - - oldSDKTransform(l, u, a, pos, vel); - - if (mHidden) - { - for (int i=0;i<3;++i) - { - pos.mdV[i] = VX_NULL_POSITION; - } + mSpatialCoordsDirty = false; } - } - - if (mWebRTCDataInterface && mWebRTCAudioInterface) - { + + F32 audio_level = 0.0; if (!mMuteMic) @@ -2376,19 +2211,24 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) uint32_t uint_audio_level = (uint32_t) (audio_level * 256); if (uint_audio_level != mAudioLevel) { - Json::FastWriter writer; - Json::Value root; root["p"] = uint_audio_level; - std::string json_data = writer.write(root); - - mWebRTCDataInterface->sendData(json_data, false); mAudioLevel = uint_audio_level; participantStatePtr_t participant = findParticipantByID(gAgentID); - if (participant) - { + if (participant) + { participant->mPower = audio_level; - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; + } + } + + + if (root.size() > 0) + { + + Json::FastWriter writer; + std::string json_data = writer.write(root); + + mWebRTCDataInterface->sendData(json_data, false); } } @@ -4361,7 +4201,6 @@ void LLWebRTCVoiceClient::updatePosition(void) LLViewerRegion *region = gAgent.getRegion(); if(region && isAgentAvatarValid()) { - LLMatrix3 rot; LLVector3d pos; LLQuaternion qrot; @@ -4369,13 +4208,12 @@ void LLWebRTCVoiceClient::updatePosition(void) // They're currently always set to zero. // Send the current camera position to the voice code - rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); LLWebRTCVoiceClient::getInstance()->setCameraPosition( pos, // position LLVector3::zero, // velocity - rot); // rotation matrix + LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix // Send the current avatar position to the voice code qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); @@ -4392,7 +4230,7 @@ void LLWebRTCVoiceClient::updatePosition(void) } } -void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { mCameraRequestedPosition = position; -- cgit v1.2.3 From 819a715a59b9926f64ba62b28299849868e577e2 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 26 Sep 2023 16:37:24 -0700 Subject: sdd stereo support to client --- indra/newview/llvoicewebrtc.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 4c687b5807..a1e63c29cf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2208,10 +2208,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) { audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); } - uint32_t uint_audio_level = (uint32_t) (audio_level * 256); + uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); if (uint_audio_level != mAudioLevel) { - root["p"] = uint_audio_level; + root["p"] = uint_audio_level; mAudioLevel = uint_audio_level; participantStatePtr_t participant = findParticipantByID(gAgentID); if (participant) @@ -2530,7 +2530,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) { removeParticipantByID(agent_id); } - F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 256; + 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 @@ -4487,13 +4487,10 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { F32 result = 0; - if (!mMuteMic) + participantStatePtr_t participant(findParticipantByID(id)); + if (participant) { - participantStatePtr_t participant(findParticipantByID(id)); - if (participant) - { - result = participant->mPower * 4; - } + result = participant->mPower * 3.5; } return result; } -- cgit v1.2.3 From d093ca0523c1162353e2553c016c15d41ab014ff Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 28 Sep 2023 10:55:13 -0700 Subject: Transmit position and power when joining --- indra/newview/llvoicewebrtc.cpp | 101 +++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 49 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a1e63c29cf..ccaa02b704 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1124,7 +1124,7 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession // tell peers that this participant has joined. Json::FastWriter writer; - Json::Value root; + Json::Value root = getPositionAndVolumeUpdateJson(true); root["j"] = true; std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); @@ -2167,60 +2167,63 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) } } -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(void) -{ - +Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) +{ + Json::Value root = Json::objectValue; - if (mWebRTCDataInterface && mWebRTCAudioInterface) + if ((mSpatialCoordsDirty || force) && inSpatialChannel()) { - Json::Value root = Json::objectValue; - - if (mSpatialCoordsDirty && inSpatialChannel()) - { - 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["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) (mCameraPosition[0] * 100); + root["lp"]["y"] = (int) (mCameraPosition[1] * 100); + root["lp"]["z"] = (int) (mCameraPosition[2] * 100); + root["lh"] = Json::objectValue; + root["lh"]["x"] = (int) (mCameraRot[0] * 100); + root["lh"]["y"] = (int) (mCameraRot[1] * 100); + root["lh"]["z"] = (int) (mCameraRot[2] * 100); + root["lh"]["w"] = (int) (mCameraRot[3] * 100); + + mSpatialCoordsDirty = false; + } - - root["lp"] = Json::objectValue; - root["lp"]["x"] = (int)(mCameraPosition[0]*100); - root["lp"]["y"] = (int)(mCameraPosition[1]*100); - root["lp"]["z"] = (int)(mCameraPosition[2]*100); - root["lh"] = Json::objectValue; - root["lh"]["x"] = (int)(mCameraRot[0]*100); - root["lh"]["y"] = (int)(mCameraRot[1]*100); - root["lh"]["z"] = (int)(mCameraRot[2]*100); - root["lh"]["w"] = (int)(mCameraRot[3]*100); + F32 audio_level = 0.0; - mSpatialCoordsDirty = false; - } - - - F32 audio_level = 0.0; - - if (!mMuteMic) - { - audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); - } - uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); - if (uint_audio_level != mAudioLevel) + if (!mMuteMic) + { + audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); + } + 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) { - root["p"] = uint_audio_level; - mAudioLevel = uint_audio_level; - participantStatePtr_t participant = findParticipantByID(gAgentID); - if (participant) - { - participant->mPower = audio_level; - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } + participant->mPower = audio_level; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } - + } + return root; +} + +void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate() +{ + + + if (mWebRTCDataInterface && mWebRTCAudioInterface) + { + Json::Value root = getPositionAndVolumeUpdateJson(false); if (root.size() > 0) { -- cgit v1.2.3 From 84f6d30afa86b9ff2a273ce98a00667e81506a1e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 28 Sep 2023 15:33:42 -0700 Subject: when a peer joins, notify them of position and heading --- indra/newview/llvoicewebrtc.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index ccaa02b704..d97d4847ee 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2514,6 +2514,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) 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); @@ -2525,6 +2526,7 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) participantStatePtr_t participant = findParticipantByID(agent_id); if (!participant && voice_data[participant_id].get("j", Json::Value(false)).asBool()) { + new_participant = true; participant = addParticipantByID(agent_id); } if (participant) @@ -2538,9 +2540,16 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) 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; + participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } } + if (new_participant) + { + Json::FastWriter writer; + Json::Value root = getPositionAndVolumeUpdateJson(true); + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); + } } } @@ -4493,7 +4502,7 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(findParticipantByID(id)); if (participant) { - result = participant->mPower * 3.5; + result = participant->mPower * 3.0; } return result; } -- cgit v1.2.3 From 35b4c7633e01e727a8ac75eedf32fc3810935617 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 29 Sep 2023 01:40:20 -0700 Subject: tweak position transmission decision --- indra/newview/llvoicewebrtc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d97d4847ee..8962b4de09 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2524,9 +2524,10 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) continue; } participantStatePtr_t participant = findParticipantByID(agent_id); - if (!participant && voice_data[participant_id].get("j", Json::Value(false)).asBool()) + bool joined = voice_data[participant_id].get("j", Json::Value(false)).asBool(); + new_participant |= joined; + if (!participant && joined) { - new_participant = true; participant = addParticipantByID(agent_id); } if (participant) -- cgit v1.2.3 From 8bd96753113a95aaa8c92fe78713498cbc42a437 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 29 Sep 2023 19:13:03 -0700 Subject: Improve reconnection logic and allow device setting when connected or not connected --- indra/newview/llvoicewebrtc.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 8962b4de09..b9e5c7a8b2 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -884,6 +884,7 @@ bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) LLSD body; body["logout"] = TRUE; httpAdapter->postAndSuspend(httpRequest, url, body); + mWebRTCSignalingInterface->shutdownConnection(); return true; } @@ -1265,6 +1266,7 @@ bool LLWebRTCVoiceClient::waitForChannel() if (getVoiceControlState() == VOICE_STATE_SESSION_RETRY) { + mIsProcessingChannels = false; return true; } @@ -2107,7 +2109,7 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return mWebRTCDeviceInterface->getAudioLevel(); + return mWebRTCDeviceInterface->getTuningAudioLevel(); } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -2200,7 +2202,7 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) if (!mMuteMic) { - audio_level = (F32) mWebRTCDeviceInterface->getAudioLevel(); + audio_level = (F32) mWebRTCAudioInterface->getAudioLevel(); } uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); if (force || (uint_audio_level != mAudioLevel)) @@ -2564,6 +2566,8 @@ 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); } @@ -4503,7 +4507,7 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(findParticipantByID(id)); if (participant) { - result = participant->mPower * 3.0; + result = participant->mPower * 2.0; } return result; } -- cgit v1.2.3 From 6b29ced7071baf3fcf5fa2f12fd92cd2cbeda9a8 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 3 Oct 2023 15:51:37 -0700 Subject: fix device selection while speaking. --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b9e5c7a8b2..8d929fd36a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2479,7 +2479,6 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * mWebRTCAudioInterface = audio_interface; mWebRTCAudioInterface->setAudioObserver(this); float speaker_volume = 0; - audio_interface->setMute(true); { LLMutexLock lock(&mVoiceStateMutex); speaker_volume = mSpeakerVolume; @@ -4872,6 +4871,7 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const LLWebRTCVoiceCli void LLWebRTCVoiceClient::sessionEstablished() { + mWebRTCAudioInterface->setMute(mMuteMic); addSession(gAgent.getRegion()->getRegionID().asString()); } -- cgit v1.2.3 From f98e714f60367322f41f6f81a9f68f34bddf03bb Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 3 Oct 2023 16:04:32 -0700 Subject: hook up listen from avatar vs camera --- indra/newview/llvoicewebrtc.cpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 8d929fd36a..3584fa31b2 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2175,6 +2175,27 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) if ((mSpatialCoordsDirty || force) && inSpatialChannel()) { + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earRot = mCameraRot; + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earRot = mAvatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earRot = mCameraRot; + break; + } + root["sp"] = Json::objectValue; root["sp"]["x"] = (int) (mAvatarPosition[0] * 100); root["sp"]["y"] = (int) (mAvatarPosition[1] * 100); @@ -2186,14 +2207,14 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) root["sh"]["w"] = (int) (mAvatarRot[3] * 100); root["lp"] = Json::objectValue; - root["lp"]["x"] = (int) (mCameraPosition[0] * 100); - root["lp"]["y"] = (int) (mCameraPosition[1] * 100); - root["lp"]["z"] = (int) (mCameraPosition[2] * 100); + 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) (mCameraRot[0] * 100); - root["lh"]["y"] = (int) (mCameraRot[1] * 100); - root["lh"]["z"] = (int) (mCameraRot[2] * 100); - root["lh"]["w"] = (int) (mCameraRot[3] * 100); + 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); mSpatialCoordsDirty = false; } -- cgit v1.2.3 From a12e23f349b782ab669e218da8e1af10d9408996 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 6 Oct 2023 21:46:02 -0700 Subject: Fix race in initialization. Fix failure to send ice candidates to janus. --- indra/newview/llvoicewebrtc.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3584fa31b2..657f587370 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -99,6 +99,7 @@ namespace { const F32 CONNECT_DNS_TIMEOUT = 5.0f; const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; + const S32 PROVISION_WAIT_TIMEOUT_SEC = 5; // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); @@ -587,6 +588,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() LLCoros::set_consuming(true); U32 retry = 0; + U32 provisionWaitTimeout = 0; setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); @@ -653,11 +655,16 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } 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: @@ -2424,16 +2431,17 @@ void LLWebRTCVoiceClient::processIceUpdates() if (mIceCandidates.size()) { - LLSD body; - + LLSD candidates = LLSD::emptyArray(); + body["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; - body["candidates"].append(body_candidate); + candidates.append(body_candidate); } + body["candidates"] = candidates; mIceCandidates.clear(); } else if (mIceCompleted) @@ -2566,13 +2574,6 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; } } - if (new_participant) - { - Json::FastWriter writer; - Json::Value root = getPositionAndVolumeUpdateJson(true); - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); - } } } -- cgit v1.2.3 From a7b70788a8f7c3cf607242851e6f65aaf446515b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 19 Oct 2023 14:50:12 -0700 Subject: quicker turnaround on re-establishing voice when server goes down. --- indra/newview/llvoicewebrtc.cpp | 108 +++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 45 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 657f587370..0aa69dc7e8 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -616,6 +616,10 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() } else { + LLMutexLock lock(&mVoiceStateMutex); + + mTrickling = false; + mIceCompleted = false; setVoiceControlStateUnless(VOICE_STATE_START_SESSION, VOICE_STATE_SESSION_RETRY); } break; @@ -673,7 +677,7 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() { // We failed to connect, give it a bit time before retrying. retry++; - F32 full_delay = llmin(5.f * (F32) retry, 60.f); + 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; @@ -686,11 +690,12 @@ void LLWebRTCVoiceClient::voiceControlStateMachine() llcoro::suspendUntilTimeout(1.f); } } - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceControlStateUnless(VOICE_STATE_DISCONNECT); break; case VOICE_STATE_SESSION_ESTABLISHED: { + retry = 0; if (mTuningMode) { performMicTuning(); @@ -2388,11 +2393,20 @@ void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserve { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - if (state == llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE) - { - LLMutexLock lock(&mVoiceStateMutex); - mIceCompleted = true; - } + 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; + } + } } void LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) @@ -2425,53 +2439,55 @@ void LLWebRTCVoiceClient::processIceUpdates() 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; { LLMutexLock lock(&mVoiceStateMutex); - if (mIceCandidates.size()) + if (!mTrickling) { - LLSD candidates = LLSD::emptyArray(); - body["candidates"] = LLSD::emptyArray(); - for (auto &ice_candidate : mIceCandidates) + if (mIceCandidates.size()) + { + LLSD candidates = LLSD::emptyArray(); + body["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["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - candidates.append(body_candidate); + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + iceCompleted = mIceCompleted; + mIceCompleted = false; } - body["candidates"] = candidates; - mIceCandidates.clear(); - } - else if (mIceCompleted) - { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - mIceCompleted = false; - } - else - { - return; + 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; } } - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, _1), - boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, 3, url, body, _1)); } -void LLWebRTCVoiceClient::onIceUpdateComplete(const LLSD& result) -{ - if (sShuttingDown) - { - return; - } -} +void LLWebRTCVoiceClient::onIceUpdateComplete(bool ice_completed, const LLSD& result) +{ mTrickling = false; } -void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result) +void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result) { if (sShuttingDown) { @@ -2482,16 +2498,18 @@ void LLWebRTCVoiceClient::onIceUpdateError(int retries, std::string url, LLSD bo if (retries >= 0) { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + 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, _1), - boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, _1)); + boost::bind(&LLWebRTCVoiceClient::onIceUpdateComplete, this, ice_completed, _1), + boost::bind(&LLWebRTCVoiceClient::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); } else { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; + setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); + mTrickling = false; } } -- cgit v1.2.3 From 39ce52f8976d10a20aa06f7567855e232cb32be5 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 24 Oct 2023 10:13:03 -0700 Subject: OSX build fix --- indra/newview/llvoicewebrtc.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0aa69dc7e8..b4d65bec0a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2406,6 +2406,8 @@ void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserve LLMutexLock lock(&mVoiceStateMutex); mIceCompleted = false; } + default: + break; } } -- cgit v1.2.3 From 2d20fbbb16a39eb3123b51c4aa3dc6df23474e00 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 8 Nov 2023 10:13:15 -0800 Subject: SL-20543 - voice over region boundaries. This commit includes code to allow the llwebrtc.dll/dylib to allow multiple connections at once. --- indra/newview/llvoicewebrtc.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b4d65bec0a..4fe2c78167 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -307,7 +307,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mIsCoroutineActive(false), mWebRTCPump("WebRTCClientPump"), mWebRTCDeviceInterface(nullptr), - mWebRTCSignalingInterface(nullptr), + mWebRTCPeerConnection(nullptr), mWebRTCAudioInterface(nullptr) { sShuttingDown = false; @@ -364,11 +364,8 @@ void LLWebRTCVoiceClient::init(LLPumpIO *pump) mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); mWebRTCDeviceInterface->setDevicesObserver(this); - mWebRTCSignalingInterface = llwebrtc::getSignalingInterface(); - mWebRTCSignalingInterface->setSignalingObserver(this); - - mWebRTCDataInterface = llwebrtc::getDataInterface(); - mWebRTCDataInterface->setDataObserver(this); + mWebRTCPeerConnection = llwebrtc::newPeerConnection(); + mWebRTCPeerConnection->setSignalingObserver(this); } void LLWebRTCVoiceClient::terminate() @@ -500,7 +497,7 @@ void LLWebRTCVoiceClient::setLoginInfo( const std::string& channel_sdp) { mRemoteChannelSDP = channel_sdp; - mWebRTCSignalingInterface->AnswerAvailable(channel_sdp); + mWebRTCPeerConnection->AnswerAvailable(channel_sdp); if(mAccountLoggedIn) { @@ -862,7 +859,7 @@ bool LLWebRTCVoiceClient::establishVoiceConnection() { return false; } - return mWebRTCSignalingInterface->initializeConnection(); + return mWebRTCPeerConnection->initializeConnection(); } bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) @@ -896,7 +893,7 @@ bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) LLSD body; body["logout"] = TRUE; httpAdapter->postAndSuspend(httpRequest, url, body); - mWebRTCSignalingInterface->shutdownConnection(); + mWebRTCPeerConnection->shutdownConnection(); return true; } @@ -1136,11 +1133,14 @@ bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession addParticipantByID(gAgent.getID()); // tell peers that this participant has joined. - Json::FastWriter writer; - Json::Value root = getPositionAndVolumeUpdateJson(true); - root["j"] = true; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + 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); @@ -2235,7 +2235,7 @@ Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) if (!mMuteMic) { - audio_level = (F32) mWebRTCAudioInterface->getAudioLevel(); + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); } uint32_t uint_audio_level = mMuteMic ? 0 : (uint32_t) (audio_level * 128); if (force || (uint_audio_level != mAudioLevel)) @@ -2451,7 +2451,6 @@ void LLWebRTCVoiceClient::processIceUpdates() if (mIceCandidates.size()) { LLSD candidates = LLSD::emptyArray(); - body["candidates"] = LLSD::emptyArray(); for (auto &ice_candidate : mIceCandidates) { LLSD body_candidate; @@ -2532,7 +2531,7 @@ void LLWebRTCVoiceClient::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface * LLMutexLock lock(&mVoiceStateMutex); speaker_volume = mSpeakerVolume; } - audio_interface->setSpeakerVolume(mSpeakerVolume); + mWebRTCDeviceInterface->setSpeakerVolume(mSpeakerVolume); setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); } @@ -2597,9 +2596,10 @@ void LLWebRTCVoiceClient::OnDataReceived(const std::string& data, bool binary) } } -void LLWebRTCVoiceClient::OnDataChannelReady() +void LLWebRTCVoiceClient::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - + mWebRTCDataInterface = data_interface; + mWebRTCDataInterface->setDataObserver(this); } @@ -4470,7 +4470,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) } if (mWebRTCAudioInterface) { - mWebRTCAudioInterface->setSpeakerVolume(volume); + mWebRTCDeviceInterface->setSpeakerVolume(volume); } } } -- cgit v1.2.3 From bce9e50cd38b8a22d4f20d950a686668d217592d Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 9 Nov 2023 18:19:18 -0800 Subject: Remove a bunch of unnecessary code. --- indra/newview/llvoicewebrtc.cpp | 2480 ++------------------------------------- 1 file changed, 84 insertions(+), 2396 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 4fe2c78167..93efd2526d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -131,12 +131,6 @@ static int scale_mic_volume(float volume) /////////////////////////////////////////////////////////////////////////////////////////////// -class LLWebRTCVoiceClientMuteListObserver : public LLMuteListObserver -{ - /* virtual */ void onChange() { LLWebRTCVoiceClient::getInstance()->muteListChanged();} -}; - - void LLVoiceWebRTCStats::reset() { mStartTime = -1.0f; @@ -227,10 +221,6 @@ LLSD LLVoiceWebRTCStats::read() return stats; } -static LLWebRTCVoiceClientMuteListObserver mutelist_listener; -static bool sMuteListListener_listening = false; - - /////////////////////////////////////////////////////////////////////////////////////////////// bool LLWebRTCVoiceClient::sShuttingDown = false; @@ -285,14 +275,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mLipSyncEnabled(false), - mVoiceFontsReceived(false), - mVoiceFontsNew(false), - mVoiceFontListDirty(false), - - mCaptureBufferMode(false), - mCaptureBufferRecording(false), - mCaptureBufferRecorded(false), - mCaptureBufferPlaying(false), mShutdownComplete(true), mPlayRequestCount(0), @@ -390,8 +372,6 @@ void LLWebRTCVoiceClient::cleanUp() LL_DEBUGS("Voice") << LL_ENDL; deleteAllSessions(); - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -418,55 +398,6 @@ void LLWebRTCVoiceClient::updateSettings() setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); } -///////////////////////////// -// utility functions - -bool LLWebRTCVoiceClient::writeString(const std::string &str) -{ - bool result = false; - LL_DEBUGS("LowVoice") << "sending:\n" << str << LL_ENDL; - - if(sConnected) - { - apr_status_t err; - apr_size_t size = (apr_size_t)str.size(); - apr_size_t written = size; - - //MARK: Turn this on to log outgoing XML - // LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; - - // check return code - sockets will fail (broken, etc.) - err = apr_socket_send( - mSocket->getSocket(), - (const char*)str.data(), - &written); - - if(err == 0 && written == size) - { - // Success. - result = true; - } - else if (err == 0 && written != size) { - // Did a short write, log it for now - LL_WARNS("Voice") << ") short write on socket sending data to WebRTC daemon." << "Sent " << written << "bytes instead of " << size <getRegionID().asString()); } void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) @@ -903,13 +837,6 @@ bool LLWebRTCVoiceClient::loginToWebRTC() mIsLoggedIn = true; notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - // Set up the mute list observer if it hasn't been set up already. - if ((!sMuteListListener_listening)) - { - LLMuteList::getInstance()->addObserver(&mutelist_listener); - sMuteListListener_listening = true; - } - // Set the initial state of mic mute, local speaker volume, etc. sendLocalAudioUpdates(); mIsLoggingIn = false; @@ -998,6 +925,8 @@ bool LLWebRTCVoiceClient::requestParcelVoiceInfo() 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"]; @@ -1050,7 +979,7 @@ bool LLWebRTCVoiceClient::requestParcelVoiceInfo() // set the spatial channel. If no voice credentials or uri are // available, then we simply drop out of voice spatially. - return !setSpatialChannel(uri, credentials); + return !setSpatialChannel(uri, ""); } bool LLWebRTCVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) @@ -1158,24 +1087,6 @@ bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) { if (!mAudioSession->mHandle.empty()) { - -#if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/WebRTCrecording"); - { - time_t now = time(NULL); - const size_t BUF_SIZE = 64; - char time_str[BUF_SIZE]; /* Flawfinder: ignore */ - - strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); - savepath += time_str; - } - recordingLoopSave(savepath); -#endif - - sessionMediaDisconnectSendMessage(mAudioSession); - if (wait) { LLSD result; @@ -1304,10 +1215,6 @@ bool LLWebRTCVoiceClient::waitForChannel() { performMicTuning(); } - else if (mCaptureBufferMode) - { - recordingAndPlaybackMode(); - } else if (checkParcelChanged() || (mNextAudioSession == NULL)) { // the parcel is changed, or we have no pending audio sessions, @@ -1399,7 +1306,6 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) } notifyParticipantObservers(); - notifyVoiceFontObservers(); LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); @@ -1465,13 +1371,6 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) } sendPositionAndVolumeUpdate(); - // Do notifications for expiring Voice Fonts. - if (mVoiceFontExpiryTimer.hasExpired()) - { - expireVoiceFonts(); - mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); - } - // send any requests to adjust mic and speaker settings if they have changed sendLocalAudioUpdates(); @@ -1537,112 +1436,6 @@ bool LLWebRTCVoiceClient::runSession(const sessionStatePtr_t &session) return true; } -void LLWebRTCVoiceClient::recordingAndPlaybackMode() -{ - LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL; - - while (true) - { - LLSD command; - do - { - command = llcoro::suspendUntilEventOn(mWebRTCPump); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(command) << LL_ENDL; - } while (!command.has("recplay")); - - if (command["recplay"].asString() == "quit") - { - mCaptureBufferMode = false; - break; - } - else if (command["recplay"].asString() == "record") - { - voiceRecordBuffer(); - } - else if (command["recplay"].asString() == "playback") - { - voicePlaybackBuffer(); - } - } - - LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL; - mCaptureBufferRecording = false; - mCaptureBufferRecorded = false; - mCaptureBufferPlaying = false; - - return; -} - -int LLWebRTCVoiceClient::voiceRecordBuffer() -{ - LLSD timeoutResult(LLSDMap("recplay", "stop")); - - LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; - - LLSD result; - - captureBufferRecordStartSendMessage(); - notifyVoiceFontObservers(); - - do - { - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - } while (!result.has("recplay")); - - mCaptureBufferRecorded = true; - - captureBufferRecordStopSendMessage(); - mCaptureBufferRecording = false; - - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); - - return true; - /*TODO expand return to move directly into play*/ -} - -int LLWebRTCVoiceClient::voicePlaybackBuffer() -{ - LLSD timeoutResult(LLSDMap("recplay", "stop")); - - LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL; - - LLSD result; - - do - { - captureBufferPlayStartSendMessage(mPreviewVoiceFont); - - // Store the voice font being previewed, so that we know to restart if it changes. - mPreviewVoiceFontLast = mPreviewVoiceFont; - - do - { - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); - - result = llcoro::suspendUntilEventOnWithTimeout(mWebRTCPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; - } while (!result.has("recplay")); - - if (result["recplay"] == "playback") - continue; // restart playback... May be a font change. - - break; - } while (true); - - // Stop playing. - captureBufferPlayStopSendMessage(); - mCaptureBufferPlaying = false; - - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); - - return true; -} - - bool LLWebRTCVoiceClient::performMicTuning() { LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; @@ -1663,191 +1456,11 @@ void LLWebRTCVoiceClient::closeSocket(void) mAccountLoggedIn = false; } -void LLWebRTCVoiceClient::loginSendMessage() -{ - std::ostringstream stream; - - bool autoPostCrashDumps = gSavedSettings.getBOOL("WebRTCAutoPostCrashDumps"); - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "" << mAccountName << "" - << "" << mAccountPassword << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "VerifyAnswer" - << "false" - << "0" - << "Application" - << "5" - << (autoPostCrashDumps?"true":"") - << "\n\n\n"; - - LL_INFOS("Voice") << "Attempting voice login" << LL_ENDL; - writeString(stream.str()); -} - void LLWebRTCVoiceClient::logout() { // Ensure that we'll re-request provisioning before logging in again mAccountPassword.clear(); - - logoutSendMessage(); -} - -void LLWebRTCVoiceClient::logoutSendMessage() -{ - if(mAccountLoggedIn) - { - LL_INFOS("Voice") << "Attempting voice logout" << LL_ENDL; - std::ostringstream stream; - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - mAccountLoggedIn = false; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::sessionGroupCreateSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "Normal" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI - << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" - << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"Session.Create.1\">" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" << session->mSIPURI << ""; - - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~"; - - if(!session->mHash.empty()) - { - stream - << "" << LLURI::escape(session->mHash, allowed_chars) << "" - << "SHA1UserName"; - } - - stream - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << font_index << "" - << "" << mChannelName << "" - << "\n\n\n"; - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) -{ - LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; - - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::string password; - if(!session->mHash.empty()) - { - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~" - ; - password = LLURI::escape(session->mHash, allowed_chars); - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mSIPURI << "" - << "" << mChannelName << "" - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << font_index << "" - << "" << password << "" - << "SHA1UserName" - << "\n\n\n" - ; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t &session) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle - << " with voice font: " << session->mVoiceFontID << " (" << font_index << ")" - << LL_ENDL; - - session->mMediaConnectInProgress = true; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.MediaConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "" << font_index << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.TextConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); + mAccountLoggedIn = false; } void LLWebRTCVoiceClient::sessionTerminate() @@ -1868,27 +1481,7 @@ void LLWebRTCVoiceClient::leaveAudioSession() { LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - if(!mAudioSession->mHandle.empty()) - { - -#if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/WebRTCrecording"); - { - time_t now = time(NULL); - const size_t BUF_SIZE = 64; - char time_str[BUF_SIZE]; /* Flawfinder: ignore */ - - strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); - savepath += time_str; - } - recordingLoopSave(savepath); -#endif - - sessionMediaDisconnectSendMessage(mAudioSession); - } - else + if(mAudioSession->mHandle.empty()) { LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; } @@ -1900,55 +1493,6 @@ void LLWebRTCVoiceClient::leaveAudioSession() sessionTerminate(); } -void LLWebRTCVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &session) -{ - std::ostringstream stream; - - sessionGroupTerminateSendMessage(session); - return; - /* - LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); - */ -} - -void LLWebRTCVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session) -{ - std::ostringstream stream; - sessionGroupTerminateSendMessage(session); - return; - /* - LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); - */ - -} - void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; @@ -2046,58 +1590,6 @@ bool LLWebRTCVoiceClient::inTuningMode() return mIsInTuningMode; } -void LLWebRTCVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{ - mTuningAudioFile = name; - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "" << (loop?"1":"0") << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::tuningRenderStopSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::tuningCaptureStartSendMessage(int loop) -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "-1" - << "" << loop << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::tuningCaptureStopSendMessage() -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); - - mTuningEnergy = 0.0f; -} - void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { int scaled_volume = scale_mic_volume(volume); @@ -2320,73 +1812,6 @@ void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } -/** - * Because of the recurring voice cutout issues (SL-15072) we are going to try - * to disable the automatic VAD (Voice Activity Detection) and set the associated - * parameters directly. We will expose them via Debug Settings and that should - * let us iterate on a collection of values that work for us. Hopefully! - * - * From the WebRTC Docs: - * - * VadAuto: A flag indicating if the automatic VAD is enabled (1) or disabled (0) - * - * VadHangover: The time (in milliseconds) that it takes - * for the VAD to switch back to silence from speech mode after the last speech - * frame has been detected. - * - * VadNoiseFloor: A dimensionless value between 0 and - * 20000 (default 576) that controls the maximum level at which the noise floor - * may be set at by the VAD's noise tracking. Too low of a value will make noise - * tracking ineffective (A value of 0 disables noise tracking and the VAD then - * relies purely on the sensitivity property). Too high of a value will make - * long speech classifiable as noise. - * - * VadSensitivity: A dimensionless value between 0 and - * 100, indicating the 'sensitivity of the VAD'. Increasing this value corresponds - * to decreasing the sensitivity of the VAD (i.e. '0' is most sensitive, - * while 100 is 'least sensitive') - */ -void LLWebRTCVoiceClient::setupVADParams(unsigned int vad_auto, - unsigned int vad_hangover, - unsigned int vad_noise_floor, - unsigned int vad_sensitivity) -{ - std::ostringstream stream; - - LL_INFOS("Voice") << "Setting the automatic VAD to " - << (vad_auto ? "True" : "False") - << " and discrete values to" - << " VadHangover = " << vad_hangover - << ", VadSensitivity = " << vad_sensitivity - << ", VadNoiseFloor = " << vad_noise_floor - << LL_ENDL; - - // Create a request to set the VAD parameters: - stream << "" - << "" << vad_auto << "" - << "" << vad_hangover << "" - << "" << vad_sensitivity << "" - << "" << vad_noise_floor << "" - << "\n\n\n"; - - if (!stream.str().empty()) - { - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::onVADSettingsChange() -{ - // pick up the VAD variables (one of which was changed) - unsigned int vad_auto = gSavedSettings.getU32("WebRTCVadAuto"); - unsigned int vad_hangover = gSavedSettings.getU32("WebRTCVadHangover"); - unsigned int vad_noise_floor = gSavedSettings.getU32("WebRTCVadNoiseFloor"); - unsigned int vad_sensitivity = gSavedSettings.getU32("WebRTCVadSensitivity"); - - // build a VAD params change request and send it to SLVoice - setupVADParams(vad_auto, vad_hangover, vad_noise_floor, vad_sensitivity); -} - ///////////////////////////// // WebRTC Signaling Handlers void LLWebRTCVoiceClient::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) @@ -2612,339 +2037,22 @@ void LLWebRTCVoiceClient::OnRenegotiationNeeded() setVoiceControlStateUnless(VOICE_STATE_SESSION_RETRY); } -///////////////////////////// -// Response/Event handlers - -void LLWebRTCVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) -{ - LLSD result = LLSD::emptyMap(); - - if(statusCode == 0) +void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) { - // Connector created, move forward. - if (connectorHandle == LLWebRTCSecurity::getInstance()->connectorHandle()) - { - LL_INFOS("Voice") << "Voice connector succeeded, WebRTC SDK version is " << versionID << " connector handle " << connectorHandle << LL_ENDL; - mVoiceVersion.serverVersion = versionID; - mConnectorEstablished = true; - mTerminateDaemon = false; - - result["connector"] = LLSD::Boolean(true); - } - else - { - // This shouldn't happen - we are somehow out of sync with SLVoice - // or possibly there are two things trying to run SLVoice at once - // or someone is trying to hack into it. - LL_WARNS("Voice") << "Connector returned wrong handle " - << "(" << connectorHandle << ")" - << " expected (" << LLWebRTCSecurity::getInstance()->connectorHandle() << ")" - << LL_ENDL; - result["connector"] = LLSD::Boolean(false); - // Give up. - mTerminateDaemon = true; - } - } - else if (statusCode == 10028) // web request timeout prior to login - { - // this is usually fatal, but a long timeout might work - result["connector"] = LLSD::Boolean(false); - result["retry"] = LLSD::Real(CONNECT_ATTEMPT_TIMEOUT); - - LL_WARNS("Voice") << "Voice connection failed" << LL_ENDL; - } - else if (statusCode == 10006) // name resolution failure - a shorter retry may work - { - // some networks have slower DNS, but a short timeout might let it catch up - result["connector"] = LLSD::Boolean(false); - result["retry"] = LLSD::Real(CONNECT_DNS_TIMEOUT); - - LL_WARNS("Voice") << "Voice connection DNS lookup failed" << LL_ENDL; - } - else // unknown failure - give up - { - LL_WARNS("Voice") << "Voice connection failure ("<< statusCode << "): " << statusString << LL_ENDL; - mTerminateDaemon = true; - result["connector"] = LLSD::Boolean(false); - } - - mWebRTCPump.post(result); -} + sessionStatePtr_t oldSession = mAudioSession; -void LLWebRTCVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) -{ - LLSD result = LLSD::emptyMap(); + mAudioSession = session; + mAudioSessionChanged = true; - LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; - - // Status code of 20200 means "bad password". We may want to special-case that at some point. - - if ( statusCode == HTTP_UNAUTHORIZED ) - { - // Login failure which is probably caused by the delay after a user's password being updated. - LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - result["login"] = LLSD::String("retry"); - } - else if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - result["login"] = LLSD::String("failed"); + // The old session may now need to be deleted. + reapSession(oldSession); } - else - { - // Login succeeded, move forward. - mAccountLoggedIn = true; - mNumberOfAliases = numberOfAliases; - result["login"] = LLSD::String("response_ok"); - } - - mWebRTCPump.post(result); - -} - -void LLWebRTCVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{ - sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId)); - - if(session) - { - session->mCreateInProgress = false; - } - - if(statusCode != 0) - { - LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "failed") - ("reason", LLSD::Integer(statusCode))); - - mWebRTCPump.post(WebRTCevent); - } - else - { - reapSession(session); - } - } - } - else - { - LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "created")); - - mWebRTCPump.post(WebRTCevent); - } -} - -void LLWebRTCVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{ - sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId)); - - if(session) - { - session->mCreateInProgress = false; - } - - if(statusCode != 0) - { - LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "failed")); - - mWebRTCPump.post(WebRTCevent); - } - else - { - reapSession(session); - } - } - } - else - { - LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } - - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(sessionHandle)) - ("session", "added")); - - mWebRTCPump.post(WebRTCevent); - - } -} - -void LLWebRTCVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) -{ - sessionStatePtr_t session(findSession(requestId)); - // 1026 is session already has media, somehow mediaconnect was called twice on the same session. - // set the session info to reflect that the user is already connected. - if (statusCode == 1026) - { - session->mVoiceActive = true; - session->mMediaConnectInProgress = false; - session->mMediaStreamState = streamStateConnected; - //session->mTextStreamState = streamStateConnected; - session->mErrorStatusCode = 0; - } - else if (statusCode != 0) - { - LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; - if (session) - { - session->mMediaConnectInProgress = false; - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - } - } - else - { - LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::logoutResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } - LLSD WebRTCevent(LLSDMap("logout", LLSD::Boolean(true))); - - mWebRTCPump.post(WebRTCevent); -} - -void LLWebRTCVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } - - sConnected = false; - mShutdownComplete = true; - - LLSD WebRTCevent(LLSDMap("connector", LLSD::Boolean(false))); - - mWebRTCPump.post(WebRTCevent); -} - -void LLWebRTCVoiceClient::sessionAddedEvent( - std::string &uriString, - std::string &alias, - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool isChannel, - bool incoming, - std::string &nameString, - std::string &applicationString) -{ - sessionStatePtr_t session; - - LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; - - session = addSession(uriString, sessionHandle); - if(session) - { - session->mGroupHandle = sessionGroupHandle; - session->mIsChannel = isChannel; - session->mIncoming = incoming; - session->mAlias = alias; - - // Generate a caller UUID -- don't need to do this for channels - if(!session->mIsChannel) - { - if(IDFromName(session->mSIPURI, session->mCallerID)) - { - // Normal URI(base64-encoded UUID) - } - else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) - { - // Wrong URI, but an alias is available. Stash the incoming URI as an alternate - session->mAlternateSIPURI = session->mSIPURI; - - // and generate a proper URI from the ID. - setSessionURI(session, session->mCallerID.asString()); - } - else - { - LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; - session->mCallerID.generate(session->mSIPURI); - session->mSynthesizedCallerID = true; - - // Can't look up the name in this case -- we have to extract it from the URI. - std::string namePortion = nameString; - - // Some incoming names may be separated with an underscore instead of a space. Fix this. - LLStringUtil::replaceChar(namePortion, '_', ' '); - - // Act like we just finished resolving the name (this stores it in all the right places) - avatarNameResolved(session->mCallerID, namePortion); - } - - LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; - - if(!session->mSynthesizedCallerID) - { - // If we got here, we don't have a proper name. Initiate a lookup. - lookupName(session->mCallerID); - } - } - } -} - -void LLWebRTCVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) -{ - LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; - -#if USE_SESSION_GROUPS - if(mMainSessionGroupHandle.empty()) - { - // This is the first (i.e. "main") session group. Save its handle. - mMainSessionGroupHandle = sessionGroupHandle; - } - else - { - LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; - } -#endif -} - -void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; - if(mAudioSession != session) - { - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(mIsJoiningSession) + + // This is the session we're joining. + if(mIsJoiningSession) { LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) ("session", "joined")); @@ -2975,53 +2083,12 @@ void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::sessionRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle) -{ - LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; - - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - leftAudioSession(session); - - // This message invalidates the session's handle. Set it to empty. - clearSessionHandle(session); - - // This also means that the session's session group is now empty. - // Terminate the session group so it doesn't leak. - sessionGroupTerminateSendMessage(session); - - // Reset the media state (we now have no info) - session->mMediaStreamState = streamStateUnknown; - //session->mTextStreamState = streamStateUnknown; - - // Conditionally delete the session - reapSession(session); - } - else - { - // Already reaped this session. - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; - } - -} - void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) { - if(session->mCreateInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; - } - else if(session->mMediaConnectInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; - } - else if(session == mAudioSession) + if(session == mAudioSession) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; } @@ -3077,526 +2144,79 @@ void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::accountLoginStateChangeEvent( - std::string &accountHandle, - int statusCode, - std::string &statusString, - int state) -{ - LLSD levent = LLSD::emptyMap(); - - /* - According to Mike S., status codes for this event are: - login_state_logged_out=0, - login_state_logged_in = 1, - login_state_logging_in = 2, - login_state_logging_out = 3, - login_state_resetting = 4, - login_state_error=100 - */ - - LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; - switch(state) - { - case 1: - levent["login"] = LLSD::String("account_login"); - - mWebRTCPump.post(levent); - break; - case 2: - break; - - case 3: - levent["login"] = LLSD::String("account_loggingOut"); - - mWebRTCPump.post(levent); - break; - case 4: - break; - - case 100: - LL_WARNS("Voice") << "account state event error" << LL_ENDL; - break; - - case 0: - levent["login"] = LLSD::String("account_logout"); - - mWebRTCPump.post(levent); - break; - - default: - //Used to be a commented out warning - LL_WARNS("Voice") << "unknown account state event: " << state << LL_ENDL; - break; - } -} -void LLWebRTCVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +void LLWebRTCVoiceClient::muteListChanged() { - LLSD result; - - if (mediaCompletionType == "AuxBufferAudioCapture") - { - mCaptureBufferRecording = false; - result["recplay"] = "end"; - } - else if (mediaCompletionType == "AuxBufferAudioRender") + // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. + if(mAudioSession) { - // Ignore all but the last stop event - if (--mPlayRequestCount <= 0) + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) { - mCaptureBufferPlaying = false; - result["recplay"] = "end"; -// result["recplay"] = "done"; - } - } - else - { - LL_WARNS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + participantStatePtr_t p(iter->second); + + // Check to see if this participant is on the mute list already + if(p->updateMuteState()) + mAudioSession->mVolumeDirty = true; + } } +} - if (!result.isUndefined()) - mWebRTCPump.post(result); +///////////////////////////// +// Managing list of participants +LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : + mURI(agent_id.asString()), + mAvatarID(agent_id), + mPTT(false), + mIsSpeaking(false), + mIsModeratorMuted(false), + mLastSpokeTimestamp(0.f), + mPower(0.f), + mVolume(LLVoiceClient::VOLUME_DEFAULT), + mUserVolume(0), + mOnMuteList(false), + mVolumeSet(false), + mVolumeDirty(false), + mAvatarIDValid(false), + mIsSelf(false) +{ } -void LLWebRTCVoiceClient::mediaStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - int statusCode, - std::string &statusString, - int state, - bool incoming) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) { - sessionStatePtr_t session(findSession(sessionHandle)); - - LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; + participantStatePtr_t result; - if(session) + participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); + + + if (iter != mParticipantsByUUID.end()) { - // We know about this session + result = iter->second; + } - // Save the state for later use - session->mMediaStreamState = state; + if(!result) + { + // participant isn't already in one list or the other. + result.reset(new participantState(agent_id)); + mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); + mParticipantsChanged = true; + + result->mAvatarIDValid = true; + result->mAvatarID = agent_id; - switch(statusCode) - { - case 0: - case HTTP_OK: - // generic success - // Don't change the saved error code (it may have been set elsewhere) - break; - default: - // save the status code for later - session->mErrorStatusCode = statusCode; - break; - } + if(result->updateMuteState()) + { + mMuteDirty = true; + } + + mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); - switch(state) + if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) { - case streamStateDisconnecting: - case streamStateIdle: - // Standard "left audio session", WebRTC state 'disconnected' - session->mVoiceActive = false; - session->mMediaConnectInProgress = false; - leftAudioSession(session); - break; - - case streamStateConnected: - session->mVoiceActive = true; - session->mMediaConnectInProgress = false; - joinedAudioSession(session); - case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. - break; - - case streamStateRinging: - if(incoming) - { - // Send the voice chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? - session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); - session->mVoiceInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } - - } - else - { - // session disconnectintg and disconnected events arriving after we have already left the session. - LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL; - } -} - -void LLWebRTCVoiceClient::participantAddedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString, - std::string &displayNameString, - int participantType) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - participantStatePtr_t participant(session->addParticipant(LLUUID(uriString))); - if(participant) - { - participant->mAccountName = nameString; - - LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - - if(participant->mAvatarIDValid) - { - // Initiate a lookup - lookupName(participant->mAvatarID); - } - else - { - // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. - std::string namePortion = displayNameString; - - if(namePortion.empty()) - { - // Problems with both of the above, fall back to the account name - namePortion = nameString; - } - - // Set the display name (which is a hint to the active speakers window not to do its own lookup) - participant->mDisplayName = namePortion; - avatarNameResolved(participant->mAvatarID, namePortion); - } - } - } -} - -void LLWebRTCVoiceClient::participantRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - participantStatePtr_t participant(session->findParticipant(uriString)); - if(participant) - { - session->removeParticipant(participant); - } - else - { - LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; - } - } - else - { - // a late arriving event on a session we have already left. - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::messageEvent( - std::string &sessionHandle, - std::string &uriString, - std::string &alias, - std::string &messageHeader, - std::string &messageBody, - std::string &applicationString) -{ - LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; -// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; - - LL_INFOS("Voice") << "WebRTC raw message:" << std::endl << messageBody << LL_ENDL; - - if(messageHeader.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos) - { - std::string message; - - { - const std::string startMarker = ", try looking for a instead. - start = messageBody.find(startSpan); - start = messageBody.find(startMarker2, start); - end = messageBody.find(endSpan); - - if(start != std::string::npos) - { - start += startMarker2.size(); - - if(end != std::string::npos) - end -= start; - - message.assign(messageBody, start, end); - } - } - } - -// LL_DEBUGS("Voice") << " raw message = \n" << message << LL_ENDL; - - // strip formatting tags - { - std::string::size_type start; - std::string::size_type end; - - while((start = message.find('<')) != std::string::npos) - { - if((end = message.find('>', start + 1)) != std::string::npos) - { - // Strip out the tag - message.erase(start, (end + 1) - start); - } - else - { - // Avoid an infinite loop - break; - } - } - } - - // Decode ampersand-escaped chars - { - std::string::size_type mark = 0; - - // The text may contain text encoded with <, >, and & - mark = 0; - while((mark = message.find("<", mark)) != std::string::npos) - { - message.replace(mark, 4, "<"); - mark += 1; - } - - mark = 0; - while((mark = message.find(">", mark)) != std::string::npos) - { - message.replace(mark, 4, ">"); - mark += 1; - } - - mark = 0; - while((mark = message.find("&", mark)) != std::string::npos) - { - message.replace(mark, 5, "&"); - mark += 1; - } - } - - // strip leading/trailing whitespace (since we always seem to get a couple newlines) - LLStringUtil::trim(message); - -// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL; - - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - bool is_do_not_disturb = gAgent.isDoNotDisturb(); - bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); - bool is_linden = LLMuteList::isLinden(session->mName); - LLChat chat; - - chat.mMuted = is_muted && !is_linden; - - if(!chat.mMuted) - { - chat.mFromID = session->mCallerID; - chat.mFromName = session->mName; - chat.mSourceType = CHAT_SOURCE_AGENT; - - if(is_do_not_disturb && !is_linden) - { - // TODO: Question: Return do not disturb mode response here? Or maybe when session is started instead? - } - - LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; - LLIMMgr::getInstance()->addMessage(session->mIMSessionID, - session->mCallerID, - session->mName.c_str(), - message.c_str(), - false, - LLStringUtil::null, // default arg - IM_NOTHING_SPECIAL, // default arg - 0, // default arg - LLUUID::null, // default arg - LLVector3::zero); // default arg - } - } - } -} - -void LLWebRTCVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - - if(session) - { - participantStatePtr_t participant(session->findParticipant(uriString)); - if(participant) - { - if (!stricmp(notificationType.c_str(), "Typing")) - { - // Other end started typing - // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). - // It requires some info for the message, which we don't have here. - } - else if (!stricmp(notificationType.c_str(), "NotTyping")) - { - // Other end stopped typing - // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). - // It requires some info for the message, which we don't have here. - } - else - { - LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id) -{ - // We don't generally need to process this. However, one occurence is when we first connect, and so it is the - // earliest opportunity to learn what we're connected to. - if (statusCode) - { - LL_WARNS("Voice") << "VoiceServiceConnectionStateChangedEvent statusCode: " << statusCode << - "statusString: " << statusString << LL_ENDL; - return; - } - if (build_id.empty()) - { - return; - } - mVoiceVersion.mBuildVersion = build_id; -} - -void LLWebRTCVoiceClient::auxAudioPropertiesEvent(F32 energy) -{ - LL_DEBUGS("VoiceEnergy") << "got energy " << energy << LL_ENDL; - mTuningEnergy = energy; -} - -void LLWebRTCVoiceClient::muteListChanged() -{ - // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - if(mAudioSession) - { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) - { - participantStatePtr_t p(iter->second); - - // Check to see if this participant is on the mute list already - if(p->updateMuteState()) - mAudioSession->mVolumeDirty = true; - } - } -} - -///////////////////////////// -// Managing list of participants -LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : - mURI(agent_id.asString()), - mAvatarID(agent_id), - mPTT(false), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLastSpokeTimestamp(0.f), - mPower(0.f), - mVolume(LLVoiceClient::VOLUME_DEFAULT), - mUserVolume(0), - mOnMuteList(false), - mVolumeSet(false), - mVolumeDirty(false), - mAvatarIDValid(false), - mIsSelf(false) -{ -} - -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) -{ - participantStatePtr_t result; - - participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); - - - if (iter != mParticipantsByUUID.end()) - { - result = iter->second; - } - - if(!result) - { - // participant isn't already in one list or the other. - result.reset(new participantState(agent_id)); - mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); - mParticipantsChanged = true; - - result->mAvatarIDValid = true; - result->mAvatarID = agent_id; - - if(result->updateMuteState()) - { - mMuteDirty = true; - } - - mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); - - if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) - { - result->mVolumeDirty = true; - mVolumeDirty = true; + result->mVolumeDirty = true; + mVolumeDirty = true; } LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; @@ -3894,7 +2514,6 @@ bool LLWebRTCVoiceClient::switchChannel( LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; mNextAudioSession = addSession(uri); - mNextAudioSession->mHash = hash; mNextAudioSession->mIsSpatial = spatial; mNextAudioSession->mReconnect = !no_reconnect; mNextAudioSession->mIsP2P = is_p2p; @@ -3931,11 +2550,9 @@ void LLWebRTCVoiceClient::setNonSpatialChannel( } bool LLWebRTCVoiceClient::setSpatialChannel( - const std::string &uri, - const std::string &credentials) + const std::string &uri, const std::string &credentials) { mSpatialSessionURI = uri; - mSpatialSessionCredentials = credentials; mAreaVoiceDisabled = mSpatialSessionURI.empty(); LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; @@ -3948,7 +2565,7 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { - return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + return switchChannel(mSpatialSessionURI, true, false, false); } } @@ -4003,13 +2620,7 @@ BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) BOOL result = TRUE; sessionStatePtr_t session(findSession(id)); - if(session) - { - // this is a p2p session with the indicated caller, or the session with the specified UUID. - if(session->mSynthesizedCallerID) - result = FALSE; - } - else + if(!session) { // Didn't find a matching session -- check the current audio session for a matching participant if(mAudioSession) @@ -4058,11 +2669,6 @@ BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) void LLWebRTCVoiceClient::declineInvite(std::string &sessionHandle) { - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - sessionMediaDisconnectSendMessage(session); - } } void LLWebRTCVoiceClient::leaveNonSpatialChannel() @@ -4643,108 +3249,6 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() return mAreaVoiceDisabled; } -void LLWebRTCVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; - - if(!mMainSessionGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << deltaFramesPerControlFrame << "" - << "" << "" << "" - << "false" - << "" << seconds << "" - << "\n\n\n"; - - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::recordingLoopSave(const std::string& filename) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Flush" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::recordingStop() -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::filePlaybackStart(const std::string& filename) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::filePlaybackStop() -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::filePlaybackSetPaused(bool paused) -{ - // TODO: Implement once WebRTC gives me a sample -} - -void LLWebRTCVoiceClient::filePlaybackSetMode(bool vox, float speed) -{ - // TODO: Implement once WebRTC gives me a sample -} - //------------------------------------------------------------------------ std::set LLWebRTCVoiceClient::sessionState::mSession; @@ -4752,11 +3256,6 @@ std::set LLWebRTCVoiceClient::session LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), mMediaStreamState(streamStateUnknown), - mCreateInProgress(false), - mMediaConnectInProgress(false), - mVoiceInvitePending(false), - mTextInvitePending(false), - mSynthesizedCallerID(false), mIsChannel(false), mIsSpatial(false), mIsP2P(false), @@ -4796,13 +3295,13 @@ bool LLWebRTCVoiceClient::sessionState::isCallBackPossible() // This may change to be explicitly specified by WebRTC in the future... // Currently, only PSTN P2P calls cannot be returned. // Conveniently, this is also the only case where we synthesize a caller UUID. - return !mSynthesizedCallerID; + return false; } bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() { // This may change to be explicitly specified by WebRTC in the future... - return !mSynthesizedCallerID; + return false; } @@ -4820,20 +3319,6 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matc return result; } -/*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri) -{ - sessionStatePtr_t result; - - // *TODO: My kingdom for a lambda! - std::set::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCreatingURI, _1, uri)); - - if (it != mSession.end()) - result = (*it).lock(); - - return result; -} - /*static*/ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByURI(const std::string &uri) { @@ -4880,7 +3365,7 @@ bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceCli { ptr_t aLock(a.lock()); - return aLock ? (aLock->mCreateInProgress && (aLock->mSIPURI == uri)) : false; + return aLock ? (aLock->mSIPURI == uri) : false; } bool LLWebRTCVoiceClient::sessionState::testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) @@ -4929,13 +3414,6 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const st return result; } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) -{ - sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); - - return result; -} - LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findSession(const LLUUID &participant_id) { sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); @@ -5305,27 +3783,6 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se { // this session's "caller ID" just resolved. Fill in the name. session->mName = name; - if (session->mTextInvitePending) - { - session->mTextInvitePending = false; - - // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. - } - if (session->mVoiceInvitePending) - { - session->mVoiceInvitePending = false; - - LLIMMgr::getInstance()->inviteToSession( - session->mIMSessionID, - session->mName, - session->mCallerID, - session->mName, - IM_SESSION_P2P_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE, - session->mHandle, - session->mSIPURI); - } - } } @@ -5334,775 +3791,6 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); } -bool LLWebRTCVoiceClient::setVoiceEffect(const LLUUID& id) -{ - if (!mAudioSession) - { - return false; - } - - if (!id.isNull()) - { - if (mVoiceFontMap.empty()) - { - LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; - return false; - } - else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) - { - LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; - return false; - } - } - - // *TODO: Check for expired fonts? - mAudioSession->mVoiceFontID = id; - - // *TODO: Separate voice font defaults for spatial chat and IM? - gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); - - sessionSetVoiceFontSendMessage(mAudioSession); - notifyVoiceFontObservers(); - - return true; -} - -const LLUUID LLWebRTCVoiceClient::getVoiceEffect() -{ - return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; -} - -LLSD LLWebRTCVoiceClient::getVoiceEffectProperties(const LLUUID& id) -{ - LLSD sd; - - voice_font_map_t::iterator iter = mVoiceFontMap.find(id); - if (iter != mVoiceFontMap.end()) - { - sd["template_only"] = false; - } - else - { - // Voice effect is not in the voice font map, see if there is a template - iter = mVoiceFontTemplateMap.find(id); - if (iter == mVoiceFontTemplateMap.end()) - { - LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; - return sd; - } - sd["template_only"] = true; - } - - voiceFontEntry *font = iter->second; - sd["name"] = font->mName; - sd["expiry_date"] = font->mExpirationDate; - sd["is_new"] = font->mIsNew; - - return sd; -} - -LLWebRTCVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : - mID(id), - mFontIndex(0), - mFontType(VOICE_FONT_TYPE_NONE), - mFontStatus(VOICE_FONT_STATUS_NONE), - mIsNew(false) -{ - mExpiryTimer.stop(); - mExpiryWarningTimer.stop(); -} - -LLWebRTCVoiceClient::voiceFontEntry::~voiceFontEntry() -{ -} - -void LLWebRTCVoiceClient::refreshVoiceEffectLists(bool clear_lists) -{ - if (clear_lists) - { - mVoiceFontsReceived = false; - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); - } - - accountGetSessionFontsSendMessage(); - accountGetTemplateFontsSendMessage(); -} - -const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectList() const -{ - return mVoiceFontList; -} - -const voice_effect_list_t& LLWebRTCVoiceClient::getVoiceEffectTemplateList() const -{ - return mVoiceFontTemplateList; -} - -void LLWebRTCVoiceClient::addVoiceFont(const S32 font_index, - const std::string &name, - const std::string &description, - const LLDate &expiration_date, - bool has_expired, - const S32 font_type, - const S32 font_status, - const bool template_font) -{ - // WebRTC SessionFontIDs are not guaranteed to remain the same between - // sessions or grids so use a UUID for the name. - - // If received name is not a UUID, fudge one by hashing the name and type. - LLUUID font_id; - if (LLUUID::validate(name)) - { - font_id = LLUUID(name); - } - else - { - font_id.generate(STRINGIZE(font_type << ":" << name)); - } - - voiceFontEntry *font = NULL; - - voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; - voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; - - // Check whether we've seen this font before. - voice_font_map_t::iterator iter = font_map.find(font_id); - bool new_font = (iter == font_map.end()); - - // Override the has_expired flag if we have passed the expiration_date as a double check. - if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) - { - has_expired = true; - } - - if (has_expired) - { - LL_DEBUGS("VoiceFont") << "Expired " << (template_font ? "Template " : "") - << expiration_date.asString() << " " << font_id - << " (" << font_index << ") " << name << LL_ENDL; - - // Remove existing session fonts that have expired since we last saw them. - if (!new_font && !template_font) - { - deleteVoiceFont(font_id); - } - return; - } - - if (new_font) - { - // If it is a new font create a new entry. - font = new voiceFontEntry(font_id); - } - else - { - // Not a new font, update the existing entry - font = iter->second; - } - - if (font) - { - font->mFontIndex = font_index; - // Use the description for the human readable name if available, as the - // "name" may be a UUID. - font->mName = description.empty() ? name : description; - font->mFontType = font_type; - font->mFontStatus = font_status; - - // If the font is new or the expiration date has changed the expiry timers need updating. - if (!template_font && (new_font || font->mExpirationDate != expiration_date)) - { - font->mExpirationDate = expiration_date; - - // Set the expiry timer to trigger a notification when the voice font can no longer be used. - font->mExpiryTimer.start(); - font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); - - // Set the warning timer to some interval before actual expiry. - S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); - if (warning_time != 0) - { - font->mExpiryWarningTimer.start(); - F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); - font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); - } - else - { - // Disable the warning timer. - font->mExpiryWarningTimer.stop(); - } - - // Only flag new session fonts after the first time we have fetched the list. - if (mVoiceFontsReceived) - { - font->mIsNew = true; - mVoiceFontsNew = true; - } - } - - LL_DEBUGS("VoiceFont") << (template_font ? "Template " : "") - << font->mExpirationDate.asString() << " " << font->mID - << " (" << font->mFontIndex << ") " << name << LL_ENDL; - - if (new_font) - { - font_map.insert(voice_font_map_t::value_type(font->mID, font)); - font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); - } - - mVoiceFontListDirty = true; - - // Debugging stuff - - if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) - { - LL_WARNS("VoiceFont") << "Unknown voice font type: " << font_type << LL_ENDL; - } - if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) - { - LL_WARNS("VoiceFont") << "Unknown voice font status: " << font_status << LL_ENDL; - } - } -} - -void LLWebRTCVoiceClient::expireVoiceFonts() -{ - // *TODO: If we are selling voice fonts in packs, there are probably - // going to be a number of fonts with the same expiration time, so would - // be more efficient to just keep a list of expiration times rather - // than checking each font individually. - - bool have_expired = false; - bool will_expire = false; - bool expired_in_use = false; - - LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) - { - voiceFontEntry* voice_font = iter->second; - LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; - LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; - - // Check for expired voice fonts - if (expiry_timer.getStarted() && expiry_timer.hasExpired()) - { - // Check whether it is the active voice font - if (voice_font->mID == current_effect) - { - // Reset to no voice effect. - setVoiceEffect(LLUUID::null); - expired_in_use = true; - } - - LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; - deleteVoiceFont(voice_font->mID); - have_expired = true; - } - - // Check for voice fonts that will expire in less that the warning time - if (warning_timer.getStarted() && warning_timer.hasExpired()) - { - LL_DEBUGS("VoiceFont") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; - will_expire = true; - warning_timer.stop(); - } - } - - LLSD args; - args["URL"] = LLTrans::getString("voice_morphing_url"); - args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); - - // Give a notification if any voice fonts have expired. - if (have_expired) - { - if (expired_in_use) - { - LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); - } - else - { - LLNotificationsUtil::add("VoiceEffectsExpired", args); - } - - // Refresh voice font lists in the UI. - notifyVoiceFontObservers(); - } - - // Give a warning notification if any voice fonts are due to expire. - if (will_expire) - { - S32Seconds seconds(gSavedSettings.getS32("VoiceEffectExpiryWarningTime")); - args["INTERVAL"] = llformat("%d", LLUnit(seconds).value()); - - LLNotificationsUtil::add("VoiceEffectsWillExpire", args); - } -} - -void LLWebRTCVoiceClient::deleteVoiceFont(const LLUUID& id) -{ - // Remove the entry from the voice font list. - voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); - while (list_iter != mVoiceFontList.end()) - { - if (list_iter->second == id) - { - LL_DEBUGS("VoiceFont") << "Removing " << id << " from the voice font list." << LL_ENDL; - list_iter = mVoiceFontList.erase(list_iter); - mVoiceFontListDirty = true; - } - else - { - ++list_iter; - } - } - - // Find the entry in the voice font map and erase its data. - voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); - if (map_iter != mVoiceFontMap.end()) - { - delete map_iter->second; - } - - // Remove the entry from the voice font map. - mVoiceFontMap.erase(map_iter); -} - -void LLWebRTCVoiceClient::deleteAllVoiceFonts() -{ - mVoiceFontList.clear(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) - { - delete iter->second; - } - mVoiceFontMap.clear(); -} - -void LLWebRTCVoiceClient::deleteVoiceFontTemplates() -{ - mVoiceFontTemplateList.clear(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) - { - delete iter->second; - } - mVoiceFontTemplateMap.clear(); -} - -S32 LLWebRTCVoiceClient::getVoiceFontIndex(const LLUUID& id) const -{ - S32 result = 0; - if (!id.isNull()) - { - voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); - if (it != mVoiceFontMap.end()) - { - result = it->second->mFontIndex; - } - else - { - LL_WARNS("VoiceFont") << "Selected voice font " << id << " is not available." << LL_ENDL; - } - } - return result; -} - -S32 LLWebRTCVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const -{ - S32 result = 0; - if (!id.isNull()) - { - voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); - if (it != mVoiceFontTemplateMap.end()) - { - result = it->second->mFontIndex; - } - else - { - LL_WARNS("VoiceFont") << "Selected voice font template " << id << " is not available." << LL_ENDL; - } - } - return result; -} - -void LLWebRTCVoiceClient::accountGetSessionFontsSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("VoiceFont") << "Requesting voice font list." << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::accountGetTemplateFontsSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("VoiceFont") << "Requesting voice font template list." << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("VoiceFont") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "" - << "" << session->mHandle << "" - << "" << font_index << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLWebRTCVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) -{ - if (mIsWaitingForFonts) - { - // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after - // receiving the last one. - LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true))); - - mWebRTCPump.post(result); - } - notifyVoiceFontObservers(); - mVoiceFontsReceived = true; -} - -void LLWebRTCVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) -{ - // Voice font list entries were updated via addVoiceFont() during parsing. - notifyVoiceFontObservers(); -} -void LLWebRTCVoiceClient::addObserver(LLVoiceEffectObserver* observer) -{ - mVoiceFontObservers.insert(observer); -} - -void LLWebRTCVoiceClient::removeObserver(LLVoiceEffectObserver* observer) -{ - mVoiceFontObservers.erase(observer); -} - -// method checks the item in VoiceMorphing menu for appropriate current voice font -bool LLWebRTCVoiceClient::onCheckVoiceEffect(const std::string& voice_effect_name) -{ - LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); - if (NULL != effect_interfacep) - { - const LLUUID& currect_voice_effect_id = effect_interfacep->getVoiceEffect(); - - if (currect_voice_effect_id.isNull()) - { - if (voice_effect_name == "NoVoiceMorphing") - { - return true; - } - } - else - { - const LLSD& voice_effect_props = effect_interfacep->getVoiceEffectProperties(currect_voice_effect_id); - if (voice_effect_props["name"].asString() == voice_effect_name) - { - return true; - } - } - } - - return false; -} - -// method changes voice font for selected VoiceMorphing menu item -void LLWebRTCVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name) -{ - LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); - if (NULL != effect_interfacep) - { - if (voice_effect_name == "NoVoiceMorphing") - { - effect_interfacep->setVoiceEffect(LLUUID()); - return; - } - const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); - if (!effect_list.empty()) - { - for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) - { - if (voice_effect_name == it->first) - { - effect_interfacep->setVoiceEffect(it->second); - return; - } - } - } - } -} - -// it updates VoiceMorphing menu items in accordance with purchased properties -void LLWebRTCVoiceClient::updateVoiceMorphingMenu() -{ - if (mVoiceFontListDirty) - { - LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface(); - if (effect_interfacep) - { - const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList(); - if (!effect_list.empty()) - { - LLMenuGL * voice_morphing_menup = gMenuBarView->findChildMenuByName("VoiceMorphing", TRUE); - - if (NULL != voice_morphing_menup) - { - S32 items = voice_morphing_menup->getItemCount(); - if (items > 0) - { - voice_morphing_menup->erase(1, items - 3, false); - - S32 pos = 1; - for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) - { - LLMenuItemCheckGL::Params p; - p.name = it->first; - p.label = it->first; - p.on_check.function(boost::bind(&LLWebRTCVoiceClient::onCheckVoiceEffect, this, it->first)); - p.on_click.function(boost::bind(&LLWebRTCVoiceClient::onClickVoiceEffect, this, it->first)); - LLMenuItemCheckGL * voice_effect_itemp = LLUICtrlFactory::create(p); - voice_morphing_menup->insert(pos++, voice_effect_itemp, false); - } - - voice_morphing_menup->needsArrange(); - } - } - } - } - } -} -void LLWebRTCVoiceClient::notifyVoiceFontObservers() -{ - LL_DEBUGS("VoiceFont") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; - - updateVoiceMorphingMenu(); - - for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); - it != mVoiceFontObservers.end();) - { - LLVoiceEffectObserver* observer = *it; - observer->onVoiceEffectChanged(mVoiceFontListDirty); - // In case onVoiceEffectChanged() deleted an entry. - it = mVoiceFontObservers.upper_bound(observer); - } - mVoiceFontListDirty = false; - - // If new Voice Fonts have been added notify the user. - if (mVoiceFontsNew) - { - if (mVoiceFontsReceived) - { - LLNotificationsUtil::add("VoiceEffectsNew"); - } - mVoiceFontsNew = false; - } -} - -void LLWebRTCVoiceClient::enablePreviewBuffer(bool enable) -{ - LLSD result; - mCaptureBufferMode = enable; - - if (enable) - result["recplay"] = "start"; - else - result["recplay"] = "quit"; - - mWebRTCPump.post(result); - - if(mCaptureBufferMode && mIsInChannel) - { - LL_DEBUGS("Voice") << "no channel" << LL_ENDL; - sessionTerminate(); - } -} - -void LLWebRTCVoiceClient::recordPreviewBuffer() -{ - if (!mCaptureBufferMode) - { - LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; - mCaptureBufferRecording = false; - return; - } - - mCaptureBufferRecording = true; - - LLSD result(LLSDMap("recplay", "record")); - mWebRTCPump.post(result); -} - -void LLWebRTCVoiceClient::playPreviewBuffer(const LLUUID& effect_id) -{ - if (!mCaptureBufferMode) - { - LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; - mCaptureBufferRecording = false; - return; - } - - if (!mCaptureBufferRecorded) - { - // Can't play until we have something recorded! - mCaptureBufferPlaying = false; - return; - } - - mPreviewVoiceFont = effect_id; - mCaptureBufferPlaying = true; - - LLSD result(LLSDMap("recplay", "playback")); - mWebRTCPump.post(result); -} - -void LLWebRTCVoiceClient::stopPreviewBuffer() -{ - mCaptureBufferRecording = false; - mCaptureBufferPlaying = false; - - LLSD result(LLSDMap("recplay", "quit")); - mWebRTCPump.post(result); -} - -bool LLWebRTCVoiceClient::isPreviewRecording() -{ - return (mCaptureBufferMode && mCaptureBufferRecording); -} - -bool LLWebRTCVoiceClient::isPreviewPlaying() -{ - return (mCaptureBufferMode && mCaptureBufferPlaying); -} - -void LLWebRTCVoiceClient::captureBufferRecordStartSendMessage() -{ if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; - - // Start capture - stream - << "" - << "" - << "\n\n\n"; - - // Unmute the mic - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "false" - << "\n\n\n"; - - // Dirty the mute mic state so that it will get reset when we finishing previewing - mMuteMicDirty = true; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::captureBufferRecordStopSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; - - // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. - stream << "" - << "" << LLWebRTCSecurity::getInstance()->connectorHandle() << "" - << "true" - << "\n\n\n"; - - // Stop capture - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) -{ - if(mAccountLoggedIn) - { - // Track how may play requests are sent, so we know how many stop events to - // expect before play actually stops. - ++mPlayRequestCount; - - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; - - S32 font_index = getVoiceFontTemplateIndex(voice_font_id); - LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" << font_index << "" - << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLWebRTCVoiceClient::captureBufferPlayStopSendMessage() -{ - if(mAccountLoggedIn) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; - - stream - << "" - << "" << LLWebRTCSecurity::getInstance()->accountHandle() << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } -- cgit v1.2.3 From d302d89891475d1431099deac99bc52a2c3046a1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 30 Nov 2023 13:14:07 -0800 Subject: Refactor/clean-up WebRTC voice to handle multiple voice streams This is useful for cross-region voice, quick voice switching, etc. --- indra/newview/llvoicewebrtc.cpp | 3252 ++++++++++++++------------------------- 1 file changed, 1196 insertions(+), 2056 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') 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,52 +388,46 @@ void LLWebRTCVoiceClient::updateSettings() ///////////////////////////// // session control messages -void LLWebRTCVoiceClient::connectorCreate() -{ +void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) +{ + session->OnConnectionEstablished(channelID); } -void LLWebRTCVoiceClient::connectorShutdown() +void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID) { - - mShutdownComplete = true; + session->OnConnectionFailure(channelID); } -void LLWebRTCVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) { + sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); +} - mAccountDisplayName = user_id; - - LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - - mAccountName = nameFromID(agentID); +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) +{ + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + mAudioSession = mNextAudioSession; + mNextAudioSession.reset(); + } + sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); } -void LLWebRTCVoiceClient::setLoginInfo( - const std::string& account_name, - const std::string& password, - const std::string& channel_sdp) +void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::string& channelID) { - mRemoteChannelSDP = channel_sdp; - mWebRTCPeerConnection->AnswerAvailable(channel_sdp); + if (channelID == mPrimaryConnectionID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } +} - 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; - } +void LLWebRTCVoiceClient::sessionState::OnConnectionFailure(const std::string &channelID) +{ + if (channelID == mPrimaryConnectionID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } } void LLWebRTCVoiceClient::idle(void* user_data) @@ -460,1609 +441,449 @@ void LLWebRTCVoiceClient::idle(void* user_data) // // -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 - { - // 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::predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +{ + session->processSessionStates(); } -void LLWebRTCVoiceClient::voiceControlStateMachine() +void LLWebRTCVoiceClient::sessionState::processSessionStates() { - if (sShuttingDown) + std::map::iterator iter; + for (iter = mWebRTCConnections.begin(); iter != mWebRTCConnections.end();) { - return; + if (!iter->second->connectionStateMachine()) + { + iter = mWebRTCConnections.erase(iter); + } + else + { + ++iter; + } } +} +void LLWebRTCVoiceClient::voiceConnectionCoro() +{ LL_DEBUGS("Voice") << "starting" << LL_ENDL; mIsCoroutineActive = true; LLCoros::set_consuming(true); - - U32 retry = 0; - U32 provisionWaitTimeout = 0; - - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); - - do + try { - 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()) + while (!sShuttingDown) { - 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: + // add session for region or parcel voice. + LLViewerRegion *regionp = gAgent.getRegion(); + if (!regionp) { - 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; + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + continue; } - 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: + if (regionp->getRegionID().isNull()) { - retry = 0; - if (mTuningMode) - { - performMicTuning(); - } - sessionEstablished(); - setVoiceControlStateUnless(VOICE_STATE_WAIT_FOR_CHANNEL, VOICE_STATE_SESSION_RETRY); + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + continue; } - break; - - case VOICE_STATE_WAIT_FOR_CHANNEL: + if (!mAudioSession || mAudioSession->mIsSpatial) { - 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; + // check to see if parcel changed. + std::string channelID = regionp->getRegionID().asString(); - case VOICE_STATE_WAIT_FOR_EXIT: - if (mVoiceEnabled) + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + S32 parcel_local_id = INVALID_PARCEL_ID; + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID && !parcel->getParcelFlagUseEstateVoiceChannel()) { - LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; - setVoiceControlStateUnless(VOICE_STATE_TP_WAIT); + parcel_local_id = parcel->getLocalID(); + channelID += "-" + std::to_string(parcel->getLocalID()); } - else + if (!mAudioSession || channelID != mAudioSession->mChannelID) { - llcoro::suspendUntilTimeout(1.0); + setSpatialChannel(channelID, "", parcel_local_id); } - break; - - default: - { - LL_WARNS("Voice") << "Unknown voice control state " << getVoiceControlState() << LL_ENDL; - break; } + updatePosition(); + sessionState::for_each(boost::bind(predProcessSessionStates, _1)); + sendPositionAndVolumeUpdate(true); + updateOwnVolume(); + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); } - } while (true); -} - -bool LLWebRTCVoiceClient::callbackEndDaemon(const LLSD& data) -{ - if (!sShuttingDown && mVoiceEnabled) + } + catch (const LLCoros::Stop&) { - 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; + LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL; } - return false; -} - -bool LLWebRTCVoiceClient::provisionVoiceAccount() -{ - LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; - - while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) + catch (const LLContinueError&) { - LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - // *TODO* Pump a message for wake up. - llcoro::suspend(); + 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; } - std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); + cleanUp(); +} - LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; +bool LLWebRTCVoiceClient::performMicTuning() +{ + LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); - LLSD body; - LLSD jsep; - jsep["type"] = "offer"; - { - LLMutexLock lock(&mVoiceStateMutex); - jsep["sdp"] = mChannelSDP; - } - body["jsep"] = jsep; + mIsInTuningMode = false; - 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(); - } - 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); +void LLWebRTCVoiceClient::sessionTerminate() +{ + mSessionTerminateRequested = true; +} - // switch to the default region channel. - switchChannel(gAgent.getRegion()->getRegionID().asString()); +void LLWebRTCVoiceClient::requestRelog() +{ + mSessionTerminateRequested = true; + mRelogRequested = true; } -void LLWebRTCVoiceClient::OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result) + +void LLWebRTCVoiceClient::leaveAudioSession() { - if (sShuttingDown) + if(mAudioSession) { - return; + LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mChannelID << LL_ENDL; } - 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 - { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying." << LL_ENDL; - } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + } + sessionTerminate(); } -bool LLWebRTCVoiceClient::establishVoiceConnection() +void LLWebRTCVoiceClient::clearCaptureDevices() { - 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 "<initializeConnection(); + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mCaptureDevices.clear(); } -bool LLWebRTCVoiceClient::breakVoiceConnection(bool corowait) +void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) { + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + mCaptureDevices.push_back(device); +} - LL_INFOS("Voice") << "Breaking voice account." << LL_ENDL; +LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() +{ + return mCaptureDevices; +} - while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) +void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) +{ + bool inTuningMode = mIsInTuningMode; + if (inTuningMode) { - LL_DEBUGS("Voice") << "no capabilities for voice breaking; waiting " << LL_ENDL; - // *TODO* Pump a message for wake up. - llcoro::suspend(); - } - - if (sShuttingDown) + tuningStop(); + } + mWebRTCDeviceInterface->setCaptureDevice(name); + if (inTuningMode) { - return false; + tuningStart(); } - - 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() +void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { - 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; + mDevicesListUpdated = state; } -void LLWebRTCVoiceClient::logoutOfWebRTC(bool wait) +void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) { - if (mIsLoggedIn) + clearRenderDevices(); + for (auto &device : render_devices) { - mAccountPassword.clear(); - breakVoiceConnection(wait); - // Ensure that we'll re-request provisioning before logging in again - mIsLoggedIn = false; + addRenderDevice(LLVoiceDevice(device.display_name, device.id)); + } + clearCaptureDevices(); + for (auto &device : capture_devices) + { + addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); } + setDevicesListUpdated(true); } -bool LLWebRTCVoiceClient::requestParcelVoiceInfo() -{ - //_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL; +void LLWebRTCVoiceClient::clearRenderDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mRenderDevices.clear(); +} + +void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) +{ + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + mRenderDevices.push_back(device); + +} + +LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() +{ + return mRenderDevices; +} + +void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) +{ + mWebRTCDeviceInterface->setRenderDevice(name); +} - LLViewerRegion * region = gAgent.getRegion(); - if (region == NULL || !region->capabilitiesReceived()) +void LLWebRTCVoiceClient::tuningStart() +{ + if (!mIsInTuningMode) { - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; - return false; + mWebRTCDeviceInterface->setTuningMode(true); + mIsInTuningMode = true; } +} - // grab the cap. - std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); - if (url.empty()) +void LLWebRTCVoiceClient::tuningStop() +{ + if (mIsInTuningMode) { - // Region dosn't have the cap. Stop probing. - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; - return false; + mWebRTCDeviceInterface->setTuningMode(false); + mIsInTuningMode = 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); +bool LLWebRTCVoiceClient::inTuningMode() +{ + return mIsInTuningMode; +} - LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD()); +void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) +{ + int scaled_volume = scale_mic_volume(volume); - if (sShuttingDown) - { - return false; - } + if(scaled_volume != mTuningMicVolume) + { + mTuningMicVolume = scaled_volume; + mTuningMicVolumeDirty = true; + } +} - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); +void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) +{ - 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 (volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = volume; + mTuningSpeakerVolumeDirty = true; + } +} + +float LLWebRTCVoiceClient::tuningGetEnergy(void) +{ + return mWebRTCDeviceInterface->getTuningAudioLevel(); +} - if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))) - { - if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) - { - LL_WARNS("Voice") << "Session terminated." << LL_ENDL; - } +bool LLWebRTCVoiceClient::deviceSettingsAvailable() +{ + bool result = true; + + if(mRenderDevices.empty() || mCaptureDevices.empty()) + result = false; + + return result; +} +bool LLWebRTCVoiceClient::deviceSettingsUpdated() +{ + bool updated = mDevicesListUpdated; + mDevicesListUpdated = false; + return updated; +} - LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL; - sessionTerminate(); - return false; - } +void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } + mWebRTCDeviceInterface->refreshDevices(); +} - std::string uri; - std::string credentials; +void LLWebRTCVoiceClient::giveUp() +{ + // All has failed. Clean up and stop trying. + LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; + cleanUp(); +} - LL_WARNS("Voice") << "Got voice credentials" << result << LL_ENDL; +void LLWebRTCVoiceClient::setHidden(bool hidden) +{ + mHidden = hidden; - if (result.has("voice_credentials")) + if (mHidden && inSpatialChannel()) { - 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; - } - } - } + // get out of the channel entirely + leaveAudioSession(); } else { - if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) - { - LL_WARNS("Voice") << "No voice credentials" << LL_ENDL; - } - else - { - LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL; - } + sendPositionAndVolumeUpdate(true); } - - // 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::sendPositionAndVolumeUpdate(bool force) { - mIsJoiningSession = true; + Json::FastWriter writer; + std::string spatial_data; + std::string volume_data; - sessionStatePtr_t oldSession = mAudioSession; - - LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL; + F32 audio_level = 0.0; + uint32_t uint_audio_level = 0.0; - mAudioSession = nextSession; - mAudioSessionChanged = true; - if (!mAudioSession || !mAudioSession->mReconnect) + if (!mMuteMic) { - mNextAudioSession.reset(); + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); + uint_audio_level = (uint32_t) (audio_level * 128); + } - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); + if (mSpatialCoordsDirty || force) + { + Json::Value spatial = Json::objectValue; + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earRot = mCameraRot; + break; - llcoro::suspend(); + case earLocAvatar: + earPosition = mAvatarPosition; + earRot = mAvatarRot; + break; - if (sShuttingDown) - { - return false; - } + case earLocMixed: + earPosition = mAvatarPosition; + earRot = mCameraRot; + break; + } - LLSD result; + 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); - 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; + mSpatialCoordsDirty = false; + if (force || (uint_audio_level != mAudioLevel)) + { + spatial["p"] = uint_audio_level; + } + spatial_data = writer.write(spatial); } - - // 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) + if (force || (uint_audio_level != mAudioLevel)) { - mSpatialJoiningNum++; + Json::Value volume = Json::objectValue; + volume["p"] = uint_audio_level; + volume_data = writer.write(volume); } + mAudioLevel = uint_audio_level; - 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) + sessionState::for_each(boost::bind(predSendData, _1, spatial_data, volume_data)); +} + +void LLWebRTCVoiceClient::updateOwnVolume() { + F32 audio_level = 0.0; + if (!mMuteMic) { - 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; - } - } + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); } - 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. + sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); +} - if (mWebRTCDataInterface) +void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level) +{ + participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); + if (participant) { - Json::FastWriter writer; - Json::Value root = getPositionAndVolumeUpdateJson(true); - root["j"] = true; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); + participant->mPower = audio_level; + participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; } - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - - return true; } -bool LLWebRTCVoiceClient::terminateAudioSession(bool wait) +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data) { - - if (mAudioSession) + if (session->mIsSpatial && !spatial_data.empty()) { - LL_INFOS("Voice") << "terminateAudioSession(" << wait << ") Terminating current voice session " << mAudioSession->mHandle << LL_ENDL; - - if (mIsLoggedIn) - { - if (!mAudioSession->mHandle.empty()) - { - 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); - - } - } - else - { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; - } - } - 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); + session->sendData(spatial_data); } - else + else if (!volume_data.empty()) { - LL_WARNS("Voice") << "terminateAudioSession(" << wait << ") with NULL mAudioSession" << LL_ENDL; + session->sendData(volume_data); } - - 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 +void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) { - 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; + for (auto& connection : mWebRTCConnections) + { + connection.second->sendData(data); + } +} -bool LLWebRTCVoiceClient::waitForChannel() +void LLWebRTCVoiceClient::sendLocalAudioUpdates() { - 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; - } +void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) + { + sessionStatePtr_t oldSession = mAudioSession; - processIceUpdates(); - switch (state) - { - case VOICE_CHANNEL_STATE_LOGIN: - if (!loginToWebRTC()) - { - return false; - } - state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING; - break; + mAudioSession = session; + mAudioSessionChanged = true; - 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 - { - LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL; - break; - } - else - { - LL_DEBUGS("Voice") - << "runSession returned true to inner loop" - << " RelogRequested=" << mRelogRequested - << " VoiceEnabled=" << mVoiceEnabled - << LL_ENDL; - } - } - - 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; - } - } - } 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) - { - 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; - } - - notifyParticipantObservers(); - - LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); - - mIsInChannel = true; - mMuteMicDirty = true; - - while (!sShuttingDown - && mVoiceEnabled - && !mSessionTerminateRequested - && !mTuningMode) - { - - 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; - } - } - } - - if (sShuttingDown) - { - return false; - } - - mIsInChannel = false; - LL_DEBUGS("Voice") << "terminating at end of runSession" << LL_ENDL; - terminateAudioSession(true); - - return true; -} - -bool LLWebRTCVoiceClient::performMicTuning() -{ - LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - - mIsInTuningMode = false; - - //--------------------------------------------------------------------- - return true; -} - -//========================================================================= - -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; -} - -void LLWebRTCVoiceClient::requestRelog() -{ - mSessionTerminateRequested = true; - mRelogRequested = true; -} - - -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; - } - } - else - { - LL_WARNS("Voice") << "called with no active session" << LL_ENDL; - } - sessionTerminate(); -} - -void LLWebRTCVoiceClient::clearCaptureDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mCaptureDevices.clear(); -} - -void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) -{ - LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; - mCaptureDevices.push_back(device); -} - -LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() -{ - return mCaptureDevices; -} - -void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) -{ - bool inTuningMode = mIsInTuningMode; - if (inTuningMode) - { - tuningStop(); - } - mWebRTCDeviceInterface->setCaptureDevice(name); - if (inTuningMode) - { - tuningStart(); - } -} -void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) -{ - mDevicesListUpdated = state; -} - -void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, - const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) -{ - clearRenderDevices(); - for (auto &device : render_devices) - { - addRenderDevice(LLVoiceDevice(device.display_name, device.id)); - } - clearCaptureDevices(); - for (auto &device : capture_devices) - { - addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); - } - setDevicesListUpdated(true); -} - -void LLWebRTCVoiceClient::clearRenderDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mRenderDevices.clear(); -} - -void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) -{ - LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; - mRenderDevices.push_back(device); - -} - -LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() -{ - return mRenderDevices; -} - -void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) -{ - mWebRTCDeviceInterface->setRenderDevice(name); -} - -void LLWebRTCVoiceClient::tuningStart() -{ - if (!mIsInTuningMode) - { - mWebRTCDeviceInterface->setTuningMode(true); - mIsInTuningMode = true; - } -} - -void LLWebRTCVoiceClient::tuningStop() -{ - if (mIsInTuningMode) - { - mWebRTCDeviceInterface->setTuningMode(false); - mIsInTuningMode = false; - } -} - -bool LLWebRTCVoiceClient::inTuningMode() -{ - return mIsInTuningMode; -} - -void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) -{ - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mTuningMicVolume) - { - mTuningMicVolume = scaled_volume; - mTuningMicVolumeDirty = true; - } -} - -void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{ - - if (volume != mTuningSpeakerVolume) - { - mTuningSpeakerVolume = volume; - mTuningSpeakerVolumeDirty = true; - } -} - -float LLWebRTCVoiceClient::tuningGetEnergy(void) -{ - return mWebRTCDeviceInterface->getTuningAudioLevel(); -} - -bool LLWebRTCVoiceClient::deviceSettingsAvailable() -{ - bool result = true; - - if(mRenderDevices.empty() || mCaptureDevices.empty()) - result = false; - - return result; -} -bool LLWebRTCVoiceClient::deviceSettingsUpdated() -{ - bool updated = mDevicesListUpdated; - mDevicesListUpdated = false; - return updated; -} - -void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) -{ - if(clearCurrentList) - { - clearCaptureDevices(); - clearRenderDevices(); - } - 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(); -} - -void LLWebRTCVoiceClient::setHidden(bool hidden) -{ - mHidden = hidden; - - if (mHidden && inSpatialChannel()) - { - // get out of the channel entirely - leaveAudioSession(); - } - else - { - sendPositionAndVolumeUpdate(); - } -} - -Json::Value LLWebRTCVoiceClient::getPositionAndVolumeUpdateJson(bool force) -{ - Json::Value root = Json::objectValue; - - if ((mSpatialCoordsDirty || force) && inSpatialChannel()) - { - LLVector3d earPosition; - LLQuaternion earRot; - switch (mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earRot = mCameraRot; - 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); - - 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) - { - participant->mPower = audio_level; - participant->mIsSpeaking = participant->mPower > SPEAKING_AUDIO_LEVEL; - } - } - return root; -} - -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate() -{ - - - if (mWebRTCDataInterface && mWebRTCAudioInterface) - { - Json::Value root = getPositionAndVolumeUpdateJson(false); - - if (root.size() > 0) - { - - Json::FastWriter writer; - std::string json_data = writer.write(root); - - mWebRTCDataInterface->sendData(json_data, false); - } - } - - - 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; - } - } - } -} - -void LLWebRTCVoiceClient::sendLocalAudioUpdates() -{ -} - -///////////////////////////// -// WebRTC Signaling Handlers -void LLWebRTCVoiceClient::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 LLWebRTCVoiceClient::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) -{ - LLMutexLock lock(&mVoiceStateMutex); - mIceCandidates.push_back(candidate); -} - -void LLWebRTCVoiceClient::processIceUpdates() -{ - 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; - { - 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; - } - } -} - -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) -{ - 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) - { - 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)); - } - 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; - { - LLMutexLock lock(&mVoiceStateMutex); - speaker_volume = mSpeakerVolume; - } - mWebRTCDeviceInterface->setSpeakerVolume(mSpeakerVolume); - setVoiceControlStateUnless(VOICE_STATE_SESSION_ESTABLISHED, VOICE_STATE_SESSION_RETRY); -} - -void LLWebRTCVoiceClient::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; - } - 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; - } - } - } -} - -void LLWebRTCVoiceClient::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) -{ - 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; - if(mAudioSession != session) - { - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(mIsJoiningSession) - { - LLSD WebRTCevent(LLSDMap("handle", LLSD::String(session->mHandle)) - ("session", "joined")); + // The old session may now need to be deleted. + reapSession(oldSession); + } + + // This is the session we're joining. + if(mIsJoiningSession) + { + LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) + ("session", "joined")); mWebRTCPump.post(WebRTCevent); 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::iterator it = mSession.begin(); - while (it != mSession.end()) + return; + /* + std::map::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::mSession; +std::map 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(); + + sessionState::ptr_t session(new sessionState()); + session->mChannelID = channelID; + session->mWebRTCConnections[channelID] = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); + session->mPrimaryConnectionID = channelID; - std::pair::iterator, bool> result = mSession.insert(ptr); + // add agent as participant + session->addParticipant(gAgentID); - if (result.second) - ptr->mMyIterator = result.first; + mSessions[channelID] = session; - return ptr; + 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::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::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::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); - - if (it != mSession.end()) - result = (*it).lock(); - + std::map::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::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 &a, sessionFunc_t func) { - ptr_t aLock(a.lock()); + ptr_t aLock(a.second.lock()); if (aLock) func(aLock); @@ -3396,425 +2154,807 @@ 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; + + result->mChannelID = channel_id; + + verifySessionState(); } + + LL_DEBUGS("Voice") << "returning existing session: CHANNEL " << channel_id << LL_ENDL; + } + + verifySessionState(); + + return result; +} + +void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) +{ + // 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) + { + mAudioSession.reset(); + mAudioSessionChanged = true; + } + + // ditto for the next audio session + if(mNextAudioSession == session) + { + mNextAudioSession.reset(); + } +} + +void LLWebRTCVoiceClient::sessionState::deleteAllSessions() +{ + mSessions.clear(); +} + +void LLWebRTCVoiceClient::verifySessionState(void) +{ + sessionState::VerifySessions(); +} + +void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyParticipantObservers() +{ + for (observer_set_t::iterator it = mParticipantObservers.begin(); + it != mParticipantObservers.end(); + ) + { + LLVoiceClientParticipantObserver* observer = *it; + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. + it = mParticipantObservers.upper_bound(observer); + } +} + +void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) +{ + LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" + << " mAudioSession=" << mAudioSession + << LL_ENDL; - if(handle != result->mHandle) + if(mAudioSession) + { + if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) { - if(handle.empty()) + switch(mAudioSession->mErrorStatusCode) { - // 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; + case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; + case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; + case 20715: + //invalid channel, we may be using a set of poorly cached + //info + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + case 1009: + //invalid username and password + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; } - else + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + } + else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + switch(mAudioSession->mErrorStatusCode) { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; - setSessionHandle(result, handle); + case HTTP_NOT_FOUND: // NOT_FOUND + // *TODO: Should this be 503? + case 480: // TEMPORARILY_UNAVAILABLE + case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT + // call failed because other user was not available + // treat this as an error case + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + break; } } + } - LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session URI " << getAudioSessionURI() + << ", proximal is " << inSpatialChannel() + << LL_ENDL; + + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + // 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 + && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) + { + bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + + gAgent.setVoiceConnected(voice_status); + + if (voice_status) + { + LLFirstUse::speak(true); + } + } +} + +void LLWebRTCVoiceClient::addObserver(LLFriendObserver* observer) +{ + mFriendObservers.insert(observer); +} + +void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +{ + mFriendObservers.erase(observer); +} + +void LLWebRTCVoiceClient::notifyFriendObservers() +{ + for (friend_observer_set_t::iterator it = mFriendObservers.begin(); + it != mFriendObservers.end(); + ) + { + LLFriendObserver* observer = *it; + it++; + // The only friend-related thing we notify on is online/offline transitions. + observer->changed(LLFriendObserver::ONLINE); + } +} + +void LLWebRTCVoiceClient::lookupName(const LLUUID &id) +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); } + mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); +} - verifySessionState(); - - return result; +void LLWebRTCVoiceClient::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + std::string display_name = av_name.getDisplayName(); + avatarNameResolved(agent_id, display_name); } -void LLWebRTCVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) +void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) { - if (session) + participantStatePtr_t participant(session->findParticipantByID(id)); + if (participant) { - 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; - } + // Found -- fill in the name + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; } - else + + // Check whether this is a p2p session whose caller name just resolved + if (session->mCallerID == id) { - LL_WARNS("Voice") << "Attempt to clear NULL session!" << LL_ENDL; + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; } - } -void LLWebRTCVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle) +void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) { - // 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; - } + sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); +} - 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)); - } +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } - verifySessionState(); -} -void LLWebRTCVoiceClient::setSessionURI(const sessionStatePtr_t &session, const std::string &uri) +///////////////////////////// +// WebRTC Signaling Handlers + +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string& channelID) : + mWebRTCPeerConnection(nullptr), + mWebRTCAudioInterface(nullptr), + mWebRTCDataInterface(nullptr), + mIceCompleted(false), + mTrickling(false), + mVoiceConnectionState(VOICE_STATE_START_SESSION), + mChannelID(channelID), + mRegionID(regionID), + mParcelLocalID(parcelLocalID), + mShutDown(false) { - // There used to be a map of session URIs to sessions, which made this complex.... - session->mSIPURI = uri; + mWebRTCPeerConnection = llwebrtc::newPeerConnection(); + mWebRTCPeerConnection->setSignalingObserver(this); +} - verifySessionState(); +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. + return; + } + mWebRTCPeerConnection->unsetSignalingObserver(this); + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; } -void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { - // 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); - } - } + LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; - // At this point, the session should be unhooked from all lists and all state should be consistent. - verifySessionState(); + 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; + } +} - // If this is the current audio session, clean up the pointer which will soon be dangling. - if(mAudioSession == session) - { - mAudioSession.reset(); - mAudioSessionChanged = true; - } +void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +{ + LLMutexLock lock(&mVoiceStateMutex); + mIceCandidates.push_back(candidate); +} - // ditto for the next audio session - if(mNextAudioSession == session) - { - mNextAudioSession.reset(); - } +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 LLWebRTCVoiceClient::deleteAllSessions() +void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { - LL_DEBUGS("Voice") << LL_ENDL; + 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; + } +} - while (!mSessionsByHandle.empty()) - { - const sessionStatePtr_t session = mSessionsByHandle.begin()->second; - deleteSession(session); - } - +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +{ + LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; + mWebRTCAudioInterface = audio_interface; + setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); } -void LLWebRTCVoiceClient::verifySessionState(void) +void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binary) { - LL_DEBUGS("Voice") << "Sessions in handle map=" << mSessionsByHandle.size() << LL_ENDL; - sessionState::VerifySessions(); + // 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 LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - mParticipantObservers.insert(observer); + 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 LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { - mParticipantObservers.erase(observer); + LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } -void LLWebRTCVoiceClient::notifyParticipantObservers() +void LLVoiceWebRTCConnection::processIceUpdates() { - for (observer_set_t::iterator it = mParticipantObservers.begin(); - it != mParticipantObservers.end(); - ) - { - LLVoiceClientParticipantObserver* observer = *it; - observer->onParticipantsChanged(); - // In case onParticipantsChanged() deleted an entry. - it = mParticipantObservers.upper_bound(observer); - } + 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; + } + } } -void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +bool LLVoiceWebRTCConnection::requestVoiceConnection() { - mStatusObservers.insert(observer); -} + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) -{ - mStatusObservers.erase(observer); -} + 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; + } -void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) -{ - LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" - << " mAudioSession=" << mAudioSession - << LL_ENDL; + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) + { + return false; + } - if(mAudioSession) - { - if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) - { - switch(mAudioSession->mErrorStatusCode) - { - case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; - case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; - case 20715: - //invalid channel, we may be using a set of poorly cached - //info - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - case 1009: - //invalid username and password - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - } + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - } - else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) - { - switch(mAudioSession->mErrorStatusCode) - { - case HTTP_NOT_FOUND: // NOT_FOUND - // *TODO: Should this be 503? - case 480: // TEMPORARILY_UNAVAILABLE - case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT - // call failed because other user was not available - // treat this as an error case - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + 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; + } - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - break; - } - } - } - - LL_DEBUGS("Voice") - << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session URI " << getAudioSessionURI() - << ", proximal is " << inSpatialChannel() - << LL_ENDL; + 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; +} - for (status_observer_set_t::iterator it = mStatusObservers.begin(); - it != mStatusObservers.end(); - ) - { - LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); - // In case onError() deleted an entry. - it = mStatusObservers.upper_bound(observer); - } +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - // skipped to avoid speak button blinking - if ( status != LLVoiceClientStatusObserver::STATUS_JOINING - && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL - && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) - { - bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + 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. - gAgent.setVoiceConnected(voice_status); + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" + << " user " << (voiceUserName.empty() ? "not set" : "set") << " password " + << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << mRemoteChannelSDP << LL_ENDL; - if (voice_status) - { - LLFirstUse::speak(true); - } - } + mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); } -void LLWebRTCVoiceClient::addObserver(LLFriendObserver* observer) +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { - mFriendObservers.insert(observer); + 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); + } } -void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) +bool LLVoiceWebRTCConnection::connectionStateMachine() { - mFriendObservers.erase(observer); -} + U32 retry = 0; + + processIceUpdates(); -void LLWebRTCVoiceClient::notifyFriendObservers() -{ - for (friend_observer_set_t::iterator it = mFriendObservers.begin(); - it != mFriendObservers.end(); - ) - { - LLFriendObserver* observer = *it; - it++; - // The only friend-related thing we notify on is online/offline transitions. - observer->changed(LLFriendObserver::ONLINE); - } -} + 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; -void LLWebRTCVoiceClient::lookupName(const LLUUID &id) -{ - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); + 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 LLWebRTCVoiceClient::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name) -{ - mAvatarNameCacheConnection.disconnect(); - std::string display_name = av_name.getDisplayName(); - avatarNameResolved(agent_id, display_name); + +void LLVoiceWebRTCConnection::sendData(const std::string& data) { + if (mWebRTCDataInterface) + { + mWebRTCDataInterface->sendData(data, false); + } } -void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) +bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) { - participantStatePtr_t participant(session->findParticipantByID(id)); - if (participant) + LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; + if (mWebRTCDataInterface) { - // Found -- fill in the name - participant->mAccountName = name; - // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; + 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; } - // Check whether this is a p2p session whose caller name just resolved - if (session->mCallerID == id) + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) { - // this session's "caller ID" just resolved. Fill in the name. - session->mName = name; + return false; } -} -void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) -{ - sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); -} + 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); -std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } + 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) { - // 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]; - - for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) - { - random_value[b] = ll_rand() & 0xff; - } - mConnectorHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); - - for (int b = 0; b < WebRTC_TOKEN_BYTES; b++) + if (LLWebRTCVoiceClient::isShuttingDown()) { - random_value[b] = ll_rand() & 0xff; + return; } - mAccountHandle = LLBase64::encode(random_value, WebRTC_TOKEN_BYTES); + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } -LLWebRTCSecurity::~LLWebRTCSecurity() +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 -- cgit v1.2.3 From 82c9e528658412293c264c1e80573e1abc496768 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 30 Nov 2023 14:37:17 -0800 Subject: fix mac build issue --- indra/newview/llvoicewebrtc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 31be3daed3..26b617bde4 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1159,7 +1159,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string& channelID, const LLUUID& id) { participantStatePtr_t result; - auto& session = sessionState::matchSessionByChannelID(channelID); + auto session = sessionState::matchSessionByChannelID(channelID); if (session) { @@ -1172,7 +1172,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantB LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string& channelID, const LLUUID &id) { participantStatePtr_t result; - auto& session = sessionState::matchSessionByChannelID(channelID); + auto session = sessionState::matchSessionByChannelID(channelID); if (session) { result = session->addParticipant(id); @@ -1183,7 +1183,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id) { participantStatePtr_t result; - auto& session = sessionState::matchSessionByChannelID(channelID); + auto session = sessionState::matchSessionByChannelID(channelID); if (session) { participantStatePtr_t participant = session->findParticipantByID(id); @@ -2957,4 +2957,4 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); -} \ No newline at end of file +} -- cgit v1.2.3 From 7a6c7964520d185eefedfedc340b0cc07365ff88 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 1 Dec 2023 01:14:33 -0800 Subject: Using the device module to set speaker/mic volume set the system mic/volume for all applications. Instead, modify the volume on the various streams. --- indra/newview/llvoicewebrtc.cpp | 92 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 11 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 26b617bde4..435e2e1245 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -746,7 +746,7 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) if (!mMuteMic) { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); + audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel() * mMicVolume; uint_audio_level = (uint32_t) (audio_level * 128); } @@ -853,6 +853,33 @@ void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) } } +void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) +{ + mMuted = muted; + for (auto& connection : mWebRTCConnections) + { + connection.second->setMuteMic(muted); + } +} + +void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) +{ + mMicGain = gain; + for (auto& connection : mWebRTCConnections) + { + connection.second->setMicGain(gain); + } +} + +void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) +{ + mSpeakerVolume = volume; + for (auto& connection : mWebRTCConnections) + { + connection.second->setSpeakerVolume(volume); + } +} + void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } @@ -1723,10 +1750,6 @@ void LLWebRTCVoiceClient::leaveChannel(void) void LLWebRTCVoiceClient::setMuteMic(bool muted) { - if (mWebRTCDeviceInterface) - { - mWebRTCDeviceInterface->setMute(muted); - } mMuteMic = muted; sessionState::for_each(boost::bind(predSetMuteMic, _1, muted)); } @@ -1738,6 +1761,17 @@ void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionState { participant->mPower = 0.0; } + session->setMuteMic(muted); +} + +void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +{ + session->setSpeakerVolume(volume); +} + +void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +{ + session->setMicGain(volume); } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -1838,10 +1872,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) mSpeakerVolume = volume; mSpeakerVolumeDirty = true; } - if (mWebRTCDeviceInterface) - { - mWebRTCDeviceInterface->setSpeakerVolume(volume); - } + sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); } } @@ -1854,6 +1885,7 @@ void LLWebRTCVoiceClient::setMicGain(F32 volume) mMicVolume = scaled_volume; mMicVolumeDirty = true; } + sessionState::for_each(boost::bind(predSetMicGain, _1, scaled_volume)); } ///////////////////////////// @@ -2059,7 +2091,9 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::crea sessionState::ptr_t session(new sessionState()); session->mChannelID = channelID; - session->mWebRTCConnections[channelID] = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); + connectionPtr_t connection = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); + session->mWebRTCConnections[channelID] = connection; + session->mPrimaryConnectionID = channelID; // add agent as participant @@ -2190,6 +2224,9 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; result = sessionState::createSession(channel_id, parcel_local_id); + result->setMuteMic(mMuteMic); + result->setMicGain(mMicVolume); + result->setSpeakerVolume(mSpeakerVolume); if (LLVoiceClient::instance().getVoiceEffectEnabled()) { @@ -2442,7 +2479,10 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 par mChannelID(channelID), mRegionID(regionID), mParcelLocalID(parcelLocalID), - mShutDown(false) + mShutDown(false), + mMuted(true), + mSpeakerVolume(0.0), + mMicGain(0.0) { mWebRTCPeerConnection = llwebrtc::newPeerConnection(); mWebRTCPeerConnection->setSignalingObserver(this); @@ -2821,6 +2861,9 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_SESSION_ESTABLISHED: { + mWebRTCAudioInterface->setMute(mMuted); + mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); + mWebRTCAudioInterface->setSendVolume(mMicGain); LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID); setVoiceConnectionState(VOICE_STATE_SESSION_UP); } @@ -2958,3 +3001,30 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } + +void LLVoiceWebRTCConnection::setMuteMic(bool muted) +{ + mMuted = true; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(muted); + } +} + +void LLVoiceWebRTCConnection::setMicGain(F32 gain) +{ + mMicGain = gain; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setSendVolume(gain); + } +} + +void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume) +{ + mSpeakerVolume = volume; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setReceiveVolume(volume); + } +} -- cgit v1.2.3 From e92e4d762ec5b2c122f17edb1a18b21ef6166e8b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 4 Dec 2023 14:22:40 -0800 Subject: Mute using enable. Muting using the device module microphone mute was muting other applications, speakers, and so on. Instead, we mute by enabling/disabling the input and output streams. --- indra/newview/llvoicewebrtc.cpp | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 435e2e1245..ddd757c39f 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -261,7 +261,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mEarLocation(0), mSpeakerVolumeDirty(true), - mSpeakerMuteDirty(true), mMicVolume(0), mMicVolumeDirty(true), @@ -583,16 +582,7 @@ LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) { - bool inTuningMode = mIsInTuningMode; - if (inTuningMode) - { - tuningStop(); - } mWebRTCDeviceInterface->setCaptureDevice(name); - if (inTuningMode) - { - tuningStart(); - } } void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { @@ -1863,12 +1853,6 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) if (volume != mSpeakerVolume) { { - int min_volume = 0.0; - if ((volume == min_volume) || (mSpeakerVolume == min_volume)) - { - mSpeakerMuteDirty = true; - } - mSpeakerVolume = volume; mSpeakerVolumeDirty = true; } -- cgit v1.2.3 From ec7bd371f612334f1f81af58a753cff643d7c86e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 4 Dec 2023 15:39:36 -0800 Subject: Voice was not renegotiating when re-enabled. --- indra/newview/llvoicewebrtc.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index ddd757c39f..a8285f9aab 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -357,6 +357,8 @@ void LLWebRTCVoiceClient::cleanUp() { LL_DEBUGS("Voice") << LL_ENDL; + mNextAudioSession.reset(); + mAudioSession.reset(); sessionState::deleteAllSessions(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -470,16 +472,19 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { while (!sShuttingDown) { + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + if (!mVoiceEnabled) + { + continue; + } // add session for region or parcel voice. LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp) { - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); continue; } if (regionp->getRegionID().isNull()) { - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); continue; } if (!mAudioSession || mAudioSession->mIsSpatial) @@ -503,7 +508,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() sessionState::for_each(boost::bind(predProcessSessionStates, _1)); sendPositionAndVolumeUpdate(true); updateOwnVolume(); - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); } } catch (const LLCoros::Stop&) @@ -1801,6 +1805,7 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); gAgent.setVoiceConnected(false); status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; + cleanUp(); } notifyStatusObservers(status); @@ -2261,6 +2266,7 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::sessionState::deleteAllSessions() { mSessions.clear(); + } void LLWebRTCVoiceClient::verifySessionState(void) -- cgit v1.2.3 From 60da67c56c41ad9e51aff30db2611203038f0f2f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 5 Dec 2023 20:10:33 -0800 Subject: Fix crash when disconnecting. When disconnecting, we need to wait for any outstanding http calls to complete as the handlers may use the session objects. Also, reap empty sessions. --- indra/newview/llvoicewebrtc.cpp | 54 +++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a8285f9aab..9c229f1144 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -411,6 +411,7 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) { mAudioSession = mNextAudioSession; mNextAudioSession.reset(); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); } @@ -419,7 +420,6 @@ void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::strin { if (channelID == mPrimaryConnectionID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } } @@ -506,6 +506,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } updatePosition(); sessionState::for_each(boost::bind(predProcessSessionStates, _1)); + sessionState::reapEmptySessions(); sendPositionAndVolumeUpdate(true); updateOwnVolume(); } @@ -1316,13 +1317,19 @@ bool LLWebRTCVoiceClient::switchChannel( // 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(); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + deleteSession(mAudioSession); } + if (channelID.empty()) { // Leave any channel we may be in LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + if (mNextAudioSession) + { + mAudioSession->shutdownAllConnections(); + } sessionStatePtr_t oldSession = mNextAudioSession; mNextAudioSession.reset(); @@ -1339,12 +1346,15 @@ bool LLWebRTCVoiceClient::switchChannel( } else { - LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - - mNextAudioSession = addSession(channelID, parcel_local_id); - mNextAudioSession->mIsSpatial = spatial; - mNextAudioSession->mReconnect = !no_reconnect; - mNextAudioSession->mIsP2P = is_p2p; + if (mNextAudioSession) + { + deleteSession(mNextAudioSession); + } + LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; + mNextAudioSession = addSession(channelID, parcel_local_id); + mNextAudioSession->mIsSpatial = spatial; + mNextAudioSession->mReconnect = !no_reconnect; + mNextAudioSession->mIsP2P = is_p2p; } } @@ -2138,7 +2148,7 @@ void LLWebRTCVoiceClient::sessionState::reapEmptySessions() std::map::iterator iter; for (iter = mSessions.begin(); iter != mSessions.end();) { - if (!iter->second->isEmpty()) + if (iter->second->isEmpty()) { iter = mSessions.erase(iter); } @@ -2266,7 +2276,6 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::sessionState::deleteAllSessions() { mSessions.clear(); - } void LLWebRTCVoiceClient::verifySessionState(void) @@ -2470,6 +2479,7 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 par mRegionID(regionID), mParcelLocalID(parcelLocalID), mShutDown(false), + mOutstandingRequests(0), mMuted(true), mSpeakerVolume(0.0), mMicGain(0.0) @@ -2526,6 +2536,7 @@ void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD return; } mTrickling = false; + mOutstandingRequests--; } void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) @@ -2546,6 +2557,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS body, boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); + mOutstandingRequests++; } else { @@ -2553,6 +2565,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mTrickling = false; } + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) @@ -2721,6 +2734,7 @@ void LLVoiceWebRTCConnection::processIceUpdates() body, boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + mOutstandingRequests++; mTrickling = true; } } @@ -2765,6 +2779,7 @@ bool LLVoiceWebRTCConnection::requestVoiceConnection() body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + mOutstandingRequests++; return true; } @@ -2780,6 +2795,12 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result { mRemoteChannelSDP = result["jsep"]["sdp"].asString(); } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + mOutstandingRequests--; + return; + } std::string voiceAccountServerUri; std::string voiceUserName = gAgent.getID().asString(); std::string voicePassword = ""; // no password for now. @@ -2789,6 +2810,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result << (voicePassword.empty() ? "not set" : "set") << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) @@ -2805,12 +2827,14 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + mOutstandingRequests++; } else { LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } + mOutstandingRequests--; } bool LLVoiceWebRTCConnection::connectionStateMachine() @@ -2897,7 +2921,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } else { - return false; + return mOutstandingRequests > 0; } } break; @@ -2962,6 +2986,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + mOutstandingRequests++; return true; } @@ -2972,6 +2997,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res return; } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) @@ -2986,10 +3012,12 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + mOutstandingRequests++; } setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::setMuteMic(bool muted) -- cgit v1.2.3 From c51309ac72ca96cdc0d22ba7549e614139595e65 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Dec 2023 19:47:37 -0800 Subject: Rework VU meter level processing to be closer to Vivox --- indra/newview/llvoicewebrtc.cpp | 134 ++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 73 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9c229f1144..b9755cdda1 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -86,9 +86,10 @@ extern void handle_voice_morphing_subscribe(); namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; + const F32 LEVEL_SCALE_WEBRTC = 0.01f; const F32 SPEAKING_TIMEOUT = 1.f; - const F32 SPEAKING_AUDIO_LEVEL = 0.05; + const F32 SPEAKING_AUDIO_LEVEL = 0.10; static const std::string VOICE_SERVER_TYPE = "WebRTC"; @@ -234,9 +235,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mSpatialJoiningNum(0), mTuningMode(false), - mTuningEnergy(0.0f), - mTuningMicVolume(0), - mTuningMicVolumeDirty(true), + mTuningMicGain(0.0), mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume mTuningSpeakerVolumeDirty(true), mDevicesListUpdated(false), @@ -261,8 +260,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mEarLocation(0), mSpeakerVolumeDirty(true), - mMicVolume(0), - mMicVolumeDirty(true), + mMicGain(0.0), mVoiceEnabled(false), @@ -314,25 +312,25 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : LLWebRTCVoiceClient::~LLWebRTCVoiceClient() { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } sShuttingDown = true; } //--------------------------------------------------- -void LLWebRTCVoiceClient::init(LLPumpIO *pump) +void LLWebRTCVoiceClient::init(LLPumpIO* pump) { - // constructor will set up LLVoiceClient::getInstance() - sPump = pump; + // constructor will set up LLVoiceClient::getInstance() + sPump = pump; -// LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", -// boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); + // LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", + // boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); llwebrtc::init(); - mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); + mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); mWebRTCDeviceInterface->setDevicesObserver(this); } @@ -342,9 +340,9 @@ void LLWebRTCVoiceClient::terminate() { return; } - - mRelogRequested = false; - mVoiceEnabled = false; + + mRelogRequested = false; + mVoiceEnabled = false; llwebrtc::terminate(); sShuttingDown = true; @@ -356,10 +354,10 @@ void LLWebRTCVoiceClient::terminate() void LLWebRTCVoiceClient::cleanUp() { LL_DEBUGS("Voice") << LL_ENDL; - + mNextAudioSession.reset(); mAudioSession.reset(); - sessionState::deleteAllSessions(); + sessionState::deleteAllSessions(); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -367,7 +365,7 @@ void LLWebRTCVoiceClient::cleanUp() const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() { - return mVoiceVersion; + return mVoiceVersion; } //--------------------------------------------------- @@ -375,15 +373,15 @@ const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() void LLWebRTCVoiceClient::updateSettings() { setVoiceEnabled(voiceEnabled()); - setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); - std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); - setCaptureDevice(inputDevice); - std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - setRenderDevice(outputDevice); - F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); - setMicGain(mic_level); - setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); + std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + setCaptureDevice(inputDevice); + std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + setRenderDevice(outputDevice); + F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); + setMicGain(mic_level); + setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); } @@ -395,7 +393,7 @@ void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient: session->OnConnectionEstablished(channelID); } -void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID) +void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) { session->OnConnectionFailure(channelID); } @@ -405,7 +403,7 @@ void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); } -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID) { if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) { @@ -413,6 +411,10 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID) mNextAudioSession.reset(); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); } @@ -658,13 +660,7 @@ bool LLWebRTCVoiceClient::inTuningMode() void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mTuningMicVolume) - { - mTuningMicVolume = scaled_volume; - mTuningMicVolumeDirty = true; - } + mTuningMicGain = volume; } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) @@ -679,7 +675,7 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return mWebRTCDeviceInterface->getTuningAudioLevel(); + return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain/2.1; } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -739,10 +735,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) F32 audio_level = 0.0; uint32_t uint_audio_level = 0.0; - if (!mMuteMic) + if (!mMuteMic && !mTuningMode) { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel() * mMicVolume; - uint_audio_level = (uint32_t) (audio_level * 128); + audio_level = (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + uint_audio_level = (uint32_t) (audio_level*128); } @@ -810,9 +806,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) void LLWebRTCVoiceClient::updateOwnVolume() { F32 audio_level = 0.0; - if (!mMuteMic) + if (!mMuteMic && !mTuningMode) { - audio_level = (F32) mWebRTCDeviceInterface->getPeerAudioLevel(); + audio_level = (F32) (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + LL_DEBUGS("Voice") << "Level " << audio_level << LL_ENDL; } sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); @@ -823,7 +820,7 @@ void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::session participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); if (participant) { - participant->mPower = audio_level; + participant->mLevel = audio_level; participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; } } @@ -991,7 +988,7 @@ LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) mIsSpeaking(false), mIsModeratorMuted(false), mLastSpokeTimestamp(0.f), - mPower(0.f), + mLevel(0.f), mVolume(LLVoiceClient::VOLUME_DEFAULT), mUserVolume(0), mOnMuteList(false), @@ -1763,7 +1760,7 @@ void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionState participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); if (participant) { - participant->mPower = 0.0; + participant->mLevel = 0.0; } session->setMuteMic(muted); } @@ -1773,9 +1770,9 @@ void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessio session->setSpeakerVolume(volume); } -void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain) { - session->setMicGain(volume); + session->setMicGain(gain); } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -1875,16 +1872,13 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) } } -void LLWebRTCVoiceClient::setMicGain(F32 volume) -{ - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mMicVolume) - { - mMicVolume = scaled_volume; - mMicVolumeDirty = true; - } - sessionState::for_each(boost::bind(predSetMicGain, _1, scaled_volume)); +void LLWebRTCVoiceClient::setMicGain(F32 gain) +{ + if (gain != mMicGain) + { + mMicGain = gain; + sessionState::for_each(boost::bind(predSetMicGain, _1, gain)); + } } ///////////////////////////// @@ -1967,7 +1961,7 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); if (participant) { - result = participant->mPower * 2.0; + result = participant->mLevel; } return result; } @@ -2224,7 +2218,7 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; result = sessionState::createSession(channel_id, parcel_local_id); result->setMuteMic(mMuteMic); - result->setMicGain(mMicVolume); + result->setMicGain(mMicGain); result->setSpeakerVolume(mSpeakerVolume); if (LLVoiceClient::instance().getVoiceEffectEnabled()) @@ -2639,12 +2633,12 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } else { - F32 energyRMS = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mPower)).asInt()) / 128; + F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; // convert to decibles - participant->mPower = energyRMS; + participant->mLevel = level; /* 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; + participant->mIsSpeaking = participant->mLevel > SPEAKING_AUDIO_LEVEL; } } } @@ -2898,14 +2892,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() break; case VOICE_STATE_DISCONNECT: - if (breakVoiceConnection(true)) - { - setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); - } - else - { - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); - } + breakVoiceConnection(true); + retry = 0; // Connected without issues break; -- cgit v1.2.3 From 1b2b5d2de9da01725898d25fea93925a82024ad4 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Dec 2023 20:35:05 -0800 Subject: fix mac build break --- indra/newview/llvoicewebrtc.cpp | 7 ------- 1 file changed, 7 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b9755cdda1..9f290b9d4a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -123,13 +123,6 @@ namespace { const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; } -static int scale_mic_volume(float volume) -{ - // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. - // Map it to WebRTC levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 - return 30 + (int)(volume * 20.0f); -} - /////////////////////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From a60593164e82057930d5e9da5173207700413197 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 12 Dec 2023 20:46:34 -0800 Subject: Better renegotiation support for parcel voice Better handle starting up and shutting down WebRTC connections simultaneously. --- indra/newview/llvoicewebrtc.cpp | 183 +++++++++++++++++++++++++++------------- 1 file changed, 126 insertions(+), 57 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9f290b9d4a..ec1e6a353d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -86,10 +86,10 @@ extern void handle_voice_morphing_subscribe(); namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; - const F32 LEVEL_SCALE_WEBRTC = 0.01f; + const F32 LEVEL_SCALE_WEBRTC = 0.008f; const F32 SPEAKING_TIMEOUT = 1.f; - const F32 SPEAKING_AUDIO_LEVEL = 0.10; + const F32 SPEAKING_AUDIO_LEVEL = 0.40; static const std::string VOICE_SERVER_TYPE = "WebRTC"; @@ -121,9 +121,19 @@ namespace { // Maximum length of capture buffer recordings in seconds. const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; +} // namespace +float LLWebRTCVoiceClient::getAudioLevel() +{ + if (mIsInTuningMode) + { + return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; + } + else + { + return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + } } - /////////////////////////////////////////////////////////////////////////////////////////////// void LLVoiceWebRTCStats::reset() @@ -350,7 +360,7 @@ void LLWebRTCVoiceClient::cleanUp() mNextAudioSession.reset(); mAudioSession.reset(); - sessionState::deleteAllSessions(); + sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -393,6 +403,14 @@ void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::ses void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) { + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); } @@ -468,10 +486,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() while (!sShuttingDown) { llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - if (!mVoiceEnabled) - { - continue; - } // add session for region or parcel voice. LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp) @@ -482,7 +496,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - if (!mAudioSession || mAudioSession->mIsSpatial) + if (mVoiceEnabled && (!mAudioSession || mAudioSession->mIsSpatial)) { // check to see if parcel changed. std::string channelID = regionp->getRegionID().asString(); @@ -499,11 +513,14 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() setSpatialChannel(channelID, "", parcel_local_id); } } - updatePosition(); sessionState::for_each(boost::bind(predProcessSessionStates, _1)); - sessionState::reapEmptySessions(); - sendPositionAndVolumeUpdate(true); - updateOwnVolume(); + reapEmptySessions(); + if (mVoiceEnabled) + { + updatePosition(); + sendPositionAndVolumeUpdate(true); + updateOwnVolume(); + } } } catch (const LLCoros::Stop&) @@ -526,15 +543,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() cleanUp(); } -bool LLWebRTCVoiceClient::performMicTuning() -{ - LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - - mIsInTuningMode = false; - - //--------------------------------------------------------------------- - return true; -} //========================================================================= @@ -665,10 +673,11 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) mTuningSpeakerVolumeDirty = true; } } - + + float LLWebRTCVoiceClient::tuningGetEnergy(void) { - return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain/2.1; + return getAudioLevel(); } bool LLWebRTCVoiceClient::deviceSettingsAvailable() @@ -730,7 +739,7 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) if (!mMuteMic && !mTuningMode) { - audio_level = (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + audio_level = getAudioLevel(); uint_audio_level = (uint32_t) (audio_level*128); } @@ -801,8 +810,8 @@ void LLWebRTCVoiceClient::updateOwnVolume() { F32 audio_level = 0.0; if (!mMuteMic && !mTuningMode) { - audio_level = (F32) (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; - LL_DEBUGS("Voice") << "Level " << audio_level << LL_ENDL; + audio_level = getAudioLevel(); + LL_WARNS("Voice") << "Level " << audio_level << LL_ENDL; } sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); @@ -2052,6 +2061,22 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() return mAreaVoiceDisabled; } +void LLWebRTCVoiceClient::reapEmptySessions() +{ + sessionState::reapEmptySessions(); + + // mAudioSession or mNextAudioSession was reaped, + // so reset them. + if (mAudioSession && !sessionState::hasSession(mAudioSession->mChannelID)) + { + mAudioSession.reset(); + } + if (mNextAudioSession && !sessionState::hasSession(mNextAudioSession->mChannelID)) + { + mNextAudioSession.reset(); + } +} + //------------------------------------------------------------------------ std::map LLWebRTCVoiceClient::sessionState::mSessions; @@ -2241,6 +2266,11 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std return result; } +void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +{ + session->shutdownAllConnections(); +} + void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) { // At this point, the session should be unhooked from all lists and all state should be consistent. @@ -2260,11 +2290,6 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::sessionState::deleteAllSessions() -{ - mSessions.clear(); -} - void LLWebRTCVoiceClient::verifySessionState(void) { sessionState::VerifySessions(); @@ -2356,6 +2381,8 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt << ", proximal is " << inSpatialChannel() << LL_ENDL; + mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; + for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); ) @@ -2365,10 +2392,7 @@ 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 @@ -2483,6 +2507,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() // by llwebrtc::terminate() on shutdown. return; } + assert(mOutstandingRequests == 0); mWebRTCPeerConnection->unsetSignalingObserver(this); llwebrtc::freePeerConnection(mWebRTCPeerConnection); mWebRTCPeerConnection = nullptr; @@ -2544,14 +2569,16 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS body, boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); - mOutstandingRequests++; + return; } - else + + LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; + if (!mShutDown) { - LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - mTrickling = false; } + mTrickling = false; + mOutstandingRequests--; } @@ -2655,7 +2682,16 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + if (!mShutDown) + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } +} + +void LLVoiceWebRTCConnection::OnPeerShutDown() +{ + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2664,6 +2700,10 @@ void LLVoiceWebRTCConnection::processIceUpdates() { return; } + if (mShutDown) + { + return; + } LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { @@ -2814,26 +2854,25 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); - mOutstandingRequests++; - } - else - { - LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } + LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; } bool LLVoiceWebRTCConnection::connectionStateMachine() -{ - U32 retry = 0; - +{ processIceUpdates(); switch (getVoiceConnectionState()) { case VOICE_STATE_START_SESSION: { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + } mTrickling = false; mIceCompleted = false; setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START); @@ -2845,9 +2884,18 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } case VOICE_STATE_WAIT_FOR_SESSION_START: { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + } break; } case VOICE_STATE_REQUEST_CONNECTION: + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; + } if (!requestVoiceConnection()) { setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); @@ -2858,10 +2906,19 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } break; case VOICE_STATE_CONNECTION_WAIT: + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + } break; case VOICE_STATE_SESSION_ESTABLISHED: { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; + } mWebRTCAudioInterface->setMute(mMuted); mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); @@ -2886,8 +2943,6 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_DISCONNECT: breakVoiceConnection(true); - - retry = 0; // Connected without issues break; case VOICE_STATE_WAIT_FOR_EXIT: @@ -2934,10 +2989,6 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) mWebRTCDataInterface = nullptr; } mWebRTCAudioInterface = nullptr; - if (mWebRTCPeerConnection) - { - mWebRTCPeerConnection->shutdownConnection(); - } LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { @@ -2967,6 +3018,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); mOutstandingRequests++; return true; } @@ -2977,7 +3029,16 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res { return; } - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + + if (mWebRTCPeerConnection) + { + mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + } mOutstandingRequests--; } @@ -2995,9 +3056,17 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url body, boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + return; + } + if (mWebRTCPeerConnection) + { mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; } -- cgit v1.2.3 From a7509747b20bb8c00a477e8bdacc41f0c657cfdf Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 18 Dec 2023 11:45:15 -0800 Subject: Touch up parcel voice enable/disable. --- indra/newview/llvoicewebrtc.cpp | 437 ++++++++++++---------------------------- 1 file changed, 134 insertions(+), 303 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index ec1e6a353d..9a1b3525d7 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -455,17 +455,29 @@ void LLWebRTCVoiceClient::idle(void* user_data) // // -void LLWebRTCVoiceClient::predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t& session) +void LLWebRTCVoiceClient::sessionState::processSessionStates() { - session->processSessionStates(); + auto iter = mSessions.begin(); + while (iter != mSessions.end()) + { + if (!iter->second->processConnectionStates()) + { + iter = mSessions.erase(iter); + } + else + { + iter++; + } + } } -void LLWebRTCVoiceClient::sessionState::processSessionStates() +bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { - std::map::iterator iter; - for (iter = mWebRTCConnections.begin(); iter != mWebRTCConnections.end();) + std::list::iterator iter = mWebRTCConnections.begin(); + + while (iter != mWebRTCConnections.end()) { - if (!iter->second->connectionStateMachine()) + if (!iter->get()->connectionStateMachine()) { iter = mWebRTCConnections.erase(iter); } @@ -474,6 +486,7 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() ++iter; } } + return !mWebRTCConnections.empty(); } void LLWebRTCVoiceClient::voiceConnectionCoro() @@ -488,33 +501,39 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); // add session for region or parcel voice. LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp) + if (!regionp || regionp->getRegionID().isNull()) { continue; } - if (regionp->getRegionID().isNull()) - { - continue; - } - if (mVoiceEnabled && (!mAudioSession || mAudioSession->mIsSpatial)) + + if (mVoiceEnabled && (!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) { // check to see if parcel changed. std::string channelID = regionp->getRegionID().asString(); LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); S32 parcel_local_id = INVALID_PARCEL_ID; - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID && !parcel->getParcelFlagUseEstateVoiceChannel()) + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - parcel_local_id = parcel->getLocalID(); - channelID += "-" + std::to_string(parcel->getLocalID()); + if (!parcel->getParcelFlagAllowVoice()) + { + channelID.clear(); + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + parcel_local_id = parcel->getLocalID(); + channelID += "-" + std::to_string(parcel->getLocalID()); + } } - if (!mAudioSession || channelID != mAudioSession->mChannelID) + + if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || + (!mAudioSession && !channelID.empty()) || + (mAudioSession && channelID != mAudioSession->mChannelID)) { setSpatialChannel(channelID, "", parcel_local_id); } } - sessionState::for_each(boost::bind(predProcessSessionStates, _1)); - reapEmptySessions(); + sessionState::processSessionStates(); if (mVoiceEnabled) { updatePosition(); @@ -829,7 +848,7 @@ void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::session void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data) { - if (session->mIsSpatial && !spatial_data.empty()) + if (session->isSpatial() && !spatial_data.empty()) { session->sendData(spatial_data); } @@ -843,7 +862,7 @@ void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) { for (auto& connection : mWebRTCConnections) { - connection.second->sendData(data); + connection->sendData(data); } } @@ -852,7 +871,7 @@ void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) mMuted = muted; for (auto& connection : mWebRTCConnections) { - connection.second->setMuteMic(muted); + connection->setMuteMic(muted); } } @@ -861,7 +880,7 @@ void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) mMicGain = gain; for (auto& connection : mWebRTCConnections) { - connection.second->setMicGain(gain); + connection->setMicGain(gain); } } @@ -870,7 +889,7 @@ void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) mSpeakerVolume = volume; for (auto& connection : mWebRTCConnections) { - connection.second->setSpeakerVolume(volume); + connection->setSpeakerVolume(volume); } } @@ -878,52 +897,6 @@ void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } - -void LLWebRTCVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) -{ - LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; - if(mAudioSession != session) - { - sessionStatePtr_t oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(mIsJoiningSession) - { - LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) - ("session", "joined")); - - mWebRTCPump.post(WebRTCevent); - - 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->mChannelID))); - if(participant) - { - if(participant->mAvatarIDValid) - { - lookupName(participant->mAvatarID); - } - else if(!session->mName.empty()) - { - participant->mDisplayName = session->mName; - avatarNameResolved(participant->mAvatarID, session->mName); - } - - // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? - LL_INFOS("Voice") << "added caller as participant (" << participant->mAvatarID << ")"<< LL_ENDL; - } - } - } -} - void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) @@ -950,19 +923,6 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) } -void LLWebRTCVoiceClient::leftAudioSession(const sessionStatePtr_t &session) -{ - if (mAudioSession == session) - { - LLSD WebRTCevent(LLSDMap("channel", session->mChannelID) - ("session", "removed")); - - mWebRTCPump.post(WebRTCevent); - } -} - - - void LLWebRTCVoiceClient::muteListChanged() { // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. @@ -1215,149 +1175,47 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co } } - -// Check for parcel boundary crossing -bool LLWebRTCVoiceClient::checkParcelChanged(bool update) -{ - LLViewerRegion *region = gAgent.getRegion(); - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(region && parcel) - { - S32 parcelLocalID = parcel->getLocalID(); - std::string regionName = region->getName(); - - // LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; - - // The region name starts out empty and gets filled in later. - // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. - // If either is empty, wait for the next time around. - if(!regionName.empty()) - { - if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) - { - // We have changed parcels. Initiate a parcel channel lookup. - if (update) - { - mCurrentParcelLocalID = parcelLocalID; - mCurrentRegionName = regionName; - } - return true; - } - } - } - return false; -} - -bool LLWebRTCVoiceClient::switchChannel( - const std::string channelID, - bool spatial, - bool no_reconnect, - bool is_p2p, - std::string hash, - S32 parcel_local_id) +bool LLWebRTCVoiceClient::switchChannel(const std::string channelID, + sessionState::ESessionType session_type, + S32 parcel_local_id) { - bool needsSwitch = false; - if (mAudioSession) { - if (mSessionTerminateRequested) + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + deleteSession(mAudioSession); + } + + if (channelID.empty()) + { + // Leave any channel we may be in + LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + + if (mNextAudioSession) { - // If a terminate has been requested, we need to compare against where the URI we're already headed to. - if(mNextAudioSession) - { - if (mNextAudioSession->mChannelID != channelID) - needsSwitch = true; - } - else - { - // mNextAudioSession is null -- this probably means we're on our way back to spatial. - if (!channelID.empty()) - { - // We do want to process a switch in this case. - needsSwitch = true; - } - } + deleteSession(mNextAudioSession); } - else + // If voice was on, turn it off + if (LLVoiceClient::getInstance()->getUserPTTState()) { - // Otherwise, compare against the URI we're in now. - if(mAudioSession) - { - if (mAudioSession->mChannelID != channelID) - { - needsSwitch = true; - } - } - else - { - 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. - LL_WARNS("Voice") << "No current audio session... Forcing switch" << LL_ENDL; - needsSwitch = true; - } - } + LLVoiceClient::getInstance()->setUserPTTState(false); } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); } else { - if (!mNextAudioSession || mNextAudioSession->mChannelID != channelID) + if (mNextAudioSession) { - needsSwitch = true; + deleteSession(mNextAudioSession); } + LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; + mNextAudioSession = addSession(channelID, parcel_local_id); + mNextAudioSession->mSessionType = session_type; } - - if(needsSwitch) - { - 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(); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - deleteSession(mAudioSession); - } - - if (channelID.empty()) - { - // Leave any channel we may be in - LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; - - if (mNextAudioSession) - { - mAudioSession->shutdownAllConnections(); - } - sessionStatePtr_t oldSession = mNextAudioSession; - mNextAudioSession.reset(); - - // The old session may now need to be deleted. - reapSession(oldSession); - - // If voice was on, turn it off - if (LLVoiceClient::getInstance()->getUserPTTState()) - { - LLVoiceClient::getInstance()->setUserPTTState(false); - } - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - } - else - { - if (mNextAudioSession) - { - deleteSession(mNextAudioSession); - } - LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - mNextAudioSession = addSession(channelID, parcel_local_id); - mNextAudioSession->mIsSpatial = spatial; - mNextAudioSession->mReconnect = !no_reconnect; - mNextAudioSession->mIsP2P = is_p2p; - } - } - - return needsSwitch; + return true; } void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) @@ -1376,7 +1234,7 @@ void LLWebRTCVoiceClient::setNonSpatialChannel( const std::string &uri, const std::string &credentials) { - switchChannel(uri, false, false, false, credentials); + switchChannel(uri, sessionState::SESSION_TYPE_P2P); } bool LLWebRTCVoiceClient::setSpatialChannel( @@ -1386,7 +1244,7 @@ bool LLWebRTCVoiceClient::setSpatialChannel( LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) + if((mAudioSession && !mAudioSession->isSpatial()) || (mNextAudioSession && !mNextAudioSession->isSpatial())) { // 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; @@ -1394,13 +1252,13 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { - return switchChannel(uri, true, false, false, "", parcel_local_id); + return switchChannel(uri, parcel_local_id == INVALID_PARCEL_ID ? sessionState::SESSION_TYPE_ESTATE : sessionState::SESSION_TYPE_PARCEL, parcel_local_id); } } void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) { - switchChannel(uuid.asString(), false, true, true); + switchChannel(uuid.asString(), sessionState::SESSION_TYPE_P2P); } void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) @@ -1419,9 +1277,7 @@ bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); if(session) { - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; + session->mSessionType = sessionState::ESessionType::SESSION_TYPE_P2P; joinSession(session); return true; @@ -1604,7 +1460,7 @@ bool LLWebRTCVoiceClient::inSpatialChannel(void) if(mAudioSession) { - result = mAudioSession->mIsSpatial; + result = mAudioSession->isSpatial(); } return result; @@ -2061,34 +1917,12 @@ BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() return mAreaVoiceDisabled; } -void LLWebRTCVoiceClient::reapEmptySessions() -{ - sessionState::reapEmptySessions(); - - // mAudioSession or mNextAudioSession was reaped, - // so reset them. - if (mAudioSession && !sessionState::hasSession(mAudioSession->mChannelID)) - { - mAudioSession.reset(); - } - if (mNextAudioSession && !sessionState::hasSession(mNextAudioSession->mChannelID)) - { - mNextAudioSession.reset(); - } -} - //------------------------------------------------------------------------ std::map LLWebRTCVoiceClient::sessionState::mSessions; LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), - mIsChannel(false), - mIsSpatial(false), - mIsP2P(false), - mIncoming(false), - mVoiceActive(false), - mReconnect(false), mVolumeDirty(false), mMuteDirty(false), mParticipantsChanged(false) @@ -2102,8 +1936,7 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::crea sessionState::ptr_t session(new sessionState()); session->mChannelID = channelID; - connectionPtr_t connection = connectionPtr_t(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); - session->mWebRTCConnections[channelID] = connection; + session->mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); session->mPrimaryConnectionID = channelID; @@ -2202,7 +2035,7 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pairmIsP2P) + if (result && result->mSessionType == sessionState::SESSION_TYPE_P2P) { return result; } @@ -2217,7 +2050,7 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() { for (auto &&connection : mWebRTCConnections) { - connection.second->shutDown(); + connection->shutDown(); } } @@ -2277,14 +2110,16 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) verifySessionState(); session->shutdownAllConnections(); // If this is the current audio session, clean up the pointer which will soon be dangling. - if(mAudioSession == session) + bool deleteAudioSession = mAudioSession == session; + bool deleteNextAudioSession = mNextAudioSession == session; + if (deleteAudioSession) { mAudioSession.reset(); mAudioSessionChanged = true; } // ditto for the next audio session - if(mNextAudioSession == session) + if (deleteNextAudioSession) { mNextAudioSession.reset(); } @@ -2696,73 +2531,68 @@ void LLVoiceWebRTCConnection::OnPeerShutDown() void LLVoiceWebRTCConnection::processIceUpdates() { - if (LLWebRTCVoiceClient::isShuttingDown()) - { - return; - } - if (mShutDown) - { - return; - } - LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); - if (!regionp || !regionp->capabilitiesReceived()) + if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { - 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()) + if (!mIceCandidates.empty() || mIceCompleted) { - LLSD candidates = LLSD::emptyArray(); - for (auto &ice_candidate : mIceCandidates) + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + if (!regionp || !regionp->capabilitiesReceived()) { - 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); + LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; + return; } - 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; + + std::string url = regionp->getCapability("VoiceSignalingRequest"); + if (url.empty()) + { + return; + } + + LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; + if (!mIceCandidates.empty()) { + 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; + } + + 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); + 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)); + mOutstandingRequests++; + mTrickling = true; } - 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)); - mOutstandingRequests++; - mTrickling = true; } } } @@ -2862,7 +2692,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i } bool LLVoiceWebRTCConnection::connectionStateMachine() -{ +{ processIceUpdates(); switch (getVoiceConnectionState()) @@ -2872,6 +2702,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() if (mShutDown) { setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; } mTrickling = false; mIceCompleted = false; -- cgit v1.2.3 From 47ebf19008f7b22c4d492223afa71ba72ed8e7ad Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Dec 2023 12:26:05 -0800 Subject: Connect to close neighboring regions and mute outgoing to them --- indra/newview/llvoicewebrtc.cpp | 300 +++++++++++++++++++++++----------------- 1 file changed, 172 insertions(+), 128 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9a1b3525d7..0ff8a09de8 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -85,6 +85,8 @@ extern LLMenuBarGL* gMenuBarView; extern void handle_voice_morphing_subscribe(); namespace { + + const F32 MAX_AUDIO_DIST = 50.0f; const F32 VOLUME_SCALE_WEBRTC = 0.01f; const F32 LEVEL_SCALE_WEBRTC = 0.008f; @@ -391,56 +393,35 @@ void LLWebRTCVoiceClient::updateSettings() ///////////////////////////// // session control messages -void LLWebRTCVoiceClient::predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) -{ - session->OnConnectionEstablished(channelID); -} - -void LLWebRTCVoiceClient::predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t& session, std::string channelID) -{ - session->OnConnectionFailure(channelID); -} - -void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID) -{ - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } - else if (mAudioSession && mAudioSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } - sessionState::for_each(boost::bind(predOnConnectionFailure, _1, channelID)); -} - -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, const LLUUID& regionID) { - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) - { - mAudioSession = mNextAudioSession; - mNextAudioSession.reset(); - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - } - else if (mAudioSession && mAudioSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - } - sessionState::for_each(boost::bind(predOnConnectionEstablished, _1, channelID)); -} - -void LLWebRTCVoiceClient::sessionState::OnConnectionEstablished(const std::string& channelID) -{ - if (channelID == mPrimaryConnectionID) + if (gAgent.getRegion()->getRegionID() == regionID) { + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } } } -void LLWebRTCVoiceClient::sessionState::OnConnectionFailure(const std::string &channelID) +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID) { - if (channelID == mPrimaryConnectionID) + if (gAgent.getRegion()->getRegionID() == regionID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + { + mAudioSession = mNextAudioSession; + mNextAudioSession.reset(); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } + else if (mAudioSession && mAudioSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + } } } @@ -457,6 +438,7 @@ void LLWebRTCVoiceClient::idle(void* user_data) void LLWebRTCVoiceClient::sessionState::processSessionStates() { + auto iter = mSessions.begin(); while (iter != mSessions.end()) { @@ -473,10 +455,20 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { - std::list::iterator iter = mWebRTCConnections.begin(); + // Estate voice requires connection to neighboring regions. + std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); + + std::list::iterator iter = mWebRTCConnections.begin(); while (iter != mWebRTCConnections.end()) { + if (neighbor_ids.find(iter->get()->getRegionID()) == neighbor_ids.end()) + { + // shut down connections to neighbors that are too far away. + iter->get()->shutDown(); + } + neighbor_ids.erase(iter->get()->getRegionID()); + if (!iter->get()->connectionStateMachine()) { iter = mWebRTCConnections.erase(iter); @@ -486,6 +478,15 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() ++iter; } } + + // add new connections for new neighbors + if (mSessionType == SESSION_TYPE_ESTATE) + { + for (auto &neighbor : neighbor_ids) + { + mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + } + } return !mWebRTCConnections.empty(); } @@ -505,26 +506,33 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - - if (mVoiceEnabled && (!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) + bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); + if ((!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) { // check to see if parcel changed. - std::string channelID = regionp->getRegionID().asString(); + std::string channelID = "Estate"; + S32 parcel_local_id = INVALID_PARCEL_ID; - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - S32 parcel_local_id = INVALID_PARCEL_ID; - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + if (voiceEnabled) { - if (!parcel->getParcelFlagAllowVoice()) - { - channelID.clear(); - } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - parcel_local_id = parcel->getLocalID(); - channelID += "-" + std::to_string(parcel->getLocalID()); + if (!parcel->getParcelFlagAllowVoice()) + { + channelID.clear(); + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + parcel_local_id = parcel->getLocalID(); + channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + } } } + else + { + channelID.clear(); + } if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || (!mAudioSession && !channelID.empty()) || @@ -532,9 +540,13 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { setSpatialChannel(channelID, "", parcel_local_id); } + } + else + { + } sessionState::processSessionStates(); - if (mVoiceEnabled) + if (voiceEnabled) { updatePosition(); sendPositionAndVolumeUpdate(true); @@ -766,26 +778,6 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) if (mSpatialCoordsDirty || force) { Json::Value spatial = Json::objectValue; - LLVector3d earPosition; - LLQuaternion earRot; - switch (mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earRot = mCameraRot; - break; - } spatial["sp"] = Json::objectValue; spatial["sp"]["x"] = (int) (mAvatarPosition[0] * 100); @@ -798,14 +790,14 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) 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["lp"]["x"] = (int) (mListenerPosition[0] * 100); + spatial["lp"]["y"] = (int) (mListenerPosition[1] * 100); + spatial["lp"]["z"] = (int) (mListenerPosition[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); + spatial["lh"]["x"] = (int) (mListenerRot[0] * 100); + spatial["lh"]["y"] = (int) (mListenerRot[1] * 100); + spatial["lh"]["z"] = (int) (mListenerRot[2] * 100); + spatial["lh"]["w"] = (int) (mListenerRot[3] * 100); mSpatialCoordsDirty = false; if (force || (uint_audio_level != mAudioLevel)) @@ -1479,79 +1471,120 @@ std::string LLWebRTCVoiceClient::getAudioSessionURI() ///////////////////////////// // Sending updates of current state -void LLWebRTCVoiceClient::enforceTether(void) +void LLWebRTCVoiceClient::enforceTether() { - LLVector3d tethered = mCameraRequestedPosition; + LLVector3d tethered = mListenerRequestedPosition; // constrain 'tethered' to within 50m of mAvatarPosition. { - F32 max_dist = 50.0f; - LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; + LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition; F32 camera_distance = (F32)camera_offset.magVec(); - if(camera_distance > max_dist) + if(camera_distance > MAX_AUDIO_DIST) { tethered = mAvatarPosition + - (max_dist / camera_distance) * camera_offset; + (MAX_AUDIO_DIST / camera_distance) * camera_offset; } } - if(dist_vec_squared(mCameraPosition, tethered) > 0.01) + if(dist_vec_squared(mListenerPosition, tethered) > 0.01) { - mCameraPosition = tethered; + mListenerPosition = tethered; mSpatialCoordsDirty = true; } } -void LLWebRTCVoiceClient::updatePosition(void) +void LLWebRTCVoiceClient::updateNeighboringRegions() { + static const std::vector neighbors {LLVector3d(0.0f, 1.0f, 0.0f), LLVector3d(0.707f, 0.707f, 0.0f), + LLVector3d(1.0f, 0.0f, 0.0f), LLVector3d(0.707f, -0.707f, 0.0f), + LLVector3d(0.0f, -1.0f, 0.0f), LLVector3d(-0.707f, -0.707f, 0.0f), + LLVector3d(-1.0f, 0.0f, 0.0f), LLVector3d(-0.707f, 0.707f, 0.0f)}; + // Estate voice requires connection to neighboring regions. + mNeighboringRegions.clear(); + + mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); + + // base off of speaker position as it'll move more slowly than camera position. + // Once we have hysteresis, we may be able to track off of speaker and camera position at 50m + // TODO: Add hysteresis so we don't flip-flop connections to neighbors + LLVector3d speaker_pos = LLWebRTCVoiceClient::getInstance()->getSpeakerPosition(); + for (auto &neighbor_pos : neighbors) + { + // include every region within 100m (2*MAX_AUDIO_DIST) to deal witht he fact that the camera + // can stray 50m away from the avatar. + LLViewerRegion *neighbor = LLWorld::instance().getRegionFromPosGlobal(speaker_pos + 2 * MAX_AUDIO_DIST * neighbor_pos); + if (neighbor && !neighbor->getRegionID().isNull()) + { + mNeighboringRegions.insert(neighbor->getRegionID()); + } + } +} + +void LLWebRTCVoiceClient::updatePosition(void) +{ LLViewerRegion *region = gAgent.getRegion(); if(region && isAgentAvatarValid()) { - LLVector3d pos; - LLQuaternion qrot; + LLVector3d avatar_pos = gAgentAvatarp->getPositionGlobal(); + LLQuaternion avatar_qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); + + avatar_pos += LLVector3d(0.f, 0.f, 1.f); // bump it up to head height // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... // They're currently always set to zero. - - // Send the current camera position to the voice code - pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); - - LLWebRTCVoiceClient::getInstance()->setCameraPosition( - pos, // position - LLVector3::zero, // velocity - LLViewerCamera::getInstance()->getQuaternion()); // rotation matrix - - // Send the current avatar position to the voice code - qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); - pos = gAgentAvatarp->getPositionGlobal(); + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + earRot = LLViewerCamera::getInstance()->getQuaternion(); + break; - // TODO: Can we get the head offset from outside the LLVOAvatar? - // pos += LLVector3d(mHeadOffset); - pos += LLVector3d(0.f, 0.f, 1.f); + case earLocAvatar: + earPosition = mAvatarPosition; + earRot = mAvatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earRot = LLViewerCamera::getInstance()->getQuaternion(); + break; + } + setListenerPosition(earPosition, // position + LLVector3::zero, // velocity + earRot); // rotation matrix - LLWebRTCVoiceClient::getInstance()->setAvatarPosition( - pos, // position - LLVector3::zero, // velocity - qrot); // rotation matrix + setAvatarPosition( + avatar_pos, // position + LLVector3::zero, // velocity + avatar_qrot); // rotation matrix enforceTether(); + + if (mSpatialCoordsDirty) + { + updateNeighboringRegions(); + } } } -void LLWebRTCVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) +void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { - mCameraRequestedPosition = position; + + mListenerPosition = position; - if(mCameraVelocity != velocity) + if(mListenerVelocity != velocity) { - mCameraVelocity = velocity; + mListenerVelocity = velocity; mSpatialCoordsDirty = true; } - if(mCameraRot != rot) + if(mListenerRot != rot) { - mCameraRot = rot; + mListenerRot = rot; mSpatialCoordsDirty = true; } } @@ -1925,7 +1958,8 @@ LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), mVolumeDirty(false), mMuteDirty(false), - mParticipantsChanged(false) + mParticipantsChanged(false), + mShuttingDown(false) { } @@ -2047,7 +2081,8 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() -{ +{ + mShuttingDown = true; for (auto &&connection : mWebRTCConnections) { connection->shutDown(); @@ -2686,7 +2721,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); return; } - LL_WARNS("Voice") << "Unable to connect voice." << result << LL_ENDL; + LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; } @@ -2753,7 +2788,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() mWebRTCAudioInterface->setMute(mMuted); mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); - LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID); + LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_SESSION_UP); } break; @@ -2767,7 +2802,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } case VOICE_STATE_SESSION_RETRY: - LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID); + LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; break; @@ -2903,10 +2938,19 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url void LLVoiceWebRTCConnection::setMuteMic(bool muted) { - mMuted = true; + mMuted = muted; if (mWebRTCAudioInterface) { - mWebRTCAudioInterface->setMute(muted); + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp && mRegionID == regionp->getRegionID()) + { + mWebRTCAudioInterface->setMute(muted); + } + else + { + // always mute to regions the agent isn't on, to prevent echo. + mWebRTCAudioInterface->setMute(true); + } } } -- cgit v1.2.3 From d2fee1c3450a71db5d88b2746ba8e2c468960ea6 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Dec 2023 13:21:06 -0800 Subject: oopse, forgot to set the requested listener position --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0ff8a09de8..0c9c5c5bdd 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1574,7 +1574,7 @@ void LLWebRTCVoiceClient::updatePosition(void) void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { - mListenerPosition = position; + mListenerRequestedPosition = position; if(mListenerVelocity != velocity) { -- cgit v1.2.3 From ec59d2477f686b7e2ee036bbc5a871d9fc735461 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 19 Dec 2023 18:37:34 -0800 Subject: add concept of primary/secondary connections --- indra/newview/llvoicewebrtc.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0c9c5c5bdd..b3de360aa9 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -5,7 +5,7 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * + * ne * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; @@ -506,6 +506,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } + mNeighboringRegions.insert(regionp->getRegionID()); bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); if ((!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) { @@ -540,10 +541,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { setSpatialChannel(channelID, "", parcel_local_id); } - } - else - { - } sessionState::processSessionStates(); if (voiceEnabled) @@ -822,7 +819,6 @@ void LLWebRTCVoiceClient::updateOwnVolume() { if (!mMuteMic && !mTuningMode) { audio_level = getAudioLevel(); - LL_WARNS("Voice") << "Level " << audio_level << LL_ENDL; } sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); @@ -2509,9 +2505,16 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } LLWebRTCVoiceClient::participantStatePtr_t participant = LLWebRTCVoiceClient::getInstance()->findParticipantByID(mChannelID, agent_id); - bool joined = voice_data[participant_id].get("j", Json::Value(false)).asBool(); + bool joined = false; + bool primary = false; + if (voice_data[participant_id].isMember("j")) + { + joined = true; + primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + } + new_participant |= joined; - if (!participant && joined) + if (!participant && joined && primary) { participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); } @@ -2541,9 +2544,15 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface { mWebRTCDataInterface = data_interface; mWebRTCDataInterface->setDataObserver(this); + Json::FastWriter writer; - Json::Value root = Json::objectValue; - root["j"] = true; + Json::Value root = Json::objectValue; + Json::Value join_obj = Json::objectValue; + if (gAgent.getRegion()->getRegionID() == mRegionID) + { + join_obj["p"] = true; + } + root["j"] = join_obj; std::string json_data = writer.write(root); mWebRTCDataInterface->sendData(json_data, false); } -- cgit v1.2.3 From 423892d0236dd202586f86dcf53c7d363553d859 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 24 Dec 2023 00:05:46 -0800 Subject: Don't need to send level data up to the server anymore --- indra/newview/llvoicewebrtc.cpp | 102 +++++++++++++++------------------------- 1 file changed, 37 insertions(+), 65 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b3de360aa9..99061c00da 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -508,45 +508,43 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } mNeighboringRegions.insert(regionp->getRegionID()); bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); - if ((!mAudioSession || mAudioSession->isSpatial()) && !mNextAudioSession) - { - // check to see if parcel changed. - std::string channelID = "Estate"; - S32 parcel_local_id = INVALID_PARCEL_ID; + // check to see if parcel changed. + std::string channelID = "Estate"; + S32 parcel_local_id = INVALID_PARCEL_ID; - if (voiceEnabled) + if (voiceEnabled) + { + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + if (!parcel->getParcelFlagAllowVoice()) { - if (!parcel->getParcelFlagAllowVoice()) - { - channelID.clear(); - } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) - { - parcel_local_id = parcel->getLocalID(); - channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); - } + channelID.clear(); + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + parcel_local_id = parcel->getLocalID(); + channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); } } - else - { - channelID.clear(); - } + } + else + { + channelID.clear(); + } - if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || - (!mAudioSession && !channelID.empty()) || - (mAudioSession && channelID != mAudioSession->mChannelID)) - { - setSpatialChannel(channelID, "", parcel_local_id); - } + if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || + (!mAudioSession && !mNextAudioSession && !channelID.empty()) || + (mAudioSession && channelID != mAudioSession->mChannelID)) + { + setSpatialChannel(channelID, "", parcel_local_id); } + sessionState::processSessionStates(); if (voiceEnabled) { updatePosition(); - sendPositionAndVolumeUpdate(true); + sendPositionUpdate(true); updateOwnVolume(); } } @@ -752,25 +750,14 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) } else { - sendPositionAndVolumeUpdate(true); + sendPositionUpdate(true); } } -void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) +void LLWebRTCVoiceClient::sendPositionUpdate(bool force) { 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 && !mTuningMode) - { - audio_level = getAudioLevel(); - uint_audio_level = (uint32_t) (audio_level*128); - - } if (mSpatialCoordsDirty || force) { @@ -797,21 +784,10 @@ void LLWebRTCVoiceClient::sendPositionAndVolumeUpdate(bool force) spatial["lh"]["w"] = (int) (mListenerRot[3] * 100); mSpatialCoordsDirty = false; - if (force || (uint_audio_level != mAudioLevel)) - { - spatial["p"] = uint_audio_level; - } spatial_data = writer.write(spatial); - } - if (force || (uint_audio_level != mAudioLevel)) - { - Json::Value volume = Json::objectValue; - volume["p"] = uint_audio_level; - volume_data = writer.write(volume); - } - mAudioLevel = uint_audio_level; - sessionState::for_each(boost::bind(predSendData, _1, spatial_data, volume_data)); + sessionState::for_each(boost::bind(predSendData, _1, spatial_data)); + } } void LLWebRTCVoiceClient::updateOwnVolume() { @@ -834,16 +810,12 @@ void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::session } } -void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data, const std::string& volume_data) +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data) { if (session->isSpatial() && !spatial_data.empty()) { session->sendData(spatial_data); } - else if (!volume_data.empty()) - { - session->sendData(volume_data); - } } void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) @@ -966,6 +938,7 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad // participant isn't already in one list or the other. result.reset(new participantState(agent_id)); mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); + mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); mParticipantsChanged = true; result->mAvatarIDValid = true; @@ -975,8 +948,6 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad { mMuteDirty = true; } - - mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) { @@ -1682,7 +1653,8 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) LL_DEBUGS("Voice") << "enabling" << LL_ENDL; LLVoiceChannel::getCurrentVoiceChannel()->activate(); status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; - + mSpatialCoordsDirty = true; + updatePosition(); if (!mIsCoroutineActive) { LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", @@ -1967,8 +1939,6 @@ LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::crea sessionState::ptr_t session(new sessionState()); session->mChannelID = channelID; session->mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); - - session->mPrimaryConnectionID = channelID; // add agent as participant session->addParticipant(gAgentID); @@ -2548,7 +2518,8 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface Json::FastWriter writer; Json::Value root = Json::objectValue; Json::Value join_obj = Json::objectValue; - if (gAgent.getRegion()->getRegionID() == mRegionID) + LLUUID regionID = gAgent.getRegion()->getRegionID(); + if (regionID == mRegionID) { join_obj["p"] = true; } @@ -2868,6 +2839,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); return false; } -- cgit v1.2.3 From c393c3bd2b6be38366c4191a190f69e700b6b822 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 24 Dec 2023 20:50:08 -0800 Subject: Add viewer-visible session ID to allow multiple sessions under same agent id --- indra/newview/llvoicewebrtc.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 99061c00da..305868e415 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -414,6 +414,10 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, { if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) { + if (mAudioSession) + { + mAudioSession->shutdownAllConnections(); + } mAudioSession = mNextAudioSession; mNextAudioSession.reset(); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); @@ -2593,6 +2597,8 @@ void LLVoiceWebRTCConnection::processIceUpdates() iceCompleted = mIceCompleted; mIceCompleted = false; } + + body["viewer_session"] = mViewerSession; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( @@ -2663,9 +2669,10 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result } LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - if (result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) + if (result.has("viewer_session") && result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) { mRemoteChannelSDP = result["jsep"]["sdp"].asString(); + mViewerSession = result["viewer_session"]; } else { @@ -2673,13 +2680,9 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result mOutstandingRequests--; return; } - 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; + << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); mOutstandingRequests--; @@ -2819,8 +2822,9 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } -void LLVoiceWebRTCConnection::sendData(const std::string& data) { - if (mWebRTCDataInterface) +void LLVoiceWebRTCConnection::sendData(const std::string& data) { + + if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) { mWebRTCDataInterface->sendData(data, false); } @@ -2858,6 +2862,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); LLSD body; body["logout"] = TRUE; + body["viewer_session"] = mViewerSession; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, -- cgit v1.2.3 From 3f2d52e5df0d31560cedb6748e2bf4134933be50 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 25 Dec 2023 17:41:48 -0800 Subject: Fix enable/disable issue when connecting to multiple regions --- indra/newview/llvoicewebrtc.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 305868e415..0c15dbccaf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -362,6 +362,7 @@ void LLWebRTCVoiceClient::cleanUp() mNextAudioSession.reset(); mAudioSession.reset(); + mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; } @@ -510,7 +511,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - mNeighboringRegions.insert(regionp->getRegionID()); bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); // check to see if parcel changed. std::string channelID = "Estate"; @@ -1215,6 +1215,7 @@ bool LLWebRTCVoiceClient::setSpatialChannel( } else { + mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); return switchChannel(uri, parcel_local_id == INVALID_PARCEL_ID ? sessionState::SESSION_TYPE_ESTATE : sessionState::SESSION_TYPE_PARCEL, parcel_local_id); } } @@ -1535,10 +1536,7 @@ void LLWebRTCVoiceClient::updatePosition(void) enforceTether(); - if (mSpatialCoordsDirty) - { - updateNeighboringRegions(); - } + updateNeighboringRegions(); } } @@ -2843,7 +2841,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); return false; } -- cgit v1.2.3 From 6abc8b5c99a2b8bf8b66eb6d5ed4acc6fecb85c6 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 2 Jan 2024 15:28:31 -0800 Subject: Set mute, speaker volume, mic gain on creation of new connection. --- indra/newview/llvoicewebrtc.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0c15dbccaf..dd18fabf47 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -358,8 +358,6 @@ void LLWebRTCVoiceClient::terminate() void LLWebRTCVoiceClient::cleanUp() { - LL_DEBUGS("Voice") << LL_ENDL; - mNextAudioSession.reset(); mAudioSession.reset(); mNeighboringRegions.clear(); @@ -489,7 +487,10 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { for (auto &neighbor : neighbor_ids) { - mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connection->setMicGain(mMicGain); + connection->setMuteMic(mMuted); + connection->setSpeakerVolume(mSpeakerVolume); } } return !mWebRTCConnections.empty(); @@ -2494,7 +2495,10 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) { - LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + if (agent_id != gAgentID) + { + LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + } } else { -- cgit v1.2.3 From b5c1f0fd6eda053bd5603cb424d35da4cbd076c5 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 12 Jan 2024 17:11:39 -0800 Subject: checkpoint for adhoc voice --- indra/newview/llvoicewebrtc.cpp | 637 ++++++++++++++++++++-------------------- 1 file changed, 324 insertions(+), 313 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index dd18fabf47..0f42c8ea8a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -246,9 +246,9 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mDevicesListUpdated(false), mAreaVoiceDisabled(false), - mAudioSession(), // TBD - should be NULL - mAudioSessionChanged(false), - mNextAudioSession(), + mSession(), // TBD - should be NULL + mSessionChanged(false), + mNextSession(), mCurrentParcelLocalID(0), @@ -358,8 +358,8 @@ void LLWebRTCVoiceClient::terminate() void LLWebRTCVoiceClient::cleanUp() { - mNextAudioSession.reset(); - mAudioSession.reset(); + mNextSession.reset(); + mSession.reset(); mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "exiting" << LL_ENDL; @@ -396,11 +396,11 @@ void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, cons { if (gAgent.getRegion()->getRegionID() == regionID) { - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + if (mNextSession && mNextSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); } - else if (mAudioSession && mAudioSession->mChannelID == channelID) + else if (mSession && mSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); } @@ -411,17 +411,17 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, { if (gAgent.getRegion()->getRegionID() == regionID) { - if (mNextAudioSession && mNextAudioSession->mChannelID == channelID) + if (mNextSession && mNextSession->mChannelID == channelID) { - if (mAudioSession) + if (mSession) { - mAudioSession->shutdownAllConnections(); + mSession->shutdownAllConnections(); } - mAudioSession = mNextAudioSession; - mNextAudioSession.reset(); + mSession = mNextSession; + mNextSession.reset(); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } - else if (mAudioSession && mAudioSession->mChannelID == channelID) + else if (mSession && mSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } @@ -458,20 +458,9 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { - - // Estate voice requires connection to neighboring regions. - std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); - std::list::iterator iter = mWebRTCConnections.begin(); while (iter != mWebRTCConnections.end()) { - if (neighbor_ids.find(iter->get()->getRegionID()) == neighbor_ids.end()) - { - // shut down connections to neighbors that are too far away. - iter->get()->shutDown(); - } - neighbor_ids.erase(iter->get()->getRegionID()); - if (!iter->get()->connectionStateMachine()) { iter = mWebRTCConnections.erase(iter); @@ -481,19 +470,62 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() ++iter; } } + return !mWebRTCConnections.empty(); +} - // add new connections for new neighbors - if (mSessionType == SESSION_TYPE_ESTATE) + +////////////////////////// +// LLWebRTCVoiceClient::estateSessionState + +LLWebRTCVoiceClient::estateSessionState::estateSessionState() +{ + mChannelID = "Estate"; + LLUUID region_id = gAgent.getRegion()->getRegionID(); + + mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, INVALID_PARCEL_ID, "Estate")); +} + +LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) +{ + LLUUID region_id = gAgent.getRegion()->getRegionID(); + mChannelID = channelID; + mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); +} + +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID) +{ +} + +bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() +{ + + // Estate voice requires connection to neighboring regions. + std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); + + for (auto& connection : mWebRTCConnections) { - for (auto &neighbor : neighbor_ids) + boost::shared_ptr spatialConnection = + boost::static_pointer_cast(connection); + + LLUUID regionID = spatialConnection.get()->getRegionID(); + + if (neighbor_ids.find(regionID) == neighbor_ids.end()) { - connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); - connection->setMicGain(mMicGain); - connection->setMuteMic(mMuted); - connection->setSpeakerVolume(mSpeakerVolume); + // shut down connections to neighbors that are too far away. + spatialConnection.get()->shutDown(); } + neighbor_ids.erase(regionID); } - return !mWebRTCConnections.empty(); + + // add new connections for new neighbors + for (auto &neighbor : neighbor_ids) + { + connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connection->setMicGain(mMicGain); + connection->setMuteMic(mMuted); + connection->setSpeakerVolume(mSpeakerVolume); + } + return LLWebRTCVoiceClient::sessionState::processConnectionStates(); } void LLWebRTCVoiceClient::voiceConnectionCoro() @@ -506,49 +538,75 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() while (!sShuttingDown) { llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - // add session for region or parcel voice. - LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp || regionp->getRegionID().isNull()) + bool voiceEnabled = mVoiceEnabled; + + if (!isAgentAvatarValid()) { continue; } - bool voiceEnabled = mVoiceEnabled && regionp->isVoiceEnabled(); - // check to see if parcel changed. - std::string channelID = "Estate"; - S32 parcel_local_id = INVALID_PARCEL_ID; - if (voiceEnabled) + if (inSpatialChannel()) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + // add session for region or parcel voice. + LLViewerRegion *regionp = gAgent.getRegion(); + if (!regionp || regionp->getRegionID().isNull()) + { + continue; + } + + updatePosition(); + + voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); + + if (voiceEnabled) { - if (!parcel->getParcelFlagAllowVoice()) + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // check to see if parcel changed. + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - channelID.clear(); + // parcel voice + if (!parcel->getParcelFlagAllowVoice()) + { + voiceEnabled = false; + } + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + { + S32 parcel_local_id = parcel->getLocalID(); + std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + + if (!inOrJoiningChannel(channelID)) + { + startParcelSession(channelID, parcel_local_id); + } + } + else + { + // parcel using estate voice + if (!inEstateChannel()) + { + startEstateSession(); + } + } } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) + else { - parcel_local_id = parcel->getLocalID(); - channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + // estate voice + if (!inEstateChannel()) + { + startEstateSession(); + } } } - } - else - { - channelID.clear(); - } - - if ((mNextAudioSession && channelID != mNextAudioSession->mChannelID) || - (!mAudioSession && !mNextAudioSession && !channelID.empty()) || - (mAudioSession && channelID != mAudioSession->mChannelID)) - { - setSpatialChannel(channelID, "", parcel_local_id); + else + { + // voice is disabled, so leave and disable PTT + leaveChannel(true); + } } sessionState::processSessionStates(); if (voiceEnabled) { - updatePosition(); sendPositionUpdate(true); updateOwnVolume(); } @@ -591,9 +649,9 @@ void LLWebRTCVoiceClient::requestRelog() void LLWebRTCVoiceClient::leaveAudioSession() { - if(mAudioSession) + if(mSession) { - LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mChannelID << LL_ENDL; + LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; } else { @@ -866,11 +924,11 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) { - if(session == mAudioSession) + if(session == mSession) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the current session)" << LL_ENDL; } - else if(session == mNextAudioSession) + else if(session == mNextSession) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the next session)" << LL_ENDL; } @@ -891,17 +949,17 @@ void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) void LLWebRTCVoiceClient::muteListChanged() { // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - if(mAudioSession) + if(mSession) { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + participantMap::iterator iter = mSession->mParticipantsByURI.begin(); - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + for(; iter != mSession->mParticipantsByURI.end(); iter++) { participantStatePtr_t p(iter->second); // Check to see if this participant is on the mute list already if(p->updateMuteState()) - mAudioSession->mVolumeDirty = true; + mSession->mVolumeDirty = true; } } } @@ -1031,32 +1089,13 @@ void LLWebRTCVoiceClient::sessionState::removeAllParticipants() } } -/*static*/ -void LLWebRTCVoiceClient::sessionState::VerifySessions() -{ - return; - /* - std::map::iterator it = mSessions.begin(); - while (it != mSessions.end()) - { - if ((*it).second.expired()) - { - LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; - it = mSessions.erase(it); - } - else - ++it; - } - */ -} - void LLWebRTCVoiceClient::getParticipantList(std::set &participants) { - if(mAudioSession) + if(mSession) { - for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); - iter != mAudioSession->mParticipantsByUUID.end(); + for(participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); + iter != mSession->mParticipantsByUUID.end(); iter++) { participants.insert(iter->first); @@ -1066,9 +1105,9 @@ void LLWebRTCVoiceClient::getParticipantList(std::set &participants) bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) { - if(mAudioSession) + if(mSession) { - return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end()); } return false; } @@ -1139,54 +1178,32 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co } } -bool LLWebRTCVoiceClient::switchChannel(const std::string channelID, - sessionState::ESessionType session_type, - S32 parcel_local_id) +bool LLWebRTCVoiceClient::startEstateSession() { - 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(); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - deleteSession(mAudioSession); - } - - if (channelID.empty()) - { - // Leave any channel we may be in - LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + leaveChannel(false); + mNextSession = addSession("Estate", sessionState::ptr_t(new estateSessionState())); + return true; +} - if (mNextAudioSession) - { - deleteSession(mNextAudioSession); - } - // If voice was on, turn it off - if (LLVoiceClient::getInstance()->getUserPTTState()) - { - LLVoiceClient::getInstance()->setUserPTTState(false); - } +bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 parcelID) +{ + leaveChannel(false); + mNextSession = addSession(channelID, sessionState::ptr_t(new parcelSessionState(channelID, parcelID))); + return true; +} - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - } - else - { - if (mNextAudioSession) - { - deleteSession(mNextAudioSession); - } - LL_DEBUGS("Voice") << "switching to channel " << channelID << LL_ENDL; - mNextAudioSession = addSession(channelID, parcel_local_id); - mNextAudioSession->mSessionType = session_type; - } - return true; +bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID) +{ + leaveChannel(false); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID))); + return true; } void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) { - mNextAudioSession = session; + mNextSession = session; - if (mAudioSession) + if (mSession) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. @@ -1194,36 +1211,9 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::setNonSpatialChannel( - const std::string &uri, - const std::string &credentials) -{ - switchChannel(uri, sessionState::SESSION_TYPE_P2P); -} - -bool LLWebRTCVoiceClient::setSpatialChannel( - const std::string &uri, const std::string &credentials, S32 parcel_local_id) -{ - mAreaVoiceDisabled = uri.empty(); - - LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - - if((mAudioSession && !mAudioSession->isSpatial()) || (mNextAudioSession && !mNextAudioSession->isSpatial())) - { - // 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; - return false; - } - else - { - mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); - return switchChannel(uri, parcel_local_id == INVALID_PARCEL_ID ? sessionState::SESSION_TYPE_ESTATE : sessionState::SESSION_TYPE_PARCEL, parcel_local_id); - } -} - void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) { - switchChannel(uuid.asString(), sessionState::SESSION_TYPE_P2P); + startAdHocSession(uuid.asString()); } void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) @@ -1242,9 +1232,7 @@ bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); if(session) { - session->mSessionType = sessionState::ESessionType::SESSION_TYPE_P2P; - - joinSession(session); + startAdHocSession(channelID); return true; } @@ -1309,14 +1297,12 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; // Make sure we don't rejoin the current session. - sessionStatePtr_t oldNextSession(mNextAudioSession); - mNextAudioSession.reset(); + sessionStatePtr_t oldNextSession(mNextSession); + mNextSession.reset(); // Most likely this will still be the current session at this point, but check it anyway. reapSession(oldNextSession); - verifySessionState(); - sessionTerminate(); } @@ -1419,13 +1405,23 @@ std::string LLWebRTCVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) return avatar->getFullname(); } -bool LLWebRTCVoiceClient::inSpatialChannel(void) +bool LLWebRTCVoiceClient::inOrJoiningChannel(const std::string& channelID) { - bool result = false; + return (mSession && mSession->mChannelID == channelID) || (mNextSession && mNextSession->mChannelID == channelID); +} + +bool LLWebRTCVoiceClient::inEstateChannel() +{ + return (mSession && mSession->isEstate()) || (mNextSession && mNextSession->isEstate()); +} + +bool LLWebRTCVoiceClient::inSpatialChannel() +{ + bool result = true; - if(mAudioSession) + if(mSession) { - result = mAudioSession->isSpatial(); + result = mSession->isSpatial(); } return result; @@ -1435,8 +1431,8 @@ std::string LLWebRTCVoiceClient::getAudioSessionURI() { std::string result; - if(mAudioSession) - result = mAudioSession->mChannelID; + if(mSession) + result = mSession->mChannelID; return result; } @@ -1599,14 +1595,29 @@ bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string return result; } -void LLWebRTCVoiceClient::leaveChannel(void) +void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) { - if (mAudioSession || mNextAudioSession) - { - LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; - mChannelName.clear(); - sessionTerminate(); - } + mChannelName.clear(); + + if (mSession) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + deleteSession(mSession); + } + + if (mNextSession) + { + deleteSession(mNextSession); + } + + // If voice was on, turn it off + if (stopTalking && LLVoiceClient::getInstance()->getUserPTTState()) + { + LLVoiceClient::getInstance()->setUserPTTState(false); + } } void LLWebRTCVoiceClient::setMuteMic(bool muted) @@ -1748,11 +1759,11 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain) BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return FALSE; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { // I'm not sure what the semantics of this should be. @@ -1766,11 +1777,11 @@ BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) { std::string result; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mDisplayName; @@ -1784,11 +1795,11 @@ std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mIsSpeaking; @@ -1800,11 +1811,11 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mIsModeratorMuted; @@ -1816,11 +1827,11 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { F32 result = 0; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if (participant) { result = participant->mLevel; @@ -1831,11 +1842,11 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) { BOOL result = FALSE; - if (!mAudioSession) + if (!mSession) { return result; } - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { // I'm not sure what the semantics of this should be. @@ -1850,7 +1861,7 @@ BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) { BOOL result = FALSE; - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mOnMuteList; @@ -1865,7 +1876,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(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mVolume; @@ -1879,9 +1890,9 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { - if(mAudioSession) + if(mSession) { - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if (participant && !participant->mIsSelf) { if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) @@ -1898,7 +1909,7 @@ void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); participant->mVolumeDirty = true; - mAudioSession->mVolumeDirty = true; + mSession->mVolumeDirty = true; } } } @@ -1907,7 +1918,7 @@ std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) { std::string result; - participantStatePtr_t participant(mAudioSession->findParticipant(id.asString())); + participantStatePtr_t participant(mSession->findParticipant(id.asString())); if(participant) { result = participant->mGroupID; @@ -1935,20 +1946,12 @@ LLWebRTCVoiceClient::sessionState::sessionState() : } /*static*/ -LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::createSession(const std::string& channelID, S32 parcelLocalID) +void LLWebRTCVoiceClient::sessionState::addSession( + const std::string &channelID, + LLWebRTCVoiceClient::sessionState::ptr_t& session) { - LLUUID region_id = gAgent.getRegion()->getRegionID(); - - sessionState::ptr_t session(new sessionState()); - session->mChannelID = channelID; - session->mWebRTCConnections.emplace_back(new LLVoiceWebRTCConnection(region_id, parcelLocalID, channelID)); - - // add agent as participant session->addParticipant(gAgentID); - mSessions[channelID] = session; - - return session; } LLWebRTCVoiceClient::sessionState::~sessionState() @@ -2038,7 +2041,7 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pairmSessionType == sessionState::SESSION_TYPE_P2P) + if (result && !result->isSpatial()) { return result; } @@ -2059,48 +2062,32 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string& channel_id, S32 parcel_local_id) +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionStatePtr_t &session) { - sessionStatePtr_t result; - - // Check whether there's already a session with this URI - result = sessionState::matchSessionByChannelID(channel_id); - - if(!result) + sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); + if (!existingSession) { // No existing session found. LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; - result = sessionState::createSession(channel_id, parcel_local_id); - result->setMuteMic(mMuteMic); - result->setMicGain(mMicGain); - result->setSpeakerVolume(mSpeakerVolume); + session->setMuteMic(mMuteMic); + session->setMicGain(mMicGain); + session->setSpeakerVolume(mSpeakerVolume); if (LLVoiceClient::instance().getVoiceEffectEnabled()) { - result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); + session->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); } + + sessionState::addSession(channel_id, session); + return session; } else { // Found an existing session - - if (channel_id != result->mChannelID) - { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing uri from " << result->mChannelID << " to " << channel_id << LL_ENDL; - - result->mChannelID = channel_id; - - verifySessionState(); - } - - LL_DEBUGS("Voice") << "returning existing session: CHANNEL " << channel_id << LL_ENDL; - } - - verifySessionState(); - - return result; + LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; + return existingSession; + } } void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) @@ -2111,29 +2098,23 @@ void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::session void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) { // 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. - bool deleteAudioSession = mAudioSession == session; - bool deleteNextAudioSession = mNextAudioSession == session; + bool deleteAudioSession = mSession == session; + bool deleteNextAudioSession = mNextSession == session; if (deleteAudioSession) { - mAudioSession.reset(); - mAudioSessionChanged = true; + mSession.reset(); + mSessionChanged = true; } // ditto for the next audio session if (deleteNextAudioSession) { - mNextAudioSession.reset(); + mNextSession.reset(); } } -void LLWebRTCVoiceClient::verifySessionState(void) -{ - sessionState::VerifySessions(); -} - void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { mParticipantObservers.insert(observer); @@ -2170,14 +2151,14 @@ void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) { LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" - << " mAudioSession=" << mAudioSession + << " mSession=" << mSession << LL_ENDL; - if(mAudioSession) + if(mSession) { if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) { - switch(mAudioSession->mErrorStatusCode) + switch(mSession->mErrorStatusCode) { case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; @@ -2193,11 +2174,11 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt } // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; + mSession->mErrorStatusCode = 0; } else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) { - switch(mAudioSession->mErrorStatusCode) + switch(mSession->mErrorStatusCode) { case HTTP_NOT_FOUND: // NOT_FOUND // *TODO: Should this be 503? @@ -2208,7 +2189,7 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; + mSession->mErrorStatusCode = 0; break; } } @@ -2315,10 +2296,63 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } +///////////////////////////// +// LLVoiceWebRTCConnection + +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection() : + mWebRTCAudioInterface(nullptr), + mWebRTCPeerConnection(nullptr), + mMuted(true), + mSpeakerVolume(0.0), + mMicGain(0.0) +{ + +} + +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. + return; + } + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; +} + + +void LLVoiceWebRTCConnection::setMuteMic(bool muted) +{ + mMuted = muted; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(muted); + } +} + +void LLVoiceWebRTCConnection::setMicGain(F32 gain) +{ + mMicGain = gain; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setSendVolume(gain); + } +} + +void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume) +{ + mSpeakerVolume = volume; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setReceiveVolume(volume); + } +} + ///////////////////////////// // WebRTC Signaling Handlers -LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string& channelID) : +LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : mWebRTCPeerConnection(nullptr), mWebRTCAudioInterface(nullptr), mWebRTCDataInterface(nullptr), @@ -2329,16 +2363,13 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, S32 par mRegionID(regionID), mParcelLocalID(parcelLocalID), mShutDown(false), - mOutstandingRequests(0), - mMuted(true), - mSpeakerVolume(0.0), - mMicGain(0.0) + mOutstandingRequests(0) { mWebRTCPeerConnection = llwebrtc::newPeerConnection(); mWebRTCPeerConnection->setSignalingObserver(this); } -LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() +LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2348,11 +2379,9 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() } assert(mOutstandingRequests == 0); mWebRTCPeerConnection->unsetSignalingObserver(this); - llwebrtc::freePeerConnection(mWebRTCPeerConnection); - mWebRTCPeerConnection = nullptr; } -void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +void LLVoiceWebRTCSpatialConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; @@ -2374,13 +2403,13 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs } } -void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +void LLVoiceWebRTCSpatialConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { LLMutexLock lock(&mVoiceStateMutex); mIceCandidates.push_back(candidate); } -void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2390,7 +2419,7 @@ void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD mOutstandingRequests--; } -void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2406,8 +2435,8 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS 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)); + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, ice_completed, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); return; } @@ -2421,7 +2450,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) +void LLVoiceWebRTCSpatialConnection::OnOfferAvailable(const std::string &sdp) { LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; LLMutexLock lock(&mVoiceStateMutex); @@ -2432,14 +2461,14 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) } } -void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +void LLVoiceWebRTCSpatialConnection::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) +void LLVoiceWebRTCSpatialConnection::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 @@ -2514,7 +2543,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } } -void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) +void LLVoiceWebRTCSpatialConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { if (data_interface) { @@ -2535,7 +2564,7 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface } } -void LLVoiceWebRTCConnection::OnRenegotiationNeeded() +void LLVoiceWebRTCSpatialConnection::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; if (!mShutDown) @@ -2544,13 +2573,13 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() } } -void LLVoiceWebRTCConnection::OnPeerShutDown() +void LLVoiceWebRTCSpatialConnection::OnPeerShutDown() { setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; } -void LLVoiceWebRTCConnection::processIceUpdates() +void LLVoiceWebRTCSpatialConnection::processIceUpdates() { if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { @@ -2611,8 +2640,8 @@ void LLVoiceWebRTCConnection::processIceUpdates() url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, iceCompleted, _1), - boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, iceCompleted, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1)); mOutstandingRequests++; mTrickling = true; } @@ -2620,7 +2649,7 @@ void LLVoiceWebRTCConnection::processIceUpdates() } } -bool LLVoiceWebRTCConnection::requestVoiceConnection() +bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() { LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); @@ -2657,13 +2686,13 @@ bool LLVoiceWebRTCConnection::requestVoiceConnection() url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); mOutstandingRequests++; return true; } -void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2690,7 +2719,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2702,8 +2731,8 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); return; } LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL; @@ -2711,7 +2740,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i mOutstandingRequests--; } -bool LLVoiceWebRTCConnection::connectionStateMachine() +bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() { processIceUpdates(); @@ -2824,7 +2853,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } -void LLVoiceWebRTCConnection::sendData(const std::string& data) { +void LLVoiceWebRTCSpatialConnection::sendData(const std::string& data) { if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) { @@ -2832,7 +2861,7 @@ void LLVoiceWebRTCConnection::sendData(const std::string& data) { } } -bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) +bool LLVoiceWebRTCSpatialConnection::breakVoiceConnection(bool corowait) { LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; if (mWebRTCDataInterface) @@ -2870,14 +2899,14 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); mOutstandingRequests++; return true; } -void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2896,7 +2925,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2908,8 +2937,8 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); return; } if (mWebRTCPeerConnection) @@ -2924,7 +2953,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url mOutstandingRequests--; } -void LLVoiceWebRTCConnection::setMuteMic(bool muted) +void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) { mMuted = muted; if (mWebRTCAudioInterface) @@ -2941,21 +2970,3 @@ void LLVoiceWebRTCConnection::setMuteMic(bool muted) } } } - -void LLVoiceWebRTCConnection::setMicGain(F32 gain) -{ - mMicGain = gain; - if (mWebRTCAudioInterface) - { - mWebRTCAudioInterface->setSendVolume(gain); - } -} - -void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume) -{ - mSpeakerVolume = volume; - if (mWebRTCAudioInterface) - { - mWebRTCAudioInterface->setReceiveVolume(volume); - } -} -- cgit v1.2.3 From ef90eba410c9357f62c8e5eaf7a0447bcb51d72f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 23 Jan 2024 14:05:12 -0800 Subject: OSX build fixes --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0f42c8ea8a..c9180b7e99 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2062,7 +2062,7 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() } -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionStatePtr_t &session) +LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session) { sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); if (!existingSession) -- cgit v1.2.3 From 0e6103e3a943c7f7726a93535048c634eb85eefc Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 25 Jan 2024 20:53:19 -0800 Subject: Checkpoint Ad-Hoc voice. Unlike vivox, P2P uses the ad-hoc voice mechanism, which is also used by group voice. --- indra/newview/llvoicewebrtc.cpp | 693 ++++++++++++++++++++++------------------ 1 file changed, 385 insertions(+), 308 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c9180b7e99..fcdd818757 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -419,15 +419,27 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, } mSession = mNextSession; mNextSession.reset(); - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } - else if (mSession && mSession->mChannelID == channelID) + + if (mSession && mSession->mChannelID == channelID) { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); } } } +void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) +{ + if (gAgent.getRegion()->getRegionID() == regionID) + { + if (mSession && mSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + } + } +} + void LLWebRTCVoiceClient::idle(void* user_data) { } @@ -492,8 +504,12 @@ LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &c mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); } -LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID) +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, const std::string& credentials) : + mCredentials(credentials) { + LLUUID region_id = gAgent.getRegion()->getRegionID(); + mChannelID = channelID; + mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); } bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() @@ -1112,7 +1128,6 @@ bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) return false; } - LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipant(const std::string &uri) { participantStatePtr_t result; @@ -1192,10 +1207,10 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p return true; } -bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID) +bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials) { leaveChannel(false); - mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID))); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials))); return true; } @@ -1211,9 +1226,8 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) } } -void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) -{ - startAdHocSession(uuid.asString()); +void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) +{ } void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) @@ -1226,16 +1240,7 @@ bool LLWebRTCVoiceClient::isValidChannel(std::string &channelID) } bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) -{ - // this is only ever used to answer incoming p2p call invites. - - sessionStatePtr_t session(findP2PSession(LLUUID(channelID))); - if(session) - { - startAdHocSession(channelID); - return true; - } - +{ return false; } @@ -1419,7 +1424,11 @@ bool LLWebRTCVoiceClient::inSpatialChannel() { bool result = true; - if(mSession) + if (mNextSession) + { + result = mNextSession->isSpatial(); + } + else if(mSession) { result = mSession->isSpatial(); } @@ -2299,77 +2308,25 @@ std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asSt ///////////////////////////// // LLVoiceWebRTCConnection -LLVoiceWebRTCConnection::LLVoiceWebRTCConnection() : - mWebRTCAudioInterface(nullptr), - mWebRTCPeerConnection(nullptr), - mMuted(true), - mSpeakerVolume(0.0), - mMicGain(0.0) -{ - -} - -LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() -{ - if (LLWebRTCVoiceClient::isShuttingDown()) - { - // peer connection and observers will be cleaned up - // by llwebrtc::terminate() on shutdown. - return; - } - llwebrtc::freePeerConnection(mWebRTCPeerConnection); - mWebRTCPeerConnection = nullptr; -} - - -void LLVoiceWebRTCConnection::setMuteMic(bool muted) -{ - mMuted = muted; - if (mWebRTCAudioInterface) - { - mWebRTCAudioInterface->setMute(muted); - } -} - -void LLVoiceWebRTCConnection::setMicGain(F32 gain) -{ - mMicGain = gain; - if (mWebRTCAudioInterface) - { - mWebRTCAudioInterface->setSendVolume(gain); - } -} - -void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume) -{ - mSpeakerVolume = volume; - if (mWebRTCAudioInterface) - { - mWebRTCAudioInterface->setReceiveVolume(volume); - } -} - -///////////////////////////// -// WebRTC Signaling Handlers - -LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : - mWebRTCPeerConnection(nullptr), +LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID) : mWebRTCAudioInterface(nullptr), mWebRTCDataInterface(nullptr), - mIceCompleted(false), - mTrickling(false), mVoiceConnectionState(VOICE_STATE_START_SESSION), - mChannelID(channelID), - mRegionID(regionID), - mParcelLocalID(parcelLocalID), + mMuted(true), mShutDown(false), - mOutstandingRequests(0) + mTrickling(false), + mIceCompleted(false), + mSpeakerVolume(0.0), + mMicGain(0.0), + mOutstandingRequests(0), + mChannelID(channelID), + mRegionID(regionID) { mWebRTCPeerConnection = llwebrtc::newPeerConnection(); mWebRTCPeerConnection->setSignalingObserver(this); } -LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() +LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2377,11 +2334,11 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() // by llwebrtc::terminate() on shutdown. return; } - assert(mOutstandingRequests == 0); - mWebRTCPeerConnection->unsetSignalingObserver(this); + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; } -void LLVoiceWebRTCSpatialConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; @@ -2403,13 +2360,13 @@ void LLVoiceWebRTCSpatialConnection::OnIceGatheringState(llwebrtc::LLWebRTCSigna } } -void LLVoiceWebRTCSpatialConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) { LLMutexLock lock(&mVoiceStateMutex); mIceCandidates.push_back(candidate); } -void LLVoiceWebRTCSpatialConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2419,7 +2376,7 @@ void LLVoiceWebRTCSpatialConnection::onIceUpdateComplete(bool ice_completed, con mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) +void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2435,8 +2392,8 @@ void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string u url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, ice_completed, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1), + boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1)); return; } @@ -2450,7 +2407,7 @@ void LLVoiceWebRTCSpatialConnection::onIceUpdateError(int retries, std::string u mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::OnOfferAvailable(const std::string &sdp) +void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; LLMutexLock lock(&mVoiceStateMutex); @@ -2461,110 +2418,14 @@ void LLVoiceWebRTCSpatialConnection::OnOfferAvailable(const std::string &sdp) } } -void LLVoiceWebRTCSpatialConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) { LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); } -void LLVoiceWebRTCSpatialConnection::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 = false; - bool primary = false; - if (voice_data[participant_id].isMember("j")) - { - joined = true; - primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); - } - - new_participant |= joined; - if (!participant && joined && primary) - { - participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); - } - if (participant) - { - if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) - { - if (agent_id != gAgentID) - { - LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); - } - } - else - { - F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; - // convert to decibles - participant->mLevel = level; - /* 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->mLevel > SPEAKING_AUDIO_LEVEL; - } - } - } - } -} - -void LLVoiceWebRTCSpatialConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) -{ - if (data_interface) - { - mWebRTCDataInterface = data_interface; - mWebRTCDataInterface->setDataObserver(this); - - Json::FastWriter writer; - Json::Value root = Json::objectValue; - Json::Value join_obj = Json::objectValue; - LLUUID regionID = gAgent.getRegion()->getRegionID(); - if (regionID == mRegionID) - { - join_obj["p"] = true; - } - root["j"] = join_obj; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); - } -} - -void LLVoiceWebRTCSpatialConnection::OnRenegotiationNeeded() +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; if (!mShutDown) @@ -2573,13 +2434,13 @@ void LLVoiceWebRTCSpatialConnection::OnRenegotiationNeeded() } } -void LLVoiceWebRTCSpatialConnection::OnPeerShutDown() +void LLVoiceWebRTCConnection::OnPeerShutDown() { setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::processIceUpdates() +void LLVoiceWebRTCConnection::processIceUpdates() { if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { @@ -2596,39 +2457,40 @@ void LLVoiceWebRTCSpatialConnection::processIceUpdates() LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { - LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; - return; + LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; + return; } std::string url = regionp->getCapability("VoiceSignalingRequest"); if (url.empty()) { - return; + return; } LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; - if (!mIceCandidates.empty()) { - LLSD candidates = LLSD::emptyArray(); - for (auto &ice_candidate : mIceCandidates) - { + if (!mIceCandidates.empty()) + { + 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(); + } + body["candidates"] = candidates; + mIceCandidates.clear(); } else if (mIceCompleted) { - LLSD body_candidate; - body_candidate["completed"] = true; - body["candidate"] = body_candidate; - iceCompleted = mIceCompleted; - mIceCompleted = false; + LLSD body_candidate; + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + iceCompleted = mIceCompleted; + mIceCompleted = false; } - + body["viewer_session"] = mViewerSession; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -2649,14 +2511,56 @@ void LLVoiceWebRTCSpatialConnection::processIceUpdates() } } -bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() + +void LLVoiceWebRTCConnection::setMuteMic(bool muted) { - LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + mMuted = muted; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(muted); + } +} - LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; +void LLVoiceWebRTCConnection::setMicGain(F32 gain) +{ + mMicGain = gain; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setSendVolume(gain); + } +} + +void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume) +{ + mSpeakerVolume = volume; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setReceiveVolume(volume); + } +} + +void LLVoiceWebRTCConnection::sendData(const std::string &data) +{ + if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && 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; + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); return false; } @@ -2666,60 +2570,105 @@ bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() return false; } - LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + 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; - LLSD jsep; - jsep["type"] = "offer"; - { - LLMutexLock lock(&mVoiceStateMutex); - jsep["sdp"] = mChannelSDP; - } - body["jsep"] = jsep; - if (mParcelLocalID != INVALID_PARCEL_ID) - { - body["parcel_local_id"] = mParcelLocalID; - } + body["logout"] = TRUE; + body["viewer_session"] = mViewerSession; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); mOutstandingRequests++; return true; } -void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { return; } - LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - if (result.has("viewer_session") && result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && result["jsep"].has("sdp")) + if (mWebRTCPeerConnection) { - mRemoteChannelSDP = result["jsep"]["sdp"].asString(); - mViewerSession = result["viewer_session"]; + mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); } else { - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - mOutstandingRequests--; - return; + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } + mOutstandingRequests--; +} - LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" +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(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); + return; + } + if (mWebRTCPeerConnection) + { + mOutstandingRequests++; + mWebRTCPeerConnection->shutdownConnection(); + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + } + mOutstandingRequests--; +} + + +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); + + if (result.has("viewer_session") && result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && + result["jsep"].has("sdp")) + { + mRemoteChannelSDP = result["jsep"]["sdp"].asString(); + mViewerSession = result["viewer_session"]; + } + else + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + mOutstandingRequests--; + return; + } + + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); mOutstandingRequests--; } -void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { @@ -2731,17 +2680,17 @@ void LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure(std::string url, LLCore::HttpRequest::DEFAULT_POLICY_ID, body, - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); + boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1)); return; } LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; } -bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() -{ +bool LLVoiceWebRTCConnection::connectionStateMachine() +{ processIceUpdates(); switch (getVoiceConnectionState()) @@ -2819,7 +2768,6 @@ bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; - break; case VOICE_STATE_DISCONNECT: breakVoiceConnection(true); @@ -2831,13 +2779,17 @@ bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() { { LLMutexLock lock(&mVoiceStateMutex); - if (!mShutDown) + if (!mShutDown) { mVoiceConnectionState = VOICE_STATE_START_SESSION; } else { - return mOutstandingRequests > 0; + if (mOutstandingRequests <= 0) + { + LLWebRTCVoiceClient::getInstance()->OnConnectionShutDown(mChannelID, mRegionID); + return false; + } } } break; @@ -2852,29 +2804,133 @@ bool LLVoiceWebRTCSpatialConnection::connectionStateMachine() return true; } +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. -void LLVoiceWebRTCSpatialConnection::sendData(const std::string& data) { - - if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) + if (binary) { - mWebRTCDataInterface->sendData(data, false); + 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 = false; + bool primary = false; + if (voice_data[participant_id].isMember("j")) + { + joined = true; + primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + } + + new_participant |= joined; + if (!participant && joined && primary) + { + participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); + } + if (participant) + { + if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) + { + if (agent_id != gAgentID) + { + LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); + } + } + else + { + F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; + // convert to decibles + participant->mLevel = level; + /* 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->mLevel > SPEAKING_AUDIO_LEVEL; + } + } + } } } -bool LLVoiceWebRTCSpatialConnection::breakVoiceConnection(bool corowait) +void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; - if (mWebRTCDataInterface) + if (data_interface) { - mWebRTCDataInterface->unsetDataObserver(this); - mWebRTCDataInterface = nullptr; + mWebRTCDataInterface = data_interface; + mWebRTCDataInterface->setDataObserver(this); + + Json::FastWriter writer; + Json::Value root = Json::objectValue; + Json::Value join_obj = Json::objectValue; + LLUUID regionID = gAgent.getRegion()->getRegionID(); + if (regionID == mRegionID) + { + join_obj["p"] = true; + } + root["j"] = join_obj; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); } - mWebRTCAudioInterface = nullptr; +} + +///////////////////////////// +// WebRTC Spatial Connection + +LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : + LLVoiceWebRTCConnection(regionID, channelID), + mParcelLocalID(parcelLocalID) +{ +} + +LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() +{ + if (LLWebRTCVoiceClient::isShuttingDown()) + { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. + return; + } + assert(mOutstandingRequests == 0); + mWebRTCPeerConnection->unsetSignalingObserver(this); +} + + +bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() +{ LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + + LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); return false; } @@ -2884,89 +2940,110 @@ bool LLVoiceWebRTCSpatialConnection::breakVoiceConnection(bool corowait) return false; } - 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); + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); LLSD body; - body["logout"] = TRUE; - body["viewer_session"] = mViewerSession; + 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(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1)); - setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); mOutstandingRequests++; return true; } -void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) +void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) { - if (LLWebRTCVoiceClient::isShuttingDown()) + mMuted = muted; + if (mWebRTCAudioInterface) { - return; + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp && mRegionID == regionp->getRegionID()) + { + mWebRTCAudioInterface->setMute(muted); + } + else + { + // always mute to regions the agent isn't on, to prevent echo. + mWebRTCAudioInterface->setMute(true); + } } +} - if (mWebRTCPeerConnection) - { - mOutstandingRequests++; - mWebRTCPeerConnection->shutdownConnection(); - } - else - { - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); - } - mOutstandingRequests--; +///////////////////////////// +// WebRTC Spatial Connection + +LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, const std::string& channelID, const std::string& credentials) : + LLVoiceWebRTCConnection(regionID, channelID), + mCredentials(credentials) +{ } -void LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) +LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { + // peer connection and observers will be cleaned up + // by llwebrtc::terminate() on shutdown. return; } - if (retries >= 0) - { - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( - url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); - return; - } - if (mWebRTCPeerConnection) + assert(mOutstandingRequests == 0); + mWebRTCPeerConnection->unsetSignalingObserver(this); +} + + +bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() +{ + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + + LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; + if (!regionp || !regionp->capabilitiesReceived()) { - mOutstandingRequests++; - mWebRTCPeerConnection->shutdownConnection(); + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + return false; } - else + + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) { - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + return false; } - mOutstandingRequests--; -} -void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) -{ - mMuted = muted; - if (mWebRTCAudioInterface) + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + + LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); + LLSD body; + LLSD jsep; + jsep["type"] = "offer"; { - LLViewerRegion *regionp = gAgent.getRegion(); - if (regionp && mRegionID == regionp->getRegionID()) - { - mWebRTCAudioInterface->setMute(muted); - } - else - { - // always mute to regions the agent isn't on, to prevent echo. - mWebRTCAudioInterface->setMute(true); - } + LLMutexLock lock(&mVoiceStateMutex); + jsep["sdp"] = mChannelSDP; } + body["jsep"] = jsep; + body["credentials"] = mCredentials; + body["channel"] = mChannelID; + body["channel_type"] = "multiagent"; + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLVoiceWebRTCAdHocConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCAdHocConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + mOutstandingRequests++; + return true; } -- cgit v1.2.3 From 02423047646cfc7d410e223611033d488f1c26b0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 29 Jan 2024 21:25:13 -0800 Subject: Treat adhoc/p2p as primary connections --- indra/newview/llvoicewebrtc.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index fcdd818757..a5c647c675 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2853,7 +2853,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } new_participant |= joined; - if (!participant && joined && primary) + if (!participant && joined && (primary || !isSpatial())) { participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); } @@ -2861,19 +2861,19 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) { - if (agent_id != gAgentID) - { + if (agent_id != gAgentID) + { LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); - } + } } else { - F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; - // convert to decibles - participant->mLevel = level; - /* 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->mLevel > SPEAKING_AUDIO_LEVEL; + F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; + // convert to decibles + participant->mLevel = level; + /* 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->mLevel > SPEAKING_AUDIO_LEVEL; } } } -- cgit v1.2.3 From ecd6b87b195321f522e27e5f62fe33eeaec48b4e Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 1 Feb 2024 09:20:11 -0800 Subject: checkpoint p2p/adhoc voice --- indra/newview/llvoicewebrtc.cpp | 51 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 29 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a5c647c675..b10e967986 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -235,7 +235,6 @@ bool LLWebRTCVoiceClient::sConnected = false; LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; LLWebRTCVoiceClient::LLWebRTCVoiceClient() : - mSessionTerminateRequested(false), mRelogRequested(false), mSpatialJoiningNum(0), @@ -247,7 +246,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mAreaVoiceDisabled(false), mSession(), // TBD - should be NULL - mSessionChanged(false), mNextSession(), mCurrentParcelLocalID(0), @@ -419,12 +417,13 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, } mSession = mNextSession; mNextSession.reset(); + mSession->addParticipant(gAgentID); } if (mSession && mSession->mChannelID == channelID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); } } } @@ -435,7 +434,7 @@ void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, con { if (mSession && mSession->mChannelID == channelID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + //LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); } } } @@ -651,29 +650,17 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() //========================================================================= -void LLWebRTCVoiceClient::sessionTerminate() -{ - mSessionTerminateRequested = true; -} - -void LLWebRTCVoiceClient::requestRelog() -{ - mSessionTerminateRequested = true; - mRelogRequested = true; -} - - void LLWebRTCVoiceClient::leaveAudioSession() { if(mSession) { LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; + mSession->shutdownAllConnections(); } else { LL_WARNS("Voice") << "called with no active session" << LL_ENDL; } - sessionTerminate(); } void LLWebRTCVoiceClient::clearCaptureDevices() @@ -1018,7 +1005,6 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad result.reset(new participantState(agent_id)); mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); - mParticipantsChanged = true; result->mAvatarIDValid = true; result->mAvatarID = agent_id; @@ -1033,6 +1019,10 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad result->mVolumeDirty = true; mVolumeDirty = true; } + if (!LLWebRTCVoiceClient::sShuttingDown) + { + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); + } LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; } @@ -1084,8 +1074,10 @@ void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceCli { mParticipantsByURI.erase(iter); mParticipantsByUUID.erase(iter2); - - mParticipantsChanged = true; + if (!LLWebRTCVoiceClient::sShuttingDown) + { + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); + } } } } @@ -1222,7 +1214,7 @@ void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. - sessionTerminate(); + mSession->shutdownAllConnections(); } } @@ -1308,7 +1300,7 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() // Most likely this will still be the current session at this point, but check it anyway. reapSession(oldNextSession); - sessionTerminate(); + leaveChannel(true); } std::string LLWebRTCVoiceClient::getCurrentChannel() @@ -1612,9 +1604,12 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. - sessionTerminate(); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + bool wasShuttingDown = mSession->mShuttingDown; deleteSession(mSession); + if (!wasShuttingDown) + { + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + } } if (mNextSession) @@ -1949,7 +1944,6 @@ LLWebRTCVoiceClient::sessionState::sessionState() : mErrorStatusCode(0), mVolumeDirty(false), mMuteDirty(false), - mParticipantsChanged(false), mShuttingDown(false) { } @@ -1959,7 +1953,6 @@ void LLWebRTCVoiceClient::sessionState::addSession( const std::string &channelID, LLWebRTCVoiceClient::sessionState::ptr_t& session) { - session->addParticipant(gAgentID); mSessions[channelID] = session; } @@ -2114,7 +2107,6 @@ void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) if (deleteAudioSession) { mSession.reset(); - mSessionChanged = true; } // ditto for the next audio session @@ -2210,7 +2202,8 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt << ", proximal is " << inSpatialChannel() << LL_ENDL; - mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN; + mIsProcessingChannels = + (status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN || status == LLVoiceClientStatusObserver::STATUS_JOINED); for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); @@ -2285,7 +2278,7 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se { // Found -- fill in the name // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); } // Check whether this is a p2p session whose caller name just resolved -- cgit v1.2.3 From 0cd8ad6ebb6e76bfc04cf6c423b0dc586161c6f7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 1 Feb 2024 21:53:33 -0800 Subject: Hang up when peer hangs up in ad-hoc driven p2p call --- indra/newview/llvoicewebrtc.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index b10e967986..17b61465cc 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -485,11 +485,9 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() } -////////////////////////// -// LLWebRTCVoiceClient::estateSessionState - LLWebRTCVoiceClient::estateSessionState::estateSessionState() { + mHangupOnLastLeave = false; mChannelID = "Estate"; LLUUID region_id = gAgent.getRegion()->getRegionID(); @@ -498,14 +496,16 @@ LLWebRTCVoiceClient::estateSessionState::estateSessionState() LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) { + mHangupOnLastLeave = false; LLUUID region_id = gAgent.getRegion()->getRegionID(); mChannelID = channelID; mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); } -LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, const std::string& credentials) : +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, const std::string& credentials, bool hangup_on_last_leave) : mCredentials(credentials) { + mHangupOnLastLeave = hangup_on_last_leave; LLUUID region_id = gAgent.getRegion()->getRegionID(); mChannelID = channelID; mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); @@ -1181,6 +1181,12 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co if (participant) { session->removeParticipant(participant); + if (session->mHangupOnLastLeave && + (id != gAgentID) && + (session->mParticipantsByURI.size() <= 1)) + { + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + } } } } @@ -1199,10 +1205,10 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p return true; } -bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials) +bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials, bool hangup_on_last_leave) { leaveChannel(false); - mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials))); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials, hangup_on_last_leave))); return true; } @@ -1602,14 +1608,7 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) if (mSession) { - // If we're already in a channel, or if we're joining one, terminate - // so we can rejoin with the new session data. - bool wasShuttingDown = mSession->mShuttingDown; deleteSession(mSession); - if (!wasShuttingDown) - { - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - } } if (mNextSession) @@ -2980,7 +2979,9 @@ void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) ///////////////////////////// // WebRTC Spatial Connection -LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, const std::string& channelID, const std::string& credentials) : +LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, + const std::string& channelID, + const std::string& credentials) : LLVoiceWebRTCConnection(regionID, channelID), mCredentials(credentials) { -- cgit v1.2.3 From 56fdc19e9521bca1387acbe8ac92eabad17d2d31 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 18:29:17 -0800 Subject: Checkpoint mute/volume --- indra/newview/llvoicewebrtc.cpp | 115 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 9 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 17b61465cc..e59e267d31 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -291,10 +291,6 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.serverType = VOICE_SERVER_TYPE; - // gMuteListp isn't set up at this point, so we defer this until later. -// gMuteListp->addObserver(&mutelist_listener); - - #if LL_DARWIN || LL_LINUX // HACK: THIS DOES NOT BELONG HERE // When the WebRTC daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. @@ -550,6 +546,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() LLCoros::set_consuming(true); try { + LLMuteList::getInstance()->addObserver(this); while (!sShuttingDown) { llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); @@ -919,6 +916,26 @@ void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) } } +void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volume) +{ + for (auto& connection : mWebRTCConnections) + { + connection->setUserVolume(id, volume); + } +} + +void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID& id, bool mute) +{ + if (mParticipantsByUUID.find(id) != mParticipantsByUUID.end()) + { + return; + } + for (auto& connection : mWebRTCConnections) + { + connection->setUserMute(id, mute); + } +} + void LLWebRTCVoiceClient::sendLocalAudioUpdates() { } @@ -1650,6 +1667,20 @@ void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionState session->setMicGain(gain); } +void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, + const LLUUID &id, + bool mute) +{ + session->setUserMute(id, mute); +} + +void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, + const LLUUID &id, + F32 volume) +{ + session->setUserVolume(id, volume); +} + void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) { LL_DEBUGS("Voice") @@ -1793,8 +1824,6 @@ std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) return result; } - - BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; @@ -1893,6 +1922,7 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { + F32 clamped_volume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); if(mSession) { participantStatePtr_t participant(mSession->findParticipant(id.asString())); @@ -1910,11 +1940,12 @@ void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); } - participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); + participant->mVolume = clamped_volume; participant->mVolumeDirty = true; mSession->mVolumeDirty = true; } } + sessionState::for_each(boost::bind(predSetUserVolume, _1, id, clamped_volume)); } std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) @@ -1930,6 +1961,24 @@ std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) return result; } +//////////////////////// +///LLMuteListObserver +/// + +void LLWebRTCVoiceClient::onChange() +{ +} + +void LLWebRTCVoiceClient::onChangeDetailed(const LLMute& mute) +{ + if (mute.mType == LLMute::AGENT) + { + bool muted = ((mute.mFlags & LLMute::flagVoiceChat) == 0); + sessionState::for_each(boost::bind(predSetUserMute, _1, mute.mID, muted)); + } +} + + BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() { return mAreaVoiceDisabled; @@ -2011,7 +2060,6 @@ void LLWebRTCVoiceClient::sessionState::reapEmptySessions() } } - bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) { ptr_t aLock(a.lock()); @@ -2153,7 +2201,6 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" << " mSession=" << mSession << LL_ENDL; - if(mSession) { if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) @@ -2531,6 +2578,29 @@ void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume) } } +void LLVoiceWebRTCConnection::setUserVolume(const LLUUID& id, F32 volume) +{ + Json::Value root = Json::objectValue; + Json::Value user_gain = Json::objectValue; + user_gain[id.asString()] = (uint32_t)volume*100; + root["ug"] = user_gain; + Json::FastWriter writer; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); +} + +void LLVoiceWebRTCConnection::setUserMute(const LLUUID& id, bool mute) +{ + Json::Value root = Json::objectValue; + Json::Value muted = Json::objectValue; + muted[id.asString()] = mute; + root["m"] = muted; + Json::FastWriter writer; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); +} + + void LLVoiceWebRTCConnection::sendData(const std::string &data) { if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) @@ -2825,6 +2895,8 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar return; } bool new_participant = false; + Json::Value mute = Json::objectValue; + Json::Value user_gain = Json::objectValue; for (auto &participant_id : voice_data.getMemberNames()) { LLUUID agent_id(participant_id); @@ -2842,6 +2914,16 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { joined = true; primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + bool isMuted = LLMuteList::getInstance()->isMuted(agent_id, LLMute::flagVoiceChat); + if (isMuted) + { + mute[participant_id] = true; + } + F32 volume; + if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) + { + user_gain[participant_id] = volume; + } } new_participant |= joined; @@ -2869,6 +2951,21 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } } } + Json::FastWriter writer; + Json::Value root = Json::objectValue; + if (mute.size() > 0) + { + root["m"] = mute; + } + if (user_gain.size() > 0) + { + root["ug"] = user_gain; + } + if (root.size() > 0) + { + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); + } } } -- cgit v1.2.3 From fd077d8a9b5e2a5d3971b4bcae06f5ee39e6c53b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 20:41:40 -0800 Subject: small logic errors in mute/volume for others code --- indra/newview/llvoicewebrtc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index e59e267d31..a3f874aacf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -918,6 +918,10 @@ void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volume) { + if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) + { + return; + } for (auto& connection : mWebRTCConnections) { connection->setUserVolume(id, volume); @@ -926,7 +930,7 @@ void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volu void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID& id, bool mute) { - if (mParticipantsByUUID.find(id) != mParticipantsByUUID.end()) + if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) { return; } @@ -2582,7 +2586,7 @@ void LLVoiceWebRTCConnection::setUserVolume(const LLUUID& id, F32 volume) { Json::Value root = Json::objectValue; Json::Value user_gain = Json::objectValue; - user_gain[id.asString()] = (uint32_t)volume*100; + user_gain[id.asString()] = (uint32_t)(volume*100); root["ug"] = user_gain; Json::FastWriter writer; std::string json_data = writer.write(root); -- cgit v1.2.3 From 73b00bfe94a6a2a4ea0abc2cf716d835820dfdba Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 3 Feb 2024 22:02:48 -0800 Subject: Handle 'device changed' callback --- indra/newview/llvoicewebrtc.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a3f874aacf..9304ce4740 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -8,7 +8,7 @@ * ne * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; + * License as published by the Free Software Foundation * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, @@ -689,16 +689,41 @@ void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) { + std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + clearRenderDevices(); + bool renderDeviceSet = false; for (auto &device : render_devices) { addRenderDevice(LLVoiceDevice(device.display_name, device.id)); + if (device.current && outputDevice == device.id) + { + setRenderDevice(outputDevice); + renderDeviceSet = true; + } + } + if (!renderDeviceSet) + { + setRenderDevice("Default"); } + clearCaptureDevices(); + bool captureDeviceSet = false; for (auto &device : capture_devices) { addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); + if (device.current && inputDevice == device.id) + { + setCaptureDevice(outputDevice); + captureDeviceSet = true; + } } + if (!captureDeviceSet) + { + setCaptureDevice("Default"); + } + setDevicesListUpdated(true); } -- cgit v1.2.3 From 33937833b7c07f4807fc78ba06b917c968b1f0f8 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 4 Feb 2024 15:49:27 -0800 Subject: Add server-generate VAD --- indra/newview/llvoicewebrtc.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9304ce4740..c70a60748d 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2951,7 +2951,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 volume; if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) { - user_gain[participant_id] = volume; + user_gain[participant_id] = uint16_t(volume*200); } } @@ -2974,9 +2974,11 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; // convert to decibles participant->mLevel = level; - /* 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->mLevel > SPEAKING_AUDIO_LEVEL; + + if (voice_data["participant_id"].isMember("v")) + { + participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool(); + } } } } -- cgit v1.2.3 From 1e882869dc56320e1bad2c5ff6da17115409477c Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 4 Feb 2024 15:51:07 -0800 Subject: Fix initial user gain send on join --- indra/newview/llvoicewebrtc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c70a60748d..a6e26644b0 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -5,7 +5,7 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * ne + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation @@ -2611,7 +2611,7 @@ void LLVoiceWebRTCConnection::setUserVolume(const LLUUID& id, F32 volume) { Json::Value root = Json::objectValue; Json::Value user_gain = Json::objectValue; - user_gain[id.asString()] = (uint32_t)(volume*100); + user_gain[id.asString()] = (uint32_t)(volume*200); // give it two decimal places with a range from 0-200, where 100 is normal root["ug"] = user_gain; Json::FastWriter writer; std::string json_data = writer.write(root); @@ -2951,7 +2951,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 volume; if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) { - user_gain[participant_id] = uint16_t(volume*200); + user_gain[participant_id] = (uint16_t)(volume*200); } } -- cgit v1.2.3 From 90efc71ef9f0fa43d6b3b8d2ebc8bda1556b669f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 6 Feb 2024 19:12:14 -0800 Subject: race between session established and data channel ready --- indra/newview/llvoicewebrtc.cpp | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index a6e26644b0..7b85795697 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2843,9 +2843,23 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID); - setVoiceConnectionState(VOICE_STATE_SESSION_UP); + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL); + break; + } + case VOICE_STATE_WAIT_FOR_DATA_CHANNEL: + { + if (mShutDown) + { + setVoiceConnectionState(VOICE_STATE_DISCONNECT); + break; + } + if (mWebRTCDataInterface) + { + sendJoin(); + setVoiceConnectionState(VOICE_STATE_SESSION_UP); + } + break; } - break; case VOICE_STATE_SESSION_UP: { if (mShutDown) @@ -3006,19 +3020,22 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface { mWebRTCDataInterface = data_interface; mWebRTCDataInterface->setDataObserver(this); + } +} - Json::FastWriter writer; - Json::Value root = Json::objectValue; - Json::Value join_obj = Json::objectValue; - LLUUID regionID = gAgent.getRegion()->getRegionID(); - if (regionID == mRegionID) - { - join_obj["p"] = true; - } - root["j"] = join_obj; - std::string json_data = writer.write(root); - mWebRTCDataInterface->sendData(json_data, false); +void LLVoiceWebRTCConnection::sendJoin() +{ + Json::FastWriter writer; + Json::Value root = Json::objectValue; + Json::Value join_obj = Json::objectValue; + LLUUID regionID = gAgent.getRegion()->getRegionID(); + if (regionID == mRegionID) + { + join_obj["p"] = true; } + root["j"] = join_obj; + std::string json_data = writer.write(root); + mWebRTCDataInterface->sendData(json_data, false); } ///////////////////////////// -- cgit v1.2.3 From d0cf247c2af3fbbdd023afe7abac8b7d83f9f8b9 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 7 Feb 2024 13:22:28 -0800 Subject: P2P checkpoint --- indra/newview/llvoicewebrtc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 7b85795697..399db275ab 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -3029,7 +3029,7 @@ void LLVoiceWebRTCConnection::sendJoin() Json::Value root = Json::objectValue; Json::Value join_obj = Json::objectValue; LLUUID regionID = gAgent.getRegion()->getRegionID(); - if (regionID == mRegionID) + if ((regionID == mRegionID) || !isSpatial()) { join_obj["p"] = true; } -- cgit v1.2.3 From c0f9baf41e93a3ab8fbd5835e579127ad6cc55a0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 19:04:01 -0800 Subject: another rebase merge issue --- indra/newview/llvoicewebrtc.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 399db275ab..df3ca08955 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1887,7 +1887,7 @@ BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { - F32 result = 0; + F32 result = 0.0; if (!mSession) { return result; @@ -1895,7 +1895,10 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) participantStatePtr_t participant(mSession->findParticipant(id.asString())); if (participant) { - result = participant->mLevel; + if (participant->mIsSpeaking) + { + result = participant->mLevel; + } } return result; } @@ -2965,7 +2968,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 volume; if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume)) { - user_gain[participant_id] = (uint16_t)(volume*200); + user_gain[participant_id] = (uint32_t)(volume * 200); } } @@ -2989,7 +2992,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar // convert to decibles participant->mLevel = level; - if (voice_data["participant_id"].isMember("v")) + if (voice_data[participant_id].isMember("v")) { participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool(); } -- cgit v1.2.3 From 70068a244b55c00a51cc48f09634a2fe4cedd54b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Fri, 9 Feb 2024 13:13:44 -0800 Subject: Fix ad-hoc voice --- indra/newview/llvoicewebrtc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index df3ca08955..d3b9f8ba2c 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -531,7 +531,9 @@ bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() // add new connections for new neighbors for (auto &neighbor : neighbor_ids) { - connectionPtr_t connection = mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + + mWebRTCConnections.push_back(connection); connection->setMicGain(mMicGain); connection->setMuteMic(mMuted); connection->setSpeakerVolume(mSpeakerVolume); -- cgit v1.2.3 From 7287cfbd7ad8f61114d0176c6fc7c3218c758900 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 22 Feb 2024 14:34:03 -0800 Subject: For spatial voice, determine voice provider based on server setting. --- indra/newview/llvoicewebrtc.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d3b9f8ba2c..0332557229 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -93,7 +93,8 @@ namespace { const F32 SPEAKING_TIMEOUT = 1.f; const F32 SPEAKING_AUDIO_LEVEL = 0.40; - static const std::string VOICE_SERVER_TYPE = "WebRTC"; + static const std::string VISIBLE_VOICE_SERVER_TYPE = "WebRTC"; + static const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.1f; @@ -289,7 +290,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mSpeakerVolume = 0.0; mVoiceVersion.serverVersion = ""; - mVoiceVersion.serverType = VOICE_SERVER_TYPE; + mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; #if LL_DARWIN || LL_LINUX // HACK: THIS DOES NOT BELONG HERE @@ -559,10 +560,24 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() continue; } + LLViewerRegion *regionp = gAgent.getRegion(); + if (!regionp) + { + continue; + } + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + { + // we've switched away from webrtc voice, so shut all channels down. + // leave channel can be called again and again without adverse effects. + // it merely tells channels to shut down if they're not already doing so. + leaveChannel(false); + continue; + } + if (inSpatialChannel()) { // add session for region or parcel voice. - LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp || regionp->getRegionID().isNull()) { continue; @@ -2752,6 +2767,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result } else { + LL_WARNS("Voice") << "Invalid voice provision request result:" << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mOutstandingRequests--; return; -- cgit v1.2.3 From 1e5e3f130a2651d0217c6e20b8971f9f6677ab81 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 22 Feb 2024 19:43:17 -0800 Subject: Initialize versions --- indra/newview/llvoicewebrtc.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 0332557229..f276811b25 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -291,6 +291,9 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.minorVersion = 0; + mVoiceVersion.majorVersion = 2; + mVoiceVersion.mBuildVersion = ""; #if LL_DARWIN || LL_LINUX // HACK: THIS DOES NOT BELONG HERE -- cgit v1.2.3 From 2117f8c4ba4565eef33f139a8c38b81f3c79648f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 3 Mar 2024 15:50:35 -0800 Subject: Refactor for vivox spatial and p2p General refactoring to improve vivox spacial and p2p voice including generalizing voice info instead of just using sip uri and credentials. Voice server type is also passed around in the generalized voice info blob. --- indra/newview/llvoicewebrtc.cpp | 205 +++++++++++++++------------------------- 1 file changed, 78 insertions(+), 127 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index f276811b25..791c733dcc 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -84,6 +84,8 @@ extern LLMenuBarGL* gMenuBarView; extern void handle_voice_morphing_subscribe(); +const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; + namespace { const F32 MAX_AUDIO_DIST = 50.0f; @@ -94,7 +96,6 @@ namespace { const F32 SPEAKING_AUDIO_LEVEL = 0.40; static const std::string VISIBLE_VOICE_SERVER_TYPE = "WebRTC"; - static const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.1f; @@ -267,8 +268,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mMicGain(0.0), mVoiceEnabled(false), - - mLipSyncEnabled(false), + mProcessChannels(false), mShutdownComplete(true), mPlayRequestCount(0), @@ -291,6 +291,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.internalVoiceServerType = WEBRTC_VOICE_SERVER_TYPE; mVoiceVersion.minorVersion = 0; mVoiceVersion.majorVersion = 2; mVoiceVersion.mBuildVersion = ""; @@ -374,7 +375,7 @@ const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() void LLWebRTCVoiceClient::updateSettings() { - setVoiceEnabled(voiceEnabled()); + setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled()); setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); @@ -383,7 +384,6 @@ void LLWebRTCVoiceClient::updateSettings() setRenderDevice(outputDevice); F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); setMicGain(mic_level); - setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); } @@ -456,7 +456,7 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() auto iter = mSessions.begin(); while (iter != mSessions.end()) { - if (!iter->second->processConnectionStates()) + if (!iter->second->processConnectionStates() && iter->second->mShuttingDown) { iter = mSessions.erase(iter); } @@ -568,8 +568,8 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() { continue; } - LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); - if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + + if (!mProcessChannels) { // we've switched away from webrtc voice, so shut all channels down. // leave channel can be called again and again without adverse effects. @@ -1271,43 +1271,15 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p return true; } -bool LLWebRTCVoiceClient::startAdHocSession(const std::string &channelID, const std::string &credentials, bool hangup_on_last_leave) +bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool hangup_on_last_leave) { leaveChannel(false); + std::string channelID = channelInfo["channel"]; + std::string credentials = channelInfo["credentials"]; mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials, hangup_on_last_leave))); return true; } -void LLWebRTCVoiceClient::joinSession(const sessionStatePtr_t &session) -{ - mNextSession = session; - - if (mSession) - { - // If we're already in a channel, or if we're joining one, terminate - // so we can rejoin with the new session data. - mSession->shutdownAllConnections(); - } -} - -void LLWebRTCVoiceClient::callUser(const LLUUID &uuid) -{ -} - -void LLWebRTCVoiceClient::endUserIMSession(const LLUUID &uuid) -{ - -} -bool LLWebRTCVoiceClient::isValidChannel(std::string &channelID) -{ - return(findP2PSession(LLUUID(channelID)) != NULL); - -} -bool LLWebRTCVoiceClient::answerInvite(std::string &channelID) -{ - return false; -} - bool LLWebRTCVoiceClient::isVoiceWorking() const { @@ -1355,16 +1327,11 @@ BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) return result; } - - -void LLWebRTCVoiceClient::declineInvite(std::string &sessionHandle) -{ -} void LLWebRTCVoiceClient::leaveNonSpatialChannel() { LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; - + // Make sure we don't rejoin the current session. sessionStatePtr_t oldNextSession(mNextSession); mNextSession.reset(); @@ -1375,9 +1342,9 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() leaveChannel(true); } -std::string LLWebRTCVoiceClient::getCurrentChannel() -{ - return getAudioSessionURI(); +void LLWebRTCVoiceClient::processChannels(bool process) +{ + mProcessChannels = process; } bool LLWebRTCVoiceClient::inProximalChannel() @@ -1385,16 +1352,6 @@ bool LLWebRTCVoiceClient::inProximalChannel() return inSpatialChannel(); } -std::string LLWebRTCVoiceClient::nameFromAvatar(LLVOAvatar *avatar) -{ - std::string result; - if(avatar) - { - result = nameFromID(avatar->getID()); - } - return result; -} - std::string LLWebRTCVoiceClient::nameFromID(const LLUUID &uuid) { std::string result; @@ -1469,11 +1426,6 @@ bool LLWebRTCVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) return result; } -std::string LLWebRTCVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) -{ - return avatar->getFullname(); -} - bool LLWebRTCVoiceClient::inOrJoiningChannel(const std::string& channelID) { return (mSession && mSession->mChannelID == channelID) || (mNextSession && mNextSession->mChannelID == channelID); @@ -1500,14 +1452,17 @@ bool LLWebRTCVoiceClient::inSpatialChannel() return result; } -std::string LLWebRTCVoiceClient::getAudioSessionURI() +std::string LLWebRTCVoiceClient::getAudioSessionChannelInfo() { - std::string result; - - if(mSession) - result = mSession->mChannelID; - - return result; + LLSD result; + + if (mSession) + { + result["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + result["channel_uri"] = mSession->mChannelID; + } + + return result; } ///////////////////////////// @@ -1670,8 +1625,6 @@ bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) { - mChannelName.clear(); - if (mSession) { deleteSession(mSession); @@ -1689,6 +1642,19 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) } } +bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo) +{ + return (channelInfo["voice_server_type"].asString() == WEBRTC_VOICE_SERVER_TYPE) && + (sessionState::hasSession(channelInfo["session_handle"].asString())); +} + +bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) +{ + return (channelInfo1["voice_server_type"] == WEBRTC_VOICE_SERVER_TYPE) && + (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) && + (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]); +} + void LLWebRTCVoiceClient::setMuteMic(bool muted) { @@ -1779,32 +1745,6 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) } } -bool LLWebRTCVoiceClient::voiceEnabled() -{ - return gSavedSettings.getBOOL("EnableVoiceChat") && - !gSavedSettings.getBOOL("CmdLineDisableVoice") && - !gNonInteractive; -} - -void LLWebRTCVoiceClient::setLipSyncEnabled(BOOL enabled) -{ - mLipSyncEnabled = enabled; -} - -BOOL LLWebRTCVoiceClient::lipSyncEnabled() -{ - - if ( mVoiceEnabled ) - { - return mLipSyncEnabled; - } - else - { - return FALSE; - } -} - - void LLWebRTCVoiceClient::setEarLocation(S32 loc) { if(mEarLocation != loc) @@ -2050,7 +1990,7 @@ LLWebRTCVoiceClient::sessionState::sessionState() : /*static*/ void LLWebRTCVoiceClient::sessionState::addSession( - const std::string &channelID, + const std::string & channelID, LLWebRTCVoiceClient::sessionState::ptr_t& session) { mSessions[channelID] = session; @@ -2152,7 +2092,6 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const } - void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() { mShuttingDown = true; @@ -2162,6 +2101,11 @@ void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() } } +void LLWebRTCVoiceClient::sessionState::revive() +{ + mShuttingDown = false; +} + LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session) { @@ -2187,6 +2131,8 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std { // Found an existing session LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; + existingSession->revive(); + return existingSession; } } @@ -2296,19 +2242,18 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session URI " << getAudioSessionURI() + << ", session channelInfo " << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL; - mIsProcessingChannels = - (status == LLVoiceClientStatusObserver::STATUS_LOGGED_IN || status == LLVoiceClientStatusObserver::STATUS_JOINED); + mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); ) { LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + observer->onChange(status, getAudioSessionChannelInfo(), inSpatialChannel()); // In case onError() deleted an entry. it = mStatusObservers.upper_bound(observer); } @@ -2459,18 +2404,19 @@ void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidat void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) { + mOutstandingRequests--; if (LLWebRTCVoiceClient::isShuttingDown()) { return; } mTrickling = false; - mOutstandingRequests--; } void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { + mOutstandingRequests--; return; } LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -2548,41 +2494,42 @@ void LLVoiceWebRTCConnection::processIceUpdates() LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); if (!regionp || !regionp->capabilitiesReceived()) { - LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; - return; + LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; + return; } std::string url = regionp->getCapability("VoiceSignalingRequest"); if (url.empty()) { - return; + return; } LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL; if (!mIceCandidates.empty()) { - 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(); + 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; + LLSD body_candidate; + body_candidate["completed"] = true; + body["candidate"] = body_candidate; + iceCompleted = mIceCompleted; + mIceCompleted = false; } body["viewer_session"] = mViewerSession; + body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( @@ -2694,6 +2641,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) LLSD body; body["logout"] = TRUE; body["viewer_session"] = mViewerSession; + body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, @@ -2708,6 +2656,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) { + mOutstandingRequests--; if (LLWebRTCVoiceClient::isShuttingDown()) { return; @@ -2722,13 +2671,13 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res { setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); } - mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { + mOutstandingRequests--; return; } if (retries >= 0) @@ -2756,6 +2705,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) { + mOutstandingRequests--; if (LLWebRTCVoiceClient::isShuttingDown()) { return; @@ -2772,7 +2722,6 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result { LL_WARNS("Voice") << "Invalid voice provision request result:" << result << LL_ENDL; setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - mOutstandingRequests--; return; } @@ -2780,13 +2729,13 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result << " channel sdp " << mRemoteChannelSDP << LL_ENDL; mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); - mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) { + mOutstandingRequests--; return; } if (retries >= 0) @@ -3117,6 +3066,8 @@ bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() body["parcel_local_id"] = mParcelLocalID; } + body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, LLCore::HttpRequest::DEFAULT_POLICY_ID, -- cgit v1.2.3 From b17c668d1257ba66e6a0bf84d0ad4c0da785ad64 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 3 Mar 2024 22:34:35 -0800 Subject: fix webrtc logic bugs and such --- indra/newview/llvoicewebrtc.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 791c733dcc..093f0a6d29 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -575,10 +575,8 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() // leave channel can be called again and again without adverse effects. // it merely tells channels to shut down if they're not already doing so. leaveChannel(false); - continue; } - - if (inSpatialChannel()) + else if (inSpatialChannel()) { // add session for region or parcel voice. if (!regionp || regionp->getRegionID().isNull()) @@ -637,7 +635,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } sessionState::processSessionStates(); - if (voiceEnabled) + if (mProcessChannels && voiceEnabled) { sendPositionUpdate(true); updateOwnVolume(); @@ -1274,8 +1272,9 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool hangup_on_last_leave) { leaveChannel(false); - std::string channelID = channelInfo["channel"]; - std::string credentials = channelInfo["credentials"]; + LL_WARNS("Voice") << "Start AdHoc Session " << channelInfo << LL_ENDL; + std::string channelID = channelInfo["channel_uri"]; + std::string credentials = channelInfo["channel_credentials"]; mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials, hangup_on_last_leave))); return true; } @@ -1452,7 +1451,7 @@ bool LLWebRTCVoiceClient::inSpatialChannel() return result; } -std::string LLWebRTCVoiceClient::getAudioSessionChannelInfo() +LLSD LLWebRTCVoiceClient::getAudioSessionChannelInfo() { LLSD result; @@ -1644,8 +1643,17 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo) { - return (channelInfo["voice_server_type"].asString() == WEBRTC_VOICE_SERVER_TYPE) && - (sessionState::hasSession(channelInfo["session_handle"].asString())); + if (channelInfo["voice_server_type"].asString() != WEBRTC_VOICE_SERVER_TYPE) + return false; + if (mSession) + { + if (!channelInfo["sessionHandle"].asString().empty()) + { + return mSession->mHandle == channelInfo["session_handle"].asString(); + } + return channelInfo["channel_uri"].asString() == mSession->mChannelID; + } + return false; } bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) @@ -2248,12 +2256,13 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; + LLSD channelInfo = getAudioSessionChannelInfo(); for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); ) { LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionChannelInfo(), inSpatialChannel()); + observer->onChange(status, channelInfo, inSpatialChannel()); // In case onError() deleted an entry. it = mStatusObservers.upper_bound(observer); } @@ -3151,6 +3160,7 @@ bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() body["credentials"] = mCredentials; body["channel"] = mChannelID; body["channel_type"] = "multiagent"; + body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, -- cgit v1.2.3 From 6d5304bd921d765dfa7e1ab6d5d0e0b64dea11f1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 5 Mar 2024 11:05:31 -0800 Subject: For webrtc, 'calling' dialog should remain up, disappearing when peer connects (p2p) --- indra/newview/llvoicewebrtc.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 093f0a6d29..77913ccdea 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -423,7 +423,10 @@ void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, if (mSession && mSession->mChannelID == channelID) { LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + if (!mSession->mNotifyOnFirstJoin) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + } } } } @@ -488,6 +491,7 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() LLWebRTCVoiceClient::estateSessionState::estateSessionState() { mHangupOnLastLeave = false; + mNotifyOnFirstJoin = false; mChannelID = "Estate"; LLUUID region_id = gAgent.getRegion()->getRegionID(); @@ -497,15 +501,20 @@ LLWebRTCVoiceClient::estateSessionState::estateSessionState() LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) { mHangupOnLastLeave = false; + mNotifyOnFirstJoin = false; LLUUID region_id = gAgent.getRegion()->getRegionID(); mChannelID = channelID; mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); } -LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, const std::string& credentials, bool hangup_on_last_leave) : +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, + const std::string& credentials, + bool notify_on_first_join, + bool hangup_on_last_leave) : mCredentials(credentials) { mHangupOnLastLeave = hangup_on_last_leave; + mNotifyOnFirstJoin = notify_on_first_join; LLUUID region_id = gAgent.getRegion()->getRegionID(); mChannelID = channelID; mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); @@ -1231,6 +1240,10 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy if (session) { result = session->addParticipant(id); + if (session->mNotifyOnFirstJoin && (id != gAgentID)) + { + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + } } return result; } @@ -1269,13 +1282,16 @@ bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 p return true; } -bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool hangup_on_last_leave) +bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) { leaveChannel(false); LL_WARNS("Voice") << "Start AdHoc Session " << channelInfo << LL_ENDL; std::string channelID = channelInfo["channel_uri"]; std::string credentials = channelInfo["channel_credentials"]; - mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, credentials, hangup_on_last_leave))); + mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, + credentials, + notify_on_first_join, + hangup_on_last_leave))); return true; } -- cgit v1.2.3 From 70044b9d2bbc594f0e8f3154feb2dbce77a7af82 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 5 Mar 2024 18:57:22 -0800 Subject: The response from the provision account call was being called twice for some reason --- indra/newview/llvoicewebrtc.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 77913ccdea..d593d5aca7 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2395,8 +2395,11 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() // by llwebrtc::terminate() on shutdown. return; } - llwebrtc::freePeerConnection(mWebRTCPeerConnection); - mWebRTCPeerConnection = nullptr; + if (mWebRTCPeerConnection) + { + llwebrtc::freePeerConnection(mWebRTCPeerConnection); + mWebRTCPeerConnection = nullptr; + } } void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) @@ -2689,8 +2692,10 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res if (mWebRTCPeerConnection) { - mOutstandingRequests++; - mWebRTCPeerConnection->shutdownConnection(); + if (mWebRTCPeerConnection->shutdownConnection()) + { + mOutstandingRequests++; + } } else { @@ -2765,6 +2770,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i } if (retries >= 0) { + LL_WARNS("Voice") << "Failure connecting to voice, retrying." << body << " RESULT: " << result << LL_ENDL; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, LLCore::HttpRequest::DEFAULT_POLICY_ID, -- cgit v1.2.3 From 42c7a335f840acf1b927b1ed6c8e06a6f00534d0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Mar 2024 23:13:11 -0800 Subject: Fix issue with spatial and p2p being up at the same time Also, fix issue with voice still happening in parcels where voice is disabled. --- indra/newview/llvoicewebrtc.cpp | 47 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index d593d5aca7..fe4c4129c5 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -523,33 +523,36 @@ LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &cha bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() { - // Estate voice requires connection to neighboring regions. - std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); - - for (auto& connection : mWebRTCConnections) + if (!mShuttingDown) { - boost::shared_ptr spatialConnection = - boost::static_pointer_cast(connection); + // Estate voice requires connection to neighboring regions. + std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); - LLUUID regionID = spatialConnection.get()->getRegionID(); - - if (neighbor_ids.find(regionID) == neighbor_ids.end()) + for (auto &connection : mWebRTCConnections) { - // shut down connections to neighbors that are too far away. - spatialConnection.get()->shutDown(); + boost::shared_ptr spatialConnection = + boost::static_pointer_cast(connection); + + LLUUID regionID = spatialConnection.get()->getRegionID(); + + if (neighbor_ids.find(regionID) == neighbor_ids.end()) + { + // shut down connections to neighbors that are too far away. + spatialConnection.get()->shutDown(); + } + neighbor_ids.erase(regionID); } - neighbor_ids.erase(regionID); - } - // add new connections for new neighbors - for (auto &neighbor : neighbor_ids) - { - connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + // add new connections for new neighbors + for (auto &neighbor : neighbor_ids) + { + connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); - mWebRTCConnections.push_back(connection); - connection->setMicGain(mMicGain); - connection->setMuteMic(mMuted); - connection->setSpeakerVolume(mSpeakerVolume); + mWebRTCConnections.push_back(connection); + connection->setMicGain(mMicGain); + connection->setMuteMic(mMuted); + connection->setSpeakerVolume(mSpeakerVolume); + } } return LLWebRTCVoiceClient::sessionState::processConnectionStates(); } @@ -636,7 +639,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } } } - else + if (!voiceEnabled) { // voice is disabled, so leave and disable PTT leaveChannel(true); -- cgit v1.2.3 From fd2d5c9c6c97254be7126171dc4472d3d5451ef8 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 9 Mar 2024 16:48:44 -0800 Subject: Code cleanup and commenting --- indra/newview/llvoicewebrtc.cpp | 2518 +++++++++++++++++---------------------- 1 file changed, 1121 insertions(+), 1397 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index fe4c4129c5..6e0cd32249 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -40,10 +40,10 @@ # include "expat/expat.h" #endif #include "llcallbacklist.h" -#include "llviewernetwork.h" // for gGridChoice +#include "llviewernetwork.h" // for gGridChoice #include "llbase64.h" #include "llviewercontrol.h" -#include "llappviewer.h" // for gDisconnected, gDisableVoice +#include "llappviewer.h" // for gDisconnected, gDisableVoice #include "llprocess.h" // Viewer includes @@ -78,12 +78,6 @@ #include "json/reader.h" #include "json/writer.h" -#define USE_SESSION_GROUPS 0 -#define VX_NULL_POSITION -2147483648.0 /*The Silence*/ - -extern LLMenuBarGL* gMenuBarView; -extern void handle_voice_morphing_subscribe(); - const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; namespace { @@ -92,51 +86,19 @@ namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; const F32 LEVEL_SCALE_WEBRTC = 0.008f; - const F32 SPEAKING_TIMEOUT = 1.f; const F32 SPEAKING_AUDIO_LEVEL = 0.40; - static const std::string VISIBLE_VOICE_SERVER_TYPE = "WebRTC"; + static const std::string REPORTED_VOICE_SERVER_TYPE = "Secondlife WebRTC Gateway"; // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - - // Timeout for connection to WebRTC - const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; - const F32 CONNECT_DNS_TIMEOUT = 5.0f; - - const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; - const S32 PROVISION_WAIT_TIMEOUT_SEC = 5; // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); - // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() - // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed - // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process - // backs up processing join requests. There is a log statement that records when channel joins take longer than 100 frames. - const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500; - - // How often to check for expired voice fonts in seconds - const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; - // Time of day at which WebRTC expires voice font subscriptions. - // Used to replace the time portion of received expiry timestamps. - static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; - - // Maximum length of capture buffer recordings in seconds. - const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; } // namespace -float LLWebRTCVoiceClient::getAudioLevel() -{ - if (mIsInTuningMode) - { - return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; - } - else - { - return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; - } -} + /////////////////////////////////////////////////////////////////////////////////////////////// @@ -233,83 +195,41 @@ LLSD LLVoiceWebRTCStats::read() /////////////////////////////////////////////////////////////////////////////////////////////// bool LLWebRTCVoiceClient::sShuttingDown = false; -bool LLWebRTCVoiceClient::sConnected = false; -LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; LLWebRTCVoiceClient::LLWebRTCVoiceClient() : - mRelogRequested(false), - mSpatialJoiningNum(0), - + mHidden(false), mTuningMode(false), mTuningMicGain(0.0), - mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume - mTuningSpeakerVolumeDirty(true), + mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume mDevicesListUpdated(false), - mAreaVoiceDisabled(false), - mSession(), // TBD - should be NULL - mNextSession(), - - mCurrentParcelLocalID(0), - - mBuddyListMapPopulated(false), - mBlockRulesListReceived(false), - mAutoAcceptRulesListReceived(false), - mSpatialCoordsDirty(false), - mIsInitialized(false), mMuteMic(false), - mMuteMicDirty(false), - mFriendsListDirty(true), mEarLocation(0), - mSpeakerVolumeDirty(true), mMicGain(0.0), mVoiceEnabled(false), mProcessChannels(false), - mShutdownComplete(true), - mPlayRequestCount(0), - mAvatarNameCacheConnection(), mIsInTuningMode(false), - mIsJoiningSession(false), - mIsWaitingForFonts(false), - mIsLoggingIn(false), mIsProcessingChannels(false), mIsCoroutineActive(false), mWebRTCPump("WebRTCClientPump"), mWebRTCDeviceInterface(nullptr) { sShuttingDown = false; - sConnected = false; - sPump = nullptr; - mSpeakerVolume = 0.0; + mSpeakerVolume = 0.0; - mVoiceVersion.serverVersion = ""; - mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.serverVersion = ""; + mVoiceVersion.voiceServerType = REPORTED_VOICE_SERVER_TYPE; mVoiceVersion.internalVoiceServerType = WEBRTC_VOICE_SERVER_TYPE; mVoiceVersion.minorVersion = 0; mVoiceVersion.majorVersion = 2; mVoiceVersion.mBuildVersion = ""; - -#if LL_DARWIN || LL_LINUX - // HACK: THIS DOES NOT BELONG HERE - // When the WebRTC daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. - // This should cause us to ignore SIGPIPE and handle the error through proper channels. - // This should really be set up elsewhere. Where should it go? - signal(SIGPIPE, SIG_IGN); - - // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. - // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. - signal(SIGCHLD, SIG_IGN); -#endif - - - gIdleCallbacks.addFunction(idle, this); } //--------------------------------------------------- @@ -328,10 +248,6 @@ LLWebRTCVoiceClient::~LLWebRTCVoiceClient() void LLWebRTCVoiceClient::init(LLPumpIO* pump) { // constructor will set up LLVoiceClient::getInstance() - sPump = pump; - - // LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", - // boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); llwebrtc::init(); mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); @@ -345,12 +261,10 @@ void LLWebRTCVoiceClient::terminate() return; } - mRelogRequested = false; mVoiceEnabled = false; llwebrtc::terminate(); sShuttingDown = true; - sPump = NULL; } //--------------------------------------------------- @@ -361,7 +275,7 @@ void LLWebRTCVoiceClient::cleanUp() mSession.reset(); mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); - LL_DEBUGS("Voice") << "exiting" << LL_ENDL; + LL_DEBUGS("Voice") << "Exiting" << LL_ENDL; } //--------------------------------------------------- @@ -386,177 +300,96 @@ void LLWebRTCVoiceClient::updateSettings() setMicGain(mic_level); } +// Observers +void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver *observer) +{ + mParticipantObservers.insert(observer); +} -///////////////////////////// -// session control messages +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver *observer) +{ + mParticipantObservers.erase(observer); +} -void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, const LLUUID& regionID) +void LLWebRTCVoiceClient::notifyParticipantObservers() { - if (gAgent.getRegion()->getRegionID() == regionID) + for (observer_set_t::iterator it = mParticipantObservers.begin(); it != mParticipantObservers.end();) { - if (mNextSession && mNextSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } - else if (mSession && mSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } + LLVoiceClientParticipantObserver *observer = *it; + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. + it = mParticipantObservers.upper_bound(observer); } } -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID) -{ - if (gAgent.getRegion()->getRegionID() == regionID) - { - if (mNextSession && mNextSession->mChannelID == channelID) - { - if (mSession) - { - mSession->shutdownAllConnections(); - } - mSession = mNextSession; - mNextSession.reset(); - mSession->addParticipant(gAgentID); - } - - if (mSession && mSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - if (!mSession->mNotifyOnFirstJoin) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - } - } - } +void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver *observer) +{ + mStatusObservers.insert(observer); } -void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) -{ - if (gAgent.getRegion()->getRegionID() == regionID) - { - if (mSession && mSession->mChannelID == channelID) - { - //LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - } - } +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver *observer) +{ + mStatusObservers.erase(observer); } -void LLWebRTCVoiceClient::idle(void* user_data) +void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) { -} + LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" + << " mSession=" << mSession << LL_ENDL; -//========================================================================= -// the following are methods to support the coroutine implementation of the -// voice connection and processing. They should only be called in the context -// of a coroutine. -// -// + LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) << ", session channelInfo " + << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL; -void LLWebRTCVoiceClient::sessionState::processSessionStates() -{ + mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; - auto iter = mSessions.begin(); - while (iter != mSessions.end()) + LLSD channelInfo = getAudioSessionChannelInfo(); + for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end();) { - if (!iter->second->processConnectionStates() && iter->second->mShuttingDown) - { - iter = mSessions.erase(iter); - } - else - { - iter++; - } + LLVoiceClientStatusObserver *observer = *it; + observer->onChange(status, channelInfo, inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); } -} -bool LLWebRTCVoiceClient::sessionState::processConnectionStates() -{ - std::list::iterator iter = mWebRTCConnections.begin(); - while (iter != mWebRTCConnections.end()) + // skipped to avoid speak button blinking + if (status != LLVoiceClientStatusObserver::STATUS_JOINING && + status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL && + status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) { - if (!iter->get()->connectionStateMachine()) - { - iter = mWebRTCConnections.erase(iter); - } - else + bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + + gAgent.setVoiceConnected(voice_status); + + if (voice_status) { - ++iter; + LLFirstUse::speak(true); } } - return !mWebRTCConnections.empty(); -} - - -LLWebRTCVoiceClient::estateSessionState::estateSessionState() -{ - mHangupOnLastLeave = false; - mNotifyOnFirstJoin = false; - mChannelID = "Estate"; - LLUUID region_id = gAgent.getRegion()->getRegionID(); - - mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, INVALID_PARCEL_ID, "Estate")); -} - -LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) -{ - mHangupOnLastLeave = false; - mNotifyOnFirstJoin = false; - LLUUID region_id = gAgent.getRegion()->getRegionID(); - mChannelID = channelID; - mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); } -LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, - const std::string& credentials, - bool notify_on_first_join, - bool hangup_on_last_leave) : - mCredentials(credentials) -{ - mHangupOnLastLeave = hangup_on_last_leave; - mNotifyOnFirstJoin = notify_on_first_join; - LLUUID region_id = gAgent.getRegion()->getRegionID(); - mChannelID = channelID; - mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); +void LLWebRTCVoiceClient::addObserver(LLFriendObserver *observer) +{ } -bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() -{ - - if (!mShuttingDown) - { - // Estate voice requires connection to neighboring regions. - std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); - - for (auto &connection : mWebRTCConnections) - { - boost::shared_ptr spatialConnection = - boost::static_pointer_cast(connection); - - LLUUID regionID = spatialConnection.get()->getRegionID(); - - if (neighbor_ids.find(regionID) == neighbor_ids.end()) - { - // shut down connections to neighbors that are too far away. - spatialConnection.get()->shutDown(); - } - neighbor_ids.erase(regionID); - } - - // add new connections for new neighbors - for (auto &neighbor : neighbor_ids) - { - connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); - - mWebRTCConnections.push_back(connection); - connection->setMicGain(mMicGain); - connection->setMuteMic(mMuted); - connection->setSpeakerVolume(mSpeakerVolume); - } - } - return LLWebRTCVoiceClient::sessionState::processConnectionStates(); +void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) +{ } +//--------------------------------------------------- +// Primary voice loop. +// This voice loop is called every 100ms plus the time it +// takes to process the various functions called in the loop +// The loop does the following: +// * gates whether we do channel processing depending on +// whether we're running a WebRTC voice channel or +// one from another voice provider. +// * If in spatial voice, it determines whether we've changed +// parcels, whether region/parcel voice settings have changed, +// etc. and manages whether the voice channel needs to change. +// * calls the state machines for the sessions to negotiate +// connection to various voice channels. +// * Sends updates to the voice server when this agent's +// voice levels, or positions have changed. void LLWebRTCVoiceClient::voiceConnectionCoro() { LL_DEBUGS("Voice") << "starting" << LL_ENDL; @@ -567,6 +400,13 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() LLMuteList::getInstance()->addObserver(this); while (!sShuttingDown) { + // TODO: Doing some measurement and calculation here, + // we could reduce the timeout to take into account the + // time spent on the previous loop to have the loop + // cycle at exactly 100ms, instead of 100ms + loop + // execution time. + // Could help with voice updates making for smoother + // voice when we're busy. llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); bool voiceEnabled = mVoiceEnabled; @@ -575,7 +415,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() continue; } - LLViewerRegion *regionp = gAgent.getRegion(); + LLViewerRegion *regionp = gAgent.getRegion(); if (!regionp) { continue; @@ -590,14 +430,14 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } else if (inSpatialChannel()) { + bool useEstateVoice = true; // add session for region or parcel voice. if (!regionp || regionp->getRegionID().isNull()) { + // no region, no voice. continue; } - updatePosition(); - voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); if (voiceEnabled) @@ -613,24 +453,18 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } else if (!parcel->getParcelFlagUseEstateVoiceChannel()) { - S32 parcel_local_id = parcel->getLocalID(); - std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + // use the parcel-specific voice channel. + S32 parcel_local_id = parcel->getLocalID(); + std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + useEstateVoice = false; if (!inOrJoiningChannel(channelID)) { startParcelSession(channelID, parcel_local_id); } } - else - { - // parcel using estate voice - if (!inEstateChannel()) - { - startEstateSession(); - } - } } - else + if (useEstateVoice && !inEstateChannel()) { // estate voice if (!inEstateChannel()) @@ -644,6 +478,12 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() // voice is disabled, so leave and disable PTT leaveChannel(true); } + else + { + // we're in spatial voice, and voice is enabled, so determine positions in order + // to send position updates. + updatePosition(); + } } sessionState::processSessionStates(); @@ -674,37 +514,68 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() cleanUp(); } +// For spatial, determine which neighboring regions to connect to +// for cross-region voice. +void LLWebRTCVoiceClient::updateNeighboringRegions() +{ + static const std::vector neighbors {LLVector3d(0.0f, 1.0f, 0.0f), LLVector3d(0.707f, 0.707f, 0.0f), + LLVector3d(1.0f, 0.0f, 0.0f), LLVector3d(0.707f, -0.707f, 0.0f), + LLVector3d(0.0f, -1.0f, 0.0f), LLVector3d(-0.707f, -0.707f, 0.0f), + LLVector3d(-1.0f, 0.0f, 0.0f), LLVector3d(-0.707f, 0.707f, 0.0f)}; + + // Estate voice requires connection to neighboring regions. + mNeighboringRegions.clear(); -//========================================================================= + mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); + + // base off of speaker position as it'll move more slowly than camera position. + // Once we have hysteresis, we may be able to track off of speaker and camera position at 50m + // TODO: Add hysteresis so we don't flip-flop connections to neighbors + LLVector3d speaker_pos = LLWebRTCVoiceClient::getInstance()->getSpeakerPosition(); + for (auto &neighbor_pos : neighbors) + { + // include every region within 100m (2*MAX_AUDIO_DIST) to deal witht he fact that the camera + // can stray 50m away from the avatar. + LLViewerRegion *neighbor = LLWorld::instance().getRegionFromPosGlobal(speaker_pos + 2 * MAX_AUDIO_DIST * neighbor_pos); + if (neighbor && !neighbor->getRegionID().isNull()) + { + mNeighboringRegions.insert(neighbor->getRegionID()); + } + } +} +//========================================================================= +// shut down the current audio session to make room for the next one. void LLWebRTCVoiceClient::leaveAudioSession() { - if(mSession) - { - LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; + if(mSession) + { + LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; mSession->shutdownAllConnections(); - } - else - { - LL_WARNS("Voice") << "called with no active session" << LL_ENDL; - } + } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + } } +//========================================================================= +// Device Management void LLWebRTCVoiceClient::clearCaptureDevices() { - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mCaptureDevices.clear(); + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mCaptureDevices.clear(); } void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) { - LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; mCaptureDevices.push_back(device); } LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices() { - return mCaptureDevices; + return mCaptureDevices; } void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) @@ -713,7 +584,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) } void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { - mDevicesListUpdated = state; + mDevicesListUpdated = state; } void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, @@ -758,21 +629,21 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi } void LLWebRTCVoiceClient::clearRenderDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mRenderDevices.clear(); +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mRenderDevices.clear(); } void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) { - LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; + LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL; mRenderDevices.push_back(device); } LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() { - return mRenderDevices; + return mRenderDevices; } void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) @@ -791,7 +662,7 @@ void LLWebRTCVoiceClient::tuningStart() void LLWebRTCVoiceClient::tuningStop() { - if (mIsInTuningMode) + if (mIsInTuningMode) { mWebRTCDeviceInterface->setTuningMode(false); mIsInTuningMode = false; @@ -809,15 +680,25 @@ void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{ +{ - if (volume != mTuningSpeakerVolume) - { - mTuningSpeakerVolume = volume; - mTuningSpeakerVolumeDirty = true; - } + if (volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = volume; + } } +float LLWebRTCVoiceClient::getAudioLevel() +{ + if (mIsInTuningMode) + { + return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; + } + else + { + return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + } +} float LLWebRTCVoiceClient::tuningGetEnergy(void) { @@ -826,420 +707,357 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void) bool LLWebRTCVoiceClient::deviceSettingsAvailable() { - bool result = true; - - if(mRenderDevices.empty() || mCaptureDevices.empty()) - result = false; - - return result; + bool result = true; + + if(mRenderDevices.empty() || mCaptureDevices.empty()) + result = false; + + return result; } bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; - mDevicesListUpdated = false; - return updated; + mDevicesListUpdated = false; + return updated; } void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) { - if(clearCurrentList) - { - clearCaptureDevices(); - clearRenderDevices(); - } + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } mWebRTCDeviceInterface->refreshDevices(); } -void LLWebRTCVoiceClient::giveUp() -{ - // All has failed. Clean up and stop trying. - LL_WARNS("Voice") << "Terminating Voice Service" << LL_ENDL; - cleanUp(); -} void LLWebRTCVoiceClient::setHidden(bool hidden) { mHidden = hidden; - if (mHidden && inSpatialChannel()) - { - // get out of the channel entirely - leaveAudioSession(); - } - else + if (inSpatialChannel()) { - sendPositionUpdate(true); + if (mHidden) + { + // get out of the channel entirely + leaveAudioSession(); + } + else + { + updatePosition(); + sendPositionUpdate(true); + } } } -void LLWebRTCVoiceClient::sendPositionUpdate(bool force) +///////////////////////////// +// session control messages. +// +// these are called by the sessions to report +// status for a given channel. By filtering +// on channel and region, these functions +// can send various notifications to +// other parts of the viewer, as well as +// managing housekeeping + +// A connection to a channel was successfully established, +// so shut down the current session and move on to the next +// if one is available. +// if the current session is the one that was established, +// notify the observers. +void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID, const LLUUID ®ionID) { - Json::FastWriter writer; - std::string spatial_data; - - if (mSpatialCoordsDirty || force) + if (gAgent.getRegion()->getRegionID() == regionID) { - Json::Value spatial = Json::objectValue; - - 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); + if (mNextSession && mNextSession->mChannelID == channelID) + { + if (mSession) + { + mSession->shutdownAllConnections(); + } + mSession = mNextSession; + mNextSession.reset(); - spatial["lp"] = Json::objectValue; - spatial["lp"]["x"] = (int) (mListenerPosition[0] * 100); - spatial["lp"]["y"] = (int) (mListenerPosition[1] * 100); - spatial["lp"]["z"] = (int) (mListenerPosition[2] * 100); - spatial["lh"] = Json::objectValue; - spatial["lh"]["x"] = (int) (mListenerRot[0] * 100); - spatial["lh"]["y"] = (int) (mListenerRot[1] * 100); - spatial["lh"]["z"] = (int) (mListenerRot[2] * 100); - spatial["lh"]["w"] = (int) (mListenerRot[3] * 100); + // Add ourselves as a participant. + mSession->addParticipant(gAgentID); + } - mSpatialCoordsDirty = false; - spatial_data = writer.write(spatial); + // The current session was established. + if (mSession && mSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - sessionState::for_each(boost::bind(predSendData, _1, spatial_data)); + // only set status to joined if asked to. This will happen in the case where we're not + // doing an ad-hoc based p2p session. Those sessions expect a STATUS_JOINED when the peer + // has, in fact, joined, which we detect elsewhere. + if (!mSession->mNotifyOnFirstJoin) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + } + } } } -void LLWebRTCVoiceClient::updateOwnVolume() { - F32 audio_level = 0.0; - if (!mMuteMic && !mTuningMode) +void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) +{ + if (gAgent.getRegion()->getRegionID() == regionID) { - audio_level = getAudioLevel(); + if (mSession && mSession->mChannelID == channelID) + { + LL_DEBUGS("Voice") << "Main WebRTC Connection Shut Down." << LL_ENDL; + } } - - sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); } - -void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string &channelID, const LLUUID ®ionID) { - participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); - if (participant) + LL_DEBUGS("Voice") << "A connection failed. channel:" << channelID << LL_ENDL; + if (gAgent.getRegion()->getRegionID() == regionID) { - participant->mLevel = audio_level; - participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; + if (mNextSession && mNextSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } + else if (mSession && mSession->mChannelID == channelID) + { + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + } } } -void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t& session, const std::string& spatial_data) +// ----------------------------------------------------------- +// positional functionality. +void LLWebRTCVoiceClient::setEarLocation(S32 loc) { - if (session->isSpatial() && !spatial_data.empty()) + if (mEarLocation != loc) { - session->sendData(spatial_data); + LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; + + mEarLocation = loc; + mSpatialCoordsDirty = true; } } -void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) +void LLWebRTCVoiceClient::updatePosition(void) { - for (auto& connection : mWebRTCConnections) + LLViewerRegion *region = gAgent.getRegion(); + if (region && isAgentAvatarValid()) { - connection->sendData(data); + // get the avatar position. + LLVector3d avatar_pos = gAgentAvatarp->getPositionGlobal(); + LLQuaternion avatar_qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); + + avatar_pos += LLVector3d(0.f, 0.f, 1.f); // bump it up to head height + + LLVector3d earPosition; + LLQuaternion earRot; + switch (mEarLocation) + { + case earLocCamera: + default: + earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + earRot = LLViewerCamera::getInstance()->getQuaternion(); + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earRot = mAvatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earRot = LLViewerCamera::getInstance()->getQuaternion(); + break; + } + setListenerPosition(earPosition, // position + LLVector3::zero, // velocity + earRot); // rotation matrix + + setAvatarPosition(avatar_pos, // position + LLVector3::zero, // velocity + avatar_qrot); // rotation matrix + + enforceTether(); + + updateNeighboringRegions(); } } -void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) +void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { - mMuted = muted; - for (auto& connection : mWebRTCConnections) + mListenerRequestedPosition = position; + + if (mListenerVelocity != velocity) { - connection->setMuteMic(muted); + mListenerVelocity = velocity; + mSpatialCoordsDirty = true; } -} -void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) -{ - mMicGain = gain; - for (auto& connection : mWebRTCConnections) + if (mListenerRot != rot) { - connection->setMicGain(gain); + mListenerRot = rot; + mSpatialCoordsDirty = true; } } -void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) +void LLWebRTCVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { - mSpeakerVolume = volume; - for (auto& connection : mWebRTCConnections) + if (dist_vec_squared(mAvatarPosition, position) > 0.01) { - connection->setSpeakerVolume(volume); + mAvatarPosition = position; + mSpatialCoordsDirty = true; } -} -void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID& id, F32 volume) -{ - if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) + if (mAvatarVelocity != velocity) { - return; + mAvatarVelocity = velocity; + mSpatialCoordsDirty = true; } - for (auto& connection : mWebRTCConnections) + + // If the two rotations are not exactly equal test their dot product + // to get the cos of the angle between them. + // If it is too small, don't update. + F32 rot_cos_diff = llabs(dot(mAvatarRot, rot)); + if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS)) { - connection->setUserVolume(id, volume); + mAvatarRot = rot; + mSpatialCoordsDirty = true; } } -void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID& id, bool mute) +// The listener (camera) must be within 50m of the +// avatar. Enforce it on the client. +// This will also be enforced on the voice server +// based on position sent from the simulator to the +// voice server. +void LLWebRTCVoiceClient::enforceTether() { - if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) + LLVector3d tethered = mListenerRequestedPosition; + + // constrain 'tethered' to within 50m of mAvatarPosition. { - return; + LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition; + F32 camera_distance = (F32) camera_offset.magVec(); + if (camera_distance > MAX_AUDIO_DIST) + { + tethered = mAvatarPosition + (MAX_AUDIO_DIST / camera_distance) * camera_offset; + } } - for (auto& connection : mWebRTCConnections) + + if (dist_vec_squared(mListenerPosition, tethered) > 0.01) { - connection->setUserMute(id, mute); + mListenerPosition = tethered; + mSpatialCoordsDirty = true; } } -void LLWebRTCVoiceClient::sendLocalAudioUpdates() -{ -} - -void LLWebRTCVoiceClient::reapSession(const sessionStatePtr_t &session) -{ - if(session) - { - if(session == mSession) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the current session)" << LL_ENDL; - } - else if(session == mNextSession) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mChannelID << " (it's the next session)" << LL_ENDL; - } - else - { - // We don't have a reason to keep tracking this session, so just delete it. - LL_DEBUGS("Voice") << "deleting session " << session->mChannelID << LL_ENDL; - deleteSession(session); - } - } - else - { -// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; - } -} - - -void LLWebRTCVoiceClient::muteListChanged() -{ - // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - if(mSession) - { - participantMap::iterator iter = mSession->mParticipantsByURI.begin(); - - for(; iter != mSession->mParticipantsByURI.end(); iter++) - { - participantStatePtr_t p(iter->second); - - // Check to see if this participant is on the mute list already - if(p->updateMuteState()) - mSession->mVolumeDirty = true; - } - } -} - -///////////////////////////// -// Managing list of participants -LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : - mURI(agent_id.asString()), - mAvatarID(agent_id), - mPTT(false), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLastSpokeTimestamp(0.f), - mLevel(0.f), - mVolume(LLVoiceClient::VOLUME_DEFAULT), - mUserVolume(0), - mOnMuteList(false), - mVolumeSet(false), - mVolumeDirty(false), - mAvatarIDValid(false), - mIsSelf(false) +// We send our position via a WebRTC data channel to the WebRTC +// server for fine-grained, low latency updates. On the server, +// these updates will be 'tethered' to the actual position of the avatar. +// Those updates are higher latency, however. +// This mechanism gives low latency spatial updates and server-enforced +// prevention of 'evesdropping' by sending camera updates beyond the +// standard 50m +void LLWebRTCVoiceClient::sendPositionUpdate(bool force) { -} + Json::FastWriter writer; + std::string spatial_data; -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) -{ - participantStatePtr_t result; - - participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); + if (mSpatialCoordsDirty || force) + { + Json::Value spatial = Json::objectValue; + 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); - if (iter != mParticipantsByUUID.end()) - { - result = iter->second; - } - - if(!result) - { - // participant isn't already in one list or the other. - result.reset(new participantState(agent_id)); - mParticipantsByURI.insert(participantMap::value_type(agent_id.asString(), result)); - mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); + spatial["lp"] = Json::objectValue; + spatial["lp"]["x"] = (int) (mListenerPosition[0] * 100); + spatial["lp"]["y"] = (int) (mListenerPosition[1] * 100); + spatial["lp"]["z"] = (int) (mListenerPosition[2] * 100); + spatial["lh"] = Json::objectValue; + spatial["lh"]["x"] = (int) (mListenerRot[0] * 100); + spatial["lh"]["y"] = (int) (mListenerRot[1] * 100); + spatial["lh"]["z"] = (int) (mListenerRot[2] * 100); + spatial["lh"]["w"] = (int) (mListenerRot[3] * 100); - result->mAvatarIDValid = true; - result->mAvatarID = agent_id; - - if(result->updateMuteState()) - { - mMuteDirty = true; - } + mSpatialCoordsDirty = false; + spatial_data = writer.write(spatial); - if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) - { - result->mVolumeDirty = true; - mVolumeDirty = true; - } - if (!LLWebRTCVoiceClient::sShuttingDown) - { - LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); - } - - LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; - } - - return result; + sessionState::for_each(boost::bind(predSendData, _1, spatial_data)); + } } -bool LLWebRTCVoiceClient::participantState::updateMuteState() -{ - bool result = false; +// Update our own volume on our participant, so it'll show up +// in the UI. This is done on all sessions, so switching +// sessions retains consistent volume levels. +void LLWebRTCVoiceClient::updateOwnVolume() { + F32 audio_level = 0.0; + if (!mMuteMic && !mTuningMode) + { + audio_level = getAudioLevel(); + } - bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - if(mOnMuteList != isMuted) - { - mOnMuteList = isMuted; - mVolumeDirty = true; - result = true; - } - return result; + sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); } -bool LLWebRTCVoiceClient::participantState::isAvatar() -{ - return mAvatarIDValid; -} +//////////////////////////////////// +// Managing list of participants -void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant) -{ - if(participant) - { - participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); - participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); - - LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; - - if(iter == mParticipantsByURI.end()) - { - LL_WARNS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; - } - else if(iter2 == mParticipantsByUUID.end()) - { - LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; - } - else if(iter->second != iter2->second) - { - LL_WARNS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; - } - else - { - mParticipantsByURI.erase(iter); - mParticipantsByUUID.erase(iter2); - if (!LLWebRTCVoiceClient::sShuttingDown) - { - LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); - } - } - } -} +// Provider-level participant management -void LLWebRTCVoiceClient::sessionState::removeAllParticipants() +BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) { - LL_DEBUGS("Voice") << "called" << LL_ENDL; - - while(!mParticipantsByURI.empty()) - { - removeParticipant(mParticipantsByURI.begin()->second); - } - - if(!mParticipantsByUUID.empty()) - { - LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; - } + // WebRTC participants are always SL avatars. + return TRUE; } - void LLWebRTCVoiceClient::getParticipantList(std::set &participants) { - if(mSession) - { - for(participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); - iter != mSession->mParticipantsByUUID.end(); - iter++) - { - participants.insert(iter->first); - } - } + if (mSession) + { + for (participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); + iter != mSession->mParticipantsByUUID.end(); + iter++) + { + participants.insert(iter->first); + } + } } bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) { - if(mSession) + if (mSession) { - return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end()); + return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end()); } - return false; -} - -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipant(const std::string &uri) -{ - participantStatePtr_t result; - - participantMap::iterator iter = mParticipantsByURI.find(uri); - - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } - - return result; + return false; } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +// protected provider-level participant management. +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string &channelID, const LLUUID &id) { participantStatePtr_t result; - participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); - if(iter != mParticipantsByUUID.end()) - { - result = iter->second; - } + if (session) + { + result = session->findParticipantByID(id); + } - return result; + return result; } -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string& channelID, const LLUUID& id) +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string &channelID, const LLUUID &id) { participantStatePtr_t result; - auto session = sessionState::matchSessionByChannelID(channelID); - - if (session) - { - result = session->findParticipantByID(id); - } - - return result; -} - -LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string& channelID, const LLUUID &id) -{ - participantStatePtr_t result; - auto session = sessionState::matchSessionByChannelID(channelID); + LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); if (session) { result = session->addParticipant(id); @@ -1254,16 +1072,14 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id) { participantStatePtr_t result; - auto session = sessionState::matchSessionByChannelID(channelID); + LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); if (session) { participantStatePtr_t participant = session->findParticipantByID(id); if (participant) { session->removeParticipant(participant); - if (session->mHangupOnLastLeave && - (id != gAgentID) && - (session->mParticipantsByURI.size() <= 1)) + if (session->mHangupOnLastLeave && (id != gAgentID) && (session->mParticipantsByUUID.size() <= 1)) { notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); } @@ -1271,6 +1087,116 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co } } + +// participantState level participant management +LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : + mURI(agent_id.asString()), + mAvatarID(agent_id), + mIsSpeaking(false), + mIsModeratorMuted(false), + mLevel(0.f), + mVolume(LLVoiceClient::VOLUME_DEFAULT), + mOnMuteList(false) +{ +} + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) +{ + participantStatePtr_t result; + + participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); + + if (iter != mParticipantsByUUID.end()) + { + result = iter->second; + } + + if(!result) + { + // participant isn't already in one list or the other. + result.reset(new participantState(agent_id)); + mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result)); + result->mAvatarID = agent_id; + + LLWebRTCVoiceClient::getInstance()->lookupName(agent_id); + + result->updateMuteState(); + + LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume); + if (!LLWebRTCVoiceClient::sShuttingDown) + { + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); + } + + LL_DEBUGS("Voice") << "Participant \"" << result->mURI << "\" added." << LL_ENDL; + } + + return result; +} + +bool LLWebRTCVoiceClient::participantState::updateMuteState() +{ + bool result = false; + + bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); + if(mOnMuteList != isMuted) + { + mOnMuteList = isMuted; + result = true; + } + return result; +} + + +// session-level participant management + +LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +{ + participantStatePtr_t result; + participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + + if(iter != mParticipantsByUUID.end()) + { + result = iter->second; + } + + return result; +} + +void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant) +{ + if (participant) + { + participantUUIDMap::iterator iter = mParticipantsByUUID.find(participant->mAvatarID); + + LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; + + if (iter == mParticipantsByUUID.end()) + { + LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; + } + else + { + mParticipantsByUUID.erase(iter); + if (!LLWebRTCVoiceClient::sShuttingDown) + { + LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); + } + } + } +} + +void LLWebRTCVoiceClient::sessionState::removeAllParticipants() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while (!mParticipantsByUUID.empty()) + { + removeParticipant(mParticipantsByUUID.begin()->second); + } +} + +// Initiated the various types of sessions. bool LLWebRTCVoiceClient::startEstateSession() { leaveChannel(false); @@ -1291,75 +1217,40 @@ bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool notify LL_WARNS("Voice") << "Start AdHoc Session " << channelInfo << LL_ENDL; std::string channelID = channelInfo["channel_uri"]; std::string credentials = channelInfo["channel_credentials"]; - mNextSession = addSession(channelID, sessionState::ptr_t(new adhocSessionState(channelID, - credentials, - notify_on_first_join, - hangup_on_last_leave))); + mNextSession = addSession(channelID, + sessionState::ptr_t(new adhocSessionState(channelID, + credentials, + notify_on_first_join, + hangup_on_last_leave))); return true; } bool LLWebRTCVoiceClient::isVoiceWorking() const { - - //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) - // Condition with joining spatial num was added to take into account possible problems with connection to voice - // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. - return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && mIsProcessingChannels; -// return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); -} - -// Returns true if the indicated participant in the current audio session is really an SL avatar. -// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. -BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) -{ - BOOL result = TRUE; - return result; + return mIsProcessingChannels; } // Returns true if calling back the session URI after the session has closed is possible. -// Currently this will be false only for PSTN P2P calls. +// Currently this will be false only for PSTN P2P calls. BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) { - BOOL result = TRUE; - sessionStatePtr_t session(findP2PSession(session_id)); - - if(session != NULL) - { - result = session->isCallBackPossible(); - } - - return result; -} - -// Returns true if the session can accept text IM's. -// Currently this will be false only for PSTN P2P calls. -BOOL LLWebRTCVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) -{ - bool result = TRUE; sessionStatePtr_t session(findP2PSession(session_id)); - - if(session != NULL) - { - result = session->isTextIMPossible(); - } - - return result; + return session && session->isCallbackPossible() ? TRUE : FALSE; } +// Channel Management void LLWebRTCVoiceClient::leaveNonSpatialChannel() { - LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; + LL_DEBUGS("Voice") << "Request to leave non-spatial channel." << LL_ENDL; - // Make sure we don't rejoin the current session. - sessionStatePtr_t oldNextSession(mNextSession); - mNextSession.reset(); - - // Most likely this will still be the current session at this point, but check it anyway. - reapSession(oldNextSession); - - leaveChannel(true); + // make sure we're not simply rejoining the current session + deleteSession(mNextSession); + + leaveChannel(true); } +// determine whether we're processing channels, or whether +// another voice provider is. void LLWebRTCVoiceClient::processChannels(bool process) { mProcessChannels = process; @@ -1367,81 +1258,7 @@ void LLWebRTCVoiceClient::processChannels(bool process) bool LLWebRTCVoiceClient::inProximalChannel() { - return inSpatialChannel(); -} - -std::string LLWebRTCVoiceClient::nameFromID(const LLUUID &uuid) -{ - std::string result; - - if (uuid.isNull()) { - //WebRTC, the uuid emtpy look for the mURIString and return that instead. - //result.assign(uuid.mURIStringName); - LLStringUtil::replaceChar(result, '_', ' '); - return result; - } - // Prepending this apparently prevents conflicts with reserved names inside the WebRTC code. - result = "x"; - - // Base64 encode and replace the pieces of base64 that are less compatible - // with e-mail local-parts. - // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" - result += LLBase64::encode(uuid.mData, UUID_BYTES); - LLStringUtil::replaceChar(result, '+', '-'); - LLStringUtil::replaceChar(result, '/', '_'); - - // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: - // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') - - // The reverse transform can be done with: - // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p - - return result; -} - -bool LLWebRTCVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) -{ - bool result = false; - - // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.WebRTC.com" - // If it is, convert to a bare name before doing the transform. - std::string name; - - // Doesn't look like a SIP URI, assume it's an actual name. - if(name.empty()) - name = inName; - - // This will only work if the name is of the proper form. - // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: - // "xFnPP04IpREWNkuw1cOXlhw==" - - if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) - { - // The name appears to have the right form. - - // Reverse the transforms done by nameFromID - std::string temp = name; - LLStringUtil::replaceChar(temp, '-', '+'); - LLStringUtil::replaceChar(temp, '_', '/'); - - U8 rawuuid[UUID_BYTES + 1]; - int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); - if(len == UUID_BYTES) - { - // The decode succeeded. Stuff the bits into the result's UUID - memcpy(uuid.mData, rawuuid, UUID_BYTES); - result = true; - } - } - - if(!result) - { - // WebRTC: not a standard account name, just copy the URI name mURIString field - // and hope for the best. bpj - uuid.setNull(); // WebRTC, set the uuid field to nulls - } - - return result; + return inSpatialChannel(); } bool LLWebRTCVoiceClient::inOrJoiningChannel(const std::string& channelID) @@ -1456,20 +1273,22 @@ bool LLWebRTCVoiceClient::inEstateChannel() bool LLWebRTCVoiceClient::inSpatialChannel() { - bool result = true; - + bool result = true; + if (mNextSession) { result = mNextSession->isSpatial(); } - else if(mSession) + else if(mSession) { - result = mSession->isSpatial(); + result = mSession->isSpatial(); } - return result; + return result; } +// retrieves information used to negotiate p2p, adhoc, and group +// channels LLSD LLWebRTCVoiceClient::getAudioSessionChannelInfo() { LLSD result; @@ -1483,167 +1302,9 @@ LLSD LLWebRTCVoiceClient::getAudioSessionChannelInfo() return result; } -///////////////////////////// -// Sending updates of current state - -void LLWebRTCVoiceClient::enforceTether() -{ - LLVector3d tethered = mListenerRequestedPosition; - - // constrain 'tethered' to within 50m of mAvatarPosition. - { - LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition; - F32 camera_distance = (F32)camera_offset.magVec(); - if(camera_distance > MAX_AUDIO_DIST) - { - tethered = mAvatarPosition + - (MAX_AUDIO_DIST / camera_distance) * camera_offset; - } - } - - if(dist_vec_squared(mListenerPosition, tethered) > 0.01) - { - mListenerPosition = tethered; - mSpatialCoordsDirty = true; - } -} - -void LLWebRTCVoiceClient::updateNeighboringRegions() -{ - static const std::vector neighbors {LLVector3d(0.0f, 1.0f, 0.0f), LLVector3d(0.707f, 0.707f, 0.0f), - LLVector3d(1.0f, 0.0f, 0.0f), LLVector3d(0.707f, -0.707f, 0.0f), - LLVector3d(0.0f, -1.0f, 0.0f), LLVector3d(-0.707f, -0.707f, 0.0f), - LLVector3d(-1.0f, 0.0f, 0.0f), LLVector3d(-0.707f, 0.707f, 0.0f)}; - - // Estate voice requires connection to neighboring regions. - mNeighboringRegions.clear(); - - mNeighboringRegions.insert(gAgent.getRegion()->getRegionID()); - - // base off of speaker position as it'll move more slowly than camera position. - // Once we have hysteresis, we may be able to track off of speaker and camera position at 50m - // TODO: Add hysteresis so we don't flip-flop connections to neighbors - LLVector3d speaker_pos = LLWebRTCVoiceClient::getInstance()->getSpeakerPosition(); - for (auto &neighbor_pos : neighbors) - { - // include every region within 100m (2*MAX_AUDIO_DIST) to deal witht he fact that the camera - // can stray 50m away from the avatar. - LLViewerRegion *neighbor = LLWorld::instance().getRegionFromPosGlobal(speaker_pos + 2 * MAX_AUDIO_DIST * neighbor_pos); - if (neighbor && !neighbor->getRegionID().isNull()) - { - mNeighboringRegions.insert(neighbor->getRegionID()); - } - } -} - -void LLWebRTCVoiceClient::updatePosition(void) -{ - LLViewerRegion *region = gAgent.getRegion(); - if(region && isAgentAvatarValid()) - { - LLVector3d avatar_pos = gAgentAvatarp->getPositionGlobal(); - LLQuaternion avatar_qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); - - avatar_pos += LLVector3d(0.f, 0.f, 1.f); // bump it up to head height - - // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... - // They're currently always set to zero. - LLVector3d earPosition; - LLQuaternion earRot; - switch (mEarLocation) - { - case earLocCamera: - default: - earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); - earRot = LLViewerCamera::getInstance()->getQuaternion(); - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earRot = LLViewerCamera::getInstance()->getQuaternion(); - break; - } - setListenerPosition(earPosition, // position - LLVector3::zero, // velocity - earRot); // rotation matrix - - setAvatarPosition( - avatar_pos, // position - LLVector3::zero, // velocity - avatar_qrot); // rotation matrix - - enforceTether(); - - updateNeighboringRegions(); - } -} - -void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) -{ - - mListenerRequestedPosition = position; - - if(mListenerVelocity != velocity) - { - mListenerVelocity = velocity; - mSpatialCoordsDirty = true; - } - - if(mListenerRot != rot) - { - mListenerRot = rot; - mSpatialCoordsDirty = true; - } -} - -void LLWebRTCVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) -{ - if(dist_vec_squared(mAvatarPosition, position) > 0.01) - { - mAvatarPosition = position; - mSpatialCoordsDirty = true; - } - - if(mAvatarVelocity != velocity) - { - mAvatarVelocity = velocity; - mSpatialCoordsDirty = true; - } - - // If the two rotations are not exactly equal test their dot product - // to get the cos of the angle between them. - // If it is too small, don't update. - F32 rot_cos_diff = llabs(dot(mAvatarRot, rot)); - if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS)) - { - mAvatarRot = rot; - mSpatialCoordsDirty = true; - } -} - -bool LLWebRTCVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) -{ - bool result = false; - - if(region) - { - name = region->getName(); - } - - if(!name.empty()) - result = true; - - return result; -} - void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) { - if (mSession) + if (mSession) { deleteSession(mSession); } @@ -1678,20 +1339,24 @@ bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo) bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) { return (channelInfo1["voice_server_type"] == WEBRTC_VOICE_SERVER_TYPE) && - (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) && - (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]); + (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) && + (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]); } + +//---------------------------------------------- +// Audio muting, volume, gain, etc. + +// we're muting the mic, so tell each session such void LLWebRTCVoiceClient::setMuteMic(bool muted) { - mMuteMic = muted; sessionState::for_each(boost::bind(predSetMuteMic, _1, muted)); } void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool muted) { - participantStatePtr_t participant = session->findParticipant(gAgentID.asString()); + participantStatePtr_t participant = session->findParticipantByID(gAgentID); if (participant) { participant->mLevel = 0.0; @@ -1699,28 +1364,34 @@ void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionState session->setMuteMic(muted); } -void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) +void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) { - session->setSpeakerVolume(volume); + if (volume != mSpeakerVolume) + { + { + mSpeakerVolume = volume; + } + sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); + } } -void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain) +void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume) { - session->setMicGain(gain); + session->setSpeakerVolume(volume); } -void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, - const LLUUID &id, - bool mute) +void LLWebRTCVoiceClient::setMicGain(F32 gain) { - session->setUserMute(id, mute); + if (gain != mMicGain) + { + mMicGain = gain; + sessionState::for_each(boost::bind(predSetMicGain, _1, gain)); + } } -void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, - const LLUUID &id, - F32 volume) +void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain) { - session->setUserVolume(id, volume); + session->setMicGain(gain); } void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) @@ -1731,18 +1402,18 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) << " coro "<< (mIsCoroutineActive ? "active" : "inactive") << LL_ENDL; - if (enabled != mVoiceEnabled) - { - // TODO: Refactor this so we don't call into LLVoiceChannel, but simply - // use the status observer - mVoiceEnabled = enabled; - LLVoiceClientStatusObserver::EStatusType status; - - if (enabled) - { + if (enabled != mVoiceEnabled) + { + // TODO: Refactor this so we don't call into LLVoiceChannel, but simply + // use the status observer + mVoiceEnabled = enabled; + LLVoiceClientStatusObserver::EStatusType status; + + if (enabled) + { LL_DEBUGS("Voice") << "enabling" << LL_ENDL; - LLVoiceChannel::getCurrentVoiceChannel()->activate(); - status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; + LLVoiceChannel::getCurrentVoiceChannel()->activate(); + status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; mSpatialCoordsDirty = true; updatePosition(); if (!mIsCoroutineActive) @@ -1751,125 +1422,95 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); } else - { - LL_DEBUGS("Voice") << "coro should be active.. not launching" << LL_ENDL; - } - } - else - { - // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. - LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); - gAgent.setVoiceConnected(false); - status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; - cleanUp(); - } - - notifyStatusObservers(status); - } - else - { - LL_DEBUGS("Voice") << " no-op" << LL_ENDL; - } -} - -void LLWebRTCVoiceClient::setEarLocation(S32 loc) -{ - if(mEarLocation != loc) - { - LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; - - mEarLocation = loc; - mSpatialCoordsDirty = true; - } -} - -void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) -{ - if (volume != mSpeakerVolume) - { + { + LL_DEBUGS("Voice") << "coro should be active.. not launching" << LL_ENDL; + } + } + else { - mSpeakerVolume = volume; - mSpeakerVolumeDirty = true; + // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. + LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); + gAgent.setVoiceConnected(false); + status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; + cleanUp(); } - sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); - } -} -void LLWebRTCVoiceClient::setMicGain(F32 gain) -{ - if (gain != mMicGain) + notifyStatusObservers(status); + } + else { - mMicGain = gain; - sessionState::for_each(boost::bind(predSetMicGain, _1, gain)); + LL_DEBUGS("Voice") << " no-op" << LL_ENDL; } } + ///////////////////////////// // Accessors for data related to nearby speakers BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) { - BOOL result = FALSE; + BOOL result = FALSE; if (!mSession) { return FALSE; } - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - // I'm not sure what the semantics of this should be. - // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. - result = TRUE; - } - - return result; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if(participant) + { + // I'm not sure what the semantics of this should be. + // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. + result = TRUE; + } + + return result; } std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) { - std::string result; + std::string result; if (!mSession) { return result; } - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - result = participant->mDisplayName; - } - - return result; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if(participant) + { + result = participant->mDisplayName; + } + + return result; } BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { - BOOL result = FALSE; + BOOL result = FALSE; if (!mSession) { return result; } - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - result = participant->mIsSpeaking; - } - - return result; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if(participant) + { + result = participant->mIsSpeaking; + } + + return result; } +// TODO: Need to pull muted status from the webrtc server BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) { - BOOL result = FALSE; + BOOL result = FALSE; if (!mSession) { return result; } - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - result = participant->mIsModeratorMuted; - } - - return result; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if(participant) + { + result = participant->mIsModeratorMuted; + } + + return result; } F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) @@ -1879,46 +1520,28 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { return result; } - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if (participant) - { + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant) + { if (participant->mIsSpeaking) { result = participant->mLevel; } - } - return result; -} - -BOOL LLWebRTCVoiceClient::getUsingPTT(const LLUUID& id) -{ - BOOL result = FALSE; - if (!mSession) - { - return result; } - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - // I'm not sure what the semantics of this should be. - // Does "using PTT" mean they're configured with a push-to-talk button? - // For now, we know there's no PTT mechanism in place, so nobody is using it. - } - - return result; + return result; } BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) { - BOOL result = FALSE; - - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - result = participant->mOnMuteList; - } + BOOL result = FALSE; + + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if(participant) + { + result = participant->mOnMuteList; + } - return result; + return result; } // External accessors. @@ -1926,58 +1549,46 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) { // Minimum volume will be returned for users with voice disabled F32 result = LLVoiceClient::VOLUME_MIN; - - participantStatePtr_t participant(mSession->findParticipant(id.asString())); + + participantStatePtr_t participant(mSession->findParticipantByID(id)); if(participant) - { - result = participant->mVolume; - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. - // LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL; - } + { + result = participant->mVolume; + } - return result; + return result; } void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { F32 clamped_volume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); - if(mSession) - { - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if (participant && !participant->mIsSelf) - { - if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) - { - // Store this volume setting for future sessions if it has been - // changed from the default - LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); - } - else - { - // Remove stored volume setting if it is returned to the default - LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); - } - - participant->mVolume = clamped_volume; - participant->mVolumeDirty = true; - mSession->mVolumeDirty = true; - } - } + if(mSession) + { + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant && (participant->mAvatarID != gAgentID)) + { + if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) + { + // Store this volume setting for future sessions if it has been + // changed from the default + LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); + } + else + { + // Remove stored volume setting if it is returned to the default + LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); + } + + participant->mVolume = clamped_volume; + } + } sessionState::for_each(boost::bind(predSetUserVolume, _1, id, clamped_volume)); } -std::string LLWebRTCVoiceClient::getGroupID(const LLUUID& id) +// set volume level (gain level) for another user. +void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID &id, F32 volume) { - std::string result; - - participantStatePtr_t participant(mSession->findParticipant(id.asString())); - if(participant) - { - result = participant->mGroupID; - } - - return result; + session->setUserVolume(id, volume); } //////////////////////// @@ -1997,51 +1608,119 @@ void LLWebRTCVoiceClient::onChangeDetailed(const LLMute& mute) } } - -BOOL LLWebRTCVoiceClient::getAreaVoiceDisabled() +void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID &id, bool mute) { - return mAreaVoiceDisabled; + session->setUserMute(id, mute); } //------------------------------------------------------------------------ +// Sessions + std::map LLWebRTCVoiceClient::sessionState::mSessions; -LLWebRTCVoiceClient::sessionState::sessionState() : - mErrorStatusCode(0), - mVolumeDirty(false), - mMuteDirty(false), +LLWebRTCVoiceClient::sessionState::sessionState() : + mHangupOnLastLeave(false), + mNotifyOnFirstJoin(false), + mMicGain(1.0), + mMuted(false), + mSpeakerVolume(1.0), mShuttingDown(false) { } +// ------------------------------------------------------------------ +// Predicates, for calls to all sessions -/*static*/ -void LLWebRTCVoiceClient::sessionState::addSession( - const std::string & channelID, - LLWebRTCVoiceClient::sessionState::ptr_t& session) +void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level) { - mSessions[channelID] = session; + participantStatePtr_t participant = session->findParticipantByID(gAgentID); + if (participant) + { + participant->mLevel = audio_level; + // TODO: Add VAD for our own voice. + participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL; + } } -LLWebRTCVoiceClient::sessionState::~sessionState() +void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string &spatial_data) +{ + if (session->isSpatial() && !spatial_data.empty()) + { + session->sendData(spatial_data); + } +} + +void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data) +{ + for (auto &connection : mWebRTCConnections) + { + connection->sendData(data); + } +} + +void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted) { - LL_INFOS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL; + mMuted = muted; + for (auto &connection : mWebRTCConnections) + { + connection->setMuteMic(muted); + } +} + +void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain) +{ + mMicGain = gain; + for (auto &connection : mWebRTCConnections) + { + connection->setMicGain(gain); + } +} + +void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume) +{ + mSpeakerVolume = volume; + for (auto &connection : mWebRTCConnections) + { + connection->setSpeakerVolume(volume); + } +} - removeAllParticipants(); +void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID &id, F32 volume) +{ + if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) + { + return; + } + for (auto &connection : mWebRTCConnections) + { + connection->setUserVolume(id, volume); + } } -bool LLWebRTCVoiceClient::sessionState::isCallBackPossible() +void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID &id, bool mute) +{ + if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end()) + { + return; + } + for (auto &connection : mWebRTCConnections) + { + connection->setUserMute(id, mute); + } +} +/*static*/ +void LLWebRTCVoiceClient::sessionState::addSession( + const std::string & channelID, + LLWebRTCVoiceClient::sessionState::ptr_t& session) { - // This may change to be explicitly specified by WebRTC in the future... - // Currently, only PSTN P2P calls cannot be returned. - // Conveniently, this is also the only case where we synthesize a caller UUID. - return false; + mSessions[channelID] = session; } -bool LLWebRTCVoiceClient::sessionState::isTextIMPossible() +LLWebRTCVoiceClient::sessionState::~sessionState() { - // This may change to be explicitly specified by WebRTC in the future... - return false; + LL_DEBUGS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL; + + removeAllParticipants(); } /*static*/ @@ -2079,20 +1758,6 @@ void LLWebRTCVoiceClient::sessionState::reapEmptySessions() } } -bool LLWebRTCVoiceClient::sessionState::testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri) -{ - ptr_t aLock(a.lock()); - - return aLock ? (aLock->mChannelID == LLUUID(uri)) : false; -} - -bool LLWebRTCVoiceClient::sessionState::testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId) -{ - ptr_t aLock(a.lock()); - - return aLock ? ((aLock->mCallerID == participantId) || (aLock->mIMSessionID == participantId)) : false; -} - /*static*/ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pair &a, sessionFunc_t func) { @@ -2106,6 +1771,31 @@ void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pairsetMuteMic(mMuteMic); + session->setMicGain(mMicGain); + session->setSpeakerVolume(mSpeakerVolume); + + sessionState::addSession(channel_id, session); + return session; + } + else + { + // Found an existing session + LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; + existingSession->revive(); + + return existingSession; + } +} + LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const LLUUID &agent_id) { sessionStatePtr_t result = sessionState::matchSessionByChannelID(agent_id.asString()); @@ -2113,55 +1803,143 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const { return result; } - - result.reset(); - return result; + + result.reset(); + return result; +} + +void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() +{ + mShuttingDown = true; + for (auto &&connection : mWebRTCConnections) + { + connection->shutDown(); + } +} + +// in case we drop into a session (spatial, etc.) right after +// telling the session to shut down, revive it so it reconnects. +void LLWebRTCVoiceClient::sessionState::revive() +{ + mShuttingDown = false; +} + +//========================================================================= +// the following are methods to support the coroutine implementation of the +// voice connection and processing. They should only be called in the context +// of a coroutine. +// +// + +void LLWebRTCVoiceClient::sessionState::processSessionStates() +{ + auto iter = mSessions.begin(); + while (iter != mSessions.end()) + { + if (!iter->second->processConnectionStates() && iter->second->mShuttingDown) + { + // if the connections associated with a session are gone, + // and this session is shutting down, remove it. + iter = mSessions.erase(iter); + } + else + { + iter++; + } + } +} + +// process the states on each connection associated with a session. +bool LLWebRTCVoiceClient::sessionState::processConnectionStates() +{ + std::list::iterator iter = mWebRTCConnections.begin(); + while (iter != mWebRTCConnections.end()) + { + if (!iter->get()->connectionStateMachine()) + { + // if the state machine returns false, the connection is shut down + // so delete it. + iter = mWebRTCConnections.erase(iter); + } + else + { + ++iter; + } + } + return !mWebRTCConnections.empty(); +} + +// processing of spatial voice connection states requires special handling. +// as neighboring regions need to be started up or shut down depending +// on our location. +bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() +{ + if (!mShuttingDown) + { + // Estate voice requires connection to neighboring regions. + std::set neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions(); + + for (auto &connection : mWebRTCConnections) + { + boost::shared_ptr spatialConnection = + boost::static_pointer_cast(connection); + + LLUUID regionID = spatialConnection.get()->getRegionID(); + + if (neighbor_ids.find(regionID) == neighbor_ids.end()) + { + // shut down connections to neighbors that are too far away. + spatialConnection.get()->shutDown(); + } + neighbor_ids.erase(regionID); + } + + // add new connections for new neighbors + for (auto &neighbor : neighbor_ids) + { + connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + + mWebRTCConnections.push_back(connection); + connection->setMicGain(mMicGain); + connection->setMuteMic(mMuted); + connection->setSpeakerVolume(mSpeakerVolume); + } + } + return LLWebRTCVoiceClient::sessionState::processConnectionStates(); } +// Various session state constructors. -void LLWebRTCVoiceClient::sessionState::shutdownAllConnections() +LLWebRTCVoiceClient::estateSessionState::estateSessionState() { - mShuttingDown = true; - for (auto &&connection : mWebRTCConnections) - { - connection->shutDown(); - } + mHangupOnLastLeave = false; + mNotifyOnFirstJoin = false; + mChannelID = "Estate"; + LLUUID region_id = gAgent.getRegion()->getRegionID(); + + mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, INVALID_PARCEL_ID, "Estate")); } -void LLWebRTCVoiceClient::sessionState::revive() +LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id) { - mShuttingDown = false; + mHangupOnLastLeave = false; + mNotifyOnFirstJoin = false; + LLUUID region_id = gAgent.getRegion()->getRegionID(); + mChannelID = channelID; + mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID)); } - -LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session) +LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID, + const std::string &credentials, + bool notify_on_first_join, + bool hangup_on_last_leave) : + mCredentials(credentials) { - sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id); - if (!existingSession) - { - // No existing session found. - - LL_DEBUGS("Voice") << "adding new session: CHANNEL " << channel_id << LL_ENDL; - session->setMuteMic(mMuteMic); - session->setMicGain(mMicGain); - session->setSpeakerVolume(mSpeakerVolume); - - if (LLVoiceClient::instance().getVoiceEffectEnabled()) - { - session->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); - } - - sessionState::addSession(channel_id, session); - return session; - } - else - { - // Found an existing session - LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL; - existingSession->revive(); - - return existingSession; - } + mHangupOnLastLeave = hangup_on_last_leave; + mNotifyOnFirstJoin = notify_on_first_join; + LLUUID region_id = gAgent.getRegion()->getRegionID(); + mChannelID = channelID; + mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials)); } void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) @@ -2171,175 +1949,40 @@ void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::session void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) { - // At this point, the session should be unhooked from all lists and all state should be consistent. + // At this point, the session should be unhooked from all lists and all state should be consistent. session->shutdownAllConnections(); - // If this is the current audio session, clean up the pointer which will soon be dangling. + // If this is the current audio session, clean up the pointer which will soon be dangling. bool deleteAudioSession = mSession == session; bool deleteNextAudioSession = mNextSession == session; if (deleteAudioSession) - { - mSession.reset(); - } + { + mSession.reset(); + } - // ditto for the next audio session + // ditto for the next audio session if (deleteNextAudioSession) - { - mNextSession.reset(); - } -} - -void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) -{ - mParticipantObservers.insert(observer); -} - -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) -{ - mParticipantObservers.erase(observer); -} - -void LLWebRTCVoiceClient::notifyParticipantObservers() -{ - for (observer_set_t::iterator it = mParticipantObservers.begin(); - it != mParticipantObservers.end(); - ) - { - LLVoiceClientParticipantObserver* observer = *it; - observer->onParticipantsChanged(); - // In case onParticipantsChanged() deleted an entry. - it = mParticipantObservers.upper_bound(observer); - } -} - -void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) -{ - mStatusObservers.insert(observer); -} - -void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) -{ - mStatusObservers.erase(observer); -} - -void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) -{ - LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" - << " mSession=" << mSession - << LL_ENDL; - if(mSession) - { - if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) - { - switch(mSession->mErrorStatusCode) - { - case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; - case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; - case 20715: - //invalid channel, we may be using a set of poorly cached - //info - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - case 1009: - //invalid username and password - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - } - - // Reset the error code to make sure it won't be reused later by accident. - mSession->mErrorStatusCode = 0; - } - else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) - { - switch(mSession->mErrorStatusCode) - { - case HTTP_NOT_FOUND: // NOT_FOUND - // *TODO: Should this be 503? - case 480: // TEMPORARILY_UNAVAILABLE - case HTTP_REQUEST_TIME_OUT: // REQUEST_TIMEOUT - // call failed because other user was not available - // treat this as an error case - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - - // Reset the error code to make sure it won't be reused later by accident. - mSession->mErrorStatusCode = 0; - break; - } - } - } - - LL_DEBUGS("Voice") - << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session channelInfo " << getAudioSessionChannelInfo() - << ", proximal is " << inSpatialChannel() - << LL_ENDL; - - mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; - - LLSD channelInfo = getAudioSessionChannelInfo(); - for (status_observer_set_t::iterator it = mStatusObservers.begin(); - it != mStatusObservers.end(); - ) - { - LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, channelInfo, inSpatialChannel()); - // In case onError() deleted an entry. - it = mStatusObservers.upper_bound(observer); - } - - // skipped to avoid speak button blinking - if ( status != LLVoiceClientStatusObserver::STATUS_JOINING - && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL - && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) - { - bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - - gAgent.setVoiceConnected(voice_status); - - if (voice_status) - { - LLFirstUse::speak(true); - } - } -} - -void LLWebRTCVoiceClient::addObserver(LLFriendObserver* observer) -{ - mFriendObservers.insert(observer); -} - -void LLWebRTCVoiceClient::removeObserver(LLFriendObserver* observer) -{ - mFriendObservers.erase(observer); + { + mNextSession.reset(); + } } -void LLWebRTCVoiceClient::notifyFriendObservers() -{ - for (friend_observer_set_t::iterator it = mFriendObservers.begin(); - it != mFriendObservers.end(); - ) - { - LLFriendObserver* observer = *it; - it++; - // The only friend-related thing we notify on is online/offline transitions. - observer->changed(LLFriendObserver::ONLINE); - } -} +// Name resolution void LLWebRTCVoiceClient::lookupName(const LLUUID &id) { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2)); } void LLWebRTCVoiceClient::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name) + const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); - std::string display_name = av_name.getDisplayName(); - avatarNameResolved(agent_id, display_name); + mAvatarNameCacheConnection.disconnect(); + std::string display_name = av_name.getDisplayName(); + avatarNameResolved(agent_id, display_name); } void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) @@ -2348,16 +1991,10 @@ void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::se if (participant) { // Found -- fill in the name + participant->mDisplayName = name; // and post a "participants updated" message to listeners later. LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); } - - // Check whether this is a p2p session whose caller name just resolved - if (session->mCallerID == id) - { - // this session's "caller ID" just resolved. Fill in the name. - session->mName = name; - } } void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) @@ -2365,13 +2002,20 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); } - -std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) { return id.asString(); } +// Leftover from vivox PTSN +std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) +{ + return id.asString(); +} ///////////////////////////// // LLVoiceWebRTCConnection - +// These connections manage state transitions, negotiating webrtc connections, +// and other such things for a single connection to a Secondlife WebRTC server. +// Multiple of these connections may be active at once, in the case of +// cross-region voice, or when a new connection is being created before the old +// has a chance to shut down. LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID) : mWebRTCAudioInterface(nullptr), mWebRTCDataInterface(nullptr), @@ -2405,9 +2049,18 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() } } + +// ICE (Interactive Connectivity Establishment) +// When WebRTC tries to negotiate a connection to the Secondlife WebRTC Server, +// the negotiation will result in a few updates about the best path +// to which to connect. +// The Secondlife servers are configured for ICE trickling, where, after a session is partially +// negotiated, updates about the best connectivity paths may trickle in. These need to be +// sent to the Secondlife WebRTC server via the simulator so that both sides have a clear +// view of the network environment. void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) { - LL_INFOS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; + LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) { @@ -2475,39 +2128,11 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS mOutstandingRequests--; } -void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) -{ - LL_INFOS("Voice") << "On Offer Available." << LL_ENDL; - LLMutexLock lock(&mVoiceStateMutex); - mChannelSDP = sdp; - if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START) - { - mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION; - } -} - -void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) -{ - LL_INFOS("Voice") << "On AudioEstablished." << LL_ENDL; - mWebRTCAudioInterface = audio_interface; - setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); -} - -void LLVoiceWebRTCConnection::OnRenegotiationNeeded() -{ - LL_INFOS("Voice") << "On Renegotiation Needed." << LL_ENDL; - if (!mShutDown) - { - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - } -} - -void LLVoiceWebRTCConnection::OnPeerShutDown() -{ - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); - mOutstandingRequests--; -} +// Ice candidates may be streamed in before or after the SDP offer is available (see below) +// This function determines whether candidates are available to send to the Secondlife WebRTC +// server via the simulator. If so, and there are no more candidates, this code +// will make the cap call to the server sending up the ICE candidates. void LLVoiceWebRTCConnection::processIceUpdates() { if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) @@ -2541,11 +2166,11 @@ void LLVoiceWebRTCConnection::processIceUpdates() LLSD candidates = LLSD::emptyArray(); for (auto &ice_candidate : mIceCandidates) { - LLSD body_candidate; - body_candidate["sdpMid"] = ice_candidate.sdp_mid; - body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; - body_candidate["candidate"] = ice_candidate.candidate; - candidates.append(body_candidate); + LLSD body_candidate; + body_candidate["sdpMid"] = ice_candidate.sdp_mid; + body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; + body_candidate["candidate"] = ice_candidate.candidate; + candidates.append(body_candidate); } body["candidates"] = candidates; mIceCandidates.clear(); @@ -2559,7 +2184,7 @@ void LLVoiceWebRTCConnection::processIceUpdates() mIceCompleted = false; } - body["viewer_session"] = mViewerSession; + body["viewer_session"] = mViewerSession; body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -2581,6 +2206,48 @@ void LLVoiceWebRTCConnection::processIceUpdates() } +// An 'Offer' comes in the form of a SDP (Session Description Protocol) +// which contains all sorts of info about the session, from network paths +// to the type of session (audio, video) to characteristics (the encoder type.) +// This SDP also serves as the 'ticket' to the server, security-wise. +// The Offer is retrieved from the WebRTC library on the client, +// and is passed to the simulator via a CAP, which then passes +// it on to the Secondlife WebRTC server. + +void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) +{ + LL_DEBUGS("Voice") << "On Offer Available." << LL_ENDL; + LLMutexLock lock(&mVoiceStateMutex); + mChannelSDP = sdp; + if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START) + { + mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION; + } +} + +// Notifications from the webrtc library. +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) +{ + LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL; + mWebRTCAudioInterface = audio_interface; + setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); +} + +void LLVoiceWebRTCConnection::OnRenegotiationNeeded() +{ + LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; + if (!mShutDown) + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } +} + +void LLVoiceWebRTCConnection::OnPeerShutDown() +{ + setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); + mOutstandingRequests--; // shut down is an async call which is handled on a webrtc thread. +} + void LLVoiceWebRTCConnection::setMuteMic(bool muted) { mMuted = muted; @@ -2631,6 +2298,8 @@ void LLVoiceWebRTCConnection::setUserMute(const LLUUID& id, bool mute) } +// Send data to the Secondlife WebRTC server via the webrtc +// data channel. void LLVoiceWebRTCConnection::sendData(const std::string &data) { if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface) @@ -2639,9 +2308,11 @@ void LLVoiceWebRTCConnection::sendData(const std::string &data) } } +// Tell the simulator that we're shutting down a voice connection. +// The simulator will pass this on to the Secondlife WebRTC server. bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) { - LL_INFOS("Voice") << "Disconnecting voice." << LL_ENDL; + LL_DEBUGS("Voice") << "Disconnecting voice." << LL_ENDL; if (mWebRTCDataInterface) { mWebRTCDataInterface->unsetDataObserver(this); @@ -2672,7 +2343,7 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) LLSD body; body["logout"] = TRUE; body["viewer_session"] = mViewerSession; - body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + body["voice_server_type"] = REPORTED_VOICE_SERVER_TYPE; LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, @@ -2715,6 +2386,7 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url } if (retries >= 0) { + // retry a few times. LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( url, LLCore::HttpRequest::DEFAULT_POLICY_ID, @@ -2736,6 +2408,55 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url } +// Tell the simulator to tell the Secondlife WebRTC server that we want a voice +// connection. The SDP is sent up as part of this, and the simulator will respond +// with an 'answer' which is in the form of another SDP. The webrtc library +// will use the offer and answer to negotiate the session. +bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() +{ + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + + LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL; + if (!regionp || !regionp->capabilitiesReceived()) + { + LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; + return false; + } + + std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); + if (url.empty()) + { + return false; + } + + LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; + + LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); + LLSD body; + LLSD jsep; + jsep["type"] = "offer"; + { + LLMutexLock lock(&mVoiceStateMutex); + jsep["sdp"] = mChannelSDP; + } + body["jsep"] = jsep; + if (mParcelLocalID != INVALID_PARCEL_ID) + { + body["parcel_local_id"] = mParcelLocalID; + } + + body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( + url, + LLCore::HttpRequest::DEFAULT_POLICY_ID, + body, + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), + boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); + mOutstandingRequests++; + return true; +} + void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) { mOutstandingRequests--; @@ -2764,6 +2485,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); } + void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result) { if (LLWebRTCVoiceClient::isShuttingDown()) @@ -2787,6 +2509,9 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i mOutstandingRequests--; } + +// Primary state machine for negotiating a single voice connection to the +// Secondlife WebRTC server. bool LLVoiceWebRTCConnection::connectionStateMachine() { processIceUpdates(); @@ -2803,12 +2528,16 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() mTrickling = false; mIceCompleted = false; setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START); + // tell the webrtc library that we want a connection. The library will + // respond with an offer on a separate thread, which will cause + // the session state to change. if (!mWebRTCPeerConnection->initializeConnection()) { setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } break; } + case VOICE_STATE_WAIT_FOR_SESSION_START: { if (mShutDown) @@ -2817,12 +2546,16 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } break; } + case VOICE_STATE_REQUEST_CONNECTION: if (mShutDown) { setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; } + // Ask the sim to ask the Secondlife WebRTC server for a connection to + // a given voice channel. On completion, we'll move on to the + // VOICE_STATE_SESSION_ESTABLISHED via a callback on a webrtc thread. if (!requestVoiceConnection()) { setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); @@ -2832,6 +2565,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() setVoiceConnectionState(VOICE_STATE_CONNECTION_WAIT); } break; + case VOICE_STATE_CONNECTION_WAIT: if (mShutDown) { @@ -2846,6 +2580,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; } + // update the peer connection with the various characteristics of + // this connection. mWebRTCAudioInterface->setMute(mMuted); mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); mWebRTCAudioInterface->setSendVolume(mMicGain); @@ -2853,6 +2589,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL); break; } + case VOICE_STATE_WAIT_FOR_DATA_CHANNEL: { if (mShutDown) @@ -2860,15 +2597,17 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; } - if (mWebRTCDataInterface) + if (mWebRTCDataInterface) // the interface will be set when the session is negotiated. { - sendJoin(); + sendJoin(); // tell the Secondlife WebRTC server that we're here via the data channel. setVoiceConnectionState(VOICE_STATE_SESSION_UP); } break; } + case VOICE_STATE_SESSION_UP: { + // we'll stay here as long as the session remains up. if (mShutDown) { setVoiceConnectionState(VOICE_STATE_DISCONNECT); @@ -2877,6 +2616,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } case VOICE_STATE_SESSION_RETRY: + // something went wrong, so notify that the connection has failed. LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; @@ -2887,6 +2627,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_WAIT_FOR_EXIT: break; + case VOICE_STATE_SESSION_EXIT: { { @@ -2897,6 +2638,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } else { + // if we still have outstanding http or webrtc calls, wait for them to + // complete so we don't delete objects while they still may be used. if (mOutstandingRequests <= 0) { LLWebRTCVoiceClient::getInstance()->OnConnectionShutDown(mChannelID, mRegionID); @@ -2916,6 +2659,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() return true; } +// Data has been received on the webrtc data channel void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binary) { // incoming data will be a json structure (if it's not binary.) We may pack @@ -2926,8 +2670,9 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar // integer indices into an agentid list, populated on join commands. For size. // Each key will point to a json object with keys identifying what's updated. // 'p' - audio source power (level/volume) (int8 as int) - // 'j' - join - object of join data (TBD) (true for now) + // 'j' - object of join data (currently only a boolean 'p' marking a primary participant) // 'l' - boolean, always true if exists. + // 'v' - boolean - voice activity has been detected. if (binary) { @@ -2959,11 +2704,19 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar LLWebRTCVoiceClient::participantStatePtr_t participant = LLWebRTCVoiceClient::getInstance()->findParticipantByID(mChannelID, agent_id); bool joined = false; - bool primary = false; + bool primary = false; // we ignore any 'joins' reported about participants + // that come from voice servers that aren't their primary + // voice server. This will happen with cross-region voice + // where a participant on a neighboring region may be + // connected to multiple servers. We don't want to + // add new identical participants from all of those servers. if (voice_data[participant_id].isMember("j")) { + // a new participant has announced that they're joining. joined = true; primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool(); + + // track incoming participants that are muted so we can mute their connections (or set their volume) bool isMuted = LLMuteList::getInstance()->isMuted(agent_id, LLMute::flagVoiceChat); if (isMuted) { @@ -2981,10 +2734,12 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id); } + if (participant) { if (voice_data[participant_id].get("l", Json::Value(false)).asBool()) { + // an existing participant is leaving. if (agent_id != gAgentID) { LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id); @@ -2992,6 +2747,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } else { + // we got a 'power' update. F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; // convert to decibles participant->mLevel = level; @@ -3003,6 +2759,9 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } } } + + // tell the simulator to set the mute and volume data for this + // participant, if there are any updates. Json::FastWriter writer; Json::Value root = Json::objectValue; if (mute.size() > 0) @@ -3030,6 +2789,12 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface } } +// tell the Secondlife WebRTC server that +// we're joining and whether we're +// joining a server associated with the +// the region we currently occupy or not (primary) +// The WebRTC voice server will pass this info +// to peers. void LLVoiceWebRTCConnection::sendJoin() { Json::FastWriter writer; @@ -3048,7 +2813,9 @@ void LLVoiceWebRTCConnection::sendJoin() ///////////////////////////// // WebRTC Spatial Connection -LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID) : +LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, + S32 parcelLocalID, + const std::string &channelID) : LLVoiceWebRTCConnection(regionID, channelID), mParcelLocalID(parcelLocalID) { @@ -3066,52 +2833,6 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() mWebRTCPeerConnection->unsetSignalingObserver(this); } - -bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() -{ - LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); - - LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; - if (!regionp || !regionp->capabilitiesReceived()) - { - LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - return false; - } - - std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); - if (url.empty()) - { - return false; - } - - LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; - - LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); - LLSD body; - LLSD jsep; - jsep["type"] = "offer"; - { - LLMutexLock lock(&mVoiceStateMutex); - jsep["sdp"] = mChannelSDP; - } - body["jsep"] = jsep; - if (mParcelLocalID != INVALID_PARCEL_ID) - { - body["parcel_local_id"] = mParcelLocalID; - } - - body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; - - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost( - url, - LLCore::HttpRequest::DEFAULT_POLICY_ID, - body, - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1), - boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1)); - mOutstandingRequests++; - return true; -} - void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) { mMuted = muted; @@ -3124,7 +2845,9 @@ void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) } else { - // always mute to regions the agent isn't on, to prevent echo. + // Always mute this agent with respect to neighboring regions. + // Peers don't want to hear this agent from multiple regions + // as that'll echo. mWebRTCAudioInterface->setMute(true); } } @@ -3153,12 +2876,15 @@ LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection() mWebRTCPeerConnection->unsetSignalingObserver(this); } - +// Add-hoc connections require a different channel type +// as they go to a different set of Secondlife WebRTC servers. +// They also require credentials for the given channels. +// So, we have a separate requestVoiceConnection call. bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() { LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); - LL_INFOS("Voice") << "Requesting voice connection." << LL_ENDL; + LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL; if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; @@ -3171,8 +2897,6 @@ bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() return false; } - LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; - LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); LLSD body; LLSD jsep; -- cgit v1.2.3 From 7714850fbe2ad4c2dc914798971d82cbb82f7832 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sat, 9 Mar 2024 23:00:00 -0800 Subject: code beautification/comments --- indra/newview/llvoicewebrtc.cpp | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 6e0cd32249..6eee8e70b2 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -597,8 +597,8 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi bool renderDeviceSet = false; for (auto &device : render_devices) { - addRenderDevice(LLVoiceDevice(device.display_name, device.id)); - if (device.current && outputDevice == device.id) + addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID)); + if (device.mCurrent && outputDevice == device.mID) { setRenderDevice(outputDevice); renderDeviceSet = true; @@ -613,8 +613,8 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi bool captureDeviceSet = false; for (auto &device : capture_devices) { - addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); - if (device.current && inputDevice == device.id) + addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID)); + if (device.mCurrent && inputDevice == device.mID) { setCaptureDevice(outputDevice); captureDeviceSet = true; @@ -696,7 +696,7 @@ float LLWebRTCVoiceClient::getAudioLevel() } else { - return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; + return (1.0 - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; } } @@ -2030,8 +2030,8 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const s mChannelID(channelID), mRegionID(regionID) { - mWebRTCPeerConnection = llwebrtc::newPeerConnection(); - mWebRTCPeerConnection->setSignalingObserver(this); + mWebRTCPeerConnectionInterface = llwebrtc::newPeerConnection(); + mWebRTCPeerConnectionInterface->setSignalingObserver(this); } LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() @@ -2042,10 +2042,10 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() // by llwebrtc::terminate() on shutdown. return; } - if (mWebRTCPeerConnection) + if (mWebRTCPeerConnectionInterface) { - llwebrtc::freePeerConnection(mWebRTCPeerConnection); - mWebRTCPeerConnection = nullptr; + llwebrtc::freePeerConnection(mWebRTCPeerConnectionInterface); + mWebRTCPeerConnectionInterface = nullptr; } } @@ -2058,19 +2058,19 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() // negotiated, updates about the best connectivity paths may trickle in. These need to be // sent to the Secondlife WebRTC server via the simulator so that both sides have a clear // view of the network environment. -void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) { - case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: + case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_COMPLETE: { LLMutexLock lock(&mVoiceStateMutex); mIceCompleted = true; break; } - case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: + case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW: { LLMutexLock lock(&mVoiceStateMutex); mIceCompleted = false; @@ -2167,9 +2167,9 @@ void LLVoiceWebRTCConnection::processIceUpdates() 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; + body_candidate["sdpMid"] = ice_candidate.mSdpMid; + body_candidate["sdpMLineIndex"] = ice_candidate.mMLineIndex; + body_candidate["candidate"] = ice_candidate.mCandidate; candidates.append(body_candidate); } body["candidates"] = candidates; @@ -2242,7 +2242,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() } } -void LLVoiceWebRTCConnection::OnPeerShutDown() +void LLVoiceWebRTCConnection::OnPeerConnectionShutdown() { setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); mOutstandingRequests--; // shut down is an async call which is handled on a webrtc thread. @@ -2364,9 +2364,9 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res return; } - if (mWebRTCPeerConnection) + if (mWebRTCPeerConnectionInterface) { - if (mWebRTCPeerConnection->shutdownConnection()) + if (mWebRTCPeerConnectionInterface->shutdownConnection()) { mOutstandingRequests++; } @@ -2395,10 +2395,10 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1)); return; } - if (mWebRTCPeerConnection) + if (mWebRTCPeerConnectionInterface) { mOutstandingRequests++; - mWebRTCPeerConnection->shutdownConnection(); + mWebRTCPeerConnectionInterface->shutdownConnection(); } else { @@ -2482,7 +2482,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response" << " channel sdp " << mRemoteChannelSDP << LL_ENDL; - mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); + mWebRTCPeerConnectionInterface->AnswerAvailable(mRemoteChannelSDP); } @@ -2531,7 +2531,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // tell the webrtc library that we want a connection. The library will // respond with an offer on a separate thread, which will cause // the session state to change. - if (!mWebRTCPeerConnection->initializeConnection()) + if (!mWebRTCPeerConnectionInterface->initializeConnection()) { setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } @@ -2830,7 +2830,7 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() return; } assert(mOutstandingRequests == 0); - mWebRTCPeerConnection->unsetSignalingObserver(this); + mWebRTCPeerConnectionInterface->unsetSignalingObserver(this); } void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) @@ -2873,7 +2873,7 @@ LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection() return; } assert(mOutstandingRequests == 0); - mWebRTCPeerConnection->unsetSignalingObserver(this); + mWebRTCPeerConnectionInterface->unsetSignalingObserver(this); } // Add-hoc connections require a different channel type -- cgit v1.2.3 From fc462b2b0f7ee153626d162bd97a05110f0804b7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Mar 2024 00:11:43 -0800 Subject: Remove trailing spaces. Other code cleanup. --- indra/newview/llvoicewebrtc.cpp | 218 ++++++++++++++++------------------------ 1 file changed, 87 insertions(+), 131 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 6eee8e70b2..02917d2135 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1,25 +1,25 @@ - /** + /** * @file LLWebRTCVoiceClient.cpp * @brief Implementation of LLWebRTCVoiceClient class which is the interface to the voice client process. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -92,7 +92,7 @@ namespace { // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - + // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); @@ -302,12 +302,12 @@ void LLWebRTCVoiceClient::updateSettings() // Observers void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver *observer) -{ +{ mParticipantObservers.insert(observer); } void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver *observer) -{ +{ mParticipantObservers.erase(observer); } @@ -323,12 +323,12 @@ void LLWebRTCVoiceClient::notifyParticipantObservers() } void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver *observer) -{ +{ mStatusObservers.insert(observer); } void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver *observer) -{ +{ mStatusObservers.erase(observer); } @@ -352,7 +352,7 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt } // skipped to avoid speak button blinking - if (status != LLVoiceClientStatusObserver::STATUS_JOINING && + if (status != LLVoiceClientStatusObserver::STATUS_JOINING && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) { @@ -368,11 +368,11 @@ void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESt } void LLWebRTCVoiceClient::addObserver(LLFriendObserver *observer) -{ +{ } void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) -{ +{ } //--------------------------------------------------- @@ -381,12 +381,12 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) // takes to process the various functions called in the loop // The loop does the following: // * gates whether we do channel processing depending on -// whether we're running a WebRTC voice channel or +// whether we're running a WebRTC voice channel or // one from another voice provider. // * If in spatial voice, it determines whether we've changed // parcels, whether region/parcel voice settings have changed, // etc. and manages whether the voice channel needs to change. -// * calls the state machines for the sessions to negotiate +// * calls the state machines for the sessions to negotiate // connection to various voice channels. // * Sends updates to the voice server when this agent's // voice levels, or positions have changed. @@ -485,7 +485,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() updatePosition(); } } - + sessionState::processSessionStates(); if (mProcessChannels && voiceEnabled) { @@ -629,7 +629,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi } void LLWebRTCVoiceClient::clearRenderDevices() -{ +{ LL_DEBUGS("Voice") << "called" << LL_ENDL; mRenderDevices.clear(); } @@ -661,7 +661,7 @@ void LLWebRTCVoiceClient::tuningStart() } void LLWebRTCVoiceClient::tuningStop() -{ +{ if (mIsInTuningMode) { mWebRTCDeviceInterface->setTuningMode(false); @@ -680,7 +680,7 @@ void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) -{ +{ if (volume != mTuningSpeakerVolume) { @@ -708,17 +708,17 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void) bool LLWebRTCVoiceClient::deviceSettingsAvailable() { bool result = true; - + if(mRenderDevices.empty() || mCaptureDevices.empty()) result = false; - + return result; } bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; mDevicesListUpdated = false; - return updated; + return updated; } void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) @@ -758,7 +758,7 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) // status for a given channel. By filtering // on channel and region, these functions // can send various notifications to -// other parts of the viewer, as well as +// other parts of the viewer, as well as // managing housekeeping // A connection to a channel was successfully established, @@ -997,14 +997,14 @@ void LLWebRTCVoiceClient::sendPositionUpdate(bool force) // Update our own volume on our participant, so it'll show up // in the UI. This is done on all sessions, so switching // sessions retains consistent volume levels. -void LLWebRTCVoiceClient::updateOwnVolume() { +void LLWebRTCVoiceClient::updateOwnVolume() { F32 audio_level = 0.0; if (!mMuteMic && !mTuningMode) { audio_level = getAudioLevel(); } - sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); + sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level)); } //////////////////////////////////// @@ -1020,9 +1020,9 @@ BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id) void LLWebRTCVoiceClient::getParticipantList(std::set &participants) { - if (mSession) + if (mProcessChannels && mSession) { - for (participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); + for (participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin(); iter != mSession->mParticipantsByUUID.end(); iter++) { @@ -1033,7 +1033,7 @@ void LLWebRTCVoiceClient::getParticipantList(std::set &participants) bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id) { - if (mSession) + if (mProcessChannels && mSession) { return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end()); } @@ -1089,28 +1089,27 @@ void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, co // participantState level participant management -LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : +LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) : mURI(agent_id.asString()), mAvatarID(agent_id), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLevel(0.f), - mVolume(LLVoiceClient::VOLUME_DEFAULT), - mOnMuteList(false) + mIsSpeaking(false), + mIsModeratorMuted(false), + mLevel(0.f), + mVolume(LLVoiceClient::VOLUME_DEFAULT) { } LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) { participantStatePtr_t result; - + participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); if (iter != mParticipantsByUUID.end()) { result = iter->second; } - + if(!result) { // participant isn't already in one list or the other. @@ -1119,31 +1118,16 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad result->mAvatarID = agent_id; LLWebRTCVoiceClient::getInstance()->lookupName(agent_id); - - result->updateMuteState(); LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume); if (!LLWebRTCVoiceClient::sShuttingDown) { LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers(); } - + LL_DEBUGS("Voice") << "Participant \"" << result->mURI << "\" added." << LL_ENDL; } - - return result; -} -bool LLWebRTCVoiceClient::participantState::updateMuteState() -{ - bool result = false; - - bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - if(mOnMuteList != isMuted) - { - mOnMuteList = isMuted; - result = true; - } return result; } @@ -1218,7 +1202,7 @@ bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool notify std::string channelID = channelInfo["channel_uri"]; std::string credentials = channelInfo["channel_credentials"]; mNextSession = addSession(channelID, - sessionState::ptr_t(new adhocSessionState(channelID, + sessionState::ptr_t(new adhocSessionState(channelID, credentials, notify_on_first_join, hangup_on_last_leave))); @@ -1231,7 +1215,7 @@ bool LLWebRTCVoiceClient::isVoiceWorking() const } // Returns true if calling back the session URI after the session has closed is possible. -// Currently this will be false only for PSTN P2P calls. +// Currently this will be false only for PSTN P2P calls. BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) { sessionStatePtr_t session(findP2PSession(session_id)); @@ -1245,14 +1229,14 @@ void LLWebRTCVoiceClient::leaveNonSpatialChannel() // make sure we're not simply rejoining the current session deleteSession(mNextSession); - + leaveChannel(true); } // determine whether we're processing channels, or whether // another voice provider is. void LLWebRTCVoiceClient::processChannels(bool process) -{ +{ mProcessChannels = process; } @@ -1274,8 +1258,8 @@ bool LLWebRTCVoiceClient::inEstateChannel() bool LLWebRTCVoiceClient::inSpatialChannel() { bool result = true; - - if (mNextSession) + + if (mNextSession) { result = mNextSession->isSpatial(); } @@ -1283,7 +1267,7 @@ bool LLWebRTCVoiceClient::inSpatialChannel() { result = mSession->isSpatial(); } - + return result; } @@ -1323,8 +1307,11 @@ void LLWebRTCVoiceClient::leaveChannel(bool stopTalking) bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo) { - if (channelInfo["voice_server_type"].asString() != WEBRTC_VOICE_SERVER_TYPE) + if (!mProcessChannels || (channelInfo["voice_server_type"].asString() != WEBRTC_VOICE_SERVER_TYPE)) + { return false; + } + if (mSession) { if (!channelInfo["sessionHandle"].asString().empty()) @@ -1339,7 +1326,7 @@ bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo) bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) { return (channelInfo1["voice_server_type"] == WEBRTC_VOICE_SERVER_TYPE) && - (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) && + (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) && (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]); } @@ -1401,14 +1388,14 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) << " was "<< (mVoiceEnabled ? "enabled" : "disabled") << " coro "<< (mIsCoroutineActive ? "active" : "inactive") << LL_ENDL; - + if (enabled != mVoiceEnabled) { // TODO: Refactor this so we don't call into LLVoiceChannel, but simply // use the status observer mVoiceEnabled = enabled; LLVoiceClientStatusObserver::EStatusType status; - + if (enabled) { LL_DEBUGS("Voice") << "enabling" << LL_ENDL; @@ -1446,53 +1433,32 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) ///////////////////////////// // Accessors for data related to nearby speakers -BOOL LLWebRTCVoiceClient::getVoiceEnabled(const LLUUID& id) -{ - BOOL result = FALSE; - if (!mSession) - { - return FALSE; - } - participantStatePtr_t participant(mSession->findParticipantByID(id)); - if(participant) - { - // I'm not sure what the semantics of this should be. - // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. - result = TRUE; - } - - return result; -} std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id) { std::string result; - if (!mSession) - { - return result; - } - participantStatePtr_t participant(mSession->findParticipantByID(id)); - if(participant) + if (mProcessChannels && mSession) { - result = participant->mDisplayName; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant) + { + result = participant->mDisplayName; + } } - return result; } BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; - if (!mSession) - { - return result; - } - participantStatePtr_t participant(mSession->findParticipantByID(id)); - if(participant) + if (mProcessChannels && mSession) { - result = participant->mIsSpeaking; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant) + { + result = participant->mIsSpeaking; + } } - return result; } @@ -1500,23 +1466,21 @@ BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id) BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id) { BOOL result = FALSE; - if (!mSession) - { - return result; - } - participantStatePtr_t participant(mSession->findParticipantByID(id)); - if(participant) + if (mProcessChannels && mSession) { - result = participant->mIsModeratorMuted; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant) + { + result = participant->mIsModeratorMuted; + } } - return result; } F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) { F32 result = 0.0; - if (!mSession) + if (!mProcessChannels || !mSession) { return result; } @@ -1531,25 +1495,12 @@ F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id) return result; } -BOOL LLWebRTCVoiceClient::getOnMuteList(const LLUUID& id) -{ - BOOL result = FALSE; - - participantStatePtr_t participant(mSession->findParticipantByID(id)); - if(participant) - { - result = participant->mOnMuteList; - } - - return result; -} - // External accessors. F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) { // Minimum volume will be returned for users with voice disabled F32 result = LLVoiceClient::VOLUME_MIN; - + participantStatePtr_t participant(mSession->findParticipantByID(id)); if(participant) { @@ -1619,7 +1570,7 @@ void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStat std::map LLWebRTCVoiceClient::sessionState::mSessions; -LLWebRTCVoiceClient::sessionState::sessionState() : +LLWebRTCVoiceClient::sessionState::sessionState() : mHangupOnLastLeave(false), mNotifyOnFirstJoin(false), mMicGain(1.0), @@ -1803,7 +1754,7 @@ LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const { return result; } - + result.reset(); return result; } @@ -1943,12 +1894,17 @@ LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &cha } void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session) -{ +{ session->shutdownAllConnections(); } void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session) { + if (!session) + { + return; + } + // At this point, the session should be unhooked from all lists and all state should be consistent. session->shutdownAllConnections(); // If this is the current audio session, clean up the pointer which will soon be dangling. @@ -2004,7 +1960,7 @@ void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string // Leftover from vivox PTSN std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) -{ +{ return id.asString(); } @@ -2014,7 +1970,7 @@ std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id) // These connections manage state transitions, negotiating webrtc connections, // and other such things for a single connection to a Secondlife WebRTC server. // Multiple of these connections may be active at once, in the case of -// cross-region voice, or when a new connection is being created before the old +// cross-region voice, or when a new connection is being created before the old // has a chance to shut down. LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID) : mWebRTCAudioInterface(nullptr), @@ -2132,7 +2088,7 @@ void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLS // Ice candidates may be streamed in before or after the SDP offer is available (see below) // This function determines whether candidates are available to send to the Secondlife WebRTC // server via the simulator. If so, and there are no more candidates, this code -// will make the cap call to the server sending up the ICE candidates. +// will make the cap call to the server sending up the ICE candidates. void LLVoiceWebRTCConnection::processIceUpdates() { if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) @@ -2211,7 +2167,7 @@ void LLVoiceWebRTCConnection::processIceUpdates() // to the type of session (audio, video) to characteristics (the encoder type.) // This SDP also serves as the 'ticket' to the server, security-wise. // The Offer is retrieved from the WebRTC library on the client, -// and is passed to the simulator via a CAP, which then passes +// and is passed to the simulator via a CAP, which then passes // it on to the Secondlife WebRTC server. void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) @@ -2510,7 +2466,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i } -// Primary state machine for negotiating a single voice connection to the +// Primary state machine for negotiating a single voice connection to the // Secondlife WebRTC server. bool LLVoiceWebRTCConnection::connectionStateMachine() { @@ -2553,7 +2509,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() setVoiceConnectionState(VOICE_STATE_DISCONNECT); break; } - // Ask the sim to ask the Secondlife WebRTC server for a connection to + // Ask the sim to ask the Secondlife WebRTC server for a connection to // a given voice channel. On completion, we'll move on to the // VOICE_STATE_SESSION_ESTABLISHED via a callback on a webrtc thread. if (!requestVoiceConnection()) @@ -2708,7 +2664,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar // that come from voice servers that aren't their primary // voice server. This will happen with cross-region voice // where a participant on a neighboring region may be - // connected to multiple servers. We don't want to + // connected to multiple servers. We don't want to // add new identical participants from all of those servers. if (voice_data[participant_id].isMember("j")) { @@ -2751,7 +2707,7 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128; // convert to decibles participant->mLevel = level; - + if (voice_data[participant_id].isMember("v")) { participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool(); @@ -2821,7 +2777,7 @@ LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ® { } -LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() +LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() { if (LLWebRTCVoiceClient::isShuttingDown()) { -- cgit v1.2.3 From 37837edd69da48fe9ff74e69084e29c491327a78 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 11 Mar 2024 22:12:21 -0700 Subject: Fixup some logic relating to teleporting and new regions --- indra/newview/llvoicewebrtc.cpp | 45 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'indra/newview/llvoicewebrtc.cpp') diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 02917d2135..2bb16cb336 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -467,10 +467,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() if (useEstateVoice && !inEstateChannel()) { // estate voice - if (!inEstateChannel()) - { - startEstateSession(); - } + startEstateSession(); } } if (!voiceEnabled) @@ -487,7 +484,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } sessionState::processSessionStates(); - if (mProcessChannels && voiceEnabled) + if (mProcessChannels && voiceEnabled && !mHidden) { sendPositionUpdate(true); updateOwnVolume(); @@ -741,10 +738,13 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) if (mHidden) { // get out of the channel entirely - leaveAudioSession(); + // mute the microphone. + sessionState::for_each(boost::bind(predSetMuteMic, _1, true)); } else { + // and put it back + sessionState::for_each(boost::bind(predSetMuteMic, _1, mMuteMic)); updatePosition(); sendPositionUpdate(true); } @@ -1338,7 +1338,11 @@ bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD & void LLWebRTCVoiceClient::setMuteMic(bool muted) { mMuteMic = muted; - sessionState::for_each(boost::bind(predSetMuteMic, _1, muted)); + // when you're hidden, your mic is always muted. + if (!mHidden) + { + sessionState::for_each(boost::bind(predSetMuteMic, _1, muted)); + } } void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool muted) @@ -2791,20 +2795,23 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) { - mMuted = muted; - if (mWebRTCAudioInterface) + if (mMuted != muted) { - LLViewerRegion *regionp = gAgent.getRegion(); - if (regionp && mRegionID == regionp->getRegionID()) + mMuted = muted; + if (mWebRTCAudioInterface) { - mWebRTCAudioInterface->setMute(muted); - } - else - { - // Always mute this agent with respect to neighboring regions. - // Peers don't want to hear this agent from multiple regions - // as that'll echo. - mWebRTCAudioInterface->setMute(true); + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp && mRegionID == regionp->getRegionID()) + { + mWebRTCAudioInterface->setMute(muted); + } + else + { + // Always mute this agent with respect to neighboring regions. + // Peers don't want to hear this agent from multiple regions + // as that'll echo. + mWebRTCAudioInterface->setMute(true); + } } } } -- cgit v1.2.3