summaryrefslogtreecommitdiff
path: root/indra/newview/llvoicevivox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llvoicevivox.cpp')
-rwxr-xr-xindra/newview/llvoicevivox.cpp3449
1 files changed, 1709 insertions, 1740 deletions
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index a6a7a35b03..e9e004f492 100755
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -60,10 +60,14 @@
#include "lltrans.h"
#include "llviewerwindow.h"
#include "llviewercamera.h"
+#include "llversioninfo.h"
#include "llviewernetwork.h"
#include "llnotificationsutil.h"
+#include "llcorehttputil.h"
+#include "lleventfilter.h"
+
#include "stringize.h"
// for base64 decoding
@@ -85,17 +89,19 @@ static const std::string VOICE_SERVER_TYPE = "Vivox";
const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
// Don't send positional updates more frequently than this:
-const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
+const F32 UPDATE_THROTTLE_SECONDS = 0.5f;
const F32 LOGIN_RETRY_SECONDS = 10.0f;
const int MAX_LOGIN_RETRIES = 12;
+// Cosine of a "trivially" small angle
+const F32 MINUSCULE_ANGLE_COS = 0.999f;
+
// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine()
-// which is treated as normal. If this number is exceeded we suspect there is a problem with connection
-// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen
-// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is
-// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability.
-const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50;
+// 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;
@@ -122,65 +128,8 @@ static int scale_speaker_volume(float volume)
}
-class LLVivoxVoiceAccountProvisionResponder :
- public LLHTTPClient::Responder
-{
- LOG_CLASS(LLVivoxVoiceAccountProvisionResponder);
-public:
- LLVivoxVoiceAccountProvisionResponder(int retries)
- {
- mRetries = retries;
- }
-
-private:
- /* virtual */ void httpFailure()
- {
- LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, "
- << ( (mRetries > 0) ? "retrying" : "too many retries (giving up)" )
- << " " << dumpResponse() << LL_ENDL;
-
- if ( mRetries > 0 )
- {
- LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision(mRetries - 1);
- }
- else
- {
- LLVivoxVoiceClient::getInstance()->giveUp();
- }
- }
-
- /* virtual */ void httpSuccess()
- {
- std::string voice_sip_uri_hostname;
- std::string voice_account_server_uri;
-
- LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL;
-
- const LLSD& content = getContent();
- if (!content.isMap())
- {
- failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
- return;
- }
- if(content.has("voice_sip_uri_hostname"))
- voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
-
- // this key is actually misnamed -- it will be an entire URI, not just a hostname.
- if(content.has("voice_account_server_name"))
- voice_account_server_uri = content["voice_account_server_name"].asString();
-
- LLVivoxVoiceClient::getInstance()->login(
- content["username"].asString(),
- content["password"].asString(),
- voice_sip_uri_hostname,
- voice_account_server_uri);
- }
-
-private:
- int mRetries;
-};
-
-
+const int ERROR_VIVOX_OBJECT_NOT_FOUND = 1001;
+const int ERROR_VIVOX_NOT_LOGGED_IN = 1007;
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -194,59 +143,6 @@ static LLVivoxVoiceClientMuteListObserver mutelist_listener;
static bool sMuteListListener_listening = false;
///////////////////////////////////////////////////////////////////////////////////////////////
-
-class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder
-{
- LOG_CLASS(LLVivoxVoiceClientCapResponder);
-public:
- LLVivoxVoiceClientCapResponder(LLVivoxVoiceClient::state requesting_state) : mRequestingState(requesting_state) {};
-
-private:
- // called with bad status codes
- /* virtual */ void httpFailure();
- /* virtual */ void httpSuccess();
-
- LLVivoxVoiceClient::state mRequestingState; // state
-};
-
-void LLVivoxVoiceClientCapResponder::httpFailure()
-{
- LL_WARNS("Voice") << dumpResponse() << LL_ENDL;
- LLVivoxVoiceClient::getInstance()->sessionTerminate();
-}
-
-void LLVivoxVoiceClientCapResponder::httpSuccess()
-{
- LLSD::map_const_iterator iter;
-
- LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << dumpResponse() << LL_ENDL;
-
- std::string uri;
- std::string credentials;
-
- const LLSD& content = getContent();
- if ( content.has("voice_credentials") )
- {
- LLSD voice_credentials = content["voice_credentials"];
- if ( voice_credentials.has("channel_uri") )
- {
- uri = voice_credentials["channel_uri"].asString();
- }
- if ( voice_credentials.has("channel_credentials") )
- {
- credentials =
- voice_credentials["channel_credentials"].asString();
- }
- }
-
- // set the spatial channel. If no voice credentials or uri are
- // available, then we simply drop out of voice spatially.
- if(LLVivoxVoiceClient::getInstance()->parcelVoiceInfoReceived(mRequestingState))
- {
- LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials);
- }
-}
-
static LLProcessPtr sGatewayPtr;
static bool isGatewayRunning()
@@ -265,7 +161,6 @@ static void killGateway()
///////////////////////////////////////////////////////////////////////////////////////////////
LLVivoxVoiceClient::LLVivoxVoiceClient() :
- mState(stateDisabled),
mSessionTerminateRequested(false),
mRelogRequested(false),
mConnected(false),
@@ -277,14 +172,14 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mTuningEnergy(0.0f),
mTuningMicVolume(0),
mTuningMicVolumeDirty(true),
- mTuningSpeakerVolume(0),
+ mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume
mTuningSpeakerVolumeDirty(true),
- mTuningExitState(stateDisabled),
+ mDevicesListUpdated(false),
mAreaVoiceDisabled(false),
- mAudioSession(NULL),
+ mAudioSession(),
mAudioSessionChanged(false),
- mNextAudioSession(NULL),
+ mNextAudioSession(),
mCurrentParcelLocalID(0),
mNumberOfAliases(0),
@@ -326,7 +221,16 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mShutdownComplete(true),
mPlayRequestCount(0),
- mAvatarNameCacheConnection()
+ mAvatarNameCacheConnection(),
+ mIsInTuningMode(false),
+ mIsInChannel(false),
+ mIsJoiningSession(false),
+ mIsWaitingForFonts(false),
+ mIsLoggingIn(false),
+ mIsLoggedIn(false),
+ mIsProcessingChannels(false),
+ mIsCoroutineActive(false),
+ mVivoxPump("vivoxClientPump")
{
mSpeakerVolume = scale_speaker_volume(0);
@@ -349,9 +253,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
signal(SIGCHLD, SIG_IGN);
#endif
- // set up state machine
- setState(stateDisabled);
-
+
gIdleCallbacks.addFunction(idle, this);
}
@@ -371,25 +273,22 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump)
{
// constructor will set up LLVoiceClient::getInstance()
LLVivoxVoiceClient::getInstance()->mPump = pump;
+
+// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
+// boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance()));
+
}
void LLVivoxVoiceClient::terminate()
{
+ // needs to be done manually here since we will not get another pass in
+ // coroutines... that mechanism is long since gone.
+ if (mIsLoggedIn)
+ logoutOfVivox(false);
if(mConnected)
{
- logout();
- connectorShutdown();
-#ifdef LL_WINDOWS
- int count=0;
- while (!mShutdownComplete && 10 > count++)
- {
- stateMachine();
- _sleep(1000);
- }
-
-#endif
- closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
- cleanUp();
+ breakVoiceConnection(false);
+ mConnected = false;
}
else
{
@@ -435,6 +334,8 @@ void LLVivoxVoiceClient::updateSettings()
bool LLVivoxVoiceClient::writeString(const std::string &str)
{
bool result = false;
+// LL_WARNS("LOW Voice") << "sending:\n" << str << LL_ENDL;
+
if(mConnected)
{
apr_status_t err;
@@ -450,17 +351,20 @@ bool LLVivoxVoiceClient::writeString(const std::string &str)
(const char*)str.data(),
&written);
- if(err == 0)
+ if(err == 0 && written == size)
{
// Success.
result = true;
}
- // TODO: handle partial writes (written is number of bytes written)
- // Need to set socket to non-blocking before this will work.
-// else if(APR_STATUS_IS_EAGAIN(err))
-// {
-// //
-// }
+ else if (err == 0 && written != size) {
+ // Did a short write, log it for now
+ LL_WARNS("Voice") << ") short write on socket sending data to vivox daemon." << "Sent " << written << "bytes instead of " << size <<LL_ENDL;
+ }
+ else if(APR_STATUS_IS_EAGAIN(err))
+ {
+ char buf[MAX_STRING];
+ LL_WARNS("Voice") << "EAGAIN error " << err << " (" << apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
+ }
else
{
// Assume any socket error means something bad. For now, just close the socket.
@@ -483,8 +387,6 @@ void LLVivoxVoiceClient::connectorCreate()
std::string loglevel = "0";
// Transition to stateConnectorStarted when the connector handle comes back.
- setState(stateConnectorStarting);
-
std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
if(savedLogLevel != "0")
@@ -498,22 +400,21 @@ void LLVivoxVoiceClient::connectorCreate()
<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
<< "<Mode>Normal</Mode>"
<< "<Logging>"
- << "<Folder>" << logpath << "</Folder>"
- << "<FileNamePrefix>Connector</FileNamePrefix>"
- << "<FileNameSuffix>.log</FileNameSuffix>"
- << "<LogLevel>" << loglevel << "</LogLevel>"
+ << "<Folder>" << logpath << "</Folder>"
+ << "<FileNamePrefix>Connector</FileNamePrefix>"
+ << "<FileNameSuffix>.log</FileNameSuffix>"
+ << "<LogLevel>" << loglevel << "</LogLevel>"
<< "</Logging>"
- << "<Application></Application>" //Name can cause problems per vivox.
- << "<MaxCalls>12</MaxCalls>"
- << "</Request>\n\n\n";
+ << "<Application>" << LLVersionInfo::getChannel().c_str() << " " << LLVersionInfo::getVersion().c_str() << "</Application>"
+ //<< "<Application></Application>" //Name can cause problems per vivox.
+ << "<MaxCalls>12</MaxCalls>"
+ << "</Request>\n\n\n";
writeString(stream.str());
}
void LLVivoxVoiceClient::connectorShutdown()
{
- setState(stateConnectorStopping);
-
if(!mConnectorHandle.empty())
{
std::ostringstream stream;
@@ -540,33 +441,7 @@ void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID
mAccountName = nameFromID(agentID);
}
-void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
-{
- LLViewerRegion *region = gAgent.getRegion();
-
- // If we've not received the capability yet, return.
- // the password will remain empty, so we'll remain in
- // stateIdle
- if ( region &&
- region->capabilitiesReceived() &&
- (mVoiceEnabled || !mIsInitialized))
- {
- std::string url =
- region->getCapability("ProvisionVoiceAccountRequest");
-
- if ( !url.empty() )
- {
- LLHTTPClient::post(
- url,
- LLSD(),
- new LLVivoxVoiceAccountProvisionResponder(retries));
-
- setState(stateConnectorStart);
- }
- }
-}
-
-void LLVivoxVoiceClient::login(
+void LLVivoxVoiceClient::setLoginInfo(
const std::string& account_name,
const std::string& password,
const std::string& voice_sip_uri_hostname,
@@ -634,1032 +509,1244 @@ void LLVivoxVoiceClient::login(
void LLVivoxVoiceClient::idle(void* user_data)
{
- LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data;
- self->stateMachine();
-}
-
-std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState)
-{
- std::string result = "UNKNOWN";
-
- // Prevent copy-paste errors when updating this list...
-#define CASE(x) case x: result = #x; break
-
- switch(inState)
- {
- CASE(stateDisableCleanup);
- CASE(stateDisabled);
- CASE(stateStart);
- CASE(stateDaemonLaunched);
- CASE(stateConnecting);
- CASE(stateConnected);
- CASE(stateIdle);
- CASE(stateMicTuningStart);
- CASE(stateMicTuningRunning);
- CASE(stateMicTuningStop);
- CASE(stateCaptureBufferPaused);
- CASE(stateCaptureBufferRecStart);
- CASE(stateCaptureBufferRecording);
- CASE(stateCaptureBufferPlayStart);
- CASE(stateCaptureBufferPlaying);
- CASE(stateConnectorStart);
- CASE(stateConnectorStarting);
- CASE(stateConnectorStarted);
- CASE(stateLoginRetry);
- CASE(stateLoginRetryWait);
- CASE(stateNeedsLogin);
- CASE(stateLoggingIn);
- CASE(stateLoggedIn);
- CASE(stateVoiceFontsWait);
- CASE(stateVoiceFontsReceived);
- CASE(stateCreatingSessionGroup);
- CASE(stateNoChannel);
- CASE(stateRetrievingParcelVoiceInfo);
- CASE(stateJoiningSession);
- CASE(stateSessionJoined);
- CASE(stateRunning);
- CASE(stateLeavingSession);
- CASE(stateSessionTerminated);
- CASE(stateLoggingOut);
- CASE(stateLoggedOut);
- CASE(stateConnectorStopping);
- CASE(stateConnectorStopped);
- CASE(stateConnectorFailed);
- CASE(stateConnectorFailedWaiting);
- CASE(stateLoginFailed);
- CASE(stateLoginFailedWaiting);
- CASE(stateJoinSessionFailed);
- CASE(stateJoinSessionFailedWaiting);
- CASE(stateJail);
- }
-
-#undef CASE
-
- return result;
}
+//=========================================================================
+// the following are methods to support the coroutine implementation of the
+// voice connection and processing. They should only be called in the context
+// of a coroutine.
+//
+//
+void LLVivoxVoiceClient::voiceControlCoro()
+{
+ mIsCoroutineActive = true;
+ LLCoros::set_consuming(true);
+
+ do
+ {
+
+ startAndConnectSession();
+
+ if (mTuningMode)
+ {
+ performMicTuning();
+ }
+ else if (mVoiceEnabled)
+ {
+ waitForChannel();
+ }
+
+ endAndDisconnectSession();
+
+ // if we hit this and mRelogRequested is true, that indicates
+ // that we attempted to relog into Vivox and were rejected.
+ // Rather than just quit out of voice, we will tear it down (above)
+ // and then reconstruct the voice connecino from scratch.
+ if (mRelogRequested)
+ {
+ while (isGatewayRunning())
+ {
+ llcoro::suspendUntilTimeout(1.0);
+ }
+ }
+ }
+ while (mRelogRequested);
+ mIsCoroutineActive = false;
+}
+
-void LLVivoxVoiceClient::setState(state inState)
+bool LLVivoxVoiceClient::startAndConnectSession()
{
- LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL;
-
- mState = inState;
+ if (!startAndLaunchDaemon())
+ {
+ return false;
+ }
+
+ if (!provisionVoiceAccount())
+ {
+ giveUp();
+ return false;
+ }
+
+ if (!establishVoiceConnection())
+ {
+ giveUp();
+ return false;
+ }
+
+ return true;
}
-void LLVivoxVoiceClient::stateMachine()
+bool LLVivoxVoiceClient::endAndDisconnectSession()
{
- if(gDisconnected)
- {
- // The viewer has been disconnected from the sim. Disable voice.
- setVoiceEnabled(false);
- }
-
- if(mVoiceEnabled || (!mIsInitialized &&!mTerminateDaemon) )
- {
- updatePosition();
- }
- else if(mTuningMode)
- {
- // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled.
- }
- else
- {
- if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
- {
- // User turned off voice support. Send the cleanup messages, close the socket, and reset.
- if(!mConnected || mTerminateDaemon)
- {
- // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
- LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
- killGateway();
- mTerminateDaemon = false;
- }
-
- logout();
- connectorShutdown();
-
- setState(stateDisableCleanup);
- }
- }
-
+ breakVoiceConnection(true);
- switch(getState())
- {
- //MARK: stateDisableCleanup
- case stateDisableCleanup:
- // Clean up and reset everything.
- closeSocket();
- cleanUp();
+ killGateway();
- mAccountHandle.clear();
- mAccountPassword.clear();
- mVoiceAccountServerURI.clear();
-
- setState(stateDisabled);
- break;
-
- //MARK: stateDisabled
- case stateDisabled:
- if(mTuningMode || ((mVoiceEnabled || !mIsInitialized) && !mAccountName.empty()))
- {
- setState(stateStart);
- }
- break;
-
- //MARK: stateStart
- case stateStart:
- if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
- {
- // Voice is locked out, we must not launch the vivox daemon.
- setState(stateJail);
- }
- else if(!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat"))
- {
- if (true) // production build, not test
- {
- // Launch the voice daemon
-
- // *FIX:Mani - Using the executable dir instead
- // of mAppRODataDir, the working directory from which the app
- // is launched.
- //std::string exe_path = gDirUtilp->getAppRODataDir();
- std::string exe_path = gDirUtilp->getExecutableDir();
- exe_path += gDirUtilp->getDirDelimiter();
+ return true;
+}
+
+
+bool LLVivoxVoiceClient::startAndLaunchDaemon()
+{
+ //---------------------------------------------------------------------
+ if (gSavedSettings.getBOOL("CmdLineDisableVoice"))
+ {
+ // Voice is locked out, we must not launch the vivox daemon.
+ return false;
+ }
+
+ if (!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat"))
+ {
+#ifndef VIVOXDAEMON_REMOTEHOST
+ // Launch the voice daemon
+
+ // *FIX:Mani - Using the executable dir instead
+ // of mAppRODataDir, the working directory from which the app
+ // is launched.
+ //std::string exe_path = gDirUtilp->getAppRODataDir();
+ std::string exe_path = gDirUtilp->getExecutableDir();
+ exe_path += gDirUtilp->getDirDelimiter();
#if LL_WINDOWS
- exe_path += "SLVoice.exe";
+ exe_path += "SLVoice.exe";
#elif LL_DARWIN
- exe_path += "../Resources/SLVoice";
+ exe_path += "../Resources/SLVoice";
#else
- exe_path += "SLVoice";
+ exe_path += "SLVoice";
#endif
- // See if the vivox executable exists
- llstat s;
- if (!LLFile::stat(exe_path, &s))
- {
- // vivox executable exists. Build the command line and launch the daemon.
- LLProcess::Params params;
- params.executable = exe_path;
+ // See if the vivox executable exists
+ llstat s;
+ if (!LLFile::stat(exe_path, &s))
+ {
+ // vivox executable exists. Build the command line and launch the daemon.
+ LLProcess::Params params;
+ params.executable = exe_path;
- std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
- std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout");
- if(loglevel.empty())
- {
- loglevel = "0"; // turn logging off completely
- }
-
- params.args.add("-ll");
- params.args.add(loglevel);
+ std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
+ std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout");
+ if (loglevel.empty())
+ {
+ loglevel = "-1"; // turn logging off completely, was 0 for error level logging.
+ }
- std::string log_folder = gSavedSettings.getString("VivoxLogDirectory");
-
- if (log_folder.empty())
- {
- log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
- }
-
- params.args.add("-lf");
- params.args.add(log_folder);
+ params.args.add("-ll");
+ params.args.add(loglevel);
- if(!shutdown_timeout.empty())
- {
- params.args.add("-st");
- params.args.add(shutdown_timeout);
- }
- params.cwd = gDirUtilp->getAppRODataDir();
- sGatewayPtr = LLProcess::create(params);
+ std::string log_folder = gSavedSettings.getString("VivoxLogDirectory");
- mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
- }
- else
- {
- LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
- }
- }
- else
- {
- // SLIM SDK: port changed from 44124 to 44125.
- // We can connect to a client gateway running on another host. This is useful for testing.
- // To do this, launch the gateway on a nearby host like this:
- // vivox-gw.exe -p tcp -i 0.0.0.0:44125
- // and put that host's IP address here.
- mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
- }
+ if (log_folder.empty())
+ {
+ log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+ }
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+ params.args.add("-lf");
+ params.args.add(log_folder);
- setState(stateDaemonLaunched);
-
- // Dirty the states we'll need to sync with the daemon when it comes up.
- mMuteMicDirty = true;
- mMicVolumeDirty = true;
- mSpeakerVolumeDirty = true;
- mSpeakerMuteDirty = true;
- // These only need to be set if they're not default (i.e. empty string).
- mCaptureDeviceDirty = !mCaptureDevice.empty();
- mRenderDeviceDirty = !mRenderDevice.empty();
-
- mMainSessionGroupHandle.clear();
- }
- break;
+ if (!shutdown_timeout.empty())
+ {
+ params.args.add("-st");
+ params.args.add(shutdown_timeout);
+ }
+ params.cwd = gDirUtilp->getAppRODataDir();
+ sGatewayPtr = LLProcess::create(params);
- //MARK: stateDaemonLaunched
- case stateDaemonLaunched:
- if(mUpdateTimer.hasExpired())
- {
- LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
+ mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
+ }
+ else
+ {
+ LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
+ return false;
+ }
+#else
+ // SLIM SDK: port changed from 44124 to 44125.
+ // We can connect to a client gateway running on another host. This is useful for testing.
+ // To do this, launch the gateway on a nearby host like this:
+ // vivox-gw.exe -p tcp -i 0.0.0.0:44125
+ // and put that host's IP address here.
+ mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
+#endif
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+ // Dirty the states we'll need to sync with the daemon when it comes up.
+ mMuteMicDirty = true;
+ mMicVolumeDirty = true;
+ mSpeakerVolumeDirty = true;
+ mSpeakerMuteDirty = true;
+ // These only need to be set if they're not default (i.e. empty string).
+ mCaptureDeviceDirty = !mCaptureDevice.empty();
+ mRenderDeviceDirty = !mRenderDevice.empty();
- if(!mSocket)
- {
- mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
- }
-
- mConnected = mSocket->blockingConnect(mDaemonHost);
- if(mConnected)
- {
- setState(stateConnecting);
- }
- else
- {
- // If the connect failed, the socket may have been put into a bad state. Delete it.
- closeSocket();
- }
- }
- break;
+ mMainSessionGroupHandle.clear();
+ }
- //MARK: stateConnecting
- case stateConnecting:
- // Can't do this until we have the pump available.
- if(mPump)
- {
- // MBW -- Note to self: pumps and pipes examples in
- // indra/test/io.cpp
- // indra/test/llpipeutil.{cpp|h}
+ //---------------------------------------------------------------------
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
- // Attach the pumps and pipes
-
- LLPumpIO::chain_t readChain;
+ LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
- readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
- readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
+ int connectAttempt = 0;
- mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
+ while (!mConnected)
+ {
+ ++connectAttempt;
+ LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << " (#" << connectAttempt << ")" << LL_ENDL;
+ closeSocket();
+ if (!mSocket)
+ {
+ mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ }
- setState(stateConnected);
- }
+ mConnected = mSocket->blockingConnect(mDaemonHost);
+ if (!mConnected)
+ llcoro::suspendUntilTimeout(CONNECT_THROTTLE_SECONDS);
+ }
- break;
-
- //MARK: stateConnected
- case stateConnected:
- // Initial devices query
- getCaptureDevicesSendMessage();
- getRenderDevicesSendMessage();
+ //---------------------------------------------------------------------
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
- mLoginRetryCount = 0;
+ while (!mPump)
+ { // Can't do this until we have the pump available.
+ llcoro::suspend();
+ }
+
+ // MBW -- Note to self: pumps and pipes examples in
+ // indra/test/io.cpp
+ // indra/test/llpipeutil.{cpp|h}
- setState(stateIdle);
- break;
+ // Attach the pumps and pipes
- //MARK: stateIdle
- case stateIdle:
- // This is the idle state where we're connected to the daemon but haven't set up a connector yet.
- if(mTuningMode)
- {
- mTuningExitState = stateIdle;
- setState(stateMicTuningStart);
- }
- else if(!mVoiceEnabled && mIsInitialized)
- {
- // We never started up the connector. This will shut down the daemon.
- setState(stateConnectorStopped);
- }
- else if(!mAccountName.empty())
- {
- if ( mAccountPassword.empty() )
- {
- requestVoiceAccountProvision();
- }
- }
- break;
+ LLPumpIO::chain_t readChain;
- //MARK: stateMicTuningStart
- case stateMicTuningStart:
- if(mUpdateTimer.hasExpired())
- {
- if(mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // These can't be changed while in tuning mode. Set them before starting.
- std::ostringstream stream;
-
- buildSetCaptureDevice(stream);
- buildSetRenderDevice(stream);
+ readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
+ readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
- if(!stream.str().empty())
- {
- writeString(stream.str());
- }
+ mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
- // This will come around again in the same state and start the capture, after the timer expires.
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- }
- else
- {
- // duration parameter is currently unused, per Mike S.
- tuningCaptureStartSendMessage(10000);
+ //---------------------------------------------------------------------
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
- setState(stateMicTuningRunning);
- }
- }
-
- break;
-
- //MARK: stateMicTuningRunning
- case stateMicTuningRunning:
- if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // All of these conditions make us leave tuning mode.
- setState(stateMicTuningStop);
- }
- else
- {
- // process mic/speaker volume changes
- if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
- {
- std::ostringstream stream;
-
- if(mTuningMicVolumeDirty)
- {
- LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
- << "<Level>" << mTuningMicVolume << "</Level>"
- << "</Request>\n\n\n";
- }
-
- if(mTuningSpeakerVolumeDirty)
- {
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
- << "<Level>" << mTuningSpeakerVolume << "</Level>"
- << "</Request>\n\n\n";
- }
-
- mTuningMicVolumeDirty = false;
- mTuningSpeakerVolumeDirty = false;
+ // Initial devices query
+ getCaptureDevicesSendMessage();
+ getRenderDevicesSendMessage();
- if(!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- }
- break;
-
- //MARK: stateMicTuningStop
- case stateMicTuningStop:
- {
- // transition out of mic tuning
- tuningCaptureStopSendMessage();
-
- setState(mTuningExitState);
-
- // if we exited just to change devices, this will keep us from re-entering too fast.
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-
- }
- break;
+ mLoginRetryCount = 0;
- //MARK: stateCaptureBufferPaused
- case stateCaptureBufferPaused:
- if (!mCaptureBufferMode)
- {
- // Leaving capture mode.
+ return true;
+}
- mCaptureBufferRecording = false;
- mCaptureBufferRecorded = false;
- mCaptureBufferPlaying = false;
+bool LLVivoxVoiceClient::provisionVoiceAccount()
+{
+ LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL;
+ while (!gAgent.getRegion())
+ {
+ // *TODO* Set up a call back on agent that sends a message to a pump we can use to wake up.
+ llcoro::suspend();
+ }
- // Return to stateNoChannel to trigger reconnection to a channel.
- setState(stateNoChannel);
- }
- else if (mCaptureBufferRecording)
- {
- setState(stateCaptureBufferRecStart);
- }
- else if (mCaptureBufferPlaying)
- {
- setState(stateCaptureBufferPlayStart);
- }
- break;
+ LLViewerRegion *region = gAgent.getRegion();
- //MARK: stateCaptureBufferRecStart
- case stateCaptureBufferRecStart:
- captureBufferRecordStartSendMessage();
+ while (!region->capabilitiesReceived())
+ {
+ // *TODO* Pump a message for wake up.
+ llcoro::suspend();
+ }
- // Flag that something is recorded to allow playback.
- mCaptureBufferRecorded = true;
+ std::string url = region->getCapability("ProvisionVoiceAccountRequest");
- // Start the timer, recording will be stopped when it expires.
- mCaptureTimer.start();
- mCaptureTimer.setTimerExpirySec(CAPTURE_BUFFER_MAX_TIME);
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+ int retryCount(0);
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
+ LLSD result;
- setState(stateCaptureBufferRecording);
- break;
+ do
+ {
+ result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
- //MARK: stateCaptureBufferRecording
- case stateCaptureBufferRecording:
- if (!mCaptureBufferMode || !mCaptureBufferRecording ||
- mCaptureBufferPlaying || mCaptureTimer.hasExpired())
- {
- // Stop recording
- captureBufferRecordStopSendMessage();
- mCaptureBufferRecording = false;
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
+ if (status == LLCore::HttpStatus(404))
+ {
+ if (++retryCount > 5)
+ {
+ LL_WARNS("Voice") << "Could not access voice provision cap after 5 attempts." << LL_ENDL;
+ return false;
+ }
+ LL_WARNS("Voice") << "Provision CAP 404. Retrying in 1.0" << LL_ENDL;
+ llcoro::suspendUntilTimeout(1.0);
- setState(stateCaptureBufferPaused);
- }
- break;
+ continue;
+ }
+ else if (!status)
+ {
+ LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL;
+ return false;
+ }
+ break;
+ } while (true);
- //MARK: stateCaptureBufferPlayStart
- case stateCaptureBufferPlayStart:
- captureBufferPlayStartSendMessage(mPreviewVoiceFont);
+ std::string voiceSipUriHostname;
+ std::string voiceAccountServerUri;
+ std::string voiceUserName = result["username"].asString();
+ std::string voicePassword = result["password"].asString();
- // Store the voice font being previewed, so that we know to restart if it changes.
- mPreviewVoiceFontLast = mPreviewVoiceFont;
+ //LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL;
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
+ if (result.has("voice_sip_uri_hostname"))
+ voiceSipUriHostname = result["voice_sip_uri_hostname"].asString();
- setState(stateCaptureBufferPlaying);
- break;
+ // this key is actually misnamed -- it will be an entire URI, not just a hostname.
+ if (result.has("voice_account_server_name"))
+ voiceAccountServerUri = result["voice_account_server_name"].asString();
- //MARK: stateCaptureBufferPlaying
- case stateCaptureBufferPlaying:
- if (mCaptureBufferPlaying && mPreviewVoiceFont != mPreviewVoiceFontLast)
- {
- // If the preview voice font changes, restart playing with the new font.
- setState(stateCaptureBufferPlayStart);
- }
- else if (!mCaptureBufferMode || !mCaptureBufferPlaying || mCaptureBufferRecording)
- {
- // Stop playing.
- captureBufferPlayStopSendMessage();
- mCaptureBufferPlaying = false;
+ setLoginInfo(voiceUserName, voicePassword, voiceSipUriHostname, voiceAccountServerUri);
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
+ return true;
+}
- setState(stateCaptureBufferPaused);
- }
- break;
+bool LLVivoxVoiceClient::establishVoiceConnection()
+{
+ LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump");
- //MARK: stateConnectorStart
- case stateConnectorStart:
- if(!mVoiceEnabled && mIsInitialized)
- {
- // We were never logged in. This will shut down the connector.
- setState(stateLoggedOut);
- }
- else if(!mVoiceAccountServerURI.empty())
- {
- connectorCreate();
- }
- break;
-
- //MARK: stateConnectorStarting
- case stateConnectorStarting: // waiting for connector handle
- // connectorCreateResponse() will transition from here to stateConnectorStarted.
- break;
-
- //MARK: stateConnectorStarted
- case stateConnectorStarted: // connector handle received
- if(!mVoiceEnabled && mIsInitialized)
- {
- // We were never logged in. This will shut down the connector.
- setState(stateLoggedOut);
- }
- else
- {
- // The connector is started. Send a login message.
- setState(stateNeedsLogin);
- }
- break;
-
- //MARK: stateLoginRetry
- case stateLoginRetry:
- if(mLoginRetryCount == 0)
- {
- // First retry -- display a message to the user
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
- }
-
- mLoginRetryCount++;
-
- if(mLoginRetryCount > MAX_LOGIN_RETRIES)
- {
- LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
- setState(stateLoginFailed);
- LLSD args;
- std::stringstream errs;
- errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
- args["HOSTID"] = errs.str();
- mTerminateDaemon = true;
- if (LLGridManager::getInstance()->isSystemGrid())
- {
- LLNotificationsUtil::add("NoVoiceConnect", args);
- }
- else
- {
- LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
- }
- }
- else
- {
- LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
- setState(stateLoginRetryWait);
- }
- break;
-
- //MARK: stateLoginRetryWait
- case stateLoginRetryWait:
- if(mUpdateTimer.hasExpired())
- {
- setState(stateNeedsLogin);
- }
- break;
-
- //MARK: stateNeedsLogin
- case stateNeedsLogin:
- if(!mAccountPassword.empty())
- {
- setState(stateLoggingIn);
- loginSendMessage();
- }
- break;
-
- //MARK: stateLoggingIn
- case stateLoggingIn: // waiting for account handle
- // loginResponse() will transition from here to stateLoggedIn.
- break;
-
- //MARK: stateLoggedIn
- case stateLoggedIn: // account handle received
+ if (!mVoiceEnabled && mIsInitialized)
+ return false;
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+ connectorCreate();
- if (LLVoiceClient::instance().getVoiceEffectEnabled())
- {
- // Request the set of available voice fonts.
- setState(stateVoiceFontsWait);
- refreshVoiceEffectLists(true);
- }
- else
- {
- // If voice effects are disabled, pretend we've received them and carry on.
- setState(stateVoiceFontsReceived);
- }
+ LLSD result;
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voiceConnectPump);
+ LL_WARNS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ }
+ while (!result.has("connector"));
- // Set up the mute list observer if it hasn't been set up already.
- if((!sMuteListListener_listening))
- {
- LLMuteList::getInstance()->addObserver(&mutelist_listener);
- sMuteListListener_listening = true;
- }
-
- // Set the initial state of mic mute, local speaker volume, etc.
- {
- std::ostringstream stream;
-
- buildLocalAudioUpdates(stream);
-
- if(!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- break;
-
- //MARK: stateVoiceFontsWait
- case stateVoiceFontsWait: // Await voice font list
- // accountGetSessionFontsResponse() will transition from here to
- // stateVoiceFontsReceived, to ensure we have the voice font list
- // before attempting to create a session.
- break;
-
- //MARK: stateVoiceFontsReceived
- case stateVoiceFontsReceived: // Voice font list received
- // Set up the timer to check for expiring voice fonts
- mVoiceFontExpiryTimer.start();
- mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
+ if (!result["connector"])
+ {
+ return false;
+ }
+
+ if (!mVoiceEnabled && mIsInitialized)
+ return false;
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait)
+{
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ bool retval(true);
+
+ mShutdownComplete = false;
+ connectorShutdown();
+
+ if (corowait)
+ {
+ LLSD result = llcoro::suspendUntilEventOn(voicePump);
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+
+ retval = result.has("connector");
+ }
+ else
+ { // If we are not doing a corowait then we must sleep until the connector has responded
+ // otherwise we may very well close the socket too early.
+#if LL_WINDOWS
+ int count = 0;
+ while (!mShutdownComplete && 10 > count++)
+ { // Rider: This comes out to a max wait time of 10 seconds.
+ // The situation that brings us here is a call from ::terminate()
+ // and so the viewer is attempting to go away. Don't slow it down
+ // longer than this.
+ _sleep(1000);
+ }
+#endif
+ }
+
+ closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
+ cleanUp();
+ mConnected = false;
+
+ return retval;
+}
+
+bool LLVivoxVoiceClient::loginToVivox()
+{
+ int loginRetryCount(0);
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ bool response_ok(false);
+ bool account_login(false);
+ bool send_login(true);
+
+ do
+ {
+ mIsLoggingIn = true;
+ if (send_login)
+ loginSendMessage();
+
+ send_login = false;
+
+ LLSD result = llcoro::suspendUntilEventOn(voicePump);
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+
+ if (result.has("login"))
+ {
+ std::string loginresp = result["login"];
+
+ if (loginresp == "retry")
+ {
+ if (!loginRetryCount)
+ { // on first retry notify user
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
+ }
+
+ if ((++loginRetryCount > MAX_LOGIN_RETRIES) || (!result["login_retry"]))
+ {
+ LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
+ LLSD args;
+ std::stringstream errs;
+ errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
+ args["HOSTID"] = errs.str();
+ mTerminateDaemon = true;
+ if (LLGridManager::getInstance()->isSystemGrid())
+ {
+ LLNotificationsUtil::add("NoVoiceConnect", args);
+ }
+ else
+ {
+ LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
+ }
+
+ mIsLoggingIn = false;
+ return false;
+ }
+
+ response_ok = false;
+ account_login = false;
+ send_login = true;
+
+ LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
+ llcoro::suspendUntilTimeout(LOGIN_RETRY_SECONDS);
+ }
+ else if (loginresp == "failed")
+ {
+ mIsLoggingIn = false;
+ return false;
+ }
+ else if (loginresp == "response_ok")
+ {
+ response_ok = true;
+ }
+ else if (loginresp == "account_login")
+ {
+ account_login = true;
+ }
+ }
+
+ } while (!response_ok || !account_login);
+
+ mRelogRequested = false;
+ mIsLoggedIn = true;
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+ // Set up the mute list observer if it hasn't been set up already.
+ if ((!sMuteListListener_listening))
+ {
+ LLMuteList::getInstance()->addObserver(&mutelist_listener);
+ sMuteListListener_listening = true;
+ }
+
+ // Set the initial state of mic mute, local speaker volume, etc.
+ sendLocalAudioUpdates();
+
+ return true;
+}
+
+void LLVivoxVoiceClient::logoutOfVivox(bool wait)
+{
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ // Ensure that we'll re-request provisioning before logging in again
+ mAccountPassword.clear();
+ mVoiceAccountServerURI.clear();
+
+ logoutSendMessage();
+
+ if (wait)
+ {
+ LLSD result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+
+ if (result.has("logout"))
+ {
+ }
+ }
+
+}
+
+
+bool LLVivoxVoiceClient::retrieveVoiceFonts()
+{
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ // Request the set of available voice fonts.
+ refreshVoiceEffectLists(true);
+
+ mIsWaitingForFonts = true;
+ LLSD result;
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("voice_fonts"))
+ break;
+ } while (true);
+ mIsWaitingForFonts = false;
+
+ mVoiceFontExpiryTimer.start();
+ mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
+
+ return result["voice_fonts"].asBoolean();
+}
+
+bool LLVivoxVoiceClient::requestParcelVoiceInfo()
+{
+ //_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL;
+
+ LLViewerRegion * region = gAgent.getRegion();
+ if (region == NULL || !region->capabilitiesReceived())
+ {
+ LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL;
+ return false;
+ }
+
+ // grab the cap.
+ std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
+ if (url.empty())
+ {
+ // Region dosn't have the cap. Stop probing.
+ LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL;
+ return false;
+ }
+
+ // update the parcel
+ checkParcelChanged(true);
+
+ LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
+
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD());
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
+ {
+ // if a terminate request has been received,
+ // bail and go to the stateSessionTerminated
+ // state. If the cap request is still pending,
+ // the responder will check to see if we've moved
+ // to a new session and won't change any state.
+ terminateAudioSession(true);
+ return false;
+ }
+
+ if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)))
+ {
+ if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
+ {
+ LL_WARNS("Voice") << "Session terminated." << LL_ENDL;
+ }
+
+ LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL;
+ sessionTerminate();
+ return false;
+ }
+
+ std::string uri;
+ std::string credentials;
+
+ if (result.has("voice_credentials"))
+ {
+ LLSD voice_credentials = result["voice_credentials"];
+ if (voice_credentials.has("channel_uri"))
+ {
+ uri = voice_credentials["channel_uri"].asString();
+ }
+ if (voice_credentials.has("channel_credentials"))
+ {
+ credentials =
+ voice_credentials["channel_credentials"].asString();
+ }
+ }
+
+ if (!uri.empty())
+ LL_INFOS("Voice") << "Voice URI is " << uri << LL_ENDL;
+
+ // set the spatial channel. If no voice credentials or uri are
+ // available, then we simply drop out of voice spatially.
+ return !setSpatialChannel(uri, credentials);
+}
+
+bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
+{
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ mIsJoiningSession = true;
+
+ sessionStatePtr_t oldSession = mAudioSession;
+
+ LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL;
+
+ mAudioSession = nextSession;
+ mAudioSessionChanged = true;
+ if (!mAudioSession->mReconnect)
+ {
+ mNextAudioSession.reset();
+ }
+
+ // The old session may now need to be deleted.
+ reapSession(oldSession);
+
+ if (!mAudioSession->mHandle.empty())
+ {
+ // Connect to a session by session handle
+
+ sessionMediaConnectSendMessage(mAudioSession);
+ }
+ else
+ {
+ // Connect to a session by URI
+ sessionCreateSendMessage(mAudioSession, true, false);
+ }
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+
+ llcoro::suspend();
+
+ LLSD result;
+
+ if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
+ {
+ // Notify observers to let them know there is problem with voice
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
+ LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL;
+ }
+
+ // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
+ // example for p2p many times while waiting for response, so it can't be used to detect errors
+ if (mAudioSession && mAudioSession->mIsSpatial)
+ {
+ mSpatialJoiningNum++;
+ }
+
+ if (!mVoiceEnabled && mIsInitialized)
+ {
+ mIsJoiningSession = false;
+ // User bailed out during connect -- jump straight to teardown.
+ terminateAudioSession(true);
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
+ return false;
+ }
+ else if (mSessionTerminateRequested)
+ {
+ if (mAudioSession && !mAudioSession->mHandle.empty())
+ {
+ // Only allow direct exits from this state in p2p calls (for cancelling an invite).
+ // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+ if (mAudioSession->mIsP2P)
+ {
+ terminateAudioSession(true);
+ mIsJoiningSession = false;
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+ return false;
+ }
+ }
+ }
+
+ bool added(true);
+ bool joined(false);
+
+ // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
+ // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
+ // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
+ // This is a cheap way to make sure both have happened before proceeding.
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_WARNS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("session"))
+ {
+ if (result.has("handle"))
+ {
+ if (result["handle"] != mAudioSession->mHandle)
+ {
+ LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL;
+ continue;
+ }
+ }
+
+ std::string message = result["session"].asString();
+ if ((message == "added") || (message == "created"))
+ added = true;
+ else if (message == "joined")
+ joined = true;
+ else if ((message == "failed") || (message == "removed"))
+ { // we will get a removed message if a voice call is declined.
+
+ if (message == "failed")
+ {
+ int reason = result["reason"].asInteger();
+ LL_WARNS("Voice") << "Add and join failed for reason " << reason << LL_ENDL;
+
+ if ((reason == ERROR_VIVOX_NOT_LOGGED_IN) ||
+ (reason == ERROR_VIVOX_OBJECT_NOT_FOUND))
+ {
+ LL_INFOS("Voice") << "Requesting reprovision and login." << LL_ENDL;
+ requestRelog();
+ }
+
+ }
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+ mIsJoiningSession = false;
+ return false;
+ }
+ }
+ } while (!added || !joined);
+
+ mIsJoiningSession = false;
+
+ if (mSpatialJoiningNum > 100)
+ {
+ LL_WARNS("Voice") << "There seems to be problem with connecting to a voice channel. Frames to join were " << mSpatialJoiningNum << LL_ENDL;
+ }
+
+ mSpatialJoiningNum = 0;
+
+ // Events that need to happen when a session is joined could go here.
+ // send an initial positional information immediately upon joining.
+ //
+ // do an initial update for position and the camera position, then send a
+ // positional update.
+ updatePosition();
+ enforceTether();
+
+ // Dirty state that may need to be sync'ed with the daemon.
+ mMuteMicDirty = true;
+ mSpeakerVolumeDirty = true;
+ mSpatialCoordsDirty = true;
+
+ sendPositionalUpdate();
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::terminateAudioSession(bool wait)
+{
+
+ if (mAudioSession)
+ {
+ LL_INFOS("Voice") << "Terminating current voice session " << mAudioSession->mHandle << LL_ENDL;
+
+ if (!mAudioSession->mHandle.empty())
+ {
+
+#if RECORD_EVERYTHING
+ // HACK: for testing only
+ // Save looped recording
+ std::string savepath("/tmp/vivoxrecording");
+ {
+ time_t now = time(NULL);
+ const size_t BUF_SIZE = 64;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+
+ strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
+ savepath += time_str;
+ }
+ recordingLoopSave(savepath);
+#endif
+
+ sessionMediaDisconnectSendMessage(mAudioSession);
+
+ if (wait)
+ {
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLSD result;
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("session"))
+ {
+ if (result.has("handle"))
+ {
+ if (result["handle"] != mAudioSession->mHandle)
+ {
+ LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL;
+ continue;
+ }
+ }
+
+ std::string message = result["session"].asString();
+ if (message == "removed")
+ break;
+ }
+ } while (true);
+
+ }
+ }
+ else
+ {
+ LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
+ }
+
+ sessionStatePtr_t oldSession = mAudioSession;
+
+ mAudioSession.reset();
+ // We just notified status observers about this change. Don't do it again.
+ mAudioSessionChanged = false;
+
+ // The old session may now need to be deleted.
+ reapSession(oldSession);
+
+ }
+ else
+ {
+ LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
+ }
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+
+ // Always reset the terminate request flag when we get here.
+ // Some slower PCs have a race condition where they can switch to an incoming P2P call faster than the state machine leaves
+ // the region chat.
+ mSessionTerminateRequested = false;
+
+ if ((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting())
+ {
+ // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
+ return true;
+ }
+
+ return false;
+}
+
+bool LLVivoxVoiceClient::waitForChannel()
+{
+ LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL;
+
+ do
+ {
+ if (!loginToVivox())
+ {
+ return false;
+ }
+
+ if (LLVoiceClient::instance().getVoiceEffectEnabled())
+ {
+ retrieveVoiceFonts();
+
+ // Request the set of available voice fonts.
+ refreshVoiceEffectLists(true);
+ }
#if USE_SESSION_GROUPS
- // create the main session group
- setState(stateCreatingSessionGroup);
- sessionGroupCreateSendMessage();
-#else
- setState(stateNoChannel);
+ // Rider: This code is completely unchanged from the original state machine
+ // It does not seem to be in active use... but I'd rather not rip it out.
+ // create the main session group
+ setState(stateCreatingSessionGroup);
+ sessionGroupCreateSendMessage();
#endif
- break;
-
- //MARK: stateCreatingSessionGroup
- case stateCreatingSessionGroup:
- if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
- {
- // *TODO: Question: is this the right way out of this state
- setState(stateSessionTerminated);
- }
- else if(!mMainSessionGroupHandle.empty())
- {
- // Start looped recording (needed for "panic button" anti-griefing tool)
- recordingLoopStart();
- setState(stateNoChannel);
- }
- break;
-
- //MARK: stateRetrievingParcelVoiceInfo
- case stateRetrievingParcelVoiceInfo:
- // wait until parcel voice info is received.
- if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
- {
- // if a terminate request has been received,
- // bail and go to the stateSessionTerminated
- // state. If the cap request is still pending,
- // the responder will check to see if we've moved
- // to a new session and won't change any state.
- setState(stateSessionTerminated);
- }
- break;
-
-
- //MARK: stateNoChannel
- case stateNoChannel:
- LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL;
- mSpatialJoiningNum = 0;
-
- if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
- {
- // TODO: Question: Is this the right way out of this state?
- setState(stateSessionTerminated);
- }
- else if(mTuningMode)
- {
- mTuningExitState = stateNoChannel;
- setState(stateMicTuningStart);
- }
- else if(mCaptureBufferMode)
- {
- setState(stateCaptureBufferPaused);
- }
- else if(checkParcelChanged() || (mNextAudioSession == NULL))
- {
- // the parcel is changed, or we have no pending audio sessions,
- // so try to request the parcel voice info
- // if we have the cap, we move to the appropriate state
- if(requestParcelVoiceInfo())
- {
- setState(stateRetrievingParcelVoiceInfo);
- }
- }
- else if(sessionNeedsRelog(mNextAudioSession))
- {
- requestRelog();
- setState(stateSessionTerminated);
- }
- else if(mNextAudioSession)
- {
- sessionState *oldSession = mAudioSession;
+ do
+ {
+ mIsProcessingChannels = true;
+ llcoro::suspend();
- mAudioSession = mNextAudioSession;
- mAudioSessionChanged = true;
- if(!mAudioSession->mReconnect)
- {
- mNextAudioSession = NULL;
- }
-
- // The old session may now need to be deleted.
- reapSession(oldSession);
-
- if(!mAudioSession->mHandle.empty())
- {
- // Connect to a session by session handle
+ if (mTuningMode)
+ {
+ performMicTuning();
+ }
+ else if (mCaptureBufferMode)
+ {
+ recordingAndPlaybackMode();
+ }
+ else if (checkParcelChanged() || (mNextAudioSession == NULL))
+ {
+ // the parcel is changed, or we have no pending audio sessions,
+ // so try to request the parcel voice info
+ // if we have the cap, we move to the appropriate state
+ requestParcelVoiceInfo();
+ }
+ else if (sessionNeedsRelog(mNextAudioSession))
+ {
+ requestRelog();
+ break;
+ }
+ else if (mNextAudioSession)
+ {
+ sessionStatePtr_t joinSession = mNextAudioSession;
+ mNextAudioSession.reset();
+ if (!runSession(joinSession))
+ break;
+ }
- sessionMediaConnectSendMessage(mAudioSession);
- }
- else
- {
- // Connect to a session by URI
- sessionCreateSendMessage(mAudioSession, true, false);
- }
+ if (!mNextAudioSession)
+ llcoro::suspendUntilTimeout(1.0);
+ } while (mVoiceEnabled && !mRelogRequested);
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
- setState(stateJoiningSession);
- }
- break;
-
- //MARK: stateJoiningSession
- case stateJoiningSession: // waiting for session handle
-
- // If this is true we have problem with connection to voice server (EXT-4313).
- // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM.
- if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
- {
- // Notify observers to let them know there is problem with voice
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
- LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL;
- }
-
- // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
- // example for p2p many times while waiting for response, so it can't be used to detect errors
- if(mAudioSession && mAudioSession->mIsSpatial)
- {
-
- mSpatialJoiningNum++;
- }
-
- // joinedAudioSession() will transition from here to stateSessionJoined.
- if(!mVoiceEnabled && mIsInitialized)
- {
- // User bailed out during connect -- jump straight to teardown.
- setState(stateSessionTerminated);
- }
- else if(mSessionTerminateRequested)
- {
- if(mAudioSession && !mAudioSession->mHandle.empty())
- {
- // Only allow direct exits from this state in p2p calls (for cancelling an invite).
- // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
- if(mAudioSession->mIsP2P)
- {
- sessionMediaDisconnectSendMessage(mAudioSession);
- setState(stateSessionTerminated);
- }
- }
- }
- break;
-
- //MARK: stateSessionJoined
- case stateSessionJoined: // session handle received
+ mIsProcessingChannels = false;
+ logoutOfVivox(true);
- mSpatialJoiningNum = 0;
- // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
- // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
- // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
- // This is a cheap way to make sure both have happened before proceeding.
- if(mAudioSession && mAudioSession->mVoiceEnabled)
- {
- // Dirty state that may need to be sync'ed with the daemon.
- mMuteMicDirty = true;
- mSpeakerVolumeDirty = true;
- mSpatialCoordsDirty = true;
-
- setState(stateRunning);
-
- // Start the throttle timer
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+ if (mRelogRequested)
+ {
+ if (!provisionVoiceAccount())
+ {
+ giveUp();
+ return false;
+ }
+ }
+ } while (mVoiceEnabled && mRelogRequested);
- // Events that need to happen when a session is joined could go here.
- // Maybe send initial spatial data?
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+ return true;
+}
- }
- else if(!mVoiceEnabled && mIsInitialized)
- {
- // User bailed out during connect -- jump straight to teardown.
- setState(stateSessionTerminated);
- }
- else if(mSessionTerminateRequested)
- {
- // Only allow direct exits from this state in p2p calls (for cancelling an invite).
- // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
- if(mAudioSession && mAudioSession->mIsP2P)
- {
- sessionMediaDisconnectSendMessage(mAudioSession);
- setState(stateSessionTerminated);
- }
- }
- break;
-
- //MARK: stateRunning
- case stateRunning: // steady state
- // Disabling voice or disconnect requested.
- if((!mVoiceEnabled && mIsInitialized) || mSessionTerminateRequested)
- {
- leaveAudioSession();
- }
- else
- {
-
- if(!inSpatialChannel())
- {
- // When in a non-spatial channel, never send positional updates.
- mSpatialCoordsDirty = false;
- }
- else
- {
- if(checkParcelChanged())
- {
- // if the parcel has changed, attempted to request the
- // cap for the parcel voice info. If we can't request it
- // then we don't have the cap URL so we do nothing and will
- // recheck next time around
- if(requestParcelVoiceInfo())
- {
- // we did get the cap, and we made the request,
- // so go wait for the response.
- setState(stateRetrievingParcelVoiceInfo);
- }
- }
- // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
- enforceTether();
-
- }
-
- // Do notifications for expiring Voice Fonts.
- if (mVoiceFontExpiryTimer.hasExpired())
- {
- expireVoiceFonts();
- mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
- }
+bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
+{
+ LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL;
- // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often
- // -- the user can only click so fast) or every 10hz, whichever is sooner.
- // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged.
- if((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty || mUpdateTimer.hasExpired())
- {
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- sendPositionalUpdate();
- }
- mIsInitialized = true;
- }
- break;
-
- //MARK: stateLeavingSession
- case stateLeavingSession: // waiting for terminate session response
- // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
- break;
+ if (!addAndJoinSession(session))
+ {
+ notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
- //MARK: stateSessionTerminated
- case stateSessionTerminated:
-
- // Must do this first, since it uses mAudioSession.
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
-
- if(mAudioSession)
- {
- sessionState *oldSession = mAudioSession;
+ if (mSessionTerminateRequested)
+ terminateAudioSession(true);
- mAudioSession = NULL;
- // We just notified status observers about this change. Don't do it again.
- mAudioSessionChanged = false;
+ // if a relog has been requested then addAndJoineSession
+ // failed in a spectacular way and we need to back out.
+ // If this is not the case then we were simply trying to
+ // make a call and the other party rejected it.
+ return !mRelogRequested;
+ }
- // The old session may now need to be deleted.
- reapSession(oldSession);
- }
- else
- {
- LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
- }
-
- // Always reset the terminate request flag when we get here.
- mSessionTerminateRequested = false;
+ notifyParticipantObservers();
+ notifyVoiceFontObservers();
- if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting())
- {
- // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
- setState(stateNoChannel);
- }
- else
- {
- // Shutting down voice, continue with disconnecting.
- logout();
-
- // The state machine will take it from here
- mRelogRequested = false;
- }
-
- break;
-
- //MARK: stateLoggingOut
- case stateLoggingOut: // waiting for logout response
- // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut.
- break;
-
- //MARK: stateLoggedOut
- case stateLoggedOut: // logout response received
-
- // Once we're logged out, these things are invalid.
- mAccountHandle.clear();
- cleanUp();
+ LLSD timeoutEvent = LLSD::emptyMap();
+ timeoutEvent["timeout"] = LLSD::Boolean(true);
- if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested)
- {
- // User was logged out, but wants to be logged in. Send a new login request.
- setState(stateNeedsLogin);
- }
- else
- {
- // shut down the connector
- connectorShutdown();
- }
- break;
-
- //MARK: stateConnectorStopping
- case stateConnectorStopping: // waiting for connector stop
- // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
- mShutdownComplete = true;
- break;
-
- //MARK: stateConnectorStopped
- case stateConnectorStopped: // connector stop received
- setState(stateDisableCleanup);
- break;
-
- //MARK: stateConnectorFailed
- case stateConnectorFailed:
- setState(stateConnectorFailedWaiting);
- break;
- //MARK: stateConnectorFailedWaiting
- case stateConnectorFailedWaiting:
- if(!mVoiceEnabled)
- {
- setState(stateDisableCleanup);
- }
- break;
-
- //MARK: stateLoginFailed
- case stateLoginFailed:
- setState(stateLoginFailedWaiting);
- break;
- //MARK: stateLoginFailedWaiting
- case stateLoginFailedWaiting:
- if(!mVoiceEnabled)
- {
- setState(stateDisableCleanup);
- }
- break;
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLEventTimeout timeout(voicePump);
+ mIsInChannel = true;
+ mMuteMicDirty = true;
- //MARK: stateJoinSessionFailed
- case stateJoinSessionFailed:
- // Transition to error state. Send out any notifications here.
- if(mAudioSession)
- {
- LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
- }
- else
- {
- LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
- }
-
- notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
- setState(stateJoinSessionFailedWaiting);
- break;
-
- //MARK: stateJoinSessionFailedWaiting
- case stateJoinSessionFailedWaiting:
- // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
- // Region crossings may leave this state and try the join again.
- if(mSessionTerminateRequested)
- {
- setState(stateSessionTerminated);
- }
- break;
-
- //MARK: stateJail
- case stateJail:
- // We have given up. Do nothing.
- break;
+ while (mVoiceEnabled && !mSessionTerminateRequested && !mTuningMode)
+ {
+ if (mAudioSession && mAudioSession->mParticipantsChanged)
+ {
+ mAudioSession->mParticipantsChanged = false;
+ notifyParticipantObservers();
+ }
+
+ if (!inSpatialChannel())
+ {
+ // When in a non-spatial channel, never send positional updates.
+ mSpatialCoordsDirty = false;
+ }
+ else
+ {
+ updatePosition();
- }
-
- if (mAudioSessionChanged)
- {
- mAudioSessionChanged = false;
- notifyParticipantObservers();
- notifyVoiceFontObservers();
- }
- else if (mAudioSession && mAudioSession->mParticipantsChanged)
- {
- mAudioSession->mParticipantsChanged = false;
- notifyParticipantObservers();
- }
+ if (checkParcelChanged())
+ {
+ // *RIDER: I think I can just return here if the parcel has changed
+ // and grab the new voice channel from the outside loop.
+ //
+ // if the parcel has changed, attempted to request the
+ // cap for the parcel voice info. If we can't request it
+ // then we don't have the cap URL so we do nothing and will
+ // recheck next time around
+ if (requestParcelVoiceInfo())
+ { // The parcel voice URI has changed.. break out and reconnect.
+ break;
+ }
+ }
+ // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
+ enforceTether();
+ sendPositionalUpdate();
+ }
+
+ // Do notifications for expiring Voice Fonts.
+ if (mVoiceFontExpiryTimer.hasExpired())
+ {
+ expireVoiceFonts();
+ mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
+ }
+
+ // send any requests to adjust mic and speaker settings if they have changed
+ sendLocalAudioUpdates();
+
+ mIsInitialized = true;
+ timeout.eventAfter(UPDATE_THROTTLE_SECONDS, timeoutEvent);
+ LLSD result = llcoro::suspendUntilEventOn(timeout);
+ if (!result.has("timeout")) // logging the timeout event spams the log
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("session"))
+ {
+ if (result.has("handle"))
+ {
+ if (result["handle"] != mAudioSession->mHandle)
+ {
+ LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL;
+ continue;
+ }
+ }
+
+ std::string message = result["session"];
+
+ if (message == "removed")
+ {
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+ break;
+ }
+ }
+
+ }
+
+ mIsInChannel = false;
+ terminateAudioSession(true);
+
+ return true;
}
+void LLVivoxVoiceClient::recordingAndPlaybackMode()
+{
+ LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL;
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ while (true)
+ {
+ LLSD command;
+ do
+ {
+ command = llcoro::suspendUntilEventOn(voicePump);
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(command) << LL_ENDL;
+ } while (!command.has("recplay"));
+
+ if (command["recplay"].asString() == "quit")
+ {
+ mCaptureBufferMode = false;
+ break;
+ }
+ else if (command["recplay"].asString() == "record")
+ {
+ voiceRecordBuffer();
+ }
+ else if (command["recplay"].asString() == "playback")
+ {
+ voicePlaybackBuffer();
+ }
+ }
+
+ LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL;
+ mCaptureBufferRecording = false;
+ mCaptureBufferRecorded = false;
+ mCaptureBufferPlaying = false;
+
+ return;
+}
+
+int LLVivoxVoiceClient::voiceRecordBuffer()
+{
+ LLSD timeoutResult;
+ timeoutResult["recplay"] = LLSD::String("stop");
+
+ LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL;
+
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLEventTimeout timeout(voicePump);
+ timeout.eventAfter(CAPTURE_BUFFER_MAX_TIME, timeoutResult);
+ LLSD result;
+
+ captureBufferRecordStartSendMessage();
+
+ notifyVoiceFontObservers();
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ } while (!result.has("recplay"));
+
+ mCaptureBufferRecorded = true;
+
+ captureBufferRecordStopSendMessage();
+ mCaptureBufferRecording = false;
+
+ // Update UI, should really use a separate callback.
+ notifyVoiceFontObservers();
+
+ return true;
+ /*TODO expand return to move directly into play*/
+}
+
+int LLVivoxVoiceClient::voicePlaybackBuffer()
+{
+ LLSD timeoutResult;
+ timeoutResult["recplay"] = LLSD::String("stop");
+
+ LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL;
+
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLEventTimeout timeout(voicePump);
+ timeout.eventAfter(CAPTURE_BUFFER_MAX_TIME, timeoutResult);
+ LLSD result;
+
+ do
+ {
+ captureBufferPlayStartSendMessage(mPreviewVoiceFont);
+
+ // Store the voice font being previewed, so that we know to restart if it changes.
+ mPreviewVoiceFontLast = mPreviewVoiceFont;
+
+ do
+ {
+ // Update UI, should really use a separate callback.
+ notifyVoiceFontObservers();
+
+ result = llcoro::suspendUntilEventOn(voicePump);
+ LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ } while (!result.has("recplay"));
+
+ if (result["recplay"] == "playback")
+ continue; // restart playback... May be a font change.
+
+ break;
+ } while (true);
+
+ // Stop playing.
+ captureBufferPlayStopSendMessage();
+ mCaptureBufferPlaying = false;
+
+ // Update UI, should really use a separate callback.
+ notifyVoiceFontObservers();
+
+ return true;
+}
+
+
+bool LLVivoxVoiceClient::performMicTuning()
+{
+ LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL;
+
+ mIsInTuningMode = true;
+ llcoro::suspend();
+
+ while (mTuningMode)
+ {
+
+ if (mCaptureDeviceDirty || mRenderDeviceDirty)
+ {
+ // These can't be changed while in tuning mode. Set them before starting.
+ std::ostringstream stream;
+
+ buildSetCaptureDevice(stream);
+ buildSetRenderDevice(stream);
+
+ if (!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ }
+
+ // loop mic back to render device.
+ //setMuteMic(0); // make sure the mic is not muted
+ std::ostringstream stream;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>false</Value>"
+ << "</Request>\n\n\n";
+
+ // Dirty the mute mic state so that it will get reset when we finishing previewing
+ mMuteMicDirty = true;
+ mTuningSpeakerVolumeDirty = true;
+
+ writeString(stream.str());
+ tuningCaptureStartSendMessage(1); // 1-loop, zero, don't loop
+
+ //---------------------------------------------------------------------
+ llcoro::suspend();
+
+ while (mTuningMode && !mCaptureDeviceDirty && !mRenderDeviceDirty)
+ {
+ // process mic/speaker volume changes
+ if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
+ {
+ std::ostringstream stream;
+
+ if (mTuningMicVolumeDirty)
+ {
+ LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
+ << "<Level>" << mTuningMicVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ if (mTuningSpeakerVolumeDirty)
+ {
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
+ << "<Level>" << mTuningSpeakerVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ mTuningMicVolumeDirty = false;
+ mTuningSpeakerVolumeDirty = false;
+
+ if (!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+ }
+ llcoro::suspend();
+ }
+
+ //---------------------------------------------------------------------
+
+ // transition out of mic tuning
+ tuningCaptureStopSendMessage();
+ if (mCaptureDeviceDirty || mRenderDeviceDirty)
+ {
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ }
+ }
+
+ mIsInTuningMode = false;
+
+ //---------------------------------------------------------------------
+ return true;
+}
+
+//=========================================================================
+
void LLVivoxVoiceClient::closeSocket(void)
{
mSocket.reset();
@@ -1680,7 +1767,8 @@ void LLVivoxVoiceClient::loginSendMessage()
<< "<AccountName>" << mAccountName << "</AccountName>"
<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
- << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>"
+ << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>"
+ << "<EnablePresencePersistence>0</EnablePresencePersistence>"
<< "<BuddyManagementMode>Application</BuddyManagementMode>"
<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
<< (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"")
@@ -1695,7 +1783,6 @@ void LLVivoxVoiceClient::logout()
mAccountPassword.clear();
mVoiceAccountServerURI.clear();
- setState(stateLoggingOut);
logoutSendMessage();
}
@@ -1735,7 +1822,7 @@ void LLVivoxVoiceClient::sessionGroupCreateSendMessage()
}
}
-void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
+void LLVivoxVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText)
{
LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
@@ -1775,7 +1862,7 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st
writeString(stream.str());
}
-void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
+void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText)
{
LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
@@ -1816,7 +1903,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session
writeString(stream.str());
}
-void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
+void LLVivoxVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t &session)
{
LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL;
@@ -1838,7 +1925,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
writeString(stream.str());
}
-void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session)
+void LLVivoxVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session)
{
LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
@@ -1871,64 +1958,45 @@ void LLVivoxVoiceClient::leaveAudioSession()
{
LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
- switch(getState())
+ if(!mAudioSession->mHandle.empty())
{
- case stateNoChannel:
- // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
- // Skip the join failed transition state so we don't send out error notifications.
- setState(stateJoinSessionFailedWaiting);
- break;
- case stateJoiningSession:
- case stateSessionJoined:
- case stateRunning:
- if(!mAudioSession->mHandle.empty())
- {
#if RECORD_EVERYTHING
- // HACK: for testing only
- // Save looped recording
- std::string savepath("/tmp/vivoxrecording");
- {
- time_t now = time(NULL);
- const size_t BUF_SIZE = 64;
- char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+ // HACK: for testing only
+ // Save looped recording
+ std::string savepath("/tmp/vivoxrecording");
+ {
+ time_t now = time(NULL);
+ const size_t BUF_SIZE = 64;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
- strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
- savepath += time_str;
- }
- recordingLoopSave(savepath);
+ strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
+ savepath += time_str;
+ }
+ recordingLoopSave(savepath);
#endif
- sessionMediaDisconnectSendMessage(mAudioSession);
- setState(stateLeavingSession);
- }
- else
- {
- LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
- setState(stateSessionTerminated);
- }
- break;
- case stateJoinSessionFailed:
- case stateJoinSessionFailedWaiting:
- setState(stateSessionTerminated);
- break;
-
- default:
- LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
- break;
+ sessionMediaDisconnectSendMessage(mAudioSession);
+ }
+ else
+ {
+ LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
}
}
else
{
LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
- setState(stateSessionTerminated);
}
+ sessionTerminate();
}
-void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session)
+void LLVivoxVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &session)
{
std::ostringstream stream;
-
+
+ sessionGroupTerminateSendMessage(session);
+ return;
+ /*
LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
@@ -1936,9 +2004,10 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session)
<< "</Request>\n\n\n";
writeString(stream.str());
+ */
}
-void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
+void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session)
{
std::ostringstream stream;
@@ -1951,10 +2020,12 @@ void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
writeString(stream.str());
}
-void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
+void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session)
{
std::ostringstream stream;
-
+ sessionGroupTerminateSendMessage(session);
+ return;
+ /*
LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
@@ -1964,22 +2035,10 @@ void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session
<< "</Request>\n\n\n";
writeString(stream.str());
+ */
}
-void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
-{
- std::ostringstream stream;
-
- LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
- << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
- << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
- << "</Request>\n\n\n";
-
- writeString(stream.str());
-}
void LLVivoxVoiceClient::getCaptureDevicesSendMessage()
{
@@ -2038,6 +2097,10 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
}
}
}
+void LLVivoxVoiceClient::setDevicesListUpdated(bool state)
+{
+ mDevicesListUpdated = state;
+}
void LLVivoxVoiceClient::clearRenderDevices()
{
@@ -2079,9 +2142,14 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name)
void LLVivoxVoiceClient::tuningStart()
{
- mTuningMode = true;
- LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL;
- if(getState() >= stateNoChannel)
+ LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL;
+ mTuningMode = true;
+ if (!mIsCoroutineActive)
+ {
+ LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
+ boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance()));
+ }
+ else if (mIsInChannel)
{
LL_DEBUGS("Voice") << "no channel" << LL_ENDL;
sessionTerminate();
@@ -2095,16 +2163,7 @@ void LLVivoxVoiceClient::tuningStop()
bool LLVivoxVoiceClient::inTuningMode()
{
- bool result = false;
- switch(getState())
- {
- case stateMicTuningRunning:
- result = true;
- break;
- default:
- break;
- }
- return result;
+ return mIsInTuningMode;
}
void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
@@ -2131,14 +2190,15 @@ void LLVivoxVoiceClient::tuningRenderStopSendMessage()
writeString(stream.str());
}
-void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration)
+void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int loop)
{
LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
std::ostringstream stream;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
- << "<Duration>" << duration << "</Duration>"
+ << "<Duration>-1</Duration>"
+ << "<LoopToRenderDevice>" << loop << "</LoopToRenderDevice>"
<< "</Request>\n\n\n";
writeString(stream.str());
@@ -2197,6 +2257,16 @@ bool LLVivoxVoiceClient::deviceSettingsAvailable()
return result;
}
+bool LLVivoxVoiceClient::deviceSettingsUpdated()
+{
+ if (mDevicesListUpdated)
+ {
+ // a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel.
+ mDevicesListUpdated = !mDevicesListUpdated; // toggle the setting
+ return true;
+ }
+ return false;
+}
void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList)
{
@@ -2215,7 +2285,7 @@ void LLVivoxVoiceClient::daemonDied()
LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL;
// Try to relaunch the daemon
- setState(stateDisableCleanup);
+ /*TODO:*/
}
void LLVivoxVoiceClient::giveUp()
@@ -2223,8 +2293,6 @@ void LLVivoxVoiceClient::giveUp()
// All has failed. Clean up and stop trying.
closeSocket();
cleanUp();
-
- setState(stateJail);
}
static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
@@ -2373,10 +2441,12 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
stream << "<SpeakerPosition>";
+ LLMatrix3 avatarRot = mAvatarRot.getMatrix3();
+
// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
- l = mAvatarRot.getLeftRow();
- u = mAvatarRot.getUpRow();
- a = mAvatarRot.getFwdRow();
+ l = avatarRot.getLeftRow();
+ u = avatarRot.getUpRow();
+ a = avatarRot.getFwdRow();
pos = mAvatarPosition;
vel = mAvatarVelocity;
@@ -2440,7 +2510,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
case earLocAvatar:
earPosition = mAvatarPosition;
earVelocity = mAvatarVelocity;
- earRot = mAvatarRot;
+ earRot = avatarRot;
break;
case earLocMixed:
@@ -2499,6 +2569,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
stream << "</ListenerPosition>";
+ stream << "<ReqDispositionType>1</ReqDispositionType>"; //do not generate responses for update requests
stream << "</Request>\n\n\n";
}
@@ -2511,7 +2582,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
{
- participantState *p = iter->second;
+ participantStatePtr_t p(iter->second);
if(p->mVolumeDirty)
{
@@ -2567,7 +2638,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
}
}
- buildLocalAudioUpdates(stream);
+ //sendLocalAudioUpdates(); obsolete, used to send volume setting on position updates
if(!stream.str().empty())
{
@@ -2607,68 +2678,73 @@ void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
}
}
-void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
+void LLVivoxVoiceClient::sendLocalAudioUpdates()
{
- buildSetCaptureDevice(stream);
+ // Check all of the dirty states and then send messages to those needing to be changed.
+ // Tuningmode hands its own mute settings.
- buildSetRenderDevice(stream);
+ std::ostringstream stream;
- if(mMuteMicDirty)
+ if (mMuteMicDirty && !mTuningMode)
{
mMuteMicDirty = false;
// Send a local mute command.
-
- LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic?"true":"false") << LL_ENDL;
+
+ LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL;
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
- << "<Value>" << (mMuteMic?"true":"false") << "</Value>"
+ << "<Value>" << (mMuteMic ? "true" : "false") << "</Value>"
<< "</Request>\n\n\n";
-
+
}
- if(mSpeakerMuteDirty)
+ if (mSpeakerMuteDirty && !mTuningMode)
{
- const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false");
+ const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false");
mSpeakerMuteDirty = false;
- LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
-
+ LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
+
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
<< "<Value>" << muteval << "</Value>"
- << "</Request>\n\n\n";
-
+ << "</Request>\n\n\n";
+
}
-
- if(mSpeakerVolumeDirty)
+
+ if (mSpeakerVolumeDirty)
{
mSpeakerVolumeDirty = false;
- LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
+ LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
<< "<Value>" << mSpeakerVolume << "</Value>"
<< "</Request>\n\n\n";
-
+
}
-
- if(mMicVolumeDirty)
+
+ if (mMicVolumeDirty)
{
mMicVolumeDirty = false;
- LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
+ LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
<< "<Value>" << mMicVolume << "</Value>"
- << "</Request>\n\n\n";
+ << "</Request>\n\n\n";
}
-
+
+ if (!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
}
/////////////////////////////
@@ -2676,10 +2752,11 @@ void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
{
+ LLSD result = LLSD::emptyMap();
+
if(statusCode != 0)
{
LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
- setState(stateConnectorFailed);
LLSD args;
std::stringstream errs;
errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
@@ -2693,6 +2770,8 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
{
LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
}
+
+ result["connector"] = LLSD::Boolean(false);
}
else
{
@@ -2701,16 +2780,18 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
mVoiceVersion.serverVersion = versionID;
mConnectorHandle = connectorHandle;
mTerminateDaemon = false;
- if(getState() == stateConnectorStarting)
- {
- setState(stateConnectorStarted);
- }
+
+ result["connector"] = LLSD::Boolean(true);
}
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
{
- LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
+ LLSD result = LLSD::emptyMap();
+
+ LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
// Status code of 20200 means "bad password". We may want to special-case that at some point.
@@ -2718,29 +2799,28 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString
{
// Login failure which is probably caused by the delay after a user's password being updated.
LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
- setState(stateLoginRetry);
+ result["login"] = LLSD::String("retry");
}
else if(statusCode != 0)
{
LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
- setState(stateLoginFailed);
+ result["login"] = LLSD::String("failed");
}
else
{
// Login succeeded, move forward.
mAccountHandle = accountHandle;
mNumberOfAliases = numberOfAliases;
- // This needs to wait until the AccountLoginStateChangeEvent is received.
-// if(getState() == stateLoggingIn)
-// {
-// setState(stateLoggedIn);
-// }
+ result["login"] = LLSD::String("response_ok");
}
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
+
}
void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
{
- sessionState *session = findSessionBeingCreatedByURI(requestId);
+ sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId));
if(session)
{
@@ -2756,8 +2836,14 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
session->mErrorStatusString = statusString;
if(session == mAudioSession)
{
- setState(stateJoinSessionFailed);
- }
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("failed");
+ vivoxevent["reason"] = LLSD::Integer(statusCode);
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+ }
else
{
reapSession(session);
@@ -2771,12 +2857,18 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
{
setSessionHandle(session, sessionHandle);
}
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("created");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
}
}
void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
{
- sessionState *session = findSessionBeingCreatedByURI(requestId);
+ sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId));
if(session)
{
@@ -2792,7 +2884,12 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId,
session->mErrorStatusString = statusString;
if(session == mAudioSession)
{
- setState(stateJoinSessionFailed);
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("failed");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
}
else
{
@@ -2807,28 +2904,46 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId,
{
setSessionHandle(session, sessionHandle);
}
+
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("added");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+
}
}
void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
{
- sessionState *session = findSession(requestId);
- if(statusCode != 0)
+ sessionStatePtr_t session(findSession(requestId));
+ // 1026 is session already has media, somehow mediaconnect was called twice on the same session.
+ // set the session info to reflect that the user is already connected.
+ if (statusCode == 1026)
+ {
+ session->mVoiceEnabled = true;
+ session->mMediaConnectInProgress = false;
+ session->mMediaStreamState = streamStateConnected;
+ //session->mTextStreamState = streamStateConnected;
+ session->mErrorStatusCode = 0;
+ }
+ else if (statusCode != 0)
{
LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
- if(session)
+ if (session)
{
session->mMediaConnectInProgress = false;
- session->mErrorStatusCode = statusCode;
+ session->mErrorStatusCode = statusCode;
session->mErrorStatusString = statusString;
- if(session == mAudioSession)
- setState(stateJoinSessionFailed);
}
}
else
{
LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL;
}
+
+ /*TODO: Post response?*/
}
void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString)
@@ -2838,6 +2953,11 @@ void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusStrin
LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
// Should this ever fail? do we care if it does?
}
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["logout"] = LLSD::Boolean(true);
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
}
void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
@@ -2850,10 +2970,11 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &
mConnected = false;
- if(getState() == stateConnectorStopping)
- {
- setState(stateConnectorStopped);
- }
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["connector"] = LLSD::Boolean(false);
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
}
void LLVivoxVoiceClient::sessionAddedEvent(
@@ -2866,7 +2987,7 @@ void LLVivoxVoiceClient::sessionAddedEvent(
std::string &nameString,
std::string &applicationString)
{
- sessionState *session = NULL;
+ sessionStatePtr_t session;
LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
@@ -2929,7 +3050,7 @@ void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
{
LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
-#if USE_SESSION_GROUPS
+#if USE_SESSION_GROUPS
if(mMainSessionGroupHandle.empty())
{
// This is the first (i.e. "main") session group. Save its handle.
@@ -2942,12 +3063,12 @@ void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
#endif
}
-void LLVivoxVoiceClient::joinedAudioSession(sessionState *session)
+void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)
{
LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL;
if(mAudioSession != session)
{
- sessionState *oldSession = mAudioSession;
+ sessionStatePtr_t oldSession = mAudioSession;
mAudioSession = session;
mAudioSessionChanged = true;
@@ -2957,13 +3078,17 @@ void LLVivoxVoiceClient::joinedAudioSession(sessionState *session)
}
// This is the session we're joining.
- if(getState() == stateJoiningSession)
+ if(mIsJoiningSession)
{
- setState(stateSessionJoined);
-
- // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(session->mHandle);
+ vivoxevent["session"] = LLSD::String("joined");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+
// Add the current user as a participant here.
- participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
+ participantStatePtr_t participant(session->addParticipant(sipURIFromName(mAccountName)));
if(participant)
{
participant->mIsSelf = true;
@@ -2976,7 +3101,7 @@ void LLVivoxVoiceClient::joinedAudioSession(sessionState *session)
if(!session->mIsChannel)
{
// this is a p2p session. Make sure the other end is added as a participant.
- participantState *participant = session->addParticipant(session->mSIPURI);
+ participantStatePtr_t participant(session->addParticipant(session->mSIPURI));
if(participant)
{
if(participant->mAvatarIDValid)
@@ -3003,7 +3128,7 @@ void LLVivoxVoiceClient::sessionRemovedEvent(
{
LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
leftAudioSession(session);
@@ -3017,26 +3142,25 @@ void LLVivoxVoiceClient::sessionRemovedEvent(
// Reset the media state (we now have no info)
session->mMediaStreamState = streamStateUnknown;
- session->mTextStreamState = streamStateUnknown;
+ //session->mTextStreamState = streamStateUnknown;
// Conditionally delete the session
reapSession(session);
}
else
{
- LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
+ // Already reaped this session.
+ LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
}
+
}
-void LLVivoxVoiceClient::reapSession(sessionState *session)
+void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session)
{
if(session)
{
- if(!session->mHandle.empty())
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
- }
- else if(session->mCreateInProgress)
+
+ if(session->mCreateInProgress)
{
LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
}
@@ -3054,11 +3178,9 @@ void LLVivoxVoiceClient::reapSession(sessionState *session)
}
else
{
- // TODO: Question: Should we check for queued text messages here?
// We don't have a reason to keep tracking this session, so just delete it.
LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
deleteSession(session);
- session = NULL;
}
}
else
@@ -3068,11 +3190,11 @@ void LLVivoxVoiceClient::reapSession(sessionState *session)
}
// Returns true if the session seems to indicate we've moved to a region on a different voice server
-bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session)
+bool LLVivoxVoiceClient::sessionNeedsRelog(const sessionStatePtr_t &session)
{
bool result = false;
- if(session != NULL)
+ if(session)
{
// Only make this check for spatial channels (so it won't happen for group or p2p calls)
if(session->mIsSpatial)
@@ -3100,35 +3222,17 @@ bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session)
return result;
}
-void LLVivoxVoiceClient::leftAudioSession(
- sessionState *session)
+void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session)
{
- if(mAudioSession == session)
- {
- switch(getState())
- {
- case stateJoiningSession:
- case stateSessionJoined:
- case stateRunning:
- case stateLeavingSession:
- case stateJoinSessionFailed:
- case stateJoinSessionFailedWaiting:
- // normal transition
- LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
- setState(stateSessionTerminated);
- break;
-
- case stateSessionTerminated:
- // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
- LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
- break;
-
- default:
- LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
- setState(stateSessionTerminated);
- break;
- }
- }
+ if (mAudioSession == session)
+ {
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(session->mHandle);
+ vivoxevent["session"] = LLSD::String("removed");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+ }
}
void LLVivoxVoiceClient::accountLoginStateChangeEvent(
@@ -3137,6 +3241,8 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
std::string &statusString,
int state)
{
+ LLSD levent = LLSD::emptyMap();
+
/*
According to Mike S., status codes for this event are:
login_state_logged_out=0,
@@ -3151,34 +3257,37 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
switch(state)
{
case 1:
- if(getState() == stateLoggingIn)
- {
- setState(stateLoggedIn);
- }
- break;
+ levent["login"] = LLSD::String("account_login");
+ LLEventPumps::instance().post("vivoxClientPump", levent);
+ break;
case 3:
- // The user is in the process of logging out.
- setState(stateLoggingOut);
- break;
+ levent["login"] = LLSD::String("account_loggingOut");
+
+ LLEventPumps::instance().post("vivoxClientPump", levent);
+ break;
case 0:
- // The user has been logged out.
- setState(stateLoggedOut);
- break;
+ levent["login"] = LLSD::String("account_logout");
+
+ LLEventPumps::instance().post("vivoxClientPump", levent);
+ break;
default:
//Used to be a commented out warning
LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
- break;
+ break;
}
}
void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType)
{
+ LLSD result;
+
if (mediaCompletionType == "AuxBufferAudioCapture")
{
mCaptureBufferRecording = false;
+ result["recplay"] = "end";
}
else if (mediaCompletionType == "AuxBufferAudioRender")
{
@@ -3186,12 +3295,17 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s
if (--mPlayRequestCount <= 0)
{
mCaptureBufferPlaying = false;
- }
+ result["recplay"] = "end";
+// result["recplay"] = "done";
+ }
}
else
{
LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL;
}
+
+ if (!result.isUndefined())
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
@@ -3202,7 +3316,7 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
int state,
bool incoming)
{
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
@@ -3228,8 +3342,9 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
switch(state)
{
- case streamStateIdle:
- // Standard "left audio session"
+ case streamStateDisconnecting:
+ case streamStateIdle:
+ // Standard "left audio session", Vivox state 'disconnected'
session->mVoiceEnabled = false;
session->mMediaConnectInProgress = false;
leftAudioSession(session);
@@ -3239,6 +3354,7 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
session->mVoiceEnabled = true;
session->mMediaConnectInProgress = false;
joinedAudioSession(session);
+ case streamStateConnecting: // do nothing, but prevents a warning getting into the logs.
break;
case streamStateRinging:
@@ -3269,54 +3385,8 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
}
else
{
- LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
- }
-}
-
-void LLVivoxVoiceClient::textStreamUpdatedEvent(
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- bool enabled,
- int state,
- bool incoming)
-{
- sessionState *session = findSession(sessionHandle);
-
- if(session)
- {
- // Save the state for later use
- session->mTextStreamState = state;
-
- // We know about this session
- switch(state)
- {
- case 0: // We see this when the text stream closes
- LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
- break;
-
- case 1: // We see this on an incoming call from the Connector
- // Try to send any text messages queued for this session.
- sendQueuedTextMessages(session);
-
- // Send the text chat invite to the GUI layer
- // TODO: Question: Should we correlate with the mute list here?
- session->mTextInvitePending = true;
- if(session->mName.empty())
- {
- lookupName(session->mCallerID);
- }
- else
- {
- // Act like we just finished resolving the name
- avatarNameResolved(session->mCallerID, session->mName);
- }
- break;
-
- default:
- LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
- break;
-
- }
+ // session disconnectintg and disconnected events arriving after we have already left the session.
+ LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL;
}
}
@@ -3329,10 +3399,10 @@ void LLVivoxVoiceClient::participantAddedEvent(
std::string &displayNameString,
int participantType)
{
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
- participantState *participant = session->addParticipant(uriString);
+ participantStatePtr_t participant(session->addParticipant(uriString));
if(participant)
{
participant->mAccountName = nameString;
@@ -3375,10 +3445,10 @@ void LLVivoxVoiceClient::participantRemovedEvent(
std::string &alias,
std::string &nameString)
{
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
- participantState *participant = session->findParticipant(uriString);
+ participantStatePtr_t participant(session->findParticipant(uriString));
if(participant)
{
session->removeParticipant(participant);
@@ -3390,6 +3460,7 @@ void LLVivoxVoiceClient::participantRemovedEvent(
}
else
{
+ // a late arriving event on a session we have already left.
LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
}
}
@@ -3405,13 +3476,15 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
int volume,
F32 energy)
{
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
- participantState *participant = session->findParticipant(uriString);
+ participantStatePtr_t participant(session->findParticipant(uriString));
if(participant)
{
+ //LL_INFOS("Voice") << "Participant Update for " << participant->mDisplayName << LL_ENDL;
+
participant->mIsSpeaking = isSpeaking;
participant->mIsModeratorMuted = isModeratorMuted;
@@ -3474,7 +3547,7 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
}
else
{
- LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
+ LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
}
}
@@ -3488,7 +3561,9 @@ void LLVivoxVoiceClient::messageEvent(
{
LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL;
-
+
+ LL_INFOS("Voice") << "Vivox raw message:" << std::endl << messageBody << LL_ENDL;
+
if(messageHeader.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos)
{
std::string message;
@@ -3592,7 +3667,7 @@ void LLVivoxVoiceClient::messageEvent(
// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL;
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
bool is_do_not_disturb = gAgent.isDoNotDisturb();
@@ -3633,11 +3708,11 @@ void LLVivoxVoiceClient::messageEvent(
void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
{
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
- participantState *participant = session->findParticipant(uriString);
+ participantStatePtr_t participant(session->findParticipant(uriString));
if(participant)
{
if (!stricmp(notificationType.c_str(), "Typing"))
@@ -3683,7 +3758,7 @@ void LLVivoxVoiceClient::muteListChanged()
for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
{
- participantState *p = iter->second;
+ participantStatePtr_t p(iter->second);
// Check to see if this participant is on the mute list already
if(p->updateMuteState())
@@ -3711,9 +3786,9 @@ LLVivoxVoiceClient::participantState::participantState(const std::string &uri) :
{
}
-LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri)
+LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri)
{
- participantState *result = NULL;
+ participantStatePtr_t result;
bool useAlternateURI = false;
// Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it
@@ -3741,7 +3816,7 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti
if(!result)
{
// participant isn't already in one list or the other.
- result = new participantState(useAlternateURI?mSIPURI:uri);
+ result.reset(new participantState(useAlternateURI?mSIPURI:uri));
mParticipantsByURI.insert(participantMap::value_type(result->mURI, result));
mParticipantsChanged = true;
@@ -3760,12 +3835,11 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti
result->mAvatarID.generate(uri);
}
}
-
- if(result->updateMuteState())
- {
- mMuteDirty = true;
- }
+ if(result->updateMuteState())
+ {
+ mMuteDirty = true;
+ }
mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result));
@@ -3784,8 +3858,6 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti
bool LLVivoxVoiceClient::participantState::updateMuteState()
{
bool result = false;
-
-
bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
if(mOnMuteList != isMuted)
@@ -3802,7 +3874,7 @@ bool LLVivoxVoiceClient::participantState::isAvatar()
return mAvatarIDValid;
}
-void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant)
+void LLVivoxVoiceClient::sessionState::removeParticipant(const LLVivoxVoiceClient::participantStatePtr_t &participant)
{
if(participant)
{
@@ -3827,9 +3899,8 @@ void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::par
{
mParticipantsByURI.erase(iter);
mParticipantsByUUID.erase(iter2);
-
- delete participant;
- mParticipantsChanged = true;
+
+ mParticipantsChanged = true;
}
}
}
@@ -3872,9 +3943,9 @@ bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id)
}
-LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri)
+LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri)
{
- participantState *result = NULL;
+ participantStatePtr_t result;
participantMap::iterator iter = mParticipantsByURI.find(uri);
@@ -3896,9 +3967,9 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findPart
return result;
}
-LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
+LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
{
- participantState * result = NULL;
+ participantStatePtr_t result;
participantUUIDMap::iterator iter = mParticipantsByUUID.find(id);
if(iter != mParticipantsByUUID.end())
@@ -3909,9 +3980,9 @@ LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findPart
return result;
}
-LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id)
+LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::findParticipantByID(const LLUUID& id)
{
- participantState * result = NULL;
+ participantStatePtr_t result;
if(mAudioSession)
{
@@ -3956,137 +4027,67 @@ bool LLVivoxVoiceClient::checkParcelChanged(bool update)
return false;
}
-bool LLVivoxVoiceClient::parcelVoiceInfoReceived(state requesting_state)
-{
- // pop back to the state we were in when the parcel changed and we managed to
- // do the request.
- if(getState() == stateRetrievingParcelVoiceInfo)
- {
- setState(requesting_state);
- return true;
- }
- else
- {
- // we've dropped out of stateRetrievingParcelVoiceInfo
- // before we received the cap result, due to a terminate
- // or transition to a non-voice channel. Don't switch channels.
- return false;
- }
-}
-
-
-bool LLVivoxVoiceClient::requestParcelVoiceInfo()
-{
- LLViewerRegion * region = gAgent.getRegion();
- if (region == NULL || !region->capabilitiesReceived())
- {
- // we don't have the cap yet, so return false so the caller can try again later.
-
- LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL;
- return false;
- }
-
- // grab the cap.
- std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
- if (url.empty())
- {
- // Region dosn't have the cap. Stop probing.
- LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL;
- setState(stateDisableCleanup);
- return false;
- }
- else
- {
- // if we've already retrieved the cap from the region, go ahead and make the request,
- // and return true so we can go into the state that waits for the response.
- checkParcelChanged(true);
- LLSD data;
- LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
-
- LLHTTPClient::post(
- url,
- data,
- new LLVivoxVoiceClientCapResponder(getState()));
- return true;
- }
-}
-
-void LLVivoxVoiceClient::switchChannel(
+bool LLVivoxVoiceClient::switchChannel(
std::string uri,
bool spatial,
bool no_reconnect,
bool is_p2p,
std::string hash)
{
- bool needsSwitch = false;
+ bool needsSwitch = !mIsInChannel;
- LL_DEBUGS("Voice")
- << "called in state " << state2string(getState())
- << " with uri \"" << uri << "\""
- << (spatial?", spatial is true":", spatial is false")
- << LL_ENDL;
-
- switch(getState())
- {
- case stateJoinSessionFailed:
- case stateJoinSessionFailedWaiting:
- case stateNoChannel:
- case stateRetrievingParcelVoiceInfo:
- // Always switch to the new URI from these states.
- needsSwitch = true;
- break;
+ if (mIsInChannel)
+ {
+ if (mSessionTerminateRequested)
+ {
+ // If a terminate has been requested, we need to compare against where the URI we're already headed to.
+ if(mNextAudioSession)
+ {
+ if(mNextAudioSession->mSIPURI != uri)
+ needsSwitch = true;
+ }
+ else
+ {
+ // mNextAudioSession is null -- this probably means we're on our way back to spatial.
+ if(!uri.empty())
+ {
+ // We do want to process a switch in this case.
+ needsSwitch = true;
+ }
+ }
+ }
+ else
+ {
+ // Otherwise, compare against the URI we're in now.
+ if(mAudioSession)
+ {
+ if(mAudioSession->mSIPURI != uri)
+ {
+ needsSwitch = true;
+ }
+ }
+ else
+ {
+ if(!uri.empty())
+ {
+ // mAudioSession is null -- it's not clear what case would cause this.
+ // For now, log it as a warning and see if it ever crops up.
+ LL_WARNS("Voice") << "No current audio session... Forcing switch" << LL_ENDL;
+ needsSwitch = true;
+ }
+ }
+ }
+ }
- default:
- if(mSessionTerminateRequested)
- {
- // If a terminate has been requested, we need to compare against where the URI we're already headed to.
- if(mNextAudioSession)
- {
- if(mNextAudioSession->mSIPURI != uri)
- needsSwitch = true;
- }
- else
- {
- // mNextAudioSession is null -- this probably means we're on our way back to spatial.
- if(!uri.empty())
- {
- // We do want to process a switch in this case.
- needsSwitch = true;
- }
- }
- }
- else
- {
- // Otherwise, compare against the URI we're in now.
- if(mAudioSession)
- {
- if(mAudioSession->mSIPURI != uri)
- {
- needsSwitch = true;
- }
- }
- else
- {
- if(!uri.empty())
- {
- // mAudioSession is null -- it's not clear what case would cause this.
- // For now, log it as a warning and see if it ever crops up.
- LL_WARNS("Voice") << "No current audio session." << LL_ENDL;
- }
- }
- }
- break;
- }
-
- if(needsSwitch)
+ if(needsSwitch)
{
if(uri.empty())
{
// Leave any channel we may be in
LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
- sessionState *oldSession = mNextAudioSession;
- mNextAudioSession = NULL;
+ sessionStatePtr_t oldSession = mNextAudioSession;
+ mNextAudioSession.reset();
// The old session may now need to be deleted.
reapSession(oldSession);
@@ -4104,28 +4105,27 @@ void LLVivoxVoiceClient::switchChannel(
mNextAudioSession->mIsP2P = is_p2p;
}
- if(getState() >= stateRetrievingParcelVoiceInfo)
+ if (mIsInChannel)
{
// If we're already in a channel, or if we're joining one, terminate
// so we can rejoin with the new session data.
sessionTerminate();
}
}
+
+ return needsSwitch;
}
-void LLVivoxVoiceClient::joinSession(sessionState *session)
+void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session)
{
mNextAudioSession = session;
-
- if(getState() <= stateNoChannel)
- {
- // We're already set up to join a channel, just needed to fill in the session handle
- }
- else
- {
- // State machine will come around and rejoin if uri/handle is not empty.
- sessionTerminate();
- }
+
+ if (mIsInChannel)
+ {
+ // If we're already in a channel, or if we're joining one, terminate
+ // so we can rejoin with the new session data.
+ sessionTerminate();
+ }
}
void LLVivoxVoiceClient::setNonSpatialChannel(
@@ -4135,7 +4135,7 @@ void LLVivoxVoiceClient::setNonSpatialChannel(
switchChannel(uri, false, false, false, credentials);
}
-void LLVivoxVoiceClient::setSpatialChannel(
+bool LLVivoxVoiceClient::setSpatialChannel(
const std::string &uri,
const std::string &credentials)
{
@@ -4145,14 +4145,15 @@ void LLVivoxVoiceClient::setSpatialChannel(
LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
- if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
+ if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
{
// User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
+ return false;
}
else
{
- switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
+ return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
}
}
@@ -4163,10 +4164,10 @@ void LLVivoxVoiceClient::callUser(const LLUUID &uuid)
switchChannel(userURI, false, true, true);
}
-LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid)
+LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid)
{
// Figure out if a session with the user already exists
- sessionState *session = findSession(uuid);
+ sessionStatePtr_t session(findSession(uuid));
if(!session)
{
// No session with user, need to start one.
@@ -4174,7 +4175,8 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L
session = addSession(uri);
llassert(session);
- if (!session) return NULL;
+ if (!session)
+ return session;
session->mIsSpatial = false;
session->mReconnect = false;
@@ -4185,7 +4187,7 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L
if(session->mHandle.empty())
{
// Session isn't active -- start it up.
- sessionCreateSendMessage(session, false, true);
+ sessionCreateSendMessage(session, false, false);
}
else
{
@@ -4196,70 +4198,17 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L
return session;
}
-BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
-{
- bool result = false;
-
- // Attempt to locate the indicated session
- sessionState *session = startUserIMSession(participant_id);
- if(session)
- {
- // found the session, attempt to send the message
- session->mTextMsgQueue.push(message);
-
- // Try to send queued messages (will do nothing if the session is not open yet)
- sendQueuedTextMessages(session);
-
- // The message is queued, so we succeed.
- result = true;
- }
- else
- {
- LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
- }
-
- return result;
-}
-
-void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session)
-{
- if(session->mTextStreamState == 1)
- {
- if(!session->mTextMsgQueue.empty())
- {
- std::ostringstream stream;
-
- while(!session->mTextMsgQueue.empty())
- {
- std::string message = session->mTextMsgQueue.front();
- session->mTextMsgQueue.pop();
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
- << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
- << "<MessageHeader>text/HTML</MessageHeader>"
- << "<MessageBody>" << message << "</MessageBody>"
- << "</Request>"
- << "\n\n\n";
- }
- writeString(stream.str());
- }
- }
- else
- {
- // Session isn't connected yet, defer until later.
- }
-}
void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid)
{
// Figure out if a session with the user exists
- sessionState *session = findSession(uuid);
+ sessionStatePtr_t session(findSession(uuid));
if(session)
{
// found the session
if(!session->mHandle.empty())
{
- sessionTextDisconnectSendMessage(session);
+ // sessionTextDisconnectSendMessage(session); // a SLim leftover, not used any more.
}
}
else
@@ -4276,7 +4225,7 @@ bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle)
{
// this is only ever used to answer incoming p2p call invites.
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
session->mIsSpatial = false;
@@ -4292,10 +4241,12 @@ bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle)
bool LLVivoxVoiceClient::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) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated);
+
+ //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.
@@ -4303,7 +4254,7 @@ bool LLVivoxVoiceClient::isVoiceWorking() const
BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id)
{
BOOL result = TRUE;
- sessionState *session = findSession(id);
+ sessionStatePtr_t session(findSession(id));
if(session != NULL)
{
@@ -4316,7 +4267,7 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id)
// Didn't find a matching session -- check the current audio session for a matching participant
if(mAudioSession != NULL)
{
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant != NULL)
{
result = participant->isAvatar();
@@ -4332,7 +4283,7 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id)
BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
{
BOOL result = TRUE;
- sessionState *session = findSession(session_id);
+ sessionStatePtr_t session(findSession(session_id));
if(session != NULL)
{
@@ -4342,12 +4293,12 @@ BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
return result;
}
-// Returns true if the session can accepte text IM's.
+// Returns true if the session can accept text IM's.
// Currently this will be false only for PSTN P2P calls.
BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)
{
bool result = TRUE;
- sessionState *session = findSession(session_id);
+ sessionStatePtr_t session(findSession(session_id));
if(session != NULL)
{
@@ -4360,7 +4311,7 @@ BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)
void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle)
{
- sessionState *session = findSession(sessionHandle);
+ sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
sessionMediaDisconnectSendMessage(session);
@@ -4369,13 +4320,11 @@ void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle)
void LLVivoxVoiceClient::leaveNonSpatialChannel()
{
- LL_DEBUGS("Voice")
- << "called in state " << state2string(getState())
- << LL_ENDL;
+ LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL;
// Make sure we don't rejoin the current session.
- sessionState *oldNextSession = mNextAudioSession;
- mNextAudioSession = NULL;
+ sessionStatePtr_t oldNextSession(mNextAudioSession);
+ mNextAudioSession.reset();
// Most likely this will still be the current session at this point, but check it anyway.
reapSession(oldNextSession);
@@ -4389,7 +4338,7 @@ std::string LLVivoxVoiceClient::getCurrentChannel()
{
std::string result;
- if((getState() == stateRunning) && !mSessionTerminateRequested)
+ if (mIsInChannel && !mSessionTerminateRequested)
{
result = getAudioSessionURI();
}
@@ -4401,7 +4350,7 @@ bool LLVivoxVoiceClient::inProximalChannel()
{
bool result = false;
- if((getState() == stateRunning) && !mSessionTerminateRequested)
+ if (mIsInChannel && !mSessionTerminateRequested)
{
result = inSpatialChannel();
}
@@ -4610,12 +4559,13 @@ void LLVivoxVoiceClient::enforceTether(void)
void LLVivoxVoiceClient::updatePosition(void)
{
-
+
LLViewerRegion *region = gAgent.getRegion();
if(region && isAgentAvatarValid())
{
LLMatrix3 rot;
LLVector3d pos;
+ LLQuaternion qrot;
// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
// They're currently always set to zero.
@@ -4630,7 +4580,7 @@ void LLVivoxVoiceClient::updatePosition(void)
rot); // rotation matrix
// Send the current avatar position to the voice code
- rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3();
+ qrot = gAgentAvatarp->getRootJoint()->getWorldRotation();
pos = gAgentAvatarp->getPositionGlobal();
// TODO: Can we get the head offset from outside the LLVOAvatar?
@@ -4640,7 +4590,7 @@ void LLVivoxVoiceClient::updatePosition(void)
LLVivoxVoiceClient::getInstance()->setAvatarPosition(
pos, // position
LLVector3::zero, // velocity
- rot); // rotation matrix
+ qrot); // rotation matrix
}
}
@@ -4661,7 +4611,7 @@ void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLV
}
}
-void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot)
{
if(dist_vec_squared(mAvatarPosition, position) > 0.01)
{
@@ -4675,8 +4625,9 @@ void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLV
mSpatialCoordsDirty = true;
}
- if(mAvatarRot != rot)
- {
+ if ((mAvatarRot != rot) && (llabs(dot(mAvatarRot, rot)) > MINUSCULE_ANGLE_COS))
+ { // if the two rotations are not exactly equal test their dot product
+ // to get the cos of the angle between them. If it is minuscule don't update.
mAvatarRot = rot;
mSpatialCoordsDirty = true;
}
@@ -4699,7 +4650,7 @@ bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &
void LLVivoxVoiceClient::leaveChannel(void)
{
- if(getState() == stateRunning)
+ if (mIsInChannel)
{
LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL;
mChannelName.clear();
@@ -4730,6 +4681,12 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled)
{
LLVoiceChannel::getCurrentVoiceChannel()->activate();
status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
+
+ if (!mIsCoroutineActive)
+ {
+ LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
+ boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance()));
+ }
}
else
{
@@ -4754,7 +4711,7 @@ void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled)
BOOL LLVivoxVoiceClient::lipSyncEnabled()
{
- if ( mVoiceEnabled && stateDisabled != getState() )
+ if ( mVoiceEnabled )
{
return mLipSyncEnabled;
}
@@ -4809,7 +4766,7 @@ void LLVivoxVoiceClient::setMicGain(F32 volume)
BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id)
{
BOOL result = FALSE;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
// I'm not sure what the semantics of this should be.
@@ -4823,7 +4780,7 @@ BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id)
std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id)
{
std::string result;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mDisplayName;
@@ -4838,7 +4795,7 @@ BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id)
{
BOOL result = FALSE;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
@@ -4855,7 +4812,7 @@ BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id)
{
BOOL result = FALSE;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mIsModeratorMuted;
@@ -4867,7 +4824,7 @@ BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id)
F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id)
{
F32 result = 0;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mPower;
@@ -4882,7 +4839,7 @@ BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id)
{
BOOL result = FALSE;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
// I'm not sure what the semantics of this should be.
@@ -4897,7 +4854,7 @@ BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id)
{
BOOL result = FALSE;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mOnMuteList;
@@ -4909,11 +4866,11 @@ BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id)
// External accessors.
F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id)
{
- // Minimum volume will be returned for users with voice disabled
- F32 result = LLVoiceClient::VOLUME_MIN;
+ // Minimum volume will be returned for users with voice disabled
+ F32 result = LLVoiceClient::VOLUME_MIN;
- participantState *participant = findParticipantByID(id);
- if(participant)
+ participantStatePtr_t participant(findParticipantByID(id));
+ if(participant)
{
result = participant->mVolume;
@@ -4928,7 +4885,7 @@ void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
{
if(mAudioSession)
{
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if (participant && !participant->mIsSelf)
{
if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT))
@@ -4954,7 +4911,7 @@ std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id)
{
std::string result;
- participantState *participant = findParticipantByID(id);
+ participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mGroupID;
@@ -5073,7 +5030,7 @@ void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed)
LLVivoxVoiceClient::sessionState::sessionState() :
mErrorStatusCode(0),
mMediaStreamState(streamStateUnknown),
- mTextStreamState(streamStateUnknown),
+ //mTextStreamState(streamStateUnknown),
mCreateInProgress(false),
mMediaConnectInProgress(false),
mVoiceInvitePending(false),
@@ -5122,9 +5079,9 @@ LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void)
}
-LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::string &handle)
+LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const std::string &handle)
{
- sessionState *result = NULL;
+ sessionStatePtr_t result;
sessionMap::iterator iter = mSessionsByHandle.find(handle);
if(iter != mSessionsByHandle.end())
{
@@ -5134,12 +5091,12 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::str
return result;
}
-LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
+LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
{
- sessionState *result = NULL;
+ sessionStatePtr_t result;
for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
{
- sessionState *session = *iter;
+ sessionStatePtr_t session = *iter;
if(session->mCreateInProgress && (session->mSIPURI == uri))
{
result = session;
@@ -5150,13 +5107,13 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByU
return result;
}
-LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &participant_id)
+LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const LLUUID &participant_id)
{
- sessionState *result = NULL;
+ sessionStatePtr_t result;
for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
{
- sessionState *session = *iter;
+ sessionStatePtr_t session = *iter;
if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id))
{
result = session;
@@ -5167,9 +5124,9 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &
return result;
}
-LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle)
+LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle)
{
- sessionState *result = NULL;
+ sessionStatePtr_t result;
if(handle.empty())
{
@@ -5177,7 +5134,7 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri
// Check whether there's already a session with this URI
for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
{
- sessionState *s = *iter;
+ sessionStatePtr_t s(*iter);
if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
{
// TODO: I need to think about this logic... it's possible that this case should raise an internal error.
@@ -5202,7 +5159,7 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri
// No existing session found.
LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
- result = new sessionState();
+ result.reset(new sessionState());
result->mSIPURI = uri;
result->mHandle = handle;
@@ -5252,7 +5209,7 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri
return result;
}
-void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
+void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle)
{
// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
@@ -5285,7 +5242,7 @@ void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::stri
verifySessionState();
}
-void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
+void LLVivoxVoiceClient::setSessionURI(const sessionStatePtr_t &session, const std::string &uri)
{
// There used to be a map of session URIs to sessions, which made this complex....
session->mSIPURI = uri;
@@ -5293,7 +5250,7 @@ void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string
verifySessionState();
}
-void LLVivoxVoiceClient::deleteSession(sessionState *session)
+void LLVivoxVoiceClient::deleteSession(const sessionStatePtr_t &session)
{
// Remove the session from the handle map
if(!session->mHandle.empty())
@@ -5318,18 +5275,18 @@ void LLVivoxVoiceClient::deleteSession(sessionState *session)
// If this is the current audio session, clean up the pointer which will soon be dangling.
if(mAudioSession == session)
{
- mAudioSession = NULL;
+ mAudioSession.reset();
mAudioSessionChanged = true;
}
// ditto for the next audio session
if(mNextAudioSession == session)
{
- mNextAudioSession = NULL;
+ mNextAudioSession.reset();
}
// delete the session
- delete session;
+ //delete session;
}
void LLVivoxVoiceClient::deleteAllSessions()
@@ -5354,7 +5311,7 @@ void LLVivoxVoiceClient::verifySessionState(void)
for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
{
- sessionState *session = *iter;
+ sessionStatePtr_t session(*iter);
LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
@@ -5379,7 +5336,7 @@ void LLVivoxVoiceClient::verifySessionState(void)
// check that every entry in the handle map points to a valid session in the session set
for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
{
- sessionState *session = iter->second;
+ sessionStatePtr_t session(iter->second);
sessionIterator i2 = mSessions.find(session);
if(i2 == mSessions.end())
{
@@ -5548,9 +5505,9 @@ void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string
// Iterate over all sessions.
for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
{
- sessionState *session = *iter;
+ sessionStatePtr_t session(*iter);
// Check for this user as a participant in this session
- participantState *participant = session->findParticipantByID(id);
+ participantStatePtr_t participant(session->findParticipantByID(id));
if(participant)
{
// Found -- fill in the name
@@ -6021,7 +5978,7 @@ void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage()
}
}
-void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session)
+void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session)
{
S32 font_index = getVoiceFontIndex(session->mVoiceFontID);
LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL;
@@ -6039,12 +5996,16 @@ void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session)
void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString)
{
- // Voice font list entries were updated via addVoiceFont() during parsing.
- if(getState() == stateVoiceFontsWait)
- {
- setState(stateVoiceFontsReceived);
- }
+ if (mIsWaitingForFonts)
+ {
+ // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after
+ // receiving the last one.
+ LLSD result = LLSD::emptyMap();
+ result["voice_fonts"] = LLSD::Boolean(true);
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
+ }
notifyVoiceFontObservers();
mVoiceFontsReceived = true;
}
@@ -6187,8 +6148,17 @@ void LLVivoxVoiceClient::notifyVoiceFontObservers()
void LLVivoxVoiceClient::enablePreviewBuffer(bool enable)
{
- mCaptureBufferMode = enable;
- if(mCaptureBufferMode && getState() >= stateNoChannel)
+ LLSD result;
+ mCaptureBufferMode = enable;
+
+ if (enable)
+ result["recplay"] = "start";
+ else
+ result["recplay"] = "quit";
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
+
+ if(mCaptureBufferMode && mIsInChannel)
{
LL_DEBUGS("Voice") << "no channel" << LL_ENDL;
sessionTerminate();
@@ -6205,6 +6175,10 @@ void LLVivoxVoiceClient::recordPreviewBuffer()
}
mCaptureBufferRecording = true;
+
+ LLSD result;
+ result["recplay"] = "record";
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id)
@@ -6225,12 +6199,20 @@ void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id)
mPreviewVoiceFont = effect_id;
mCaptureBufferPlaying = true;
+
+ LLSD result;
+ result["recplay"] = "playback";
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::stopPreviewBuffer()
{
mCaptureBufferRecording = false;
mCaptureBufferPlaying = false;
+
+ LLSD result;
+ result["recplay"] = "quit";
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
bool LLVivoxVoiceClient::isPreviewRecording()
@@ -6669,6 +6651,10 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
uriString = string;
else if (!stricmp("Presence", tag))
statusString = string;
+ else if (!stricmp("CaptureDevices", tag))
+ LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true);
+ else if (!stricmp("RenderDevices", tag))
+ LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true);
else if (!stricmp("CaptureDevice", tag))
{
LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString);
@@ -6797,7 +6783,16 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
if (isEvent)
{
const char *eventTypeCstr = eventTypeString.c_str();
- if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
+// LL_WARNS("LOW Voice") << eventTypeCstr << LL_ENDL;
+
+ if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
+ {
+ // These happen so often that logging them is pretty useless.
+ squelchDebugOutput = true;
+// LL_WARNS("LOW Voice") << "Updated Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << isModeratorMuted << ", " << isSpeaking << ", " << volume << ", " << energy << LL_ENDL;
+ LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
+ }
+ else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
{
LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
}
@@ -6819,6 +6814,10 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
{
LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
}
+ else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent"))
+ {
+ //nothng useful to process for this event, but we should not WARN that we have received it.
+ }
else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
{
LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle);
@@ -6847,19 +6846,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
*/
LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType);
}
- else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
- {
- /*
- <Event type="TextStreamUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
- <Enabled>true</Enabled>
- <State>1</State>
- <Incoming>true</Incoming>
- </Event>
- */
- LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
- }
else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
{
/*
@@ -6872,6 +6858,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
<ParticipantType>0</ParticipantType>
</Event>
*/
+// LL_WARNS("LOW Voice") << "Added Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << ", " << displayNameString << ", " << participantType << LL_ENDL;
LLVivoxVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
}
else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
@@ -6884,54 +6871,24 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
<AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
</Event>
*/
+// LL_WARNS("LOW Voice") << "Removed params:" << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << LL_ENDL;
+
LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
}
- else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
- {
- /*
- <Event type="ParticipantUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
- <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
- <IsModeratorMuted>false</IsModeratorMuted>
- <IsSpeaking>true</IsSpeaking>
- <Volume>44</Volume>
- <Energy>0.0879437</Energy>
- </Event>
- */
-
- // These happen so often that logging them is pretty useless.
- squelchDebugOutput = true;
-
- LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
- }
else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
{
// These are really spammy in tuning mode
squelchDebugOutput = true;
-
LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy);
}
- else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
- {
- /*
- <Event type="BuddyChangedEvent">
- <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
- <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
- <DisplayName>Monroe Tester</DisplayName>
- <BuddyData />
- <GroupID>0</GroupID>
- <ChangeType>Set</ChangeType>
- </Event>
- */
- // TODO: Question: Do we need to process this at all?
- }
else if (!stricmp(eventTypeCstr, "MessageEvent"))
{
+ //TODO: This probably is not received any more, it was used to support SLim clients
LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
}
else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))
{
+ //TODO: This probably is not received any more, it was used to support SLim clients
LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType);
}
else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))
@@ -6951,19 +6908,29 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
*/
// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
}
-
- else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))
+ else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))
{
- /*
- <Event type="SessionGroupRemovedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- </Event>
- */
// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
}
- else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent"))
+ else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent"))
{ // Yet another ignored event
}
+ else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent"))
+ {
+ /*
+ <Event type = "AudioDeviceHotSwapEvent">
+ <EventType>RenderDeviceChanged< / EventType>
+ <RelevantDevice>
+ <Device>Speakers(Turtle Beach P11 Headset)< / Device>
+ <DisplayName>Speakers(Turtle Beach P11 Headset)< / DisplayName>
+ <Type>SpecificDevice< / Type>
+ < / RelevantDevice>
+ < / Event>
+ */
+ // an audio device was removed or added, fetch and update the local list of audio devices.
+ LLVivoxVoiceClient::getInstance()->getCaptureDevicesSendMessage();
+ LLVivoxVoiceClient::getInstance()->getRenderDevicesSendMessage();
+ }
else
{
LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
@@ -6972,7 +6939,14 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
else
{
const char *actionCstr = actionString.c_str();
- if (!stricmp(actionCstr, "Connector.Create.1"))
+// LL_WARNS("LOW Voice") << actionCstr << LL_ENDL;
+
+ if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
+ {
+ // We don't need to process these, but they're so spammy we don't want to log them.
+ squelchDebugOutput = true;
+ }
+ else if (!stricmp(actionCstr, "Connector.Create.1"))
{
LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
}
@@ -7000,11 +6974,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
{
LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);
}
- else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
- {
- // We don't need to process these, but they're so spammy we don't want to log them.
- squelchDebugOutput = true;
- }
else if (!stricmp(actionCstr, "Account.GetSessionFonts.1"))
{
LLVivoxVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString);