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/CMakeLists.txt | 6 + indra/newview/app_settings/settings.xml | 2 +- indra/newview/llvieweraudio.cpp | 4 +- indra/newview/llviewerregion.cpp | 1 + indra/newview/llvoiceclient.cpp | 10 +- indra/newview/llvoiceclient.h | 1 + indra/newview/llvoicewebrtc.cpp | 7299 +++++++++++++++++++++++++++++++ indra/newview/llvoicewebrtc.h | 1102 +++++ 8 files changed, 8419 insertions(+), 6 deletions(-) create mode 100644 indra/newview/llvoicewebrtc.cpp create mode 100644 indra/newview/llvoicewebrtc.h (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7a70d0b6e6..6d9b0ab2dc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -30,6 +30,7 @@ include(LLKDU) include(LLPhysicsExtensions) include(LLPrimitive) include(LLWindow) +include(LLWebRTC) include(NDOF) include(NVAPI) include(OPENAL) @@ -689,6 +690,7 @@ set(viewer_SOURCE_FILES llvoiceclient.cpp llvoicevisualizer.cpp llvoicevivox.cpp + llvoicewebrtc.cpp llvoinventorylistener.cpp llvopartgroup.cpp llvosky.cpp @@ -1333,6 +1335,7 @@ set(viewer_HEADER_FILES llvoiceclient.h llvoicevisualizer.h llvoicevivox.h + llvoicewebrtc.h llvoinventorylistener.h llvopartgroup.h llvosky.h @@ -1433,6 +1436,7 @@ if (LINUX) endif (LINUX) if (WINDOWS) + list(APPEND viewer_SOURCE_FILES llappviewerwin32.cpp llwindebug.cpp @@ -1724,6 +1728,7 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/openjp2.dll ${SHARED_LIB_STAGING_DIR}/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/uriparser.dll + ${CMAKE_BINARY_DIR}/llwebrtc/Release/llwebrtc.dll #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/SLVoice.exe #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/libsndfile-1.dll #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/vivoxoal.dll @@ -1920,6 +1925,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} llcorehttp llcommon llmeshoptimizer + llwebrtc ll::ndof lllogin llprimitive diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 00b59f9a4d..a91726917d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15128,7 +15128,7 @@ Type String Value - vivox + webrtc WLSkyDetail diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 6a0edbecb1..184b6d8e93 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -480,11 +480,11 @@ void audio_update_volume(bool force_update) if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized"))) { - voice_inst->setMuteMic(true); + //voice_inst->setMuteMic(true); } else { - voice_inst->setMuteMic(false); + //voice_inst->setMuteMic(false); } } } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 452dcdd8fd..84b007eaa4 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3136,6 +3136,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ParcelVoiceInfoRequest"); capabilityNames.append("ProductInfoRequest"); capabilityNames.append("ProvisionVoiceAccountRequest"); + capabilityNames.append("VoiceSignalingRequest"); capabilityNames.append("ReadOfflineMsgs"); // Requires to respond reliably: AcceptFriendship, AcceptGroupInvite, DeclineFriendship, DeclineGroupInvite capabilityNames.append("RegionObjects"); capabilityNames.append("RemoteParcelRequest"); diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 68d9f4ffab..294ae0c9ad 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -24,13 +24,13 @@ * $/LicenseInfo$ */ -#include "llviewerprecompiledheaders.h" #include "llvoiceclient.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" #include "llvoicevivox.h" +#include "llvoicewebrtc.h" #include "llviewernetwork.h" +#include "llviewercontrol.h" #include "llcommandhandler.h" +#include "lldir.h" #include "llhttpnode.h" #include "llnotificationsutil.h" #include "llsdserialize.h" @@ -161,6 +161,10 @@ void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &age { mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance(); } + if (voice_server == "webrtc") + { + mVoiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); + } else { mVoiceModule = NULL; diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index aa67502908..1e8ff21b4b 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -34,6 +34,7 @@ class LLVOAvatar; #include "lliosocket.h" #include "v3math.h" #include "llframetimer.h" +#include "llsingleton.h" #include "llcallingcard.h" // for LLFriendObserver #include "llsecapi.h" #include "llcontrol.h" 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() +{ +} diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h new file mode 100644 index 0000000000..56ca74f6e1 --- /dev/null +++ b/indra/newview/llvoicewebrtc.h @@ -0,0 +1,1102 @@ +/** + * @file llvoicewebrtc.h + * @brief Declaration 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$ + */ +#ifndef LL_VOICE_WEBRTC_H +#define LL_VOICE_WEBRTC_H + +class LLVOAvatar; +class LLWebRTCProtocolParser; + +#include "lliopipe.h" +#include "llpumpio.h" +#include "llchainio.h" +#include "lliosocket.h" +#include "v3math.h" +#include "llframetimer.h" +#include "llviewerregion.h" +#include "llcallingcard.h" // for LLFriendObserver +#include "lleventcoro.h" +#include "llcoros.h" +#include + +#ifdef LL_USESYSTEMLIBS +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llvoiceclient.h" + +// WebRTC Includes +#include + +class LLAvatarName; +class LLWebRTCVoiceClientMuteListObserver; + + +class LLWebRTCVoiceClient : public LLSingleton, + virtual public LLVoiceModuleInterface, + virtual public LLVoiceEffectInterface, + public llwebrtc::LLWebRTCDevicesObserver, + public llwebrtc::LLWebRTCSignalingObserver +{ + LLSINGLETON(LLWebRTCVoiceClient); + LOG_CLASS(LLWebRTCVoiceClient); + virtual ~LLWebRTCVoiceClient(); + +public: + /// @name LLVoiceModuleInterface virtual implementations + /// @see LLVoiceModuleInterface + //@{ + virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + virtual void terminate(); // Call this to clean up during shutdown + + virtual const LLVoiceVersionInfo& getVersion(); + + virtual void updateSettings(); // call after loading settings and whenever they change + + // Returns true if WebRTC has successfully logged in and is not in error state + virtual bool isVoiceWorking() const; + + ///////////////////// + /// @name Tuning + //@{ + virtual void tuningStart(); + virtual void tuningStop(); + virtual bool inTuningMode(); + + virtual void tuningSetMicVolume(float volume); + virtual void tuningSetSpeakerVolume(float volume); + virtual float tuningGetEnergy(void); + //@} + + ///////////////////// + /// @name Devices + //@{ + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + virtual bool deviceSettingsAvailable(); + virtual bool deviceSettingsUpdated(); //return if the list has been updated and never fetched, only to be called from the voicepanel. + + // Requery the WebRTC daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + virtual void refreshDeviceLists(bool clearCurrentList = true); + + virtual void setCaptureDevice(const std::string& name); + virtual void setRenderDevice(const std::string& name); + + virtual LLVoiceDeviceList& getCaptureDevices(); + virtual LLVoiceDeviceList& getRenderDevices(); + //@} + + virtual void getParticipantList(std::set &participants); + virtual bool isParticipant(const LLUUID& speaker_id); + + // Send a text message to the specified user, initiating the session if necessary. + // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; + + // close any existing text IM session with the specified user + virtual void endUserIMSession(const LLUUID &uuid); + + // Returns true if calling back the session URI after the session has closed is possible. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); + + // Returns true if the session can accepte text IM's. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); + + + //////////////////////////// + /// @name Channel stuff + //@{ + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + virtual bool inProximalChannel(); + + virtual void setNonSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual bool setSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual void leaveNonSpatialChannel(); + + virtual void leaveChannel(void); + + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + virtual std::string getCurrentChannel(); + //@} + + + ////////////////////////// + /// @name invitations + //@{ + // start a voice channel with the specified user + virtual void callUser(const LLUUID &uuid); + virtual bool isValidChannel(std::string &channelHandle); + virtual bool answerInvite(std::string &channelHandle); + virtual void declineInvite(std::string &channelHandle); + //@} + + ///////////////////////// + /// @name Volume/gain + //@{ + virtual void setVoiceVolume(F32 volume); + virtual void setMicGain(F32 volume); + //@} + + ///////////////////////// + /// @name enable disable voice and features + //@{ + virtual bool voiceEnabled(); + virtual void setVoiceEnabled(bool enabled); + virtual BOOL lipSyncEnabled(); + virtual void setLipSyncEnabled(BOOL enabled); + virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + //@} + + ////////////////////////// + /// @name nearby speaker accessors + //@{ + virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + virtual std::string getDisplayName(const LLUUID& id); + virtual BOOL isParticipantAvatar(const LLUUID &id); + virtual BOOL getIsSpeaking(const LLUUID& id); + virtual BOOL getIsModeratorMuted(const LLUUID& id); + virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + virtual BOOL getOnMuteList(const LLUUID& id); + virtual F32 getUserVolume(const LLUUID& id); + virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + //@} + + // authorize the user + virtual void userAuthorized(const std::string& user_id, + const LLUUID &agentID); + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceClientStatusObserver* observer); + virtual void removeObserver(LLVoiceClientStatusObserver* observer); + virtual void addObserver(LLFriendObserver* observer); + virtual void removeObserver(LLFriendObserver* observer); + virtual void addObserver(LLVoiceClientParticipantObserver* observer); + virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + //@} + + virtual std::string sipURIFromID(const LLUUID &id); + //@} + + /// @name LLVoiceEffectInterface virtual implementations + /// @see LLVoiceEffectInterface + //@{ + + ////////////////////////// + /// @name Accessors + //@{ + virtual bool setVoiceEffect(const LLUUID& id); + virtual const LLUUID getVoiceEffect(); + virtual LLSD getVoiceEffectProperties(const LLUUID& id); + + virtual void refreshVoiceEffectLists(bool clear_lists); + virtual const voice_effect_list_t& getVoiceEffectList() const; + virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceEffectObserver* observer); + virtual void removeObserver(LLVoiceEffectObserver* observer); + //@} + + ////////////////////////////// + /// @name Devices change notification + // LLWebRTCDevicesObserver + //@{ + void OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; + void OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; + //@} + + ////////////////////////////// + /// @name Signaling notification + // LLWebRTCSignalingObserver + //@{ + void OnIceGatheringState(IceGatheringState state) override; + void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; + void OnOfferAvailable(const std::string &sdp) override; + void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + //@} + + void processIceUpdates(); + void onIceUpdateComplete(const LLSD& result); + void onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result); + + + ////////////////////////////// + /// @name Effect preview buffer + //@{ + virtual void enablePreviewBuffer(bool enable); + virtual void recordPreviewBuffer(); + virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); + virtual void stopPreviewBuffer(); + + virtual bool isPreviewRecording(); + virtual bool isPreviewPlaying(); + //@} + + //@} + + bool onCheckVoiceEffect(const std::string& voice_effect_name); + void onClickVoiceEffect(const std::string& voice_effect_name); + +protected: + ////////////////////// + // WebRTC Specific definitions + + friend class LLWebRTCVoiceClientMuteListObserver; + friend class LLWebRTCVoiceClientFriendsObserver; + + + enum streamState + { + streamStateUnknown = 0, + streamStateIdle = 1, + streamStateConnected = 2, + streamStateRinging = 3, + streamStateConnecting = 6, // same as WebRTC session_media_connecting enum + streamStateDisconnecting = 7, //Same as WebRTC session_media_disconnecting enum + }; + + struct participantState + { + public: + participantState(const std::string &uri); + + bool updateMuteState(); // true if mute state has changed + bool isAvatar(); + + std::string mURI; + LLUUID mAvatarID; + std::string mAccountName; + std::string mDisplayName; + LLFrameTimer mSpeakingTimeout; + F32 mLastSpokeTimestamp; + F32 mPower; + F32 mVolume; + std::string mGroupID; + int mUserVolume; + bool mPTT; + bool mIsSpeaking; + bool mIsModeratorMuted; + bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) + bool mVolumeSet; // true if incoming volume messages should not change the volume + bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) + bool mAvatarIDValid; + bool mIsSelf; + }; + typedef boost::shared_ptr participantStatePtr_t; + typedef boost::weak_ptr participantStateWptr_t; + + typedef std::map participantMap; + typedef std::map participantUUIDMap; + + struct sessionState + { + public: + typedef boost::shared_ptr ptr_t; + typedef boost::weak_ptr wptr_t; + + typedef boost::function sessionFunc_t; + + static ptr_t createSession(); + ~sessionState(); + + participantStatePtr_t addParticipant(const std::string &uri); + void removeParticipant(const participantStatePtr_t &participant); + void removeAllParticipants(); + + participantStatePtr_t findParticipant(const std::string &uri); + participantStatePtr_t findParticipantByID(const LLUUID& id); + + static ptr_t matchSessionByHandle(const std::string &handle); + static ptr_t matchCreatingSessionByURI(const std::string &uri); + static ptr_t matchSessionByURI(const std::string &uri); + static ptr_t matchSessionByParticipant(const LLUUID &participant_id); + + bool isCallBackPossible(); + bool isTextIMPossible(); + + static void for_each(sessionFunc_t func); + + std::string mHandle; + std::string mGroupHandle; + std::string mSIPURI; + std::string mAlias; + std::string mName; + std::string mAlternateSIPURI; + std::string mHash; // Channel password + std::string mErrorStatusString; + std::queue mTextMsgQueue; + + LLUUID mIMSessionID; + LLUUID mCallerID; + int mErrorStatusCode; + int mMediaStreamState; + bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. + bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. + bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) + bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) + bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. + bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) + bool mIsSpatial; // True for spatial channels + bool mIsP2P; + bool mIncoming; + bool mVoiceActive; + bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + + // Set to true when the volume/mute state of someone in the participant list changes. + // The code will have to walk the list to find the changed participant(s). + bool mVolumeDirty; + bool mMuteDirty; + + bool mParticipantsChanged; + participantMap mParticipantsByURI; + participantUUIDMap mParticipantsByUUID; + + LLUUID mVoiceFontID; + + static void VerifySessions(); + + private: + sessionState(); + + static std::set mSession; // canonical list of outstanding sessions. + std::set::iterator mMyIterator; // used for delete + + static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); + + static bool testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle); + static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); + + }; + typedef boost::shared_ptr sessionStatePtr_t; + + typedef std::map sessionMap; + + /////////////////////////////////////////////////////// + // Private Member Functions + ////////////////////////////////////////////////////// + + + + ////////////////////////////// + /// @name TVC/Server management and communication + //@{ + // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. + void daemonDied(); + + // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. + void giveUp(); + + // write to the tvc + bool writeString(const std::string &str); + + void connectorCreate(); + void connectorShutdown(); + void closeSocket(void); + +// void requestVoiceAccountProvision(S32 retries = 3); + void setLoginInfo( + const std::string& account_name, + const std::string& password, + const std::string& channel_sdp); + void loginSendMessage(); + void logout(); + void logoutSendMessage(); + + + //@} + + //------------------------------------ + // tuning + + void tuningRenderStartSendMessage(const std::string& name, bool loop); + void tuningRenderStopSendMessage(); + + void tuningCaptureStartSendMessage(int duration); + void tuningCaptureStopSendMessage(); + + //---------------------------------- + // devices + void clearCaptureDevices(); + void addCaptureDevice(const LLVoiceDevice& device); + + void clearRenderDevices(); + void addRenderDevice(const LLVoiceDevice& device); + void setDevicesListUpdated(bool state); + void buildSetAudioDevices(std::ostringstream &stream); + + // local audio updates, mic mute, speaker mute, mic volume and speaker volumes + void sendLocalAudioUpdates(); + + ///////////////////////////// + // Response/Event handlers + void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); + void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); + void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); + void logoutResponse(int statusCode, std::string &statusString); + void connectorShutdownResponse(int statusCode, std::string &statusString); + + void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); + void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); + void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); + void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); + void sessionGroupAddedEvent(std::string &sessionGroupHandle); + void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); + void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); + void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); + void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); + void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); + void auxAudioPropertiesEvent(F32 energy); + void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); + void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); + + void muteListChanged(); + + ///////////////////////////// + // VAD changes + // disable auto-VAD and configure VAD parameters explicitly + void setupVADParams(unsigned int vad_auto, unsigned int vad_hangover, unsigned int vad_noise_floor, unsigned int vad_sensitivity); + void onVADSettingsChange(); + + ///////////////////////////// + // Sending updates of current state + void updatePosition(void); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); + bool channelFromRegion(LLViewerRegion *region, std::string &name); + + void setEarLocation(S32 loc); + + + ///////////////////////////// + // Accessors for data related to nearby speakers + + // MBW -- XXX -- Not sure how to get this data out of the TVC + BOOL getUsingPTT(const LLUUID& id); + std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) + + ///////////////////////////// + BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. + // Use this to determine whether to show a "no speech" icon in the menu bar. + + + ///////////////////////////// + // Recording controls + void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); + void recordingLoopSave(const std::string& filename); + void recordingStop(); + + // Playback controls + void filePlaybackStart(const std::string& filename); + void filePlaybackStop(); + void filePlaybackSetPaused(bool paused); + void filePlaybackSetMode(bool vox = false, float speed = 1.0f); + + participantStatePtr_t findParticipantByID(const LLUUID& id); + + +#if 0 + //////////////////////////////////////// + // voice sessions. + typedef std::set sessionSet; + + typedef sessionSet::iterator sessionIterator; + sessionIterator sessionsBegin(void); + sessionIterator sessionsEnd(void); +#endif + + sessionStatePtr_t findSession(const std::string &handle); + sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); + sessionStatePtr_t findSession(const LLUUID &participant_id); + + sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); + void clearSessionHandle(const sessionStatePtr_t &session); + void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle); + void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); + void deleteSession(const sessionStatePtr_t &session); + void deleteAllSessions(void); + + void verifySessionState(void); + + void joinedAudioSession(const sessionStatePtr_t &session); + void leftAudioSession(const sessionStatePtr_t &session); + + // This is called in several places where the session _may_ need to be deleted. + // It contains logic for whether to delete the session or keep it around. + void reapSession(const sessionStatePtr_t &session); + + // Returns true if the session seems to indicate we've moved to a region on a different voice server + bool sessionNeedsRelog(const sessionStatePtr_t &session); + + + ////////////////////////////////////// + // buddy list stuff, needed for SLIM later + struct buddyListEntry + { + buddyListEntry(const std::string &uri); + std::string mURI; + std::string mDisplayName; + LLUUID mUUID; + bool mOnlineSL; + bool mOnlineSLim; + bool mCanSeeMeOnline; + bool mHasBlockListEntry; + bool mHasAutoAcceptListEntry; + bool mNameResolved; + bool mInSLFriends; + bool mInWebRTCBuddies; + }; + + typedef std::map buddyListMap; + + ///////////////////////////// + // session control messages + + void accountListBlockRulesSendMessage(); + void accountListAutoAcceptRulesSendMessage(); + + void sessionGroupCreateSendMessage(); + void sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); + void sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); + void sessionMediaConnectSendMessage(const sessionStatePtr_t &session); // just joins the audio session + void sessionTextConnectSendMessage(const sessionStatePtr_t &session); // just joins the text session + void sessionTerminateSendMessage(const sessionStatePtr_t &session); + void sessionGroupTerminateSendMessage(const sessionStatePtr_t &session); + void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session); + // void sessionTextDisconnectSendMessage(sessionState *session); + + + + // Pokes the state machine to leave the audio session next time around. + void sessionTerminate(); + + // Pokes the state machine to shut down the connector and restart it. + void requestRelog(); + + // Does the actual work to get out of the audio session + void leaveAudioSession(); + + friend class LLWebRTCVoiceClientCapResponder; + + + void lookupName(const LLUUID &id); + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + void avatarNameResolved(const LLUUID &id, const std::string &name); + static void predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name); + + boost::signals2::connection mAvatarNameCacheConnection; + + ///////////////////////////// + // Voice fonts + + void addVoiceFont(const S32 id, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font = false); + void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); + void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); + +private: + + LLVoiceVersionInfo mVoiceVersion; + + // Coroutine support methods + //--- + void voiceControlCoro(); + void voiceControlStateMachine(); + int mVoiceControlState; + + bool endAndDisconnectSession(); + + bool callbackEndDaemon(const LLSD& data); + bool provisionVoiceAccount(); + void OnVoiceAccountProvisioned(const LLSD& body); + void OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result); + bool establishVoiceConnection(); + bool breakVoiceConnection(bool wait); + bool loginToWebRTC(); + void logoutOfWebRTC(bool wait); + + bool requestParcelVoiceInfo(); + + bool addAndJoinSession(const sessionStatePtr_t &nextSession); + bool terminateAudioSession(bool wait); + + bool waitForChannel(); + bool runSession(const sessionStatePtr_t &session); + + void recordingAndPlaybackMode(); + int voiceRecordBuffer(); + int voicePlaybackBuffer(); + + bool performMicTuning(); + //--- + /// Clean up objects created during a voice session. + void cleanUp(); + + bool mSessionTerminateRequested; + bool mRelogRequested; + // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). + // The larger it is the greater is possibility there is a problem with connection to voice server. + // Introduced while fixing EXT-4313. + int mSpatialJoiningNum; + + static void idle(void *user_data); + + LLHost mDaemonHost; + LLSocket::ptr_t mSocket; + + // We should kill the voice daemon in case of connection alert + bool mTerminateDaemon; + + friend class LLWebRTCProtocolParser; + + std::string mAccountName; + std::string mAccountPassword; + std::string mChannelSDP; + std::string mRemoteChannelSDP; + std::string mAccountDisplayName; + + + bool mTuningMode; + float mTuningEnergy; + std::string mTuningAudioFile; + int mTuningMicVolume; + bool mTuningMicVolumeDirty; + int mTuningSpeakerVolume; + bool mTuningSpeakerVolumeDirty; + bool mDevicesListUpdated; // set to true when the device list has been updated + // and false when the panelvoicedevicesettings has queried for an update status. + + std::string mSpatialSessionURI; + std::string mSpatialSessionCredentials; + + std::string mMainSessionGroupHandle; // handle of the "main" session group. + + std::string mChannelName; // Name of the channel to be looked up + bool mAreaVoiceDisabled; + sessionStatePtr_t mAudioSession; // Session state for the current audio session + bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. + + sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join + + S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings + std::string mCurrentRegionName; // Used to detect parcel boundary crossings + + bool mConnectorEstablished; // set by "Create Connector" response + bool mAccountLoggedIn; // set by login message + int mNumberOfAliases; + U32 mCommandCookie; + + int mLoginRetryCount; + + sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. +#if 0 + sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. +#endif + + bool mBuddyListMapPopulated; + bool mBlockRulesListReceived; + bool mAutoAcceptRulesListReceived; + buddyListMap mBuddyListMap; + + llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; + llwebrtc::LLWebRTCSignalInterface *mWebRTCSignalingInterface; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + + LLVoiceDeviceList mCaptureDevices; + LLVoiceDeviceList mRenderDevices; + std::vector mIceCandidates; + bool mIceCompleted; + + bool mIsInitialized; + bool mShutdownComplete; + + bool checkParcelChanged(bool update = false); + bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + void joinSession(const sessionStatePtr_t &session); + + std::string nameFromAvatar(LLVOAvatar *avatar); + std::string nameFromID(const LLUUID &id); + bool IDFromName(const std::string name, LLUUID &uuid); + std::string displayNameFromAvatar(LLVOAvatar *avatar); + + + bool inSpatialChannel(void); + std::string getAudioSessionURI(); + std::string getAudioSessionHandle(); + + void setHidden(bool hidden); //virtual + void sendPositionAndVolumeUpdate(void); + + void sendFriendsListUpdates(); + +#if 0 + // start a text IM session with the specified user + // This will be asynchronous, the session may be established at a future time. + sessionStatePtr_t startUserIMSession(const LLUUID& uuid); +#endif + + void enforceTether(void); + + bool mSpatialCoordsDirty; + + LLVector3d mCameraPosition; + LLVector3d mCameraRequestedPosition; + LLVector3 mCameraVelocity; + LLMatrix3 mCameraRot; + + LLVector3d mAvatarPosition; + LLVector3 mAvatarVelocity; + LLQuaternion mAvatarRot; + + bool mMuteMic; + bool mMuteMicDirty; + bool mHidden; //Set to true during teleport to hide the agent's position. + + // Set to true when the friends list is known to have changed. + bool mFriendsListDirty; + + enum + { + earLocCamera = 0, // ear at camera + earLocAvatar, // ear at avatar + earLocMixed // ear at avatar location/camera direction + }; + + S32 mEarLocation; + + bool mSpeakerVolumeDirty; + bool mSpeakerMuteDirty; + int mSpeakerVolume; + + int mMicVolume; + bool mMicVolumeDirty; + + bool mVoiceEnabled; + bool mWriteInProgress; + std::string mWriteString; + size_t mWriteOffset; + + BOOL mLipSyncEnabled; + + typedef std::set observer_set_t; + observer_set_t mParticipantObservers; + + void notifyParticipantObservers(); + + typedef std::set status_observer_set_t; + status_observer_set_t mStatusObservers; + + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); + + typedef std::set friend_observer_set_t; + friend_observer_set_t mFriendObservers; + void notifyFriendObservers(); + + // Voice Fonts + + void expireVoiceFonts(); + void deleteVoiceFont(const LLUUID& id); + void deleteAllVoiceFonts(); + void deleteVoiceFontTemplates(); + + S32 getVoiceFontIndex(const LLUUID& id) const; + S32 getVoiceFontTemplateIndex(const LLUUID& id) const; + + void accountGetSessionFontsSendMessage(); + void accountGetTemplateFontsSendMessage(); + void sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session); + + void updateVoiceMorphingMenu(); + void notifyVoiceFontObservers(); + + typedef enum e_voice_font_type + { + VOICE_FONT_TYPE_NONE = 0, + VOICE_FONT_TYPE_ROOT = 1, + VOICE_FONT_TYPE_USER = 2, + VOICE_FONT_TYPE_UNKNOWN + } EVoiceFontType; + + typedef enum e_voice_font_status + { + VOICE_FONT_STATUS_NONE = 0, + VOICE_FONT_STATUS_FREE = 1, + VOICE_FONT_STATUS_NOT_FREE = 2, + VOICE_FONT_STATUS_UNKNOWN + } EVoiceFontStatus; + + struct voiceFontEntry + { + voiceFontEntry(LLUUID& id); + ~voiceFontEntry(); + + LLUUID mID; + S32 mFontIndex; + std::string mName; + LLDate mExpirationDate; + S32 mFontType; + S32 mFontStatus; + bool mIsNew; + + LLFrameTimer mExpiryTimer; + LLFrameTimer mExpiryWarningTimer; + }; + + bool mVoiceFontsReceived; + bool mVoiceFontsNew; + bool mVoiceFontListDirty; + voice_effect_list_t mVoiceFontList; + voice_effect_list_t mVoiceFontTemplateList; + + typedef std::map voice_font_map_t; + voice_font_map_t mVoiceFontMap; + voice_font_map_t mVoiceFontTemplateMap; + + typedef std::set voice_font_observer_set_t; + voice_font_observer_set_t mVoiceFontObservers; + + LLFrameTimer mVoiceFontExpiryTimer; + + + // Audio capture buffer + + void captureBufferRecordStartSendMessage(); + void captureBufferRecordStopSendMessage(); + void captureBufferPlayStartSendMessage(const LLUUID& voice_font_id = LLUUID::null); + void captureBufferPlayStopSendMessage(); + + bool mCaptureBufferMode; // Disconnected from voice channels while using the capture buffer. + bool mCaptureBufferRecording; // A voice sample is being captured. + bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. + bool mCaptureBufferPlaying; // A voice sample is being played. + + LLTimer mCaptureTimer; + LLUUID mPreviewVoiceFont; + LLUUID mPreviewVoiceFontLast; + S32 mPlayRequestCount; + bool mIsInTuningMode; + bool mIsInChannel; + bool mIsJoiningSession; + bool mIsWaitingForFonts; + bool mIsLoggingIn; + bool mIsLoggedIn; + bool mIsProcessingChannels; + bool mIsCoroutineActive; + + // This variables can last longer than WebRTC in coroutines so we need them as static + static bool sShuttingDown; + static bool sConnected; + static LLPumpIO* sPump; + + LLEventMailDrop mWebRTCPump; +}; + + +/** + * @class LLWebRTCProtocolParser + * @brief This class helps construct new LLIOPipe specializations + * @see LLIOPipe + * + * THOROUGH_DESCRIPTION + */ +class LLWebRTCProtocolParser : public LLIOPipe +{ + LOG_CLASS(LLWebRTCProtocolParser); +public: + LLWebRTCProtocolParser(); + virtual ~LLWebRTCProtocolParser(); + +protected: + /* @name LLIOPipe virtual implementations + */ + //@{ + /** + * @brief Process the data in buffer + */ + virtual EStatus process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump); + //@} + + std::string mInput; + + // Expat control members + XML_Parser parser; + int responseDepth; + bool ignoringTags; + bool isEvent; + int ignoreDepth; + + // Members for processing responses. The values are transient and only valid within a call to processResponse(). + int returnCode; + int statusCode; + std::string statusString; + std::string requestId; + std::string actionString; + std::string connectorHandle; + std::string versionID; + std::string mBuildID; + std::string accountHandle; + std::string sessionHandle; + std::string sessionGroupHandle; + std::string alias; + std::string applicationString; + + // Members for processing events. The values are transient and only valid within a call to processResponse(). + std::string eventTypeString; + int state; + std::string uriString; + bool isChannel; + bool incoming; + bool enabled; + std::string nameString; + std::string audioMediaString; + std::string deviceString; + std::string displayNameString; + int participantType; + bool isLocallyMuted; + bool isModeratorMuted; + bool isSpeaking; + int volume; + F32 energy; + std::string messageHeader; + std::string messageBody; + std::string notificationType; + bool hasText; + bool hasAudio; + bool hasVideo; + bool terminated; + std::string blockMask; + std::string presenceOnly; + std::string autoAcceptMask; + std::string autoAddAsBuddy; + int numberOfAliases; + std::string subscriptionHandle; + std::string subscriptionType; + S32 id; + std::string descriptionString; + LLDate expirationDate; + bool hasExpired; + S32 fontType; + S32 fontStatus; + std::string mediaCompletionType; + + // Members for processing text between tags + std::string textBuffer; + bool accumulateText; + + void reset(); + + void processResponse(std::string tag); + + static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); + static void XMLCALL ExpatEndTag(void *data, const char *el); + static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); + + void StartTag(const char *tag, const char **attr); + void EndTag(const char *tag); + void CharData(const char *buffer, int length); + LLDate expiryTimeStampToLLDate(const std::string& WebRTC_ts); + +}; + +class LLWebRTCSecurity : public LLSingleton +{ + LLSINGLETON(LLWebRTCSecurity); + virtual ~LLWebRTCSecurity(); + + public: + std::string connectorHandle() { return mConnectorHandle; }; + std::string accountHandle() { return mAccountHandle; }; + + private: + std::string mConnectorHandle; + std::string mAccountHandle; +}; + +class LLVoiceWebRTCStats : public LLSingleton +{ + LLSINGLETON(LLVoiceWebRTCStats); + LOG_CLASS(LLVoiceWebRTCStats); + virtual ~LLVoiceWebRTCStats(); + + private: + F64SecondsImplicit mStartTime; + + U32 mConnectCycles; + + F64 mConnectTime; + U32 mConnectAttempts; + + F64 mProvisionTime; + U32 mProvisionAttempts; + + F64 mEstablishTime; + U32 mEstablishAttempts; + + public: + + void reset(); + void connectionAttemptStart(); + void connectionAttemptEnd(bool success); + void provisionAttemptStart(); + void provisionAttemptEnd(bool success); + void establishAttemptStart(); + void establishAttemptEnd(bool success); + LLSD read(); +}; + +#endif //LL_WebRTC_VOICE_CLIENT_H + -- 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/llappviewer.h | 1 + indra/newview/llstartup.cpp | 2 +- indra/newview/llviewerregion.h | 1 + indra/newview/llvoicewebrtc.cpp | 21 ++---- indra/newview/llvoicewebrtc.h | 152 +++++++++++++++++++-------------------- indra/newview/viewer_manifest.py | 14 ++++ 6 files changed, 97 insertions(+), 94 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 6d1496d517..bfa1dea324 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -43,6 +43,7 @@ #ifndef LL_LLAPPVIEWER_H #define LL_LLAPPVIEWER_H +#include "llapp.h" #include "llallocator.h" #include "llapr.h" #include "llcontrol.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index d0b76848f7..e86ea5f2e3 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3337,7 +3337,7 @@ LLSD transform_cert_args(LLPointer cert) // are actually arrays, and we want to format them as comma separated // strings, so special case those. LLSDSerialize::toXML(cert_info[iter->first], std::cout); - if((iter->first == std::string(CERT_KEY_USAGE)) || + if((iter->first== std::string(CERT_KEY_USAGE)) || (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) { value = ""; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index a409d837a4..b24ff51479 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -43,6 +43,7 @@ #include "m4math.h" // LLMatrix4 #include "llframetimer.h" #include "llreflectionmap.h" +#include "llpointer.h" // Surface id's #define LAND 1 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); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 56ca74f6e1..22c022ffdb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -61,7 +61,7 @@ class LLWebRTCVoiceClient : public LLSingleton, public llwebrtc::LLWebRTCDevicesObserver, public llwebrtc::LLWebRTCSignalingObserver { - LLSINGLETON(LLWebRTCVoiceClient); + LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); virtual ~LLWebRTCVoiceClient(); @@ -69,26 +69,26 @@ public: /// @name LLVoiceModuleInterface virtual implementations /// @see LLVoiceModuleInterface //@{ - virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - virtual void terminate(); // Call this to clean up during shutdown + void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) + void terminate() override; // Call this to clean up during shutdown - virtual const LLVoiceVersionInfo& getVersion(); + const LLVoiceVersionInfo& getVersion() override; - virtual void updateSettings(); // call after loading settings and whenever they change + void updateSettings() override; // call after loading settings and whenever they change // Returns true if WebRTC has successfully logged in and is not in error state - virtual bool isVoiceWorking() const; + bool isVoiceWorking() const override; ///////////////////// /// @name Tuning //@{ - virtual void tuningStart(); - virtual void tuningStop(); - virtual bool inTuningMode(); + void tuningStart() override; + void tuningStop() override; + bool inTuningMode() override; - virtual void tuningSetMicVolume(float volume); - virtual void tuningSetSpeakerVolume(float volume); - virtual float tuningGetEnergy(void); + void tuningSetMicVolume(float volume) override; + void tuningSetSpeakerVolume(float volume) override; + float tuningGetEnergy(void) override; //@} ///////////////////// @@ -96,40 +96,40 @@ public: //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. - virtual bool deviceSettingsAvailable(); - virtual bool deviceSettingsUpdated(); //return if the list has been updated and never fetched, only to be called from the voicepanel. + bool deviceSettingsAvailable() override; + bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the WebRTC daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - virtual void refreshDeviceLists(bool clearCurrentList = true); + void refreshDeviceLists(bool clearCurrentList = true) override; - virtual void setCaptureDevice(const std::string& name); - virtual void setRenderDevice(const std::string& name); + void setCaptureDevice(const std::string& name) override; + void setRenderDevice(const std::string& name) override; - virtual LLVoiceDeviceList& getCaptureDevices(); - virtual LLVoiceDeviceList& getRenderDevices(); + LLVoiceDeviceList& getCaptureDevices() override; + LLVoiceDeviceList& getRenderDevices() override; //@} - virtual void getParticipantList(std::set &participants); - virtual bool isParticipant(const LLUUID& speaker_id); + void getParticipantList(std::set &participants) override; + bool isParticipant(const LLUUID& speaker_id) override; // Send a text message to the specified user, initiating the session if necessary. // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; // close any existing text IM session with the specified user - virtual void endUserIMSession(const LLUUID &uuid); + void endUserIMSession(const LLUUID &uuid) override; // Returns true if calling back the session URI after the session has closed is possible. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. - virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); + BOOL isSessionCallBackPossible(const LLUUID &session_id) override; // Returns true if the session can accepte text IM's. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. - virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); + BOOL isSessionTextIMPossible(const LLUUID &session_id) override; //////////////////////////// @@ -137,21 +137,21 @@ public: //@{ // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. - virtual bool inProximalChannel(); + bool inProximalChannel() override; - virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials); + void setNonSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual bool setSpatialChannel(const std::string &uri, - const std::string &credentials); + bool setSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual void leaveNonSpatialChannel(); + void leaveNonSpatialChannel() override; - virtual void leaveChannel(void); + void leaveChannel(void) override; // Returns the URI of the current channel, or an empty string if not currently in a channel. // NOTE that it will return an empty string if it's in the process of joining a channel. - virtual std::string getCurrentChannel(); + std::string getCurrentChannel() override; //@} @@ -159,59 +159,59 @@ public: /// @name invitations //@{ // start a voice channel with the specified user - virtual void callUser(const LLUUID &uuid); - virtual bool isValidChannel(std::string &channelHandle); - virtual bool answerInvite(std::string &channelHandle); - virtual void declineInvite(std::string &channelHandle); + void callUser(const LLUUID &uuid) override; + bool isValidChannel(std::string &channelHandle) override; + bool answerInvite(std::string &channelHandle) override; + void declineInvite(std::string &channelHandle) override; //@} ///////////////////////// /// @name Volume/gain //@{ - virtual void setVoiceVolume(F32 volume); - virtual void setMicGain(F32 volume); + void setVoiceVolume(F32 volume) override; + void setMicGain(F32 volume) override; //@} ///////////////////////// /// @name enable disable voice and features //@{ - virtual bool voiceEnabled(); - virtual void setVoiceEnabled(bool enabled); - virtual BOOL lipSyncEnabled(); - virtual void setLipSyncEnabled(BOOL enabled); - virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + bool voiceEnabled() override; + void setVoiceEnabled(bool enabled) override; + BOOL lipSyncEnabled() override; + void setLipSyncEnabled(BOOL enabled) override; + void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} ////////////////////////// /// @name nearby speaker accessors //@{ - virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - virtual std::string getDisplayName(const LLUUID& id); - virtual BOOL isParticipantAvatar(const LLUUID &id); - virtual BOOL getIsSpeaking(const LLUUID& id); - virtual BOOL getIsModeratorMuted(const LLUUID& id); - virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id); - virtual F32 getUserVolume(const LLUUID& id); - virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar + std::string getDisplayName(const LLUUID& id) override; + BOOL isParticipantAvatar(const LLUUID &id) override; + BOOL getIsSpeaking(const LLUUID& id) override; + BOOL getIsModeratorMuted(const LLUUID& id) override; + F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id) override; + F32 getUserVolume(const LLUUID& id) override; + void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} // authorize the user - virtual void userAuthorized(const std::string& user_id, - const LLUUID &agentID); + void userAuthorized(const std::string& user_id, + const LLUUID &agentID) override; ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceClientStatusObserver* observer); - virtual void removeObserver(LLVoiceClientStatusObserver* observer); - virtual void addObserver(LLFriendObserver* observer); - virtual void removeObserver(LLFriendObserver* observer); - virtual void addObserver(LLVoiceClientParticipantObserver* observer); - virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + void addObserver(LLVoiceClientStatusObserver* observer) override; + void removeObserver(LLVoiceClientStatusObserver* observer) override; + void addObserver(LLFriendObserver* observer) override; + void removeObserver(LLFriendObserver* observer) override; + void addObserver(LLVoiceClientParticipantObserver* observer) override; + void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - virtual std::string sipURIFromID(const LLUUID &id); + std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -221,20 +221,20 @@ public: ////////////////////////// /// @name Accessors //@{ - virtual bool setVoiceEffect(const LLUUID& id); - virtual const LLUUID getVoiceEffect(); - virtual LLSD getVoiceEffectProperties(const LLUUID& id); + bool setVoiceEffect(const LLUUID& id) override; + const LLUUID getVoiceEffect() override; + LLSD getVoiceEffectProperties(const LLUUID& id) override; - virtual void refreshVoiceEffectLists(bool clear_lists); - virtual const voice_effect_list_t& getVoiceEffectList() const; - virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + void refreshVoiceEffectLists(bool clear_lists) override; + const voice_effect_list_t& getVoiceEffectList() const override; + const voice_effect_list_t& getVoiceEffectTemplateList() const override; //@} ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceEffectObserver* observer); - virtual void removeObserver(LLVoiceEffectObserver* observer); + void addObserver(LLVoiceEffectObserver* observer) override; + void removeObserver(LLVoiceEffectObserver* observer) override; //@} ////////////////////////////// @@ -263,13 +263,13 @@ public: ////////////////////////////// /// @name Effect preview buffer //@{ - virtual void enablePreviewBuffer(bool enable); - virtual void recordPreviewBuffer(); - virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); - virtual void stopPreviewBuffer(); + void enablePreviewBuffer(bool enable) override; + void recordPreviewBuffer() override; + void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; + void stopPreviewBuffer() override; - virtual bool isPreviewRecording(); - virtual bool isPreviewPlaying(); + bool isPreviewRecording() override; + bool isPreviewPlaying() override; //@} //@} @@ -773,7 +773,7 @@ private: std::string getAudioSessionURI(); std::string getAudioSessionHandle(); - void setHidden(bool hidden); //virtual + void setHidden(bool hidden) override; //virtual void sendPositionAndVolumeUpdate(void); void sendFriendsListUpdates(); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 1fa4df1682..9230cb0589 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -547,6 +547,12 @@ class Windows_x86_64_Manifest(ViewerManifest): # Get shared libs from the shared libs staging directory with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'sharedlibs', self.args['buildtype'])): + # WebRTC libraries + for libfile in ( + 'llwebrtc.dll', + ): + self.path(libfile) + # Get fmodstudio dll if needed if self.args['fmodstudio'] == 'ON': if(self.args['buildtype'].lower() == 'debug'): @@ -990,6 +996,14 @@ class Darwin_x86_64_Manifest(ViewerManifest): print("Skipping %s" % dst) return added + # WebRTC libraries + with self.prefix(src=os.path.join(self.args['build'], os.pardir, + 'sharedlibs', self.args['buildtype'], 'Resources')): + for libfile in ( + 'libllwebrtc.dylib', + ): + self.path(libfile) + # dylibs is a list of all the .dylib files we expect to need # in our bundled sub-apps. For each of these we'll create a # symlink from sub-app/Contents/Resources to the real .dylib. -- cgit v1.2.3 From 3923fe4b4c84a6ef12c44491a6eede7d94058748 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 10 Sep 2023 00:39:58 -0700 Subject: remove unnecessary build file --- indra/newview/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6d9b0ab2dc..c804f5403c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -30,7 +30,6 @@ include(LLKDU) include(LLPhysicsExtensions) include(LLPrimitive) include(LLWindow) -include(LLWebRTC) include(NDOF) include(NVAPI) include(OPENAL) -- cgit v1.2.3 From 4079c05dd8c2d36a736b07d857152ae7989b1310 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 11 Sep 2023 15:38:58 -0700 Subject: some build tweaks --- indra/newview/viewer_manifest.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 9230cb0589..b7b737ee4b 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1004,6 +1004,12 @@ class Darwin_x86_64_Manifest(ViewerManifest): ): self.path(libfile) + oldpath = os.path.join("@rpath", libfile) + self.run_command( + ['install_name_tool', '-change', oldpath, + '@executable_path/../Resources/%s' % libfile, + executable]) + # dylibs is a list of all the .dylib files we expect to need # in our bundled sub-apps. For each of these we'll create a # symlink from sub-app/Contents/Resources to the real .dylib. -- 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 +++++++++++++++++++++++++--------------- indra/newview/llvoicewebrtc.h | 13 ++++ 2 files changed, 94 insertions(+), 48 deletions(-) (limited to 'indra/newview') 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 diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 22c022ffdb..061e00581e 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -252,6 +252,7 @@ public: void OnIceGatheringState(IceGatheringState state) override; void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; void OnOfferAvailable(const std::string &sdp) override; + void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; //@} @@ -651,7 +652,19 @@ private: //--- void voiceControlCoro(); void voiceControlStateMachine(); + int mVoiceControlState; + LLMutex mVoiceStateMutex; + void setVoiceControlState(int new_voice_control_state) + { + LLMutexLock lock(&mVoiceStateMutex); + mVoiceControlState = new_voice_control_state; + } + int getVoiceControlState() + { + LLMutexLock lock(&mVoiceStateMutex); + return mVoiceControlState; + } bool endAndDisconnectSession(); -- 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 +++++++++++++++++++++-------------------- indra/newview/llvoicewebrtc.h | 7 ++++-- 2 files changed, 30 insertions(+), 25 deletions(-) (limited to 'indra/newview') 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); } ///////////////////////////// diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 061e00581e..f4e2ac0b7f 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -655,10 +655,13 @@ private: int mVoiceControlState; LLMutex mVoiceStateMutex; - void setVoiceControlState(int new_voice_control_state) + void setVoiceControlStateUnless(int new_voice_control_state, int unless=-1) { LLMutexLock lock(&mVoiceStateMutex); - mVoiceControlState = new_voice_control_state; + if (mVoiceControlState != unless) + { + mVoiceControlState = new_voice_control_state; + } } int getVoiceControlState() { -- 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 ++-------------------------------------- indra/newview/llvoicewebrtc.h | 114 ------ 2 files changed, 25 insertions(+), 886 deletions(-) (limited to 'indra/newview') 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() { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f4e2ac0b7f..e040552ab2 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -712,8 +712,6 @@ private: // We should kill the voice daemon in case of connection alert bool mTerminateDaemon; - friend class LLWebRTCProtocolParser; - std::string mAccountName; std::string mAccountPassword; std::string mChannelSDP; @@ -956,118 +954,6 @@ private: LLEventMailDrop mWebRTCPump; }; - -/** - * @class LLWebRTCProtocolParser - * @brief This class helps construct new LLIOPipe specializations - * @see LLIOPipe - * - * THOROUGH_DESCRIPTION - */ -class LLWebRTCProtocolParser : public LLIOPipe -{ - LOG_CLASS(LLWebRTCProtocolParser); -public: - LLWebRTCProtocolParser(); - virtual ~LLWebRTCProtocolParser(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - - std::string mInput; - - // Expat control members - XML_Parser parser; - int responseDepth; - bool ignoringTags; - bool isEvent; - int ignoreDepth; - - // Members for processing responses. The values are transient and only valid within a call to processResponse(). - int returnCode; - int statusCode; - std::string statusString; - std::string requestId; - std::string actionString; - std::string connectorHandle; - std::string versionID; - std::string mBuildID; - std::string accountHandle; - std::string sessionHandle; - std::string sessionGroupHandle; - std::string alias; - std::string applicationString; - - // Members for processing events. The values are transient and only valid within a call to processResponse(). - std::string eventTypeString; - int state; - std::string uriString; - bool isChannel; - bool incoming; - bool enabled; - std::string nameString; - std::string audioMediaString; - std::string deviceString; - std::string displayNameString; - int participantType; - bool isLocallyMuted; - bool isModeratorMuted; - bool isSpeaking; - int volume; - F32 energy; - std::string messageHeader; - std::string messageBody; - std::string notificationType; - bool hasText; - bool hasAudio; - bool hasVideo; - bool terminated; - std::string blockMask; - std::string presenceOnly; - std::string autoAcceptMask; - std::string autoAddAsBuddy; - int numberOfAliases; - std::string subscriptionHandle; - std::string subscriptionType; - S32 id; - std::string descriptionString; - LLDate expirationDate; - bool hasExpired; - S32 fontType; - S32 fontStatus; - std::string mediaCompletionType; - - // Members for processing text between tags - std::string textBuffer; - bool accumulateText; - - void reset(); - - void processResponse(std::string tag); - - static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); - static void XMLCALL ExpatEndTag(void *data, const char *el); - static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); - - void StartTag(const char *tag, const char **attr); - void EndTag(const char *tag); - void CharData(const char *buffer, int length); - LLDate expiryTimeStampToLLDate(const std::string& WebRTC_ts); - -}; - class LLWebRTCSecurity : public LLSingleton { LLSINGLETON(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') 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 +++++++++--- indra/newview/llvoicewebrtc.h | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'indra/newview') 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); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e040552ab2..0dad2909d4 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -829,7 +829,7 @@ private: bool mSpeakerVolumeDirty; bool mSpeakerMuteDirty; - int mSpeakerVolume; + float mSpeakerVolume; int mMicVolume; bool mMicVolumeDirty; -- cgit v1.2.3 From f3701803ebb7bc2c93e1642ecaf9c3cf64f10bcb Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 13 Sep 2023 22:50:06 -0700 Subject: build fix on newer osx machines --- indra/newview/llviewerhelp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index 3181ae6283..61e70dc820 100644 --- a/indra/newview/llviewerhelp.cpp +++ b/indra/newview/llviewerhelp.cpp @@ -45,7 +45,7 @@ public: // requests will be throttled from a non-trusted browser LLHelpHandler() : LLCommandHandler("help", UNTRUSTED_CLICK_ONLY) {} - bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) override { LLViewerHelp* vhelp = LLViewerHelp::getInstance(); if (! vhelp) -- 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 +++++++++++++++------------------------- indra/newview/llvoicewebrtc.h | 2 - 2 files changed, 134 insertions(+), 229 deletions(-) (limited to 'indra/newview') 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); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 0dad2909d4..eb3893fe2b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -669,8 +669,6 @@ private: return mVoiceControlState; } - bool endAndDisconnectSession(); - bool callbackEndDaemon(const LLSD& data); bool provisionVoiceAccount(); void OnVoiceAccountProvisioned(const LLSD& body); -- 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') 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') 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') 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 976a75ed24f11e3211a98120b1ac57126e625d6c Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 18 Sep 2023 11:25:47 -0700 Subject: Fix voice device settings --- indra/newview/llpanelvoicedevicesettings.cpp | 79 ++++++++++++++++------------ 1 file changed, 44 insertions(+), 35 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 28631e2b7b..48c90d6856 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -236,44 +236,47 @@ void LLPanelVoiceDeviceSettings::refresh() if(mCtrlInputDevices) { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - - for(device=LLVoiceClient::getInstance()->getCaptureDevices().begin(); - device != LLVoiceClient::getInstance()->getCaptureDevices().end(); - device++) - { - mCtrlInputDevices->add(getLocalizedDeviceName(device->display_name), device->full_name, ADD_BOTTOM); - } - - // Fix invalid input audio device preference. - if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE)) - { - mCtrlInputDevices->setValue(DEFAULT_DEVICE); - gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE); - mInputDevice = DEFAULT_DEVICE; - } + LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); + if (devices.size() > 0) // if zero, we've not received our devices yet + { + mCtrlInputDevices->removeall(); + mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + for (auto& device : devices) + { + mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); + } + + // Fix invalid input audio device preference. + if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE)) + { + mCtrlInputDevices->setValue(DEFAULT_DEVICE); + gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE); + mInputDevice = DEFAULT_DEVICE; + } + } } if(mCtrlOutputDevices) { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - - for(device = LLVoiceClient::getInstance()->getRenderDevices().begin(); - device != LLVoiceClient::getInstance()->getRenderDevices().end(); - device++) - { - mCtrlOutputDevices->add(getLocalizedDeviceName(device->display_name), device->full_name, ADD_BOTTOM); - } - - // Fix invalid output audio device preference. - if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE)) - { - mCtrlOutputDevices->setValue(DEFAULT_DEVICE); - gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE); - mOutputDevice = DEFAULT_DEVICE; - } + LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); + if (devices.size() > 0) // if zero, we've not received our devices yet + { + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + + for (auto& device : devices) + { + mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); + } + + // Fix invalid output audio device preference. + if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE)) + { + mCtrlOutputDevices->setValue(DEFAULT_DEVICE); + gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE); + mOutputDevice = DEFAULT_DEVICE; + } + } } } } @@ -316,8 +319,11 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice() if(LLVoiceClient::getInstance()) { mInputDevice = mCtrlInputDevices->getValue().asString(); - LLVoiceClient::getInstance()->setRenderDevice(mInputDevice); + LLVoiceClient::getInstance()->setCaptureDevice(mInputDevice); } + // the preferences floater stuff is a mess, hence apply will never + // be called when 'ok' is pressed, so just force it for now. + apply(); } void LLPanelVoiceDeviceSettings::onCommitOutputDevice() @@ -328,6 +334,9 @@ void LLPanelVoiceDeviceSettings::onCommitOutputDevice() mOutputDevice = mCtrlOutputDevices->getValue().asString(); LLVoiceClient::getInstance()->setRenderDevice(mOutputDevice); } + // the preferences floater stuff is a mess, hence apply will never + // be called when 'ok' is pressed, so just force it for now. + apply(); } void LLPanelVoiceDeviceSettings::onOutputDevicesClicked() -- 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 +++++++++ indra/newview/llvoicewebrtc.h | 13 +++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'indra/newview') 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; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index eb3893fe2b..0d6988e1ef 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -59,7 +59,8 @@ class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, public llwebrtc::LLWebRTCDevicesObserver, - public llwebrtc::LLWebRTCSignalingObserver + public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCDataObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); @@ -254,6 +255,13 @@ public: void OnOfferAvailable(const std::string &sdp) override; void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + //@} + + ///////////////////////// + /// @name Data Notification + /// LLWebRTCDataObserver + //@{ + void OnDataReceived(const std::string& data, bool binary) override; //@} void processIceUpdates(); @@ -761,7 +769,8 @@ private: llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; llwebrtc::LLWebRTCSignalInterface *mWebRTCSignalingInterface; - llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; -- 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 +++++++++++----------------------------- indra/newview/llvoicewebrtc.h | 24 ++- 2 files changed, 136 insertions(+), 332 deletions(-) (limited to 'indra/newview') 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() diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 0d6988e1ef..eb898ab4eb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -60,6 +60,7 @@ class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceEffectInterface, public llwebrtc::LLWebRTCDevicesObserver, public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCAudioObserver, public llwebrtc::LLWebRTCDataObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); @@ -80,6 +81,8 @@ public: // Returns true if WebRTC has successfully logged in and is not in error state bool isVoiceWorking() const override; + std::string sipURIFromID(const LLUUID &id) override; + ///////////////////// /// @name Tuning //@{ @@ -212,7 +215,6 @@ public: void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -242,8 +244,8 @@ public: /// @name Devices change notification // LLWebRTCDevicesObserver //@{ - void OnRenderDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; - void OnCaptureDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices) override; + void OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override; //@} ////////////////////////////// @@ -256,6 +258,13 @@ public: void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; //@} + + ////////////////////////////// + /// @name Signaling notification + // LLWebRTCAudioObserver + //@{ + void OnAudioLevel(float level) override; + //@} ///////////////////////// /// @name Data Notification @@ -307,7 +316,7 @@ protected: struct participantState { public: - participantState(const std::string &uri); + participantState(const LLUUID& agent_id); bool updateMuteState(); // true if mute state has changed bool isAvatar(); @@ -348,7 +357,7 @@ protected: static ptr_t createSession(); ~sessionState(); - participantStatePtr_t addParticipant(const std::string &uri); + participantStatePtr_t addParticipant(const LLUUID& agent_id); void removeParticipant(const participantStatePtr_t &participant); void removeAllParticipants(); @@ -496,7 +505,6 @@ protected: void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); - void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); void auxAudioPropertiesEvent(F32 energy); void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); @@ -545,7 +553,7 @@ protected: void filePlaybackSetMode(bool vox = false, float speed = 1.0f); participantStatePtr_t findParticipantByID(const LLUUID& id); - + participantStatePtr_t addParticipantByID(const LLUUID &id); #if 0 //////////////////////////////////////// @@ -556,7 +564,7 @@ protected: sessionIterator sessionsBegin(void); sessionIterator sessionsEnd(void); #endif - + void sessionEstablished(); sessionStatePtr_t findSession(const std::string &handle); sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); sessionStatePtr_t findSession(const LLUUID &participant_id); -- 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 +++++++++++++++++++++---- indra/newview/llvoicewebrtc.h | 3 ++- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'indra/newview') 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 diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index eb898ab4eb..6ddda0ef94 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -554,7 +554,8 @@ protected: participantStatePtr_t findParticipantByID(const LLUUID& id); participantStatePtr_t addParticipantByID(const LLUUID &id); - + void removeParticipantByID(const LLUUID &id); + #if 0 //////////////////////////////////////// // voice sessions. -- 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 ++++++++++ indra/newview/llvoicewebrtc.h | 1 + 2 files changed, 11 insertions(+) (limited to 'indra/newview') 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() { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 6ddda0ef94..bc858dcb32 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -271,6 +271,7 @@ public: /// LLWebRTCDataObserver //@{ void OnDataReceived(const std::string& data, bool binary) override; + void OnDataChannelReady() override; //@} void processIceUpdates(); -- 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 ++++++++---------------- indra/newview/llvoicewebrtc.h | 1 - 2 files changed, 8 insertions(+), 17 deletions(-) (limited to 'indra/newview') 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 diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index bc858dcb32..518fee53ef 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -263,7 +263,6 @@ public: /// @name Signaling notification // LLWebRTCAudioObserver //@{ - void OnAudioLevel(float level) override; //@} ///////////////////////// -- 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 ++++++++++----- indra/newview/llvoicewebrtc.h | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'indra/newview') 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; + } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 518fee53ef..e3aa33bfd9 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -786,6 +786,8 @@ private: std::vector mIceCandidates; bool mIceCompleted; + uint32_t mAudioLevel; + bool mIsInitialized; bool mShutdownComplete; -- 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') 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') 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 +++++++--------------------------------- indra/newview/llvoicewebrtc.h | 4 +- 2 files changed, 45 insertions(+), 207 deletions(-) (limited to 'indra/newview') 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; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e3aa33bfd9..1d013c6609 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -521,7 +521,7 @@ protected: ///////////////////////////// // Sending updates of current state void updatePosition(void); - void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); bool channelFromRegion(LLViewerRegion *region, std::string &name); @@ -823,7 +823,7 @@ private: LLVector3d mCameraPosition; LLVector3d mCameraRequestedPosition; LLVector3 mCameraVelocity; - LLMatrix3 mCameraRot; + LLQuaternion mCameraRot; LLVector3d mAvatarPosition; LLVector3 mAvatarVelocity; -- 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') 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 +++++++++++++++++++++------------------- indra/newview/llvoicewebrtc.h | 4 +- 2 files changed, 55 insertions(+), 50 deletions(-) (limited to 'indra/newview') 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) { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 1d013c6609..bd4346022f 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -40,6 +40,7 @@ class LLWebRTCProtocolParser; #include "lleventcoro.h" #include "llcoros.h" #include +#include "json/reader.h" #ifdef LL_USESYSTEMLIBS # include "expat.h" @@ -806,7 +807,8 @@ private: std::string getAudioSessionHandle(); void setHidden(bool hidden) override; //virtual - void sendPositionAndVolumeUpdate(void); + Json::Value getPositionAndVolumeUpdateJson(bool force); + void sendPositionAndVolumeUpdate(); void sendFriendsListUpdates(); -- 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') 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') 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') 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') 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') 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') 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 795edd490b38ee5f3330dd2dedf980f10950ec22 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 12 Oct 2023 17:31:02 -0700 Subject: build fix --- indra/newview/llviewerhelp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index 61e70dc820..6374d68988 100644 --- a/indra/newview/llviewerhelp.cpp +++ b/indra/newview/llviewerhelp.cpp @@ -45,7 +45,7 @@ public: // requests will be throttled from a non-trusted browser LLHelpHandler() : LLCommandHandler("help", UNTRUSTED_CLICK_ONLY) {} - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) override + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) override { LLViewerHelp* vhelp = LLViewerHelp::getInstance(); if (! vhelp) -- cgit v1.2.3 From 5c574d44c5770353c5063209668b10a5fbdd5e05 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 12 Oct 2023 21:21:50 -0700 Subject: copy llwebrtc.dll to the right place on build. --- indra/newview/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c804f5403c..3f92665805 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1727,7 +1727,7 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/openjp2.dll ${SHARED_LIB_STAGING_DIR}/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/uriparser.dll - ${CMAKE_BINARY_DIR}/llwebrtc/Release/llwebrtc.dll + ${SHARED_LIB_STAGING_DIR}/llwebrtc.dll #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/SLVoice.exe #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/libsndfile-1.dll #${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/vivoxoal.dll @@ -1794,13 +1794,14 @@ if (WINDOWS) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py stage_third_party_libs + llwebrtc ${COPY_INPUT_DEPENDENCIES} COMMENT "Performing viewer_manifest copy" ) add_custom_target(copy_w_viewer_manifest ALL DEPENDS ${CMAKE_CFG_INTDIR}/copy_touched.bat) - add_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon copy_w_viewer_manifest) + add_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon llwebrtc copy_w_viewer_manifest) if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts) -- 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 +++++++++++++++++++++++----------------- indra/newview/llvoicewebrtc.h | 5 +- 2 files changed, 66 insertions(+), 47 deletions(-) (limited to 'indra/newview') 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; } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index bd4346022f..fd7094cb46 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -275,8 +275,8 @@ public: //@} void processIceUpdates(); - void onIceUpdateComplete(const LLSD& result); - void onIceUpdateError(int retries, std::string url, LLSD body, const LLSD& result); + void onIceUpdateComplete(bool ice_completed, const LLSD& result); + void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); ////////////////////////////// @@ -786,6 +786,7 @@ private: LLVoiceDeviceList mRenderDevices; std::vector mIceCandidates; bool mIceCompleted; + bool mTrickling; uint32_t mAudioLevel; -- 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') 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 ++++++++++++++++++++-------------------- indra/newview/llvoicewebrtc.h | 4 ++-- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'indra/newview') 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); } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index fd7094cb46..b33dc26dff 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -271,7 +271,7 @@ public: /// LLWebRTCDataObserver //@{ void OnDataReceived(const std::string& data, bool binary) override; - void OnDataChannelReady() override; + void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} void processIceUpdates(); @@ -778,7 +778,7 @@ private: buddyListMap mBuddyListMap; llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; - llwebrtc::LLWebRTCSignalInterface *mWebRTCSignalingInterface; + llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; -- 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 ++------------------------------------- indra/newview/llvoicewebrtc.h | 252 +--- 2 files changed, 121 insertions(+), 2611 deletions(-) (limited to 'indra/newview') 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(); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index b33dc26dff..ee7edb3030 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -53,8 +53,6 @@ class LLWebRTCProtocolParser; #include class LLAvatarName; -class LLWebRTCVoiceClientMuteListObserver; - class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, @@ -200,6 +198,40 @@ public: F32 getUserVolume(const LLUUID& id) override; void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} + + ////////////////////////// + /// @name Effect Accessors + //@{ + bool setVoiceEffect(const LLUUID &id) override { return false; } + const LLUUID getVoiceEffect() override { return LLUUID(); } + LLSD getVoiceEffectProperties(const LLUUID &id) override { return LLSD(); } + + void refreshVoiceEffectLists(bool clear_lists) override {}; + const voice_effect_list_t &getVoiceEffectList() const override { return mVoiceEffectList; } + const voice_effect_list_t &getVoiceEffectTemplateList() const override { return mVoiceEffectList; } + + voice_effect_list_t mVoiceEffectList; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + void addObserver(LLVoiceEffectObserver *observer) override {} + void removeObserver(LLVoiceEffectObserver *observer) override {} + //@} + + ////////////////////////////// + /// @name Preview buffer + //@{ + void enablePreviewBuffer(bool enable) override {} + void recordPreviewBuffer() override {} + void playPreviewBuffer(const LLUUID &effect_id = LLUUID::null) override {} + void stopPreviewBuffer() override {} + + bool isPreviewRecording() override { return false; } + bool isPreviewPlaying() override { return false; } + //@} + // authorize the user void userAuthorized(const std::string& user_id, @@ -218,28 +250,7 @@ public: //@} - /// @name LLVoiceEffectInterface virtual implementations - /// @see LLVoiceEffectInterface - //@{ - ////////////////////////// - /// @name Accessors - //@{ - bool setVoiceEffect(const LLUUID& id) override; - const LLUUID getVoiceEffect() override; - LLSD getVoiceEffectProperties(const LLUUID& id) override; - - void refreshVoiceEffectLists(bool clear_lists) override; - const voice_effect_list_t& getVoiceEffectList() const override; - const voice_effect_list_t& getVoiceEffectTemplateList() const override; - //@} - - ////////////////////////////// - /// @name Status notification - //@{ - void addObserver(LLVoiceEffectObserver* observer) override; - void removeObserver(LLVoiceEffectObserver* observer) override; - //@} ////////////////////////////// /// @name Devices change notification @@ -279,29 +290,11 @@ public: void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); - ////////////////////////////// - /// @name Effect preview buffer - //@{ - void enablePreviewBuffer(bool enable) override; - void recordPreviewBuffer() override; - void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; - void stopPreviewBuffer() override; - - bool isPreviewRecording() override; - bool isPreviewPlaying() override; //@} - //@} - - bool onCheckVoiceEffect(const std::string& voice_effect_name); - void onClickVoiceEffect(const std::string& voice_effect_name); - protected: ////////////////////// // WebRTC Specific definitions - - friend class LLWebRTCVoiceClientMuteListObserver; - friend class LLWebRTCVoiceClientFriendsObserver; enum streamState @@ -366,7 +359,6 @@ protected: participantStatePtr_t findParticipantByID(const LLUUID& id); static ptr_t matchSessionByHandle(const std::string &handle); - static ptr_t matchCreatingSessionByURI(const std::string &uri); static ptr_t matchSessionByURI(const std::string &uri); static ptr_t matchSessionByParticipant(const LLUUID &participant_id); @@ -381,7 +373,6 @@ protected: std::string mAlias; std::string mName; std::string mAlternateSIPURI; - std::string mHash; // Channel password std::string mErrorStatusString; std::queue mTextMsgQueue; @@ -389,11 +380,6 @@ protected: LLUUID mCallerID; int mErrorStatusCode; int mMediaStreamState; - bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. - bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. - bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) - bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) - bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) bool mIsSpatial; // True for spatial channels bool mIsP2P; @@ -447,8 +433,6 @@ protected: // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. void giveUp(); - // write to the tvc - bool writeString(const std::string &str); void connectorCreate(); void connectorShutdown(); @@ -459,21 +443,10 @@ protected: const std::string& account_name, const std::string& password, const std::string& channel_sdp); - void loginSendMessage(); - void logout(); - void logoutSendMessage(); + void logout(); //@} - - //------------------------------------ - // tuning - - void tuningRenderStartSendMessage(const std::string& name, bool loop); - void tuningRenderStopSendMessage(); - - void tuningCaptureStartSendMessage(int duration); - void tuningCaptureStopSendMessage(); //---------------------------------- // devices @@ -489,35 +462,9 @@ protected: void sendLocalAudioUpdates(); ///////////////////////////// - // Response/Event handlers - void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); - void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); - void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); - void logoutResponse(int statusCode, std::string &statusString); - void connectorShutdownResponse(int statusCode, std::string &statusString); - - void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); - void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); - void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); - void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); - void sessionGroupAddedEvent(std::string &sessionGroupHandle); - void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); - void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); - void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); - void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); - void auxAudioPropertiesEvent(F32 energy); - void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); - void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); - + // Event handlers + void muteListChanged(); - - ///////////////////////////// - // VAD changes - // disable auto-VAD and configure VAD parameters explicitly - void setupVADParams(unsigned int vad_auto, unsigned int vad_hangover, unsigned int vad_noise_floor, unsigned int vad_sensitivity); - void onVADSettingsChange(); ///////////////////////////// // Sending updates of current state @@ -539,19 +486,6 @@ protected: ///////////////////////////// BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. // Use this to determine whether to show a "no speech" icon in the menu bar. - - - ///////////////////////////// - // Recording controls - void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); - void recordingLoopSave(const std::string& filename); - void recordingStop(); - - // Playback controls - void filePlaybackStart(const std::string& filename); - void filePlaybackStop(); - void filePlaybackSetPaused(bool paused); - void filePlaybackSetMode(bool vox = false, float speed = 1.0f); participantStatePtr_t findParticipantByID(const LLUUID& id); participantStatePtr_t addParticipantByID(const LLUUID &id); @@ -568,7 +502,6 @@ protected: #endif void sessionEstablished(); sessionStatePtr_t findSession(const std::string &handle); - sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); sessionStatePtr_t findSession(const LLUUID &participant_id); sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); @@ -611,24 +544,6 @@ protected: typedef std::map buddyListMap; - ///////////////////////////// - // session control messages - - void accountListBlockRulesSendMessage(); - void accountListAutoAcceptRulesSendMessage(); - - void sessionGroupCreateSendMessage(); - void sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); - void sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); - void sessionMediaConnectSendMessage(const sessionStatePtr_t &session); // just joins the audio session - void sessionTextConnectSendMessage(const sessionStatePtr_t &session); // just joins the text session - void sessionTerminateSendMessage(const sessionStatePtr_t &session); - void sessionGroupTerminateSendMessage(const sessionStatePtr_t &session); - void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session); - // void sessionTextDisconnectSendMessage(sessionState *session); - - - // Pokes the state machine to leave the audio session next time around. void sessionTerminate(); @@ -704,10 +619,6 @@ private: bool waitForChannel(); bool runSession(const sessionStatePtr_t &session); - void recordingAndPlaybackMode(); - int voiceRecordBuffer(); - int voicePlaybackBuffer(); - bool performMicTuning(); //--- /// Clean up objects created during a voice session. @@ -811,14 +722,6 @@ private: Json::Value getPositionAndVolumeUpdateJson(bool force); void sendPositionAndVolumeUpdate(); - void sendFriendsListUpdates(); - -#if 0 - // start a text IM session with the specified user - // This will be asynchronous, the session may be established at a future time. - sessionStatePtr_t startUserIMSession(const LLUUID& uuid); -#endif - void enforceTether(void); bool mSpatialCoordsDirty; @@ -876,87 +779,6 @@ private: friend_observer_set_t mFriendObservers; void notifyFriendObservers(); - // Voice Fonts - - void expireVoiceFonts(); - void deleteVoiceFont(const LLUUID& id); - void deleteAllVoiceFonts(); - void deleteVoiceFontTemplates(); - - S32 getVoiceFontIndex(const LLUUID& id) const; - S32 getVoiceFontTemplateIndex(const LLUUID& id) const; - - void accountGetSessionFontsSendMessage(); - void accountGetTemplateFontsSendMessage(); - void sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session); - - void updateVoiceMorphingMenu(); - void notifyVoiceFontObservers(); - - typedef enum e_voice_font_type - { - VOICE_FONT_TYPE_NONE = 0, - VOICE_FONT_TYPE_ROOT = 1, - VOICE_FONT_TYPE_USER = 2, - VOICE_FONT_TYPE_UNKNOWN - } EVoiceFontType; - - typedef enum e_voice_font_status - { - VOICE_FONT_STATUS_NONE = 0, - VOICE_FONT_STATUS_FREE = 1, - VOICE_FONT_STATUS_NOT_FREE = 2, - VOICE_FONT_STATUS_UNKNOWN - } EVoiceFontStatus; - - struct voiceFontEntry - { - voiceFontEntry(LLUUID& id); - ~voiceFontEntry(); - - LLUUID mID; - S32 mFontIndex; - std::string mName; - LLDate mExpirationDate; - S32 mFontType; - S32 mFontStatus; - bool mIsNew; - - LLFrameTimer mExpiryTimer; - LLFrameTimer mExpiryWarningTimer; - }; - - bool mVoiceFontsReceived; - bool mVoiceFontsNew; - bool mVoiceFontListDirty; - voice_effect_list_t mVoiceFontList; - voice_effect_list_t mVoiceFontTemplateList; - - typedef std::map voice_font_map_t; - voice_font_map_t mVoiceFontMap; - voice_font_map_t mVoiceFontTemplateMap; - - typedef std::set voice_font_observer_set_t; - voice_font_observer_set_t mVoiceFontObservers; - - LLFrameTimer mVoiceFontExpiryTimer; - - - // Audio capture buffer - - void captureBufferRecordStartSendMessage(); - void captureBufferRecordStopSendMessage(); - void captureBufferPlayStartSendMessage(const LLUUID& voice_font_id = LLUUID::null); - void captureBufferPlayStopSendMessage(); - - bool mCaptureBufferMode; // Disconnected from voice channels while using the capture buffer. - bool mCaptureBufferRecording; // A voice sample is being captured. - bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. - bool mCaptureBufferPlaying; // A voice sample is being played. - - LLTimer mCaptureTimer; - LLUUID mPreviewVoiceFont; - LLUUID mPreviewVoiceFontLast; S32 mPlayRequestCount; bool mIsInTuningMode; bool mIsInChannel; -- 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 ++++++++++++++------------------------- indra/newview/llvoicewebrtc.h | 398 +++-- 2 files changed, 1384 insertions(+), 2266 deletions(-) (limited to 'indra/newview') 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 diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ee7edb3030..456681ed25 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -39,6 +39,7 @@ class LLWebRTCProtocolParser; #include "llcallingcard.h" // for LLFriendObserver #include "lleventcoro.h" #include "llcoros.h" +#include "llparcel.h" #include #include "json/reader.h" @@ -53,14 +54,13 @@ class LLWebRTCProtocolParser; #include class LLAvatarName; +class LLVoiceWebRTCConnection; +typedef boost::shared_ptr connectionPtr_t; class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, - public llwebrtc::LLWebRTCDevicesObserver, - public llwebrtc::LLWebRTCSignalingObserver, - public llwebrtc::LLWebRTCAudioObserver, - public llwebrtc::LLWebRTCDataObserver + public llwebrtc::LLWebRTCDevicesObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); @@ -72,6 +72,8 @@ public: //@{ void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) void terminate() override; // Call this to clean up during shutdown + + static bool isShuttingDown() { return sShuttingDown; } const LLVoiceVersionInfo& getVersion() override; @@ -145,8 +147,12 @@ public: void setNonSpatialChannel(const std::string &uri, const std::string &credentials) override; - bool setSpatialChannel(const std::string &uri, - const std::string &credentials) override; + bool setSpatialChannel(const std::string &uri, const std::string &credentials) override + { + return setSpatialChannel(uri, credentials, INVALID_PARCEL_ID); + } + + bool setSpatialChannel(const std::string &uri, const std::string &credentials, S32 localParcelID); void leaveNonSpatialChannel() override; @@ -163,9 +169,9 @@ public: //@{ // start a voice channel with the specified user void callUser(const LLUUID &uuid) override; - bool isValidChannel(std::string &channelHandle) override; - bool answerInvite(std::string &channelHandle) override; - void declineInvite(std::string &channelHandle) override; + bool isValidChannel(std::string &channelID) override; + bool answerInvite(std::string &channelID) override; + void declineInvite(std::string &channelID) override; //@} ///////////////////////// @@ -232,10 +238,14 @@ public: bool isPreviewPlaying() override { return false; } //@} - // authorize the user - void userAuthorized(const std::string& user_id, - const LLUUID &agentID) override; + void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; + + + void OnConnectionEstablished(const std::string& channelID); + void OnConnectionFailure(const std::string &channelID); + void sendPositionAndVolumeUpdate(bool force); + void updateOwnVolume(); ////////////////////////////// /// @name Status notification @@ -247,10 +257,6 @@ public: void addObserver(LLVoiceClientParticipantObserver* observer) override; void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - - //@} - - ////////////////////////////// /// @name Devices change notification @@ -260,64 +266,17 @@ public: const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override; //@} - ////////////////////////////// - /// @name Signaling notification - // LLWebRTCSignalingObserver - //@{ - void OnIceGatheringState(IceGatheringState state) override; - void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; - void OnOfferAvailable(const std::string &sdp) override; - void OnRenegotiationNeeded() override; - void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; - //@} - - ////////////////////////////// - /// @name Signaling notification - // LLWebRTCAudioObserver - //@{ - //@} - - ///////////////////////// - /// @name Data Notification - /// LLWebRTCDataObserver - //@{ - void OnDataReceived(const std::string& data, bool binary) override; - void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; - //@} - - void processIceUpdates(); - void onIceUpdateComplete(bool ice_completed, const LLSD& result); - void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD& result); - - - //@} - -protected: - ////////////////////// - // WebRTC Specific definitions - - - enum streamState - { - streamStateUnknown = 0, - streamStateIdle = 1, - streamStateConnected = 2, - streamStateRinging = 3, - streamStateConnecting = 6, // same as WebRTC session_media_connecting enum - streamStateDisconnecting = 7, //Same as WebRTC session_media_disconnecting enum - }; struct participantState { public: participantState(const LLUUID& agent_id); - bool updateMuteState(); // true if mute state has changed + bool updateMuteState(); // true if mute state has changed bool isAvatar(); std::string mURI; LLUUID mAvatarID; - std::string mAccountName; std::string mDisplayName; LLFrameTimer mSpeakingTimeout; F32 mLastSpokeTimestamp; @@ -337,6 +296,12 @@ protected: typedef boost::shared_ptr participantStatePtr_t; typedef boost::weak_ptr participantStateWptr_t; + participantStatePtr_t findParticipantByID(const std::string &channelID, const LLUUID &id); + participantStatePtr_t addParticipantByID(const std::string& channelID, const LLUUID &id); + void removeParticipantByID(const std::string& channelID, const LLUUID &id); + + protected: + typedef std::map participantMap; typedef std::map participantUUIDMap; @@ -348,7 +313,7 @@ protected: typedef boost::function sessionFunc_t; - static ptr_t createSession(); + static ptr_t createSession(const std::string& channelID, S32 parcel_local_id); ~sessionState(); participantStatePtr_t addParticipant(const LLUUID& agent_id); @@ -358,28 +323,37 @@ protected: participantStatePtr_t findParticipant(const std::string &uri); participantStatePtr_t findParticipantByID(const LLUUID& id); - static ptr_t matchSessionByHandle(const std::string &handle); - static ptr_t matchSessionByURI(const std::string &uri); - static ptr_t matchSessionByParticipant(const LLUUID &participant_id); + static ptr_t matchSessionByChannelID(const std::string& channel_id); + + void shutdownAllConnections(); bool isCallBackPossible(); bool isTextIMPossible(); + + void processSessionStates(); + + void OnConnectionEstablished(const std::string &channelID); + void OnConnectionFailure(const std::string &channelID); + + void sendData(const std::string &data); static void for_each(sessionFunc_t func); + static void reapEmptySessions(); + + bool isEmpty() { return mWebRTCConnections.empty(); } + std::string mHandle; std::string mGroupHandle; - std::string mSIPURI; + std::string mChannelID; std::string mAlias; std::string mName; - std::string mAlternateSIPURI; std::string mErrorStatusString; std::queue mTextMsgQueue; LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; - int mMediaStreamState; bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) bool mIsSpatial; // True for spatial channels bool mIsP2P; @@ -399,18 +373,20 @@ protected: LLUUID mVoiceFontID; static void VerifySessions(); + static void deleteAllSessions(); private: + + std::map mWebRTCConnections; + std::string mPrimaryConnectionID; + sessionState(); - static std::set mSession; // canonical list of outstanding sessions. - std::set::iterator mMyIterator; // used for delete + static std::map mSessions; // canonical list of outstanding sessions. - static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); + static void for_eachPredicate(const std::pair &a, sessionFunc_t func); - static bool testByHandle(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string handle); static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); - static bool testBySIPOrAlterateURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); }; @@ -422,28 +398,20 @@ protected: // Private Member Functions ////////////////////////////////////////////////////// - - + static void predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t &session); + static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); + static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); + static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); + static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); + static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); ////////////////////////////// /// @name TVC/Server management and communication //@{ - // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. - void daemonDied(); // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. void giveUp(); - - void connectorCreate(); - void connectorShutdown(); - void closeSocket(void); - // void requestVoiceAccountProvision(S32 retries = 3); - void setLoginInfo( - const std::string& account_name, - const std::string& password, - const std::string& channel_sdp); - void logout(); //@} @@ -486,30 +454,12 @@ protected: ///////////////////////////// BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. // Use this to determine whether to show a "no speech" icon in the menu bar. - - participantStatePtr_t findParticipantByID(const LLUUID& id); - participantStatePtr_t addParticipantByID(const LLUUID &id); - void removeParticipantByID(const LLUUID &id); -#if 0 - //////////////////////////////////////// - // voice sessions. - typedef std::set sessionSet; - - typedef sessionSet::iterator sessionIterator; - sessionIterator sessionsBegin(void); - sessionIterator sessionsEnd(void); -#endif - void sessionEstablished(); - sessionStatePtr_t findSession(const std::string &handle); - sessionStatePtr_t findSession(const LLUUID &participant_id); - - sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); - void clearSessionHandle(const sessionStatePtr_t &session); - void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle); - void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); + void sessionEstablished(const LLUUID& region_id); + sessionStatePtr_t findP2PSession(const LLUUID &agent_id); + + sessionStatePtr_t addSession(const std::string& channel_id, S32 parcel_local_id); void deleteSession(const sessionStatePtr_t &session); - void deleteAllSessions(void); void verifySessionState(void); @@ -518,11 +468,7 @@ protected: // This is called in several places where the session _may_ need to be deleted. // It contains logic for whether to delete the session or keep it around. - void reapSession(const sessionStatePtr_t &session); - - // Returns true if the session seems to indicate we've moved to a region on a different voice server - bool sessionNeedsRelog(const sessionStatePtr_t &session); - + void reapSession(const sessionStatePtr_t &session); ////////////////////////////////////// // buddy list stuff, needed for SLIM later @@ -583,41 +529,9 @@ private: // Coroutine support methods //--- - void voiceControlCoro(); - void voiceControlStateMachine(); - - int mVoiceControlState; - LLMutex mVoiceStateMutex; - void setVoiceControlStateUnless(int new_voice_control_state, int unless=-1) - { - LLMutexLock lock(&mVoiceStateMutex); - if (mVoiceControlState != unless) - { - mVoiceControlState = new_voice_control_state; - } - } - int getVoiceControlState() - { - LLMutexLock lock(&mVoiceStateMutex); - return mVoiceControlState; - } - - bool callbackEndDaemon(const LLSD& data); - bool provisionVoiceAccount(); - void OnVoiceAccountProvisioned(const LLSD& body); - void OnVoiceAccountProvisionFailure(std::string url, int retries, LLSD body, const LLSD& result); - bool establishVoiceConnection(); - bool breakVoiceConnection(bool wait); - bool loginToWebRTC(); - void logoutOfWebRTC(bool wait); - - bool requestParcelVoiceInfo(); + void voiceConnectionCoro(); - bool addAndJoinSession(const sessionStatePtr_t &nextSession); - bool terminateAudioSession(bool wait); - - bool waitForChannel(); - bool runSession(const sessionStatePtr_t &session); + void voiceConnectionStateMachine(); bool performMicTuning(); //--- @@ -632,72 +546,38 @@ private: int mSpatialJoiningNum; static void idle(void *user_data); - - LLHost mDaemonHost; - LLSocket::ptr_t mSocket; - - // We should kill the voice daemon in case of connection alert - bool mTerminateDaemon; - - std::string mAccountName; - std::string mAccountPassword; - std::string mChannelSDP; - std::string mRemoteChannelSDP; - std::string mAccountDisplayName; - bool mTuningMode; float mTuningEnergy; - std::string mTuningAudioFile; int mTuningMicVolume; bool mTuningMicVolumeDirty; int mTuningSpeakerVolume; bool mTuningSpeakerVolumeDirty; bool mDevicesListUpdated; // set to true when the device list has been updated // and false when the panelvoicedevicesettings has queried for an update status. - - std::string mSpatialSessionURI; std::string mSpatialSessionCredentials; std::string mMainSessionGroupHandle; // handle of the "main" session group. std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; - sessionStatePtr_t mAudioSession; // Session state for the current audio session + sessionStatePtr_t mAudioSession; // Session state for the current audio session bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings std::string mCurrentRegionName; // Used to detect parcel boundary crossings - - bool mConnectorEstablished; // set by "Create Connector" response - bool mAccountLoggedIn; // set by login message - int mNumberOfAliases; - U32 mCommandCookie; - - int mLoginRetryCount; - - sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. -#if 0 - sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. -#endif - + bool mBuddyListMapPopulated; bool mBlockRulesListReceived; bool mAutoAcceptRulesListReceived; buddyListMap mBuddyListMap; llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; - llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; - llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; - llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; - std::vector mIceCandidates; - bool mIceCompleted; - bool mTrickling; uint32_t mAudioLevel; @@ -705,7 +585,12 @@ private: bool mShutdownComplete; bool checkParcelChanged(bool update = false); - bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + bool switchChannel(const std::string channelID, + bool spatial = true, + bool no_reconnect = false, + bool is_p2p = false, + std::string hash = "", + S32 parcel_local_id = INVALID_PARCEL_ID); void joinSession(const sessionStatePtr_t &session); std::string nameFromAvatar(LLVOAvatar *avatar); @@ -716,11 +601,8 @@ private: bool inSpatialChannel(void); std::string getAudioSessionURI(); - std::string getAudioSessionHandle(); void setHidden(bool hidden) override; //virtual - Json::Value getPositionAndVolumeUpdateJson(bool force); - void sendPositionAndVolumeUpdate(); void enforceTether(void); @@ -759,9 +641,6 @@ private: bool mMicVolumeDirty; bool mVoiceEnabled; - bool mWriteInProgress; - std::string mWriteString; - size_t mWriteOffset; BOOL mLipSyncEnabled; @@ -781,15 +660,13 @@ private: S32 mPlayRequestCount; bool mIsInTuningMode; - bool mIsInChannel; bool mIsJoiningSession; bool mIsWaitingForFonts; bool mIsLoggingIn; - bool mIsLoggedIn; bool mIsProcessingChannels; bool mIsCoroutineActive; - // This variables can last longer than WebRTC in coroutines so we need them as static + // These variables can last longer than WebRTC in coroutines so we need them as static static bool sShuttingDown; static bool sConnected; static LLPumpIO* sPump; @@ -797,19 +674,6 @@ private: LLEventMailDrop mWebRTCPump; }; -class LLWebRTCSecurity : public LLSingleton -{ - LLSINGLETON(LLWebRTCSecurity); - virtual ~LLWebRTCSecurity(); - - public: - std::string connectorHandle() { return mConnectorHandle; }; - std::string accountHandle() { return mAccountHandle; }; - - private: - std::string mConnectorHandle; - std::string mAccountHandle; -}; class LLVoiceWebRTCStats : public LLSingleton { @@ -843,5 +707,119 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; +class LLVoiceWebRTCConnection : + public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCDataObserver +{ + public: + LLVoiceWebRTCConnection(const LLUUID& regionID, S32 parcelLocalID, const std::string& channelID); + + virtual ~LLVoiceWebRTCConnection(); + + ////////////////////////////// + /// @name Signaling notification + // LLWebRTCSignalingObserver + //@{ + void OnIceGatheringState(IceGatheringState state) override; + void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; + void OnOfferAvailable(const std::string &sdp) override; + void OnRenegotiationNeeded() override; + void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + //@} + + ///////////////////////// + /// @name Data Notification + /// LLWebRTCDataObserver + //@{ + void OnDataReceived(const std::string &data, bool binary) override; + void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; + //@} + + void processIceUpdates(); + void onIceUpdateComplete(bool ice_completed, const LLSD &result); + void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); + + bool requestVoiceConnection(); + void OnVoiceConnectionRequestSuccess(const LLSD &body); + void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + + bool connectionStateMachine(); + + void sendData(const std::string &data); + + void shutDown() + { + LLMutexLock lock(&mVoiceStateMutex); + mShutDown = true; + } + +protected: + typedef enum e_voice_connection_state + { + VOICE_STATE_ERROR = 0x0, + VOICE_STATE_START_SESSION = 0x1, + VOICE_STATE_WAIT_FOR_SESSION_START = 0x2, + VOICE_STATE_REQUEST_CONNECTION = 0x4, + VOICE_STATE_CONNECTION_WAIT = 0x8, + VOICE_STATE_SESSION_ESTABLISHED = 0x10, + VOICE_STATE_SESSION_UP = 0x20, + VOICE_STATE_SESSION_RETRY = 0x40, + VOICE_STATE_DISCONNECT = 0x80, + VOICE_STATE_WAIT_FOR_EXIT = 0x100, + VOICE_STATE_SESSION_EXIT = 0x200, + VOICE_STATE_SESSION_STOPPING = 0x3c0, + VOICE_STATE_SESSION_WAITING = 0x10a + } EVoiceConnectionState; + + EVoiceConnectionState mVoiceConnectionState; + LLMutex mVoiceStateMutex; + void setVoiceConnectionState(EVoiceConnectionState new_voice_connection_state) + { + LLMutexLock lock(&mVoiceStateMutex); + + if (new_voice_connection_state & VOICE_STATE_SESSION_STOPPING) + { + // the new state is shutdown or restart. + mVoiceConnectionState = new_voice_connection_state; + return; + } + if (mVoiceConnectionState & VOICE_STATE_SESSION_STOPPING) + { + // we're currently shutting down or restarting, so ignore any + // state changes. + return; + } + + mVoiceConnectionState = new_voice_connection_state; + } + EVoiceConnectionState getVoiceConnectionState() + { + LLMutexLock lock(&mVoiceStateMutex); + return mVoiceConnectionState; + } + + bool breakVoiceConnection(bool wait); + void OnVoiceDisconnectionRequestSuccess(const LLSD &body); + void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + + std::string mChannelSDP; + std::string mRemoteChannelSDP; + + std::string mChannelID; + LLUUID mRegionID; + S32 mParcelLocalID; + + bool mShutDown; + + std::vector mIceCandidates; + bool mIceCompleted; + bool mTrickling; + + llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; + llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; +}; + + #endif //LL_WebRTC_VOICE_CLIENT_H -- 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') 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 ++++++++++++++++++++++++++++++++++++----- indra/newview/llvoicewebrtc.h | 18 ++++++++ 2 files changed, 99 insertions(+), 11 deletions(-) (limited to 'indra/newview') 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); + } +} diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 456681ed25..f0549495e1 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -336,6 +336,10 @@ public: void OnConnectionFailure(const std::string &channelID); void sendData(const std::string &data); + + void setMuteMic(bool muted); + void setMicGain(F32 volume); + void setSpeakerVolume(F32 volume); static void for_each(sessionFunc_t func); @@ -350,6 +354,10 @@ public: std::string mName; std::string mErrorStatusString; std::queue mTextMsgQueue; + + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; LLUUID mIMSessionID; LLUUID mCallerID; @@ -404,6 +412,9 @@ public: static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); + static void predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); + static void predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); + ////////////////////////////// /// @name TVC/Server management and communication //@{ @@ -746,6 +757,9 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); void sendData(const std::string &data); + void setMuteMic(bool muted); + void setMicGain(F32 volume); + void setSpeakerVolume(F32 volume); void shutDown() { @@ -811,6 +825,10 @@ protected: bool mShutDown; + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; + std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- 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 ---------------- indra/newview/llvoicewebrtc.h | 1 - 2 files changed, 17 deletions(-) (limited to 'indra/newview') 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; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f0549495e1..2eb74ed2cb 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -645,7 +645,6 @@ private: S32 mEarLocation; bool mSpeakerVolumeDirty; - bool mSpeakerMuteDirty; float mSpeakerVolume; int mMicVolume; -- 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') 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 +++++++++++++++++++++++++++++++---------- indra/newview/llvoicewebrtc.h | 1 + 2 files changed, 42 insertions(+), 13 deletions(-) (limited to 'indra/newview') 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) diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 2eb74ed2cb..af6e67c8b4 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -823,6 +823,7 @@ protected: S32 mParcelLocalID; bool mShutDown; + S32 mOutstandingRequests; bool mMuted; F32 mMicGain; -- 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 ++++++++++++++++++---------------------- indra/newview/llvoicewebrtc.h | 9 +-- 2 files changed, 64 insertions(+), 79 deletions(-) (limited to 'indra/newview') 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; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index af6e67c8b4..46faf52da5 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -280,7 +280,7 @@ public: std::string mDisplayName; LLFrameTimer mSpeakingTimeout; F32 mLastSpokeTimestamp; - F32 mPower; + F32 mLevel; F32 mVolume; std::string mGroupID; int mUserVolume; @@ -559,9 +559,7 @@ private: static void idle(void *user_data); bool mTuningMode; - float mTuningEnergy; - int mTuningMicVolume; - bool mTuningMicVolumeDirty; + F32 mTuningMicGain; int mTuningSpeakerVolume; bool mTuningSpeakerVolumeDirty; bool mDevicesListUpdated; // set to true when the device list has been updated @@ -647,8 +645,7 @@ private: bool mSpeakerVolumeDirty; float mSpeakerVolume; - int mMicVolume; - bool mMicVolumeDirty; + F32 mMicGain; bool mVoiceEnabled; -- 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') 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 +++++++++++++++++++++++++++------------- indra/newview/llvoicewebrtc.h | 8 +- 2 files changed, 132 insertions(+), 59 deletions(-) (limited to 'indra/newview') 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--; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 46faf52da5..f311f241dc 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -381,7 +381,7 @@ public: LLUUID mVoiceFontID; static void VerifySessions(); - static void deleteAllSessions(); + static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } private: @@ -414,6 +414,8 @@ public: static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); static void predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); static void predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); + static void predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t &session); + void reapEmptySessions(); ////////////////////////////// /// @name TVC/Server management and communication @@ -535,6 +537,8 @@ public: void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); private: + + float getAudioLevel(); LLVoiceVersionInfo mVoiceVersion; @@ -544,7 +548,6 @@ private: void voiceConnectionStateMachine(); - bool performMicTuning(); //--- /// Clean up objects created during a voice session. void cleanUp(); @@ -732,6 +735,7 @@ class LLVoiceWebRTCConnection : void OnOfferAvailable(const std::string &sdp) override; void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + void OnPeerShutDown() override; //@} ///////////////////////// -- 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 ++++++++++++---------------------------- indra/newview/llvoicewebrtc.h | 43 ++-- 2 files changed, 159 insertions(+), 321 deletions(-) (limited to 'indra/newview') 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; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f311f241dc..1e8ecdf5c2 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -330,7 +330,9 @@ public: bool isCallBackPossible(); bool isTextIMPossible(); - void processSessionStates(); + static void processSessionStates(); + + bool processConnectionStates(); void OnConnectionEstablished(const std::string &channelID); void OnConnectionFailure(const std::string &channelID); @@ -347,6 +349,15 @@ public: bool isEmpty() { return mWebRTCConnections.empty(); } + bool isSpatial() { return mSessionType == SESSION_TYPE_ESTATE || mSessionType == SESSION_TYPE_PARCEL; } + + typedef enum e_session_type + { + SESSION_TYPE_ESTATE = 1, + SESSION_TYPE_PARCEL, + SESSION_TYPE_P2P + } ESessionType; + std::string mHandle; std::string mGroupHandle; std::string mChannelID; @@ -362,9 +373,8 @@ public: LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; - bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) - bool mIsSpatial; // True for spatial channels - bool mIsP2P; + ESessionType mSessionType; + bool mIncoming; bool mVoiceActive; bool mReconnect; // Whether we should try to reconnect to this session if it's dropped @@ -381,16 +391,17 @@ public: LLUUID mVoiceFontID; static void VerifySessions(); - static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } + static bool hasSession(const std::string &sessionID) + { return mSessions.find(sessionID) != mSessions.end(); } private: - std::map mWebRTCConnections; - std::string mPrimaryConnectionID; + std::list mWebRTCConnections; + std::string mPrimaryConnectionID; + static std::map mSessions; // canonical list of outstanding sessions. sessionState(); - static std::map mSessions; // canonical list of outstanding sessions. static void for_eachPredicate(const std::pair &a, sessionFunc_t func); @@ -406,7 +417,6 @@ public: // Private Member Functions ////////////////////////////////////////////////////// - static void predProcessSessionStates(const LLWebRTCVoiceClient::sessionStatePtr_t &session); static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); @@ -415,7 +425,6 @@ public: static void predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); static void predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); static void predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t &session); - void reapEmptySessions(); ////////////////////////////// /// @name TVC/Server management and communication @@ -476,9 +485,6 @@ public: void verifySessionState(void); - void joinedAudioSession(const sessionStatePtr_t &session); - void leftAudioSession(const sessionStatePtr_t &session); - // This is called in several places where the session _may_ need to be deleted. // It contains logic for whether to delete the session or keep it around. void reapSession(const sessionStatePtr_t &session); @@ -596,12 +602,8 @@ private: bool mIsInitialized; bool mShutdownComplete; - bool checkParcelChanged(bool update = false); bool switchChannel(const std::string channelID, - bool spatial = true, - bool no_reconnect = false, - bool is_p2p = false, - std::string hash = "", + sessionState::ESessionType sessionType, S32 parcel_local_id = INVALID_PARCEL_ID); void joinSession(const sessionStatePtr_t &session); @@ -808,6 +810,10 @@ protected: } EVoiceConnectionState getVoiceConnectionState() { + if (mVoiceStateMutex.isLocked()) + { + LL_WARNS("Voice") << "LOCKED." << LL_ENDL; + } LLMutexLock lock(&mVoiceStateMutex); return mVoiceConnectionState; } @@ -839,6 +845,7 @@ protected: llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; }; +#define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__); #endif //LL_WebRTC_VOICE_CLIENT_H -- 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 +++++++++++++++++++++++----------------- indra/newview/llvoicewebrtc.h | 47 ++++--- 2 files changed, 199 insertions(+), 148 deletions(-) (limited to 'indra/newview') 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); + } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 1e8ecdf5c2..9b8372cba7 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -242,8 +242,8 @@ public: void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; - void OnConnectionEstablished(const std::string& channelID); - void OnConnectionFailure(const std::string &channelID); + void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); + void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); void sendPositionAndVolumeUpdate(bool force); void updateOwnVolume(); @@ -312,6 +312,12 @@ public: typedef boost::weak_ptr wptr_t; typedef boost::function sessionFunc_t; + typedef enum e_session_type + { + SESSION_TYPE_ESTATE = 1, + SESSION_TYPE_PARCEL, + SESSION_TYPE_P2P + } ESessionType; static ptr_t createSession(const std::string& channelID, S32 parcel_local_id); ~sessionState(); @@ -334,9 +340,6 @@ public: bool processConnectionStates(); - void OnConnectionEstablished(const std::string &channelID); - void OnConnectionFailure(const std::string &channelID); - void sendData(const std::string &data); void setMuteMic(bool muted); @@ -351,12 +354,7 @@ public: bool isSpatial() { return mSessionType == SESSION_TYPE_ESTATE || mSessionType == SESSION_TYPE_PARCEL; } - typedef enum e_session_type - { - SESSION_TYPE_ESTATE = 1, - SESSION_TYPE_PARCEL, - SESSION_TYPE_P2P - } ESessionType; + ESessionType getSessionType() { return mSessionType; } std::string mHandle; std::string mGroupHandle; @@ -378,6 +376,7 @@ public: bool mIncoming; bool mVoiceActive; bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + bool mShuttingDown; // Set to true when the volume/mute state of someone in the participant list changes. // The code will have to walk the list to find the changed participant(s). @@ -417,8 +416,6 @@ public: // Private Member Functions ////////////////////////////////////////////////////// - static void predOnConnectionEstablished(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); - static void predOnConnectionFailure(const LLWebRTCVoiceClient::sessionStatePtr_t &session, std::string channelID); static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); @@ -459,10 +456,13 @@ public: ///////////////////////////// // Sending updates of current state void updatePosition(void); - void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); + void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); bool channelFromRegion(LLViewerRegion *region, std::string &name); + LLVector3d getListenerPosition() { return mListenerPosition; } + LLVector3d getSpeakerPosition() { return mAvatarPosition; } + void setEarLocation(S32 loc); @@ -613,23 +613,28 @@ private: std::string displayNameFromAvatar(LLVOAvatar *avatar); - bool inSpatialChannel(void); + bool inSpatialChannel(); std::string getAudioSessionURI(); void setHidden(bool hidden) override; //virtual - void enforceTether(void); + void enforceTether(); + + void updateNeighboringRegions(); + std::set getNeighboringRegions() { return mNeighboringRegions; } bool mSpatialCoordsDirty; - LLVector3d mCameraPosition; - LLVector3d mCameraRequestedPosition; - LLVector3 mCameraVelocity; - LLQuaternion mCameraRot; + LLVector3d mListenerPosition; + LLVector3d mListenerRequestedPosition; + LLVector3 mListenerVelocity; + LLQuaternion mListenerRot; LLVector3d mAvatarPosition; LLVector3 mAvatarVelocity; LLQuaternion mAvatarRot; + + std::set mNeighboringRegions; // includes current region bool mMuteMic; bool mMuteMicDirty; @@ -763,6 +768,8 @@ class LLVoiceWebRTCConnection : void setMicGain(F32 volume); void setSpeakerVolume(F32 volume); + LLUUID getRegionID() { return mRegionID; } + void shutDown() { LLMutexLock lock(&mVoiceStateMutex); -- 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') 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 +++++++++++++++++++---------- indra/newview/llvoicewebrtc.h | 2 ++ 2 files changed, 21 insertions(+), 10 deletions(-) (limited to 'indra/newview') 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); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 9b8372cba7..f89501e15c 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -843,6 +843,8 @@ protected: F32 mMicGain; F32 mSpeakerVolume; + bool mPrimary; + std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- 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 +++++++++++++++------------------------- indra/newview/llvoicewebrtc.h | 9 +--- 2 files changed, 39 insertions(+), 72 deletions(-) (limited to 'indra/newview') 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; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f89501e15c..f042ecca85 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -244,7 +244,7 @@ public: void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); - void sendPositionAndVolumeUpdate(bool force); + void sendPositionUpdate(bool force); void updateOwnVolume(); ////////////////////////////// @@ -396,7 +396,6 @@ public: private: std::list mWebRTCConnections; - std::string mPrimaryConnectionID; static std::map mSessions; // canonical list of outstanding sessions. sessionState(); @@ -416,7 +415,7 @@ public: // Private Member Functions ////////////////////////////////////////////////////// - static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data, const std::string& volume_data); + static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data); static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute); static void predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); @@ -597,8 +596,6 @@ private: LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; - uint32_t mAudioLevel; - bool mIsInitialized; bool mShutdownComplete; @@ -843,8 +840,6 @@ protected: F32 mMicGain; F32 mSpeakerVolume; - bool mPrimary; - std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- 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 +++++++++++++-------- indra/newview/llvoicewebrtc.h | 2 ++ 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'indra/newview') 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, diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f042ecca85..c8ba3ee115 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -829,6 +829,8 @@ protected: std::string mChannelSDP; std::string mRemoteChannelSDP; + LLUUID mViewerSession; + std::string mChannelID; LLUUID mRegionID; S32 mParcelLocalID; -- 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') 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') 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 9a8c4c509c7b5655d02e9af64dd4a8363b48d68f Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 3 Jan 2024 20:33:01 -0800 Subject: some comments --- indra/newview/llvoicewebrtc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index c8ba3ee115..bf5c68ed2b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -364,9 +364,9 @@ public: std::string mErrorStatusString; std::queue mTextMsgQueue; - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; + bool mMuted; // this session is muted. + F32 mMicGain; // gain for this session. + F32 mSpeakerVolume; // volume for this session. LLUUID mIMSessionID; LLUUID mCallerID; -- 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 ++++++++++++++++++++-------------------- indra/newview/llvoicewebrtc.h | 146 ++++++--- 2 files changed, 422 insertions(+), 361 deletions(-) (limited to 'indra/newview') 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); - } -} diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index bf5c68ed2b..ce4ab9fed6 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -144,19 +144,23 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string &uri, - const std::string &credentials) override; + void setNonSpatialChannel(const std::string& uri, + const std::string& credentials) override + { + + } bool setSpatialChannel(const std::string &uri, const std::string &credentials) override { - return setSpatialChannel(uri, credentials, INVALID_PARCEL_ID); + // this is a vivox-related call + return false; } - - bool setSpatialChannel(const std::string &uri, const std::string &credentials, S32 localParcelID); void leaveNonSpatialChannel() override; - void leaveChannel(void) override; + void leaveChannel(void) override { leaveChannel(true); } + + void leaveChannel(bool stopTalking); // Returns the URI of the current channel, or an empty string if not currently in a channel. // NOTE that it will return an empty string if it's in the process of joining a channel. @@ -305,22 +309,16 @@ public: typedef std::map participantMap; typedef std::map participantUUIDMap; - struct sessionState + class sessionState { public: typedef boost::shared_ptr ptr_t; typedef boost::weak_ptr wptr_t; typedef boost::function sessionFunc_t; - typedef enum e_session_type - { - SESSION_TYPE_ESTATE = 1, - SESSION_TYPE_PARCEL, - SESSION_TYPE_P2P - } ESessionType; - - static ptr_t createSession(const std::string& channelID, S32 parcel_local_id); - ~sessionState(); + + static void addSession(const std::string &channelID, ptr_t& session); + virtual ~sessionState(); participantStatePtr_t addParticipant(const LLUUID& agent_id); void removeParticipant(const participantStatePtr_t &participant); @@ -338,7 +336,7 @@ public: static void processSessionStates(); - bool processConnectionStates(); + virtual bool processConnectionStates(); void sendData(const std::string &data); @@ -352,9 +350,8 @@ public: bool isEmpty() { return mWebRTCConnections.empty(); } - bool isSpatial() { return mSessionType == SESSION_TYPE_ESTATE || mSessionType == SESSION_TYPE_PARCEL; } - - ESessionType getSessionType() { return mSessionType; } + virtual bool isSpatial() = 0; + virtual bool isEstate() = 0; std::string mHandle; std::string mGroupHandle; @@ -371,7 +368,6 @@ public: LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; - ESessionType mSessionType; bool mIncoming; bool mVoiceActive; @@ -389,27 +385,56 @@ public: LLUUID mVoiceFontID; - static void VerifySessions(); static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } + protected: + sessionState(); + std::list mWebRTCConnections; + private: - std::list mWebRTCConnections; static std::map mSessions; // canonical list of outstanding sessions. - sessionState(); - - static void for_eachPredicate(const std::pair &a, sessionFunc_t func); static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); }; - typedef boost::shared_ptr sessionStatePtr_t; + typedef boost::shared_ptr sessionStatePtr_t; typedef std::map sessionMap; + + class estateSessionState : public sessionState + { + public: + estateSessionState(); + virtual bool processConnectionStates() override; + + virtual bool isSpatial() { return true; } + virtual bool isEstate() { return true; } + }; + + class parcelSessionState : public sessionState + { + public: + parcelSessionState(const std::string& channelID, S32 parcel_local_id); + + virtual bool isSpatial() { return true; } + virtual bool isEstate() { return false; } + }; + + class adhocSessionState : public sessionState + { + public: + adhocSessionState(const std::string &channelID); + + virtual bool isSpatial() { return false; } + virtual bool isEstate() { return false; } + }; + + /////////////////////////////////////////////////////// // Private Member Functions @@ -479,11 +504,9 @@ public: void sessionEstablished(const LLUUID& region_id); sessionStatePtr_t findP2PSession(const LLUUID &agent_id); - sessionStatePtr_t addSession(const std::string& channel_id, S32 parcel_local_id); + sessionStatePtr_t addSession(const std::string &channel_id, sessionStatePtr_t &session); void deleteSession(const sessionStatePtr_t &session); - void verifySessionState(void); - // This is called in several places where the session _may_ need to be deleted. // It contains logic for whether to delete the session or keep it around. void reapSession(const sessionStatePtr_t &session); @@ -578,10 +601,10 @@ private: std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; - sessionStatePtr_t mAudioSession; // Session state for the current audio session - bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. + sessionStatePtr_t mSession; // Session state for the current session + bool mSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. - sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join + sessionStatePtr_t mNextSession; // Session state for the session we're trying to join S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings std::string mCurrentRegionName; // Used to detect parcel boundary crossings @@ -598,10 +621,12 @@ private: bool mIsInitialized; bool mShutdownComplete; - - bool switchChannel(const std::string channelID, - sessionState::ESessionType sessionType, - S32 parcel_local_id = INVALID_PARCEL_ID); + + bool startEstateSession(); + bool startParcelSession(const std::string& channelID, S32 parcelID); + bool startAdHocSession(const std::string& channelID); + + void joinSession(const sessionStatePtr_t &session); std::string nameFromAvatar(LLVOAvatar *avatar); @@ -611,6 +636,9 @@ private: bool inSpatialChannel(); + bool inOrJoiningChannel(const std::string &channelID); + bool inEstateChannel(); + std::string getAudioSessionURI(); void setHidden(bool hidden) override; //virtual @@ -721,14 +749,42 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; -class LLVoiceWebRTCConnection : +class LLVoiceWebRTCConnection +{ + public: + LLVoiceWebRTCConnection(); + + virtual ~LLVoiceWebRTCConnection() = 0; + + virtual bool connectionStateMachine() = 0; + + virtual void sendData(const std::string &data) {}; + virtual void setMuteMic(bool muted); + virtual void setMicGain(F32 volume); + virtual void setSpeakerVolume(F32 volume); + + virtual void shutDown() = 0; + +protected: + + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; + + llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; + llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; +}; + + +class LLVoiceWebRTCSpatialConnection : + public LLVoiceWebRTCConnection, public llwebrtc::LLWebRTCSignalingObserver, public llwebrtc::LLWebRTCDataObserver { public: - LLVoiceWebRTCConnection(const LLUUID& regionID, S32 parcelLocalID, const std::string& channelID); + LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); - virtual ~LLVoiceWebRTCConnection(); + virtual ~LLVoiceWebRTCSpatialConnection(); ////////////////////////////// /// @name Signaling notification @@ -760,10 +816,8 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); - void sendData(const std::string &data); - void setMuteMic(bool muted); - void setMicGain(F32 volume); - void setSpeakerVolume(F32 volume); + void sendData(const std::string &data) override; + void setMuteMic(bool muted) override; LLUUID getRegionID() { return mRegionID; } @@ -838,10 +892,6 @@ protected: bool mShutDown; S32 mOutstandingRequests; - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; - std::vector mIceCandidates; bool mIceCompleted; bool mTrickling; -- 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 +- indra/newview/llvoicewebrtc.h | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'indra/newview') 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) diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ce4ab9fed6..b2672ac108 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -410,10 +410,10 @@ public: { public: estateSessionState(); - virtual bool processConnectionStates() override; + bool processConnectionStates() override; - virtual bool isSpatial() { return true; } - virtual bool isEstate() { return true; } + bool isSpatial() override { return true; } + bool isEstate() override { return true; } }; class parcelSessionState : public sessionState @@ -421,8 +421,8 @@ public: public: parcelSessionState(const std::string& channelID, S32 parcel_local_id); - virtual bool isSpatial() { return true; } - virtual bool isEstate() { return false; } + bool isSpatial() override { return true; } + bool isEstate() override { return false; } }; class adhocSessionState : public sessionState @@ -430,8 +430,8 @@ public: public: adhocSessionState(const std::string &channelID); - virtual bool isSpatial() { return false; } - virtual bool isEstate() { return false; } + bool isSpatial() override { return false; } + bool isEstate() override { return false; } }; @@ -504,7 +504,7 @@ public: void sessionEstablished(const LLUUID& region_id); sessionStatePtr_t findP2PSession(const LLUUID &agent_id); - sessionStatePtr_t addSession(const std::string &channel_id, sessionStatePtr_t &session); + sessionStatePtr_t addSession(const std::string &channel_id, sessionState::ptr_t session); void deleteSession(const sessionStatePtr_t &session); // This is called in several places where the session _may_ need to be deleted. @@ -814,14 +814,14 @@ class LLVoiceWebRTCSpatialConnection : void OnVoiceConnectionRequestSuccess(const LLSD &body); void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); - bool connectionStateMachine(); + bool connectionStateMachine() override; void sendData(const std::string &data) override; void setMuteMic(bool muted) override; LLUUID getRegionID() { return mRegionID; } - void shutDown() + void shutDown() override { LLMutexLock lock(&mVoiceStateMutex); mShutDown = true; -- 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/llimview.cpp | 8 +- indra/newview/llvoicechannel.cpp | 1 + indra/newview/llvoiceclient.cpp | 7 + indra/newview/llvoiceclient.h | 2 + indra/newview/llvoicewebrtc.cpp | 693 ++++++++++++++++++++++----------------- indra/newview/llvoicewebrtc.h | 229 +++++++------ 6 files changed, 513 insertions(+), 427 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 61a01d7418..3e03dbef8f 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -665,8 +665,11 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // set P2P type by default mSessionType = P2P_SESSION; - if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) + if ((IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) && LLVoiceClient::getInstance()->hasP2PInterface()) { + // only use LLVoiceChannelP2P if the provider can handle the special P2P interface, + // which uses the voice server to relay calls and invites. Otherwise, + // we use the group voice provider. mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); } else @@ -2034,7 +2037,8 @@ bool LLIMModel::sendStartSession( return true; } - else if ( dialog == IM_SESSION_CONFERENCE_START ) + else if (( dialog == IM_SESSION_CONFERENCE_START ) || + (((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) && !LLVoiceClient::getInstance()->hasP2PInterface())) { LLSD agents; for (int i = 0; i < (S32) ids.size(); i++) diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index b0eb8d962c..afac9ed6f8 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -654,6 +654,7 @@ void LLVoiceChannelGroup::voiceCallCapCoro(std::string url) LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " << iter->first << LL_ENDL; } + LL_INFOS("Voice") << "LLVoiceCallCapResponder::result got " << result << LL_ENDL; channelp->setChannelInfo( result["voice_credentials"]["channel_uri"].asString(), diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 294ae0c9ad..54840a1235 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -486,6 +486,13 @@ std::string LLVoiceClient::getCurrentChannel() //--------------------------------------- // invitations +bool LLVoiceClient::hasP2PInterface() +{ + if (mVoiceModule) + return mVoiceModule->hasP2PInterface(); + return false; +} + void LLVoiceClient::callUser(const LLUUID &uuid) { if (mVoiceModule) mVoiceModule->callUser(uuid); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 1e8ff21b4b..1a20de6109 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -186,6 +186,7 @@ public: /// @name invitations //@{ // start a voice channel with the specified user + virtual bool hasP2PInterface()=0; virtual void callUser(const LLUUID &uuid)=0; virtual bool isValidChannel(std::string& channelHandle)=0; virtual bool answerInvite(std::string &channelHandle)=0; @@ -382,6 +383,7 @@ public: // NOTE that it will return an empty string if it's in the process of joining a channel. std::string getCurrentChannel(); // start a voice channel with the specified user + bool hasP2PInterface(); // true - can use the following. false - use conference/ad-hoc instead void callUser(const LLUUID &uuid); bool isValidChannel(std::string& channelHandle); bool answerInvite(std::string &channelHandle); 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; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index b2672ac108..f81c8c556e 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -144,10 +144,8 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string& uri, - const std::string& credentials) override - { - + void setNonSpatialChannel(const std::string& uri, const std::string& credentials) override { + startAdHocSession(uri, credentials); } bool setSpatialChannel(const std::string &uri, const std::string &credentials) override @@ -172,6 +170,7 @@ public: /// @name invitations //@{ // start a voice channel with the specified user + bool hasP2PInterface() override { return false; } void callUser(const LLUUID &uuid) override; bool isValidChannel(std::string &channelID) override; bool answerInvite(std::string &channelID) override; @@ -247,6 +246,7 @@ public: void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); + void OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID); void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); void sendPositionUpdate(bool force); void updateOwnVolume(); @@ -338,7 +338,7 @@ public: virtual bool processConnectionStates(); - void sendData(const std::string &data); + virtual void sendData(const std::string &data); void setMuteMic(bool muted); void setMicGain(F32 volume); @@ -427,11 +427,17 @@ public: class adhocSessionState : public sessionState { - public: - adhocSessionState(const std::string &channelID); + public: + adhocSessionState(const std::string &channelID, const std::string& credentials); bool isSpatial() override { return false; } bool isEstate() override { return false; } + + // don't send spatial data to adhoc sessions. + void sendData(const std::string &data) override { } + + protected: + std::string mCredentials; }; @@ -511,26 +517,6 @@ public: // It contains logic for whether to delete the session or keep it around. void reapSession(const sessionStatePtr_t &session); - ////////////////////////////////////// - // buddy list stuff, needed for SLIM later - struct buddyListEntry - { - buddyListEntry(const std::string &uri); - std::string mURI; - std::string mDisplayName; - LLUUID mUUID; - bool mOnlineSL; - bool mOnlineSLim; - bool mCanSeeMeOnline; - bool mHasBlockListEntry; - bool mHasAutoAcceptListEntry; - bool mNameResolved; - bool mInSLFriends; - bool mInWebRTCBuddies; - }; - - typedef std::map buddyListMap; - // Pokes the state machine to leave the audio session next time around. void sessionTerminate(); @@ -612,7 +598,6 @@ private: bool mBuddyListMapPopulated; bool mBlockRulesListReceived; bool mAutoAcceptRulesListReceived; - buddyListMap mBuddyListMap; llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; @@ -624,8 +609,7 @@ private: bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); - bool startAdHocSession(const std::string& channelID); - + bool startAdHocSession(const std::string& channelID, const std::string& credentials); void joinSession(const sessionStatePtr_t &session); @@ -749,147 +733,126 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; -class LLVoiceWebRTCConnection -{ - public: - LLVoiceWebRTCConnection(); - - virtual ~LLVoiceWebRTCConnection() = 0; - - virtual bool connectionStateMachine() = 0; - - virtual void sendData(const std::string &data) {}; - virtual void setMuteMic(bool muted); - virtual void setMicGain(F32 volume); - virtual void setSpeakerVolume(F32 volume); - - virtual void shutDown() = 0; - -protected: - - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; - - llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; - llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; -}; - - -class LLVoiceWebRTCSpatialConnection : - public LLVoiceWebRTCConnection, - public llwebrtc::LLWebRTCSignalingObserver, +class LLVoiceWebRTCConnection : + public llwebrtc::LLWebRTCSignalingObserver, public llwebrtc::LLWebRTCDataObserver { public: - LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); + LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID); - virtual ~LLVoiceWebRTCSpatialConnection(); + virtual ~LLVoiceWebRTCConnection() = 0; ////////////////////////////// /// @name Signaling notification // LLWebRTCSignalingObserver //@{ - void OnIceGatheringState(IceGatheringState state) override; - void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; - void OnOfferAvailable(const std::string &sdp) override; - void OnRenegotiationNeeded() override; - void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; - void OnPeerShutDown() override; + virtual void OnIceGatheringState(IceGatheringState state); + virtual void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate); + virtual void OnOfferAvailable(const std::string &sdp); + virtual void OnRenegotiationNeeded() override; + virtual void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface); + virtual void OnPeerShutDown(); //@} ///////////////////////// /// @name Data Notification /// LLWebRTCDataObserver //@{ - void OnDataReceived(const std::string &data, bool binary) override; - void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; + virtual void OnDataReceived(const std::string &data, bool binary); + virtual void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface); //@} - void processIceUpdates(); - void onIceUpdateComplete(bool ice_completed, const LLSD &result); - void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); + void sendData(const std::string &data); - bool requestVoiceConnection(); - void OnVoiceConnectionRequestSuccess(const LLSD &body); - void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + virtual void processIceUpdates(); + virtual void onIceUpdateComplete(bool ice_completed, const LLSD &result); + virtual void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); - bool connectionStateMachine() override; + virtual void setMuteMic(bool muted); + virtual void setMicGain(F32 volume); + virtual void setSpeakerVolume(F32 volume); - void sendData(const std::string &data) override; - void setMuteMic(bool muted) override; + + bool connectionStateMachine(); LLUUID getRegionID() { return mRegionID; } - void shutDown() override - { - LLMutexLock lock(&mVoiceStateMutex); - mShutDown = true; - } + void shutDown() + { + LLMutexLock lock(&mVoiceStateMutex); + mShutDown = true; + } -protected: + void OnVoiceConnectionRequestSuccess(const LLSD &body); + void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + + protected: typedef enum e_voice_connection_state { - VOICE_STATE_ERROR = 0x0, - VOICE_STATE_START_SESSION = 0x1, - VOICE_STATE_WAIT_FOR_SESSION_START = 0x2, - VOICE_STATE_REQUEST_CONNECTION = 0x4, - VOICE_STATE_CONNECTION_WAIT = 0x8, - VOICE_STATE_SESSION_ESTABLISHED = 0x10, - VOICE_STATE_SESSION_UP = 0x20, - VOICE_STATE_SESSION_RETRY = 0x40, - VOICE_STATE_DISCONNECT = 0x80, - VOICE_STATE_WAIT_FOR_EXIT = 0x100, - VOICE_STATE_SESSION_EXIT = 0x200, - VOICE_STATE_SESSION_STOPPING = 0x3c0, - VOICE_STATE_SESSION_WAITING = 0x10a + VOICE_STATE_ERROR = 0x0, + VOICE_STATE_START_SESSION = 0x1, + VOICE_STATE_WAIT_FOR_SESSION_START = 0x2, + VOICE_STATE_REQUEST_CONNECTION = 0x4, + VOICE_STATE_CONNECTION_WAIT = 0x8, + VOICE_STATE_SESSION_ESTABLISHED = 0x10, + VOICE_STATE_SESSION_UP = 0x20, + VOICE_STATE_SESSION_RETRY = 0x40, + VOICE_STATE_DISCONNECT = 0x80, + VOICE_STATE_WAIT_FOR_EXIT = 0x100, + VOICE_STATE_SESSION_EXIT = 0x200, + VOICE_STATE_SESSION_STOPPING = 0x3c0, + VOICE_STATE_SESSION_WAITING = 0x10a } EVoiceConnectionState; EVoiceConnectionState mVoiceConnectionState; - LLMutex mVoiceStateMutex; - void setVoiceConnectionState(EVoiceConnectionState new_voice_connection_state) + LLMutex mVoiceStateMutex; + void setVoiceConnectionState(EVoiceConnectionState new_voice_connection_state) { LLMutexLock lock(&mVoiceStateMutex); - if (new_voice_connection_state & VOICE_STATE_SESSION_STOPPING) - { - // the new state is shutdown or restart. + if (new_voice_connection_state & VOICE_STATE_SESSION_STOPPING) + { + // the new state is shutdown or restart. mVoiceConnectionState = new_voice_connection_state; return; - } + } if (mVoiceConnectionState & VOICE_STATE_SESSION_STOPPING) - { - // we're currently shutting down or restarting, so ignore any - // state changes. + { + // we're currently shutting down or restarting, so ignore any + // state changes. return; - } + } mVoiceConnectionState = new_voice_connection_state; } EVoiceConnectionState getVoiceConnectionState() { - if (mVoiceStateMutex.isLocked()) - { + if (mVoiceStateMutex.isLocked()) + { LL_WARNS("Voice") << "LOCKED." << LL_ENDL; - } + } LLMutexLock lock(&mVoiceStateMutex); return mVoiceConnectionState; } + virtual bool requestVoiceConnection() = 0; + bool breakVoiceConnection(bool wait); void OnVoiceDisconnectionRequestSuccess(const LLSD &body); void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + LLUUID mRegionID; + LLUUID mViewerSession; + std::string mChannelID; + std::string mChannelSDP; std::string mRemoteChannelSDP; - LLUUID mViewerSession; - - std::string mChannelID; - LLUUID mRegionID; - S32 mParcelLocalID; + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; - bool mShutDown; + bool mShutDown; S32 mOutstandingRequests; std::vector mIceCandidates; @@ -901,6 +864,38 @@ protected: llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; }; + +class LLVoiceWebRTCSpatialConnection : + public LLVoiceWebRTCConnection +{ + public: + LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); + + virtual ~LLVoiceWebRTCSpatialConnection(); + + void setMuteMic(bool muted) override; + + +protected: + + bool requestVoiceConnection() override; + + S32 mParcelLocalID; +}; + +class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection +{ + public: + LLVoiceWebRTCAdHocConnection(const LLUUID ®ionID, const std::string &channelID, const std::string& credentials); + + virtual ~LLVoiceWebRTCAdHocConnection(); + + protected: + bool requestVoiceConnection() override; + + std::string mCredentials; +}; + #define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__); #endif //LL_WebRTC_VOICE_CLIENT_H -- 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/llvoicevivox.h | 13 +++++++------ indra/newview/llvoicewebrtc.cpp | 20 ++++++++++---------- indra/newview/llvoicewebrtc.h | 9 ++++++++- 3 files changed, 25 insertions(+), 17 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index e3ab99c675..bd1f18aec6 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -154,17 +154,18 @@ public: /// @name invitations //@{ // start a voice channel with the specified user - virtual void callUser(const LLUUID &uuid); - virtual bool isValidChannel(std::string &channelHandle); - virtual bool answerInvite(std::string &channelHandle); - virtual void declineInvite(std::string &channelHandle); + bool hasP2PInterface() override { return true; } + void callUser(const LLUUID &uuid); + bool isValidChannel(std::string &channelHandle) override; + bool answerInvite(std::string &channelHandle) override; + void declineInvite(std::string &channelHandle) override; //@} ///////////////////////// /// @name Volume/gain //@{ - virtual void setVoiceVolume(F32 volume); - virtual void setMicGain(F32 volume); + void setVoiceVolume(F32 volume) override; + void setMicGain(F32 volume) override; //@} ///////////////////////// 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; } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index f81c8c556e..38d867db8d 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -150,8 +150,9 @@ public: bool setSpatialChannel(const std::string &uri, const std::string &credentials) override { + leaveNonSpatialChannel(); // this is a vivox-related call - return false; + return true; } void leaveNonSpatialChannel() override; @@ -775,6 +776,8 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); + virtual bool isSpatial() = 0; + LLUUID getRegionID() { return mRegionID; } void shutDown() @@ -875,6 +878,8 @@ class LLVoiceWebRTCSpatialConnection : void setMuteMic(bool muted) override; + bool isSpatial() override { return true; } + protected: @@ -890,6 +895,8 @@ class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection virtual ~LLVoiceWebRTCAdHocConnection(); + bool isSpatial() override { return false; } + protected: bool requestVoiceConnection() override; -- cgit v1.2.3 From 4b65e2b266d689028d627d0fbe6f2d97c997e7ed Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 29 Jan 2024 21:26:36 -0800 Subject: mac build fixes --- indra/newview/llvoicewebrtc.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 38d867db8d..6562a737ae 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -747,20 +747,20 @@ class LLVoiceWebRTCConnection : /// @name Signaling notification // LLWebRTCSignalingObserver //@{ - virtual void OnIceGatheringState(IceGatheringState state); - virtual void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate); - virtual void OnOfferAvailable(const std::string &sdp); - virtual void OnRenegotiationNeeded() override; - virtual void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface); - virtual void OnPeerShutDown(); + void OnIceGatheringState(IceGatheringState state) override; + void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; + void OnOfferAvailable(const std::string &sdp) override; + void OnRenegotiationNeeded() override; + void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; + void OnPeerShutDown() override; //@} ///////////////////////// /// @name Data Notification /// LLWebRTCDataObserver //@{ - virtual void OnDataReceived(const std::string &data, bool binary); - virtual void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface); + void OnDataReceived(const std::string &data, bool binary) override; + void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} void sendData(const std::string &data); -- 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/llimview.cpp | 7 +++++- indra/newview/llvoicewebrtc.cpp | 51 ++++++++++++++++++----------------------- indra/newview/llvoicewebrtc.h | 11 +-------- 3 files changed, 29 insertions(+), 40 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 3e03dbef8f..78017337e8 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -681,10 +681,15 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& { mSessionType = GROUP_SESSION; } - else + else if (LLVoiceClient::getInstance()->hasP2PInterface()) { mSessionType = ADHOC_SESSION; } + else + { + // webrtc uses adhoc channels for p2p + mSessionType = P2P_SESSION; + } } if(mVoiceChannel) 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 diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 6562a737ae..5ddfbd9ea4 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -380,7 +380,6 @@ public: bool mVolumeDirty; bool mMuteDirty; - bool mParticipantsChanged; participantMap mParticipantsByURI; participantUUIDMap mParticipantsByUUID; @@ -516,13 +515,7 @@ public: // This is called in several places where the session _may_ need to be deleted. // It contains logic for whether to delete the session or keep it around. - void reapSession(const sessionStatePtr_t &session); - - // Pokes the state machine to leave the audio session next time around. - void sessionTerminate(); - - // Pokes the state machine to shut down the connector and restart it. - void requestRelog(); + void reapSession(const sessionStatePtr_t &session); // Does the actual work to get out of the audio session void leaveAudioSession(); @@ -567,7 +560,6 @@ private: /// Clean up objects created during a voice session. void cleanUp(); - bool mSessionTerminateRequested; bool mRelogRequested; // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). // The larger it is the greater is possibility there is a problem with connection to voice server. @@ -589,7 +581,6 @@ private: std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; sessionStatePtr_t mSession; // Session state for the current session - bool mSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. sessionStatePtr_t mNextSession; // Session state for the session we're trying to join -- 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/llvoicechannel.cpp | 30 ++++++++++++++++++++++++++++-- indra/newview/llvoiceclient.cpp | 5 +++-- indra/newview/llvoiceclient.h | 10 +++++++--- indra/newview/llvoicevivox.cpp | 3 ++- indra/newview/llvoicevivox.h | 3 ++- indra/newview/llvoicewebrtc.cpp | 29 +++++++++++++++-------------- indra/newview/llvoicewebrtc.h | 10 ++++++---- 7 files changed, 63 insertions(+), 27 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index afac9ed6f8..05c22dd645 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -437,7 +437,8 @@ void LLVoiceChannelGroup::activate() // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, - mCredentials); + mCredentials, + !LLVoiceClient::getInstance()->hasP2PInterface()); if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel { @@ -518,7 +519,8 @@ void LLVoiceChannelGroup::setChannelInfo( // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, - mCredentials); + mCredentials, + !LLVoiceClient::getInstance()->hasP2PInterface()); } } @@ -527,6 +529,30 @@ void LLVoiceChannelGroup::handleStatusChange(EStatusType type) // status updates switch(type) { + case STATUS_LEFT_CHANNEL: + { + if (!LLVoiceClient::getInstance()->hasP2PInterface()) + { + // we're using group/adhoc for p2p + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) + { + // *TODO: use it to show DECLINE voice notification + if (mState == STATE_RINGING) + { + // other user declined call + LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs); + } + else + { + // other user hung up, so we didn't end the call + mCallEndedByAgent = false; + } + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + return; + } + } case STATUS_JOINED: mRetries = 3; mIsRetrying = FALSE; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 54840a1235..de7fb248b0 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -436,11 +436,12 @@ bool LLVoiceClient::inProximalChannel() void LLVoiceClient::setNonSpatialChannel( const std::string &uri, - const std::string &credentials) + const std::string &credentials, + bool hangup_on_last_leave) { if (mVoiceModule) { - mVoiceModule->setNonSpatialChannel(uri, credentials); + mVoiceModule->setNonSpatialChannel(uri, credentials, hangup_on_last_leave); } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 1a20de6109..69004f2000 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -167,7 +167,8 @@ public: virtual bool inProximalChannel()=0; virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials)=0; + const std::string &credentials, + bool hangup_on_last_leave = false)=0; virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials)=0; @@ -371,9 +372,12 @@ public: // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. bool inProximalChannel(); + void setNonSpatialChannel( - const std::string &uri, - const std::string &credentials); + const std::string &uri, + const std::string &credentials, + bool hangup_on_last_leave = false); + void setSpatialChannel( const std::string &uri, const std::string &credentials); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 3725510b6a..3c01f00596 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -5025,7 +5025,8 @@ void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session) void LLVivoxVoiceClient::setNonSpatialChannel( const std::string &uri, - const std::string &credentials) + const std::string &credentials, + bool hangup_on_last_leave) { switchChannel(uri, false, false, false, credentials); } diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index bd1f18aec6..929ccce32b 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -135,7 +135,8 @@ public: virtual bool inProximalChannel(); virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials); + const std::string &credentials, + bool hangup_on_last_leave); virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials); 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) { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 5ddfbd9ea4..e1a740929b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -144,8 +144,8 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string& uri, const std::string& credentials) override { - startAdHocSession(uri, credentials); + void setNonSpatialChannel(const std::string& uri, const std::string& credentials, bool hangup_on_last_leave) override { + startAdHocSession(uri, credentials, hangup_on_last_leave); } bool setSpatialChannel(const std::string &uri, const std::string &credentials) override @@ -388,6 +388,8 @@ public: static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } + bool mHangupOnLastLeave; + protected: sessionState(); std::list mWebRTCConnections; @@ -428,7 +430,7 @@ public: class adhocSessionState : public sessionState { public: - adhocSessionState(const std::string &channelID, const std::string& credentials); + adhocSessionState(const std::string &channelID, const std::string& credentials, bool hangup_on_last_leave); bool isSpatial() override { return false; } bool isEstate() override { return false; } @@ -601,7 +603,7 @@ private: bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); - bool startAdHocSession(const std::string& channelID, const std::string& credentials); + bool startAdHocSession(const std::string& channelID, const std::string& credentials, bool hangup_on_last_leave); void joinSession(const sessionStatePtr_t &session); -- cgit v1.2.3 From 4bf9dc229174647b34d8e1aa108e6985086b118b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 1 Feb 2024 21:57:05 -0800 Subject: fix mac build break --- indra/newview/llvoicevivox.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 929ccce32b..6e6ffdb1e4 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -134,9 +134,9 @@ public: // Note that gestures should only fire if this returns true. virtual bool inProximalChannel(); - virtual void setNonSpatialChannel(const std::string &uri, + void setNonSpatialChannel(const std::string &uri, const std::string &credentials, - bool hangup_on_last_leave); + bool hangup_on_last_leave) override; virtual bool setSpatialChannel(const std::string &uri, const std::string &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 ++++++++++++++++++++++++++++++++++++---- indra/newview/llvoicewebrtc.h | 20 ++++++- 2 files changed, 124 insertions(+), 11 deletions(-) (limited to 'indra/newview') 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); + } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e1a740929b..d7d6ca6229 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -40,6 +40,7 @@ class LLWebRTCProtocolParser; #include "lleventcoro.h" #include "llcoros.h" #include "llparcel.h" +#include "llmutelist.h" #include #include "json/reader.h" @@ -60,7 +61,8 @@ typedef boost::shared_ptr connectionPtr_t; class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, - public llwebrtc::LLWebRTCDevicesObserver + public llwebrtc::LLWebRTCDevicesObserver, + public LLMuteListObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); LOG_CLASS(LLWebRTCVoiceClient); @@ -240,6 +242,13 @@ public: bool isPreviewRecording() override { return false; } bool isPreviewPlaying() override { return false; } + //@} + + ////////////////// + /// @name LLMuteListObserver + //@{ + void onChange() override; + void onChangeDetailed(const LLMute& ) override; //@} // authorize the user @@ -344,6 +353,9 @@ public: void setMuteMic(bool muted); void setMicGain(F32 volume); void setSpeakerVolume(F32 volume); + void setUserVolume(const LLUUID& id, F32 volume); + + void setUserMute(const LLUUID& id, bool mute); static void for_each(sessionFunc_t func); @@ -454,6 +466,8 @@ public: static void predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); static void predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume); static void predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t &session); + static void predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, bool mute); + static void predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, F32 volume); ////////////////////////////// /// @name TVC/Server management and communication @@ -765,7 +779,9 @@ class LLVoiceWebRTCConnection : virtual void setMuteMic(bool muted); virtual void setMicGain(F32 volume); virtual void setSpeakerVolume(F32 volume); - + + void setUserVolume(const LLUUID& id, F32 volume); + void setUserMute(const LLUUID& id, bool mute); bool connectionStateMachine(); -- 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') 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 ++++++++++++++++++++++++++- indra/newview/llvoicewebrtc.h | 1 - 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'indra/newview') 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); } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index d7d6ca6229..83cb3c7eef 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -102,7 +102,6 @@ public: /// @name Devices //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - // i.e. when the daemon is running and connected, and the device lists are populated. bool deviceSettingsAvailable() override; bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. -- 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') 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') 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 ++++++++++++++++++++++++++++------------- indra/newview/llvoicewebrtc.h | 15 +++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) (limited to 'indra/newview') 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); } ///////////////////////////// diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 83cb3c7eef..16bcadb144 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -769,6 +769,7 @@ class LLVoiceWebRTCConnection : void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} + void sendJoin(); void sendData(const std::string &data); virtual void processIceUpdates(); @@ -806,13 +807,13 @@ class LLVoiceWebRTCConnection : VOICE_STATE_REQUEST_CONNECTION = 0x4, VOICE_STATE_CONNECTION_WAIT = 0x8, VOICE_STATE_SESSION_ESTABLISHED = 0x10, - VOICE_STATE_SESSION_UP = 0x20, - VOICE_STATE_SESSION_RETRY = 0x40, - VOICE_STATE_DISCONNECT = 0x80, - VOICE_STATE_WAIT_FOR_EXIT = 0x100, - VOICE_STATE_SESSION_EXIT = 0x200, - VOICE_STATE_SESSION_STOPPING = 0x3c0, - VOICE_STATE_SESSION_WAITING = 0x10a + VOICE_STATE_WAIT_FOR_DATA_CHANNEL = 0x20, + VOICE_STATE_SESSION_UP = 0x40, + VOICE_STATE_SESSION_RETRY = 0x80, + VOICE_STATE_DISCONNECT = 0x100, + VOICE_STATE_WAIT_FOR_EXIT = 0x200, + VOICE_STATE_SESSION_EXIT = 0x400, + VOICE_STATE_SESSION_STOPPING = 0x780 } EVoiceConnectionState; EVoiceConnectionState mVoiceConnectionState; -- 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/llimview.cpp | 11 +++++++---- indra/newview/llimview.h | 3 ++- indra/newview/llvoicechannel.cpp | 11 ++++++----- indra/newview/llvoicechannel.h | 3 ++- indra/newview/llvoicewebrtc.cpp | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 78017337e8..a6eeaeffc6 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -674,8 +674,6 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& } else { - mVoiceChannel = new LLVoiceChannelGroup(session_id, name); - // determine whether it is group or conference session if (gAgent.isInGroup(mSessionID)) { @@ -690,6 +688,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // webrtc uses adhoc channels for p2p mSessionType = P2P_SESSION; } + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, mSessionType == P2P_SESSION); } if(mVoiceChannel) @@ -3432,7 +3431,8 @@ void LLIMMgr::inviteToSession( EInstantMessage type, EInvitationType inv_type, const std::string& session_handle, - const std::string& session_uri) + const std::string& session_uri, + const std::string& voice_server_type) { std::string notify_box_type; // voice invite question is different from default only for group call (EXT-7118) @@ -4153,12 +4153,15 @@ public: return; } + std::string voice_server_type = input["body"]["voice"].get("voice_server_type").asString(); + BOOL session_type_p2p = input["body"]["voice"].get("p2p").asBoolean(); + gIMMgr->inviteToSession( input["body"]["session_id"].asUUID(), input["body"]["session_name"].asString(), input["body"]["from_id"].asUUID(), input["body"]["from_name"].asString(), - IM_SESSION_INVITE, + session_type_p2p ? IM_SESSION_P2P_INVITE : IM_SESSION_INVITE, LLIMMgr::INVITATION_TYPE_VOICE); } else if ( input["body"].has("immediate") ) diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 946eb02f26..49c73bd495 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -417,7 +417,8 @@ public: EInstantMessage type, EInvitationType inv_type, const std::string& session_handle = LLStringUtil::null, - const std::string& session_uri = LLStringUtil::null); + const std::string& session_uri = LLStringUtil::null, + const std::string& voice_server_type = LLStringUtil::null); void processIMTypingStart(const LLUUID& from_id, const EInstantMessage im_type); void processIMTypingStop(const LLUUID& from_id, const EInstantMessage im_type); diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 05c22dd645..4ca876bed1 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -410,8 +410,9 @@ boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallbac // LLVoiceChannelGroup // -LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : - LLVoiceChannel(session_id, session_name) +LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, BOOL hangup_on_last_leave) : + LLVoiceChannel(session_id, session_name), + mHangupOnLastLeave(hangup_on_last_leave) { mRetries = DEFAULT_RETRIES_COUNT; mIsRetrying = FALSE; @@ -438,7 +439,7 @@ void LLVoiceChannelGroup::activate() LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, mCredentials, - !LLVoiceClient::getInstance()->hasP2PInterface()); + mHangupOnLastLeave); if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel { @@ -520,7 +521,7 @@ void LLVoiceChannelGroup::setChannelInfo( LLVoiceClient::getInstance()->setNonSpatialChannel( mURI, mCredentials, - !LLVoiceClient::getInstance()->hasP2PInterface()); + mHangupOnLastLeave); } } @@ -792,7 +793,7 @@ void LLVoiceChannelProximal::deactivate() // LLVoiceChannelP2P // LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : - LLVoiceChannelGroup(session_id, session_name), + LLVoiceChannelGroup(session_id, session_name, true), mOtherUserID(other_user_id), mReceivedCall(FALSE) { diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 309c3eebdd..84ef3e7c08 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -144,7 +144,7 @@ private: class LLVoiceChannelGroup : public LLVoiceChannel { public: - LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); + LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, BOOL hangup_on_last_leave); /*virtual*/ void handleStatusChange(EStatusType status); /*virtual*/ void handleError(EStatusType status); @@ -163,6 +163,7 @@ private: U32 mRetries; BOOL mIsRetrying; + BOOL mHangupOnLastLeave; }; class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton 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 41054efc2481beec137ff23893fff06858882fd7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 12:40:56 -0800 Subject: Add new P@P multiagentchat handler for webrtc voice --- indra/newview/llimview.cpp | 63 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index a6eeaeffc6..de1422829b 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -87,7 +87,10 @@ const S32 XL8_PADDING = 3; // XL8_START_TAG.size() + XL8_END_TAG.size() /** Timeout of outgoing session initialization (in seconds) */ const static U32 SESSION_INITIALIZATION_TIMEOUT = 30; -void startConfrenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents); +void startConferenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents); + +void startP2PCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId); + void chatterBoxInvitationCoro(std::string url, LLUUID sessionId, LLIMMgr::EInvitationType invitationType); void chatterBoxHistoryCoro(std::string url, LLUUID sessionId, std::string from, std::string message, U32 timestamp); void start_deprecated_conference_chat(const LLUUID& temp_session_id, const LLUUID& creator_id, const LLUUID& other_participant_id, const LLSD& agents_to_invite); @@ -396,7 +399,7 @@ void on_new_message(const LLSD& msg) notify_of_message(msg, false); } -void startConfrenceCoro(std::string url, +void startConferenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -437,6 +440,35 @@ void startConfrenceCoro(std::string url, } } +void startP2PCoro(std::string url, LLUUID sessionID, LLUUID creatorId, LLUUID otherParticipantId) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("ConferenceChatStart", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData; + postData["method"] = "start p2p"; + postData["session-id"] = sessionID; + postData["params"] = otherParticipantId; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("LLIMModel") << "Failed to start conference" << LL_ENDL; + // try an "old school" way. + // *TODO: What about other error status codes? 4xx 5xx? + if (status == LLCore::HttpStatus(HTTP_BAD_REQUEST)) + { + static const std::string error_string("session_does_not_exist_error"); + gIMMgr->showSessionStartError(error_string, sessionID); + } + } +} + void chatterBoxInvitationCoro(std::string url, LLUUID sessionId, LLIMMgr::EInvitationType invitationType) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -2041,8 +2073,7 @@ bool LLIMModel::sendStartSession( return true; } - else if (( dialog == IM_SESSION_CONFERENCE_START ) || - (((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) && !LLVoiceClient::getInstance()->hasP2PInterface())) + else if (dialog == IM_SESSION_CONFERENCE_START ) { LLSD agents; for (int i = 0; i < (S32) ids.size(); i++) @@ -2057,8 +2088,8 @@ bool LLIMModel::sendStartSession( std::string url = region->getCapability( "ChatSessionRequest"); - LLCoros::instance().launch("startConfrenceCoro", - boost::bind(&startConfrenceCoro, url, + LLCoros::instance().launch("startConferenceCoro", + boost::bind(&startConferenceCoro, url, temp_session_id, gAgent.getID(), other_participant_id, agents)); } else @@ -2073,6 +2104,26 @@ bool LLIMModel::sendStartSession( //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id) return true; } + else if (((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) && !LLVoiceClient::getInstance()->hasP2PInterface()) + { + LLSD agents; + for (int i = 0; i < (S32) ids.size(); i++) + { + agents.append(ids[i]); + } + + // we have a new way of starting conference calls now + LLViewerRegion *region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); + + LLCoros::instance().launch( + "startP2P", + boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); + } + return true; + } return false; } -- cgit v1.2.3 From 250a4024cc94639788b695a5bbf1437fd3a31aba Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 8 Feb 2024 19:00:22 -0800 Subject: rebase merge fix --- indra/newview/llvoicevivox.h | 138 +++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 69 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 6e6ffdb1e4..5ddb72fa24 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -56,7 +56,7 @@ class LLVivoxVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface { - LLSINGLETON(LLVivoxVoiceClient); + LLSINGLETON_C11(LLVivoxVoiceClient); LOG_CLASS(LLVivoxVoiceClient); virtual ~LLVivoxVoiceClient(); @@ -64,26 +64,26 @@ public: /// @name LLVoiceModuleInterface virtual implementations /// @see LLVoiceModuleInterface //@{ - virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - virtual void terminate(); // Call this to clean up during shutdown + void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) + void terminate() override; // Call this to clean up during shutdown - virtual const LLVoiceVersionInfo& getVersion(); + const LLVoiceVersionInfo& getVersion() override; - virtual void updateSettings(); // call after loading settings and whenever they change + void updateSettings() override; // call after loading settings and whenever they change // Returns true if vivox has successfully logged in and is not in error state - virtual bool isVoiceWorking() const; + bool isVoiceWorking() const override; ///////////////////// /// @name Tuning //@{ - virtual void tuningStart(); - virtual void tuningStop(); - virtual bool inTuningMode(); + void tuningStart() override; + void tuningStop() override; + bool inTuningMode() override; - virtual void tuningSetMicVolume(float volume); - virtual void tuningSetSpeakerVolume(float volume); - virtual float tuningGetEnergy(void); + void tuningSetMicVolume(float volume) override; + void tuningSetSpeakerVolume(float volume) override; + float tuningGetEnergy(void) override; //@} ///////////////////// @@ -91,40 +91,40 @@ public: //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. - virtual bool deviceSettingsAvailable(); - virtual bool deviceSettingsUpdated(); //return if the list has been updated and never fetched, only to be called from the voicepanel. + bool deviceSettingsAvailable() override; + bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - virtual void refreshDeviceLists(bool clearCurrentList = true); + void refreshDeviceLists(bool clearCurrentList = true) override; - virtual void setCaptureDevice(const std::string& name); - virtual void setRenderDevice(const std::string& name); + void setCaptureDevice(const std::string& name) override; + void setRenderDevice(const std::string& name) override; - virtual LLVoiceDeviceList& getCaptureDevices(); - virtual LLVoiceDeviceList& getRenderDevices(); - //@} + LLVoiceDeviceList& getCaptureDevices() override; + LLVoiceDeviceList& getRenderDevices() override; + //@} - virtual void getParticipantList(std::set &participants); - virtual bool isParticipant(const LLUUID& speaker_id); + void getParticipantList(std::set &participants) override; + bool isParticipant(const LLUUID& speaker_id) override; // Send a text message to the specified user, initiating the session if necessary. // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; // close any existing text IM session with the specified user - virtual void endUserIMSession(const LLUUID &uuid); + void endUserIMSession(const LLUUID &uuid) override; // Returns true if calling back the session URI after the session has closed is possible. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. - virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); + BOOL isSessionCallBackPossible(const LLUUID &session_id) override; // Returns true if the session can accepte text IM's. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. - virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); + BOOL isSessionTextIMPossible(const LLUUID &session_id) override; //////////////////////////// @@ -132,22 +132,22 @@ public: //@{ // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. - virtual bool inProximalChannel(); + bool inProximalChannel() override; void setNonSpatialChannel(const std::string &uri, const std::string &credentials, bool hangup_on_last_leave) override; - virtual bool setSpatialChannel(const std::string &uri, - const std::string &credentials); + bool setSpatialChannel(const std::string &uri, + const std::string &credentials) override; - virtual void leaveNonSpatialChannel(); + void leaveNonSpatialChannel() override; - virtual void leaveChannel(void); + void leaveChannel(void) override; // Returns the URI of the current channel, or an empty string if not currently in a channel. // NOTE that it will return an empty string if it's in the process of joining a channel. - virtual std::string getCurrentChannel(); + std::string getCurrentChannel() override; //@} @@ -156,7 +156,7 @@ public: //@{ // start a voice channel with the specified user bool hasP2PInterface() override { return true; } - void callUser(const LLUUID &uuid); + void callUser(const LLUUID &uuid) override; bool isValidChannel(std::string &channelHandle) override; bool answerInvite(std::string &channelHandle) override; void declineInvite(std::string &channelHandle) override; @@ -172,43 +172,43 @@ public: ///////////////////////// /// @name enable disable voice and features //@{ - virtual bool voiceEnabled(); - virtual void setVoiceEnabled(bool enabled); - virtual BOOL lipSyncEnabled(); - virtual void setLipSyncEnabled(BOOL enabled); - virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + bool voiceEnabled() override; + void setVoiceEnabled(bool enabled) override; + BOOL lipSyncEnabled() override; + void setLipSyncEnabled(BOOL enabled) override; + void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} ////////////////////////// /// @name nearby speaker accessors //@{ - virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - virtual std::string getDisplayName(const LLUUID& id); - virtual BOOL isParticipantAvatar(const LLUUID &id); - virtual BOOL getIsSpeaking(const LLUUID& id); - virtual BOOL getIsModeratorMuted(const LLUUID& id); - virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id); - virtual F32 getUserVolume(const LLUUID& id); - virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar + std::string getDisplayName(const LLUUID& id) override; + BOOL isParticipantAvatar(const LLUUID &id) override; + BOOL getIsSpeaking(const LLUUID& id) override; + BOOL getIsModeratorMuted(const LLUUID& id) override; + F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id) override; + F32 getUserVolume(const LLUUID& id) override; + void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} // authorize the user virtual void userAuthorized(const std::string& user_id, - const LLUUID &agentID); + const LLUUID &agentID) override; ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceClientStatusObserver* observer); - virtual void removeObserver(LLVoiceClientStatusObserver* observer); - virtual void addObserver(LLFriendObserver* observer); - virtual void removeObserver(LLFriendObserver* observer); - virtual void addObserver(LLVoiceClientParticipantObserver* observer); - virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + void addObserver(LLVoiceClientStatusObserver* observer) override; + void removeObserver(LLVoiceClientStatusObserver* observer) override; + void addObserver(LLFriendObserver* observer) override; + void removeObserver(LLFriendObserver* observer) override; + void addObserver(LLVoiceClientParticipantObserver* observer) override; + void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - virtual std::string sipURIFromID(const LLUUID &id); + std::string sipURIFromID(const LLUUID &id) override; //@} /// @name LLVoiceEffectInterface virtual implementations @@ -218,32 +218,32 @@ public: ////////////////////////// /// @name Accessors //@{ - virtual bool setVoiceEffect(const LLUUID& id); - virtual const LLUUID getVoiceEffect(); - virtual LLSD getVoiceEffectProperties(const LLUUID& id); + bool setVoiceEffect(const LLUUID& id) override; + const LLUUID getVoiceEffect() override; + LLSD getVoiceEffectProperties(const LLUUID& id) override; - virtual void refreshVoiceEffectLists(bool clear_lists); - virtual const voice_effect_list_t& getVoiceEffectList() const; - virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + void refreshVoiceEffectLists(bool clear_lists) override; + const voice_effect_list_t& getVoiceEffectList() const override; + const voice_effect_list_t& getVoiceEffectTemplateList() const override; //@} ////////////////////////////// /// @name Status notification //@{ - virtual void addObserver(LLVoiceEffectObserver* observer); - virtual void removeObserver(LLVoiceEffectObserver* observer); + void addObserver(LLVoiceEffectObserver* observer) override; + void removeObserver(LLVoiceEffectObserver* observer) override; //@} ////////////////////////////// /// @name Effect preview buffer //@{ - virtual void enablePreviewBuffer(bool enable); - virtual void recordPreviewBuffer(); - virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); - virtual void stopPreviewBuffer(); + void enablePreviewBuffer(bool enable) override; + void recordPreviewBuffer() override; + void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; + void stopPreviewBuffer() override; - virtual bool isPreviewRecording(); - virtual bool isPreviewPlaying(); + bool isPreviewRecording() override; + bool isPreviewPlaying() override; //@} //@} @@ -752,7 +752,7 @@ private: std::string getAudioSessionURI(); std::string getAudioSessionHandle(); - void setHidden(bool hidden); //virtual + void setHidden(bool hidden) override; //virtual void sendPositionAndVolumeUpdate(void); void sendCaptureAndRenderDevices(); -- 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') 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/llimview.cpp | 29 ++++++++++++++++------------- indra/newview/llvoicewebrtc.cpp | 4 +++- 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index de1422829b..9b2cd46a4b 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -697,12 +697,19 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // set P2P type by default mSessionType = P2P_SESSION; - if ((IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) && LLVoiceClient::getInstance()->hasP2PInterface()) - { - // only use LLVoiceChannelP2P if the provider can handle the special P2P interface, - // which uses the voice server to relay calls and invites. Otherwise, - // we use the group voice provider. - mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); + if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) + { + if (LLVoiceClient::getInstance()->hasP2PInterface()) + { + // only use LLVoiceChannelP2P if the provider can handle the special P2P interface, + // which uses the voice server to relay calls and invites. Otherwise, + // we use the group voice provider. + mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); + } + else + { + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); + } } else { @@ -710,17 +717,13 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& if (gAgent.isInGroup(mSessionID)) { mSessionType = GROUP_SESSION; + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); } - else if (LLVoiceClient::getInstance()->hasP2PInterface()) + else { mSessionType = ADHOC_SESSION; + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); } - else - { - // webrtc uses adhoc channels for p2p - mSessionType = P2P_SESSION; - } - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, mSessionType == P2P_SESSION); } if(mVoiceChannel) 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/llappviewer.cpp | 8 +-- indra/newview/llimview.cpp | 5 +- indra/newview/llviewerregion.h | 2 +- indra/newview/llvoiceclient.cpp | 132 +++++++++++++++++++++++++++++----------- indra/newview/llvoiceclient.h | 17 +++++- indra/newview/llvoicevivox.cpp | 29 ++++++++- indra/newview/llvoicewebrtc.cpp | 22 ++++++- 7 files changed, 166 insertions(+), 49 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 4a43133ff6..9691bb9a8c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3344,15 +3344,15 @@ LLSD LLAppViewer::getViewerInfo() const { LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); const std::string build_version = version.mBuildVersion; - std::ostringstream version_string; - if (std::equal(build_version.begin(), build_version.begin() + version.serverVersion.size(), + std::ostringstream version_string; + if (std::equal(version.mBuildVersion.begin(), version.mBuildVersion.begin() + version.serverVersion.size(), version.serverVersion.begin())) { // Normal case: Show type and build version. - version_string << version.serverType << " " << build_version << std::endl; + version_string << version.voiceServerType << " " << version.mBuildVersion << std::endl; } else { // Mismatch: Show both versions. - version_string << version.serverVersion << "/" << build_version << std::endl; + version_string << version.voiceServerType << " " << version.serverVersion << "/" << version.mBuildVersion << std::endl; } info["VOICE_VERSION"] = version_string.str(); } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9b2cd46a4b..1c8cdfd641 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -4216,7 +4216,10 @@ public: input["body"]["from_id"].asUUID(), input["body"]["from_name"].asString(), session_type_p2p ? IM_SESSION_P2P_INVITE : IM_SESSION_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE); + LLIMMgr::INVITATION_TYPE_VOICE, + LLStringUtil::null, // session_handle + LLStringUtil::null, + voice_server_type); } else if ( input["body"].has("immediate") ) { diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index b24ff51479..5817bd7d8d 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -546,7 +546,7 @@ public: U8 mCentralBakeVersion; LLVOCacheEntry* mLastVisitedEntry; - U32 mInvisibilityCheckHistory; + U32 mInvisibilityCheckHistory; // Information for Homestead / CR-53 S32 mClassID; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index de7fb248b0..3532d8e986 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -133,7 +133,6 @@ LLVoiceClient::LLVoiceClient(LLPumpIO *pump) mMuteMic(false), mDisableMic(false) { - updateSettings(); init(pump); } @@ -149,31 +148,71 @@ void LLVoiceClient::init(LLPumpIO *pump) { // Initialize all of the voice modules m_servicePump = pump; + LLWebRTCVoiceClient::getInstance()->init(pump); + LLVivoxVoiceClient::getInstance()->init(pump); } void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) { - // In the future, we should change this to allow voice module registration - // with a table lookup of sorts. - std::string voice_server = gSavedSettings.getString("VoiceServerType"); - LL_DEBUGS("Voice") << "voice server type " << voice_server << LL_ENDL; - if(voice_server == "vivox") - { - mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance(); - } - if (voice_server == "webrtc") + mUserID = user_id; + mAgentID = agentID; + gAgent.addRegionChangedCallback(boost::bind(&LLVoiceClient::onRegionChanged, this)); +} + +void LLVoiceClient::onRegionChanged() +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } + else if (region) + { + if (mSimulatorFeaturesReceivedSlot.connected()) + { + mSimulatorFeaturesReceivedSlot.disconnect(); + } + mSimulatorFeaturesReceivedSlot = + region->setSimulatorFeaturesReceivedCallback( + boost::bind(&LLVoiceClient::onSimulatorFeaturesReceived, this, _1)); + } +} + +void LLVoiceClient::onSimulatorFeaturesReceived(const LLUUID& region_id) +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && (region->getRegionID() == region_id)) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } +} + +void LLVoiceClient::setVoiceModule(const std::string& voice_server_type) +{ + if (voice_server_type == "vivox" || voice_server_type.empty()) + { + mVoiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance(); + } + else if (voice_server_type == "webrtc") { mVoiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); } else { - mVoiceModule = NULL; - return; + LLNotificationsUtil::add("VoiceVersionMismatch"); + mVoiceModule = nullptr; + return; } - mVoiceModule->init(m_servicePump); - mVoiceModule->userAuthorized(user_id, agentID); + mVoiceModule->userAuthorized(mUserID, mAgentID); + updateSettings(); } + + void LLVoiceClient::setHidden(bool hidden) { if (mVoiceModule) @@ -205,7 +244,7 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() { LLVoiceVersionInfo result; result.serverVersion = std::string(); - result.serverType = std::string(); + result.voiceServerType = std::string(); result.mBuildVersion = std::string(); return result; } @@ -864,31 +903,54 @@ LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const class LLViewerRequiredVoiceVersion : public LLHTTPNode { - static BOOL sAlertedUser; + static bool sAlertedUser; virtual void post( LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const { - //You received this messsage (most likely on region cross or - //teleport) - if ( input.has("body") && input["body"].has("major_version") ) + + + std::string voice_server_type = "vivox"; + if (input.has("body") && input["body"].has("voice_server_type")) { - int major_voice_version = - input["body"]["major_version"].asInteger(); - // int minor_voice_version = - // input["body"]["minor_version"].asInteger(); - LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion(); - - if (major_voice_version > 1) - { - if (!sAlertedUser) - { - //sAlertedUser = TRUE; - LLNotificationsUtil::add("VoiceVersionMismatch"); - gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener - } - } + voice_server_type = input["body"]["voice_server_type"].asString(); + } + + LLVoiceModuleInterface *voiceModule = NULL; + + if (voice_server_type == "vivox" || voice_server_type.empty()) + { + voiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance(); + } + else if (voice_server_type == "webrtc") + { + voiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); + } + else + { + LL_WARNS("Voice") << "Unknown voice server type " << voice_server_type << LL_ENDL; + if (!sAlertedUser) + { + // sAlertedUser = true; + LLNotificationsUtil::add("VoiceVersionMismatch"); + } + return; + } + + LLVoiceVersionInfo versionInfo = voiceModule->getVersion(); + if (input.has("body") && input["body"].has("major_version") && + input["body"]["major_version"].asInteger() > versionInfo.majorVersion) + { + if (!sAlertedUser) + { + // sAlertedUser = true; + LLNotificationsUtil::add("VoiceVersionMismatch"); + LL_WARNS("Voice") << "Voice server version mismatch " << input["body"]["major_version"].asInteger() << "/" + << versionInfo.majorVersion + << LL_ENDL; + } + return; } } }; @@ -1099,7 +1161,7 @@ void LLSpeakerVolumeStorage::save() } } -BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE; +bool LLViewerRequiredVoiceVersion::sAlertedUser = false; LLHTTPRegistration gHTTPRegistrationMessageParcelVoiceInfo( diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 69004f2000..4ed3cd5066 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -94,8 +94,10 @@ public: struct LLVoiceVersionInfo { - std::string serverType; - std::string serverVersion; + std::string voiceServerType; + int majorVersion; + int minorVersion; + std::string serverVersion; std::string mBuildVersion; }; @@ -122,6 +124,8 @@ public: virtual void setHidden(bool hidden)=0; // Hides the user from voice. virtual const LLVoiceVersionInfo& getVersion()=0; + + ///////////////////// /// @name Tuning @@ -449,9 +453,13 @@ public: void endUserIMSession(const LLUUID &uuid); //@} + void setVoiceModule(const std::string& voice_server_type); void userAuthorized(const std::string& user_id, - const LLUUID &agentID); + const LLUUID &agentID); + + void onRegionChanged(); + void onSimulatorFeaturesReceived(const LLUUID ®ion_id); void addObserver(LLVoiceClientStatusObserver* observer); void removeObserver(LLVoiceClientStatusObserver* observer); @@ -478,6 +486,9 @@ protected: LLVoiceModuleInterface* mVoiceModule; LLPumpIO *m_servicePump; + std::string mUserID; + LLUUID mAgentID; + boost::signals2::connection mSimulatorFeaturesReceivedSlot; LLCachedControl mVoiceEffectEnabled; LLCachedControl mVoiceEffectDefault; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 3c01f00596..d708870314 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -85,7 +85,8 @@ namespace { const F32 SPEAKING_TIMEOUT = 1.f; - static const std::string VOICE_SERVER_TYPE = "Vivox"; + static const std::string VISIBLE_VOICE_SERVER_TYPE = "Vivox"; + static const std::string VIVOX_VOICE_SERVER_TYPE = "vivox"; // Don't retry connecting to the daemon more frequently than this: const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; @@ -358,7 +359,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mSpeakerVolume = scale_speaker_volume(0); mVoiceVersion.serverVersion = ""; - mVoiceVersion.serverType = VOICE_SERVER_TYPE; + mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; // gMuteListp isn't set up at this point, so we defer this until later. // gMuteListp->addObserver(&mutelist_listener); @@ -737,6 +738,19 @@ void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state) return; } + + // grab the active voice server version info to see if we should use + // vivox. + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + { + // we've switched to another voice provider, so + // disable voice and shut down vivox. Don't + // notify, though. + mVoiceEnabled = false; + } + + switch (coro_state) { case VOICE_STATE_TP_WAIT: @@ -1972,6 +1986,17 @@ bool LLVivoxVoiceClient::waitForChannel() return false; } + // grab the active voice server version info to see if we should use + // vivox. + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) + { + // we've switched to another voice provider, so + // disable voice and shut down vivox. Don't + // notify, though. + mVoiceEnabled = false; + } + switch (state) { case VOICE_CHANNEL_STATE_LOGIN: 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/llvoicevivox.cpp | 4 ++++ indra/newview/llvoicewebrtc.cpp | 3 +++ 2 files changed, 7 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index d708870314..e003dc3317 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -360,6 +360,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.majorVersion = 1; + mVoiceVersion.minorVersion = 0; + mVoiceVersion.mBuildVersion = ""; + mVoiceVersion.serverVersion = ""; // gMuteListp isn't set up at this point, so we defer this until later. // gMuteListp->addObserver(&mutelist_listener); 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/llagent.cpp | 6 +- indra/newview/llappviewer.cpp | 2 +- indra/newview/llconversationview.cpp | 2 +- indra/newview/llfloaterimsession.cpp | 2 +- indra/newview/llfloaterimsession.h | 3 +- indra/newview/llimview.cpp | 258 +++++------------ indra/newview/llimview.h | 28 +- indra/newview/llpanelgroup.cpp | 2 +- indra/newview/llpanelgroup.h | 2 +- indra/newview/llpanelpeople.cpp | 2 +- indra/newview/llpanelpeople.h | 2 +- indra/newview/llpanelprofile.cpp | 2 +- indra/newview/llpanelprofile.h | 2 +- indra/newview/llvoavatar.cpp | 12 +- indra/newview/llvoavatar.h | 4 +- indra/newview/llvoicechannel.cpp | 185 +++++-------- indra/newview/llvoicechannel.h | 68 +++-- indra/newview/llvoiceclient.cpp | 521 ++++++++++++++--------------------- indra/newview/llvoiceclient.h | 122 ++++---- indra/newview/llvoicevivox.cpp | 425 ++++++++-------------------- indra/newview/llvoicevivox.h | 67 +++-- indra/newview/llvoicewebrtc.cpp | 205 ++++++-------- indra/newview/llvoicewebrtc.h | 59 ++-- 23 files changed, 737 insertions(+), 1244 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 3853aaa8fd..ca27dbd818 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -311,7 +311,7 @@ bool LLAgent::isActionAllowed(const LLSD& sdname) } else { - allow_agent_voice = channel->isActive() && channel->callStarted(); + allow_agent_voice = channel->isActive(); } } @@ -4078,10 +4078,6 @@ bool LLAgent::teleportCore(bool is_local) } make_ui_sound("UISndTeleportOut"); - // MBW -- Let the voice client know a teleport has begun so it can leave the existing channel. - // This was breaking the case of teleporting within a single sim. Backing it out for now. -// LLVoiceClient::getInstance()->leaveChannel(); - return true; } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9691bb9a8c..c079b3d1a1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5121,7 +5121,7 @@ void LLAppViewer::sendLogoutRequest() if(LLVoiceClient::instanceExists()) { - LLVoiceClient::getInstance()->leaveChannel(); + LLVoiceClient::getInstance()->setVoiceEnabled(false); } } } diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 48c7df40df..42194c9c16 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -57,7 +57,7 @@ public: : conversation(conv) {} - virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) + virtual void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) { conversation->showVoiceIndicator(conversation && status != STATUS_JOINING diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index ee9dc35283..1b04db89c4 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -550,7 +550,7 @@ void LLFloaterIMSession::onCallButtonClicked() } } -void LLFloaterIMSession::onChange(EStatusType status, const std::string &channelURI, bool proximal) +void LLFloaterIMSession::onChange(EStatusType status, const LLSD& channelInfo, bool proximal) { if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) { diff --git a/indra/newview/llfloaterimsession.h b/indra/newview/llfloaterimsession.h index 28464fc14b..fc431f3ced 100644 --- a/indra/newview/llfloaterimsession.h +++ b/indra/newview/llfloaterimsession.h @@ -114,8 +114,7 @@ public: // Implements LLVoiceClientStatusObserver::onChange() to enable the call // button when voice is available - void onChange(EStatusType status, const std::string &channelURI, - bool proximal); + void onChange(EStatusType status, const LLSD& channelInfo, bool proximal); virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } virtual void onVoiceChannelStateChanged( diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 1c8cdfd641..a675c44274 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -111,7 +111,7 @@ BOOL LLSessionTimeoutTimer::tick() { gIMMgr->showSessionStartError("session_initialization_timed_out_error", mSessionId); } - return TRUE; + return TRUE; } @@ -140,7 +140,7 @@ void process_dnd_im(const LLSD& notification) name, IM_NOTHING_SPECIAL, fromID, - false, + LLSD(), false); //will need slight refactor to retrieve whether offline message or not (assume online for now) } @@ -411,6 +411,9 @@ void startConferenceCoro(std::string url, postData["method"] = "start conference"; postData["session-id"] = tempSessionId; postData["params"] = agents; + LLSD altParams; + altParams["voice_server_type"] = gSavedSettings.getString("VoiceServerType"); + postData["alt_params"] = altParams; LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); @@ -450,6 +453,9 @@ void startP2PCoro(std::string url, LLUUID sessionID, LLUUID creatorId, LLUUID ot postData["method"] = "start p2p"; postData["session-id"] = sessionID; postData["params"] = otherParticipantId; + LLSD altParams; + altParams["voice_server_type"] = gSavedSettings.getString("VoiceServerType"); + postData["alt_params"] = altParams; LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); @@ -458,7 +464,7 @@ void startP2PCoro(std::string url, LLUUID sessionID, LLUUID creatorId, LLUUID ot if (!status) { - LL_WARNS("LLIMModel") << "Failed to start conference" << LL_ENDL; + LL_WARNS("LLIMModel") << "Failed to start conference:" << postData << "->" << result << LL_ENDL; // try an "old school" way. // *TODO: What about other error status codes? 4xx 5xx? if (status == LLCore::HttpStatus(HTTP_BAD_REQUEST)) @@ -675,7 +681,13 @@ LLIMModel::LLIMModel() LLCallDialogManager::instance(); } -LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) +LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, + const std::string& name, + const EInstantMessage& type, + const LLUUID& other_participant_id, + const uuid_vec_t& ids, + const LLSD& voiceChannelInfo, + bool has_offline_msg) : mSessionID(session_id), mName(name), mType(type), @@ -690,24 +702,30 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mCallBackEnabled(true), mTextIMPossible(true), mStartCallOnInitialize(false), - mStartedAsIMCall(voice), + mStartedAsIMCall(!voiceChannelInfo.isUndefined()), mIsDNDsend(false), mAvatarNameCacheConnection() { // set P2P type by default - mSessionType = P2P_SESSION; + mSessionType = P2P_SESSION; + bool p2pAsAdhocCall = false; if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) { - if (LLVoiceClient::getInstance()->hasP2PInterface()) + LLVoiceP2POutgoingCallInterface *outgoingInterface = + LLVoiceClient::getInstance()->getOutgoingCallInterface(voiceChannelInfo); + + if (outgoingInterface) { // only use LLVoiceChannelP2P if the provider can handle the special P2P interface, // which uses the voice server to relay calls and invites. Otherwise, // we use the group voice provider. - mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); + mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id, outgoingInterface); + mVoiceChannel->setChannelInfo(voiceChannelInfo); } else { + p2pAsAdhocCall = true; mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); } } @@ -741,8 +759,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& //we need to wait for session initialization for outgoing ad-hoc and group chat session //correct session id for initiated ad-hoc chat will be received from the server - if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, - mInitialTargetIDs, mType)) + if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType, p2pAsAdhocCall)) { //we don't need to wait for any responses //so we're already initialized @@ -891,22 +908,6 @@ LLIMModel::LLIMSession::~LLIMSession() delete mSpeakers; mSpeakers = NULL; - // End the text IM session if necessary - if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull()) - { - switch(mType) - { - case IM_NOTHING_SPECIAL: - case IM_SESSION_P2P_INVITE: - LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID); - break; - - default: - // Appease the linux compiler - break; - } - } - mVoiceChannelStateChangeConnection.disconnect(); // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point). @@ -1481,7 +1482,7 @@ void LLIMModel::testMessages() //session name should not be empty bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) + const LLUUID& other_participant_id, const uuid_vec_t& ids, const LLSD& voiceChannelInfo, bool has_offline_msg) { if (name.empty()) { @@ -1495,7 +1496,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co return false; } - LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg); + LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voiceChannelInfo, has_offline_msg); mId2SessionMap[session_id] = session; // When notifying observer, name of session is used instead of "name", because they may not be the @@ -1507,11 +1508,11 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co } -bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg) +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const LLSD& voiceChannelInfo, bool has_offline_msg) { uuid_vec_t ids; ids.push_back(other_participant_id); - return newSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg); + return newSession(session_id, name, type, other_participant_id, ids, voiceChannelInfo, has_offline_msg); } bool LLIMModel::clearSession(const LLUUID& session_id) @@ -2060,7 +2061,8 @@ bool LLIMModel::sendStartSession( const LLUUID& temp_session_id, const LLUUID& other_participant_id, const uuid_vec_t& ids, - EInstantMessage dialog) + EInstantMessage dialog, + bool p2p_as_adhoc_call) { if ( dialog == IM_SESSION_GROUP_START ) { @@ -2076,7 +2078,7 @@ bool LLIMModel::sendStartSession( return true; } - else if (dialog == IM_SESSION_CONFERENCE_START ) + else if ((dialog == IM_SESSION_CONFERENCE_START ) || p2p_as_adhoc_call) { LLSD agents; for (int i = 0; i < (S32) ids.size(); i++) @@ -2107,27 +2109,16 @@ bool LLIMModel::sendStartSession( //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id) return true; } - else if (((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) && !LLVoiceClient::getInstance()->hasP2PInterface()) + else if ((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) { - LLSD agents; - for (int i = 0; i < (S32) ids.size(); i++) - { - agents.append(ids[i]); - } - - // we have a new way of starting conference calls now LLViewerRegion *region = gAgent.getRegion(); if (region) { std::string url = region->getCapability("ChatSessionRequest"); - - LLCoros::instance().launch( - "startP2P", - boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); + LLCoros::instance().launch("startP2P", boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); } return true; } - return false; } @@ -2720,21 +2711,21 @@ mAvatarNameCacheConnection() void LLIncomingCallDialog::onLifetimeExpired() { - std::string session_handle = mPayload["session_handle"].asString(); - if (LLVoiceClient::getInstance()->isValidChannel(session_handle)) - { - // restart notification's timer if call is still valid - mLifetimeTimer.start(); - } - else - { - // close invitation if call is already not valid - mLifetimeTimer.stop(); - LLUUID session_id = mPayload["session_id"].asUUID(); - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - closeFloater(); - } + LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(mPayload["voice_session_info"]); + if (call) + { + // restart notification's timer if call is still valid + mLifetimeTimer.start(); + } + else + { + // close invitation if call is already not valid + mLifetimeTimer.stop(); + LLUUID session_id = mPayload["session_id"].asUUID(); + gIMMgr->clearPendingAgentListUpdates(session_id); + gIMMgr->clearPendingInvitation(session_id); + closeFloater(); + } } BOOL LLIncomingCallDialog::postBuild() @@ -2914,10 +2905,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload { // create a normal IM session session_id = gIMMgr->addP2PSession( - session_name, - caller_id, - payload["session_handle"].asString(), - payload["session_uri"].asString()); + session_name, caller_id, payload["voice_channel_info"]); if (voice) { @@ -2997,11 +2985,11 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload { if (type == IM_SESSION_P2P_INVITE) { - if(LLVoiceClient::getInstance()) - { - std::string s = payload["session_handle"].asString(); - LLVoiceClient::getInstance()->declineInvite(s); - } + LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(payload["voice_session_info"]); + if (call) + { + call->declineInvite(); + } } else { @@ -3023,90 +3011,6 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload } } -bool inviteUserResponse(const LLSD& notification, const LLSD& response) -{ - if (!gIMMgr) - return false; - - const LLSD& payload = notification["payload"]; - LLUUID session_id = payload["session_id"].asUUID(); - EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); - LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - switch(option) - { - case 0: // accept - { - if (type == IM_SESSION_P2P_INVITE) - { - // create a normal IM session - session_id = gIMMgr->addP2PSession( - payload["session_name"].asString(), - payload["caller_id"].asUUID(), - payload["session_handle"].asString(), - payload["session_uri"].asString()); - - gIMMgr->startCall(session_id); - - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - } - else - { - gIMMgr->addSession( - payload["session_name"].asString(), - type, - session_id, true); - - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - LLCoros::instance().launch("chatterBoxInvitationCoro", - boost::bind(&chatterBoxInvitationCoro, url, - session_id, inv_type)); - } - } - break; - case 2: // mute (also implies ignore, so this falls through to the "ignore" case below) - { - // mute the sender of this invite - if (!LLMuteList::getInstance()->isMuted(payload["caller_id"].asUUID())) - { - LLMute mute(payload["caller_id"].asUUID(), payload["caller_name"].asString(), LLMute::AGENT); - LLMuteList::getInstance()->add(mute); - } - } - /* FALLTHROUGH */ - - case 1: // decline - { - if (type == IM_SESSION_P2P_INVITE) - { - std::string s = payload["session_handle"].asString(); - LLVoiceClient::getInstance()->declineInvite(s); - } - else - { - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - LLSD data; - data["method"] = "decline invitation"; - data["session-id"] = session_id; - LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, - "Invitation declined.", - "Invitation decline failed."); - } - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - break; - } - - return false; -} - // // Member Functions // @@ -3178,7 +3082,7 @@ void LLIMMgr::addMessage( { fixed_session_name = av_name.getDisplayName(); } - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg); + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, LLSD(), is_offline_msg); LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); if (session) @@ -3338,22 +3242,11 @@ void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id) } LLUUID LLIMMgr::addP2PSession(const std::string& name, - const LLUUID& other_participant_id, - const std::string& voice_session_handle, - const std::string& caller_uri) + const LLUUID& other_participant_id, + const LLSD& voice_channel_info) { - LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id, true); - - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - if (speaker_mgr) - { - LLVoiceChannelP2P* voice_channel = dynamic_cast(speaker_mgr->getVoiceChannel()); - if (voice_channel) - { - voice_channel->setSessionHandle(voice_session_handle, caller_uri); - } - } - return session_id; + LL_WARNS("Voice") << "ADD P2P VOICE CHANNEL INFO: " << voice_channel_info << LL_ENDL; + return addSession(name, IM_NOTHING_SPECIAL, other_participant_id, voice_channel_info); } // This adds a session to the talk view. The name is the local name of @@ -3363,11 +3256,12 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name, LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, - const LLUUID& other_participant_id, bool voice) + const LLUUID& other_participant_id, + const LLSD& voiceChannelInfo) { std::vector ids; ids.push_back(other_participant_id); - LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voice); + LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voiceChannelInfo); return session_id; } @@ -3377,7 +3271,8 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const std::vector& ids, bool voice, + const std::vector& ids, + const LLSD& voiceChannelInfo, const LLUUID& floater_id) { if (ids.empty()) @@ -3423,7 +3318,7 @@ LLUUID LLIMMgr::addSession( //Notify observers that a session was added if (new_session) { - LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice); + LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voiceChannelInfo); } //Notifies observers that the session was already added else @@ -3484,9 +3379,7 @@ void LLIMMgr::inviteToSession( const std::string& caller_name, EInstantMessage type, EInvitationType inv_type, - const std::string& session_handle, - const std::string& session_uri, - const std::string& voice_server_type) + const LLSD& voice_channel_info) { std::string notify_box_type; // voice invite question is different from default only for group call (EXT-7118) @@ -3528,11 +3421,12 @@ void LLIMMgr::inviteToSession( payload["caller_name"] = caller_name; payload["type"] = type; payload["inv_type"] = inv_type; - payload["session_handle"] = session_handle; - payload["session_uri"] = session_uri; + payload["voice_channel_info"] = voice_channel_info; payload["notify_box_type"] = notify_box_type; payload["question_type"] = question_type; + LL_WARNS("Voice") << "INVITE PAYLOAD: " << payload << LL_ENDL; + //ignore invites from muted residents if (!is_linden) { @@ -3581,7 +3475,7 @@ void LLIMMgr::inviteToSession( fixed_session_name = av_name.getDisplayName(); } } - LLIMModel::getInstance()->newSession(session_id, fixed_session_name, IM_NOTHING_SPECIAL, caller_id, false, false); + LLIMModel::getInstance()->newSession(session_id, fixed_session_name, IM_NOTHING_SPECIAL, caller_id, LLSD(), false); } LLSD args; @@ -3810,7 +3704,6 @@ bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection dir { LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id); if (!voice_channel) return false; - voice_channel->setCallDirection(direction); voice_channel->activate(); return true; @@ -4207,7 +4100,6 @@ public: return; } - std::string voice_server_type = input["body"]["voice"].get("voice_server_type").asString(); BOOL session_type_p2p = input["body"]["voice"].get("p2p").asBoolean(); gIMMgr->inviteToSession( @@ -4217,9 +4109,7 @@ public: input["body"]["from_name"].asString(), session_type_p2p ? IM_SESSION_P2P_INVITE : IM_SESSION_INVITE, LLIMMgr::INVITATION_TYPE_VOICE, - LLStringUtil::null, // session_handle - LLStringUtil::null, - voice_server_type); + input["body"]["voice"]); } else if ( input["body"].has("immediate") ) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 49c73bd495..8a059e0756 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -80,7 +80,7 @@ public: } SType; LLIMSession(const LLUUID& session_id, const std::string& name, - const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg); + const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, const LLSD& voiceChannelInfo, bool has_offline_msg); virtual ~LLIMSession(); void sessionInitReplyReceived(const LLUUID& new_session_id); @@ -199,10 +199,10 @@ public: * @param name session name should not be empty, will return false if empty */ bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, - const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false); + const uuid_vec_t& ids, const LLSD& voiceChannelInfo = LLSD(), bool has_offline_msg = false); - bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false); + bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID &other_participant_id, + const LLSD &voiceChannelInfo = LLSD(), bool has_offline_msg = false); /** * Remove all session data associated with a session specified by session_id @@ -296,7 +296,7 @@ public: static void sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id); static bool sendStartSession(const LLUUID& temp_session_id, const LLUUID& other_participant_id, - const uuid_vec_t& ids, EInstantMessage dialog); + const uuid_vec_t& ids, EInstantMessage dialog, bool p2p_as_adhoc_call); static void sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing); static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id, const LLUUID& other_participant_id, EInstantMessage dialog); @@ -379,7 +379,8 @@ public: // session. LLUUID addSession(const std::string& name, EInstantMessage dialog, - const LLUUID& other_participant_id, bool voice = false); + const LLUUID& other_participant_id, + const LLSD& voiceChannelInfo = LLSD()); // Adds a session using a specific group of starting agents // the dialog type is assumed correct. Returns the uuid of the session. @@ -387,7 +388,8 @@ public: LLUUID addSession(const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const std::vector& ids, bool voice = false, + const std::vector &ids, + const LLSD& voiceChannelInfo = LLSD(), const LLUUID& floater_id = LLUUID::null); /** @@ -397,10 +399,7 @@ public: * @param caller_uri - sip URI of caller. It should be always be passed into the method to avoid * incorrect working of LLVoiceChannel instances. See EXT-2985. */ - LLUUID addP2PSession(const std::string& name, - const LLUUID& other_participant_id, - const std::string& voice_session_handle, - const std::string& caller_uri); + LLUUID addP2PSession(const std::string &name, const LLUUID &other_participant_id, const LLSD &voice_call_info); /** * Leave the session with session id. Send leave session notification @@ -415,10 +414,9 @@ public: const LLUUID& caller, const std::string& caller_name, EInstantMessage type, - EInvitationType inv_type, - const std::string& session_handle = LLStringUtil::null, - const std::string& session_uri = LLStringUtil::null, - const std::string& voice_server_type = LLStringUtil::null); + EInvitationType inv_type, + const LLSD &voice_channel_info = LLSD() + ); void processIMTypingStart(const LLUUID& from_id, const EInstantMessage im_type); void processIMTypingStop(const LLUUID& from_id, const EInstantMessage im_type); diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index ab255d5215..a06b50390f 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -276,7 +276,7 @@ void LLPanelGroup::changed(LLGroupChange gc) } // virtual -void LLPanelGroup::onChange(EStatusType status, const std::string &channelURI, bool proximal) +void LLPanelGroup::onChange(EStatusType status, const LLSD& channelInfo, bool proximal) { if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) { diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index be40b08a6d..3ca6426887 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -62,7 +62,7 @@ public: // Implements LLVoiceClientStatusObserver::onChange() to enable the call // button when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + /*virtual*/ void onChange(EStatusType status, const LLSD& channelInfo, bool proximal); void showNotice(const std::string& subject, const std::string& message, diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 13b52e97c5..27019badd2 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -733,7 +733,7 @@ BOOL LLPanelPeople::postBuild() } // virtual -void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI, bool proximal) +void LLPanelPeople::onChange(EStatusType status, const LLSD& channelInfo, bool proximal) { if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) { diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 14205cebe2..e4484b66c6 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -55,7 +55,7 @@ public: /*virtual*/ bool notifyChildren(const LLSD& info); // Implements LLVoiceClientStatusObserver::onChange() to enable call buttons // when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + /*virtual*/ void onChange(EStatusType status, const LLSD& channelInfo, bool proximal); // internals class Updater; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index ffbed778c1..132b710620 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -1446,7 +1446,7 @@ void LLPanelProfileSecondLife::changed(U32 mask) } // virtual, called by LLVoiceClient -void LLPanelProfileSecondLife::onChange(EStatusType status, const std::string &channelURI, bool proximal) +void LLPanelProfileSecondLife::onChange(EStatusType status, const LLSD& channelInfo, bool proximal) { if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) { diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index 11632a10ae..ea2bb25ac2 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -85,7 +85,7 @@ public: // Implements LLVoiceClientStatusObserver::onChange() to enable the call // button when voice is available - void onChange(EStatusType status, const std::string &channelURI, bool proximal) override; + void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override; void setAvatarId(const LLUUID& avatar_id) override; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index fee00eb6f4..5d7adb9613 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -614,7 +614,8 @@ BOOL LLVOAvatar::sShowAnimationDebug = FALSE; BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE; F32 LLVOAvatar::sLODFactor = 1.f; F32 LLVOAvatar::sPhysicsLODFactor = 1.f; -BOOL LLVOAvatar::sJointDebug = FALSE; +BOOL LLVOAvatar::sJointDebug = FALSE; +BOOL LLVOAvatar::sLipSyncEnabled = FALSE; F32 LLVOAvatar::sUnbakedTime = 0.f; F32 LLVOAvatar::sUnbakedUpdateTime = 0.f; F32 LLVOAvatar::sGreyTime = 0.f; @@ -1155,6 +1156,7 @@ void LLVOAvatar::initClass() LLControlAvatar::sRegionChangedSlot = gAgent.addRegionChangedCallback(&LLControlAvatar::onRegionChanged); sCloudTexture = LLViewerTextureManager::getFetchedTextureFromFile("cloud-particle.j2c"); + gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&LLVOAvatar::handleVOAvatarPrefsChanged, _2)); } @@ -1162,6 +1164,12 @@ void LLVOAvatar::cleanupClass() { } +bool LLVOAvatar::handleVOAvatarPrefsChanged(const LLSD &newvalue) +{ + sLipSyncEnabled = gSavedSettings.getBOOL("LipSyncEnabled"); + return true; +} + // virtual void LLVOAvatar::initInstance() { @@ -3072,7 +3080,7 @@ void LLVOAvatar::idleUpdateLipSync(bool voice_enabled) // Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync if ( voice_enabled && mLastRezzedStatus > 0 // no point updating lip-sync for clouds - && (LLVoiceClient::getInstance()->lipSyncEnabled()) + && sLipSyncEnabled && LLVoiceClient::getInstance()->getIsSpeaking( mID ) ) { F32 ooh_morph_amount = 0.0f; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 48bfd5293a..87c9d468a2 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -106,9 +106,10 @@ public: virtual void markDead(); static void initClass(); // Initialize data that's only init'd once per class. static void cleanupClass(); // Cleanup data that's only init'd once per class. - virtual void initInstance(); // Called after construction to initialize the class. + virtual void initInstance(); // Called after construction to initialize the class. protected: virtual ~LLVOAvatar(); + static bool LLVOAvatar::handleVOAvatarPrefsChanged(const LLSD &newvalue); /** Initialization ** ** @@ -365,6 +366,7 @@ public: static F32 sLODFactor; // user-settable LOD factor static F32 sPhysicsLODFactor; // user-settable physics LOD factor static BOOL sJointDebug; // output total number of joints being touched for each avatar + static BOOL sLipSyncEnabled; static LLPointer sCloudTexture; diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 4ca876bed1..10cccb3671 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -39,7 +39,6 @@ #include "llcorehttputil.h" LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; -LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; LLVoiceChannel::channel_changed_signal_t LLVoiceChannel::sCurrentVoiceChannelChangedSignal; @@ -89,29 +88,18 @@ LLVoiceChannel::~LLVoiceChannel() } sVoiceChannelMap.erase(mSessionID); - sVoiceChannelURIMap.erase(mURI); } -void LLVoiceChannel::setChannelInfo( - const std::string& uri, - const std::string& credentials) +void LLVoiceChannel::setChannelInfo(const LLSD &channelInfo) { - setURI(uri); - - mCredentials = credentials; + mChannelInfo = channelInfo; if (mState == STATE_NO_CHANNEL_INFO) { - if (mURI.empty()) - { - LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); - LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL; - deactivate(); - } - else if (mCredentials.empty()) + if (mChannelInfo.isUndefined()) { LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); - LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL; + LL_WARNS("Voice") << "Received empty channel info for channel " << mSessionName << LL_ENDL; deactivate(); } else @@ -130,11 +118,17 @@ void LLVoiceChannel::setChannelInfo( } } -void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) +void LLVoiceChannel::onChange(EStatusType type, const LLSD& channelInfo, bool proximal) { - if (channelURI != mURI) + LL_WARNS("Voice") << channelInfo << LL_ENDL; + LL_WARNS("Voice") << mChannelInfo << LL_ENDL; + if (mChannelInfo.isUndefined()) { - return; + mChannelInfo = channelInfo; + } + if (!LLVoiceClient::getInstance()->compareChannels(mChannelInfo, channelInfo)) + { + return; } if (type < BEGIN_ERROR_STATUS) @@ -193,7 +187,7 @@ void LLVoiceChannel::handleError(EStatusType type) BOOL LLVoiceChannel::isActive() { // only considered active when currently bound channel matches what our channel - return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; + return callStarted() && LLVoiceClient::getInstance()->isCurrentChannel(mChannelInfo); } BOOL LLVoiceChannel::callStarted() @@ -246,10 +240,8 @@ void LLVoiceChannel::activate() // activating the proximal channel between IM calls LLVoiceChannel* old_channel = sCurrentVoiceChannel; sCurrentVoiceChannel = this; - mCallDialogPayload["old_channel_name"] = ""; if (old_channel) { - mCallDialogPayload["old_channel_name"] = old_channel->getSessionName(); old_channel->deactivate(); } } @@ -257,7 +249,7 @@ void LLVoiceChannel::activate() if (mState == STATE_NO_CHANNEL_INFO) { // responsible for setting status to active - getChannelInfo(); + requestChannelInfo(); } else { @@ -270,7 +262,7 @@ void LLVoiceChannel::activate() sCurrentVoiceChannelChangedSignal(this->mSessionID); } -void LLVoiceChannel::getChannelInfo() +void LLVoiceChannel::requestChannelInfo() { // pretend we have everything we need if (sCurrentVoiceChannel == this) @@ -293,20 +285,6 @@ LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) } } -//static -LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) -{ - voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); - if (found_it == sVoiceChannelURIMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel() { return sCurrentVoiceChannel; @@ -319,13 +297,6 @@ void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); } -void LLVoiceChannel::setURI(std::string uri) -{ - sVoiceChannelURIMap.erase(mURI); - mURI = uri; - sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); -} - void LLVoiceChannel::setState(EState state) { switch(state) @@ -436,10 +407,8 @@ void LLVoiceChannelGroup::activate() if (callStarted()) { // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials, - mHangupOnLastLeave); + LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo, + mHangupOnLastLeave); if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel { @@ -473,7 +442,7 @@ void LLVoiceChannelGroup::activate() } } -void LLVoiceChannelGroup::getChannelInfo() +void LLVoiceChannelGroup::requestChannelInfo() { LLViewerRegion* region = gAgent.getRegion(); if (region) @@ -485,17 +454,13 @@ void LLVoiceChannelGroup::getChannelInfo() } } -void LLVoiceChannelGroup::setChannelInfo( - const std::string& uri, - const std::string& credentials) +void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo) { - setURI(uri); - - mCredentials = credentials; + mChannelInfo = channelInfo; if (mState == STATE_NO_CHANNEL_INFO) { - if(!mURI.empty() && !mCredentials.empty()) + if(mChannelInfo.isUndefined()) { setState(STATE_READY); @@ -518,10 +483,8 @@ void LLVoiceChannelGroup::setChannelInfo( else if ( mIsRetrying ) { // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials, - mHangupOnLastLeave); + LLVoiceClient::getInstance()->setNonSpatialChannel(channelInfo, + mHangupOnLastLeave); } } @@ -530,30 +493,6 @@ void LLVoiceChannelGroup::handleStatusChange(EStatusType type) // status updates switch(type) { - case STATUS_LEFT_CHANNEL: - { - if (!LLVoiceClient::getInstance()->hasP2PInterface()) - { - // we're using group/adhoc for p2p - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - // *TODO: use it to show DECLINE voice notification - if (mState == STATE_RINGING) - { - // other user declined call - LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs); - } - else - { - // other user hung up, so we didn't end the call - mCallEndedByAgent = false; - } - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - return; - } - } case STATUS_JOINED: mRetries = 3; mIsRetrying = FALSE; @@ -583,7 +522,7 @@ void LLVoiceChannelGroup::handleError(EStatusType status) mIsRetrying = TRUE; mIgnoreNextSessionLeave = TRUE; - getChannelInfo(); + requestChannelInfo(); return; } else @@ -678,15 +617,12 @@ void LLVoiceChannelGroup::voiceCallCapCoro(std::string url) LLSD::map_const_iterator iter; for (iter = result.beginMap(); iter != result.endMap(); ++iter) { - LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " + LL_DEBUGS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " << iter->first << LL_ENDL; } - LL_INFOS("Voice") << "LLVoiceCallCapResponder::result got " << result << LL_ENDL; - - channelp->setChannelInfo( - result["voice_credentials"]["channel_uri"].asString(), - result["voice_credentials"]["channel_credentials"].asString()); + LL_INFOS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " << result << LL_ENDL; + channelp->setChannelInfo(result["voice_credentials"]); } @@ -710,13 +646,14 @@ void LLVoiceChannelProximal::activate() if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED)) { // we're connected to a non-spatial channel, so disconnect. - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); } + LLVoiceClient::getInstance()->activateSpatialChannel(true); LLVoiceChannel::activate(); } -void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) +void LLVoiceChannelProximal::onChange(EStatusType type, const LLSD& channelInfo, bool proximal) { if (!proximal) { @@ -786,19 +723,22 @@ void LLVoiceChannelProximal::deactivate() { setState(STATE_HUNG_UP); } + LLVoiceClient::getInstance()->activateSpatialChannel(false); } // // LLVoiceChannelP2P // -LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : +LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID &session_id, + const std::string &session_name, + const LLUUID &other_user_id, + LLVoiceP2POutgoingCallInterface* outgoing_call_interface) : LLVoiceChannelGroup(session_id, session_name, true), mOtherUserID(other_user_id), - mReceivedCall(FALSE) + mReceivedCall(FALSE), + mOutgoingCallInterface(outgoing_call_interface) { - // make sure URI reflects encoded version of other user's agent id - setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); } void LLVoiceChannelP2P::handleStatusChange(EStatusType type) @@ -865,23 +805,23 @@ void LLVoiceChannelP2P::activate() if (callStarted()) { // no session handle yet, we're starting the call - if (mSessionHandle.empty()) + if (mIncomingCallInterface == nullptr) { mReceivedCall = FALSE; - LLVoiceClient::getInstance()->callUser(mOtherUserID); + mOutgoingCallInterface->callUser(mOtherUserID); } // otherwise answering the call else { - if (!LLVoiceClient::getInstance()->answerInvite(mSessionHandle)) + if (!mIncomingCallInterface->answerInvite()) { mCallEndedByAgent = false; - mSessionHandle.clear(); + mIncomingCallInterface.reset(); handleError(ERROR_UNKNOWN); return; } - // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. - mSessionHandle.clear(); + // using the incoming call interface invalidates it. Clear it out here so we can't reuse it by accident. + mIncomingCallInterface.reset(); } // Add the party to the list of people with which we've recently interacted. @@ -895,7 +835,17 @@ void LLVoiceChannelP2P::activate() } } -void LLVoiceChannelP2P::getChannelInfo() +void LLVoiceChannelP2P::deactivate() +{ + if (callStarted()) + { + mOutgoingCallInterface->hangup(); + } + LLVoiceChannel::deactivate(); +} + + +void LLVoiceChannelP2P::requestChannelInfo() { // pretend we have everything we need, since P2P doesn't use channel info if (sCurrentVoiceChannel == this) @@ -905,8 +855,9 @@ void LLVoiceChannelP2P::getChannelInfo() } // receiving session from other user who initiated call -void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI) +void LLVoiceChannelP2P::setChannelInfo(const LLSD& channel_info) { + mChannelInfo = channel_info; BOOL needs_activate = FALSE; if (callStarted()) { @@ -921,28 +872,16 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s { // we are active and have priority, invite the other user again // under the assumption they will join this new session - mSessionHandle.clear(); - LLVoiceClient::getInstance()->callUser(mOtherUserID); + mOutgoingCallInterface->callUser(mOtherUserID); return; } } - - mSessionHandle = handle; - - // The URI of a p2p session should always be the other end's SIP URI. - if(!inURI.empty()) - { - setURI(inURI); - } - else - { - LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL; - // See LLVoiceClient::sessionAddedEvent() - setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); - } mReceivedCall = TRUE; - + if (!channel_info.isUndefined()) + { + mIncomingCallInterface = LLVoiceClient::getInstance()->getIncomingCallInterface(channel_info); + } if (needs_activate) { activate(); diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 84ef3e7c08..dd24c72891 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -66,16 +66,14 @@ public: LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); virtual ~LLVoiceChannel(); - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + virtual void onChange(EStatusType status, const LLSD& channelInfo, bool proximal); virtual void handleStatusChange(EStatusType status); virtual void handleError(EStatusType status); virtual void deactivate(); virtual void activate(); - virtual void setChannelInfo( - const std::string& uri, - const std::string& credentials); - virtual void getChannelInfo(); + virtual void setChannelInfo(const LLSD &channelInfo); + virtual void requestChannelInfo(); virtual BOOL isActive(); virtual BOOL callStarted(); @@ -96,7 +94,6 @@ public: EDirection getCallDirection() {return mCallDirection;} static LLVoiceChannel* getChannelByID(const LLUUID& session_id); - static LLVoiceChannel* getChannelByURI(std::string uri); static LLVoiceChannel* getCurrentVoiceChannel(); static void initClass(); @@ -110,29 +107,24 @@ protected: * Use this method if you want mStateChangedCallback to be executed while state is changed */ void doSetState(const EState& state); - void setURI(std::string uri); // there can be two directions INCOMING and OUTGOING EDirection mCallDirection; - std::string mURI; - std::string mCredentials; LLUUID mSessionID; EState mState; std::string mSessionName; - LLSD mNotifyArgs; - LLSD mCallDialogPayload; + LLSD mNotifyArgs; + LLSD mChannelInfo; // true if call was ended by agent bool mCallEndedByAgent; - BOOL mIgnoreNextSessionLeave; + bool mIgnoreNextSessionLeave; LLHandle mLoginNotificationHandle; typedef std::map voice_channel_map_t; static voice_channel_map_t sVoiceChannelMap; - typedef std::map voice_channel_map_uri_t; - static voice_channel_map_uri_t sVoiceChannelURIMap; - + static LLVoiceChannel* sProximalVoiceChannel; static LLVoiceChannel* sCurrentVoiceChannel; static LLVoiceChannel* sSuspendedVoiceChannel; static BOOL sSuspended; @@ -146,14 +138,12 @@ class LLVoiceChannelGroup : public LLVoiceChannel public: LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, BOOL hangup_on_last_leave); - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ void activate(); - /*virtual*/ void deactivate(); - /*vritual*/ void setChannelInfo( - const std::string& uri, - const std::string& credentials); - /*virtual*/ void getChannelInfo(); + void handleStatusChange(EStatusType status) override; + void handleError(EStatusType status) override; + void activate() override; + void deactivate() override; + void setChannelInfo(const LLSD &channelInfo) override; + void requestChannelInfo() override; protected: virtual void setState(EState state); @@ -171,26 +161,30 @@ class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton(gSavedSettings, "VoiceMorphingEnabled", true)), mVoiceEffectDefault(LLCachedControl(gSavedPerAccountSettings, "VoiceEffectDefault", "00000000-0000-0000-0000-000000000000")), @@ -141,7 +156,7 @@ LLVoiceClient::LLVoiceClient(LLPumpIO *pump) LLVoiceClient::~LLVoiceClient() { - llassert(!mVoiceModule); + llassert(!mSpatialVoiceModule); } void LLVoiceClient::init(LLPumpIO *pump) @@ -154,9 +169,9 @@ void LLVoiceClient::init(LLPumpIO *pump) void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) { - mUserID = user_id; - mAgentID = agentID; gAgent.addRegionChangedCallback(boost::bind(&LLVoiceClient::onRegionChanged, this)); + LLWebRTCVoiceClient::getInstance()->userAuthorized(user_id, agentID); + LLVivoxVoiceClient::getInstance()->userAuthorized(user_id, agentID); } void LLVoiceClient::onRegionChanged() @@ -166,7 +181,7 @@ void LLVoiceClient::onRegionChanged() { LLSD simulatorFeatures; region->getSimulatorFeatures(simulatorFeatures); - setVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); } else if (region) { @@ -187,44 +202,57 @@ void LLVoiceClient::onSimulatorFeaturesReceived(const LLUUID& region_id) { LLSD simulatorFeatures; region->getSimulatorFeatures(simulatorFeatures); - setVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); } } -void LLVoiceClient::setVoiceModule(const std::string& voice_server_type) +void LLVoiceClient::setSpatialVoiceModule(const std::string &voice_server_type) { - if (voice_server_type == "vivox" || voice_server_type.empty()) - { - mVoiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance(); - } - else if (voice_server_type == "webrtc") - { - mVoiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); - } - else + LLVoiceModuleInterface *module = getVoiceModule(voice_server_type); + if (!module) { - LLNotificationsUtil::add("VoiceVersionMismatch"); - mVoiceModule = nullptr; return; } - mVoiceModule->userAuthorized(mUserID, mAgentID); - updateSettings(); + if (module != mSpatialVoiceModule) + { + if (inProximalChannel()) + { + mSpatialVoiceModule->processChannels(false); + } + module->processChannels(true); + mSpatialVoiceModule = module; + mSpatialVoiceModule->updateSettings(); + } } - +void LLVoiceClient::setNonSpatialVoiceModule(const std::string &voice_server_type) +{ + mNonSpatialVoiceModule = getVoiceModule(voice_server_type); + if (!mNonSpatialVoiceModule) + { + // we don't have a non-spatial voice module, + // so revert to spatial. + if (mSpatialVoiceModule) + { + mSpatialVoiceModule->processChannels(true); + } + return; + } + mNonSpatialVoiceModule->updateSettings(); +} void LLVoiceClient::setHidden(bool hidden) { - if (mVoiceModule) + if (mSpatialVoiceModule) { - mVoiceModule->setHidden(hidden); + mSpatialVoiceModule->setHidden(hidden); } } void LLVoiceClient::terminate() { - if (mVoiceModule) mVoiceModule->terminate(); - mVoiceModule = NULL; + if (mSpatialVoiceModule) mSpatialVoiceModule->terminate(); + mSpatialVoiceModule = NULL; m_servicePump = NULL; // Shutdown speaker volume storage before LLSingletonBase::deleteAll() does it @@ -236,9 +264,9 @@ void LLVoiceClient::terminate() const LLVoiceVersionInfo LLVoiceClient::getVersion() { - if (mVoiceModule) + if (mSpatialVoiceModule) { - return mVoiceModule->getVersion(); + return mSpatialVoiceModule->getVersion(); } else { @@ -258,10 +286,8 @@ void LLVoiceClient::updateSettings() updateMicMuteLogic(); - if (mVoiceModule) - { - mVoiceModule->updateSettings(); - } + LLWebRTCVoiceClient::getInstance()->updateSettings(); + LLVivoxVoiceClient::getInstance()->updateSettings(); } //-------------------------------------------------- @@ -269,117 +295,76 @@ void LLVoiceClient::updateSettings() void LLVoiceClient::tuningStart() { - if (mVoiceModule) mVoiceModule->tuningStart(); + LLWebRTCVoiceClient::getInstance()->tuningStart(); + LLVivoxVoiceClient::getInstance()->tuningStart(); } void LLVoiceClient::tuningStop() { - if (mVoiceModule) mVoiceModule->tuningStop(); + LLWebRTCVoiceClient::getInstance()->tuningStop(); + LLVivoxVoiceClient::getInstance()->tuningStop(); + } bool LLVoiceClient::inTuningMode() { - if (mVoiceModule) - { - return mVoiceModule->inTuningMode(); - } - else - { - return false; - } + return LLWebRTCVoiceClient::getInstance()->inTuningMode(); } void LLVoiceClient::tuningSetMicVolume(float volume) { - if (mVoiceModule) mVoiceModule->tuningSetMicVolume(volume); + LLWebRTCVoiceClient::getInstance()->tuningSetMicVolume(volume); } void LLVoiceClient::tuningSetSpeakerVolume(float volume) { - if (mVoiceModule) mVoiceModule->tuningSetSpeakerVolume(volume); + LLWebRTCVoiceClient::getInstance()->tuningSetSpeakerVolume(volume); } float LLVoiceClient::tuningGetEnergy(void) { - if (mVoiceModule) - { - return mVoiceModule->tuningGetEnergy(); - } - else - { - return 0.0; - } + return LLWebRTCVoiceClient::getInstance()->tuningGetEnergy(); } - //------------------------------------------------ // devices bool LLVoiceClient::deviceSettingsAvailable() -{ - if (mVoiceModule) - { - return mVoiceModule->deviceSettingsAvailable(); - } - else - { - return false; - } +{ + return LLWebRTCVoiceClient::getInstance()->deviceSettingsAvailable(); } bool LLVoiceClient::deviceSettingsUpdated() -{ - if (mVoiceModule) - { - return mVoiceModule->deviceSettingsUpdated(); - } - else - { - return false; - } +{ + return LLWebRTCVoiceClient::getInstance()->deviceSettingsUpdated(); } void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) { - if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList); + LLWebRTCVoiceClient::getInstance()->refreshDeviceLists(clearCurrentList); } void LLVoiceClient::setCaptureDevice(const std::string& name) { - if (mVoiceModule) mVoiceModule->setCaptureDevice(name); - + LLWebRTCVoiceClient::getInstance()->setCaptureDevice(name); + LLVivoxVoiceClient::getInstance()->setCaptureDevice(name); } void LLVoiceClient::setRenderDevice(const std::string& name) { - if (mVoiceModule) mVoiceModule->setRenderDevice(name); + LLWebRTCVoiceClient::getInstance()->setRenderDevice(name); + LLVivoxVoiceClient::getInstance()->setRenderDevice(name); } const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() { - static LLVoiceDeviceList nullCaptureDevices; - if (mVoiceModule) - { - return mVoiceModule->getCaptureDevices(); - } - else - { - return nullCaptureDevices; - } + return LLWebRTCVoiceClient::getInstance()->getCaptureDevices(); } const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() -{ - static LLVoiceDeviceList nullRenderDevices; - if (mVoiceModule) - { - return mVoiceModule->getRenderDevices(); - } - else - { - return nullRenderDevices; - } +{ + return LLWebRTCVoiceClient::getInstance()->getRenderDevices(); } @@ -388,84 +373,51 @@ const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() void LLVoiceClient::getParticipantList(std::set &participants) { - if (mVoiceModule) - { - mVoiceModule->getParticipantList(participants); - } - else - { - participants = std::set(); - } + LLWebRTCVoiceClient::getInstance()->getParticipantList(participants); + LLVivoxVoiceClient::getInstance()->getParticipantList(participants); } bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) { - if(mVoiceModule) - { - return mVoiceModule->isParticipant(speaker_id); - } - return false; + return LLWebRTCVoiceClient::getInstance()->isParticipant(speaker_id) || LLVivoxVoiceClient::getInstance()->isParticipant(speaker_id); } //-------------------------------------------------- // text chat - BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id) { - if (mVoiceModule) + if (mSpatialVoiceModule) { - return mVoiceModule->isSessionTextIMPossible(id); + return mSpatialVoiceModule->isSessionTextIMPossible(id); } else { return FALSE; - } -} - -BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) -{ - if (mVoiceModule) - { - return mVoiceModule->isSessionCallBackPossible(id); } - else - { - return FALSE; - } } -/* obsolete -BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) +BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) { - if (mVoiceModule) + if (mSpatialVoiceModule) { - return mVoiceModule->sendTextMessage(participant_id, message); + return mSpatialVoiceModule->isSessionCallBackPossible(id); } else { return FALSE; } } -*/ - -void LLVoiceClient::endUserIMSession(const LLUUID& participant_id) -{ - if (mVoiceModule) - { - // mVoiceModule->endUserIMSession(participant_id); // A SLim leftover - } -} //---------------------------------------------- // channels bool LLVoiceClient::inProximalChannel() { - if (mVoiceModule) + if (mSpatialVoiceModule) { - return mVoiceModule->inProximalChannel(); + return mSpatialVoiceModule->inProximalChannel(); } else { @@ -474,112 +426,111 @@ bool LLVoiceClient::inProximalChannel() } void LLVoiceClient::setNonSpatialChannel( - const std::string &uri, - const std::string &credentials, + const LLSD& channelInfo, bool hangup_on_last_leave) { - if (mVoiceModule) + setNonSpatialVoiceModule(channelInfo["voice_server_type"].asString()); + if (mSpatialVoiceModule) { - mVoiceModule->setNonSpatialChannel(uri, credentials, hangup_on_last_leave); + mSpatialVoiceModule->processChannels(false); } + if (mNonSpatialVoiceModule) + { + mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, hangup_on_last_leave); + mNonSpatialVoiceModule->processChannels(true); + } } -void LLVoiceClient::setSpatialChannel( - const std::string &uri, - const std::string &credentials) +void LLVoiceClient::setSpatialChannel(const LLSD &channelInfo) { - if (mVoiceModule) + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) { - mVoiceModule->setSpatialChannel(uri, credentials); + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); } -} - -void LLVoiceClient::leaveNonSpatialChannel() -{ - if (mVoiceModule) + if (mNonSpatialVoiceModule) { - mVoiceModule->leaveNonSpatialChannel(); + mNonSpatialVoiceModule->leaveNonSpatialChannel(); + mNonSpatialVoiceModule->processChannels(false); + mNonSpatialVoiceModule = nullptr; } -} - -void LLVoiceClient::leaveChannel(void) -{ - if (mVoiceModule) + if (mSpatialVoiceModule) { - mVoiceModule->leaveChannel(); + mSpatialVoiceModule->setSpatialChannel(channelInfo); + mSpatialVoiceModule->processChannels(true); } } -std::string LLVoiceClient::getCurrentChannel() +void LLVoiceClient::leaveNonSpatialChannel() { - if (mVoiceModule) - { - return mVoiceModule->getCurrentChannel(); - } - else + if (mNonSpatialVoiceModule) + { + mNonSpatialVoiceModule->leaveNonSpatialChannel(); + mNonSpatialVoiceModule->processChannels(false); + mNonSpatialVoiceModule = nullptr; + } + if (mSpatialVoiceModule) { - return std::string(); + mSpatialVoiceModule->processChannels(true); + ; } } - -//--------------------------------------- -// invitations - -bool LLVoiceClient::hasP2PInterface() +void LLVoiceClient::activateSpatialChannel(bool activate) { - if (mVoiceModule) - return mVoiceModule->hasP2PInterface(); - return false; + if (mSpatialVoiceModule) + { + mSpatialVoiceModule->processChannels(activate); + } } -void LLVoiceClient::callUser(const LLUUID &uuid) +bool LLVoiceClient::isCurrentChannel(const LLSD& channelInfo) { - if (mVoiceModule) mVoiceModule->callUser(uuid); + return LLWebRTCVoiceClient::getInstance()->isCurrentChannel(channelInfo) || + LLVivoxVoiceClient::getInstance()->isCurrentChannel(channelInfo); } -bool LLVoiceClient::isValidChannel(std::string &session_handle) +bool LLVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) { - if (mVoiceModule) - { - return mVoiceModule->isValidChannel(session_handle); - } - else - { - return false; - } + return LLWebRTCVoiceClient::getInstance()->compareChannels(channelInfo1, channelInfo2) || + LLVivoxVoiceClient::getInstance()->compareChannels(channelInfo1, channelInfo2); } -bool LLVoiceClient::answerInvite(std::string &channelHandle) +LLVoiceP2PIncomingCallInterfacePtr LLVoiceClient::getIncomingCallInterface(const LLSD& voice_call_info) { - if (mVoiceModule) - { - return mVoiceModule->answerInvite(channelHandle); - } - else + LLVoiceModuleInterface *module = getVoiceModule(voice_call_info["voice_server_type"]); + if (module) { - return false; + return module->getIncomingCallInterface(voice_call_info); } + return nullptr; + } -void LLVoiceClient::declineInvite(std::string &channelHandle) +//--------------------------------------- +// outgoing calls +LLVoiceP2POutgoingCallInterface *LLVoiceClient::getOutgoingCallInterface(const LLSD& voiceChannelInfo) { - if (mVoiceModule) mVoiceModule->declineInvite(channelHandle); + LLVoiceModuleInterface *module = getVoiceModule(voiceChannelInfo["voice_server_type"].asString()); + return dynamic_cast(module); } - //------------------------------------------ // Volume/gain void LLVoiceClient::setVoiceVolume(F32 volume) { - if (mVoiceModule) mVoiceModule->setVoiceVolume(volume); + LLWebRTCVoiceClient::getInstance()->setVoiceVolume(volume); + LLVivoxVoiceClient::getInstance()->setVoiceVolume(volume); } -void LLVoiceClient::setMicGain(F32 volume) +void LLVoiceClient::setMicGain(F32 gain) { - if (mVoiceModule) mVoiceModule->setMicGain(volume); + LLWebRTCVoiceClient::getInstance()->setMicGain(gain); + LLVivoxVoiceClient::getInstance()->setMicGain(gain); } @@ -588,22 +539,13 @@ void LLVoiceClient::setMicGain(F32 volume) bool LLVoiceClient::voiceEnabled() { - if (mVoiceModule) - { - return mVoiceModule->voiceEnabled(); - } - else - { - return false; - } + return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice") && !gNonInteractive; } void LLVoiceClient::setVoiceEnabled(bool enabled) { - if (mVoiceModule) - { - mVoiceModule->setVoiceEnabled(enabled); - } + LLWebRTCVoiceClient::getInstance()->setVoiceEnabled(enabled); + LLVivoxVoiceClient::getInstance()->setVoiceEnabled(enabled); } void LLVoiceClient::updateMicMuteLogic() @@ -622,25 +564,8 @@ void LLVoiceClient::updateMicMuteLogic() // Either of these always overrides any other PTT setting. new_mic_mute = true; } - - if (mVoiceModule) mVoiceModule->setMuteMic(new_mic_mute); -} - -void LLVoiceClient::setLipSyncEnabled(BOOL enabled) -{ - if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled); -} - -BOOL LLVoiceClient::lipSyncEnabled() -{ - if (mVoiceModule) - { - return mVoiceModule->lipSyncEnabled(); - } - else - { - return false; - } + LLWebRTCVoiceClient::getInstance()->setMuteMic(new_mic_mute); + LLVivoxVoiceClient::getInstance()->setMuteMic(new_mic_mute); } void LLVoiceClient::setMuteMic(bool muted) @@ -726,9 +651,13 @@ void LLVoiceClient::toggleUserPTTState(void) BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) { - if (mVoiceModule) + if (mNonSpatialVoiceModule) + { + return mNonSpatialVoiceModule->getVoiceEnabled(id); + } + else if (mSpatialVoiceModule) { - return mVoiceModule->getVoiceEnabled(id); + return mSpatialVoiceModule->getVoiceEnabled(id); } else { @@ -738,9 +667,13 @@ BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) std::string LLVoiceClient::getDisplayName(const LLUUID& id) { - if (mVoiceModule) + if (mNonSpatialVoiceModule) + { + return mNonSpatialVoiceModule->getDisplayName(id); + } + else if (mSpatialVoiceModule) { - return mVoiceModule->getDisplayName(id); + return mSpatialVoiceModule->getDisplayName(id); } else { @@ -750,47 +683,35 @@ std::string LLVoiceClient::getDisplayName(const LLUUID& id) bool LLVoiceClient::isVoiceWorking() const { - if (mVoiceModule) - { - return mVoiceModule->isVoiceWorking(); - } - return false; + return LLVivoxVoiceClient::getInstance()->isVoiceWorking() || LLWebRTCVoiceClient::getInstance()->isVoiceWorking(); } BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id) { - if (mVoiceModule) - { - return mVoiceModule->isParticipantAvatar(id); - } - else - { - return FALSE; - } + return TRUE; } BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id) { - return FALSE; + return FALSE; } BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) { - if (mVoiceModule) - { - return mVoiceModule->getIsSpeaking(id); - } - else - { - return FALSE; - } + return LLVivoxVoiceClient::getInstance()->getIsSpeaking(id) || LLWebRTCVoiceClient::getInstance()->getIsSpeaking(id); } BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) { - if (mVoiceModule) + // don't bother worrying about p2p calls, as + // p2p calls don't have mute. + if (mNonSpatialVoiceModule) + { + return mNonSpatialVoiceModule->getIsModeratorMuted(id); + } + else if (mSpatialVoiceModule) { - return mVoiceModule->getIsModeratorMuted(id); + return mSpatialVoiceModule->getIsModeratorMuted(id); } else { @@ -799,22 +720,21 @@ BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) } F32 LLVoiceClient::getCurrentPower(const LLUUID& id) -{ - if (mVoiceModule) - { - return mVoiceModule->getCurrentPower(id); - } - else - { - return 0.0; - } +{ + return std::fmax(LLVivoxVoiceClient::getInstance()->getCurrentPower(id), LLWebRTCVoiceClient::getInstance()->getCurrentPower(id)); } BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) { - if (mVoiceModule) + // don't bother worrying about p2p calls, as + // p2p calls don't have mute. + if (mNonSpatialVoiceModule) + { + return mNonSpatialVoiceModule->getOnMuteList(id); + } + else if (mSpatialVoiceModule) { - return mVoiceModule->getOnMuteList(id); + return mSpatialVoiceModule->getOnMuteList(id); } else { @@ -824,19 +744,13 @@ BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) F32 LLVoiceClient::getUserVolume(const LLUUID& id) { - if (mVoiceModule) - { - return mVoiceModule->getUserVolume(id); - } - else - { - return 0.0; - } + return std::fmax(LLVivoxVoiceClient::getInstance()->getUserVolume(id), LLWebRTCVoiceClient::getInstance()->getUserVolume(id)); } void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { - if (mVoiceModule) mVoiceModule->setUserVolume(id, volume); + LLWebRTCVoiceClient::getInstance()->setUserVolume(id, volume); + LLVivoxVoiceClient::getInstance()->setUserVolume(id, volume); } //-------------------------------------------------- @@ -844,48 +758,49 @@ void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) { - if (mVoiceModule) mVoiceModule->addObserver(observer); + LLVivoxVoiceClient::getInstance()->addObserver(observer); + LLWebRTCVoiceClient::getInstance()->addObserver(observer); } void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) { - if (mVoiceModule) - { - mVoiceModule->removeObserver(observer); - } + LLVivoxVoiceClient::getInstance()->removeObserver(observer); + LLWebRTCVoiceClient::getInstance()->removeObserver(observer); } void LLVoiceClient::addObserver(LLFriendObserver* observer) { - if (mVoiceModule) mVoiceModule->addObserver(observer); + LLVivoxVoiceClient::getInstance()->addObserver(observer); + LLWebRTCVoiceClient::getInstance()->addObserver(observer); } void LLVoiceClient::removeObserver(LLFriendObserver* observer) { - if (mVoiceModule) - { - mVoiceModule->removeObserver(observer); - } + LLVivoxVoiceClient::getInstance()->removeObserver(observer); + LLWebRTCVoiceClient::getInstance()->removeObserver(observer); } void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { - if (mVoiceModule) mVoiceModule->addObserver(observer); + LLVivoxVoiceClient::getInstance()->addObserver(observer); + LLWebRTCVoiceClient::getInstance()->addObserver(observer); } void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) { - if (mVoiceModule) - { - mVoiceModule->removeObserver(observer); - } + LLVivoxVoiceClient::getInstance()->removeObserver(observer); + LLWebRTCVoiceClient::getInstance()->removeObserver(observer); } std::string LLVoiceClient::sipURIFromID(const LLUUID &id) { - if (mVoiceModule) + if (mNonSpatialVoiceModule) { - return mVoiceModule->sipURIFromID(id); + return mNonSpatialVoiceModule->sipURIFromID(id); + } + else if (mSpatialVoiceModule) + { + return mSpatialVoiceModule->sipURIFromID(id); } else { @@ -895,7 +810,7 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id) LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const { - return getVoiceEffectEnabled() ? dynamic_cast(mVoiceModule) : NULL; + return getVoiceEffectEnabled() ? dynamic_cast(mSpatialVoiceModule) : NULL; } /////////////////// @@ -983,22 +898,8 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode //local parcel id to make sure it's for the same parcel //we believe we're in if ( body.has("voice_credentials") ) - { - LLSD voice_credentials = body["voice_credentials"]; - std::string uri; - std::string credentials; - - if ( voice_credentials.has("channel_uri") ) - { - uri = voice_credentials["channel_uri"].asString(); - } - if ( voice_credentials.has("channel_credentials") ) - { - credentials = - voice_credentials["channel_credentials"].asString(); - } - - LLVoiceClient::getInstance()->setSpatialChannel(uri, credentials); + { + LLVoiceClient::getInstance()->setSpatialChannel(body["voice_credentials"]); } } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 4ed3cd5066..87b6469783 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -38,6 +38,7 @@ class LLVOAvatar; #include "llcallingcard.h" // for LLFriendObserver #include "llsecapi.h" #include "llcontrol.h" +#include // devices @@ -87,7 +88,7 @@ public: } EStatusType; virtual ~LLVoiceClientStatusObserver() { } - virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) = 0; + virtual void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) = 0; static std::string status2string(EStatusType inStatus); }; @@ -95,12 +96,45 @@ public: struct LLVoiceVersionInfo { std::string voiceServerType; + std::string internalVoiceServerType; int majorVersion; int minorVersion; std::string serverVersion; std::string mBuildVersion; }; +////////////////////////////////// +/// @class LLVoiceP2POutgoingCallInterface +/// @brief Outgoing call interface +/// +/// For providers that support P2P signaling (vivox) +///////////////////////////////// + +class LLVoiceP2POutgoingCallInterface +{ + public: + // initiate an outgoing call to a user + virtual void callUser(const LLUUID &agentID) = 0; + virtual void hangup() = 0; +}; + +////////////////////////////////// +/// @class LLVoiceP2PIncomingCallInterface +/// @brief Incoming call interface +/// +/// For providers that support P2P signaling (vivox) +///////////////////////////////// +class LLVoiceP2PIncomingCallInterface +{ + public: + virtual ~LLVoiceP2PIncomingCallInterface() {} + + virtual bool answerInvite() = 0; + virtual void declineInvite() = 0; +}; + +typedef boost::shared_ptr LLVoiceP2PIncomingCallInterfacePtr; + ////////////////////////////////// /// @class LLVoiceModuleInterface /// @brief Voice module interface @@ -170,32 +204,31 @@ public: // Note that gestures should only fire if this returns true. virtual bool inProximalChannel()=0; - virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials, + virtual void setNonSpatialChannel(const LLSD& channelInfo, bool hangup_on_last_leave = false)=0; - virtual bool setSpatialChannel(const std::string &uri, - const std::string &credentials)=0; - - virtual void leaveNonSpatialChannel()=0; + virtual bool setSpatialChannel(const LLSD& channelInfo)=0; - virtual void leaveChannel(void)=0; - - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - virtual std::string getCurrentChannel()=0; + virtual void leaveNonSpatialChannel() = 0; + virtual void processChannels(bool process) = 0; + + virtual bool isCurrentChannel(const LLSD &channelInfo) = 0; + virtual bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) = 0; + //@} ////////////////////////// - /// @name invitations + /// @name p2p //@{ - // start a voice channel with the specified user - virtual bool hasP2PInterface()=0; - virtual void callUser(const LLUUID &uuid)=0; - virtual bool isValidChannel(std::string& channelHandle)=0; - virtual bool answerInvite(std::string &channelHandle)=0; - virtual void declineInvite(std::string &channelHandle)=0; + + // initiate a call with a peer using the P2P interface, which only applies to some + // voice server types. Otherwise, a group call should be used for P2P + virtual LLVoiceP2POutgoingCallInterface* getOutgoingCallInterface() = 0; + + // an incoming call was received, and the incoming call dialogue is asking for an interface to + // answer or decline. + virtual LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) = 0; //@} ///////////////////////// @@ -208,10 +241,7 @@ public: ///////////////////////// /// @name enable disable voice and features //@{ - virtual bool voiceEnabled()=0; virtual void setVoiceEnabled(bool enabled)=0; - virtual void setLipSyncEnabled(BOOL enabled)=0; - virtual BOOL lipSyncEnabled()=0; virtual void setMuteMic(bool muted)=0; // Set the mute state of the local mic. //@} @@ -235,7 +265,6 @@ public: virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0; virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0; //virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; - virtual void endUserIMSession(const LLUUID &uuid)=0; //@} // authorize the user @@ -377,27 +406,24 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel(); - void setNonSpatialChannel( - const std::string &uri, - const std::string &credentials, - bool hangup_on_last_leave = false); + void setNonSpatialChannel(const LLSD& channelInfo, + bool hangup_on_last_leave = false); + + void setSpatialChannel(const LLSD &channelInfo); + + void activateSpatialChannel(bool activate); - void setSpatialChannel( - const std::string &uri, - const std::string &credentials); void leaveNonSpatialChannel(); - - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - std::string getCurrentChannel(); - // start a voice channel with the specified user - bool hasP2PInterface(); // true - can use the following. false - use conference/ad-hoc instead - void callUser(const LLUUID &uuid); - bool isValidChannel(std::string& channelHandle); - bool answerInvite(std::string &channelHandle); - void declineInvite(std::string &channelHandle); - void leaveChannel(void); // call this on logout or teleport begin - + + bool isCurrentChannel(const LLSD& channelInfo); + + bool compareChannels(const LLSD& channelInfo1, const LLSD& channelInfo2); + + // initiate a call with a peer using the P2P interface, which only applies to some + // voice server types. Otherwise, a group call should be used for P2P + LLVoiceP2POutgoingCallInterface* getOutgoingCallInterface(const LLSD& voiceChannelInfo); + + LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voiceCallInfo); ///////////////////////////// // Sending updates of current state @@ -407,7 +433,6 @@ public: void setMicGain(F32 volume); void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) bool voiceEnabled(); - void setLipSyncEnabled(BOOL enabled); void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. void setUserPTTState(bool ptt); bool getUserPTTState(); @@ -421,8 +446,6 @@ public: void updateMicMuteLogic(); - BOOL lipSyncEnabled(); - boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); } @@ -450,10 +473,10 @@ public: BOOL isSessionTextIMPossible(const LLUUID& id); BOOL isSessionCallBackPossible(const LLUUID& id); //BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return true;} ; - void endUserIMSession(const LLUUID &uuid); //@} - void setVoiceModule(const std::string& voice_server_type); + void setSpatialVoiceModule(const std::string& voice_server_type); + void setNonSpatialVoiceModule(const std::string &voice_server_type); void userAuthorized(const std::string& user_id, const LLUUID &agentID); @@ -483,11 +506,10 @@ private: void init(LLPumpIO *pump); protected: - LLVoiceModuleInterface* mVoiceModule; + LLVoiceModuleInterface* mSpatialVoiceModule; + LLVoiceModuleInterface* mNonSpatialVoiceModule; LLPumpIO *m_servicePump; - std::string mUserID; - LLUUID mAgentID; boost::signals2::connection mSimulatorFeaturesReceivedSlot; LLCachedControl mVoiceEffectEnabled; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index e003dc3317..4562dd89fc 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -80,13 +80,14 @@ extern LLMenuBarGL* gMenuBarView; extern void handle_voice_morphing_subscribe(); +const std::string VIVOX_VOICE_SERVER_TYPE = "vivox"; + namespace { const F32 VOLUME_SCALE_VIVOX = 0.01f; const F32 SPEAKING_TIMEOUT = 1.f; static const std::string VISIBLE_VOICE_SERVER_TYPE = "Vivox"; - static const std::string VIVOX_VOICE_SERVER_TYPE = "vivox"; // Don't retry connecting to the daemon more frequently than this: const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; @@ -326,10 +327,9 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mMicVolumeDirty(true), mVoiceEnabled(false), + mProcessChannels(false), mWriteInProgress(false), - mLipSyncEnabled(false), - mVoiceFontsReceived(false), mVoiceFontsNew(false), mVoiceFontListDirty(false), @@ -360,6 +360,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceVersion.serverVersion = ""; mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.internalVoiceServerType = VIVOX_VOICE_SERVER_TYPE; mVoiceVersion.majorVersion = 1; mVoiceVersion.minorVersion = 0; mVoiceVersion.mBuildVersion = ""; @@ -460,7 +461,7 @@ const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion() void LLVivoxVoiceClient::updateSettings() { - setVoiceEnabled(voiceEnabled()); + setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled()); setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); @@ -469,7 +470,6 @@ void LLVivoxVoiceClient::updateSettings() setRenderDevice(outputDevice); F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); setMicGain(mic_level); - setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); } ///////////////////////////// @@ -742,19 +742,6 @@ void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state) return; } - - // grab the active voice server version info to see if we should use - // vivox. - LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); - if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) - { - // we've switched to another voice provider, so - // disable voice and shut down vivox. Don't - // notify, though. - mVoiceEnabled = false; - } - - switch (coro_state) { case VOICE_STATE_TP_WAIT: @@ -932,7 +919,7 @@ bool LLVivoxVoiceClient::callbackEndDaemon(const LLSD& data) bool LLVivoxVoiceClient::startAndLaunchDaemon() { //--------------------------------------------------------------------- - if (!voiceEnabled()) + if (!LLVoiceClient::getInstance()->voiceEnabled()) { // Voice is locked out, we must not launch the vivox daemon. LL_WARNS("Voice") << "voice disabled; not starting daemon" << LL_ENDL; @@ -1212,7 +1199,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() LL_WARNS("Voice") << "Could not access voice provision cap after " << retryCount << " attempts." << LL_ENDL; return false; } - + LL_WARNS("Voice") << "Voice Provision Result." << result << LL_ENDL; std::string voiceSipUriHostname; std::string voiceAccountServerUri; std::string voiceUserName = result["username"].asString(); @@ -1608,48 +1595,7 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo() 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 (!result.has("voice_credentials")) { if (LLViewerParcelMgr::getInstance()->allowAgentVoice()) { @@ -1663,7 +1609,7 @@ bool LLVivoxVoiceClient::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(result["voice_credentials"]); } bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) @@ -1990,17 +1936,6 @@ bool LLVivoxVoiceClient::waitForChannel() return false; } - // grab the active voice server version info to see if we should use - // vivox. - LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); - if (version.voiceServerType != VISIBLE_VOICE_SERVER_TYPE) - { - // we've switched to another voice provider, so - // disable voice and shut down vivox. Don't - // notify, though. - mVoiceEnabled = false; - } - switch (state) { case VOICE_CHANNEL_STATE_LOGIN: @@ -2037,7 +1972,6 @@ bool LLVivoxVoiceClient::waitForChannel() break; case VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING: - mIsProcessingChannels = true; llcoro::suspend(); state = VOICE_CHANNEL_STATE_PROCESS_CHANNEL; break; @@ -2051,7 +1985,7 @@ bool LLVivoxVoiceClient::waitForChannel() { recordingAndPlaybackMode(); } - else if (checkParcelChanged() || (mNextAudioSession == NULL)) + else if (mProcessChannels && (mNextAudioSession == NULL) && checkParcelChanged()) { // the parcel is changed, or we have no pending audio sessions, // so try to request the parcel voice info @@ -2066,15 +2000,23 @@ bool LLVivoxVoiceClient::waitForChannel() } else if (mNextAudioSession) { + if (!mNextAudioSession->mIsP2P && !mProcessChannels) + { + llcoro::suspend(); + break; + } sessionStatePtr_t joinSession = mNextAudioSession; mNextAudioSession.reset(); + mIsProcessingChannels = true; if (!runSession(joinSession)) //suspends { + mIsProcessingChannels = false; LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL; break; } else { + mIsProcessingChannels = false; LL_DEBUGS("Voice") << "runSession returned true to inner loop" << " RelogRequested=" << mRelogRequested @@ -2190,6 +2132,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) mIsInChannel = true; mMuteMicDirty = true; + mSessionTerminateRequested = false; while (!sShuttingDown && mVoiceEnabled @@ -2447,95 +2390,11 @@ int LLVivoxVoiceClient::voicePlaybackBuffer() bool LLVivoxVoiceClient::performMicTuning() { LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; - mIsInTuningMode = true; - llcoro::suspend(); while (mTuningMode && !sShuttingDown) { - - if (mCaptureDeviceDirty || mRenderDeviceDirty) - { - // These can't be changed while in tuning mode. Set them before starting. - std::ostringstream stream; - - buildSetCaptureDevice(stream); - buildSetRenderDevice(stream); - - if (!stream.str().empty()) - { - writeString(stream.str()); - } - - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - } - - // loop mic back to render device. - //setMuteMic(0); // make sure the mic is not muted - std::ostringstream stream; - - stream << "" - << "" << LLVivoxSecurity::getInstance()->connectorHandle() << "" - << "false" - << "\n\n\n"; - - // Dirty the mute mic state so that it will get reset when we finishing previewing - mMuteMicDirty = true; - mTuningSpeakerVolumeDirty = true; - - writeString(stream.str()); - tuningCaptureStartSendMessage(1); // 1-loop, zero, don't loop - - //--------------------------------------------------------------------- - if (!sShuttingDown) - { - llcoro::suspend(); - } - - while (mTuningMode && !mCaptureDeviceDirty && !mRenderDeviceDirty && !sShuttingDown) - { - // process mic/speaker volume changes - if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) - { - std::ostringstream stream; - - if (mTuningMicVolumeDirty) - { - LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; - stream - << "" - << "" << mTuningMicVolume << "" - << "\n\n\n"; - } - - if (mTuningSpeakerVolumeDirty) - { - LL_INFOS("Voice") << "setting tuning speaker level to " << mTuningSpeakerVolume << LL_ENDL; - stream - << "" - << "" << mTuningSpeakerVolume << "" - << "\n\n\n"; - } - - mTuningMicVolumeDirty = false; - mTuningSpeakerVolumeDirty = false; - - if (!stream.str().empty()) - { - writeString(stream.str()); - } - } - llcoro::suspend(); - } - - //--------------------------------------------------------------------- - - // transition out of mic tuning - tuningCaptureStopSendMessage(); - if ((mCaptureDeviceDirty || mRenderDeviceDirty) && !sShuttingDown) - { - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - } + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); } mIsInTuningMode = false; @@ -2661,6 +2520,7 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &sessi << "" << font_index << "" << "" << mChannelName << "" << "\n\n\n"; + LL_WARNS("Voice") << "Session.Create: " << stream.str() << LL_ENDL; writeString(stream.str()); } @@ -2960,6 +2820,9 @@ void LLVivoxVoiceClient::tuningStart() void LLVivoxVoiceClient::tuningStop() { mTuningMode = false; + // force a renegotiation. + mCurrentParcelLocalID = 0; + mCurrentRegionName = ""; } bool LLVivoxVoiceClient::inTuningMode() @@ -4961,8 +4824,10 @@ bool LLVivoxVoiceClient::switchChannel( // 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->mSIPURI != uri) + { needsSwitch = true; + } } else { @@ -5052,23 +4917,19 @@ void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session) } } -void LLVivoxVoiceClient::setNonSpatialChannel( - const std::string &uri, - const std::string &credentials, - bool hangup_on_last_leave) +void LLVivoxVoiceClient::setNonSpatialChannel(const LLSD& channelInfo, bool hangup_on_last_leave) { - switchChannel(uri, false, false, false, credentials); + switchChannel(channelInfo["channel_uri"].asString(), false, false, false, channelInfo["channel_credentials"].asString()); } -bool LLVivoxVoiceClient::setSpatialChannel( - const std::string &uri, - const std::string &credentials) +bool LLVivoxVoiceClient::setSpatialChannel(const LLSD& channelInfo) { - mSpatialSessionURI = uri; - mSpatialSessionCredentials = credentials; + mProcessChannels = true; + mSpatialSessionURI = channelInfo["channel_uri"].asString(); + mSpatialSessionCredentials = channelInfo["channel_credentials"].asString(); mAreaVoiceDisabled = mSpatialSessionURI.empty(); - LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; + LL_DEBUGS("Voice") << "got spatial channel uri: \"" << mSpatialSessionURI << "\"" << LL_ENDL; if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) { @@ -5089,85 +4950,39 @@ void LLVivoxVoiceClient::callUser(const LLUUID &uuid) switchChannel(userURI, false, true, true); } -#if 0 -// Vivox text IMs are not in use. -LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::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; +void LLVivoxVoiceClient::hangup() { leaveChannel(); } - 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 LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) +LLVoiceP2PIncomingCallInterfacePtr LLVivoxVoiceClient::getIncomingCallInterface(const LLSD &voice_call_info) { -#if 0 - // Vivox 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 + return boost::make_shared(voice_call_info); } -bool LLVivoxVoiceClient::isValidChannel(std::string &sessionHandle) + +bool LLVivoxVoiceClient::answerInvite(const std::string &sessionHandle) { - return(findSession(sessionHandle) != NULL); - + // 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 LLVivoxVoiceClient::answerInvite(std::string &sessionHandle) + +void LLVivoxVoiceClient::declineInvite(const 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; + if (session) + { + sessionMediaDisconnectSendMessage(session); + } } bool LLVivoxVoiceClient::isVoiceWorking() const @@ -5238,21 +5053,11 @@ BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) return result; } - - -void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle) -{ - sessionStatePtr_t session(findSession(sessionHandle)); - if(session) - { - sessionMediaDisconnectSendMessage(session); - } -} void LLVivoxVoiceClient::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(); @@ -5265,16 +5070,31 @@ void LLVivoxVoiceClient::leaveNonSpatialChannel() sessionTerminate(); } -std::string LLVivoxVoiceClient::getCurrentChannel() +void LLVivoxVoiceClient::processChannels(bool process) { - std::string result; - - if (mIsInChannel && !mSessionTerminateRequested) - { - result = getAudioSessionURI(); - } - - return result; + mProcessChannels = process; +} + +bool LLVivoxVoiceClient::isCurrentChannel(const LLSD &channelInfo) +{ + if (channelInfo["voice_server_type"].asString() != VIVOX_VOICE_SERVER_TYPE) + return false; + if (mAudioSession) + { + if (!channelInfo["sessionHandle"].asString().empty()) + { + return mAudioSession->mHandle == channelInfo["session_handle"].asString(); + } + return channelInfo["channel_uri"].asString() == mAudioSession->mSIPURI; + } + return false; +} + +bool LLVivoxVoiceClient::compareChannels(const LLSD& channelInfo1, const LLSD& channelInfo2) +{ + return (channelInfo1["voice_server_type"] == VIVOX_VOICE_SERVER_TYPE) && + (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) && + (channelInfo1["channel_uri"] == channelInfo2["channel_uri"]); } bool LLVivoxVoiceClient::inProximalChannel() @@ -5314,16 +5134,6 @@ std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) return result; } -std::string LLVivoxVoiceClient::nameFromAvatar(LLVOAvatar *avatar) -{ - std::string result; - if(avatar) - { - result = nameFromID(avatar->getID()); - } - return result; -} - std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid) { std::string result; @@ -5398,11 +5208,6 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) return result; } -std::string LLVivoxVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) -{ - return avatar->getFullname(); -} - std::string LLVivoxVoiceClient::sipURIFromName(std::string &name) { std::string result; @@ -5443,12 +5248,15 @@ bool LLVivoxVoiceClient::inSpatialChannel(void) return result; } -std::string LLVivoxVoiceClient::getAudioSessionURI() + +LLSD LLVivoxVoiceClient::getAudioSessionChannelInfo() { - std::string result; + LLSD result; - if(mAudioSession) - result = mAudioSession->mSIPURI; + if (mAudioSession) + { + result = mAudioSession->getVoiceChannelInfo(); + } return result; } @@ -5650,31 +5458,6 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) } } -bool LLVivoxVoiceClient::voiceEnabled() -{ - return gSavedSettings.getBOOL("EnableVoiceChat") && - !gSavedSettings.getBOOL("CmdLineDisableVoice") && - !gNonInteractive; -} - -void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled) -{ - mLipSyncEnabled = enabled; -} - -BOOL LLVivoxVoiceClient::lipSyncEnabled() -{ - - if ( mVoiceEnabled ) - { - return mLipSyncEnabled; - } - else - { - return FALSE; - } -} - void LLVivoxVoiceClient::setEarLocation(S32 loc) { @@ -6005,6 +5788,18 @@ LLVivoxVoiceClient::sessionState::sessionState() : { } +LLSD LLVivoxVoiceClient::sessionState::getVoiceChannelInfo() +{ + LLSD result; + + result["voice_server_type"] = VIVOX_VOICE_SERVER_TYPE; + result["channel_credentials"] = mHash; + result["channel_uri"] = mSIPURI; + result["session_handle"] = mHandle; + + return result; +} + /*static*/ LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::createSession() { @@ -6451,7 +6246,7 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session URI " << getAudioSessionURI() + << ", session channelInfo " << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL; @@ -6460,7 +6255,7 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta ) { LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + observer->onChange(status, getAudioSessionChannelInfo(), inSpatialChannel()); // In case onError() deleted an entry. it = mStatusObservers.upper_bound(observer); } @@ -6472,6 +6267,7 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta { bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + LL_WARNS("Voice") << "Setting voice connected " << (voice_status ? "True" : "False") << LL_ENDL; gAgent.setVoiceConnected(voice_status); if (voice_status) @@ -6546,7 +6342,6 @@ void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sess if (session->mVoiceInvitePending) { session->mVoiceInvitePending = false; - LLIMMgr::getInstance()->inviteToSession( session->mIMSessionID, session->mName, @@ -6554,10 +6349,8 @@ void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sess session->mName, IM_SESSION_P2P_INVITE, LLIMMgr::INVITATION_TYPE_VOICE, - session->mHandle, - session->mSIPURI); + session->getVoiceChannelInfo()); } - } } @@ -8111,3 +7904,7 @@ LLVivoxSecurity::LLVivoxSecurity() LLVivoxSecurity::~LLVivoxSecurity() { } + +bool LLVivoxVoiceP2PIncomingCall::answerInvite() { return LLVivoxVoiceClient::getInstance()->answerInvite(mCallInfo["session_handle"]); } + +void LLVivoxVoiceP2PIncomingCall::declineInvite() { LLVivoxVoiceClient::getInstance()->declineInvite(mCallInfo["session_handle"]); } diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 5ddb72fa24..76d92e25eb 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -51,10 +51,25 @@ class LLVivoxProtocolParser; class LLAvatarName; class LLVivoxVoiceClientMuteListObserver; +extern const std::string VIVOX_VOICE_SERVER_TYPE; + +class LLVivoxVoiceP2PIncomingCall : public LLVoiceP2PIncomingCallInterface +{ + public: + LLVivoxVoiceP2PIncomingCall(const LLSD& call_info) : mCallInfo(call_info) {} + ~LLVivoxVoiceP2PIncomingCall() override {} + + bool answerInvite() override; + void declineInvite() override; + + protected: + LLSD mCallInfo; +}; class LLVivoxVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, - virtual public LLVoiceEffectInterface + virtual public LLVoiceEffectInterface, + virtual public LLVoiceP2POutgoingCallInterface { LLSINGLETON_C11(LLVivoxVoiceClient); LOG_CLASS(LLVivoxVoiceClient); @@ -112,9 +127,6 @@ public: // Send a text message to the specified user, initiating the session if necessary. // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; - - // close any existing text IM session with the specified user - void endUserIMSession(const LLUUID &uuid) override; // 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. @@ -134,33 +146,37 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string &uri, - const std::string &credentials, - bool hangup_on_last_leave) override; + void setNonSpatialChannel(const LLSD& channelInfo, + bool hangup_on_last_leave) override; - bool setSpatialChannel(const std::string &uri, - const std::string &credentials) override; + bool setSpatialChannel(const LLSD& channelInfo) override; void leaveNonSpatialChannel() override; + + void processChannels(bool process) override; - void leaveChannel(void) override; - - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - std::string getCurrentChannel() override; + void leaveChannel(void); + + bool isCurrentChannel(const LLSD &channelInfo) override; + bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; + //@} ////////////////////////// - /// @name invitations + /// @name LLVoiceP2POutgoingCallInterface //@{ // start a voice channel with the specified user - bool hasP2PInterface() override { return true; } void callUser(const LLUUID &uuid) override; - bool isValidChannel(std::string &channelHandle) override; - bool answerInvite(std::string &channelHandle) override; - void declineInvite(std::string &channelHandle) override; + void hangup() override; //@} + + LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return this; } + + LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override; + + bool answerInvite(const std::string &sessionHandle); + void declineInvite(const std::string &sessionHandle); ///////////////////////// /// @name Volume/gain @@ -172,10 +188,7 @@ public: ///////////////////////// /// @name enable disable voice and features //@{ - bool voiceEnabled() override; void setVoiceEnabled(bool enabled) override; - BOOL lipSyncEnabled() override; - void setLipSyncEnabled(BOOL enabled) override; void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} @@ -312,6 +325,8 @@ protected: static ptr_t createSession(); ~sessionState(); + + LLSD getVoiceChannelInfo(); participantStatePtr_t addParticipant(const std::string &uri); void removeParticipant(const participantStatePtr_t &participant); @@ -327,6 +342,7 @@ protected: bool isCallBackPossible(); bool isTextIMPossible(); + bool isSpatial() { return mIsSpatial; } static void for_each(sessionFunc_t func); @@ -738,10 +754,8 @@ private: bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); void joinSession(const sessionStatePtr_t &session); - std::string nameFromAvatar(LLVOAvatar *avatar); std::string nameFromID(const LLUUID &id); bool IDFromName(const std::string name, LLUUID &uuid); - std::string displayNameFromAvatar(LLVOAvatar *avatar); std::string sipURIFromAvatar(LLVOAvatar *avatar); std::string sipURIFromName(std::string &name); @@ -749,7 +763,7 @@ private: std::string nameFromsipURI(const std::string &uri); bool inSpatialChannel(void); - std::string getAudioSessionURI(); + LLSD getAudioSessionChannelInfo(); std::string getAudioSessionHandle(); void setHidden(bool hidden) override; //virtual @@ -805,11 +819,10 @@ private: bool mMicVolumeDirty; bool mVoiceEnabled; + bool mProcessChannels; bool mWriteInProgress; std::string mWriteString; size_t mWriteOffset; - - BOOL mLipSyncEnabled; typedef std::set observer_set_t; observer_set_t mParticipantObservers; 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, diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 16bcadb144..0bfbca8079 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -26,7 +26,6 @@ #ifndef LL_VOICE_WEBRTC_H #define LL_VOICE_WEBRTC_H -class LLVOAvatar; class LLWebRTCProtocolParser; #include "lliopipe.h" @@ -58,6 +57,8 @@ class LLAvatarName; class LLVoiceWebRTCConnection; typedef boost::shared_ptr connectionPtr_t; +extern const std::string WEBRTC_VOICE_SERVER_TYPE; + class LLWebRTCVoiceClient : public LLSingleton, virtual public LLVoiceModuleInterface, virtual public LLVoiceEffectInterface, @@ -123,9 +124,6 @@ public: // Send a text message to the specified user, initiating the session if necessary. // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; - - // close any existing text IM session with the specified user - void endUserIMSession(const LLUUID &uuid) override; // 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. @@ -145,39 +143,31 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const std::string& uri, const std::string& credentials, bool hangup_on_last_leave) override { - startAdHocSession(uri, credentials, hangup_on_last_leave); + void setNonSpatialChannel(const LLSD& channelInfo, bool hangup_on_last_leave) override + { + startAdHocSession(channelInfo, hangup_on_last_leave); } - bool setSpatialChannel(const std::string &uri, const std::string &credentials) override + bool setSpatialChannel(const LLSD &channelInfo) override { - leaveNonSpatialChannel(); - // this is a vivox-related call + processChannels(true); + return true; } void leaveNonSpatialChannel() override; - - void leaveChannel(void) override { leaveChannel(true); } + + void processChannels(bool process) override; void leaveChannel(bool stopTalking); - - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - std::string getCurrentChannel() override; - //@} - - - ////////////////////////// - /// @name invitations - //@{ - // start a voice channel with the specified user - bool hasP2PInterface() override { return false; } - void callUser(const LLUUID &uuid) override; - bool isValidChannel(std::string &channelID) override; - bool answerInvite(std::string &channelID) override; - void declineInvite(std::string &channelID) override; + + bool isCurrentChannel(const LLSD &channelInfo) override; + bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; //@} + + LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; } + + LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override { return nullptr; } ///////////////////////// /// @name Volume/gain @@ -189,10 +179,7 @@ public: ///////////////////////// /// @name enable disable voice and features //@{ - bool voiceEnabled() override; void setVoiceEnabled(bool enabled) override; - BOOL lipSyncEnabled() override; - void setLipSyncEnabled(BOOL enabled) override; void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} @@ -339,6 +326,7 @@ public: static ptr_t matchSessionByChannelID(const std::string& channel_id); void shutdownAllConnections(); + void revive(); bool isCallBackPossible(); bool isTextIMPossible(); @@ -593,7 +581,6 @@ private: std::string mMainSessionGroupHandle; // handle of the "main" session group. - std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; sessionStatePtr_t mSession; // Session state for the current session @@ -616,21 +603,16 @@ private: bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); - bool startAdHocSession(const std::string& channelID, const std::string& credentials, bool hangup_on_last_leave); - - void joinSession(const sessionStatePtr_t &session); + bool startAdHocSession(const LLSD &channelInfo, bool hangup_on_last_leave); - std::string nameFromAvatar(LLVOAvatar *avatar); std::string nameFromID(const LLUUID &id); bool IDFromName(const std::string name, LLUUID &uuid); - std::string displayNameFromAvatar(LLVOAvatar *avatar); - bool inSpatialChannel(); bool inOrJoiningChannel(const std::string &channelID); bool inEstateChannel(); - std::string getAudioSessionURI(); + std::string getAudioSessionChannelInfo(); void setHidden(bool hidden) override; //virtual @@ -674,6 +656,7 @@ private: F32 mMicGain; bool mVoiceEnabled; + bool mProcessChannels; BOOL mLipSyncEnabled; -- cgit v1.2.3 From 66d65edbc4da1ac5d4a32224dc77a372e025e5ff Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Sun, 3 Mar 2024 16:13:41 -0800 Subject: OSX build fixes --- indra/newview/llvoavatar.h | 2 +- indra/newview/llvoicechannel.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 87c9d468a2..a19476153c 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -109,7 +109,7 @@ public: virtual void initInstance(); // Called after construction to initialize the class. protected: virtual ~LLVOAvatar(); - static bool LLVOAvatar::handleVOAvatarPrefsChanged(const LLSD &newvalue); + static bool handleVOAvatarPrefsChanged(const LLSD &newvalue); /** Initialization ** ** diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index dd24c72891..7ddc6c3e27 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -146,7 +146,7 @@ public: void requestChannelInfo() override; protected: - virtual void setState(EState state); + void setState(EState state) override; private: void voiceCallCapCoro(std::string url); @@ -158,7 +158,7 @@ private: class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton { - LLSINGLETON(LLVoiceChannelProximal); + LLSINGLETON_C11(LLVoiceChannelProximal); public: void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override; @@ -184,10 +184,10 @@ public: void requestChannelInfo() override; void deactivate() override; - void setChannelInfo(const LLSD& channel_info); + void setChannelInfo(const LLSD& channel_info) override; protected: - virtual void setState(EState state); + void setState(EState state) override; private: -- 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/llvoicechannel.cpp | 2 +- indra/newview/llvoiceclient.cpp | 7 ++++++- indra/newview/llvoicewebrtc.cpp | 30 ++++++++++++++++++++---------- indra/newview/llvoicewebrtc.h | 2 +- 4 files changed, 28 insertions(+), 13 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 10cccb3671..a009641a34 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -460,7 +460,7 @@ void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo) if (mState == STATE_NO_CHANNEL_INFO) { - if(mChannelInfo.isUndefined()) + if(!mChannelInfo.isUndefined()) { setState(STATE_READY); diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 3df94d8ba0..087c93a3f5 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -513,7 +513,12 @@ LLVoiceP2PIncomingCallInterfacePtr LLVoiceClient::getIncomingCallInterface(const // outgoing calls LLVoiceP2POutgoingCallInterface *LLVoiceClient::getOutgoingCallInterface(const LLSD& voiceChannelInfo) { - LLVoiceModuleInterface *module = getVoiceModule(voiceChannelInfo["voice_server_type"].asString()); + std::string voiceServerType = gSavedSettings.getString("VoiceServerType"); + if (voiceChannelInfo.has("voice_server_type")) + { + voiceServerType = voiceChannelInfo["voice_server_type"].asString(); + } + LLVoiceModuleInterface *module = getVoiceModule(voiceServerType); return dynamic_cast(module); } 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, diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 0bfbca8079..104331f44e 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -612,7 +612,7 @@ private: bool inOrJoiningChannel(const std::string &channelID); bool inEstateChannel(); - std::string getAudioSessionChannelInfo(); + LLSD getAudioSessionChannelInfo(); void setHidden(bool hidden) override; //virtual -- cgit v1.2.3 From f4c1e2e795f17ff57b75b774c02b5c9bb1c53901 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 4 Mar 2024 15:23:20 -0800 Subject: Pass invitation type (p2p/group/adhoc) around --- indra/newview/llimview.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index a675c44274..43edaf3589 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -87,6 +87,16 @@ const S32 XL8_PADDING = 3; // XL8_START_TAG.size() + XL8_END_TAG.size() /** Timeout of outgoing session initialization (in seconds) */ const static U32 SESSION_INITIALIZATION_TIMEOUT = 30; + +enum EMultiAgentChatSessionType +{ + GROUP_CHAT_SESSION = 0, + CONFERENCE_SESSION = 1, + P2P_CHAT_SESSION = 2, + SESSION_TYPE_COUNT +}; + + void startConferenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents); void startP2PCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId); @@ -2078,7 +2088,7 @@ bool LLIMModel::sendStartSession( return true; } - else if ((dialog == IM_SESSION_CONFERENCE_START ) || p2p_as_adhoc_call) + else if (dialog == IM_SESSION_CONFERENCE_START ) { LLSD agents; for (int i = 0; i < (S32) ids.size(); i++) @@ -4100,8 +4110,8 @@ public: return; } - BOOL session_type_p2p = input["body"]["voice"].get("p2p").asBoolean(); - + BOOL session_type_p2p = input["body"]["voice"].get("invitation_type").asInteger() == EMultiAgentChatSessionType::P2P_CHAT_SESSION; + LL_WARNS("Voice") << "VOICE DATA: " << input["body"]["voice"] << LL_ENDL; gIMMgr->inviteToSession( input["body"]["session_id"].asUUID(), input["body"]["session_name"].asString(), -- 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/llimview.cpp | 20 +++++++++++++------- indra/newview/llvoicechannel.cpp | 12 +++++++++--- indra/newview/llvoicechannel.h | 5 +++-- indra/newview/llvoiceclient.cpp | 3 ++- indra/newview/llvoiceclient.h | 6 ++++-- indra/newview/llvoicevivox.cpp | 2 +- indra/newview/llvoicevivox.h | 1 + indra/newview/llvoicewebrtc.cpp | 24 ++++++++++++++++++++---- indra/newview/llvoicewebrtc.h | 12 ++++++++---- 9 files changed, 61 insertions(+), 24 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 43edaf3589..bc3bc4ca1d 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -474,7 +474,7 @@ void startP2PCoro(std::string url, LLUUID sessionID, LLUUID creatorId, LLUUID ot if (!status) { - LL_WARNS("LLIMModel") << "Failed to start conference:" << postData << "->" << result << LL_ENDL; + LL_WARNS("LLIMModel") << "Failed to start p2p session:" << postData << "->" << result << LL_ENDL; // try an "old school" way. // *TODO: What about other error status codes? 4xx 5xx? if (status == LLCore::HttpStatus(HTTP_BAD_REQUEST)) @@ -736,7 +736,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, else { p2pAsAdhocCall = true; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true, true); } } else @@ -745,12 +745,12 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, if (gAgent.isInGroup(mSessionID)) { mSessionType = GROUP_SESSION; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false, false); } else { mSessionType = ADHOC_SESSION; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false, true); } } @@ -2750,7 +2750,7 @@ BOOL LLIncomingCallDialog::postBuild() LLUUID session_id = mPayload["session_id"].asUUID(); LLSD caller_id = mPayload["caller_id"]; - std::string caller_name = mPayload["caller_name"].asString(); + std::string caller_name = mPayload["caller_name"].asString(); if (session_id.isNull() && caller_id.asUUID().isNull()) { @@ -2899,6 +2899,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload LLUUID session_id = payload["session_id"].asUUID(); LLUUID caller_id = payload["caller_id"].asUUID(); std::string session_name = payload["session_name"].asString(); + if (session_name.empty()) + { + session_name = payload["caller_name"].asString(); + } EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); bool voice = true; @@ -3315,7 +3319,9 @@ LLUUID LLIMMgr::addSession( bool new_session = (LLIMModel::getInstance()->findIMSession(session_id) == NULL); //works only for outgoing ad-hoc sessions - if (new_session && IM_SESSION_CONFERENCE_START == dialog && ids.size()) + if (new_session && + ((IM_NOTHING_SPECIAL == dialog) || (IM_SESSION_P2P_INVITE == dialog) || (IM_SESSION_CONFERENCE_START == dialog)) && + ids.size()) { LLIMModel::LLIMSession* ad_hoc_found = LLIMModel::getInstance()->findAdHocIMSession(ids); if (ad_hoc_found) @@ -4111,7 +4117,7 @@ public: } BOOL session_type_p2p = input["body"]["voice"].get("invitation_type").asInteger() == EMultiAgentChatSessionType::P2P_CHAT_SESSION; - LL_WARNS("Voice") << "VOICE DATA: " << input["body"]["voice"] << LL_ENDL; + LL_WARNS("Voice") << "VOICE DATA: " << input["body"]<< LL_ENDL; gIMMgr->inviteToSession( input["body"]["session_id"].asUUID(), input["body"]["session_name"].asString(), diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index a009641a34..9303a09402 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -381,8 +381,12 @@ boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallbac // LLVoiceChannelGroup // -LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, BOOL hangup_on_last_leave) : - LLVoiceChannel(session_id, session_name), +LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID &session_id, + const std::string &session_name, + bool notify_on_first_join, + bool hangup_on_last_leave) : + LLVoiceChannel(session_id, session_name), + mNotifyOnFirstJoin(notify_on_first_join), mHangupOnLastLeave(hangup_on_last_leave) { mRetries = DEFAULT_RETRIES_COUNT; @@ -408,6 +412,7 @@ void LLVoiceChannelGroup::activate() { // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo, + mNotifyOnFirstJoin, mHangupOnLastLeave); if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel @@ -484,6 +489,7 @@ void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo) { // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel(channelInfo, + mNotifyOnFirstJoin, mHangupOnLastLeave); } } @@ -734,7 +740,7 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID &session_id, const std::string &session_name, const LLUUID &other_user_id, LLVoiceP2POutgoingCallInterface* outgoing_call_interface) : - LLVoiceChannelGroup(session_id, session_name, true), + LLVoiceChannelGroup(session_id, session_name, true, true), mOtherUserID(other_user_id), mReceivedCall(FALSE), mOutgoingCallInterface(outgoing_call_interface) diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 7ddc6c3e27..21f6536b60 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -136,7 +136,7 @@ private: class LLVoiceChannelGroup : public LLVoiceChannel { public: - LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, BOOL hangup_on_last_leave); + LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, bool notify_on_first_join, bool hangup_on_last_leave); void handleStatusChange(EStatusType status) override; void handleError(EStatusType status) override; @@ -153,7 +153,8 @@ private: U32 mRetries; BOOL mIsRetrying; - BOOL mHangupOnLastLeave; + bool mHangupOnLastLeave; + bool mNotifyOnFirstJoin; }; class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 087c93a3f5..b642c43f34 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -427,6 +427,7 @@ bool LLVoiceClient::inProximalChannel() void LLVoiceClient::setNonSpatialChannel( const LLSD& channelInfo, + bool notify_on_first_join, bool hangup_on_last_leave) { setNonSpatialVoiceModule(channelInfo["voice_server_type"].asString()); @@ -436,7 +437,7 @@ void LLVoiceClient::setNonSpatialChannel( } if (mNonSpatialVoiceModule) { - mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, hangup_on_last_leave); + mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, notify_on_first_join, hangup_on_last_leave); mNonSpatialVoiceModule->processChannels(true); } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 87b6469783..4c6cf92276 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -205,7 +205,8 @@ public: virtual bool inProximalChannel()=0; virtual void setNonSpatialChannel(const LLSD& channelInfo, - bool hangup_on_last_leave = false)=0; + bool notify_on_first_join, + bool hangup_on_last_leave)=0; virtual bool setSpatialChannel(const LLSD& channelInfo)=0; @@ -407,7 +408,8 @@ public: bool inProximalChannel(); void setNonSpatialChannel(const LLSD& channelInfo, - bool hangup_on_last_leave = false); + bool notify_on_first_join, + bool hangup_on_last_leave); void setSpatialChannel(const LLSD &channelInfo); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 4562dd89fc..529abff01b 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -4917,7 +4917,7 @@ void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session) } } -void LLVivoxVoiceClient::setNonSpatialChannel(const LLSD& channelInfo, bool hangup_on_last_leave) +void LLVivoxVoiceClient::setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) { switchChannel(channelInfo["channel_uri"].asString(), false, false, false, channelInfo["channel_credentials"].asString()); } diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 76d92e25eb..a141ac9465 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -147,6 +147,7 @@ public: bool inProximalChannel() override; void setNonSpatialChannel(const LLSD& channelInfo, + bool notify_on_first_join, bool hangup_on_last_leave) override; bool setSpatialChannel(const LLSD& channelInfo) override; 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; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 104331f44e..46ca4e4eda 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -143,9 +143,9 @@ public: // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - void setNonSpatialChannel(const LLSD& channelInfo, bool hangup_on_last_leave) override + void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override { - startAdHocSession(channelInfo, hangup_on_last_leave); + startAdHocSession(channelInfo, notify_on_first_join, hangup_on_last_leave); } bool setSpatialChannel(const LLSD &channelInfo) override @@ -388,6 +388,7 @@ public: { return mSessions.find(sessionID) != mSessions.end(); } bool mHangupOnLastLeave; + bool mNotifyOnFirstJoin; protected: sessionState(); @@ -429,7 +430,10 @@ public: class adhocSessionState : public sessionState { public: - adhocSessionState(const std::string &channelID, const std::string& credentials, bool hangup_on_last_leave); + adhocSessionState(const std::string &channelID, + const std::string& credentials, + bool notify_on_first_join, + bool hangup_on_last_leave); bool isSpatial() override { return false; } bool isEstate() override { return false; } @@ -603,7 +607,7 @@ private: bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); - bool startAdHocSession(const LLSD &channelInfo, bool hangup_on_last_leave); + bool startAdHocSession(const LLSD &channelInfo, bool notify_on_first_join, bool hangup_on_last_leave); std::string nameFromID(const LLUUID &id); bool IDFromName(const std::string name, LLUUID &uuid); -- cgit v1.2.3 From 2c2529d3c8452c5c6909c6c9bbda6b4259a29b59 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 5 Mar 2024 11:48:56 -0800 Subject: Notify join on connect instead of on first peer join, for adhoc-p2p incoming calls --- indra/newview/llimview.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index bc3bc4ca1d..4fad1153ac 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -731,12 +731,11 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, // which uses the voice server to relay calls and invites. Otherwise, // we use the group voice provider. mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id, outgoingInterface); - mVoiceChannel->setChannelInfo(voiceChannelInfo); } else { p2pAsAdhocCall = true; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true, true); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, voiceChannelInfo.isUndefined(), true); } } else @@ -754,10 +753,8 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, } } - if(mVoiceChannel) - { - mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3)); - } + mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3)); + mVoiceChannel->setChannelInfo(voiceChannelInfo); mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); -- 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/llvoicechannel.cpp | 9 +++------ indra/newview/llvoicewebrtc.cpp | 14 ++++++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 9303a09402..c3ffbd5426 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -438,12 +438,9 @@ void LLVoiceChannelGroup::activate() } } - //Mic default state is OFF on initiating/joining Ad-Hoc/Group calls - if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle()) - { - LLVoiceClient::getInstance()->inputUserControlState(true); - } - + // Mic default state is OFF on initiating/joining Ad-Hoc/Group calls. It's on for P2P using the AdHoc infra. + + LLVoiceClient::getInstance()->setUserPTTState(mNotifyOnFirstJoin); } } 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 967534352be21b23df96020f40c6449b2501e5b1 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 5 Mar 2024 20:55:13 -0800 Subject: more p2p logic fixes --- indra/newview/llimview.cpp | 6 +-- indra/newview/llvoicechannel.cpp | 80 +++++++++++++++++++++++----------------- indra/newview/llvoicechannel.h | 7 ++-- 3 files changed, 53 insertions(+), 40 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 4fad1153ac..fa8075673c 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -735,7 +735,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, else { p2pAsAdhocCall = true; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, voiceChannelInfo.isUndefined(), true); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); } } else @@ -744,12 +744,12 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, if (gAgent.isInGroup(mSessionID)) { mSessionType = GROUP_SESSION; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false, false); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); } else { mSessionType = ADHOC_SESSION; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false, true); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); } } diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index c3ffbd5426..a1eb58447d 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -383,11 +383,9 @@ boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallbac LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID &session_id, const std::string &session_name, - bool notify_on_first_join, - bool hangup_on_last_leave) : + bool is_p2p) : LLVoiceChannel(session_id, session_name), - mNotifyOnFirstJoin(notify_on_first_join), - mHangupOnLastLeave(hangup_on_last_leave) + mIsP2P(is_p2p) { mRetries = DEFAULT_RETRIES_COUNT; mIsRetrying = FALSE; @@ -400,7 +398,15 @@ void LLVoiceChannelGroup::deactivate() LLVoiceClient::getInstance()->leaveNonSpatialChannel(); } LLVoiceChannel::deactivate(); -} + + if (mIsP2P) + { + // void the channel info for p2p adhoc channels + // so we request it again, hence throwing up the + // connect dialogue on the other side. + setState(STATE_NO_CHANNEL_INFO); + } + } void LLVoiceChannelGroup::activate() { @@ -411,36 +417,42 @@ void LLVoiceChannelGroup::activate() if (callStarted()) { // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo, - mNotifyOnFirstJoin, - mHangupOnLastLeave); + LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo, + mCallDirection == OUTGOING_CALL, + mIsP2P); - if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel - { - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionID); - // Adding ad-hoc call participants to Recent People List. - // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we - // called(both online and offline) as source to get people for recent (STORM-210). - if (session->isOutgoingAdHoc()) - { - for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); - it!=session->mInitialTargetIDs.end();++it) - { - const LLUUID id = *it; - LLRecentPeople::instance().add(id); - } - } - // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs - // would lead to EXT-8246. So in this case we get them from speakers list. - else - { - LLIMModel::addSpeakersToRecent(mSessionID); - } - } + if (mIsP2P) + { + LLIMModel::addSpeakersToRecent(mSessionID); + } + else + { + if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel + { + LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(mSessionID); + // Adding ad-hoc call participants to Recent People List. + // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we + // called(both online and offline) as source to get people for recent (STORM-210). + if (session->isOutgoingAdHoc()) + { + for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); it != session->mInitialTargetIDs.end(); ++it) + { + const LLUUID id = *it; + LLRecentPeople::instance().add(id); + } + } + // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs + // would lead to EXT-8246. So in this case we get them from speakers list. + else + { + LLIMModel::addSpeakersToRecent(mSessionID); + } + } + } // Mic default state is OFF on initiating/joining Ad-Hoc/Group calls. It's on for P2P using the AdHoc infra. - LLVoiceClient::getInstance()->setUserPTTState(mNotifyOnFirstJoin); + LLVoiceClient::getInstance()->setUserPTTState(mIsP2P); } } @@ -486,8 +498,8 @@ void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo) { // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel(channelInfo, - mNotifyOnFirstJoin, - mHangupOnLastLeave); + mCallDirection == OUTGOING_CALL, + mIsP2P); } } @@ -737,7 +749,7 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID &session_id, const std::string &session_name, const LLUUID &other_user_id, LLVoiceP2POutgoingCallInterface* outgoing_call_interface) : - LLVoiceChannelGroup(session_id, session_name, true, true), + LLVoiceChannelGroup(session_id, session_name, true), mOtherUserID(other_user_id), mReceivedCall(FALSE), mOutgoingCallInterface(outgoing_call_interface) diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 21f6536b60..5e6b769bef 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -136,7 +136,9 @@ private: class LLVoiceChannelGroup : public LLVoiceChannel { public: - LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name, bool notify_on_first_join, bool hangup_on_last_leave); + LLVoiceChannelGroup(const LLUUID& session_id, + const std::string& session_name, + bool is_p2p); void handleStatusChange(EStatusType status) override; void handleError(EStatusType status) override; @@ -153,8 +155,7 @@ private: U32 mRetries; BOOL mIsRetrying; - bool mHangupOnLastLeave; - bool mNotifyOnFirstJoin; + bool mIsP2P; }; class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton -- cgit v1.2.3 From 28032582f22b543199c1e7db7f75a82eec2f0c8b Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 6 Mar 2024 11:17:54 -0800 Subject: Shutting down a p2p via adhoc call resets the channel info. We shouldn't throw up a blank dialogue when that happens --- indra/newview/llimview.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index fa8075673c..956c3a0276 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -2368,6 +2368,11 @@ void LLCallDialogManager::onVoiceChannelStateChangedInt(const LLVoiceChannel::ES return; } break; + case LLVoiceChannel::STATE_NO_CHANNEL_INFO : + // This will happen in p2p calls using the adhoc + // infrastructure, which marks the channel as no channel info + // after the call is closed, which forces a dialogue. + return; case LLVoiceChannel::STATE_HUNG_UP: // this state is coming before session is changed @@ -3394,6 +3399,13 @@ void LLIMMgr::inviteToSession( EInvitationType inv_type, const LLSD& voice_channel_info) { + + if (caller_id == gAgentID) + { + // ignore invites from ourself. + return; + } + std::string notify_box_type; // voice invite question is different from default only for group call (EXT-7118) std::string question_type = "VoiceInviteQuestionDefault"; -- cgit v1.2.3 From 041946455d3409a95c4faebac2ac54020b418743 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Mar 2024 15:00:04 -0800 Subject: Send down preferred voice server type when initiating a call --- indra/newview/llvoicechannel.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index a1eb58447d..4d85f160c2 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -593,6 +593,9 @@ void LLVoiceChannelGroup::voiceCallCapCoro(std::string url) LLSD postData; postData["method"] = "call"; postData["session-id"] = mSessionID; + LLSD altParams; + altParams["preferred_voice_server_type"] = gSavedSettings.getString("VoiceServerType"); + postData["alt_params"] = altParams; LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL; -- cgit v1.2.3 From 425f089ffcfa715b4e85dd65c91bec1b8e2beb10 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 7 Mar 2024 19:30:32 -0800 Subject: addSession had a changed parameter...which wasn't changed everywhere --- indra/newview/llavataractions.cpp | 7 +++---- indra/newview/llgroupactions.cpp | 2 +- indra/newview/llimview.cpp | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 313339f131..156b5b4047 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -241,7 +241,7 @@ static void on_avatar_name_cache_start_call(const LLUUID& agent_id, const LLAvatarName& av_name) { std::string name = av_name.getDisplayName(); - LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id, true); + LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id, LLSD()); if (session_id != LLUUID::null) { gIMMgr->startCall(session_id); @@ -277,8 +277,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate // create the new ad hoc voice session const std::string title = LLTrans::getString("conference-title"); - LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, - ids[0], id_array, true, floater_id); + LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, LLSD(), floater_id); if (session_id == LLUUID::null) { return; @@ -322,7 +321,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float id_array.push_back(*it); } const std::string title = LLTrans::getString("conference-title"); - LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, false, floater_id); + LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, LLSD(), floater_id); if (session_id == LLUUID::null) { diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 380e49c320..9d39da148c 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -254,7 +254,7 @@ void LLGroupActions::startCall(const LLUUID& group_id) return; } - LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, true); + LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, LLSD()); if (session_id == LLUUID::null) { LL_WARNS() << "Error adding session" << LL_ENDL; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 956c3a0276..b2769e9bab 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -2971,7 +2971,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload } } - gIMMgr->addSession(correct_session_name, type, session_id, true); + gIMMgr->addSession(correct_session_name, type, session_id, payload["voice_channel_info"]); std::string url = gAgent.getRegion()->getCapability( "ChatSessionRequest"); -- 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/llvoiceclient.cpp | 10 ++------- indra/newview/llvoicewebrtc.cpp | 47 ++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 30 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index b642c43f34..9f99549829 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -400,14 +400,8 @@ BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id) BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) { - if (mSpatialVoiceModule) - { - return mSpatialVoiceModule->isSessionCallBackPossible(id); - } - else - { - return FALSE; - } + // we don't support PSTN calls anymore. (did we ever?) + return TRUE; } //---------------------------------------------- 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/llvoiceclient.h | 2 - indra/newview/llvoicevivox.cpp | 7 - indra/newview/llvoicevivox.h | 6 - indra/newview/llvoicewebrtc.cpp | 2518 +++++++++++++++++---------------------- indra/newview/llvoicewebrtc.h | 776 +++++------- 5 files changed, 1431 insertions(+), 1878 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 4c6cf92276..79bca23fc4 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -464,8 +464,6 @@ public: F32 getUserVolume(const LLUUID& id); ///////////////////////////// - BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. - // Use this to determine whether to show a "no speech" icon in the menu bar. void getParticipantList(std::set &participants); bool isParticipant(const LLUUID& speaker_id); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 529abff01b..1454342083 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -295,7 +295,6 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mTuningSpeakerVolumeDirty(true), mDevicesListUpdated(false), - mAreaVoiceDisabled(false), mAudioSession(), // TBD - should be NULL mAudioSessionChanged(false), mNextAudioSession(), @@ -4927,7 +4926,6 @@ bool LLVivoxVoiceClient::setSpatialChannel(const LLSD& channelInfo) mProcessChannels = true; mSpatialSessionURI = channelInfo["channel_uri"].asString(); mSpatialSessionCredentials = channelInfo["channel_credentials"].asString(); - mAreaVoiceDisabled = mSpatialSessionURI.empty(); LL_DEBUGS("Voice") << "got spatial channel uri: \"" << mSpatialSessionURI << "\"" << LL_ENDL; @@ -5657,11 +5655,6 @@ std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id) return result; } -BOOL LLVivoxVoiceClient::getAreaVoiceDisabled() -{ - return mAreaVoiceDisabled; -} - void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) { // LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index a141ac9465..14f6422ebb 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -511,11 +511,6 @@ protected: // MBW -- XXX -- Not sure how to get this data out of the TVC BOOL getUsingPTT(const LLUUID& id); std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) - - ///////////////////////////// - BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. - // Use this to determine whether to show a "no speech" icon in the menu bar. - ///////////////////////////// // Recording controls @@ -711,7 +706,6 @@ private: std::string mMainSessionGroupHandle; // handle of the "main" session group. std::string mChannelName; // Name of the channel to be looked up - bool mAreaVoiceDisabled; sessionStatePtr_t mAudioSession; // Session state for the current audio session bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index fe4c4129c5..6e0cd32249 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -40,10 +40,10 @@ # include "expat/expat.h" #endif #include "llcallbacklist.h" -#include "llviewernetwork.h" // for gGridChoice +#include "llviewernetwork.h" // for gGridChoice #include "llbase64.h" #include "llviewercontrol.h" -#include "llappviewer.h" // for gDisconnected, gDisableVoice +#include "llappviewer.h" // for gDisconnected, gDisableVoice #include "llprocess.h" // Viewer includes @@ -78,12 +78,6 @@ #include "json/reader.h" #include "json/writer.h" -#define USE_SESSION_GROUPS 0 -#define VX_NULL_POSITION -2147483648.0 /*The Silence*/ - -extern LLMenuBarGL* gMenuBarView; -extern void handle_voice_morphing_subscribe(); - const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; namespace { @@ -92,51 +86,19 @@ namespace { const F32 VOLUME_SCALE_WEBRTC = 0.01f; const F32 LEVEL_SCALE_WEBRTC = 0.008f; - const F32 SPEAKING_TIMEOUT = 1.f; const F32 SPEAKING_AUDIO_LEVEL = 0.40; - static const std::string VISIBLE_VOICE_SERVER_TYPE = "WebRTC"; + static const std::string REPORTED_VOICE_SERVER_TYPE = "Secondlife WebRTC Gateway"; // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - - // Timeout for connection to WebRTC - const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; - const F32 CONNECT_DNS_TIMEOUT = 5.0f; - - const F32 LOGOUT_ATTEMPT_TIMEOUT = 5.0f; - const S32 PROVISION_WAIT_TIMEOUT_SEC = 5; // Cosine of a "trivially" small angle const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f); const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES); - // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() - // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed - // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process - // backs up processing join requests. There is a log statement that records when channel joins take longer than 100 frames. - const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500; - - // How often to check for expired voice fonts in seconds - const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; - // Time of day at which WebRTC expires voice font subscriptions. - // Used to replace the time portion of received expiry timestamps. - static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; - - // Maximum length of capture buffer recordings in seconds. - const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; } // namespace -float LLWebRTCVoiceClient::getAudioLevel() -{ - if (mIsInTuningMode) - { - return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1; - } - else - { - return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; - } -} + /////////////////////////////////////////////////////////////////////////////////////////////// @@ -233,83 +195,41 @@ LLSD LLVoiceWebRTCStats::read() /////////////////////////////////////////////////////////////////////////////////////////////// bool LLWebRTCVoiceClient::sShuttingDown = false; -bool LLWebRTCVoiceClient::sConnected = false; -LLPumpIO *LLWebRTCVoiceClient::sPump = nullptr; LLWebRTCVoiceClient::LLWebRTCVoiceClient() : - mRelogRequested(false), - mSpatialJoiningNum(0), - + mHidden(false), mTuningMode(false), mTuningMicGain(0.0), - mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume - mTuningSpeakerVolumeDirty(true), + mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume mDevicesListUpdated(false), - mAreaVoiceDisabled(false), - mSession(), // TBD - should be NULL - mNextSession(), - - mCurrentParcelLocalID(0), - - mBuddyListMapPopulated(false), - mBlockRulesListReceived(false), - mAutoAcceptRulesListReceived(false), - mSpatialCoordsDirty(false), - mIsInitialized(false), mMuteMic(false), - mMuteMicDirty(false), - mFriendsListDirty(true), mEarLocation(0), - mSpeakerVolumeDirty(true), mMicGain(0.0), mVoiceEnabled(false), mProcessChannels(false), - mShutdownComplete(true), - mPlayRequestCount(0), - mAvatarNameCacheConnection(), mIsInTuningMode(false), - mIsJoiningSession(false), - mIsWaitingForFonts(false), - mIsLoggingIn(false), mIsProcessingChannels(false), mIsCoroutineActive(false), mWebRTCPump("WebRTCClientPump"), mWebRTCDeviceInterface(nullptr) { sShuttingDown = false; - sConnected = false; - sPump = nullptr; - mSpeakerVolume = 0.0; + mSpeakerVolume = 0.0; - mVoiceVersion.serverVersion = ""; - mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE; + mVoiceVersion.serverVersion = ""; + mVoiceVersion.voiceServerType = REPORTED_VOICE_SERVER_TYPE; mVoiceVersion.internalVoiceServerType = WEBRTC_VOICE_SERVER_TYPE; mVoiceVersion.minorVersion = 0; mVoiceVersion.majorVersion = 2; mVoiceVersion.mBuildVersion = ""; - -#if LL_DARWIN || LL_LINUX - // HACK: THIS DOES NOT BELONG HERE - // When the WebRTC daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. - // This should cause us to ignore SIGPIPE and handle the error through proper channels. - // This should really be set up elsewhere. Where should it go? - signal(SIGPIPE, SIG_IGN); - - // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. - // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. - signal(SIGCHLD, SIG_IGN); -#endif - - - gIdleCallbacks.addFunction(idle, this); } //--------------------------------------------------- @@ -328,10 +248,6 @@ LLWebRTCVoiceClient::~LLWebRTCVoiceClient() void LLWebRTCVoiceClient::init(LLPumpIO* pump) { // constructor will set up LLVoiceClient::getInstance() - sPump = pump; - - // LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", - // boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); llwebrtc::init(); mWebRTCDeviceInterface = llwebrtc::getDeviceInterface(); @@ -345,12 +261,10 @@ void LLWebRTCVoiceClient::terminate() return; } - mRelogRequested = false; mVoiceEnabled = false; llwebrtc::terminate(); sShuttingDown = true; - sPump = NULL; } //--------------------------------------------------- @@ -361,7 +275,7 @@ void LLWebRTCVoiceClient::cleanUp() mSession.reset(); mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); - LL_DEBUGS("Voice") << "exiting" << LL_ENDL; + LL_DEBUGS("Voice") << "Exiting" << LL_ENDL; } //--------------------------------------------------- @@ -386,177 +300,96 @@ void LLWebRTCVoiceClient::updateSettings() setMicGain(mic_level); } +// Observers +void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver *observer) +{ + mParticipantObservers.insert(observer); +} -///////////////////////////// -// session control messages +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver *observer) +{ + mParticipantObservers.erase(observer); +} -void LLWebRTCVoiceClient::OnConnectionFailure(const std::string& channelID, const LLUUID& regionID) +void LLWebRTCVoiceClient::notifyParticipantObservers() { - if (gAgent.getRegion()->getRegionID() == regionID) + for (observer_set_t::iterator it = mParticipantObservers.begin(); it != mParticipantObservers.end();) { - if (mNextSession && mNextSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } - else if (mSession && mSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - } + LLVoiceClientParticipantObserver *observer = *it; + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. + it = mParticipantObservers.upper_bound(observer); } } -void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID) -{ - if (gAgent.getRegion()->getRegionID() == regionID) - { - if (mNextSession && mNextSession->mChannelID == channelID) - { - if (mSession) - { - mSession->shutdownAllConnections(); - } - mSession = mNextSession; - mNextSession.reset(); - mSession->addParticipant(gAgentID); - } - - if (mSession && mSession->mChannelID == channelID) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - if (!mSession->mNotifyOnFirstJoin) - { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - } - } - } +void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver *observer) +{ + mStatusObservers.insert(observer); } -void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID) -{ - if (gAgent.getRegion()->getRegionID() == regionID) - { - if (mSession && mSession->mChannelID == channelID) - { - //LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - } - } +void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver *observer) +{ + mStatusObservers.erase(observer); } -void LLWebRTCVoiceClient::idle(void* user_data) +void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) { -} + LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" + << " mSession=" << mSession << LL_ENDL; -//========================================================================= -// the following are methods to support the coroutine implementation of the -// voice connection and processing. They should only be called in the context -// of a coroutine. -// -// + LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) << ", session channelInfo " + << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL; -void LLWebRTCVoiceClient::sessionState::processSessionStates() -{ + mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED; - auto iter = mSessions.begin(); - while (iter != mSessions.end()) + LLSD channelInfo = getAudioSessionChannelInfo(); + for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end();) { - if (!iter->second->processConnectionStates() && iter->second->mShuttingDown) - { - iter = mSessions.erase(iter); - } - else - { - iter++; - } + LLVoiceClientStatusObserver *observer = *it; + observer->onChange(status, channelInfo, inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); } -} -bool LLWebRTCVoiceClient::sessionState::processConnectionStates() -{ - std::list::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; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 46ca4e4eda..e6f01fd181 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -59,175 +59,137 @@ typedef boost::shared_ptr connectionPtr_t; extern const std::string WEBRTC_VOICE_SERVER_TYPE; -class LLWebRTCVoiceClient : public LLSingleton, - virtual public LLVoiceModuleInterface, - virtual public LLVoiceEffectInterface, +class LLWebRTCVoiceClient : public LLSingleton, + virtual public LLVoiceModuleInterface, public llwebrtc::LLWebRTCDevicesObserver, public LLMuteListObserver { LLSINGLETON_C11(LLWebRTCVoiceClient); - LOG_CLASS(LLWebRTCVoiceClient); - virtual ~LLWebRTCVoiceClient(); + LOG_CLASS(LLWebRTCVoiceClient); + virtual ~LLWebRTCVoiceClient(); public: - /// @name LLVoiceModuleInterface virtual implementations - /// @see LLVoiceModuleInterface - //@{ - void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) - void terminate() override; // Call this to clean up during shutdown - - static bool isShuttingDown() { return sShuttingDown; } - - const LLVoiceVersionInfo& getVersion() override; - - void updateSettings() override; // call after loading settings and whenever they change - - // Returns true if WebRTC has successfully logged in and is not in error state - bool isVoiceWorking() const override; - - std::string sipURIFromID(const LLUUID &id) override; - - ///////////////////// - /// @name Tuning - //@{ - void tuningStart() override; - void tuningStop() override; - bool inTuningMode() override; - - void tuningSetMicVolume(float volume) override; - void tuningSetSpeakerVolume(float volume) override; - float tuningGetEnergy(void) override; - //@} - - ///////////////////// - /// @name Devices - //@{ - // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - bool deviceSettingsAvailable() override; - bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. - - // Requery the WebRTC daemon for the current list of input/output devices. - // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed - // (use this if you want to know when it's done). - // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - void refreshDeviceLists(bool clearCurrentList = true) override; - - void setCaptureDevice(const std::string& name) override; - void setRenderDevice(const std::string& name) override; - - LLVoiceDeviceList& getCaptureDevices() override; - LLVoiceDeviceList& getRenderDevices() override; - //@} - - void getParticipantList(std::set &participants) override; - bool isParticipant(const LLUUID& speaker_id) override; - - // Send a text message to the specified user, initiating the session if necessary. - // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; - - // Returns true if calling back the session URI after the session has closed is possible. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. - BOOL isSessionCallBackPossible(const LLUUID &session_id) override; - - // Returns true if the session can accepte text IM's. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. - BOOL isSessionTextIMPossible(const LLUUID &session_id) override; - - - //////////////////////////// - /// @name Channel stuff - //@{ - // returns true iff the user is currently in a proximal (local spatial) channel. - // Note that gestures should only fire if this returns true. - bool inProximalChannel() override; - - void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override - { + /// @name LLVoiceModuleInterface virtual implementations + /// @see LLVoiceModuleInterface + //@{ + void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) + void terminate() override; // Call this to clean up during shutdown + + static bool isShuttingDown() { return sShuttingDown; } + + const LLVoiceVersionInfo& getVersion() override; + + void updateSettings() override; // call after loading settings and whenever they change + + // Returns true if WebRTC has successfully logged in and is not in error state + bool isVoiceWorking() const override; + + std::string sipURIFromID(const LLUUID &id) override; + + ///////////////////// + /// @name Tuning + //@{ + void tuningStart() override; + void tuningStop() override; + bool inTuningMode() override; + + void tuningSetMicVolume(float volume) override; + void tuningSetSpeakerVolume(float volume) override; + float tuningGetEnergy(void) override; + //@} + + ///////////////////// + /// @name Devices + //@{ + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + bool deviceSettingsAvailable() override; + bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. + + // Requery the WebRTC daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + void refreshDeviceLists(bool clearCurrentList = true) override; + + void setCaptureDevice(const std::string& name) override; + void setRenderDevice(const std::string& name) override; + + LLVoiceDeviceList& getCaptureDevices() override; + LLVoiceDeviceList& getRenderDevices() override; + //@} + + void getParticipantList(std::set &participants) override; + bool isParticipant(const LLUUID& speaker_id) override; + + // Send a text message to the specified user, initiating the session if necessary. + // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; + + // Returns true if calling back the session URI after the session has closed is possible. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + BOOL isSessionCallBackPossible(const LLUUID &session_id) override; + + // WebRTC doesn't preclude text im + BOOL isSessionTextIMPossible(const LLUUID &session_id) override { return TRUE; } + + //////////////////////////// + /// @name Channel stuff + //@{ + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + bool inProximalChannel() override; + + void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override + { startAdHocSession(channelInfo, notify_on_first_join, hangup_on_last_leave); - } - - bool setSpatialChannel(const LLSD &channelInfo) override - { + } + + bool setSpatialChannel(const LLSD &channelInfo) override + { processChannels(true); - return true; - } - - void leaveNonSpatialChannel() override; + } + + void leaveNonSpatialChannel() override; - void processChannels(bool process) override; + void processChannels(bool process) override; - void leaveChannel(bool stopTalking); + void leaveChannel(bool stopTalking); - bool isCurrentChannel(const LLSD &channelInfo) override; - bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; - //@} + bool isCurrentChannel(const LLSD &channelInfo) override; + bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; + //@} - LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; } + LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; } LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override { return nullptr; } - - ///////////////////////// - /// @name Volume/gain - //@{ - void setVoiceVolume(F32 volume) override; - void setMicGain(F32 volume) override; - //@} - - ///////////////////////// - /// @name enable disable voice and features - //@{ - void setVoiceEnabled(bool enabled) override; - void setMuteMic(bool muted) override; // Set the mute state of the local mic. - //@} - - ////////////////////////// - /// @name nearby speaker accessors - //@{ - BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar - std::string getDisplayName(const LLUUID& id) override; - BOOL isParticipantAvatar(const LLUUID &id) override; - BOOL getIsSpeaking(const LLUUID& id) override; - BOOL getIsModeratorMuted(const LLUUID& id) override; - F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - BOOL getOnMuteList(const LLUUID& id) override; - F32 getUserVolume(const LLUUID& id) override; - void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) - //@} - - ////////////////////////// - /// @name Effect Accessors + + ///////////////////////// + /// @name Volume/gain //@{ - bool setVoiceEffect(const LLUUID &id) override { return false; } - const LLUUID getVoiceEffect() override { return LLUUID(); } - LLSD getVoiceEffectProperties(const LLUUID &id) override { return LLSD(); } - - void refreshVoiceEffectLists(bool clear_lists) override {}; - const voice_effect_list_t &getVoiceEffectList() const override { return mVoiceEffectList; } - const voice_effect_list_t &getVoiceEffectTemplateList() const override { return mVoiceEffectList; } - - voice_effect_list_t mVoiceEffectList; + void setVoiceVolume(F32 volume) override; + void setMicGain(F32 volume) override; //@} - - ////////////////////////////// - /// @name Status notification + + ///////////////////////// + /// @name enable disable voice and features //@{ - void addObserver(LLVoiceEffectObserver *observer) override {} - void removeObserver(LLVoiceEffectObserver *observer) override {} + void setVoiceEnabled(bool enabled) override; + void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} - - ////////////////////////////// - /// @name Preview buffer + + ////////////////////////// + /// @name nearby speaker accessors //@{ - void enablePreviewBuffer(bool enable) override {} - void recordPreviewBuffer() override {} - void playPreviewBuffer(const LLUUID &effect_id = LLUUID::null) override {} - void stopPreviewBuffer() override {} - - bool isPreviewRecording() override { return false; } - bool isPreviewPlaying() override { return false; } + BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar + std::string getDisplayName(const LLUUID& id) override; + BOOL isParticipantAvatar(const LLUUID &id) override; + BOOL getIsSpeaking(const LLUUID& id) override; + BOOL getIsModeratorMuted(const LLUUID& id) override; + F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id) override; + F32 getUserVolume(const LLUUID& id) override; + void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} ////////////////// @@ -237,76 +199,66 @@ public: void onChangeDetailed(const LLMute& ) override; //@} - // authorize the user + // authorize the user void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {}; - void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); + void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); void OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID); void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); void sendPositionUpdate(bool force); void updateOwnVolume(); - - ////////////////////////////// - /// @name Status notification - //@{ - void addObserver(LLVoiceClientStatusObserver* observer) override; - void removeObserver(LLVoiceClientStatusObserver* observer) override; - void addObserver(LLFriendObserver* observer) override; - void removeObserver(LLFriendObserver* observer) override; - void addObserver(LLVoiceClientParticipantObserver* observer) override; - void removeObserver(LLVoiceClientParticipantObserver* observer) override; - //@} - - ////////////////////////////// + + ////////////////////////////// + /// @name Status notification + //@{ + void addObserver(LLVoiceClientStatusObserver* observer) override; + void removeObserver(LLVoiceClientStatusObserver* observer) override; + void addObserver(LLFriendObserver* observer) override; + void removeObserver(LLFriendObserver* observer) override; + void addObserver(LLVoiceClientParticipantObserver* observer) override; + void removeObserver(LLVoiceClientParticipantObserver* observer) override; + //@} + + ////////////////////////////// /// @name Devices change notification - // LLWebRTCDevicesObserver + // LLWebRTCDevicesObserver //@{ void OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override; //@} - struct participantState - { - public: - participantState(const LLUUID& agent_id); - - bool updateMuteState(); // true if mute state has changed - bool isAvatar(); - - std::string mURI; - LLUUID mAvatarID; - std::string mDisplayName; - LLFrameTimer mSpeakingTimeout; - F32 mLastSpokeTimestamp; - F32 mLevel; - F32 mVolume; - std::string mGroupID; - int mUserVolume; - bool mPTT; - bool mIsSpeaking; - bool mIsModeratorMuted; - bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) - bool mVolumeSet; // true if incoming volume messages should not change the volume - bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) - bool mAvatarIDValid; - bool mIsSelf; - }; + struct participantState + { + public: + participantState(const LLUUID& agent_id); + + bool updateMuteState(); // true if mute state has changed + bool isAvatar(); + + std::string mURI; + LLUUID mAvatarID; + std::string mDisplayName; + LLFrameTimer mSpeakingTimeout; + F32 mLevel; // the current audio level of the participant + F32 mVolume; // the gain applied to the participant + bool mIsSpeaking; + bool mIsModeratorMuted; + bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) + }; typedef boost::shared_ptr participantStatePtr_t; - typedef boost::weak_ptr participantStateWptr_t; - participantStatePtr_t findParticipantByID(const std::string &channelID, const LLUUID &id); - participantStatePtr_t addParticipantByID(const std::string& channelID, const LLUUID &id); - void removeParticipantByID(const std::string& channelID, const LLUUID &id); + participantStatePtr_t findParticipantByID(const std::string &channelID, const LLUUID &id); + participantStatePtr_t addParticipantByID(const std::string& channelID, const LLUUID &id); + void removeParticipantByID(const std::string& channelID, const LLUUID &id); protected: - typedef std::map participantMap; typedef std::map participantUUIDMap; - - class sessionState - { + + class sessionState + { public: typedef boost::shared_ptr ptr_t; typedef boost::weak_ptr wptr_t; @@ -314,28 +266,24 @@ public: typedef boost::function sessionFunc_t; static void addSession(const std::string &channelID, ptr_t& session); - virtual ~sessionState(); - + virtual ~sessionState(); + participantStatePtr_t addParticipant(const LLUUID& agent_id); void removeParticipant(const participantStatePtr_t &participant); - void removeAllParticipants(); + void removeAllParticipants(); - participantStatePtr_t findParticipant(const std::string &uri); participantStatePtr_t findParticipantByID(const LLUUID& id); static ptr_t matchSessionByChannelID(const std::string& channel_id); - void shutdownAllConnections(); + void shutdownAllConnections(); void revive(); - bool isCallBackPossible(); - bool isTextIMPossible(); - - static void processSessionStates(); + static void processSessionStates(); virtual bool processConnectionStates(); - virtual void sendData(const std::string &data); + virtual void sendData(const std::string &data); void setMuteMic(bool muted); void setMicGain(F32 volume); @@ -343,80 +291,61 @@ public: void setUserVolume(const LLUUID& id, F32 volume); void setUserMute(const LLUUID& id, bool mute); - + static void for_each(sessionFunc_t func); - static void reapEmptySessions(); + static void reapEmptySessions(); - bool isEmpty() { return mWebRTCConnections.empty(); } + bool isEmpty() { return mWebRTCConnections.empty(); } - virtual bool isSpatial() = 0; + virtual bool isSpatial() = 0; virtual bool isEstate() = 0; + virtual bool isCallbackPossible() = 0; - std::string mHandle; - std::string mGroupHandle; - std::string mChannelID; - std::string mAlias; - std::string mName; - std::string mErrorStatusString; - std::queue mTextMsgQueue; + std::string mHandle; + std::string mChannelID; + std::string mName; - bool mMuted; // this session is muted. - F32 mMicGain; // gain for this session. - F32 mSpeakerVolume; // volume for this session. - - LLUUID mIMSessionID; - LLUUID mCallerID; - int mErrorStatusCode; - - bool mIncoming; - bool mVoiceActive; - bool mReconnect; // Whether we should try to reconnect to this session if it's dropped - bool mShuttingDown; + bool mMuted; // this session is muted. + F32 mMicGain; // gain for this session. + F32 mSpeakerVolume; // volume for this session. - // Set to true when the volume/mute state of someone in the participant list changes. - // The code will have to walk the list to find the changed participant(s). - bool mVolumeDirty; - bool mMuteDirty; - - participantMap mParticipantsByURI; - participantUUIDMap mParticipantsByUUID; + bool mShuttingDown; - LLUUID mVoiceFontID; + participantUUIDMap mParticipantsByUUID; static bool hasSession(const std::string &sessionID) - { return mSessions.find(sessionID) != mSessions.end(); } + { return mSessions.find(sessionID) != mSessions.end(); } - bool mHangupOnLastLeave; - bool mNotifyOnFirstJoin; + bool mHangupOnLastLeave; // notify observers after the session becomes empty. + bool mNotifyOnFirstJoin; // notify observers when the first peer joins. protected: sessionState(); - std::list mWebRTCConnections; + std::list mWebRTCConnections; private: static std::map mSessions; // canonical list of outstanding sessions. - static void for_eachPredicate(const std::pair &a, sessionFunc_t func); - - static bool testByCreatingURI(const LLWebRTCVoiceClient::sessionState::wptr_t &a, std::string uri); - static bool testByCallerId(const LLWebRTCVoiceClient::sessionState::wptr_t &a, LLUUID participantId); - - }; + static void for_eachPredicate(const std::pair &a, + sessionFunc_t func); + }; typedef boost::shared_ptr sessionStatePtr_t; typedef std::map sessionMap; - class estateSessionState : public sessionState - { + class estateSessionState : public sessionState + { public: estateSessionState(); bool processConnectionStates() override; - bool isSpatial() override { return true; } + bool isSpatial() override { return true; } bool isEstate() override { return true; } - }; + bool isCallbackPossible() override { return false; } + }; class parcelSessionState : public sessionState { @@ -425,31 +354,34 @@ public: bool isSpatial() override { return true; } bool isEstate() override { return false; } + bool isCallbackPossible() override { return false; } }; class adhocSessionState : public sessionState { public: adhocSessionState(const std::string &channelID, - const std::string& credentials, - bool notify_on_first_join, - bool hangup_on_last_leave); + const std::string& credentials, + bool notify_on_first_join, + bool hangup_on_last_leave); bool isSpatial() override { return false; } bool isEstate() override { return false; } - // don't send spatial data to adhoc sessions. - void sendData(const std::string &data) override { } + // only p2p-type adhoc sessions allow callback + bool isCallbackPossible() override { return mNotifyOnFirstJoin && mHangupOnLastLeave; } - protected: + // don't send spatial data to adhoc sessions. + void sendData(const std::string &data) override { } + + protected: std::string mCredentials; }; - - /////////////////////////////////////////////////////// - // Private Member Functions - ////////////////////////////////////////////////////// + /////////////////////////////////////////////////////// + // Private Member Functions + ////////////////////////////////////////////////////// static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data); static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level); @@ -460,236 +392,148 @@ public: static void predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, bool mute); static void predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, F32 volume); - ////////////////////////////// - /// @name TVC/Server management and communication - //@{ - - // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. - void giveUp(); - -// void requestVoiceAccountProvision(S32 retries = 3); - - - //@} - - //---------------------------------- - // devices - void clearCaptureDevices(); - void addCaptureDevice(const LLVoiceDevice& device); + //---------------------------------- + // devices + void clearCaptureDevices(); + void addCaptureDevice(const LLVoiceDevice& device); void clearRenderDevices(); - void addRenderDevice(const LLVoiceDevice& device); - void setDevicesListUpdated(bool state); - void buildSetAudioDevices(std::ostringstream &stream); - - // local audio updates, mic mute, speaker mute, mic volume and speaker volumes - void sendLocalAudioUpdates(); - - ///////////////////////////// - // Event handlers - - void muteListChanged(); - - ///////////////////////////// - // Sending updates of current state - void updatePosition(void); - void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); - void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); - bool channelFromRegion(LLViewerRegion *region, std::string &name); - - LLVector3d getListenerPosition() { return mListenerPosition; } - LLVector3d getSpeakerPosition() { return mAvatarPosition; } + void addRenderDevice(const LLVoiceDevice& device); + void setDevicesListUpdated(bool state); - void setEarLocation(S32 loc); + ///////////////////////////// + // Sending updates of current state + void updatePosition(void); + void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); - - ///////////////////////////// - // Accessors for data related to nearby speakers + LLVector3d getListenerPosition() { return mListenerPosition; } + LLVector3d getSpeakerPosition() { return mAvatarPosition; } - // MBW -- XXX -- Not sure how to get this data out of the TVC - BOOL getUsingPTT(const LLUUID& id); - std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) + void setEarLocation(S32 loc); - ///////////////////////////// - BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. - // Use this to determine whether to show a "no speech" icon in the menu bar. - void sessionEstablished(const LLUUID& region_id); + ///////////////////////////// + // Accessors for data related to nearby speakers + + ///////////////////////////// sessionStatePtr_t findP2PSession(const LLUUID &agent_id); - + sessionStatePtr_t addSession(const std::string &channel_id, sessionState::ptr_t session); void deleteSession(const sessionStatePtr_t &session); - - // This is called in several places where the session _may_ need to be deleted. - // It contains logic for whether to delete the session or keep it around. - void reapSession(const sessionStatePtr_t &session); - - // Does the actual work to get out of the audio session - void leaveAudioSession(); - - friend class LLWebRTCVoiceClientCapResponder; - - - void lookupName(const LLUUID &id); - void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - void avatarNameResolved(const LLUUID &id, const std::string &name); + + // Does the actual work to get out of the audio session + void leaveAudioSession(); + + friend class LLWebRTCVoiceClientCapResponder; + + + void lookupName(const LLUUID &id); + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + void avatarNameResolved(const LLUUID &id, const std::string &name); static void predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name); - boost::signals2::connection mAvatarNameCacheConnection; - - ///////////////////////////// - // Voice fonts - - void addVoiceFont(const S32 id, - const std::string &name, - const std::string &description, - const LLDate &expiration_date, - bool has_expired, - const S32 font_type, - const S32 font_status, - const bool template_font = false); - void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); - void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); + boost::signals2::connection mAvatarNameCacheConnection; private: - float getAudioLevel(); - - LLVoiceVersionInfo mVoiceVersion; + // helper function to retrieve the audio level + // Used in multiple places. + float getAudioLevel(); // Coroutine support methods //--- void voiceConnectionCoro(); - void voiceConnectionStateMachine(); - //--- /// Clean up objects created during a voice session. - void cleanUp(); - - bool mRelogRequested; - // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). - // The larger it is the greater is possibility there is a problem with connection to voice server. - // Introduced while fixing EXT-4313. - int mSpatialJoiningNum; - - static void idle(void *user_data); - - bool mTuningMode; - F32 mTuningMicGain; - int mTuningSpeakerVolume; - bool mTuningSpeakerVolumeDirty; - bool mDevicesListUpdated; // set to true when the device list has been updated - // and false when the panelvoicedevicesettings has queried for an update status. - std::string mSpatialSessionCredentials; - - std::string mMainSessionGroupHandle; // handle of the "main" session group. - - bool mAreaVoiceDisabled; + void cleanUp(); + + bool mTuningMode; + F32 mTuningMicGain; + int mTuningSpeakerVolume; + bool mDevicesListUpdated; // set to true when the device list has been updated + // and false when the panelvoicedevicesettings has queried for an update status. + std::string mSpatialSessionCredentials; + + std::string mMainSessionGroupHandle; // handle of the "main" session group. + sessionStatePtr_t mSession; // Session state for the current session - sessionStatePtr_t mNextSession; // Session state for the session we're trying to join - - S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings - std::string mCurrentRegionName; // Used to detect parcel boundary crossings - - bool mBuddyListMapPopulated; - bool mBlockRulesListReceived; - bool mAutoAcceptRulesListReceived; - - llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; + sessionStatePtr_t mNextSession; // Session state for the session we're trying to join - LLVoiceDeviceList mCaptureDevices; - LLVoiceDeviceList mRenderDevices; + llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface; - bool mIsInitialized; - bool mShutdownComplete; + LLVoiceDeviceList mCaptureDevices; + LLVoiceDeviceList mRenderDevices; - bool startEstateSession(); + bool startEstateSession(); bool startParcelSession(const std::string& channelID, S32 parcelID); bool startAdHocSession(const LLSD &channelInfo, bool notify_on_first_join, bool hangup_on_last_leave); - - std::string nameFromID(const LLUUID &id); - bool IDFromName(const std::string name, LLUUID &uuid); - bool inSpatialChannel(); + bool inSpatialChannel(); bool inOrJoiningChannel(const std::string &channelID); bool inEstateChannel(); - LLSD getAudioSessionChannelInfo(); - + LLSD getAudioSessionChannelInfo(); + void setHidden(bool hidden) override; //virtual - void enforceTether(); + void enforceTether(); - void updateNeighboringRegions(); + void updateNeighboringRegions(); std::set getNeighboringRegions() { return mNeighboringRegions; } - - bool mSpatialCoordsDirty; - - LLVector3d mListenerPosition; - LLVector3d mListenerRequestedPosition; - LLVector3 mListenerVelocity; - LLQuaternion mListenerRot; - - LLVector3d mAvatarPosition; - LLVector3 mAvatarVelocity; - LLQuaternion mAvatarRot; - - std::set mNeighboringRegions; // includes current region - - bool mMuteMic; - bool mMuteMicDirty; - bool mHidden; //Set to true during teleport to hide the agent's position. - - // Set to true when the friends list is known to have changed. - bool mFriendsListDirty; - - enum - { - earLocCamera = 0, // ear at camera - earLocAvatar, // ear at avatar - earLocMixed // ear at avatar location/camera direction - }; - - S32 mEarLocation; - - bool mSpeakerVolumeDirty; - float mSpeakerVolume; - - F32 mMicGain; - - bool mVoiceEnabled; - bool mProcessChannels; - - BOOL mLipSyncEnabled; - - typedef std::set observer_set_t; - observer_set_t mParticipantObservers; - - void notifyParticipantObservers(); - - typedef std::set status_observer_set_t; - status_observer_set_t mStatusObservers; - - void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); - - typedef std::set friend_observer_set_t; - friend_observer_set_t mFriendObservers; - void notifyFriendObservers(); - - S32 mPlayRequestCount; + + LLVoiceVersionInfo mVoiceVersion; + + bool mSpatialCoordsDirty; + + LLVector3d mListenerPosition; + LLVector3d mListenerRequestedPosition; + LLVector3 mListenerVelocity; + LLQuaternion mListenerRot; + + LLVector3d mAvatarPosition; + LLVector3 mAvatarVelocity; + LLQuaternion mAvatarRot; + + std::set mNeighboringRegions; // includes current region + + bool mMuteMic; + bool mHidden; //Set to true during teleport to hide the agent's position. + + enum + { + earLocCamera = 0, // ear at camera + earLocAvatar, // ear at avatar + earLocMixed // ear at avatar location/camera direction + }; + + S32 mEarLocation; + + float mSpeakerVolume; + + F32 mMicGain; + + bool mVoiceEnabled; + bool mProcessChannels; + + typedef std::set observer_set_t; + observer_set_t mParticipantObservers; + + void notifyParticipantObservers(); + + typedef std::set status_observer_set_t; + status_observer_set_t mStatusObservers; + + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); + bool mIsInTuningMode; - bool mIsJoiningSession; - bool mIsWaitingForFonts; - bool mIsLoggingIn; bool mIsProcessingChannels; bool mIsCoroutineActive; // These variables can last longer than WebRTC in coroutines so we need them as static static bool sShuttingDown; - static bool sConnected; - static LLPumpIO* sPump; LLEventMailDrop mWebRTCPump; }; @@ -728,15 +572,15 @@ class LLVoiceWebRTCStats : public LLSingleton }; class LLVoiceWebRTCConnection : - public llwebrtc::LLWebRTCSignalingObserver, - public llwebrtc::LLWebRTCDataObserver + public llwebrtc::LLWebRTCSignalingObserver, + public llwebrtc::LLWebRTCDataObserver { public: LLVoiceWebRTCConnection(const LLUUID ®ionID, const std::string &channelID); virtual ~LLVoiceWebRTCConnection() = 0; - ////////////////////////////// + ////////////////////////////// /// @name Signaling notification // LLWebRTCSignalingObserver //@{ @@ -759,7 +603,7 @@ class LLVoiceWebRTCConnection : void sendJoin(); void sendData(const std::string &data); - virtual void processIceUpdates(); + virtual void processIceUpdates(); virtual void onIceUpdateComplete(bool ice_completed, const LLSD &result); virtual void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result); @@ -772,9 +616,9 @@ class LLVoiceWebRTCConnection : bool connectionStateMachine(); - virtual bool isSpatial() = 0; + virtual bool isSpatial() = 0; - LLUUID getRegionID() { return mRegionID; } + LLUUID getRegionID() { return mRegionID; } void shutDown() { @@ -840,16 +684,16 @@ class LLVoiceWebRTCConnection : void OnVoiceDisconnectionRequestSuccess(const LLSD &body); void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); - LLUUID mRegionID; + LLUUID mRegionID; LLUUID mViewerSession; std::string mChannelID; std::string mChannelSDP; std::string mRemoteChannelSDP; - bool mMuted; - F32 mMicGain; - F32 mSpeakerVolume; + bool mMuted; + F32 mMicGain; + F32 mSpeakerVolume; bool mShutDown; S32 mOutstandingRequests; @@ -865,7 +709,7 @@ class LLVoiceWebRTCConnection : class LLVoiceWebRTCSpatialConnection : - public LLVoiceWebRTCConnection + public LLVoiceWebRTCConnection { public: LLVoiceWebRTCSpatialConnection(const LLUUID ®ionID, S32 parcelLocalID, const std::string &channelID); @@ -874,12 +718,12 @@ class LLVoiceWebRTCSpatialConnection : void setMuteMic(bool muted) override; - bool isSpatial() override { return true; } + bool isSpatial() override { return true; } protected: - bool requestVoiceConnection() override; + bool requestVoiceConnection() override; S32 mParcelLocalID; }; @@ -891,12 +735,12 @@ class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection virtual ~LLVoiceWebRTCAdHocConnection(); - bool isSpatial() override { return false; } + bool isSpatial() override { return false; } protected: bool requestVoiceConnection() override; - std::string mCredentials; + std::string mCredentials; }; #define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__); -- 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 ++++++++++++++++++++--------------------- indra/newview/llvoicewebrtc.h | 6 ++--- 2 files changed, 28 insertions(+), 28 deletions(-) (limited to 'indra/newview') 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 diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e6f01fd181..3ff801ed56 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -584,12 +584,12 @@ class LLVoiceWebRTCConnection : /// @name Signaling notification // LLWebRTCSignalingObserver //@{ - void OnIceGatheringState(IceGatheringState state) override; + void OnIceGatheringState(EIceGatheringState state) override; void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override; void OnOfferAvailable(const std::string &sdp) override; void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; - void OnPeerShutDown() override; + void OnPeerConnectionShutdown() override; //@} ///////////////////////// @@ -702,7 +702,7 @@ class LLVoiceWebRTCConnection : bool mIceCompleted; bool mTrickling; - llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; + llwebrtc::LLWebRTCPeerConnectionInterface *mWebRTCPeerConnectionInterface; llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface; }; -- 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/llvoiceclient.cpp | 138 ++---- indra/newview/llvoiceclient.h | 110 +++-- indra/newview/llvoicevivox.cpp | 1002 +++++++++++++++++++-------------------- indra/newview/llvoicevivox.h | 262 +++++----- indra/newview/llvoicewebrtc.cpp | 218 ++++----- indra/newview/llvoicewebrtc.h | 125 +++-- 6 files changed, 873 insertions(+), 982 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 9f99549829..33e76c14ae 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1,25 +1,25 @@ - /** + /** * @file llvoiceclient.cpp * @brief Voice client delegation class implementation. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, 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$ */ @@ -81,10 +81,10 @@ LLVoiceHandler gVoiceHandler; std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus) { std::string result = "UNTRANSLATED"; - + // Prevent copy-paste errors when updating this list... #define CASE(x) case x: result = #x; break - + switch(inStatus) { CASE(STATUS_LOGIN_RETRY); @@ -107,9 +107,9 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv } break; } - + #undef CASE - + return result; } @@ -209,7 +209,7 @@ void LLVoiceClient::onSimulatorFeaturesReceived(const LLUUID& region_id) void LLVoiceClient::setSpatialVoiceModule(const std::string &voice_server_type) { LLVoiceModuleInterface *module = getVoiceModule(voice_server_type); - if (!module) + if (!module) { return; } @@ -264,7 +264,7 @@ void LLVoiceClient::terminate() const LLVoiceVersionInfo LLVoiceClient::getVersion() { - if (mSpatialVoiceModule) + if (mSpatialVoiceModule) { return mSpatialVoiceModule->getVersion(); } @@ -330,12 +330,12 @@ float LLVoiceClient::tuningGetEnergy(void) // devices bool LLVoiceClient::deviceSettingsAvailable() -{ +{ return LLWebRTCVoiceClient::getInstance()->deviceSettingsAvailable(); } bool LLVoiceClient::deviceSettingsUpdated() -{ +{ return LLWebRTCVoiceClient::getInstance()->deviceSettingsUpdated(); } @@ -363,7 +363,7 @@ const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() -{ +{ return LLWebRTCVoiceClient::getInstance()->getRenderDevices(); } @@ -379,7 +379,8 @@ void LLVoiceClient::getParticipantList(std::set &participants) bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) { - return LLWebRTCVoiceClient::getInstance()->isParticipant(speaker_id) || LLVivoxVoiceClient::getInstance()->isParticipant(speaker_id); + return LLWebRTCVoiceClient::getInstance()->isParticipant(speaker_id) || + LLVivoxVoiceClient::getInstance()->isParticipant(speaker_id); } @@ -388,14 +389,8 @@ bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id) { - if (mSpatialVoiceModule) - { - return mSpatialVoiceModule->isSessionTextIMPossible(id); - } - else - { - return FALSE; - } + // all sessions can do TextIM, as we no longer support PSTN + return TRUE; } BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) @@ -409,7 +404,7 @@ BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) bool LLVoiceClient::inProximalChannel() { - if (mSpatialVoiceModule) + if (mSpatialVoiceModule) { return mSpatialVoiceModule->inProximalChannel(); } @@ -552,7 +547,7 @@ void LLVoiceClient::updateMicMuteLogic() { // If not configured to use PTT, the mic should be open (otherwise the user will be unable to speak). bool new_mic_mute = false; - + if(mUsePTT) { // If configured to use PTT, track the user state. @@ -603,7 +598,7 @@ void LLVoiceClient::setUsePTT(bool usePTT) mUserPTTState = false; } mUsePTT = usePTT; - + updateMicMuteLogic(); } @@ -614,7 +609,7 @@ void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle) // When the user turns off toggle, reset the current state. mUserPTTState = false; } - + mPTTIsToggle = PTTIsToggle; updateMicMuteLogic(); @@ -629,12 +624,12 @@ void LLVoiceClient::inputUserControlState(bool down) { if(mPTTIsToggle) { - if(down) // toggle open-mic state on 'down' + if(down) // toggle open-mic state on 'down' { toggleUserPTTState(); } } - else // set open-mic state as an absolute + else // set open-mic state as an absolute { setUserPTTState(down); } @@ -651,39 +646,23 @@ void LLVoiceClient::toggleUserPTTState(void) BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) { - if (mNonSpatialVoiceModule) - { - return mNonSpatialVoiceModule->getVoiceEnabled(id); - } - else if (mSpatialVoiceModule) - { - return mSpatialVoiceModule->getVoiceEnabled(id); - } - else - { - return FALSE; - } + return isParticipant(id) ? TRUE : FALSE; } std::string LLVoiceClient::getDisplayName(const LLUUID& id) { - if (mNonSpatialVoiceModule) + std::string result = LLWebRTCVoiceClient::getInstance()->getDisplayName(id); + if (result.empty()) { - return mNonSpatialVoiceModule->getDisplayName(id); - } - else if (mSpatialVoiceModule) - { - return mSpatialVoiceModule->getDisplayName(id); - } - else - { - return std::string(); + result = LLVivoxVoiceClient::getInstance()->getDisplayName(id); } + return result; } bool LLVoiceClient::isVoiceWorking() const { - return LLVivoxVoiceClient::getInstance()->isVoiceWorking() || LLWebRTCVoiceClient::getInstance()->isVoiceWorking(); + return LLVivoxVoiceClient::getInstance()->isVoiceWorking() || + LLWebRTCVoiceClient::getInstance()->isVoiceWorking(); } BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id) @@ -698,48 +677,29 @@ BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id) BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) { - return LLVivoxVoiceClient::getInstance()->getIsSpeaking(id) || LLWebRTCVoiceClient::getInstance()->getIsSpeaking(id); + return LLWebRTCVoiceClient::getInstance()->getIsSpeaking(id) || + LLVivoxVoiceClient::getInstance()->getIsSpeaking(id); } BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) { // don't bother worrying about p2p calls, as // p2p calls don't have mute. - if (mNonSpatialVoiceModule) - { - return mNonSpatialVoiceModule->getIsModeratorMuted(id); - } - else if (mSpatialVoiceModule) - { - return mSpatialVoiceModule->getIsModeratorMuted(id); - } - else - { - return FALSE; - } + return LLWebRTCVoiceClient::getInstance()->getIsModeratorMuted(id) || + LLVivoxVoiceClient::getInstance()->getIsModeratorMuted(id); } F32 LLVoiceClient::getCurrentPower(const LLUUID& id) -{ - return std::fmax(LLVivoxVoiceClient::getInstance()->getCurrentPower(id), LLWebRTCVoiceClient::getInstance()->getCurrentPower(id)); +{ + return std::fmax(LLVivoxVoiceClient::getInstance()->getCurrentPower(id), + LLWebRTCVoiceClient::getInstance()->getCurrentPower(id)); } BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) { // don't bother worrying about p2p calls, as // p2p calls don't have mute. - if (mNonSpatialVoiceModule) - { - return mNonSpatialVoiceModule->getOnMuteList(id); - } - else if (mSpatialVoiceModule) - { - return mSpatialVoiceModule->getOnMuteList(id); - } - else - { - return FALSE; - } + return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); } F32 LLVoiceClient::getUserVolume(const LLUUID& id) @@ -798,7 +758,7 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id) { return mNonSpatialVoiceModule->sipURIFromID(id); } - else if (mSpatialVoiceModule) + else if (mSpatialVoiceModule) { return mSpatialVoiceModule->sipURIFromID(id); } @@ -854,7 +814,7 @@ class LLViewerRequiredVoiceVersion : public LLHTTPNode } LLVoiceVersionInfo versionInfo = voiceModule->getVersion(); - if (input.has("body") && input["body"].has("major_version") && + if (input.has("body") && input["body"].has("major_version") && input["body"]["major_version"].asInteger() > versionInfo.majorVersion) { if (!sAlertedUser) @@ -879,26 +839,26 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode { //the parcel you are in has changed something about its //voice information - + //this is a misnomer, as it can also be when you are not in //a parcel at all. Should really be something like //LLViewerVoiceInfoChanged..... if ( input.has("body") ) { LLSD body = input["body"]; - + //body has "region_name" (str), "parcel_local_id"(int), //"voice_credentials" (map). - + //body["voice_credentials"] has "channel_uri" (str), //body["voice_credentials"] has "channel_credentials" (str) - + //if we really wanted to be extra careful, //we'd check the supplied //local parcel id to make sure it's for the same parcel //we believe we're in if ( body.has("voice_credentials") ) - { + { LLVoiceClient::getInstance()->setSpatialChannel(body["voice_credentials"]); } } @@ -941,7 +901,7 @@ void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 vo bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume) { speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id); - + if (it != mSpeakersData.end()) { volume = it->second; @@ -1020,9 +980,9 @@ void LLSpeakerVolumeStorage::load() if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(settings_llsd, file)) { LL_WARNS("Voice") << "failed to parse " << filename << LL_ENDL; - + } - + } for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 79bca23fc4..46dd325e21 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -1,25 +1,25 @@ -/** +/** * @file llvoiceclient.h * @brief Declaration of LLVoiceClient class which is the interface to the voice client process. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, 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$ */ @@ -147,32 +147,32 @@ class LLVoiceModuleInterface public: LLVoiceModuleInterface() {} virtual ~LLVoiceModuleInterface() {} - + virtual void init(LLPumpIO *pump)=0; // Call this once at application startup (creates connector) virtual void terminate()=0; // Call this to clean up during shutdown - + virtual void updateSettings()=0; // call after loading settings and whenever they change - + virtual bool isVoiceWorking() const = 0; // connected to a voice server and voice channel - + virtual void setHidden(bool hidden)=0; // Hides the user from voice. virtual const LLVoiceVersionInfo& getVersion()=0; - + ///////////////////// /// @name Tuning //@{ virtual void tuningStart()=0; virtual void tuningStop()=0; virtual bool inTuningMode()=0; - + virtual void tuningSetMicVolume(float volume)=0; virtual void tuningSetSpeakerVolume(float volume)=0; virtual float tuningGetEnergy(void)=0; //@} - + ///////////////////// /// @name Devices //@{ @@ -180,36 +180,36 @@ public: // i.e. when the daemon is running and connected, and the device lists are populated. virtual bool deviceSettingsAvailable()=0; virtual bool deviceSettingsUpdated() = 0; - + // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. virtual void refreshDeviceLists(bool clearCurrentList = true)=0; - + virtual void setCaptureDevice(const std::string& name)=0; virtual void setRenderDevice(const std::string& name)=0; - + virtual LLVoiceDeviceList& getCaptureDevices()=0; virtual LLVoiceDeviceList& getRenderDevices()=0; - + virtual void getParticipantList(std::set &participants)=0; virtual bool isParticipant(const LLUUID& speaker_id)=0; //@} - + //////////////////////////// /// @ name Channel stuff //@{ // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. virtual bool inProximalChannel()=0; - + virtual void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave)=0; - + virtual bool setSpatialChannel(const LLSD& channelInfo)=0; - + virtual void leaveNonSpatialChannel() = 0; virtual void processChannels(bool process) = 0; @@ -217,12 +217,12 @@ public: virtual bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) = 0; //@} - - + + ////////////////////////// /// @name p2p //@{ - + // initiate a call with a peer using the P2P interface, which only applies to some // voice server types. Otherwise, a group call should be used for P2P virtual LLVoiceP2POutgoingCallInterface* getOutgoingCallInterface() = 0; @@ -231,35 +231,33 @@ public: // answer or decline. virtual LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) = 0; //@} - + ///////////////////////// /// @name Volume/gain //@{ virtual void setVoiceVolume(F32 volume)=0; virtual void setMicGain(F32 volume)=0; //@} - + ///////////////////////// /// @name enable disable voice and features //@{ virtual void setVoiceEnabled(bool enabled)=0; virtual void setMuteMic(bool muted)=0; // Set the mute state of the local mic. //@} - + ////////////////////////// /// @name nearby speaker accessors //@{ - virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar virtual std::string getDisplayName(const LLUUID& id)=0; virtual BOOL isParticipantAvatar(const LLUUID &id)=0; virtual BOOL getIsSpeaking(const LLUUID& id)=0; virtual BOOL getIsModeratorMuted(const LLUUID& id)=0; virtual F32 getCurrentPower(const LLUUID& id)=0; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id)=0; virtual F32 getUserVolume(const LLUUID& id)=0; - virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal) + virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} - + ////////////////////////// /// @name text chat //@{ @@ -267,25 +265,25 @@ public: virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0; //virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; //@} - + // authorize the user virtual void userAuthorized(const std::string& user_id, const LLUUID &agentID)=0; - + ////////////////////////////// /// @name Status notification //@{ virtual void addObserver(LLVoiceClientStatusObserver* observer)=0; virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0; virtual void addObserver(LLFriendObserver* observer)=0; - virtual void removeObserver(LLFriendObserver* observer)=0; + virtual void removeObserver(LLFriendObserver* observer)=0; virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0; - virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0; + virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0; //@} - + virtual std::string sipURIFromID(const LLUUID &id)=0; //@} - + }; @@ -357,9 +355,9 @@ public: micro_changed_signal_t mMicroChangedSignal; void terminate(); // Call this to clean up during shutdown - + const LLVoiceVersionInfo getVersion(); - + static const F32 OVERDRIVEN_POWER_LEVEL; static const F32 VOLUME_MIN; @@ -374,18 +372,18 @@ public: void tuningStart(); void tuningStop(); bool inTuningMode(); - + void tuningSetMicVolume(float volume); void tuningSetSpeakerVolume(float volume); float tuningGetEnergy(void); - + // devices - + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. bool deviceSettingsAvailable(); bool deviceSettingsUpdated(); // returns true when the device list has been updated recently. - + // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). @@ -402,7 +400,7 @@ public: //////////////////////////// // Channel stuff // - + // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. bool inProximalChannel(); @@ -426,35 +424,35 @@ public: LLVoiceP2POutgoingCallInterface* getOutgoingCallInterface(const LLSD& voiceChannelInfo); LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voiceCallInfo); - + ///////////////////////////// // Sending updates of current state - + void setVoiceVolume(F32 volume); void setMicGain(F32 volume); - void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) bool voiceEnabled(); void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. void setUserPTTState(bool ptt); bool getUserPTTState(); void toggleUserPTTState(void); - void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs void setVoiceEnabled(bool enabled); void setUsePTT(bool usePTT); void setPTTIsToggle(bool PTTIsToggle); - bool getPTTIsToggle(); + bool getPTTIsToggle(); void updateMicMuteLogic(); boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); } - + ///////////////////////////// // Accessors for data related to nearby speakers BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - std::string getDisplayName(const LLUUID& id); + std::string getDisplayName(const LLUUID& id); BOOL isOnlineSIP(const LLUUID &id); BOOL isParticipantAvatar(const LLUUID &id); BOOL getIsSpeaking(const LLUUID& id); @@ -466,7 +464,7 @@ public: ///////////////////////////// void getParticipantList(std::set &participants); bool isParticipant(const LLUUID& speaker_id); - + ////////////////////////// /// @name text chat //@{ @@ -474,7 +472,7 @@ public: BOOL isSessionCallBackPossible(const LLUUID& id); //BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return true;} ; //@} - + void setSpatialVoiceModule(const std::string& voice_server_type); void setNonSpatialVoiceModule(const std::string &voice_server_type); @@ -483,15 +481,15 @@ public: void onRegionChanged(); void onSimulatorFeaturesReceived(const LLUUID ®ion_id); - + void addObserver(LLVoiceClientStatusObserver* observer); void removeObserver(LLVoiceClientStatusObserver* observer); void addObserver(LLFriendObserver* observer); void removeObserver(LLFriendObserver* observer); void addObserver(LLVoiceClientParticipantObserver* observer); void removeObserver(LLVoiceClientParticipantObserver* observer); - - std::string sipURIFromID(const LLUUID &id); + + std::string sipURIFromID(const LLUUID &id); ////////////////////////// /// @name Voice effects @@ -517,7 +515,7 @@ protected: bool mPTTDirty; bool mPTT; - + bool mUsePTT; S32 mPTTMouseButton; KEY mPTTKey; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 1454342083..9a1fb925eb 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1,25 +1,25 @@ - /** + /** * @file LLVivoxVoiceClient.cpp * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, 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$ */ @@ -96,7 +96,7 @@ namespace { // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.5f; - // Timeout for connection to Vivox + // Timeout for connection to Vivox const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f; const F32 CONNECT_DNS_TIMEOUT = 5.0f; const int CONNECT_RETRY_MAX = 3; @@ -115,8 +115,8 @@ namespace { 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 + // 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; @@ -135,17 +135,17 @@ namespace { 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 Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 + // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. + // Map it to Vivox 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 Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 + // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. + // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 return 30 + (int)(volume * 40.0f); - + } @@ -364,18 +364,18 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceVersion.minorVersion = 0; mVoiceVersion.mBuildVersion = ""; mVoiceVersion.serverVersion = ""; - + // 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 vivox 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); @@ -415,13 +415,13 @@ void LLVivoxVoiceClient::terminate() return; } - // needs to be done manually here since we will not get another pass in + // needs to be done manually here since we will not get another pass in // coroutines... that mechanism is long since gone. if (mIsLoggedIn) { logoutOfVivox(false); } - + if(sConnected) { breakVoiceConnection(false); @@ -442,7 +442,7 @@ void LLVivoxVoiceClient::terminate() void LLVivoxVoiceClient::cleanUp() { LL_DEBUGS("Voice") << LL_ENDL; - + deleteAllSessions(); deleteAllVoiceFonts(); deleteVoiceFontTemplates(); @@ -484,7 +484,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str) 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; @@ -493,7 +493,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str) mSocket->getSocket(), (const char*)str.data(), &written); - + if(err == 0 && written == size) { // Success. @@ -516,7 +516,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str) daemonDied(); } } - + return result; } @@ -527,7 +527,7 @@ void LLVivoxVoiceClient::connectorCreate() { std::ostringstream stream; std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); - + // Transition to stateConnectorStarted when the connector handle comes back. std::string vivoxLogLevel = gSavedSettings.getString("VivoxDebugLevel"); if ( vivoxLogLevel.empty() ) @@ -535,8 +535,8 @@ void LLVivoxVoiceClient::connectorCreate() vivoxLogLevel = "0"; } LL_DEBUGS("Voice") << "creating connector with log level " << vivoxLogLevel << LL_ENDL; - - stream + + stream << "" << "V2 SDK" << "" << mVoiceAccountServerURI << "" @@ -552,7 +552,7 @@ void LLVivoxVoiceClient::connectorCreate() //<< "" //Name can cause problems per vivox. << "12" << "\n\n\n"; - + writeString(stream.str()); } @@ -566,10 +566,10 @@ void LLVivoxVoiceClient::connectorShutdown() << "" << LLVivoxSecurity::getInstance()->connectorHandle() << "" << "" << "\n\n\n"; - + mShutdownComplete = false; mConnectorEstablished = false; - + writeString(stream.str()); } else @@ -601,7 +601,7 @@ void LLVivoxVoiceClient::setLoginInfo( { // Already logged in. LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; - + // Don't process another login. return; } @@ -616,14 +616,14 @@ void LLVivoxVoiceClient::setLoginInfo( } std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); - + if( !debugSIPURIHostName.empty() ) { LL_INFOS("Voice") << "Overriding account server based on VivoxDebugSIPURIHostName: " << debugSIPURIHostName << LL_ENDL; mVoiceSIPURIHostName = debugSIPURIHostName; } - + if( mVoiceSIPURIHostName.empty() ) { // we have an empty account server name @@ -643,7 +643,7 @@ void LLVivoxVoiceClient::setLoginInfo( << mVoiceSIPURIHostName << LL_ENDL; } - + std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); if( !debugAccountServerURI.empty() ) @@ -652,11 +652,11 @@ void LLVivoxVoiceClient::setLoginInfo( << debugAccountServerURI << LL_ENDL; mVoiceAccountServerURI = debugAccountServerURI; } - + if( mVoiceAccountServerURI.empty() ) { // If the account server URI isn't specified, construct it from the SIP URI hostname - mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/"; + mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/"; LL_INFOS("Voice") << "Inferring account server based on SIP URI Host name: " << mVoiceAccountServerURI << LL_ENDL; } @@ -667,11 +667,11 @@ void LLVivoxVoiceClient::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 +// 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 { @@ -820,7 +820,7 @@ void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state) case VOICE_STATE_SESSION_ESTABLISHED: { - // enable/disable the automatic VAD and explicitly set the initial values of + // enable/disable the automatic VAD and explicitly set the initial values of // the VAD variables ourselves when it is off - see SL-15072 for more details // note: we set the other parameters too even if the auto VAD is on which is ok unsigned int vad_auto = gSavedSettings.getU32("VivoxVadAuto"); @@ -995,7 +995,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() { LLFile::rename(new_log, old_log); } - + std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout"); if (!shutdown_timeout.empty()) { @@ -1076,7 +1076,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() llcoro::suspendUntilTimeout(DAEMON_CONNECT_THROTTLE_SECONDS); } } - + //--------------------------------------------------------------------- if (sShuttingDown && !sConnected) { @@ -1094,7 +1094,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() { return false; } - + // MBW -- Note to self: pumps and pipes examples in // indra/test/io.cpp // indra/test/llpipeutil.{cpp|h} @@ -1132,7 +1132,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() // *TODO* Pump a message for wake up. llcoro::suspend(); } - + if (sShuttingDown) { return false; @@ -1184,7 +1184,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() else { provisioned = true; - } + } } while (!provisioned && ++retryCount <= PROVISION_RETRY_MAX && !sShuttingDown); if (sShuttingDown && !provisioned) @@ -1208,7 +1208,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() { voiceSipUriHostname = result["voice_sip_uri_hostname"].asString(); } - + // this key is actually misnamed -- it will be an entire URI, not just a hostname. if (result.has("voice_account_server_name")) { @@ -1239,7 +1239,7 @@ bool LLVivoxVoiceClient::establishVoiceConnection() { return false; } - + LLSD result; bool connected(false); bool giving_up(false); @@ -1358,7 +1358,7 @@ bool LLVivoxVoiceClient::loginToVivox() bool account_login(false); bool send_login(true); - do + do { mIsLoggingIn = true; if (send_login) @@ -1366,7 +1366,7 @@ bool LLVivoxVoiceClient::loginToVivox() loginSendMessage(); send_login = false; } - + LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult); if (sShuttingDown) @@ -1406,7 +1406,7 @@ bool LLVivoxVoiceClient::loginToVivox() // tell the user there is a problem LL_WARNS("Voice") << "login " << loginresp << " will retry login in " << timeout << " seconds." << LL_ENDL; - + if (!sShuttingDown) { // Todo: this is way to long, viewer can get stuck waiting during shutdown @@ -1511,7 +1511,7 @@ bool LLVivoxVoiceClient::retrieveVoiceFonts() mIsWaitingForFonts = true; LLSD result; - do + do { result = llcoro::suspendUntilEventOn(mVivoxPump); @@ -1546,7 +1546,7 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo() LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; return false; } - + // update the parcel checkParcelChanged(true); @@ -1606,7 +1606,7 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo() } } - // set the spatial channel. If no voice credentials or uri are + // set the spatial channel. If no voice credentials or uri are // available, then we simply drop out of voice spatially. return setSpatialChannel(result["voice_credentials"]); } @@ -1661,7 +1661,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) 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) @@ -1703,8 +1703,8 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) 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. + // 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. mVivoxPump.discard(); @@ -1747,25 +1747,25 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) } else if ((message == "failed") || (message == "removed") || (message == "timeout")) { // we will get a removed message if a voice call is declined. - - if (message == "failed") + + if (message == "failed") { int reason = result["reason"].asInteger(); LL_WARNS("Voice") << "Add and join failed for reason " << reason << LL_ENDL; - + if ( (reason == ERROR_VIVOX_NOT_LOGGED_IN) || (reason == ERROR_VIVOX_OBJECT_NOT_FOUND)) { LL_DEBUGS("Voice") << "Requesting reprovision and login." << LL_ENDL; requestRelog(); - } + } } else { LL_WARNS("Voice") << "session '" << message << "' " << LL_ENDL; } - + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); mIsJoiningSession = false; return false; @@ -1784,8 +1784,8 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) // Events that need to happen when a session is joined could go here. // send an initial positional information immediately upon joining. - // - // do an initial update for position and the camera position, then send a + // + // do an initial update for position and the camera position, then send a // positional update. updatePosition(); enforceTether(); @@ -1892,7 +1892,7 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait) 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 + // 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; @@ -1927,7 +1927,7 @@ bool LLVivoxVoiceClient::waitForChannel() EVoiceWaitForChannelState state = VOICE_CHANNEL_STATE_LOGIN; - do + do { if (sShuttingDown) { @@ -2117,10 +2117,10 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) LL_DEBUGS("Voice") << "runSession terminate requested " << LL_ENDL; terminateAudioSession(true); } - // if a relog has been requested then addAndJoineSession + // 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. + // make a call and the other party rejected it. return !mRelogRequested; } @@ -2156,7 +2156,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) mAudioSession->mParticipantsChanged = false; notifyParticipantObservers(); } - + if (!inSpatialChannel()) { // When in a non-spatial channel, never send positional updates. @@ -2168,9 +2168,9 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) if (checkParcelChanged()) { - // *RIDER: I think I can just return here if the parcel has changed + // *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 @@ -2213,7 +2213,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; } if (result.has("session")) - { + { if (result.has("handle")) { if (!mAudioSession) @@ -2318,7 +2318,7 @@ void LLVivoxVoiceClient::recordingAndPlaybackMode() int LLVivoxVoiceClient::voiceRecordBuffer() { - LLSD timeoutResult(LLSDMap("recplay", "stop")); + LLSD timeoutResult(LLSDMap("recplay", "stop")); LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; @@ -2441,7 +2441,7 @@ void LLVivoxVoiceClient::logout() // Ensure that we'll re-request provisioning before logging in again mAccountPassword.clear(); mVoiceAccountServerURI.clear(); - + logoutSendMessage(); } @@ -2466,7 +2466,7 @@ void LLVivoxVoiceClient::logoutSendMessage() void LLVivoxVoiceClient::sessionGroupCreateSendMessage() { if(mAccountLoggedIn) - { + { std::ostringstream stream; LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; @@ -2535,7 +2535,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr { session->mMediaConnectInProgress = true; } - + std::string password; if(!session->mHash.empty()) { @@ -2560,7 +2560,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr << "SHA1UserName" << "\n\n\n" ; - + writeString(stream.str()); } @@ -2572,7 +2572,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t << LL_ENDL; session->mMediaConnectInProgress = true; - + std::ostringstream stream; stream @@ -2589,7 +2589,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t void LLVivoxVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session) { LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; - + std::ostringstream stream; stream @@ -2630,7 +2630,7 @@ void LLVivoxVoiceClient::leaveAudioSession() 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; } @@ -2641,7 +2641,7 @@ void LLVivoxVoiceClient::leaveAudioSession() } else { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; } } else @@ -2658,12 +2658,12 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &se sessionGroupTerminateSendMessage(session); return; /* - LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; + LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; stream << "" << "" << session->mHandle << "" << "\n\n\n"; - + writeString(stream.str()); */ } @@ -2671,13 +2671,13 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &se void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session) { std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; + + LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; stream << "" << "" << session->mGroupHandle << "" << "\n\n\n"; - + writeString(stream.str()); } @@ -2687,17 +2687,17 @@ void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr sessionGroupTerminateSendMessage(session); return; /* - LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; + LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; stream << "" << "" << session->mGroupHandle << "" << "" << session->mHandle << "" << "Audio" << "\n\n\n"; - + writeString(stream.str()); */ - + } @@ -2707,7 +2707,7 @@ void LLVivoxVoiceClient::getCaptureDevicesSendMessage() stream << "" << "\n\n\n"; - + writeString(stream.str()); } @@ -2717,7 +2717,7 @@ void LLVivoxVoiceClient::getRenderDevicesSendMessage() stream << "" << "\n\n\n"; - + writeString(stream.str()); } @@ -2745,7 +2745,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) if(!mCaptureDevice.empty()) { mCaptureDevice.clear(); - mCaptureDeviceDirty = true; + mCaptureDeviceDirty = true; } } else @@ -2753,7 +2753,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) if(mCaptureDevice != name) { mCaptureDevice = name; - mCaptureDeviceDirty = true; + mCaptureDeviceDirty = true; } } } @@ -2763,7 +2763,7 @@ void LLVivoxVoiceClient::setDevicesListUpdated(bool state) } void LLVivoxVoiceClient::clearRenderDevices() -{ +{ LL_DEBUGS("Voice") << "called" << LL_ENDL; mRenderDevices.clear(); } @@ -2786,7 +2786,7 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name) if(!mRenderDevice.empty()) { mRenderDevice.clear(); - mRenderDeviceDirty = true; + mRenderDeviceDirty = true; } } else @@ -2794,10 +2794,10 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name) if(mRenderDevice != name) { mRenderDevice = name; - mRenderDeviceDirty = true; + mRenderDeviceDirty = true; } } - + } void LLVivoxVoiceClient::tuningStart() @@ -2830,7 +2830,7 @@ bool LLVivoxVoiceClient::inTuningMode() } void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{ +{ mTuningAudioFile = name; std::ostringstream stream; stream @@ -2838,7 +2838,7 @@ void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, b << "" << mTuningAudioFile << "" << "" << (loop?"1":"0") << "" << "\n\n\n"; - + writeString(stream.str()); } @@ -2849,33 +2849,33 @@ void LLVivoxVoiceClient::tuningRenderStopSendMessage() << "" << "" << mTuningAudioFile << "" << "\n\n\n"; - + writeString(stream.str()); } void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int loop) { LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; - + std::ostringstream stream; stream << "" << "-1" << "" << loop << "" << "\n\n\n"; - + writeString(stream.str()); } void LLVivoxVoiceClient::tuningCaptureStopSendMessage() { LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; - + std::ostringstream stream; stream << "" << "\n\n\n"; - + writeString(stream.str()); mTuningEnergy = 0.0f; @@ -2894,7 +2894,7 @@ void LLVivoxVoiceClient::tuningSetMicVolume(float volume) void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume) { - int scaled_volume = scale_speaker_volume(volume); + int scaled_volume = scale_speaker_volume(volume); if(scaled_volume != mTuningSpeakerVolume) { @@ -2902,7 +2902,7 @@ void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume) mTuningSpeakerVolumeDirty = true; } } - + float LLVivoxVoiceClient::tuningGetEnergy(void) { return mTuningEnergy; @@ -2911,13 +2911,13 @@ float LLVivoxVoiceClient::tuningGetEnergy(void) bool LLVivoxVoiceClient::deviceSettingsAvailable() { bool result = true; - + if(!sConnected) result = false; - + if(mRenderDevices.empty()) result = false; - + return result; } bool LLVivoxVoiceClient::deviceSettingsUpdated() @@ -2928,7 +2928,7 @@ bool LLVivoxVoiceClient::deviceSettingsUpdated() // 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; + return updated; } void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList) @@ -2961,9 +2961,9 @@ void LLVivoxVoiceClient::giveUp() 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]; +// F32 nvel[3]; F64 npos[3]; - + // The original XML command was sent like this: /* << "" @@ -3021,7 +3021,7 @@ static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVe 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] @@ -3075,7 +3075,7 @@ static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVe left.mV[i] = nl[i]; pos.mdV[i] = npos[i]; } - + #endif } @@ -3085,7 +3085,7 @@ void LLVivoxVoiceClient::setHidden(bool hidden) if (mHidden && inSpatialChannel()) { - // get out of the channel entirely + // get out of the channel entirely leaveAudioSession(); } else @@ -3095,20 +3095,20 @@ void LLVivoxVoiceClient::setHidden(bool hidden) } void LLVivoxVoiceClient::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 << "" + stream << "" << "" << getAudioSessionHandle() << ""; - + stream << ""; LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); @@ -3124,7 +3124,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) // 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) @@ -3132,7 +3132,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) pos.mdV[i] = VX_NULL_POSITION; } } - + stream << "" << "" << pos.mdV[VX] << "" @@ -3160,7 +3160,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) << "" << l.mV [VZ] << "" << "" ; - + stream << ""; stream << ""; @@ -3168,7 +3168,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) LLVector3d earPosition; LLVector3 earVelocity; LLMatrix3 earRot; - + switch(mEarLocation) { case earLocCamera: @@ -3177,13 +3177,13 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) earVelocity = mCameraVelocity; earRot = mCameraRot; break; - + case earLocAvatar: earPosition = mAvatarPosition; earVelocity = mAvatarVelocity; earRot = avatarRot; break; - + case earLocMixed: earPosition = mAvatarPosition; earVelocity = mAvatarVelocity; @@ -3198,9 +3198,9 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) pos = earPosition; vel = earVelocity; - + oldSDKTransform(l, u, a, pos, vel); - + if (mHidden) { for (int i=0;i<3;++i) @@ -3208,7 +3208,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) pos.mdV[i] = VX_NULL_POSITION; } } - + stream << "" << "" << pos.mdV[VX] << "" @@ -3241,19 +3241,19 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) 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 @@ -3262,7 +3262,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) // scale from the range 0.0-1.0 to vivox volume in the range 0-100 S32 volume = ll_round(p->mVolume / VOLUME_SCALE_VIVOX); bool mute = p->mOnMuteList; - + if(mute) { // SetParticipantMuteForMe doesn't work in p2p sessions. @@ -3273,16 +3273,16 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) // 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() << "" @@ -3302,7 +3302,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) << "\n\n\n"; } } - + p->mVolumeDirty = false; } } @@ -3322,13 +3322,13 @@ void LLVivoxVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) if(mCaptureDeviceDirty) { LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; - - stream + + stream << "" << "" << mCaptureDevice << "" << "" << "\n\n\n"; - + mCaptureDeviceDirty = false; } } @@ -3420,7 +3420,7 @@ void LLVivoxVoiceClient::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! + * let us iterate on a collection of values that work for us. Hopefully! * * From the VIVOX Docs: * @@ -3430,16 +3430,16 @@ void LLVivoxVoiceClient::sendLocalAudioUpdates() * 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 + * 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 + * 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 + * 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, + * to decreasing the sensitivity of the VAD (i.e. '0' is most sensitive, * while 100 is 'least sensitive') */ void LLVivoxVoiceClient::setupVADParams(unsigned int vad_auto, @@ -3487,7 +3487,7 @@ void LLVivoxVoiceClient::onVADSettingsChange() // Response/Event handlers void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) -{ +{ LLSD result = LLSD::emptyMap(); if(statusCode == 0) @@ -3521,7 +3521,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st // 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 @@ -3529,7 +3529,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st // 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 @@ -3543,13 +3543,13 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st } void LLVivoxVoiceClient::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. @@ -3574,20 +3574,20 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString } void LLVivoxVoiceClient::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->mErrorStatusCode = statusCode; session->mErrorStatusString = statusString; if(session == mAudioSession) { @@ -3618,20 +3618,20 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu } void LLVivoxVoiceClient::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->mErrorStatusCode = statusCode; session->mErrorStatusString = statusString; if(session == mAudioSession) { @@ -3692,7 +3692,7 @@ void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int stat } void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString) -{ +{ if(statusCode != 0) { LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; @@ -3710,21 +3710,21 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string & 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 vivoxevent(LLSDMap("connector", LLSD::Boolean(false))); mVivoxPump.post(vivoxevent); } void LLVivoxVoiceClient::sessionAddedEvent( - std::string &uriString, - std::string &alias, - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool isChannel, + std::string &uriString, + std::string &alias, + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool isChannel, bool incoming, std::string &nameString, std::string &applicationString) @@ -3732,7 +3732,7 @@ void LLVivoxVoiceClient::sessionAddedEvent( sessionStatePtr_t session; LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; - + session = addSession(uriString, sessionHandle); if(session) { @@ -3740,19 +3740,19 @@ void LLVivoxVoiceClient::sessionAddedEvent( 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) + // 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)); } @@ -3761,7 +3761,7 @@ void LLVivoxVoiceClient::sessionAddedEvent( 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 = nameFromsipURI(session->mSIPURI); if(namePortion.empty()) @@ -3769,14 +3769,14 @@ void LLVivoxVoiceClient::sessionAddedEvent( // Didn't seem to be a SIP URI, just use the whole provided name. 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) @@ -3791,7 +3791,7 @@ void LLVivoxVoiceClient::sessionAddedEvent( void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) { LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; - + #if USE_SESSION_GROUPS if(mMainSessionGroupHandle.empty()) { @@ -3818,7 +3818,7 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) // The old session may now need to be deleted. reapSession(oldSession); } - + // This is the session we're joining. if(mIsJoiningSession) { @@ -3834,10 +3834,10 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) participant->mIsSelf = true; lookupName(participant->mAvatarID); - LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName + LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; } - + if(!session->mIsChannel) { // this is a p2p session. Make sure the other end is added as a participant. @@ -3853,9 +3853,9 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) 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 + LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; } } @@ -3863,11 +3863,11 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) } void LLVivoxVoiceClient::sessionRemovedEvent( - std::string &sessionHandle, + std::string &sessionHandle, std::string &sessionGroupHandle) { LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; - + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { @@ -3875,15 +3875,15 @@ void LLVivoxVoiceClient::sessionRemovedEvent( // 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); } @@ -3899,7 +3899,7 @@ void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) { - + if(session->mCreateInProgress) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; @@ -3921,7 +3921,7 @@ void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session) // 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 { @@ -3933,32 +3933,32 @@ void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session) bool LLVivoxVoiceClient::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); if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) { // The hostname in this URI is different from what we expect. This probably means we need to relog. - + // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. - + result = true; } } } } - + return result; } @@ -3974,9 +3974,9 @@ void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session) } void LLVivoxVoiceClient::accountLoginStateChangeEvent( - std::string &accountHandle, - int statusCode, - std::string &statusString, + std::string &accountHandle, + int statusCode, + std::string &statusString, int state) { LLSD levent = LLSD::emptyMap(); @@ -3988,9 +3988,9 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( login_state_logging_in = 2, login_state_logging_out = 3, login_state_resetting = 4, - login_state_error=100 + login_state_error=100 */ - + LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; switch(state) { @@ -4020,7 +4020,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( mVivoxPump.post(levent); break; - + default: //Used to be a commented out warning LL_WARNS("Voice") << "unknown account state event: " << state << LL_ENDL; @@ -4057,24 +4057,24 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s } void LLVivoxVoiceClient::mediaStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - int statusCode, - std::string &statusString, - int state, + 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: @@ -4102,9 +4102,9 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( session->mVoiceActive = true; session->mMediaConnectInProgress = false; joinedAudioSession(session); - case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. + case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. break; - + case streamStateRinging: if(incoming) { @@ -4123,13 +4123,13 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( } } break; - + default: LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; break; - + } - + } else { @@ -4139,12 +4139,12 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( } void LLVivoxVoiceClient::participantAddedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString, - std::string &displayNameString, + 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)); @@ -4155,7 +4155,7 @@ void LLVivoxVoiceClient::participantAddedEvent( { participant->mAccountName = nameString; - LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName + LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; if(participant->mAvatarIDValid) @@ -4177,7 +4177,7 @@ void LLVivoxVoiceClient::participantAddedEvent( // 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); @@ -4187,10 +4187,10 @@ void LLVivoxVoiceClient::participantAddedEvent( } void LLVivoxVoiceClient::participantRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, std::string &nameString) { sessionStatePtr_t session(findSession(sessionHandle)); @@ -4215,20 +4215,20 @@ void LLVivoxVoiceClient::participantRemovedEvent( void LLVivoxVoiceClient::participantUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - bool isModeratorMuted, - bool isSpeaking, - int volume, + 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; @@ -4253,25 +4253,25 @@ void LLVivoxVoiceClient::participantUpdatedEvent( { participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX; } - - // *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. + + // *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 + + // ignore session ID of local chat if (voice_cnl && voice_cnl->getSessionID().notNull()) { LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); @@ -4300,10 +4300,10 @@ void LLVivoxVoiceClient::participantUpdatedEvent( } void LLVivoxVoiceClient::messageEvent( - std::string &sessionHandle, - std::string &uriString, - std::string &alias, - std::string &messageHeader, + std::string &sessionHandle, + std::string &uriString, + std::string &alias, + std::string &messageHeader, std::string &messageBody, std::string &applicationString) { @@ -4324,7 +4324,7 @@ void LLVivoxVoiceClient::messageEvent( const std::string endSpan = ""; std::string::size_type start; std::string::size_type end; - + // Default to displaying the raw string, so the message gets through. message = messageBody; @@ -4336,38 +4336,38 @@ void LLVivoxVoiceClient::messageEvent( if(start != std::string::npos) { start += startMarker2.size(); - + if(end != std::string::npos) end -= start; - + message.assign(messageBody, start, end); } - else + else { // Didn't find a , 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) @@ -4382,7 +4382,7 @@ void LLVivoxVoiceClient::messageEvent( } } } - + // Decode ampersand-escaped chars { std::string::size_type mark = 0; @@ -4394,14 +4394,14 @@ void LLVivoxVoiceClient::messageEvent( 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) { @@ -4409,12 +4409,12 @@ void LLVivoxVoiceClient::messageEvent( 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) { @@ -4424,7 +4424,7 @@ void LLVivoxVoiceClient::messageEvent( LLChat chat; chat.mMuted = is_muted && !is_linden; - + if(!chat.mMuted) { chat.mFromID = session->mCallerID; @@ -4435,7 +4435,7 @@ void LLVivoxVoiceClient::messageEvent( { // 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, @@ -4448,14 +4448,14 @@ void LLVivoxVoiceClient::messageEvent( LLUUID::null, // default arg LLVector3::zero); // default arg } - } + } } } void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) { sessionStatePtr_t session(findSession(sessionHandle)); - + if(session) { participantStatePtr_t participant(session->findParticipant(uriString)); @@ -4518,11 +4518,11 @@ void LLVivoxVoiceClient::muteListChanged() 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; @@ -4532,18 +4532,18 @@ void LLVivoxVoiceClient::muteListChanged() ///////////////////////////// // Managing list of participants -LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : - mURI(uri), - mPTT(false), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLastSpokeTimestamp(0.f), - mPower(0.f), - mVolume(LLVoiceClient::VOLUME_DEFAULT), +LLVivoxVoiceClient::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), + mOnMuteList(false), mVolumeSet(false), - mVolumeDirty(false), + mVolumeDirty(false), mAvatarIDValid(false), mIsSelf(false) { @@ -4553,7 +4553,7 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP { participantStatePtr_t result; bool useAlternateURI = false; - + // Note: this is mostly the body of LLVivoxVoiceClient::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. { @@ -4575,14 +4575,14 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP 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; @@ -4598,12 +4598,12 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP 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)) @@ -4611,10 +4611,10 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP result->mVolumeDirty = true; mVolumeDirty = true; } - + LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; } - + return result; } @@ -4643,9 +4643,9 @@ void LLVivoxVoiceClient::sessionState::removeParticipant(const LLVivoxVoiceClien { 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; @@ -4676,7 +4676,7 @@ void LLVivoxVoiceClient::sessionState::removeAllParticipants() { removeParticipant(mParticipantsByURI.begin()->second); } - + if(!mParticipantsByUUID.empty()) { LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; @@ -4702,10 +4702,10 @@ void LLVivoxVoiceClient::sessionState::VerifySessions() void LLVivoxVoiceClient::getParticipantList(std::set &participants) { - if(mAudioSession) + if(mProcessChannels && mAudioSession) { for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); - iter != mAudioSession->mParticipantsByUUID.end(); + iter != mAudioSession->mParticipantsByUUID.end(); iter++) { participants.insert(iter->first); @@ -4715,18 +4715,18 @@ void LLVivoxVoiceClient::getParticipantList(std::set &participants) bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id) { - if(mAudioSession) + if(mProcessChannels && mAudioSession) { - return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); } - return false; + return false; } LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) { participantStatePtr_t result; - + participantMap::iterator iter = mParticipantsByURI.find(uri); if(iter == mParticipantsByURI.end()) @@ -4743,7 +4743,7 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::find { result = iter->second; } - + return result; } @@ -4763,12 +4763,12 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::find LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) { participantStatePtr_t result; - + if(mAudioSession) { result = mAudioSession->findParticipantByID(id); } - + return result; } @@ -4779,15 +4779,15 @@ bool LLVivoxVoiceClient::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. + + // 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()) @@ -4815,7 +4815,7 @@ bool LLVivoxVoiceClient::switchChannel( std::string hash) { bool needsSwitch = !mIsInChannel; - + if (mIsInChannel) { if (mSessionTerminateRequested) @@ -4892,7 +4892,7 @@ bool LLVivoxVoiceClient::switchChannel( mNextAudioSession->mReconnect = !no_reconnect; mNextAudioSession->mIsP2P = is_p2p; } - + if (mIsInChannel) { // If we're already in a channel, or if we're joining one, terminate @@ -4928,7 +4928,7 @@ bool LLVivoxVoiceClient::setSpatialChannel(const LLSD& channelInfo) mSpatialSessionCredentials = channelInfo["channel_credentials"].asString(); LL_DEBUGS("Voice") << "got spatial channel uri: \"" << mSpatialSessionURI << "\"" << 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. @@ -4997,9 +4997,9 @@ bool LLVivoxVoiceClient::isVoiceWorking() const // Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) { - BOOL result = TRUE; + 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. @@ -5018,22 +5018,22 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) } } } - + 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. +// Currently this will be false only for PSTN P2P calls. BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) { - BOOL result = TRUE; + BOOL result = TRUE; sessionStatePtr_t session(findSession(session_id)); - + if(session != NULL) { result = session->isCallBackPossible(); } - + return result; } @@ -5043,12 +5043,12 @@ BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) { bool result = TRUE; sessionStatePtr_t session(findSession(session_id)); - + if(session != NULL) { result = session->isTextIMPossible(); } - + return result; } @@ -5056,15 +5056,15 @@ void LLVivoxVoiceClient::leaveNonSpatialChannel() { LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; - // Make sure we don't rejoin the current session. + // 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(); } @@ -5075,8 +5075,10 @@ void LLVivoxVoiceClient::processChannels(bool process) bool LLVivoxVoiceClient::isCurrentChannel(const LLSD &channelInfo) { - if (channelInfo["voice_server_type"].asString() != VIVOX_VOICE_SERVER_TYPE) + if (!mProcessChannels || (channelInfo["voice_server_type"].asString() != VIVOX_VOICE_SERVER_TYPE)) + { return false; + } if (mAudioSession) { if (!channelInfo["sessionHandle"].asString().empty()) @@ -5098,12 +5100,12 @@ bool LLVivoxVoiceClient::compareChannels(const LLSD& channelInfo1, const LLSD& c bool LLVivoxVoiceClient::inProximalChannel() { bool result = false; - + if (mIsInChannel && !mSessionTerminateRequested) { result = inSpatialChannel(); } - + return result; } @@ -5114,7 +5116,7 @@ std::string LLVivoxVoiceClient::sipURIFromID(const LLUUID &id) result += nameFromID(id); result += "@"; result += mVoiceSIPURIHostName; - + return result; } @@ -5128,14 +5130,14 @@ std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) result += "@"; result += mVoiceSIPURIHostName; } - + return result; } std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid) { std::string result; - + if (uuid.isNull()) { //VIVOX, the uuid emtpy look for the mURIString and return that instead. //result.assign(uuid.mURIStringName); @@ -5144,31 +5146,31 @@ std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid) } // Prepending this apparently prevents conflicts with reserved names inside the vivox code. result = "x"; - - // Base64 encode and replace the pieces of base64 that are less compatible + + // 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 LLVivoxVoiceClient::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.vivox.com" // If it is, convert to a bare name before doing the transform. std::string name = nameFromsipURI(inName); - + // Doesn't look like a SIP URI, assume it's an actual name. if(name.empty()) name = inName; @@ -5176,7 +5178,7 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) // 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. @@ -5186,7 +5188,7 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) LLStringUtil::replaceChar(temp, '-', '+'); LLStringUtil::replaceChar(temp, '_', '/'); - U8 rawuuid[UUID_BYTES + 1]; + U8 rawuuid[UUID_BYTES + 1]; int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); if(len == UUID_BYTES) { @@ -5194,15 +5196,15 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) memcpy(uuid.mData, rawuuid, UUID_BYTES); result = true; } - } - + } + if(!result) { // VIVOX: not a standard account name, just copy the URI name mURIString field // and hope for the best. bpj uuid.setNull(); // VIVOX, set the uuid field to nulls } - + return result; } @@ -5230,19 +5232,19 @@ std::string LLVivoxVoiceClient::nameFromsipURI(const std::string &uri) { result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); } - + return result; } bool LLVivoxVoiceClient::inSpatialChannel(void) { bool result = false; - + if(mAudioSession) { result = mAudioSession->mIsSpatial; } - + return result; } @@ -5250,22 +5252,22 @@ bool LLVivoxVoiceClient::inSpatialChannel(void) LLSD LLVivoxVoiceClient::getAudioSessionChannelInfo() { LLSD result; - + if (mAudioSession) { result = mAudioSession->getVoiceChannelInfo(); } - + return result; } std::string LLVivoxVoiceClient::getAudioSessionHandle() { std::string result; - + if(mAudioSession) result = mAudioSession->mHandle; - + return result; } @@ -5284,11 +5286,11 @@ void LLVivoxVoiceClient::enforceTether(void) F32 camera_distance = (F32)camera_offset.magVec(); if(camera_distance > max_dist) { - tethered = mAvatarPosition + + tethered = mAvatarPosition + (max_dist / camera_distance) * camera_offset; } } - + if(dist_vec_squared(mCameraPosition, tethered) > 0.01) { mCameraPosition = tethered; @@ -5305,19 +5307,19 @@ void LLVivoxVoiceClient::updatePosition(void) 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()); + rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); - + LLVivoxVoiceClient::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(); @@ -5325,7 +5327,7 @@ void LLVivoxVoiceClient::updatePosition(void) // TODO: Can we get the head offset from outside the LLVOAvatar? // pos += LLVector3d(mHeadOffset); pos += LLVector3d(0.f, 0.f, 1.f); - + LLVivoxVoiceClient::getInstance()->setAvatarPosition( pos, // position LLVector3::zero, // velocity @@ -5336,13 +5338,13 @@ void LLVivoxVoiceClient::updatePosition(void) void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) { mCameraRequestedPosition = position; - + if(mCameraVelocity != velocity) { mCameraVelocity = velocity; mSpatialCoordsDirty = true; } - + if(mCameraRot != rot) { mCameraRot = rot; @@ -5357,19 +5359,19 @@ void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLV 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; } @@ -5378,15 +5380,15 @@ void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLV bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) { bool result = false; - + if(region) { name = region->getName(); } - + if(!name.empty()) result = true; - + return result; } @@ -5416,14 +5418,14 @@ void LLVivoxVoiceClient::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; @@ -5462,7 +5464,7 @@ void LLVivoxVoiceClient::setEarLocation(S32 loc) if(mEarLocation != loc) { LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; - + mEarLocation = loc; mSpatialCoordsDirty = true; } @@ -5470,7 +5472,7 @@ void LLVivoxVoiceClient::setEarLocation(S32 loc) void LLVivoxVoiceClient::setVoiceVolume(F32 volume) { - int scaled_volume = scale_speaker_volume(volume); + int scaled_volume = scale_speaker_volume(volume); if(scaled_volume != mSpeakerVolume) { @@ -5488,7 +5490,7 @@ void LLVivoxVoiceClient::setVoiceVolume(F32 volume) void LLVivoxVoiceClient::setMicGain(F32 volume) { int scaled_volume = scale_mic_volume(volume); - + if(scaled_volume != mMicVolume) { mMicVolume = scaled_volume; @@ -5498,29 +5500,19 @@ void LLVivoxVoiceClient::setMicGain(F32 volume) ///////////////////////////// // Accessors for data related to nearby speakers -BOOL LLVivoxVoiceClient::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 LLVivoxVoiceClient::getDisplayName(const LLUUID& id) { std::string result; - participantStatePtr_t participant(findParticipantByID(id)); - if(participant) - { - result = participant->mDisplayName; - } - + if (mProcessChannels) + { + participantStatePtr_t participant(findParticipantByID(id)); + if (participant) + { + result = participant->mDisplayName; + } + } + return result; } @@ -5529,42 +5521,47 @@ std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id) BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; + if (mProcessChannels) + { + participantStatePtr_t participant(findParticipantByID(id)); + if (participant) + { + if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) + { + participant->mIsSpeaking = FALSE; + } + result = participant->mIsSpeaking; + } + } - participantStatePtr_t participant(findParticipantByID(id)); - if(participant) - { - if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) - { - participant->mIsSpeaking = FALSE; - } - result = participant->mIsSpeaking; - } - return result; } BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id) { BOOL result = FALSE; - + if (!mProcessChannels) + { + return FALSE; + } participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mIsModeratorMuted; } - + return result; } F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id) -{ +{ F32 result = 0; participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mPower; } - + return result; } @@ -5581,19 +5578,6 @@ BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id) // 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 LLVivoxVoiceClient::getOnMuteList(const LLUUID& id) -{ - BOOL result = FALSE; - - participantStatePtr_t participant(findParticipantByID(id)); - if(participant) - { - result = participant->mOnMuteList; - } return result; } @@ -5603,7 +5587,7 @@ F32 LLVivoxVoiceClient::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) { @@ -5651,21 +5635,21 @@ std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id) { result = participant->mGroupID; } - + return result; } void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) { // LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; - + if(!mMainSessionGroupHandle.empty()) { std::ostringstream stream; stream << "" << "" << mMainSessionGroupHandle << "" - << "Start" + << "Start" << "" << deltaFramesPerControlFrame << "" << "" << "" << "" << "false" @@ -5687,7 +5671,7 @@ void LLVivoxVoiceClient::recordingLoopSave(const std::string& filename) stream << "" << "" << mMainSessionGroupHandle << "" - << "Flush" + << "Flush" << "" << filename << "" << "\n\n\n"; @@ -5705,7 +5689,7 @@ void LLVivoxVoiceClient::recordingStop() stream << "" << "" << mMainSessionGroupHandle << "" - << "Stop" + << "Stop" << "\n\n\n"; writeString(stream.str()); @@ -5722,7 +5706,7 @@ void LLVivoxVoiceClient::filePlaybackStart(const std::string& filename) stream << "" << "" << mMainSessionGroupHandle << "" - << "Start" + << "Start" << "" << filename << "" << "\n\n\n"; @@ -5740,7 +5724,7 @@ void LLVivoxVoiceClient::filePlaybackStop() stream << "" << "" << mMainSessionGroupHandle << "" - << "Stop" + << "Stop" << "\n\n\n"; writeString(stream.str()); @@ -5830,7 +5814,7 @@ bool LLVivoxVoiceClient::sessionState::isTextIMPossible() } -/*static*/ +/*static*/ LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) { sessionStatePtr_t result; @@ -5844,7 +5828,7 @@ LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchS return result; } -/*static*/ +/*static*/ LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri) { sessionStatePtr_t result; @@ -5891,7 +5875,7 @@ void LLVivoxVoiceClient::sessionState::for_each(sessionFunc_t func) std::for_each(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func)); } -// simple test predicates. +// simple test predicates. // *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features. bool LLVivoxVoiceClient::sessionState::testByHandle(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string handle) { @@ -5945,28 +5929,28 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const std: { result = iter->second; } - + return result; } LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) -{ +{ sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); - + return result; } LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const LLUUID &participant_id) { sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); - + return result; } LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle) { sessionStatePtr_t result; - + if(handle.empty()) { // No handle supplied. @@ -5977,7 +5961,7 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: { // Check for an existing session with this handle sessionMap::iterator iter = mSessionsByHandle.find(handle); - + if(iter != mSessionsByHandle.end()) { result = iter->second; @@ -5987,7 +5971,7 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: if(!result) { // No existing session found. - + LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL; result = sessionState::createSession(); result->mSIPURI = uri; @@ -6000,8 +5984,8 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: 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 + // *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)); } @@ -6009,7 +5993,7 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: else { // Found an existing session - + if(uri != result->mSIPURI) { // TODO: Should this be an internal error? @@ -6031,12 +6015,12 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: setSessionHandle(result, handle); } } - + LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; } verifySessionState(); - + return result; } @@ -6067,7 +6051,7 @@ void LLVivoxVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) void LLVivoxVoiceClient::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. @@ -6086,7 +6070,7 @@ void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, cons LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL; } } - + session->mHandle = handle; if(!handle.empty()) @@ -6148,7 +6132,7 @@ void LLVivoxVoiceClient::deleteAllSessions() const sessionStatePtr_t session = mSessionsByHandle.begin()->second; deleteSession(session); } - + } void LLVivoxVoiceClient::verifySessionState(void) @@ -6236,10 +6220,10 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta } } } - - LL_DEBUGS("Voice") - << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session channelInfo " << getAudioSessionChannelInfo() + + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session channelInfo " << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL; @@ -6330,7 +6314,7 @@ void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sess { session->mTextInvitePending = false; - // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. + // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. } if (session->mVoiceInvitePending) { @@ -6884,7 +6868,7 @@ void LLVivoxVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name } } -// it updates VoiceMorphing menu items in accordance with purchased properties +// it updates VoiceMorphing menu items in accordance with purchased properties void LLVivoxVoiceClient::updateVoiceMorphingMenu() { if (mVoiceFontListDirty) @@ -7125,7 +7109,7 @@ void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() LLVivoxProtocolParser::LLVivoxProtocolParser() { parser = XML_ParserCreate(NULL); - + reset(); } @@ -7158,7 +7142,7 @@ void LLVivoxProtocolParser::reset() applicationString.clear(); } -//virtual +//virtual LLVivoxProtocolParser::~LLVivoxProtocolParser() { if (parser) @@ -7184,38 +7168,38 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( 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 LLVivoxProtocolParser (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_SetUserData(parser, this); XML_Parse(parser, mInput.data() + start, delim - start, false); - + LL_DEBUGS("VivoxProtocolParser") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; start = delim + 3; } - + if(start != 0) mInput = mInput.substr(start); - + LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; - + if(!LLVivoxVoiceClient::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; } @@ -7259,11 +7243,11 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) 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 @@ -7271,7 +7255,7 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) { const char *key = *attr++; const char *value = *attr++; - + if (!stricmp("requestId", key)) { requestId = value; @@ -7297,20 +7281,20 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) else { LL_DEBUGS("VivoxProtocolParser") << 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("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; } else if (!stricmp("CaptureDevices", tag)) { LLVivoxVoiceClient::getInstance()->clearCaptureDevices(); - } + } else if (!stricmp("RenderDevices", tag)) { LLVivoxVoiceClient::getInstance()->clearRenderDevices(); @@ -7322,7 +7306,7 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) else if (!stricmp("RenderDevice", tag)) { deviceString.clear(); - } + } else if (!stricmp("SessionFont", tag)) { id = 0; @@ -7357,9 +7341,9 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) void LLVivoxProtocolParser::EndTag(const char *tag) { const std::string& string = textBuffer; - + responseDepth--; - + if (ignoringTags) { if (ignoreDepth == responseDepth) @@ -7372,11 +7356,11 @@ void LLVivoxProtocolParser::EndTag(const char *tag) LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; } } - + if (!ignoringTags) { LL_DEBUGS("VivoxProtocolParser") << "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); @@ -7431,7 +7415,7 @@ void LLVivoxProtocolParser::EndTag(const char *tag) else if (!stricmp("DisplayName", tag)) displayNameString = string; else if (!stricmp("Device", tag)) - deviceString = string; + deviceString = string; else if (!stricmp("AccountName", tag)) nameString = string; else if (!stricmp("ParticipantType", tag)) @@ -7529,7 +7513,7 @@ void LLVivoxProtocolParser::EndTag(const char *tag) textBuffer.clear(); accumulateText= false; - + if (responseDepth == 0) { // We finished all of the XML, process the data @@ -7546,7 +7530,7 @@ void LLVivoxProtocolParser::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) @@ -7573,14 +7557,14 @@ LLDate LLVivoxProtocolParser::expiryTimeStampToLLDate(const std::string& vivox_t void LLVivoxProtocolParser::processResponse(std::string tag) { LL_DEBUGS("VivoxProtocolParser") << 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(); @@ -7648,7 +7632,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) { - /* + /* c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 @@ -7680,12 +7664,12 @@ void LLVivoxProtocolParser::processResponse(std::string tag) // These are really spammy in tuning mode LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); } - else if (!stricmp(eventTypeCstr, "MessageEvent")) + else if (!stricmp(eventTypeCstr, "MessageEvent")) { //TODO: This probably is not received any more, it was used to support SLim clients LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); } - else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) + else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) { //TODO: This probably is not received any more, it was used to support SLim clients LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); @@ -7755,23 +7739,23 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(actionCstr, "Session.Create.1")) { - LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); + LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); } else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) { - LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); + LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); } else if (!stricmp(actionCstr, "Session.Connect.1")) { - LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); + LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); } else if (!stricmp(actionCstr, "Account.Logout.1")) { - LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString); + LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString); } else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) { - LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); + LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); } else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) { @@ -7800,75 +7784,75 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } 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")) { - + } */ } @@ -7886,7 +7870,7 @@ LLVivoxSecurity::LLVivoxSecurity() random_value[b] = ll_rand() & 0xff; } mConnectorHandle = LLBase64::encode(random_value, VIVOX_TOKEN_BYTES); - + for (int b = 0; b < VIVOX_TOKEN_BYTES; b++) { random_value[b] = ll_rand() & 0xff; diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 14f6422ebb..5cd9795f9e 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -1,25 +1,25 @@ -/** +/** * @file llvoicevivox.h * @brief Declaration of LLDiamondwareVoiceClient class which is the interface to the voice client process. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, 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$ */ @@ -81,12 +81,12 @@ public: //@{ void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector) void terminate() override; // Call this to clean up during shutdown - + const LLVoiceVersionInfo& getVersion() override; - + void updateSettings() override; // call after loading settings and whenever they change - // Returns true if vivox has successfully logged in and is not in error state + // Returns true if vivox has successfully logged in and is not in error state bool isVoiceWorking() const override; ///////////////////// @@ -95,12 +95,12 @@ public: void tuningStart() override; void tuningStop() override; bool inTuningMode() override; - + void tuningSetMicVolume(float volume) override; void tuningSetSpeakerVolume(float volume) override; float tuningGetEnergy(void) override; //@} - + ///////////////////// /// @name Devices //@{ @@ -108,20 +108,20 @@ public: // i.e. when the daemon is running and connected, and the device lists are populated. bool deviceSettingsAvailable() override; bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. - + // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. void refreshDeviceLists(bool clearCurrentList = true) override; - + void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; - + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} - + void getParticipantList(std::set &participants) override; bool isParticipant(const LLUUID& speaker_id) override; @@ -129,41 +129,41 @@ public: // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; // Returns true if calling back the session URI after the session has closed is possible. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. BOOL isSessionCallBackPossible(const LLUUID &session_id) override; - + // Returns true if the session can accepte text IM's. // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. + // NOTE: this will return true if the session can't be found. BOOL isSessionTextIMPossible(const LLUUID &session_id) override; - - + + //////////////////////////// /// @name Channel stuff //@{ // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - + void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override; - + bool setSpatialChannel(const LLSD& channelInfo) override; - + void leaveNonSpatialChannel() override; void processChannels(bool process) override; - + void leaveChannel(void); bool isCurrentChannel(const LLSD &channelInfo) override; bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override; //@} - - + + ////////////////////////// /// @name LLVoiceP2POutgoingCallInterface //@{ @@ -178,39 +178,37 @@ public: bool answerInvite(const std::string &sessionHandle); void declineInvite(const std::string &sessionHandle); - + ///////////////////////// /// @name Volume/gain //@{ void setVoiceVolume(F32 volume) override; void setMicGain(F32 volume) override; //@} - + ///////////////////////// /// @name enable disable voice and features //@{ void setVoiceEnabled(bool enabled) override; void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} - + ////////////////////////// /// @name nearby speaker accessors //@{ - BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar std::string getDisplayName(const LLUUID& id) override; BOOL isParticipantAvatar(const LLUUID &id) override; BOOL getIsSpeaking(const LLUUID& id) override; BOOL getIsModeratorMuted(const LLUUID& id) override; F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - BOOL getOnMuteList(const LLUUID& id) override; F32 getUserVolume(const LLUUID& id) override; void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} - + // authorize the user virtual void userAuthorized(const std::string& user_id, const LLUUID &agentID) override; - + ////////////////////////////// /// @name Status notification //@{ @@ -221,7 +219,7 @@ public: void addObserver(LLVoiceClientParticipantObserver* observer) override; void removeObserver(LLVoiceClientParticipantObserver* observer) override; //@} - + std::string sipURIFromID(const LLUUID &id) override; //@} @@ -267,12 +265,12 @@ public: protected: ////////////////////// - // Vivox Specific definitions - + // Vivox Specific definitions + friend class LLVivoxVoiceClientMuteListObserver; - friend class LLVivoxVoiceClientFriendsObserver; + friend class LLVivoxVoiceClientFriendsObserver; + - enum streamState { streamStateUnknown = 0, @@ -281,16 +279,16 @@ protected: streamStateRinging = 3, streamStateConnecting = 6, // same as Vivox session_media_connecting enum streamStateDisconnecting = 7, //Same as Vivox session_media_disconnecting enum - }; + }; struct participantState { public: participantState(const std::string &uri); - + bool updateMuteState(); // true if mute state has changed bool isAvatar(); - + std::string mURI; LLUUID mAvatarID; std::string mAccountName; @@ -315,7 +313,7 @@ protected: typedef std::map participantMap; typedef std::map participantUUIDMap; - + struct sessionState { public: @@ -328,7 +326,7 @@ protected: ~sessionState(); LLSD getVoiceChannelInfo(); - + participantStatePtr_t addParticipant(const std::string &uri); void removeParticipant(const participantStatePtr_t &participant); void removeAllParticipants(); @@ -344,7 +342,7 @@ protected: bool isCallBackPossible(); bool isTextIMPossible(); bool isSpatial() { return mIsSpatial; } - + static void for_each(sessionFunc_t func); std::string mHandle; @@ -356,7 +354,7 @@ protected: std::string mHash; // Channel password std::string mErrorStatusString; std::queue mTextMsgQueue; - + LLUUID mIMSessionID; LLUUID mCallerID; int mErrorStatusCode; @@ -403,7 +401,7 @@ protected: typedef boost::shared_ptr sessionStatePtr_t; typedef std::map sessionMap; - + /////////////////////////////////////////////////////// // Private Member Functions ////////////////////////////////////////////////////// @@ -415,17 +413,17 @@ protected: //@{ // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. void daemonDied(); - + // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. - void giveUp(); - + void giveUp(); + // write to the tvc bool writeString(const std::string &str); - + void connectorCreate(); - void connectorShutdown(); - void closeSocket(void); - + void connectorShutdown(); + void closeSocket(void); + // void requestVoiceAccountProvision(S32 retries = 3); void setLoginInfo( const std::string& account_name, @@ -434,14 +432,14 @@ protected: const std::string& voice_account_server_uri); void loginSendMessage(); void logout(); - void logoutSendMessage(); - - + void logoutSendMessage(); + + //@} - + //------------------------------------ // tuning - + void tuningRenderStartSendMessage(const std::string& name, bool loop); void tuningRenderStopSendMessage(); @@ -454,12 +452,12 @@ protected: void addCaptureDevice(const LLVoiceDevice& device); void clearRenderDevices(); void setDevicesListUpdated(bool state); - void addRenderDevice(const LLVoiceDevice& device); + void addRenderDevice(const LLVoiceDevice& device); void buildSetAudioDevices(std::ostringstream &stream); - + void getCaptureDevicesSendMessage(); void getRenderDevicesSendMessage(); - + // local audio updates, mic mute, speaker mute, mic volume and speaker volumes void sendLocalAudioUpdates(); @@ -486,9 +484,9 @@ protected: void auxAudioPropertiesEvent(F32 energy); void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); - + void muteListChanged(); - + ///////////////////////////// // VAD changes // disable auto-VAD and configure VAD parameters explicitly @@ -504,34 +502,34 @@ protected: void setEarLocation(S32 loc); - + ///////////////////////////// // Accessors for data related to nearby speakers // MBW -- XXX -- Not sure how to get this data out of the TVC BOOL getUsingPTT(const LLUUID& id); std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) - + ///////////////////////////// // Recording controls void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); void recordingLoopSave(const std::string& filename); void recordingStop(); - + // Playback controls void filePlaybackStart(const std::string& filename); void filePlaybackStop(); void filePlaybackSetPaused(bool paused); void filePlaybackSetMode(bool vox = false, float speed = 1.0f); - + participantStatePtr_t findParticipantByID(const LLUUID& id); - + #if 0 //////////////////////////////////////// // voice sessions. typedef std::set sessionSet; - + typedef sessionSet::iterator sessionIterator; sessionIterator sessionsBegin(void); sessionIterator sessionsEnd(void); @@ -540,7 +538,7 @@ protected: sessionStatePtr_t findSession(const std::string &handle); sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); sessionStatePtr_t findSession(const LLUUID &participant_id); - + sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); void clearSessionHandle(const sessionStatePtr_t &session); void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle); @@ -556,11 +554,11 @@ protected: // This is called in several places where the session _may_ need to be deleted. // It contains logic for whether to delete the session or keep it around. void reapSession(const sessionStatePtr_t &session); - + // Returns true if the session seems to indicate we've moved to a region on a different voice server bool sessionNeedsRelog(const sessionStatePtr_t &session); - - + + ////////////////////////////////////// // buddy list stuff, needed for SLIM later struct buddyListEntry @@ -580,13 +578,13 @@ protected: }; typedef std::map buddyListMap; - + ///////////////////////////// // session control messages void accountListBlockRulesSendMessage(); void accountListAutoAcceptRulesSendMessage(); - + void sessionGroupCreateSendMessage(); void sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); void sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); @@ -597,20 +595,20 @@ protected: void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session); // void sessionTextDisconnectSendMessage(sessionState *session); - - + + // Pokes the state machine to leave the audio session next time around. - void sessionTerminate(); - + void sessionTerminate(); + // Pokes the state machine to shut down the connector and restart it. void requestRelog(); - + // Does the actual work to get out of the audio session void leaveAudioSession(); - + friend class LLVivoxVoiceClientCapResponder; - - + + void lookupName(const LLUUID &id); void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); void avatarNameResolved(const LLUUID &id, const std::string &name); @@ -630,10 +628,10 @@ protected: const S32 font_status, const bool template_font = false); void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); - void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); + void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); private: - + LLVoiceVersionInfo mVoiceVersion; // Coroutine support methods @@ -675,21 +673,21 @@ private: // The larger it is the greater is possibility there is a problem with connection to voice server. // Introduced while fixing EXT-4313. int mSpatialJoiningNum; - + static void idle(void *user_data); - + LLHost mDaemonHost; LLSocket::ptr_t mSocket; - - // We should kill the voice daemon in case of connection alert + + // We should kill the voice daemon in case of connection alert bool mTerminateDaemon; - + friend class LLVivoxProtocolParser; - + std::string mAccountName; std::string mAccountPassword; std::string mAccountDisplayName; - + bool mTuningMode; float mTuningEnergy; std::string mTuningAudioFile; @@ -699,13 +697,13 @@ private: bool mTuningSpeakerVolumeDirty; bool mDevicesListUpdated; // set to true when the device list has been updated // and false when the panelvoicedevicesettings has queried for an update status. - + std::string mSpatialSessionURI; std::string mSpatialSessionCredentials; std::string mMainSessionGroupHandle; // handle of the "main" session group. - - std::string mChannelName; // Name of the channel to be looked up + + std::string mChannelName; // Name of the channel to be looked up sessionStatePtr_t mAudioSession; // Session state for the current audio session bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. @@ -713,27 +711,27 @@ private: S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings std::string mCurrentRegionName; // Used to detect parcel boundary crossings - + bool mConnectorEstablished; // set by "Create Connector" response - bool mAccountLoggedIn; // set by login message + bool mAccountLoggedIn; // set by login message int mNumberOfAliases; U32 mCommandCookie; std::string mVoiceAccountServerURI; std::string mVoiceSIPURIHostName; - + int mLoginRetryCount; - + sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. #if 0 sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. #endif - + bool mBuddyListMapPopulated; bool mBlockRulesListReceived; bool mAutoAcceptRulesListReceived; buddyListMap mBuddyListMap; - + LLVoiceDeviceList mCaptureDevices; LLVoiceDeviceList mRenderDevices; @@ -744,30 +742,30 @@ private: bool mIsInitialized; bool mShutdownComplete; - + bool checkParcelChanged(bool update = false); bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); void joinSession(const sessionStatePtr_t &session); - + std::string nameFromID(const LLUUID &id); bool IDFromName(const std::string name, LLUUID &uuid); std::string sipURIFromAvatar(LLVOAvatar *avatar); std::string sipURIFromName(std::string &name); - + // Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. - std::string nameFromsipURI(const std::string &uri); + std::string nameFromsipURI(const std::string &uri); bool inSpatialChannel(void); LLSD getAudioSessionChannelInfo(); std::string getAudioSessionHandle(); - + void setHidden(bool hidden) override; //virtual void sendPositionAndVolumeUpdate(void); - + void sendCaptureAndRenderDevices(); void buildSetCaptureDevice(std::ostringstream &stream); void buildSetRenderDevice(std::ostringstream &stream); - + void sendFriendsListUpdates(); @@ -778,9 +776,9 @@ private: #endif void enforceTether(void); - + bool mSpatialCoordsDirty; - + LLVector3d mCameraPosition; LLVector3d mCameraRequestedPosition; LLVector3 mCameraVelocity; @@ -789,30 +787,30 @@ private: LLVector3d mAvatarPosition; LLVector3 mAvatarVelocity; LLQuaternion mAvatarRot; - + bool mMuteMic; bool mMuteMicDirty; bool mHidden; //Set to true during teleport to hide the agent's position. - + // Set to true when the friends list is known to have changed. bool mFriendsListDirty; - + enum { earLocCamera = 0, // ear at camera earLocAvatar, // ear at avatar earLocMixed // ear at avatar location/camera direction }; - - S32 mEarLocation; - + + S32 mEarLocation; + bool mSpeakerVolumeDirty; bool mSpeakerMuteDirty; int mSpeakerVolume; int mMicVolume; bool mMicVolumeDirty; - + bool mVoiceEnabled; bool mProcessChannels; bool mWriteInProgress; @@ -826,7 +824,7 @@ private: typedef std::set status_observer_set_t; status_observer_set_t mStatusObservers; - + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); typedef std::set friend_observer_set_t; @@ -933,7 +931,7 @@ private: }; -/** +/** * @class LLVivoxProtocolParser * @brief This class helps construct new LLIOPipe specializations * @see LLIOPipe @@ -946,12 +944,12 @@ class LLVivoxProtocolParser : public LLIOPipe public: LLVivoxProtocolParser(); virtual ~LLVivoxProtocolParser(); - + protected: /* @name LLIOPipe virtual implementations */ //@{ - /** + /** * @brief Process the data in buffer */ virtual EStatus process_impl( @@ -961,16 +959,16 @@ protected: LLSD& context, LLPumpIO* pump); //@} - + std::string mInput; - + // Expat control members XML_Parser parser; int responseDepth; bool ignoringTags; bool isEvent; int ignoreDepth; - + // Members for processing responses. The values are transient and only valid within a call to processResponse(). int returnCode; int statusCode; @@ -985,7 +983,7 @@ protected: std::string sessionGroupHandle; std::string alias; std::string applicationString; - + // Members for processing events. The values are transient and only valid within a call to processResponse(). std::string eventTypeString; int state; @@ -1024,19 +1022,19 @@ protected: S32 fontType; S32 fontStatus; std::string mediaCompletionType; - + // Members for processing text between tags std::string textBuffer; bool accumulateText; - + void reset(); - + void processResponse(std::string tag); - + static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); static void XMLCALL ExpatEndTag(void *data, const char *el); static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); - + void StartTag(const char *tag, const char **attr); void EndTag(const char *tag); void CharData(const char *buffer, int length); @@ -1063,7 +1061,7 @@ class LLVoiceVivoxStats : public LLSingleton LLSINGLETON(LLVoiceVivoxStats); LOG_CLASS(LLVoiceVivoxStats); virtual ~LLVoiceVivoxStats(); - + private: F64SecondsImplicit mStartTime; @@ -1071,7 +1069,7 @@ class LLVoiceVivoxStats : public LLSingleton F64 mConnectTime; U32 mConnectAttempts; - + F64 mProvisionTime; U32 mProvisionAttempts; 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()) { diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 3ff801ed56..0e8ac3a183 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -1,25 +1,25 @@ -/** +/** * @file llvoicewebrtc.h * @brief Declaration 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$ */ @@ -76,12 +76,12 @@ public: void terminate() override; // Call this to clean up during shutdown static bool isShuttingDown() { return sShuttingDown; } - + const LLVoiceVersionInfo& getVersion() override; - + void updateSettings() override; // call after loading settings and whenever they change - // Returns true if WebRTC has successfully logged in and is not in error state + // Returns true if WebRTC has successfully logged in and is not in error state bool isVoiceWorking() const override; std::string sipURIFromID(const LLUUID &id) override; @@ -92,32 +92,32 @@ public: void tuningStart() override; void tuningStop() override; bool inTuningMode() override; - + void tuningSetMicVolume(float volume) override; void tuningSetSpeakerVolume(float volume) override; float tuningGetEnergy(void) override; //@} - + ///////////////////// /// @name Devices //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. bool deviceSettingsAvailable() override; bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. - + // Requery the WebRTC daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed // (use this if you want to know when it's done). // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. void refreshDeviceLists(bool clearCurrentList = true) override; - + void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; - + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; - //@} - + //@} + void getParticipantList(std::set &participants) override; bool isParticipant(const LLUUID& speaker_id) override; @@ -125,31 +125,31 @@ public: // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; // Returns true if calling back the session URI after the session has closed is possible. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. BOOL isSessionCallBackPossible(const LLUUID &session_id) override; - + // WebRTC doesn't preclude text im BOOL isSessionTextIMPossible(const LLUUID &session_id) override { return TRUE; } - + //////////////////////////// /// @name Channel stuff //@{ // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. bool inProximalChannel() override; - + void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override { startAdHocSession(channelInfo, notify_on_first_join, hangup_on_last_leave); } - - bool setSpatialChannel(const LLSD &channelInfo) override + + bool setSpatialChannel(const LLSD &channelInfo) override { processChannels(true); return true; } - + void leaveNonSpatialChannel() override; void processChannels(bool process) override; @@ -163,35 +163,32 @@ public: LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; } LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override { return nullptr; } - + ///////////////////////// /// @name Volume/gain //@{ void setVoiceVolume(F32 volume) override; void setMicGain(F32 volume) override; //@} - + ///////////////////////// /// @name enable disable voice and features //@{ void setVoiceEnabled(bool enabled) override; void setMuteMic(bool muted) override; // Set the mute state of the local mic. //@} - + ////////////////////////// /// @name nearby speaker accessors - //@{ - BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar std::string getDisplayName(const LLUUID& id) override; BOOL isParticipantAvatar(const LLUUID &id) override; BOOL getIsSpeaking(const LLUUID& id) override; BOOL getIsModeratorMuted(const LLUUID& id) override; F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - BOOL getOnMuteList(const LLUUID& id) override; F32 getUserVolume(const LLUUID& id) override; void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal) //@} - + ////////////////// /// @name LLMuteListObserver //@{ @@ -208,7 +205,7 @@ public: void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); void sendPositionUpdate(bool force); void updateOwnVolume(); - + ////////////////////////////// /// @name Status notification //@{ @@ -233,10 +230,9 @@ public: { public: participantState(const LLUUID& agent_id); - - bool updateMuteState(); // true if mute state has changed + bool isAvatar(); - + std::string mURI; LLUUID mAvatarID; std::string mDisplayName; @@ -245,7 +241,6 @@ public: F32 mVolume; // the gain applied to the participant bool mIsSpeaking; bool mIsModeratorMuted; - bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) }; typedef boost::shared_ptr participantStatePtr_t; @@ -256,7 +251,7 @@ public: protected: typedef std::map participantUUIDMap; - + class sessionState { public: @@ -267,7 +262,7 @@ public: static void addSession(const std::string &channelID, ptr_t& session); virtual ~sessionState(); - + participantStatePtr_t addParticipant(const LLUUID& agent_id); void removeParticipant(const participantStatePtr_t &participant); void removeAllParticipants(); @@ -284,14 +279,14 @@ public: virtual bool processConnectionStates(); virtual void sendData(const std::string &data); - + void setMuteMic(bool muted); void setMicGain(F32 volume); void setSpeakerVolume(F32 volume); void setUserVolume(const LLUUID& id, F32 volume); - + void setUserMute(const LLUUID& id, bool mute); - + static void for_each(sessionFunc_t func); static void reapEmptySessions(); @@ -305,7 +300,7 @@ public: std::string mHandle; std::string mChannelID; std::string mName; - + bool mMuted; // this session is muted. F32 mMicGain; // gain for this session. F32 mSpeakerVolume; // volume for this session. @@ -314,7 +309,7 @@ public: participantUUIDMap mParticipantsByUUID; - static bool hasSession(const std::string &sessionID) + static bool hasSession(const std::string &sessionID) { return mSessions.find(sessionID) != mSessions.end(); } bool mHangupOnLastLeave; // notify observers after the session becomes empty. @@ -398,7 +393,7 @@ public: void addCaptureDevice(const LLVoiceDevice& device); void clearRenderDevices(); - void addRenderDevice(const LLVoiceDevice& device); + void addRenderDevice(const LLVoiceDevice& device); void setDevicesListUpdated(bool state); ///////////////////////////// @@ -412,22 +407,22 @@ public: void setEarLocation(S32 loc); - + ///////////////////////////// // Accessors for data related to nearby speakers ///////////////////////////// sessionStatePtr_t findP2PSession(const LLUUID &agent_id); - + sessionStatePtr_t addSession(const std::string &channel_id, sessionState::ptr_t session); void deleteSession(const sessionStatePtr_t &session); - + // Does the actual work to get out of the audio session void leaveAudioSession(); - + friend class LLWebRTCVoiceClientCapResponder; - - + + void lookupName(const LLUUID &id); void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); void avatarNameResolved(const LLUUID &id, const std::string &name); @@ -448,7 +443,7 @@ private: //--- /// Clean up objects created during a voice session. void cleanUp(); - + bool mTuningMode; F32 mTuningMicGain; int mTuningSpeakerVolume; @@ -457,7 +452,7 @@ private: std::string mSpatialSessionCredentials; std::string mMainSessionGroupHandle; // handle of the "main" session group. - + sessionStatePtr_t mSession; // Session state for the current session sessionStatePtr_t mNextSession; // Session state for the session we're trying to join @@ -476,7 +471,7 @@ private: bool inEstateChannel(); LLSD getAudioSessionChannelInfo(); - + void setHidden(bool hidden) override; //virtual void enforceTether(); @@ -485,9 +480,9 @@ private: std::set getNeighboringRegions() { return mNeighboringRegions; } LLVoiceVersionInfo mVoiceVersion; - + bool mSpatialCoordsDirty; - + LLVector3d mListenerPosition; LLVector3d mListenerRequestedPosition; LLVector3 mListenerVelocity; @@ -498,23 +493,23 @@ private: LLQuaternion mAvatarRot; std::set mNeighboringRegions; // includes current region - + bool mMuteMic; bool mHidden; //Set to true during teleport to hide the agent's position. - + enum { earLocCamera = 0, // ear at camera earLocAvatar, // ear at avatar earLocMixed // ear at avatar location/camera direction }; - - S32 mEarLocation; - + + S32 mEarLocation; + float mSpeakerVolume; F32 mMicGain; - + bool mVoiceEnabled; bool mProcessChannels; @@ -525,7 +520,7 @@ private: typedef std::set status_observer_set_t; status_observer_set_t mStatusObservers; - + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); bool mIsInTuningMode; @@ -544,7 +539,7 @@ class LLVoiceWebRTCStats : public LLSingleton LLSINGLETON(LLVoiceWebRTCStats); LOG_CLASS(LLVoiceWebRTCStats); virtual ~LLVoiceWebRTCStats(); - + private: F64SecondsImplicit mStartTime; @@ -552,7 +547,7 @@ class LLVoiceWebRTCStats : public LLSingleton F64 mConnectTime; U32 mConnectAttempts; - + F64 mProvisionTime; U32 mProvisionAttempts; @@ -571,7 +566,7 @@ class LLVoiceWebRTCStats : public LLSingleton LLSD read(); }; -class LLVoiceWebRTCConnection : +class LLVoiceWebRTCConnection : public llwebrtc::LLWebRTCSignalingObserver, public llwebrtc::LLWebRTCDataObserver { @@ -610,7 +605,7 @@ class LLVoiceWebRTCConnection : virtual void setMuteMic(bool muted); virtual void setMicGain(F32 volume); virtual void setSpeakerVolume(F32 volume); - + void setUserVolume(const LLUUID& id, F32 volume); void setUserMute(const LLUUID& id, bool mute); -- cgit v1.2.3 From 1f177d837c51c0b5751204ffbd39c5e1e7a51212 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 11 Mar 2024 14:41:12 -0700 Subject: fixup tabs --- indra/newview/llimview.cpp | 113 ++++++------ indra/newview/llimview.h | 8 +- indra/newview/llpanelvoicedevicesettings.cpp | 76 ++++---- indra/newview/llvieweraudio.cpp | 4 +- indra/newview/llviewerregion.cpp | 2 +- indra/newview/llvoavatar.cpp | 2 +- indra/newview/llvoavatar.h | 6 +- indra/newview/llvoicechannel.cpp | 262 +++++++++++++-------------- indra/newview/llvoicechannel.h | 50 ++--- indra/newview/llvoiceclient.cpp | 169 +++++++++-------- indra/newview/llvoiceclient.h | 16 +- 11 files changed, 353 insertions(+), 355 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index b2769e9bab..921e757b58 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -722,20 +722,20 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) { - LLVoiceP2POutgoingCallInterface *outgoingInterface = - LLVoiceClient::getInstance()->getOutgoingCallInterface(voiceChannelInfo); + LLVoiceP2POutgoingCallInterface *outgoingInterface = + LLVoiceClient::getInstance()->getOutgoingCallInterface(voiceChannelInfo); - if (outgoingInterface) - { - // only use LLVoiceChannelP2P if the provider can handle the special P2P interface, - // which uses the voice server to relay calls and invites. Otherwise, - // we use the group voice provider. - mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id, outgoingInterface); - } + if (outgoingInterface) + { + // only use LLVoiceChannelP2P if the provider can handle the special P2P interface, + // which uses the voice server to relay calls and invites. Otherwise, + // we use the group voice provider. + mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id, outgoingInterface); + } else { - p2pAsAdhocCall = true; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); + p2pAsAdhocCall = true; + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true); } } else @@ -744,17 +744,17 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, if (gAgent.isInGroup(mSessionID)) { mSessionType = GROUP_SESSION; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); } else { mSessionType = ADHOC_SESSION; - mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); + mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false); } } mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3)); - mVoiceChannel->setChannelInfo(voiceChannelInfo); + mVoiceChannel->setChannelInfo(voiceChannelInfo); mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); @@ -2069,7 +2069,7 @@ bool LLIMModel::sendStartSession( const LLUUID& other_participant_id, const uuid_vec_t& ids, EInstantMessage dialog, - bool p2p_as_adhoc_call) + bool p2p_as_adhoc_call) { if ( dialog == IM_SESSION_GROUP_START ) { @@ -2118,13 +2118,13 @@ bool LLIMModel::sendStartSession( } else if ((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL)) { - LLViewerRegion *region = gAgent.getRegion(); - if (region) - { - std::string url = region->getCapability("ChatSessionRequest"); - LLCoros::instance().launch("startP2P", boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); - } - return true; + LLViewerRegion *region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); + LLCoros::instance().launch("startP2P", boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); + } + return true; } return false; } @@ -2368,11 +2368,12 @@ void LLCallDialogManager::onVoiceChannelStateChangedInt(const LLVoiceChannel::ES return; } break; - case LLVoiceChannel::STATE_NO_CHANNEL_INFO : + + case LLVoiceChannel::STATE_NO_CHANNEL_INFO : // This will happen in p2p calls using the adhoc // infrastructure, which marks the channel as no channel info // after the call is closed, which forces a dialogue. - return; + return; case LLVoiceChannel::STATE_HUNG_UP: // this state is coming before session is changed @@ -2716,28 +2717,28 @@ bool is_voice_call_type(const std::string &value) } LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : -LLCallDialog(payload), -mAvatarNameCacheConnection() + LLCallDialog(payload), + mAvatarNameCacheConnection() { } void LLIncomingCallDialog::onLifetimeExpired() { - LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(mPayload["voice_session_info"]); - if (call) - { - // restart notification's timer if call is still valid - mLifetimeTimer.start(); - } - else - { - // close invitation if call is already not valid - mLifetimeTimer.stop(); - LLUUID session_id = mPayload["session_id"].asUUID(); - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - closeFloater(); - } + LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(mPayload["voice_session_info"]); + if (call) + { + // restart notification's timer if call is still valid + mLifetimeTimer.start(); + } + else + { + // close invitation if call is already not valid + mLifetimeTimer.stop(); + LLUUID session_id = mPayload["session_id"].asUUID(); + gIMMgr->clearPendingAgentListUpdates(session_id); + gIMMgr->clearPendingInvitation(session_id); + closeFloater(); + } } BOOL LLIncomingCallDialog::postBuild() @@ -2752,7 +2753,7 @@ BOOL LLIncomingCallDialog::postBuild() LLUUID session_id = mPayload["session_id"].asUUID(); LLSD caller_id = mPayload["caller_id"]; - std::string caller_name = mPayload["caller_name"].asString(); + std::string caller_name = mPayload["caller_name"].asString(); if (session_id.isNull() && caller_id.asUUID().isNull()) { @@ -2903,7 +2904,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload std::string session_name = payload["session_name"].asString(); if (session_name.empty()) { - session_name = payload["caller_name"].asString(); + session_name = payload["caller_name"].asString(); } EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); @@ -3001,11 +3002,11 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload { if (type == IM_SESSION_P2P_INVITE) { - LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(payload["voice_session_info"]); - if (call) - { - call->declineInvite(); - } + LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(payload["voice_session_info"]); + if (call) + { + call->declineInvite(); + } } else { @@ -3098,7 +3099,7 @@ void LLIMMgr::addMessage( { fixed_session_name = av_name.getDisplayName(); } - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, LLSD(), is_offline_msg); + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, LLSD(), is_offline_msg); LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); if (session) @@ -3261,7 +3262,7 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name, const LLUUID& other_participant_id, const LLSD& voice_channel_info) { - LL_WARNS("Voice") << "ADD P2P VOICE CHANNEL INFO: " << voice_channel_info << LL_ENDL; + LL_DEBUGS("Voice") << "Add p2p voice channel info: " << voice_channel_info << LL_ENDL; return addSession(name, IM_NOTHING_SPECIAL, other_participant_id, voice_channel_info); } @@ -3273,7 +3274,7 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLSD& voiceChannelInfo) + const LLSD& voiceChannelInfo) { std::vector ids; ids.push_back(other_participant_id); @@ -3288,7 +3289,7 @@ LLUUID LLIMMgr::addSession( EInstantMessage dialog, const LLUUID& other_participant_id, const std::vector& ids, - const LLSD& voiceChannelInfo, + const LLSD& voiceChannelInfo, const LLUUID& floater_id) { if (ids.empty()) @@ -3397,7 +3398,7 @@ void LLIMMgr::inviteToSession( const std::string& caller_name, EInstantMessage type, EInvitationType inv_type, - const LLSD& voice_channel_info) + const LLSD& voice_channel_info) { if (caller_id == gAgentID) @@ -3446,7 +3447,7 @@ void LLIMMgr::inviteToSession( payload["caller_name"] = caller_name; payload["type"] = type; payload["inv_type"] = inv_type; - payload["voice_channel_info"] = voice_channel_info; + payload["voice_channel_info"] = voice_channel_info; payload["notify_box_type"] = notify_box_type; payload["question_type"] = question_type; @@ -3500,7 +3501,7 @@ void LLIMMgr::inviteToSession( fixed_session_name = av_name.getDisplayName(); } } - LLIMModel::getInstance()->newSession(session_id, fixed_session_name, IM_NOTHING_SPECIAL, caller_id, LLSD(), false); + LLIMModel::getInstance()->newSession(session_id, fixed_session_name, IM_NOTHING_SPECIAL, caller_id, LLSD(), false); } LLSD args; @@ -4126,7 +4127,7 @@ public: } BOOL session_type_p2p = input["body"]["voice"].get("invitation_type").asInteger() == EMultiAgentChatSessionType::P2P_CHAT_SESSION; - LL_WARNS("Voice") << "VOICE DATA: " << input["body"]<< LL_ENDL; + LL_DEBUGS("Voice") << "Received P2P voice information from the server: " << input["body"]<< LL_ENDL; gIMMgr->inviteToSession( input["body"]["session_id"].asUUID(), input["body"]["session_name"].asString(), diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 2f084d1392..93a1a95b23 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -380,7 +380,7 @@ public: LLUUID addSession(const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLSD& voiceChannelInfo = LLSD()); + const LLSD& voiceChannelInfo = LLSD()); // Adds a session using a specific group of starting agents // the dialog type is assumed correct. Returns the uuid of the session. @@ -388,8 +388,8 @@ public: LLUUID addSession(const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const std::vector &ids, - const LLSD& voiceChannelInfo = LLSD(), + const std::vector &ids, + const LLSD& voiceChannelInfo = LLSD(), const LLUUID& floater_id = LLUUID::null); /** @@ -415,7 +415,7 @@ public: const std::string& caller_name, EInstantMessage type, EInvitationType inv_type, - const LLSD &voice_channel_info = LLSD() + const LLSD &voice_channel_info = LLSD() ); void processIMTypingStart(const LLUUID& from_id, const EInstantMessage im_type); diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 48c90d6856..1ecf670179 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -236,47 +236,47 @@ void LLPanelVoiceDeviceSettings::refresh() if(mCtrlInputDevices) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet - { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - for (auto& device : devices) - { - mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); - } - - // Fix invalid input audio device preference. - if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE)) - { - mCtrlInputDevices->setValue(DEFAULT_DEVICE); - gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE); - mInputDevice = DEFAULT_DEVICE; - } - } + LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); + if (devices.size() > 0) // if zero, we've not received our devices yet + { + mCtrlInputDevices->removeall(); + mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + for (auto& device : devices) + { + mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); + } + + // Fix invalid input audio device preference. + if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE)) + { + mCtrlInputDevices->setValue(DEFAULT_DEVICE); + gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE); + mInputDevice = DEFAULT_DEVICE; + } + } } if(mCtrlOutputDevices) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet - { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - - for (auto& device : devices) - { - mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); - } - - // Fix invalid output audio device preference. - if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE)) - { - mCtrlOutputDevices->setValue(DEFAULT_DEVICE); - gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE); - mOutputDevice = DEFAULT_DEVICE; - } - } + LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); + if (devices.size() > 0) // if zero, we've not received our devices yet + { + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + + for (auto& device : devices) + { + mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); + } + + // Fix invalid output audio device preference. + if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE)) + { + mCtrlOutputDevices->setValue(DEFAULT_DEVICE); + gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE); + mOutputDevice = DEFAULT_DEVICE; + } + } } } } @@ -323,7 +323,7 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice() } // the preferences floater stuff is a mess, hence apply will never // be called when 'ok' is pressed, so just force it for now. - apply(); + apply(); } void LLPanelVoiceDeviceSettings::onCommitOutputDevice() diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 184b6d8e93..6a0edbecb1 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -480,11 +480,11 @@ void audio_update_volume(bool force_update) if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized"))) { - //voice_inst->setMuteMic(true); + voice_inst->setMuteMic(true); } else { - //voice_inst->setMuteMic(false); + voice_inst->setMuteMic(false); } } } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 84b007eaa4..39aaac57c1 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3136,7 +3136,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ParcelVoiceInfoRequest"); capabilityNames.append("ProductInfoRequest"); capabilityNames.append("ProvisionVoiceAccountRequest"); - capabilityNames.append("VoiceSignalingRequest"); + capabilityNames.append("VoiceSignalingRequest"); capabilityNames.append("ReadOfflineMsgs"); // Requires to respond reliably: AcceptFriendship, AcceptGroupInvite, DeclineFriendship, DeclineGroupInvite capabilityNames.append("RegionObjects"); capabilityNames.append("RemoteParcelRequest"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 5d7adb9613..a600482d5b 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1166,7 +1166,7 @@ void LLVOAvatar::cleanupClass() bool LLVOAvatar::handleVOAvatarPrefsChanged(const LLSD &newvalue) { - sLipSyncEnabled = gSavedSettings.getBOOL("LipSyncEnabled"); + sLipSyncEnabled = gSavedSettings.getBOOL("LipSyncEnabled"); return true; } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index a19476153c..47526707dd 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -106,10 +106,10 @@ public: virtual void markDead(); static void initClass(); // Initialize data that's only init'd once per class. static void cleanupClass(); // Cleanup data that's only init'd once per class. - virtual void initInstance(); // Called after construction to initialize the class. + virtual void initInstance(); // Called after construction to initialize the class. protected: virtual ~LLVOAvatar(); - static bool handleVOAvatarPrefsChanged(const LLSD &newvalue); + static bool handleVOAvatarPrefsChanged(const LLSD &newvalue); /** Initialization ** ** @@ -366,7 +366,7 @@ public: static F32 sLODFactor; // user-settable LOD factor static F32 sPhysicsLODFactor; // user-settable physics LOD factor static BOOL sJointDebug; // output total number of joints being touched for each avatar - static BOOL sLipSyncEnabled; + static BOOL sLipSyncEnabled; static LLPointer sCloudTexture; diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 4d85f160c2..28e895584b 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -92,7 +92,7 @@ LLVoiceChannel::~LLVoiceChannel() void LLVoiceChannel::setChannelInfo(const LLSD &channelInfo) { - mChannelInfo = channelInfo; + mChannelInfo = channelInfo; if (mState == STATE_NO_CHANNEL_INFO) { @@ -120,15 +120,15 @@ void LLVoiceChannel::setChannelInfo(const LLSD &channelInfo) void LLVoiceChannel::onChange(EStatusType type, const LLSD& channelInfo, bool proximal) { - LL_WARNS("Voice") << channelInfo << LL_ENDL; - LL_WARNS("Voice") << mChannelInfo << LL_ENDL; + LL_DEBUGS("Voice") << "Incoming channel info: " << channelInfo << LL_ENDL; + LL_DEBUGS("Voice") << "Current channel info: " << mChannelInfo << LL_ENDL; if (mChannelInfo.isUndefined()) { - mChannelInfo = channelInfo; + mChannelInfo = channelInfo; } - if (!LLVoiceClient::getInstance()->compareChannels(mChannelInfo, channelInfo)) + if (!LLVoiceClient::getInstance()->compareChannels(mChannelInfo, channelInfo)) { - return; + return; } if (type < BEGIN_ERROR_STATUS) @@ -147,7 +147,7 @@ void LLVoiceChannel::handleStatusChange(EStatusType type) switch(type) { case STATUS_LOGIN_RETRY: - // no user notice + // no user notice break; case STATUS_LOGGED_IN: break; @@ -382,10 +382,10 @@ boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallbac // LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID &session_id, - const std::string &session_name, - bool is_p2p) : - LLVoiceChannel(session_id, session_name), - mIsP2P(is_p2p) + const std::string &session_name, + bool is_p2p) : + LLVoiceChannel(session_id, session_name), + mIsP2P(is_p2p) { mRetries = DEFAULT_RETRIES_COUNT; mIsRetrying = FALSE; @@ -399,13 +399,13 @@ void LLVoiceChannelGroup::deactivate() } LLVoiceChannel::deactivate(); - if (mIsP2P) - { + if (mIsP2P) + { // void the channel info for p2p adhoc channels // so we request it again, hence throwing up the // connect dialogue on the other side. - setState(STATE_NO_CHANNEL_INFO); - } + setState(STATE_NO_CHANNEL_INFO); + } } void LLVoiceChannelGroup::activate() @@ -417,38 +417,38 @@ void LLVoiceChannelGroup::activate() if (callStarted()) { // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo, - mCallDirection == OUTGOING_CALL, - mIsP2P); + LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo, + mCallDirection == OUTGOING_CALL, + mIsP2P); if (mIsP2P) - { - LLIMModel::addSpeakersToRecent(mSessionID); - } - else - { - if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel - { - LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(mSessionID); - // Adding ad-hoc call participants to Recent People List. - // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we - // called(both online and offline) as source to get people for recent (STORM-210). - if (session->isOutgoingAdHoc()) - { - for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); it != session->mInitialTargetIDs.end(); ++it) - { - const LLUUID id = *it; - LLRecentPeople::instance().add(id); - } - } - // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs - // would lead to EXT-8246. So in this case we get them from speakers list. - else - { - LLIMModel::addSpeakersToRecent(mSessionID); - } - } - } + { + LLIMModel::addSpeakersToRecent(mSessionID); + } + else + { + if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel + { + LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(mSessionID); + // Adding ad-hoc call participants to Recent People List. + // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we + // called(both online and offline) as source to get people for recent (STORM-210). + if (session->isOutgoingAdHoc()) + { + for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); it != session->mInitialTargetIDs.end(); ++it) + { + const LLUUID id = *it; + LLRecentPeople::instance().add(id); + } + } + // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs + // would lead to EXT-8246. So in this case we get them from speakers list. + else + { + LLIMModel::addSpeakersToRecent(mSessionID); + } + } + } // Mic default state is OFF on initiating/joining Ad-Hoc/Group calls. It's on for P2P using the AdHoc infra. @@ -463,14 +463,14 @@ void LLVoiceChannelGroup::requestChannelInfo() { std::string url = region->getCapability("ChatSessionRequest"); - LLCoros::instance().launch("LLVoiceChannelGroup::voiceCallCapCoro", - boost::bind(&LLVoiceChannelGroup::voiceCallCapCoro, this, url)); + LLCoros::instance().launch("LLVoiceChannelGroup::voiceCallCapCoro", + boost::bind(&LLVoiceChannelGroup::voiceCallCapCoro, this, url)); } } void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo) { - mChannelInfo = channelInfo; + mChannelInfo = channelInfo; if (mState == STATE_NO_CHANNEL_INFO) { @@ -498,7 +498,7 @@ void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo) { // we have the channel info, just need to use it now LLVoiceClient::getInstance()->setNonSpatialChannel(channelInfo, - mCallDirection == OUTGOING_CALL, + mCallDirection == OUTGOING_CALL, mIsP2P); } } @@ -585,62 +585,62 @@ void LLVoiceChannelGroup::setState(EState state) void LLVoiceChannelGroup::voiceCallCapCoro(std::string url) { - LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); - LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t - httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceCallCapCoro", httpPolicy)); - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - - LLSD postData; - postData["method"] = "call"; - postData["session-id"] = mSessionID; - LLSD altParams; - altParams["preferred_voice_server_type"] = gSavedSettings.getString("VoiceServerType"); - postData["alt_params"] = altParams; - - LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL; - - LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); - - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if (!channelp) - { - LL_WARNS("Voice") << "Unable to retrieve channel with Id = " << mSessionID << LL_ENDL; - return; - } - - if (!status) - { - if (status == LLCore::HttpStatus(HTTP_FORBIDDEN)) - { - //403 == no ability - LLNotificationsUtil::add( - "VoiceNotAllowed", - channelp->getNotifyArgs()); - } - else - { - LLNotificationsUtil::add( - "VoiceCallGenericError", - channelp->getNotifyArgs()); - } - channelp->deactivate(); - return; - } - - result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - - LLSD::map_const_iterator iter; - for (iter = result.beginMap(); iter != result.endMap(); ++iter) - { - LL_DEBUGS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " - << iter->first << LL_ENDL; - } - LL_INFOS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " << result << LL_ENDL; - - channelp->setChannelInfo(result["voice_credentials"]); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceCallCapCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData; + postData["method"] = "call"; + postData["session-id"] = mSessionID; + LLSD altParams; + altParams["preferred_voice_server_type"] = gSavedSettings.getString("VoiceServerType"); + postData["alt_params"] = altParams; + + LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if (!channelp) + { + LL_WARNS("Voice") << "Unable to retrieve channel with Id = " << mSessionID << LL_ENDL; + return; + } + + if (!status) + { + if (status == LLCore::HttpStatus(HTTP_FORBIDDEN)) + { + //403 == no ability + LLNotificationsUtil::add( + "VoiceNotAllowed", + channelp->getNotifyArgs()); + } + else + { + LLNotificationsUtil::add( + "VoiceCallGenericError", + channelp->getNotifyArgs()); + } + channelp->deactivate(); + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + + LLSD::map_const_iterator iter; + for (iter = result.beginMap(); iter != result.endMap(); ++iter) + { + LL_DEBUGS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " + << iter->first << LL_ENDL; + } + LL_INFOS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " << result << LL_ENDL; + + channelp->setChannelInfo(result["voice_credentials"]); } @@ -666,7 +666,7 @@ void LLVoiceChannelProximal::activate() // we're connected to a non-spatial channel, so disconnect. LLVoiceClient::getInstance()->leaveNonSpatialChannel(); } - LLVoiceClient::getInstance()->activateSpatialChannel(true); + LLVoiceClient::getInstance()->activateSpatialChannel(true); LLVoiceChannel::activate(); } @@ -741,7 +741,7 @@ void LLVoiceChannelProximal::deactivate() { setState(STATE_HUNG_UP); } - LLVoiceClient::getInstance()->activateSpatialChannel(false); + LLVoiceClient::getInstance()->activateSpatialChannel(false); } @@ -749,13 +749,13 @@ void LLVoiceChannelProximal::deactivate() // LLVoiceChannelP2P // LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID &session_id, - const std::string &session_name, - const LLUUID &other_user_id, - LLVoiceP2POutgoingCallInterface* outgoing_call_interface) : - LLVoiceChannelGroup(session_id, session_name, true), - mOtherUserID(other_user_id), - mReceivedCall(FALSE), - mOutgoingCallInterface(outgoing_call_interface) + const std::string &session_name, + const LLUUID &other_user_id, + LLVoiceP2POutgoingCallInterface* outgoing_call_interface) : + LLVoiceChannelGroup(session_id, session_name, true), + mOtherUserID(other_user_id), + mReceivedCall(FALSE), + mOutgoingCallInterface(outgoing_call_interface) { } @@ -823,7 +823,7 @@ void LLVoiceChannelP2P::activate() if (callStarted()) { // no session handle yet, we're starting the call - if (mIncomingCallInterface == nullptr) + if (mIncomingCallInterface == nullptr) { mReceivedCall = FALSE; mOutgoingCallInterface->callUser(mOtherUserID); @@ -831,15 +831,15 @@ void LLVoiceChannelP2P::activate() // otherwise answering the call else { - if (!mIncomingCallInterface->answerInvite()) + if (!mIncomingCallInterface->answerInvite()) { mCallEndedByAgent = false; - mIncomingCallInterface.reset(); + mIncomingCallInterface.reset(); handleError(ERROR_UNKNOWN); return; } // using the incoming call interface invalidates it. Clear it out here so we can't reuse it by accident. - mIncomingCallInterface.reset(); + mIncomingCallInterface.reset(); } // Add the party to the list of people with which we've recently interacted. @@ -855,11 +855,11 @@ void LLVoiceChannelP2P::activate() void LLVoiceChannelP2P::deactivate() { - if (callStarted()) - { - mOutgoingCallInterface->hangup(); - } - LLVoiceChannel::deactivate(); + if (callStarted()) + { + mOutgoingCallInterface->hangup(); + } + LLVoiceChannel::deactivate(); } @@ -875,7 +875,7 @@ void LLVoiceChannelP2P::requestChannelInfo() // receiving session from other user who initiated call void LLVoiceChannelP2P::setChannelInfo(const LLSD& channel_info) { - mChannelInfo = channel_info; + mChannelInfo = channel_info; BOOL needs_activate = FALSE; if (callStarted()) { @@ -890,16 +890,16 @@ void LLVoiceChannelP2P::setChannelInfo(const LLSD& channel_info) { // we are active and have priority, invite the other user again // under the assumption they will join this new session - mOutgoingCallInterface->callUser(mOtherUserID); + mOutgoingCallInterface->callUser(mOtherUserID); return; } } - + mReceivedCall = TRUE; - if (!channel_info.isUndefined()) - { - mIncomingCallInterface = LLVoiceClient::getInstance()->getIncomingCallInterface(channel_info); - } + if (!channel_info.isUndefined()) + { + mIncomingCallInterface = LLVoiceClient::getInstance()->getIncomingCallInterface(channel_info); + } if (needs_activate) { activate(); @@ -917,7 +917,7 @@ void LLVoiceChannelP2P::setState(EState state) if (mReceivedCall && state == STATE_RINGING) { //TODO: remove or redirect this call status notification -// LLCallInfoDialog::show("answering", mNotifyArgs); + // LLCallInfoDialog::show("answering", mNotifyArgs); doSetState(state); return; } diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 5725e7ec5c..bc22bf0df6 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -72,7 +72,7 @@ public: virtual void handleError(EStatusType status); virtual void deactivate(); virtual void activate(); - virtual void setChannelInfo(const LLSD &channelInfo); + virtual void setChannelInfo(const LLSD &channelInfo); virtual void requestChannelInfo(); virtual BOOL isActive(); virtual BOOL callStarted(); @@ -95,13 +95,13 @@ public: static LLVoiceChannel* getChannelByID(const LLUUID& session_id); static LLVoiceChannel* getCurrentVoiceChannel(); - + static void initClass(); - + static void suspend(); static void resume(); -protected: + protected: virtual void setState(EState state); /** * Use this method if you want mStateChangedCallback to be executed while state is changed @@ -115,7 +115,7 @@ protected: EState mState; std::string mSessionName; LLSD mNotifyArgs; - LLSD mChannelInfo; + LLSD mChannelInfo; // true if call was ended by agent bool mCallEndedByAgent; bool mIgnoreNextSessionLeave; @@ -144,7 +144,7 @@ public: void handleError(EStatusType status) override; void activate() override; void deactivate() override; - void setChannelInfo(const LLSD &channelInfo) override; + void setChannelInfo(const LLSD &channelInfo) override; void requestChannelInfo() override; protected: @@ -163,28 +163,28 @@ class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingletonsimulatorFeaturesReceived()) - { - LLSD simulatorFeatures; - region->getSimulatorFeatures(simulatorFeatures); - setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); - } - else if (region) - { + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } + else if (region) + { if (mSimulatorFeaturesReceivedSlot.connected()) { mSimulatorFeaturesReceivedSlot.disconnect(); } mSimulatorFeaturesReceivedSlot = - region->setSimulatorFeaturesReceivedCallback( - boost::bind(&LLVoiceClient::onSimulatorFeaturesReceived, this, _1)); - } + region->setSimulatorFeaturesReceivedCallback( + boost::bind(&LLVoiceClient::onSimulatorFeaturesReceived, this, _1)); + } } void LLVoiceClient::onSimulatorFeaturesReceived(const LLUUID& region_id) { - LLViewerRegion *region = gAgent.getRegion(); - if (region && (region->getRegionID() == region_id)) - { - LLSD simulatorFeatures; - region->getSimulatorFeatures(simulatorFeatures); - setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); - } + LLViewerRegion *region = gAgent.getRegion(); + if (region && (region->getRegionID() == region_id)) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } } void LLVoiceClient::setSpatialVoiceModule(const std::string &voice_server_type) { - LLVoiceModuleInterface *module = getVoiceModule(voice_server_type); - if (!module) + LLVoiceModuleInterface *module = getVoiceModule(voice_server_type); + if (!module) { - return; + return; } - if (module != mSpatialVoiceModule) - { + if (module != mSpatialVoiceModule) + { if (inProximalChannel()) { - mSpatialVoiceModule->processChannels(false); + mSpatialVoiceModule->processChannels(false); } - module->processChannels(true); - mSpatialVoiceModule = module; - mSpatialVoiceModule->updateSettings(); - } + module->processChannels(true); + mSpatialVoiceModule = module; + mSpatialVoiceModule->updateSettings(); + } } void LLVoiceClient::setNonSpatialVoiceModule(const std::string &voice_server_type) { - mNonSpatialVoiceModule = getVoiceModule(voice_server_type); - if (!mNonSpatialVoiceModule) - { + mNonSpatialVoiceModule = getVoiceModule(voice_server_type); + if (!mNonSpatialVoiceModule) + { // we don't have a non-spatial voice module, // so revert to spatial. if (mSpatialVoiceModule) { - mSpatialVoiceModule->processChannels(true); + mSpatialVoiceModule->processChannels(true); } - return; - } - mNonSpatialVoiceModule->updateSettings(); + return; + } + mNonSpatialVoiceModule->updateSettings(); } void LLVoiceClient::setHidden(bool hidden) @@ -296,14 +296,13 @@ void LLVoiceClient::updateSettings() void LLVoiceClient::tuningStart() { LLWebRTCVoiceClient::getInstance()->tuningStart(); - LLVivoxVoiceClient::getInstance()->tuningStart(); + LLVivoxVoiceClient::getInstance()->tuningStart(); } void LLVoiceClient::tuningStop() { LLWebRTCVoiceClient::getInstance()->tuningStop(); - LLVivoxVoiceClient::getInstance()->tuningStop(); - + LLVivoxVoiceClient::getInstance()->tuningStop(); } bool LLVoiceClient::inTuningMode() @@ -347,13 +346,13 @@ void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) void LLVoiceClient::setCaptureDevice(const std::string& name) { LLWebRTCVoiceClient::getInstance()->setCaptureDevice(name); - LLVivoxVoiceClient::getInstance()->setCaptureDevice(name); + LLVivoxVoiceClient::getInstance()->setCaptureDevice(name); } void LLVoiceClient::setRenderDevice(const std::string& name) { LLWebRTCVoiceClient::getInstance()->setRenderDevice(name); - LLVivoxVoiceClient::getInstance()->setRenderDevice(name); + LLVivoxVoiceClient::getInstance()->setRenderDevice(name); } const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() @@ -373,13 +372,13 @@ const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() void LLVoiceClient::getParticipantList(std::set &participants) { - LLWebRTCVoiceClient::getInstance()->getParticipantList(participants); - LLVivoxVoiceClient::getInstance()->getParticipantList(participants); + LLWebRTCVoiceClient::getInstance()->getParticipantList(participants); + LLVivoxVoiceClient::getInstance()->getParticipantList(participants); } bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) { - return LLWebRTCVoiceClient::getInstance()->isParticipant(speaker_id) || + return LLWebRTCVoiceClient::getInstance()->isParticipant(speaker_id) || LLVivoxVoiceClient::getInstance()->isParticipant(speaker_id); } @@ -390,13 +389,13 @@ bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id) { // all sessions can do TextIM, as we no longer support PSTN - return TRUE; + return TRUE; } BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) { // we don't support PSTN calls anymore. (did we ever?) - return TRUE; + return TRUE; } //---------------------------------------------- @@ -419,17 +418,17 @@ void LLVoiceClient::setNonSpatialChannel( bool notify_on_first_join, bool hangup_on_last_leave) { - setNonSpatialVoiceModule(channelInfo["voice_server_type"].asString()); + setNonSpatialVoiceModule(channelInfo["voice_server_type"].asString()); if (mSpatialVoiceModule) - { - mSpatialVoiceModule->processChannels(false); - } + { + mSpatialVoiceModule->processChannels(false); + } if (mNonSpatialVoiceModule) { - mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, notify_on_first_join, hangup_on_last_leave); - mNonSpatialVoiceModule->processChannels(true); + mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, notify_on_first_join, hangup_on_last_leave); + mNonSpatialVoiceModule->processChannels(true); + } } -} void LLVoiceClient::setSpatialChannel(const LLSD &channelInfo) { @@ -784,48 +783,46 @@ class LLViewerRequiredVoiceVersion : public LLHTTPNode const LLSD& context, const LLSD& input) const { - - - std::string voice_server_type = "vivox"; + std::string voice_server_type = "vivox"; if (input.has("body") && input["body"].has("voice_server_type")) { - voice_server_type = input["body"]["voice_server_type"].asString(); + voice_server_type = input["body"]["voice_server_type"].asString(); } - LLVoiceModuleInterface *voiceModule = NULL; + LLVoiceModuleInterface *voiceModule = NULL; - if (voice_server_type == "vivox" || voice_server_type.empty()) - { - voiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance(); - } - else if (voice_server_type == "webrtc") - { - voiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); - } - else - { - LL_WARNS("Voice") << "Unknown voice server type " << voice_server_type << LL_ENDL; - if (!sAlertedUser) - { - // sAlertedUser = true; - LLNotificationsUtil::add("VoiceVersionMismatch"); - } - return; - } + if (voice_server_type == "vivox" || voice_server_type.empty()) + { + voiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance(); + } + else if (voice_server_type == "webrtc") + { + voiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance(); + } + else + { + LL_WARNS("Voice") << "Unknown voice server type " << voice_server_type << LL_ENDL; + if (!sAlertedUser) + { + // sAlertedUser = true; + LLNotificationsUtil::add("VoiceVersionMismatch"); + } + return; + } LLVoiceVersionInfo versionInfo = voiceModule->getVersion(); - if (input.has("body") && input["body"].has("major_version") && + if (input.has("body") && input["body"].has("major_version") && input["body"]["major_version"].asInteger() > versionInfo.majorVersion) { - if (!sAlertedUser) - { - // sAlertedUser = true; - LLNotificationsUtil::add("VoiceVersionMismatch"); - LL_WARNS("Voice") << "Voice server version mismatch " << input["body"]["major_version"].asInteger() << "/" - << versionInfo.majorVersion - << LL_ENDL; - } - return; + if (!sAlertedUser) + { + // sAlertedUser = true; + LLNotificationsUtil::add("VoiceVersionMismatch"); + LL_WARNS("Voice") << "Voice server version mismatch " << input["body"]["major_version"].asInteger() << "/" + << versionInfo.majorVersion + << LL_ENDL; + } + return; } } }; diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 46dd325e21..02f73cde7a 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -96,10 +96,10 @@ public: struct LLVoiceVersionInfo { std::string voiceServerType; - std::string internalVoiceServerType; - int majorVersion; - int minorVersion; - std::string serverVersion; + std::string internalVoiceServerType; + int majorVersion; + int minorVersion; + std::string serverVersion; std::string mBuildVersion; }; @@ -205,7 +205,7 @@ public: virtual bool inProximalChannel()=0; virtual void setNonSpatialChannel(const LLSD& channelInfo, - bool notify_on_first_join, + bool notify_on_first_join, bool hangup_on_last_leave)=0; virtual bool setSpatialChannel(const LLSD& channelInfo)=0; @@ -474,13 +474,13 @@ public: //@} void setSpatialVoiceModule(const std::string& voice_server_type); - void setNonSpatialVoiceModule(const std::string &voice_server_type); + void setNonSpatialVoiceModule(const std::string &voice_server_type); void userAuthorized(const std::string& user_id, const LLUUID &agentID); - void onRegionChanged(); - void onSimulatorFeaturesReceived(const LLUUID ®ion_id); + void onRegionChanged(); + void onSimulatorFeaturesReceived(const LLUUID ®ion_id); void addObserver(LLVoiceClientStatusObserver* observer); void removeObserver(LLVoiceClientStatusObserver* observer); -- 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/llvoiceclient.cpp | 113 +++++++++++++++++++++++++++------------- indra/newview/llvoiceclient.h | 11 +++- indra/newview/llvoicevivox.cpp | 24 ++++++--- indra/newview/llvoicewebrtc.cpp | 45 +++++++++------- indra/newview/llvoicewebrtc.h | 3 +- 5 files changed, 131 insertions(+), 65 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 1f5dbe3b97..44e00b69e5 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -135,6 +135,7 @@ LLVoiceModuleInterface *getVoiceModule(const std::string &voice_server_type) LLVoiceClient::LLVoiceClient(LLPumpIO *pump) : mSpatialVoiceModule(NULL), + mNonSpatialVoiceModule(NULL), m_servicePump(NULL), mVoiceEffectEnabled(LLCachedControl(gSavedSettings, "VoiceMorphingEnabled", true)), mVoiceEffectDefault(LLCachedControl(gSavedPerAccountSettings, "VoiceEffectDefault", "00000000-0000-0000-0000-000000000000")), @@ -174,35 +175,71 @@ void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &age LLVivoxVoiceClient::getInstance()->userAuthorized(user_id, agentID); } -void LLVoiceClient::onRegionChanged() +void LLVoiceClient::handleSimulatorFeaturesReceived(const LLSD &simulatorFeatures) { - LLViewerRegion *region = gAgent.getRegion(); - if (region && region->simulatorFeaturesReceived()) + std::string voiceServerType = simulatorFeatures["VoiceServerType"].asString(); + if (voiceServerType.empty()) { - LLSD simulatorFeatures; - region->getSimulatorFeatures(simulatorFeatures); - setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + voiceServerType = VIVOX_VOICE_SERVER_TYPE; } - else if (region) + + if (mSpatialVoiceModule && !mNonSpatialVoiceModule) { - if (mSimulatorFeaturesReceivedSlot.connected()) + // stop processing if we're going to change voice modules + // and we're not currently in non-spatial. + LLVoiceVersionInfo version = mSpatialVoiceModule->getVersion(); + if (version.internalVoiceServerType != voiceServerType) { - mSimulatorFeaturesReceivedSlot.disconnect(); + mSpatialVoiceModule->processChannels(false); } - mSimulatorFeaturesReceivedSlot = - region->setSimulatorFeaturesReceivedCallback( - boost::bind(&LLVoiceClient::onSimulatorFeaturesReceived, this, _1)); + } + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + + // if we should be in spatial voice, switch to it and set the creds + if (mSpatialVoiceModule && !mNonSpatialVoiceModule) + { + if (!mSpatialCredentials.isUndefined()) + { + mSpatialVoiceModule->setSpatialChannel(mSpatialCredentials); + } + mSpatialVoiceModule->processChannels(true); } } -void LLVoiceClient::onSimulatorFeaturesReceived(const LLUUID& region_id) +static void simulator_features_received_callback(const LLUUID& region_id) { LLViewerRegion *region = gAgent.getRegion(); if (region && (region->getRegionID() == region_id)) { LLSD simulatorFeatures; region->getSimulatorFeatures(simulatorFeatures); - setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + if (LLVoiceClient::getInstance()) + { + LLVoiceClient::getInstance()->handleSimulatorFeaturesReceived(simulatorFeatures); + } + } +} + +void LLVoiceClient::onRegionChanged() +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + if (LLVoiceClient::getInstance()) + { + LLVoiceClient::getInstance()->handleSimulatorFeaturesReceived(simulatorFeatures); + } + } + else if (region) + { + if (mSimulatorFeaturesReceivedSlot.connected()) + { + mSimulatorFeaturesReceivedSlot.disconnect(); + } + mSimulatorFeaturesReceivedSlot = + region->setSimulatorFeaturesReceivedCallback(boost::bind(&simulator_features_received_callback, _1)); } } @@ -419,37 +456,36 @@ void LLVoiceClient::setNonSpatialChannel( bool hangup_on_last_leave) { setNonSpatialVoiceModule(channelInfo["voice_server_type"].asString()); - if (mSpatialVoiceModule) + if (mSpatialVoiceModule && mSpatialVoiceModule != mNonSpatialVoiceModule) { mSpatialVoiceModule->processChannels(false); } if (mNonSpatialVoiceModule) { - mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, notify_on_first_join, hangup_on_last_leave); mNonSpatialVoiceModule->processChannels(true); + mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, notify_on_first_join, hangup_on_last_leave); } - } +} void LLVoiceClient::setSpatialChannel(const LLSD &channelInfo) { - LLViewerRegion *region = gAgent.getRegion(); - if (region && region->simulatorFeaturesReceived()) - { - LLSD simulatorFeatures; - region->getSimulatorFeatures(simulatorFeatures); - setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); - } - if (mNonSpatialVoiceModule) - { - mNonSpatialVoiceModule->leaveNonSpatialChannel(); - mNonSpatialVoiceModule->processChannels(false); - mNonSpatialVoiceModule = nullptr; - } + mSpatialCredentials = channelInfo; + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString()); + } + else + { + return; + } + if (mSpatialVoiceModule) - { - mSpatialVoiceModule->setSpatialChannel(channelInfo); - mSpatialVoiceModule->processChannels(true); - } + { + mSpatialVoiceModule->setSpatialChannel(channelInfo); + } } void LLVoiceClient::leaveNonSpatialChannel() @@ -564,9 +600,12 @@ void LLVoiceClient::updateMicMuteLogic() void LLVoiceClient::setMuteMic(bool muted) { - mMuteMic = muted; - updateMicMuteLogic(); - mMicroChangedSignal(); + if (mMuteMic != muted) + { + mMuteMic = muted; + updateMicMuteLogic(); + mMicroChangedSignal(); + } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 02f73cde7a..3b1b3bd0c4 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -480,7 +480,6 @@ public: const LLUUID &agentID); void onRegionChanged(); - void onSimulatorFeaturesReceived(const LLUUID ®ion_id); void addObserver(LLVoiceClientStatusObserver* observer); void removeObserver(LLVoiceClientStatusObserver* observer); @@ -500,12 +499,20 @@ public: // Returns NULL if voice effects are not supported, or not enabled. LLVoiceEffectInterface* getVoiceEffectInterface() const; //@} -private: + + void handleSimulatorFeaturesReceived(const LLSD &simulatorFeatures); + + private: + void init(LLPumpIO *pump); protected: + LLVoiceModuleInterface* mSpatialVoiceModule; LLVoiceModuleInterface* mNonSpatialVoiceModule; + LLSD mSpatialCredentials; // used to store spatial credentials for vivox + // so they're available when the region voice + // server is retrieved. LLPumpIO *m_servicePump; boost::signals2::connection mSimulatorFeaturesReceivedSlot; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 9a1fb925eb..29aba8ecba 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2101,13 +2101,13 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) { LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL; - bool joined_session = addAndJoinSession(session); - - if (sShuttingDown) + if (sShuttingDown || !mProcessChannels) { return false; } + bool joined_session = addAndJoinSession(session); + if (!joined_session) { notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); @@ -2137,7 +2137,8 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) && mVoiceEnabled && isGatewayRunning() && !mSessionTerminateRequested - && !mTuningMode) + && !mTuningMode + && mProcessChannels) { sendCaptureAndRenderDevices(); // suspends @@ -4923,9 +4924,14 @@ void LLVivoxVoiceClient::setNonSpatialChannel(const LLSD& channelInfo, bool noti bool LLVivoxVoiceClient::setSpatialChannel(const LLSD& channelInfo) { - mProcessChannels = true; - mSpatialSessionURI = channelInfo["channel_uri"].asString(); + mSpatialSessionURI = channelInfo["channel_uri"].asString(); mSpatialSessionCredentials = channelInfo["channel_credentials"].asString(); + if (!mProcessChannels) + { + // we're not even processing channels (another provider is) so + // save the credentials aside and exit + return false; + } LL_DEBUGS("Voice") << "got spatial channel uri: \"" << mSpatialSessionURI << "\"" << LL_ENDL; @@ -6227,6 +6233,12 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta << ", proximal is " << inSpatialChannel() << LL_ENDL; + if (!mProcessChannels) + { + // we're not processing...another voice module is. + // so nobody wants to hear from us. + return; + } for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end(); ) 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); + } } } } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 0e8ac3a183..03bbe00162 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -146,7 +146,8 @@ public: bool setSpatialChannel(const LLSD &channelInfo) override { - processChannels(true); + // we don't really have credentials for a spatial channel in webrtc, + // it's all handled by the sim. return true; } -- cgit v1.2.3 From 76460e8c2c33af185ae8641775d5673fcdab0759 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 11 Mar 2024 22:45:36 -0700 Subject: Use LLCachedControl to speed up voice enable/disable checking --- indra/newview/llvoiceclient.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 44e00b69e5..b9b8742c41 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -569,7 +569,9 @@ void LLVoiceClient::setMicGain(F32 gain) bool LLVoiceClient::voiceEnabled() { - return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice") && !gNonInteractive; + static LLCachedControl enable_voice_chat(gSavedSettings, "EnableVoiceChat"); + static LLCachedControl cmd_line_disable_voice(gSavedSettings, "CmdLineDisableVoice"); + return enable_voice_chat && !cmd_line_disable_voice && !gNonInteractive; } void LLVoiceClient::setVoiceEnabled(bool enabled) -- cgit v1.2.3