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