PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llvoicevivox.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2376 lines | 1661 code | 397 blank | 318 comment | 208 complexity | bf3d28757257273bd32b19557d17e31c MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file LLVivoxVoiceClient.cpp
  3. * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process.
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llvoicevivox.h"
  28. #include <boost/tokenizer.hpp>
  29. #include "llsdutil.h"
  30. // Linden library includes
  31. #include "llavatarnamecache.h"
  32. #include "llvoavatarself.h"
  33. #include "llbufferstream.h"
  34. #include "llfile.h"
  35. #ifdef LL_STANDALONE
  36. # include "expat.h"
  37. #else
  38. # include "expat/expat.h"
  39. #endif
  40. #include "llcallbacklist.h"
  41. #include "llviewerregion.h"
  42. #include "llviewernetwork.h" // for gGridChoice
  43. #include "llbase64.h"
  44. #include "llviewercontrol.h"
  45. #include "llappviewer.h" // for gDisconnected, gDisableVoice
  46. // Viewer includes
  47. #include "llmutelist.h" // to check for muted avatars
  48. #include "llagent.h"
  49. #include "llcachename.h"
  50. #include "llimview.h" // for LLIMMgr
  51. #include "llparcel.h"
  52. #include "llviewerparcelmgr.h"
  53. #include "llfirstuse.h"
  54. #include "llspeakers.h"
  55. #include "lltrans.h"
  56. #include "llviewerwindow.h"
  57. #include "llviewercamera.h"
  58. #include "llviewernetwork.h"
  59. #include "llnotificationsutil.h"
  60. #include "stringize.h"
  61. // for base64 decoding
  62. #include "apr_base64.h"
  63. #define USE_SESSION_GROUPS 0
  64. const F32 VOLUME_SCALE_VIVOX = 0.01f;
  65. const F32 SPEAKING_TIMEOUT = 1.f;
  66. static const std::string VOICE_SERVER_TYPE = "Vivox";
  67. // Don't retry connecting to the daemon more frequently than this:
  68. const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
  69. // Don't send positional updates more frequently than this:
  70. const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
  71. const F32 LOGIN_RETRY_SECONDS = 10.0f;
  72. const int MAX_LOGIN_RETRIES = 12;
  73. // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine()
  74. // which is treated as normal. If this number is exceeded we suspect there is a problem with connection
  75. // to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen
  76. // to make sure we don't make mistake when slight connection problems happen- situation when connection to server is
  77. // blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability.
  78. const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50;
  79. // How often to check for expired voice fonts in seconds
  80. const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f;
  81. // Time of day at which Vivox expires voice font subscriptions.
  82. // Used to replace the time portion of received expiry timestamps.
  83. static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z";
  84. // Maximum length of capture buffer recordings in seconds.
  85. const F32 CAPTURE_BUFFER_MAX_TIME = 10.f;
  86. static int scale_mic_volume(float volume)
  87. {
  88. // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
  89. // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70
  90. return 30 + (int)(volume * 20.0f);
  91. }
  92. static int scale_speaker_volume(float volume)
  93. {
  94. // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
  95. // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70
  96. return 30 + (int)(volume * 40.0f);
  97. }
  98. class LLVivoxVoiceAccountProvisionResponder :
  99. public LLHTTPClient::Responder
  100. {
  101. public:
  102. LLVivoxVoiceAccountProvisionResponder(int retries)
  103. {
  104. mRetries = retries;
  105. }
  106. virtual void error(U32 status, const std::string& reason)
  107. {
  108. if ( mRetries > 0 )
  109. {
  110. LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying. status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
  111. LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision(
  112. mRetries - 1);
  113. }
  114. else
  115. {
  116. LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up). status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
  117. LLVivoxVoiceClient::getInstance()->giveUp();
  118. }
  119. }
  120. virtual void result(const LLSD& content)
  121. {
  122. std::string voice_sip_uri_hostname;
  123. std::string voice_account_server_uri;
  124. LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
  125. if(content.has("voice_sip_uri_hostname"))
  126. voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
  127. // this key is actually misnamed -- it will be an entire URI, not just a hostname.
  128. if(content.has("voice_account_server_name"))
  129. voice_account_server_uri = content["voice_account_server_name"].asString();
  130. LLVivoxVoiceClient::getInstance()->login(
  131. content["username"].asString(),
  132. content["password"].asString(),
  133. voice_sip_uri_hostname,
  134. voice_account_server_uri);
  135. }
  136. private:
  137. int mRetries;
  138. };
  139. ///////////////////////////////////////////////////////////////////////////////////////////////
  140. class LLVivoxVoiceClientMuteListObserver : public LLMuteListObserver
  141. {
  142. /* virtual */ void onChange() { LLVivoxVoiceClient::getInstance()->muteListChanged();}
  143. };
  144. class LLVivoxVoiceClientFriendsObserver : public LLFriendObserver
  145. {
  146. public:
  147. /* virtual */ void changed(U32 mask) { LLVivoxVoiceClient::getInstance()->updateFriends(mask);}
  148. };
  149. static LLVivoxVoiceClientMuteListObserver mutelist_listener;
  150. static bool sMuteListListener_listening = false;
  151. static LLVivoxVoiceClientFriendsObserver *friendslist_listener = NULL;
  152. ///////////////////////////////////////////////////////////////////////////////////////////////
  153. class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder
  154. {
  155. public:
  156. LLVivoxVoiceClientCapResponder(LLVivoxVoiceClient::state requesting_state) : mRequestingState(requesting_state) {};
  157. virtual void error(U32 status, const std::string& reason); // called with bad status codes
  158. virtual void result(const LLSD& content);
  159. private:
  160. LLVivoxVoiceClient::state mRequestingState; // state
  161. };
  162. void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason)
  163. {
  164. LL_WARNS("Voice") << "LLVivoxVoiceClientCapResponder::error("
  165. << status << ": " << reason << ")"
  166. << LL_ENDL;
  167. LLVivoxVoiceClient::getInstance()->sessionTerminate();
  168. }
  169. void LLVivoxVoiceClientCapResponder::result(const LLSD& content)
  170. {
  171. LLSD::map_const_iterator iter;
  172. LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
  173. std::string uri;
  174. std::string credentials;
  175. if ( content.has("voice_credentials") )
  176. {
  177. LLSD voice_credentials = content["voice_credentials"];
  178. if ( voice_credentials.has("channel_uri") )
  179. {
  180. uri = voice_credentials["channel_uri"].asString();
  181. }
  182. if ( voice_credentials.has("channel_credentials") )
  183. {
  184. credentials =
  185. voice_credentials["channel_credentials"].asString();
  186. }
  187. }
  188. // set the spatial channel. If no voice credentials or uri are
  189. // available, then we simply drop out of voice spatially.
  190. if(LLVivoxVoiceClient::getInstance()->parcelVoiceInfoReceived(mRequestingState))
  191. {
  192. LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials);
  193. }
  194. }
  195. #if LL_WINDOWS
  196. static HANDLE sGatewayHandle = 0;
  197. static bool isGatewayRunning()
  198. {
  199. bool result = false;
  200. if(sGatewayHandle != 0)
  201. {
  202. DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
  203. if(waitresult != WAIT_OBJECT_0)
  204. {
  205. result = true;
  206. }
  207. }
  208. return result;
  209. }
  210. static void killGateway()
  211. {
  212. if(sGatewayHandle != 0)
  213. {
  214. TerminateProcess(sGatewayHandle,0);
  215. }
  216. }
  217. #else // Mac and linux
  218. static pid_t sGatewayPID = 0;
  219. static bool isGatewayRunning()
  220. {
  221. bool result = false;
  222. if(sGatewayPID != 0)
  223. {
  224. // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists.
  225. if(kill(sGatewayPID, 0) == 0)
  226. {
  227. result = true;
  228. }
  229. }
  230. return result;
  231. }
  232. static void killGateway()
  233. {
  234. if(sGatewayPID != 0)
  235. {
  236. kill(sGatewayPID, SIGTERM);
  237. }
  238. }
  239. #endif
  240. ///////////////////////////////////////////////////////////////////////////////////////////////
  241. LLVivoxVoiceClient::LLVivoxVoiceClient() :
  242. mState(stateDisabled),
  243. mSessionTerminateRequested(false),
  244. mRelogRequested(false),
  245. mConnected(false),
  246. mPump(NULL),
  247. mSpatialJoiningNum(0),
  248. mTuningMode(false),
  249. mTuningEnergy(0.0f),
  250. mTuningMicVolume(0),
  251. mTuningMicVolumeDirty(true),
  252. mTuningSpeakerVolume(0),
  253. mTuningSpeakerVolumeDirty(true),
  254. mTuningExitState(stateDisabled),
  255. mAreaVoiceDisabled(false),
  256. mAudioSession(NULL),
  257. mAudioSessionChanged(false),
  258. mNextAudioSession(NULL),
  259. mCurrentParcelLocalID(0),
  260. mNumberOfAliases(0),
  261. mCommandCookie(0),
  262. mLoginRetryCount(0),
  263. mBuddyListMapPopulated(false),
  264. mBlockRulesListReceived(false),
  265. mAutoAcceptRulesListReceived(false),
  266. mCaptureDeviceDirty(false),
  267. mRenderDeviceDirty(false),
  268. mSpatialCoordsDirty(false),
  269. mMuteMic(false),
  270. mMuteMicDirty(false),
  271. mFriendsListDirty(true),
  272. mEarLocation(0),
  273. mSpeakerVolumeDirty(true),
  274. mSpeakerMuteDirty(true),
  275. mMicVolume(0),
  276. mMicVolumeDirty(true),
  277. mVoiceEnabled(false),
  278. mWriteInProgress(false),
  279. mLipSyncEnabled(false),
  280. mVoiceFontsReceived(false),
  281. mVoiceFontsNew(false),
  282. mVoiceFontListDirty(false),
  283. mCaptureBufferMode(false),
  284. mCaptureBufferRecording(false),
  285. mCaptureBufferRecorded(false),
  286. mCaptureBufferPlaying(false),
  287. mPlayRequestCount(0)
  288. {
  289. mSpeakerVolume = scale_speaker_volume(0);
  290. mVoiceVersion.serverVersion = "";
  291. mVoiceVersion.serverType = VOICE_SERVER_TYPE;
  292. // gMuteListp isn't set up at this point, so we defer this until later.
  293. // gMuteListp->addObserver(&mutelist_listener);
  294. #if LL_DARWIN || LL_LINUX || LL_SOLARIS
  295. // HACK: THIS DOES NOT BELONG HERE
  296. // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
  297. // This should cause us to ignore SIGPIPE and handle the error through proper channels.
  298. // This should really be set up elsewhere. Where should it go?
  299. signal(SIGPIPE, SIG_IGN);
  300. // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
  301. // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that.
  302. signal(SIGCHLD, SIG_IGN);
  303. #endif
  304. // set up state machine
  305. setState(stateDisabled);
  306. gIdleCallbacks.addFunction(idle, this);
  307. }
  308. //---------------------------------------------------
  309. LLVivoxVoiceClient::~LLVivoxVoiceClient()
  310. {
  311. }
  312. //---------------------------------------------------
  313. void LLVivoxVoiceClient::init(LLPumpIO *pump)
  314. {
  315. // constructor will set up LLVoiceClient::getInstance()
  316. LLVivoxVoiceClient::getInstance()->mPump = pump;
  317. }
  318. void LLVivoxVoiceClient::terminate()
  319. {
  320. if(mConnected)
  321. {
  322. logout();
  323. connectorShutdown();
  324. closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
  325. cleanUp();
  326. }
  327. else
  328. {
  329. killGateway();
  330. }
  331. }
  332. //---------------------------------------------------
  333. void LLVivoxVoiceClient::cleanUp()
  334. {
  335. deleteAllSessions();
  336. deleteAllBuddies();
  337. deleteAllVoiceFonts();
  338. deleteVoiceFontTemplates();
  339. }
  340. //---------------------------------------------------
  341. const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion()
  342. {
  343. return mVoiceVersion;
  344. }
  345. //---------------------------------------------------
  346. void LLVivoxVoiceClient::updateSettings()
  347. {
  348. setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat"));
  349. setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
  350. std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
  351. setCaptureDevice(inputDevice);
  352. std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
  353. setRenderDevice(outputDevice);
  354. F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
  355. setMicGain(mic_level);
  356. setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled"));
  357. }
  358. /////////////////////////////
  359. // utility functions
  360. bool LLVivoxVoiceClient::writeString(const std::string &str)
  361. {
  362. bool result = false;
  363. if(mConnected)
  364. {
  365. apr_status_t err;
  366. apr_size_t size = (apr_size_t)str.size();
  367. apr_size_t written = size;
  368. //MARK: Turn this on to log outgoing XML
  369. // LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
  370. // check return code - sockets will fail (broken, etc.)
  371. err = apr_socket_send(
  372. mSocket->getSocket(),
  373. (const char*)str.data(),
  374. &written);
  375. if(err == 0)
  376. {
  377. // Success.
  378. result = true;
  379. }
  380. // TODO: handle partial writes (written is number of bytes written)
  381. // Need to set socket to non-blocking before this will work.
  382. // else if(APR_STATUS_IS_EAGAIN(err))
  383. // {
  384. // //
  385. // }
  386. else
  387. {
  388. // Assume any socket error means something bad. For now, just close the socket.
  389. char buf[MAX_STRING];
  390. LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
  391. daemonDied();
  392. }
  393. }
  394. return result;
  395. }
  396. /////////////////////////////
  397. // session control messages
  398. void LLVivoxVoiceClient::connectorCreate()
  399. {
  400. std::ostringstream stream;
  401. std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
  402. std::string loglevel = "0";
  403. // Transition to stateConnectorStarted when the connector handle comes back.
  404. setState(stateConnectorStarting);
  405. std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
  406. if(savedLogLevel != "-1")
  407. {
  408. LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
  409. loglevel = "10";
  410. }
  411. stream
  412. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
  413. << "<ClientName>V2 SDK</ClientName>"
  414. << "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
  415. << "<Mode>Normal</Mode>"
  416. << "<Logging>"
  417. << "<Folder>" << logpath << "</Folder>"
  418. << "<FileNamePrefix>Connector</FileNamePrefix>"
  419. << "<FileNameSuffix>.log</FileNameSuffix>"
  420. << "<LogLevel>" << loglevel << "</LogLevel>"
  421. << "</Logging>"
  422. << "<Application>SecondLifeViewer.1</Application>"
  423. << "</Request>\n\n\n";
  424. writeString(stream.str());
  425. }
  426. void LLVivoxVoiceClient::connectorShutdown()
  427. {
  428. setState(stateConnectorStopping);
  429. if(!mConnectorHandle.empty())
  430. {
  431. std::ostringstream stream;
  432. stream
  433. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">"
  434. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  435. << "</Request>"
  436. << "\n\n\n";
  437. mConnectorHandle.clear();
  438. writeString(stream.str());
  439. }
  440. }
  441. void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID)
  442. {
  443. mAccountDisplayName = user_id;
  444. LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL;
  445. mAccountName = nameFromID(agentID);
  446. }
  447. void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
  448. {
  449. LLViewerRegion *region = gAgent.getRegion();
  450. if ( region && mVoiceEnabled )
  451. {
  452. std::string url =
  453. region->getCapability("ProvisionVoiceAccountRequest");
  454. if ( url.empty() )
  455. {
  456. // we've not received the capability yet, so return.
  457. // the password will remain empty, so we'll remain in
  458. // stateIdle
  459. return;
  460. }
  461. LLHTTPClient::post(
  462. url,
  463. LLSD(),
  464. new LLVivoxVoiceAccountProvisionResponder(retries));
  465. setState(stateConnectorStart);
  466. }
  467. }
  468. void LLVivoxVoiceClient::login(
  469. const std::string& account_name,
  470. const std::string& password,
  471. const std::string& voice_sip_uri_hostname,
  472. const std::string& voice_account_server_uri)
  473. {
  474. mVoiceSIPURIHostName = voice_sip_uri_hostname;
  475. mVoiceAccountServerURI = voice_account_server_uri;
  476. if(!mAccountHandle.empty())
  477. {
  478. // Already logged in.
  479. LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL;
  480. // Don't process another login.
  481. return;
  482. }
  483. else if ( account_name != mAccountName )
  484. {
  485. //TODO: error?
  486. LL_WARNS("Voice") << "Wrong account name! " << account_name
  487. << " instead of " << mAccountName << LL_ENDL;
  488. }
  489. else
  490. {
  491. mAccountPassword = password;
  492. }
  493. std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
  494. if( !debugSIPURIHostName.empty() )
  495. {
  496. mVoiceSIPURIHostName = debugSIPURIHostName;
  497. }
  498. if( mVoiceSIPURIHostName.empty() )
  499. {
  500. // we have an empty account server name
  501. // so we fall back to hardcoded defaults
  502. if(LLGridManager::getInstance()->isInProductionGrid())
  503. {
  504. // Use the release account server
  505. mVoiceSIPURIHostName = "bhr.vivox.com";
  506. }
  507. else
  508. {
  509. // Use the development account server
  510. mVoiceSIPURIHostName = "bhd.vivox.com";
  511. }
  512. }
  513. std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
  514. if( !debugAccountServerURI.empty() )
  515. {
  516. mVoiceAccountServerURI = debugAccountServerURI;
  517. }
  518. if( mVoiceAccountServerURI.empty() )
  519. {
  520. // If the account server URI isn't specified, construct it from the SIP URI hostname
  521. mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";
  522. }
  523. }
  524. void LLVivoxVoiceClient::idle(void* user_data)
  525. {
  526. LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data;
  527. self->stateMachine();
  528. }
  529. std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState)
  530. {
  531. std::string result = "UNKNOWN";
  532. // Prevent copy-paste errors when updating this list...
  533. #define CASE(x) case x: result = #x; break
  534. switch(inState)
  535. {
  536. CASE(stateDisableCleanup);
  537. CASE(stateDisabled);
  538. CASE(stateStart);
  539. CASE(stateDaemonLaunched);
  540. CASE(stateConnecting);
  541. CASE(stateConnected);
  542. CASE(stateIdle);
  543. CASE(stateMicTuningStart);
  544. CASE(stateMicTuningRunning);
  545. CASE(stateMicTuningStop);
  546. CASE(stateCaptureBufferPaused);
  547. CASE(stateCaptureBufferRecStart);
  548. CASE(stateCaptureBufferRecording);
  549. CASE(stateCaptureBufferPlayStart);
  550. CASE(stateCaptureBufferPlaying);
  551. CASE(stateConnectorStart);
  552. CASE(stateConnectorStarting);
  553. CASE(stateConnectorStarted);
  554. CASE(stateLoginRetry);
  555. CASE(stateLoginRetryWait);
  556. CASE(stateNeedsLogin);
  557. CASE(stateLoggingIn);
  558. CASE(stateLoggedIn);
  559. CASE(stateVoiceFontsWait);
  560. CASE(stateVoiceFontsReceived);
  561. CASE(stateCreatingSessionGroup);
  562. CASE(stateNoChannel);
  563. CASE(stateRetrievingParcelVoiceInfo);
  564. CASE(stateJoiningSession);
  565. CASE(stateSessionJoined);
  566. CASE(stateRunning);
  567. CASE(stateLeavingSession);
  568. CASE(stateSessionTerminated);
  569. CASE(stateLoggingOut);
  570. CASE(stateLoggedOut);
  571. CASE(stateConnectorStopping);
  572. CASE(stateConnectorStopped);
  573. CASE(stateConnectorFailed);
  574. CASE(stateConnectorFailedWaiting);
  575. CASE(stateLoginFailed);
  576. CASE(stateLoginFailedWaiting);
  577. CASE(stateJoinSessionFailed);
  578. CASE(stateJoinSessionFailedWaiting);
  579. CASE(stateJail);
  580. }
  581. #undef CASE
  582. return result;
  583. }
  584. void LLVivoxVoiceClient::setState(state inState)
  585. {
  586. LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL;
  587. mState = inState;
  588. }
  589. void LLVivoxVoiceClient::stateMachine()
  590. {
  591. if(gDisconnected)
  592. {
  593. // The viewer has been disconnected from the sim. Disable voice.
  594. setVoiceEnabled(false);
  595. }
  596. if(mVoiceEnabled)
  597. {
  598. updatePosition();
  599. }
  600. else if(mTuningMode)
  601. {
  602. // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled.
  603. }
  604. else
  605. {
  606. if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
  607. {
  608. // User turned off voice support. Send the cleanup messages, close the socket, and reset.
  609. if(!mConnected)
  610. {
  611. // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
  612. LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
  613. killGateway();
  614. }
  615. logout();
  616. connectorShutdown();
  617. setState(stateDisableCleanup);
  618. }
  619. }
  620. switch(getState())
  621. {
  622. //MARK: stateDisableCleanup
  623. case stateDisableCleanup:
  624. // Clean up and reset everything.
  625. closeSocket();
  626. cleanUp();
  627. mAccountHandle.clear();
  628. mAccountPassword.clear();
  629. mVoiceAccountServerURI.clear();
  630. setState(stateDisabled);
  631. break;
  632. //MARK: stateDisabled
  633. case stateDisabled:
  634. if(mTuningMode || (mVoiceEnabled && !mAccountName.empty()))
  635. {
  636. setState(stateStart);
  637. }
  638. break;
  639. //MARK: stateStart
  640. case stateStart:
  641. if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
  642. {
  643. // Voice is locked out, we must not launch the vivox daemon.
  644. setState(stateJail);
  645. }
  646. else if(!isGatewayRunning())
  647. {
  648. if(true)
  649. {
  650. // Launch the voice daemon
  651. // *FIX:Mani - Using the executable dir instead
  652. // of mAppRODataDir, the working directory from which the app
  653. // is launched.
  654. //std::string exe_path = gDirUtilp->getAppRODataDir();
  655. std::string exe_path = gDirUtilp->getExecutableDir();
  656. exe_path += gDirUtilp->getDirDelimiter();
  657. #if LL_WINDOWS
  658. exe_path += "SLVoice.exe";
  659. #elif LL_DARWIN
  660. exe_path += "../Resources/SLVoice";
  661. #else
  662. exe_path += "SLVoice";
  663. #endif
  664. // See if the vivox executable exists
  665. llstat s;
  666. if(!LLFile::stat(exe_path, &s))
  667. {
  668. // vivox executable exists. Build the command line and launch the daemon.
  669. // SLIM SDK: these arguments are no longer necessary.
  670. // std::string args = " -p tcp -h -c";
  671. std::string args;
  672. std::string cmd;
  673. std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
  674. if(loglevel.empty())
  675. {
  676. loglevel = "-1"; // turn logging off completely
  677. }
  678. args += " -ll ";
  679. args += loglevel;
  680. LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL;
  681. #if LL_WINDOWS
  682. PROCESS_INFORMATION pinfo;
  683. STARTUPINFOA sinfo;
  684. memset(&sinfo, 0, sizeof(sinfo));
  685. std::string exe_dir = gDirUtilp->getAppRODataDir();
  686. cmd = "SLVoice.exe";
  687. cmd += args;
  688. // So retarded. Windows requires that the second parameter to CreateProcessA be writable (non-const) string...
  689. char *args2 = new char[args.size() + 1];
  690. strcpy(args2, args.c_str());
  691. if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo))
  692. {
  693. // DWORD dwErr = GetLastError();
  694. }
  695. else
  696. {
  697. // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
  698. // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
  699. sGatewayHandle = pinfo.hProcess;
  700. CloseHandle(pinfo.hThread); // stops leaks - nothing else
  701. }
  702. delete[] args2;
  703. #else // LL_WINDOWS
  704. // This should be the same for mac and linux
  705. {
  706. std::vector<std::string> arglist;
  707. arglist.push_back(exe_path);
  708. // Split the argument string into separate strings for each argument
  709. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  710. boost::char_separator<char> sep(" ");
  711. tokenizer tokens(args, sep);
  712. tokenizer::iterator token_iter;
  713. for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  714. {
  715. arglist.push_back(*token_iter);
  716. }
  717. // create an argv vector for the child process
  718. char **fakeargv = new char*[arglist.size() + 1];
  719. int i;
  720. for(i=0; i < arglist.size(); i++)
  721. fakeargv[i] = const_cast<char*>(arglist[i].c_str());
  722. fakeargv[i] = NULL;
  723. fflush(NULL); // flush all buffers before the child inherits them
  724. pid_t id = vfork();
  725. if(id == 0)
  726. {
  727. // child
  728. execv(exe_path.c_str(), fakeargv);
  729. // If we reach this point, the exec failed.
  730. // Use _exit() instead of exit() per the vfork man page.
  731. _exit(0);
  732. }
  733. // parent
  734. delete[] fakeargv;
  735. sGatewayPID = id;
  736. }
  737. #endif // LL_WINDOWS
  738. mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
  739. }
  740. else
  741. {
  742. LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
  743. }
  744. }
  745. else
  746. {
  747. // SLIM SDK: port changed from 44124 to 44125.
  748. // We can connect to a client gateway running on another host. This is useful for testing.
  749. // To do this, launch the gateway on a nearby host like this:
  750. // vivox-gw.exe -p tcp -i 0.0.0.0:44125
  751. // and put that host's IP address here.
  752. mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
  753. }
  754. mUpdateTimer.start();
  755. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  756. setState(stateDaemonLaunched);
  757. // Dirty the states we'll need to sync with the daemon when it comes up.
  758. mMuteMicDirty = true;
  759. mMicVolumeDirty = true;
  760. mSpeakerVolumeDirty = true;
  761. mSpeakerMuteDirty = true;
  762. // These only need to be set if they're not default (i.e. empty string).
  763. mCaptureDeviceDirty = !mCaptureDevice.empty();
  764. mRenderDeviceDirty = !mRenderDevice.empty();
  765. mMainSessionGroupHandle.clear();
  766. }
  767. break;
  768. //MARK: stateDaemonLaunched
  769. case stateDaemonLaunched:
  770. if(mUpdateTimer.hasExpired())
  771. {
  772. LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
  773. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  774. if(!mSocket)
  775. {
  776. mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
  777. }
  778. mConnected = mSocket->blockingConnect(mDaemonHost);
  779. if(mConnected)
  780. {
  781. setState(stateConnecting);
  782. }
  783. else
  784. {
  785. // If the connect failed, the socket may have been put into a bad state. Delete it.
  786. closeSocket();
  787. }
  788. }
  789. break;
  790. //MARK: stateConnecting
  791. case stateConnecting:
  792. // Can't do this until we have the pump available.
  793. if(mPump)
  794. {
  795. // MBW -- Note to self: pumps and pipes examples in
  796. // indra/test/io.cpp
  797. // indra/test/llpipeutil.{cpp|h}
  798. // Attach the pumps and pipes
  799. LLPumpIO::chain_t readChain;
  800. readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
  801. readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
  802. mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
  803. setState(stateConnected);
  804. }
  805. break;
  806. //MARK: stateConnected
  807. case stateConnected:
  808. // Initial devices query
  809. getCaptureDevicesSendMessage();
  810. getRenderDevicesSendMessage();
  811. mLoginRetryCount = 0;
  812. setState(stateIdle);
  813. break;
  814. //MARK: stateIdle
  815. case stateIdle:
  816. // This is the idle state where we're connected to the daemon but haven't set up a connector yet.
  817. if(mTuningMode)
  818. {
  819. mTuningExitState = stateIdle;
  820. setState(stateMicTuningStart);
  821. }
  822. else if(!mVoiceEnabled)
  823. {
  824. // We never started up the connector. This will shut down the daemon.
  825. setState(stateConnectorStopped);
  826. }
  827. else if(!mAccountName.empty())
  828. {
  829. if ( mAccountPassword.empty() )
  830. {
  831. requestVoiceAccountProvision();
  832. }
  833. }
  834. break;
  835. //MARK: stateMicTuningStart
  836. case stateMicTuningStart:
  837. if(mUpdateTimer.hasExpired())
  838. {
  839. if(mCaptureDeviceDirty || mRenderDeviceDirty)
  840. {
  841. // These can't be changed while in tuning mode. Set them before starting.
  842. std::ostringstream stream;
  843. buildSetCaptureDevice(stream);
  844. buildSetRenderDevice(stream);
  845. if(!stream.str().empty())
  846. {
  847. writeString(stream.str());
  848. }
  849. // This will come around again in the same state and start the capture, after the timer expires.
  850. mUpdateTimer.start();
  851. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  852. }
  853. else
  854. {
  855. // duration parameter is currently unused, per Mike S.
  856. tuningCaptureStartSendMessage(10000);
  857. setState(stateMicTuningRunning);
  858. }
  859. }
  860. break;
  861. //MARK: stateMicTuningRunning
  862. case stateMicTuningRunning:
  863. if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
  864. {
  865. // All of these conditions make us leave tuning mode.
  866. setState(stateMicTuningStop);
  867. }
  868. else
  869. {
  870. // process mic/speaker volume changes
  871. if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
  872. {
  873. std::ostringstream stream;
  874. if(mTuningMicVolumeDirty)
  875. {
  876. LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
  877. stream
  878. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
  879. << "<Level>" << mTuningMicVolume << "</Level>"
  880. << "</Request>\n\n\n";
  881. }
  882. if(mTuningSpeakerVolumeDirty)
  883. {
  884. stream
  885. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
  886. << "<Level>" << mTuningSpeakerVolume << "</Level>"
  887. << "</Request>\n\n\n";
  888. }
  889. mTuningMicVolumeDirty = false;
  890. mTuningSpeakerVolumeDirty = false;
  891. if(!stream.str().empty())
  892. {
  893. writeString(stream.str());
  894. }
  895. }
  896. }
  897. break;
  898. //MARK: stateMicTuningStop
  899. case stateMicTuningStop:
  900. {
  901. // transition out of mic tuning
  902. tuningCaptureStopSendMessage();
  903. setState(mTuningExitState);
  904. // if we exited just to change devices, this will keep us from re-entering too fast.
  905. mUpdateTimer.start();
  906. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  907. }
  908. break;
  909. //MARK: stateCaptureBufferPaused
  910. case stateCaptureBufferPaused:
  911. if (!mCaptureBufferMode)
  912. {
  913. // Leaving capture mode.
  914. mCaptureBufferRecording = false;
  915. mCaptureBufferRecorded = false;
  916. mCaptureBufferPlaying = false;
  917. // Return to stateNoChannel to trigger reconnection to a channel.
  918. setState(stateNoChannel);
  919. }
  920. else if (mCaptureBufferRecording)
  921. {
  922. setState(stateCaptureBufferRecStart);
  923. }
  924. else if (mCaptureBufferPlaying)
  925. {
  926. setState(stateCaptureBufferPlayStart);
  927. }
  928. break;
  929. //MARK: stateCaptureBufferRecStart
  930. case stateCaptureBufferRecStart:
  931. captureBufferRecordStartSendMessage();
  932. // Flag that something is recorded to allow playback.
  933. mCaptureBufferRecorded = true;
  934. // Start the timer, recording will be stopped when it expires.
  935. mCaptureTimer.start();
  936. mCaptureTimer.setTimerExpirySec(CAPTURE_BUFFER_MAX_TIME);
  937. // Update UI, should really use a separate callback.
  938. notifyVoiceFontObservers();
  939. setState(stateCaptureBufferRecording);
  940. break;
  941. //MARK: stateCaptureBufferRecording
  942. case stateCaptureBufferRecording:
  943. if (!mCaptureBufferMode || !mCaptureBufferRecording ||
  944. mCaptureBufferPlaying || mCaptureTimer.hasExpired())
  945. {
  946. // Stop recording
  947. captureBufferRecordStopSendMessage();
  948. mCaptureBufferRecording = false;
  949. // Update UI, should really use a separate callback.
  950. notifyVoiceFontObservers();
  951. setState(stateCaptureBufferPaused);
  952. }
  953. break;
  954. //MARK: stateCaptureBufferPlayStart
  955. case stateCaptureBufferPlayStart:
  956. captureBufferPlayStartSendMessage(mPreviewVoiceFont);
  957. // Store the voice font being previewed, so that we know to restart if it changes.
  958. mPreviewVoiceFontLast = mPreviewVoiceFont;
  959. // Update UI, should really use a separate callback.
  960. notifyVoiceFontObservers();
  961. setState(stateCaptureBufferPlaying);
  962. break;
  963. //MARK: stateCaptureBufferPlaying
  964. case stateCaptureBufferPlaying:
  965. if (mCaptureBufferPlaying && mPreviewVoiceFont != mPreviewVoiceFontLast)
  966. {
  967. // If the preview voice font changes, restart playing with the new font.
  968. setState(stateCaptureBufferPlayStart);
  969. }
  970. else if (!mCaptureBufferMode || !mCaptureBufferPlaying || mCaptureBufferRecording)
  971. {
  972. // Stop playing.
  973. captureBufferPlayStopSendMessage();
  974. mCaptureBufferPlaying = false;
  975. // Update UI, should really use a separate callback.
  976. notifyVoiceFontObservers();
  977. setState(stateCaptureBufferPaused);
  978. }
  979. break;
  980. //MARK: stateConnectorStart
  981. case stateConnectorStart:
  982. if(!mVoiceEnabled)
  983. {
  984. // We were never logged in. This will shut down the connector.
  985. setState(stateLoggedOut);
  986. }
  987. else if(!mVoiceAccountServerURI.empty())
  988. {
  989. connectorCreate();
  990. }
  991. break;
  992. //MARK: stateConnectorStarting
  993. case stateConnectorStarting: // waiting for connector handle
  994. // connectorCreateResponse() will transition from here to stateConnectorStarted.
  995. break;
  996. //MARK: stateConnectorStarted
  997. case stateConnectorStarted: // connector handle received
  998. if(!mVoiceEnabled)
  999. {
  1000. // We were never logged in. This will shut down the connector.
  1001. setState(stateLoggedOut);
  1002. }
  1003. else
  1004. {
  1005. // The connector is started. Send a login message.
  1006. setState(stateNeedsLogin);
  1007. }
  1008. break;
  1009. //MARK: stateLoginRetry
  1010. case stateLoginRetry:
  1011. if(mLoginRetryCount == 0)
  1012. {
  1013. // First retry -- display a message to the user
  1014. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
  1015. }
  1016. mLoginRetryCount++;
  1017. if(mLoginRetryCount > MAX_LOGIN_RETRIES)
  1018. {
  1019. LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
  1020. setState(stateLoginFailed);
  1021. LLSD args;
  1022. std::stringstream errs;
  1023. errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
  1024. args["HOSTID"] = errs.str();
  1025. if (LLGridManager::getInstance()->isSystemGrid())
  1026. {
  1027. LLNotificationsUtil::add("NoVoiceConnect", args);
  1028. }
  1029. else
  1030. {
  1031. LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
  1032. }
  1033. }
  1034. else
  1035. {
  1036. LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
  1037. mUpdateTimer.start();
  1038. mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
  1039. setState(stateLoginRetryWait);
  1040. }
  1041. break;
  1042. //MARK: stateLoginRetryWait
  1043. case stateLoginRetryWait:
  1044. if(mUpdateTimer.hasExpired())
  1045. {
  1046. setState(stateNeedsLogin);
  1047. }
  1048. break;
  1049. //MARK: stateNeedsLogin
  1050. case stateNeedsLogin:
  1051. if(!mAccountPassword.empty())
  1052. {
  1053. setState(stateLoggingIn);
  1054. loginSendMessage();
  1055. }
  1056. break;
  1057. //MARK: stateLoggingIn
  1058. case stateLoggingIn: // waiting for account handle
  1059. // loginResponse() will transition from here to stateLoggedIn.
  1060. break;
  1061. //MARK: stateLoggedIn
  1062. case stateLoggedIn: // account handle received
  1063. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
  1064. if (LLVoiceClient::instance().getVoiceEffectEnabled())
  1065. {
  1066. // Request the set of available voice fonts.
  1067. setState(stateVoiceFontsWait);
  1068. refreshVoiceEffectLists(true);
  1069. }
  1070. else
  1071. {
  1072. // If voice effects are disabled, pretend we've received them and carry on.
  1073. setState(stateVoiceFontsReceived);
  1074. }
  1075. // request the current set of block rules (we'll need them when updating the friends list)
  1076. accountListBlockRulesSendMessage();
  1077. // request the current set of auto-accept rules
  1078. accountListAutoAcceptRulesSendMessage();
  1079. // Set up the mute list observer if it hasn't been set up already.
  1080. if((!sMuteListListener_listening))
  1081. {
  1082. LLMuteList::getInstance()->addObserver(&mutelist_listener);
  1083. sMuteListListener_listening = true;
  1084. }
  1085. // Set up the friends list observer if it hasn't been set up already.
  1086. if(friendslist_listener == NULL)
  1087. {
  1088. friendslist_listener = new LLVivoxVoiceClientFriendsObserver;
  1089. LLAvatarTracker::instance().addObserver(friendslist_listener);
  1090. }
  1091. // Set the initial state of mic mute, local speaker volume, etc.
  1092. {
  1093. std::ostringstream stream;
  1094. buildLocalAudioUpdates(stream);
  1095. if(!stream.str().empty())
  1096. {
  1097. writeString(stream.str());
  1098. }
  1099. }
  1100. break;
  1101. //MARK: stateVoiceFontsWait
  1102. case stateVoiceFontsWait: // Await voice font list
  1103. // accountGetSessionFontsResponse() will transition from here to
  1104. // stateVoiceFontsReceived, to ensure we have the voice font list
  1105. // before attempting to create a session.
  1106. break;
  1107. //MARK: stateVoiceFontsReceived
  1108. case stateVoiceFontsReceived: // Voice font list received
  1109. // Set up the timer to check for expiring voice fonts
  1110. mVoiceFontExpiryTimer.start();
  1111. mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
  1112. #if USE_SESSION_GROUPS
  1113. // create the main session group
  1114. setState(stateCreatingSessionGroup);
  1115. sessionGroupCreateSendMessage();
  1116. #else
  1117. setState(stateNoChannel);
  1118. #endif
  1119. break;
  1120. //MARK: stateCreatingSessionGroup
  1121. case stateCreatingSessionGroup:
  1122. if(mSessionTerminateRequested || !mVoiceEnabled)
  1123. {
  1124. // *TODO: Question: is this the right way out of this state
  1125. setState(stateSessionTerminated);
  1126. }
  1127. else if(!mMainSessionGroupHandle.empty())
  1128. {
  1129. // Start looped recording (needed for "panic button" anti-griefing tool)
  1130. recordingLoopStart();
  1131. setState(stateNoChannel);
  1132. }
  1133. break;
  1134. //MARK: stateRetrievingParcelVoiceInfo
  1135. case stateRetrievingParcelVoiceInfo:
  1136. // wait until parcel voice info is received.
  1137. if(mSessionTerminateRequested || !mVoiceEnabled)
  1138. {
  1139. // if a terminate request has been received,
  1140. // bail and go to the stateSessionTerminated
  1141. // state. If the cap request is still pending,
  1142. // the responder will check to see if we've moved
  1143. // to a new session and won't change any state.
  1144. setState(stateSessionTerminated);
  1145. }
  1146. break;
  1147. //MARK: stateNoChannel
  1148. case stateNoChannel:
  1149. LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL;
  1150. mSpatialJoiningNum = 0;
  1151. // Do this here as well as inside sendPositionalUpdate().
  1152. // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync.
  1153. sendFriendsListUpdates();
  1154. if(mSessionTerminateRequested || !mVoiceEnabled)
  1155. {
  1156. // TODO: Question: Is this the right way out of this state?
  1157. setState(stateSessionTerminated);
  1158. }
  1159. else if(mTuningMode)
  1160. {
  1161. mTuningExitState = stateNoChannel;
  1162. setState(stateMicTuningStart);
  1163. }
  1164. else if(mCaptureBufferMode)
  1165. {
  1166. setState(stateCaptureBufferPaused);
  1167. }
  1168. else if(checkParcelChanged() || (mNextAudioSession == NULL))
  1169. {
  1170. // the parcel is changed, or we have no pending audio sessions,
  1171. // so try to request the parcel voice info
  1172. // if we have the cap, we move to the appropriate state
  1173. if(requestParcelVoiceInfo())
  1174. {
  1175. setState(stateRetrievingParcelVoiceInfo);
  1176. }
  1177. }
  1178. else if(sessionNeedsRelog(mNextAudioSession))
  1179. {
  1180. requestRelog();
  1181. setState(stateSessionTerminated);
  1182. }
  1183. else if(mNextAudioSession)
  1184. {
  1185. sessionState *oldSession = mAudioSession;
  1186. mAudioSession = mNextAudioSession;
  1187. mAudioSessionChanged = true;
  1188. if(!mAudioSession->mReconnect)
  1189. {
  1190. mNextAudioSession = NULL;
  1191. }
  1192. // The old session may now need to be deleted.
  1193. reapSession(oldSession);
  1194. if(!mAudioSession->mHandle.empty())
  1195. {
  1196. // Connect to a session by session handle
  1197. sessionMediaConnectSendMessage(mAudioSession);
  1198. }
  1199. else
  1200. {
  1201. // Connect to a session by URI
  1202. sessionCreateSendMessage(mAudioSession, true, false);
  1203. }
  1204. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
  1205. setState(stateJoiningSession);
  1206. }
  1207. break;
  1208. //MARK: stateJoiningSession
  1209. case stateJoiningSession: // waiting for session handle
  1210. // If this is true we have problem with connection to voice server (EXT-4313).
  1211. // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM.
  1212. if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
  1213. {
  1214. // Notify observers to let them know there is problem with voice
  1215. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
  1216. llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl;
  1217. }
  1218. // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
  1219. // example for p2p many times while waiting for response, so it can't be used to detect errors
  1220. if(mAudioSession && mAudioSession->mIsSpatial)
  1221. {
  1222. mSpatialJoiningNum++;
  1223. }
  1224. // joinedAudioSession() will transition from here to stateSessionJoined.
  1225. if(!mVoiceEnabled)
  1226. {
  1227. // User bailed out during connect -- jump straight to teardown.
  1228. setState(stateSessionTerminated);
  1229. }
  1230. else if(mSessionTerminateRequested)
  1231. {
  1232. if(mAudioSession && !mAudioSession->mHandle.empty())
  1233. {
  1234. // Only allow direct exits from this state in p2p calls (for cancelling an invite).
  1235. // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
  1236. if(mAudioSession->mIsP2P)
  1237. {
  1238. sessionMediaDisconnectSendMessage(mAudioSession);
  1239. setState(stateSessionTerminated);
  1240. }
  1241. }
  1242. }
  1243. break;
  1244. //MARK: stateSessionJoined
  1245. case stateSessionJoined: // session handle received
  1246. mSpatialJoiningNum = 0;
  1247. // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
  1248. // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
  1249. // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
  1250. // This is a cheap way to make sure both have happened before proceeding.
  1251. if(mAudioSession && mAudioSession->mVoiceEnabled)
  1252. {
  1253. // Dirty state that may need to be sync'ed with the daemon.
  1254. mMuteMicDirty = true;
  1255. mSpeakerVolumeDirty = true;
  1256. mSpatialCoordsDirty = true;
  1257. setState(stateRunning);
  1258. // Start the throttle timer
  1259. mUpdateTimer.start();
  1260. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1261. // Events that need to happen when a session is joined could go here.
  1262. // Maybe send initial spatial data?
  1263. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
  1264. }
  1265. else if(!mVoiceEnabled)
  1266. {
  1267. // User bailed out during connect -- jump straight to teardown.
  1268. setState(stateSessionTerminated);
  1269. }
  1270. else if(mSessionTerminateRequested)
  1271. {
  1272. // Only allow direct exits from this state in p2p calls (for cancelling an invite).
  1273. // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
  1274. if(mAudioSession && mAudioSession->mIsP2P)
  1275. {
  1276. sessionMediaDisconnectSendMessage(mAudioSession);
  1277. setState(stateSessionTerminated);
  1278. }
  1279. }
  1280. break;
  1281. //MARK: stateRunning
  1282. case stateRunning: // steady state
  1283. // Disabling voice or disconnect requested.
  1284. if(!mVoiceEnabled || mSessionTerminateRequested)
  1285. {
  1286. leaveAudioSession();
  1287. }
  1288. else
  1289. {
  1290. if(!inSpatialChannel())
  1291. {
  1292. // When in a non-spatial channel, never send positional updates.
  1293. mSpatialCoordsDirty = false;
  1294. }
  1295. else
  1296. {
  1297. if(checkParcelChanged())
  1298. {
  1299. // if the parcel has changed, attempted to request the
  1300. // cap for the parcel voice info. If we can't request it
  1301. // then we don't have the cap URL so we do nothing and will
  1302. // recheck next time around
  1303. if(requestParcelVoiceInfo())
  1304. {
  1305. // we did get the cap, and we made the request,
  1306. // so go wait for the response.
  1307. setState(stateRetrievingParcelVoiceInfo);
  1308. }
  1309. }
  1310. // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
  1311. enforceTether();
  1312. }
  1313. // Do notifications for expiring Voice Fonts.
  1314. if (mVoiceFontExpiryTimer.hasExpired())
  1315. {
  1316. expireVoiceFonts();
  1317. mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
  1318. }
  1319. // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often
  1320. // -- the user can only click so fast) or every 10hz, whichever is sooner.
  1321. // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged.
  1322. if((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty || mUpdateTimer.hasExpired())
  1323. {
  1324. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1325. sendPositionalUpdate();
  1326. }
  1327. }
  1328. break;
  1329. //MARK: stateLeavingSession
  1330. case stateLeavingSession: // waiting for terminate session response
  1331. // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
  1332. break;
  1333. //MARK: stateSessionTerminated
  1334. case stateSessionTerminated:
  1335. // Must do this first, since it uses mAudioSession.
  1336. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
  1337. if(mAudioSession)
  1338. {
  1339. sessionState *oldSession = mAudioSession;
  1340. mAudioSession = NULL;
  1341. // We just notified status observers about this change. Don't do it again.
  1342. mAudioSessionChanged = false;
  1343. // The old session may now need to be deleted.
  1344. reapSession(oldSession);
  1345. }
  1346. else
  1347. {
  1348. LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
  1349. }
  1350. // Always reset the terminate request flag when we get here.
  1351. mSessionTerminateRequested = false;
  1352. if(mVoiceEnabled && !mRelogRequested)
  1353. {
  1354. // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
  1355. setState(stateNoChannel);
  1356. }
  1357. else
  1358. {
  1359. // Shutting down voice, continue with disconnecting.
  1360. logout();
  1361. // The state machine will take it from here
  1362. mRelogRequested = false;
  1363. }
  1364. break;
  1365. //MARK: stateLoggingOut
  1366. case stateLoggingOut: // waiting for logout response
  1367. // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut.
  1368. break;
  1369. //MARK: stateLoggedOut
  1370. case stateLoggedOut: // logout response received
  1371. // Once we're logged out, these things are invalid.
  1372. mAccountHandle.clear();
  1373. cleanUp();
  1374. if(mVoiceEnabled && !mRelogRequested)
  1375. {
  1376. // User was logged out, but wants to be logged in. Send a new login request.
  1377. setState(stateNeedsLogin);
  1378. }
  1379. else
  1380. {
  1381. // shut down the connector
  1382. connectorShutdown();
  1383. }
  1384. break;
  1385. //MARK: stateConnectorStopping
  1386. case stateConnectorStopping: // waiting for connector stop
  1387. // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
  1388. break;
  1389. //MARK: stateConnectorStopped
  1390. case stateConnectorStopped: // connector stop received
  1391. setState(stateDisableCleanup);
  1392. break;
  1393. //MARK: stateConnectorFailed
  1394. case stateConnectorFailed:
  1395. setState(stateConnectorFailedWaiting);
  1396. break;
  1397. //MARK: stateConnectorFailedWaiting
  1398. case stateConnectorFailedWaiting:
  1399. if(!mVoiceEnabled)
  1400. {
  1401. setState(stateDisableCleanup);
  1402. }
  1403. break;
  1404. //MARK: stateLoginFailed
  1405. case stateLoginFailed:
  1406. setState(stateLoginFailedWaiting);
  1407. break;
  1408. //MARK: stateLoginFailedWaiting
  1409. case stateLoginFailedWaiting:
  1410. if(!mVoiceEnabled)
  1411. {
  1412. setState(stateDisableCleanup);
  1413. }
  1414. break;
  1415. //MARK: stateJoinSessionFailed
  1416. case stateJoinSessionFailed:
  1417. // Transition to error state. Send out any notifications here.
  1418. if(mAudioSession)
  1419. {
  1420. LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
  1421. }
  1422. else
  1423. {
  1424. LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
  1425. }
  1426. notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
  1427. setState(stateJoinSessionFailedWaiting);
  1428. break;
  1429. //MARK: stateJoinSessionFailedWaiting
  1430. case stateJoinSessionFailedWaiting:
  1431. // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
  1432. // Region crossings may leave this state and try the join again.
  1433. if(mSessionTerminateRequested)
  1434. {
  1435. setState(stateSessionTerminated);
  1436. }
  1437. break;
  1438. //MARK: stateJail
  1439. case stateJail:
  1440. // We have given up. Do nothing.
  1441. break;
  1442. }
  1443. if (mAudioSessionChanged)
  1444. {
  1445. mAudioSessionChanged = false;
  1446. notifyParticipantObservers();
  1447. notifyVoiceFontObservers();
  1448. }
  1449. else if (mAudioSession && mAudioSession->mParticipantsChanged)
  1450. {
  1451. mAudioSession->mParticipantsChanged = false;
  1452. notifyParticipantObservers();
  1453. }
  1454. }
  1455. void LLVivoxVoiceClient::closeSocket(void)
  1456. {
  1457. mSocket.reset();
  1458. mConnected = false;
  1459. mConnectorHandle.clear();
  1460. mAccountHandle.clear();
  1461. }
  1462. void LLVivoxVoiceClient::loginSendMessage()
  1463. {
  1464. std::ostringstream stream;
  1465. bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps");
  1466. stream
  1467. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">"
  1468. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  1469. << "<AccountName>" << mAccountName << "</AccountName>"
  1470. << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
  1471. << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
  1472. << "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
  1473. << "<BuddyManagementMode>Application</BuddyManagementMode>"
  1474. << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
  1475. << (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"")
  1476. << "</Request>\n\n\n";
  1477. writeString(stream.str());
  1478. }
  1479. void LLVivoxVoiceClient::logout()
  1480. {
  1481. // Ensure that we'll re-request provisioning before logging in again
  1482. mAccountPassword.clear();
  1483. mVoiceAccountServerURI.clear();
  1484. setState(stateLoggingOut);
  1485. logoutSendMessage();
  1486. }
  1487. void LLVivoxVoiceClient::logoutSendMessage()
  1488. {
  1489. if(!mAccountHandle.empty())
  1490. {
  1491. std::ostringstream stream;
  1492. stream
  1493. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">"
  1494. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1495. << "</Request>"
  1496. << "\n\n\n";
  1497. mAccountHandle.clear();
  1498. writeString(stream.str());
  1499. }
  1500. }
  1501. void LLVivoxVoiceClient::accountListBlockRulesSendMessage()
  1502. {
  1503. if(!mAccountHandle.empty())
  1504. {
  1505. std::ostringstream stream;
  1506. LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
  1507. stream
  1508. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">"
  1509. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1510. << "</Request>"
  1511. << "\n\n\n";
  1512. writeString(stream.str());
  1513. }
  1514. }
  1515. void LLVivoxVoiceClient::accountListAutoAcceptRulesSendMessage()
  1516. {
  1517. if(!mAccountHandle.empty())
  1518. {
  1519. std::ostringstream stream;
  1520. LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
  1521. stream
  1522. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">"
  1523. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1524. << "</Request>"
  1525. << "\n\n\n";
  1526. writeString(stream.str());
  1527. }
  1528. }
  1529. void LLVivoxVoiceClient::sessionGroupCreateSendMessage()
  1530. {
  1531. if(!mAccountHandle.empty())
  1532. {
  1533. std::ostringstream stream;
  1534. LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
  1535. stream
  1536. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">"
  1537. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1538. << "<Type>Normal</Type>"
  1539. << "</Request>"
  1540. << "\n\n\n";
  1541. writeString(stream.str());
  1542. }
  1543. }
  1544. void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
  1545. {
  1546. LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
  1547. S32 font_index = getVoiceFontIndex(session->mVoiceFontID);
  1548. LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL;
  1549. session->mCreateInProgress = true;
  1550. if(startAudio)
  1551. {
  1552. session->mMediaConnectInProgress = true;
  1553. }
  1554. std::ostringstream stream;
  1555. stream
  1556. << "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">"
  1557. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1558. << "<URI>" << session->mSIPURI << "</URI>";
  1559. static const std::string allowed_chars =
  1560. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  1561. "0123456789"
  1562. "-._~";
  1563. if(!session->mHash.empty())
  1564. {
  1565. stream
  1566. << "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
  1567. << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
  1568. }
  1569. stream
  1570. << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
  1571. << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
  1572. << "<VoiceFontID>" << font_index << "</VoiceFontID>"
  1573. << "<Name>" << mChannelName << "</Name>"
  1574. << "</Request>\n\n\n";
  1575. writeString(stream.str());
  1576. }
  1577. void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
  1578. {
  1579. LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
  1580. S32 font_index = getVoiceFontIndex(session->mVoiceFontID);
  1581. LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL;
  1582. session->mCreateInProgress = true;
  1583. if(startAudio)
  1584. {
  1585. session->mMediaConnectInProgress = true;
  1586. }
  1587. std::string password;
  1588. if(!session->mHash.empty())
  1589. {
  1590. static const std::string allowed_chars =
  1591. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  1592. "0123456789"
  1593. "-._~"
  1594. ;
  1595. password = LLURI::escape(session->mHash, allowed_chars);
  1596. }
  1597. std::ostringstream stream;
  1598. stream
  1599. << "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">"
  1600. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  1601. << "<URI>" << session->mSIPURI << "</URI>"
  1602. << "<Name>" << mChannelName << "</Name>"
  1603. << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
  1604. << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
  1605. << "<VoiceFontID>" << font_index << "</VoiceFontID>"
  1606. << "<Password>" << password << "</Password>"
  1607. << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
  1608. << "</Request>\n\n\n"
  1609. ;
  1610. writeString(stream.str());
  1611. }
  1612. void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
  1613. {
  1614. LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL;
  1615. S32 font_index = getVoiceFontIndex(session->mVoiceFontID);
  1616. LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL;
  1617. session->mMediaConnectInProgress = true;
  1618. std::ostringstream stream;
  1619. stream
  1620. << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">"
  1621. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  1622. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  1623. << "<VoiceFontID>" << font_index << "</VoiceFontID>"
  1624. << "<Media>Audio</Media>"
  1625. << "</Request>\n\n\n";
  1626. writeString(stream.str());
  1627. }
  1628. void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session)
  1629. {
  1630. LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
  1631. std::ostringstream stream;
  1632. stream
  1633. << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">"
  1634. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  1635. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  1636. << "</Request>\n\n\n";
  1637. writeString(stream.str());
  1638. }
  1639. void LLVivoxVoiceClient::sessionTerminate()
  1640. {
  1641. mSessionTerminateRequested = true;
  1642. }
  1643. void LLVivoxVoiceClient::requestRelog()
  1644. {
  1645. mSessionTerminateRequested = true;
  1646. mRelogRequested = true;
  1647. }
  1648. void LLVivoxVoiceClient::leaveAudioSession()
  1649. {
  1650. if(mAudioSession)
  1651. {
  1652. LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
  1653. switch(getState())
  1654. {
  1655. case stateNoChannel:
  1656. // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
  1657. // Skip the join failed transition state so we don't send out error notifications.
  1658. setState(stateJoinSessionFailedWaiting);
  1659. break;
  1660. case stateJoiningSession:
  1661. case stateSessionJoined:
  1662. case stateRunning:
  1663. if(!mAudioSession->mHandle.empty())
  1664. {
  1665. #if RECORD_EVERYTHING
  1666. // HACK: for testing only
  1667. // Save looped recording
  1668. std::string savepath("/tmp/vivoxrecording");
  1669. {
  1670. time_t now = time(NULL);
  1671. const size_t BUF_SIZE = 64;
  1672. char time_str[BUF_SIZE]; /* Flawfinder: ignore */
  1673. strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
  1674. savepath += time_str;
  1675. }
  1676. recordingLoopSave(savepath);
  1677. #endif
  1678. sessionMediaDisconnectSendMessage(mAudioSession);
  1679. setState(stateLeavingSession);
  1680. }
  1681. else
  1682. {
  1683. LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
  1684. setState(stateSessionTerminated);
  1685. }
  1686. break;
  1687. case stateJoinSessionFailed:
  1688. case stateJoinSessionFailedWaiting:
  1689. setState(stateSessionTerminated);
  1690. break;
  1691. default:
  1692. LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
  1693. break;
  1694. }
  1695. }
  1696. else
  1697. {
  1698. LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
  1699. setState(stateSessionTerminated);
  1700. }
  1701. }
  1702. void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session)
  1703. {
  1704. std::ostringstream stream;
  1705. LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
  1706. stream
  1707. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
  1708. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  1709. << "</Request>\n\n\n";
  1710. writeString(stream.str());
  1711. }
  1712. void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
  1713. {
  1714. std::ostringstream stream;
  1715. LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;
  1716. stream
  1717. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Terminate.1\">"
  1718. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  1719. << "</Request>\n\n\n";
  1720. writeString(stream.str());
  1721. }
  1722. void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
  1723. {
  1724. std::ostringstream stream;
  1725. LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
  1726. stream
  1727. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
  1728. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  1729. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  1730. << "<Media>Audio</Media>"
  1731. << "</Request>\n\n\n";
  1732. writeString(stream.str());
  1733. }
  1734. void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
  1735. {
  1736. std::ostringstream stream;
  1737. LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;
  1738. stream
  1739. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
  1740. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  1741. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  1742. << "</Request>\n\n\n";
  1743. writeString(stream.str());
  1744. }
  1745. void LLVivoxVoiceClient::getCaptureDevicesSendMessage()
  1746. {
  1747. std::ostringstream stream;
  1748. stream
  1749. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
  1750. << "</Request>\n\n\n";
  1751. writeString(stream.str());
  1752. }
  1753. void LLVivoxVoiceClient::getRenderDevicesSendMessage()
  1754. {
  1755. std::ostringstream stream;
  1756. stream
  1757. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
  1758. << "</Request>\n\n\n";
  1759. writeString(stream.str());
  1760. }
  1761. void LLVivoxVoiceClient::clearCaptureDevices()
  1762. {
  1763. LL_DEBUGS("Voice") << "called" << LL_ENDL;
  1764. mCaptureDevices.clear();
  1765. }
  1766. void LLVivoxVoiceClient::addCaptureDevice(const std::string& name)
  1767. {
  1768. LL_DEBUGS("Voice") << name << LL_ENDL;
  1769. mCaptureDevices.push_back(name);
  1770. }
  1771. LLVoiceDeviceList& LLVivoxVoiceClient::getCaptureDevices()
  1772. {
  1773. return mCaptureDevices;
  1774. }
  1775. void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
  1776. {
  1777. if(name == "Default")
  1778. {
  1779. if(!mCaptureDevice.empty())
  1780. {
  1781. mCaptureDevice.clear();
  1782. mCaptureDeviceDirty = true;
  1783. }
  1784. }
  1785. else
  1786. {
  1787. if(mCaptureDevice != name)
  1788. {
  1789. mCaptureDevice = name;
  1790. mCaptureDeviceDirty = true;
  1791. }
  1792. }
  1793. }
  1794. void LLVivoxVoiceClient::clearRenderDevices()
  1795. {
  1796. LL_DEBUGS("Voice") << "called" << LL_ENDL;
  1797. mRenderDevices.clear();
  1798. }
  1799. void LLVivoxVoiceClient::addRenderDevice(const std::string& name)
  1800. {
  1801. LL_DEBUGS("Voice") << name << LL_ENDL;
  1802. mRenderDevices.push_back(name);
  1803. }
  1804. LLVoiceDeviceList& LLVivoxVoiceClient::getRenderDevices()
  1805. {
  1806. return mRenderDevices;
  1807. }
  1808. void LLVivoxVoiceClient::setRenderDevice(const std::string& name)
  1809. {
  1810. if(name == "Default")
  1811. {
  1812. if(!mRenderDevice.empty())
  1813. {
  1814. mRenderDevice.clear();
  1815. mRenderDeviceDirty = true;
  1816. }
  1817. }
  1818. else
  1819. {
  1820. if(mRenderDevice != name)
  1821. {
  1822. mRenderDevice = name;
  1823. mRenderDeviceDirty = true;
  1824. }
  1825. }
  1826. }
  1827. void LLVivoxVoiceClient::tuningStart()
  1828. {
  1829. mTuningMode = true;
  1830. LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL;
  1831. if(getState() >= stateNoChannel)
  1832. {
  1833. LL_DEBUGS("Voice") << "no channel" << LL_ENDL;
  1834. sessionTerminate();
  1835. }
  1836. }
  1837. void LLVivoxVoiceClient::tuningStop()
  1838. {
  1839. mTuningMode = false;
  1840. }
  1841. bool LLVivoxVoiceClient::inTuningMode()
  1842. {
  1843. bool result = false;
  1844. switch(getState())
  1845. {
  1846. case stateMicTuningRunning:
  1847. result = true;
  1848. break;
  1849. default:
  1850. break;
  1851. }
  1852. return result;
  1853. }
  1854. void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
  1855. {
  1856. mTuningAudioFile = name;
  1857. std::ostringstream stream;
  1858. stream
  1859. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">"
  1860. << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
  1861. << "<Loop>" << (loop?"1":"0") << "</Loop>"
  1862. << "</Request>\n\n\n";
  1863. writeString(stream.str());
  1864. }
  1865. void LLVivoxVoiceClient::tuningRenderStopSendMessage()
  1866. {
  1867. std::ostringstream stream;
  1868. stream
  1869. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
  1870. << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
  1871. << "</Request>\n\n\n";
  1872. writeString(stream.str());
  1873. }
  1874. void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration)
  1875. {
  1876. LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
  1877. std::ostringstream stream;
  1878. stream
  1879. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
  1880. << "<Duration>" << duration << "</Duration>"
  1881. << "</Request>\n\n\n";
  1882. writeString(stream.str());
  1883. }
  1884. void LLVivoxVoiceClient::tuningCaptureStopSendMessage()
  1885. {
  1886. LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL;
  1887. std::ostringstream stream;
  1888. stream
  1889. << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
  1890. << "</Request>\n\n\n";
  1891. writeString(stream.str());
  1892. mTuningEnergy = 0.0f;
  1893. }
  1894. void LLVivoxVoiceClient::tuningSetMicVolume(float volume)
  1895. {
  1896. int scaled_volume = scale_mic_volume(volume);
  1897. if(scaled_volume != mTuningMicVolume)
  1898. {
  1899. mTuningMicVolume = scaled_volume;
  1900. mTuningMicVolumeDirty = true;
  1901. }
  1902. }
  1903. void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume)
  1904. {
  1905. int scaled_volume = scale_speaker_volume(volume);
  1906. if(scaled_volume != mTuningSpeakerVolume)
  1907. {
  1908. mTuningSpeakerVolume = scaled_volume;
  1909. mTuningSpeakerVolumeDirty = true;
  1910. }
  1911. }
  1912. float LLVivoxVoiceClient::tuningGetEnergy(void)
  1913. {
  1914. return mTuningEnergy;
  1915. }
  1916. bool LLVivoxVoiceClient::deviceSettingsAvailable()
  1917. {
  1918. bool result = true;
  1919. if(!mConnected)
  1920. result = false;
  1921. if(mRenderDevices.empty())
  1922. result = false;
  1923. return result;
  1924. }
  1925. void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList)
  1926. {
  1927. if(clearCurrentList)
  1928. {
  1929. clearCaptureDevices();
  1930. clearRenderDevices();
  1931. }
  1932. getCaptureDevicesSendMessage();
  1933. getRenderDevicesSendMessage();
  1934. }
  1935. void LLVivoxVoiceClient::daemonDied()
  1936. {
  1937. // The daemon died, so the connection is gone. Reset everything and start over.
  1938. LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL;
  1939. // Try to relaunch the daemon
  1940. setState(stateDisableCleanup);
  1941. }
  1942. void LLVivoxVoiceClient::giveUp()
  1943. {
  1944. // All has failed. Clean up and stop trying.
  1945. closeSocket();
  1946. cleanUp();
  1947. setState(stateJail);
  1948. }
  1949. static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
  1950. {
  1951. F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the new position and velocity
  1952. F64 npos[3];
  1953. // The original XML command was sent like this:
  1954. /*
  1955. << "<Position>"
  1956. << "<X>" << pos[VX] << "</X>"
  1957. << "<Y>" << pos[VZ] << "</Y>"
  1958. << "<Z>" << pos[VY] << "</Z>"
  1959. << "</Position>"
  1960. << "<Velocity>"
  1961. << "<X>" << mAvatarVelocity[VX] << "</X>"
  1962. << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
  1963. << "<Z>" << mAvatarVelocity[VY] << "</Z>"
  1964. << "</Velocity>"
  1965. << "<AtOrientation>"
  1966. << "<X>" << l.mV[VX] << "</X>"
  1967. << "<Y>" << u.mV[VX] << "</Y>"
  1968. << "<Z>" << a.mV[VX] << "</Z>"
  1969. << "</AtOrientation>"
  1970. << "<UpOrientation>"
  1971. << "<X>" << l.mV[VZ] << "</X>"
  1972. << "<Y>" << u.mV[VY] << "</Y>"
  1973. << "<Z>" << a.mV[VZ] << "</Z>"
  1974. << "</UpOrientation>"
  1975. << "<LeftOrientation>"
  1976. << "<X>" << l.mV [VY] << "</X>"
  1977. << "<Y>" << u.mV [VZ] << "</Y>"
  1978. << "<Z>" << a.mV [VY] << "</Z>"