llvoiceclient.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:192k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1.  /** 
  2.  * @file llvoiceclient.cpp
  3.  * @brief Implementation of LLVoiceClient class which is the interface to the voice client process.
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llvoiceclient.h"
  34. #include <boost/tokenizer.hpp>
  35. // library includes
  36. #include "llnotificationsutil.h"
  37. #include "llsdserialize.h"
  38. #include "llsdutil.h"
  39. // project includes
  40. #include "llvoavatar.h"
  41. #include "llbufferstream.h"
  42. #include "llfile.h"
  43. #ifdef LL_STANDALONE
  44. # include "expat.h"
  45. #else
  46. # include "expat/expat.h"
  47. #endif
  48. #include "llcallbacklist.h"
  49. #include "llcallingcard.h"   // for LLFriendObserver
  50. #include "llviewerregion.h"
  51. #include "llviewernetwork.h" // for gGridChoice
  52. #include "llbase64.h"
  53. #include "llviewercontrol.h"
  54. #include "llkeyboard.h"
  55. #include "llappviewer.h" // for gDisconnected, gDisableVoice
  56. #include "llmutelist.h"  // to check for muted avatars
  57. #include "llagent.h"
  58. #include "llcachename.h"
  59. #include "llimview.h" // for LLIMMgr
  60. #include "llparcel.h"
  61. #include "llviewerparcelmgr.h"
  62. //#include "llfirstuse.h"
  63. #include "llspeakers.h"
  64. #include "lltrans.h"
  65. #include "llviewerwindow.h"
  66. #include "llviewercamera.h"
  67. #include "llvoavatarself.h"
  68. #include "llvoicechannel.h"
  69. // for base64 decoding
  70. #include "apr_base64.h"
  71. // for SHA1 hash
  72. #include "apr_sha1.h"
  73. // for MD5 hash
  74. #include "llmd5.h"
  75. #define USE_SESSION_GROUPS 0
  76. static bool sConnectingToAgni = false;
  77. F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
  78. const F32 SPEAKING_TIMEOUT = 1.f;
  79. const int VOICE_MAJOR_VERSION = 1;
  80. const int VOICE_MINOR_VERSION = 0;
  81. LLVoiceClient *gVoiceClient = NULL;
  82. // Don't retry connecting to the daemon more frequently than this:
  83. const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
  84. // Don't send positional updates more frequently than this:
  85. const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
  86. const F32 LOGIN_RETRY_SECONDS = 10.0f;
  87. const int MAX_LOGIN_RETRIES = 12;
  88. static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
  89. {
  90. LLMD5 md5_uuid;
  91. md5_uuid.update((const unsigned char*)str.data(), str.size());
  92. md5_uuid.finalize();
  93. md5_uuid.raw_digest(uuid.mData);
  94. }
  95. static int scale_mic_volume(float volume)
  96. {
  97. // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
  98. // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70
  99. return 30 + (int)(volume * 20.0f);
  100. }
  101. static int scale_speaker_volume(float volume)
  102. {
  103. // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
  104. // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70
  105. return 30 + (int)(volume * 40.0f);
  106. }
  107. class LLViewerVoiceAccountProvisionResponder :
  108. public LLHTTPClient::Responder
  109. {
  110. public:
  111. LLViewerVoiceAccountProvisionResponder(int retries)
  112. {
  113. mRetries = retries;
  114. }
  115. virtual void error(U32 status, const std::string& reason)
  116. {
  117. if ( mRetries > 0 )
  118. {
  119. LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying.  status = " << status << ", reason = "" << reason << """ << LL_ENDL;
  120. if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
  121. mRetries - 1);
  122. }
  123. else
  124. {
  125. LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up).  status = " << status << ", reason = "" << reason << """ << LL_ENDL;
  126. if ( gVoiceClient ) gVoiceClient->giveUp();
  127. }
  128. }
  129. virtual void result(const LLSD& content)
  130. {
  131. if ( gVoiceClient )
  132. {
  133. std::string voice_sip_uri_hostname;
  134. std::string voice_account_server_uri;
  135. LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
  136. if(content.has("voice_sip_uri_hostname"))
  137. voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
  138. // this key is actually misnamed -- it will be an entire URI, not just a hostname.
  139. if(content.has("voice_account_server_name"))
  140. voice_account_server_uri = content["voice_account_server_name"].asString();
  141. gVoiceClient->login(
  142. content["username"].asString(),
  143. content["password"].asString(),
  144. voice_sip_uri_hostname,
  145. voice_account_server_uri);
  146. }
  147. }
  148. private:
  149. int mRetries;
  150. };
  151. /** 
  152.  * @class LLVivoxProtocolParser
  153.  * @brief This class helps construct new LLIOPipe specializations
  154.  * @see LLIOPipe
  155.  *
  156.  * THOROUGH_DESCRIPTION
  157.  */
  158. class LLVivoxProtocolParser : public LLIOPipe
  159. {
  160. LOG_CLASS(LLVivoxProtocolParser);
  161. public:
  162. LLVivoxProtocolParser();
  163. virtual ~LLVivoxProtocolParser();
  164. protected:
  165. /* @name LLIOPipe virtual implementations
  166.  */
  167. //@{
  168. /** 
  169.  * @brief Process the data in buffer
  170.  */
  171. virtual EStatus process_impl(
  172. const LLChannelDescriptors& channels,
  173. buffer_ptr_t& buffer,
  174. bool& eos,
  175. LLSD& context,
  176. LLPumpIO* pump);
  177. //@}
  178. std::string  mInput;
  179. // Expat control members
  180. XML_Parser parser;
  181. int responseDepth;
  182. bool ignoringTags;
  183. bool isEvent;
  184. int ignoreDepth;
  185. // Members for processing responses. The values are transient and only valid within a call to processResponse().
  186. bool squelchDebugOutput;
  187. int returnCode;
  188. int statusCode;
  189. std::string statusString;
  190. std::string requestId;
  191. std::string actionString;
  192. std::string connectorHandle;
  193. std::string versionID;
  194. std::string accountHandle;
  195. std::string sessionHandle;
  196. std::string sessionGroupHandle;
  197. std::string alias;
  198. std::string applicationString;
  199. // Members for processing events. The values are transient and only valid within a call to processResponse().
  200. std::string eventTypeString;
  201. int state;
  202. std::string uriString;
  203. bool isChannel;
  204. bool incoming;
  205. bool enabled;
  206. std::string nameString;
  207. std::string audioMediaString;
  208. std::string displayNameString;
  209. std::string deviceString;
  210. int participantType;
  211. bool isLocallyMuted;
  212. bool isModeratorMuted;
  213. bool isSpeaking;
  214. int volume;
  215. F32 energy;
  216. std::string messageHeader;
  217. std::string messageBody;
  218. std::string notificationType;
  219. bool hasText;
  220. bool hasAudio;
  221. bool hasVideo;
  222. bool terminated;
  223. std::string blockMask;
  224. std::string presenceOnly;
  225. std::string autoAcceptMask;
  226. std::string autoAddAsBuddy;
  227. int numberOfAliases;
  228. std::string subscriptionHandle;
  229. std::string subscriptionType;
  230. // Members for processing text between tags
  231. std::string textBuffer;
  232. bool accumulateText;
  233. void reset();
  234. void processResponse(std::string tag);
  235. static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
  236. static void XMLCALL ExpatEndTag(void *data, const char *el);
  237. static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
  238. void StartTag(const char *tag, const char **attr);
  239. void EndTag(const char *tag);
  240. void CharData(const char *buffer, int length);
  241. };
  242. LLVivoxProtocolParser::LLVivoxProtocolParser()
  243. {
  244. parser = NULL;
  245. parser = XML_ParserCreate(NULL);
  246. reset();
  247. }
  248. void LLVivoxProtocolParser::reset()
  249. {
  250. responseDepth = 0;
  251. ignoringTags = false;
  252. accumulateText = false;
  253. energy = 0.f;
  254. hasText = false;
  255. hasAudio = false;
  256. hasVideo = false;
  257. terminated = false;
  258. ignoreDepth = 0;
  259. isChannel = false;
  260. incoming = false;
  261. enabled = false;
  262. isEvent = false;
  263. isLocallyMuted = false;
  264. isModeratorMuted = false;
  265. isSpeaking = false;
  266. participantType = 0;
  267. squelchDebugOutput = false;
  268. returnCode = -1;
  269. state = 0;
  270. statusCode = 0;
  271. volume = 0;
  272. textBuffer.clear();
  273. alias.clear();
  274. numberOfAliases = 0;
  275. applicationString.clear();
  276. }
  277. //virtual 
  278. LLVivoxProtocolParser::~LLVivoxProtocolParser()
  279. {
  280. if (parser)
  281. XML_ParserFree(parser);
  282. }
  283. // virtual
  284. LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
  285. const LLChannelDescriptors& channels,
  286. buffer_ptr_t& buffer,
  287. bool& eos,
  288. LLSD& context,
  289. LLPumpIO* pump)
  290. {
  291. LLBufferStream istr(channels, buffer.get());
  292. std::ostringstream ostr;
  293. while (istr.good())
  294. {
  295. char buf[1024];
  296. istr.read(buf, sizeof(buf));
  297. mInput.append(buf, istr.gcount());
  298. }
  299. // Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser.
  300. int start = 0;
  301. int delim;
  302. while((delim = mInput.find("nnn", start)) != std::string::npos)
  303. {
  304. // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
  305. reset();
  306. XML_ParserReset(parser, NULL);
  307. XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
  308. XML_SetCharacterDataHandler(parser, ExpatCharHandler);
  309. XML_SetUserData(parser, this);
  310. XML_Parse(parser, mInput.data() + start, delim - start, false);
  311. // If this message isn't set to be squelched, output the raw XML received.
  312. if(!squelchDebugOutput)
  313. {
  314. LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
  315. }
  316. start = delim + 3;
  317. }
  318. if(start != 0)
  319. mInput = mInput.substr(start);
  320. LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
  321. if(!gVoiceClient->mConnected)
  322. {
  323. // If voice has been disabled, we just want to close the socket.  This does so.
  324. LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL;
  325. return STATUS_STOP;
  326. }
  327. return STATUS_OK;
  328. }
  329. void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr)
  330. {
  331. if (data)
  332. {
  333. LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
  334. object->StartTag(el, attr);
  335. }
  336. }
  337. // --------------------------------------------------------------------------------
  338. void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el)
  339. {
  340. if (data)
  341. {
  342. LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
  343. object->EndTag(el);
  344. }
  345. }
  346. // --------------------------------------------------------------------------------
  347. void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len)
  348. {
  349. if (data)
  350. {
  351. LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
  352. object->CharData(s, len);
  353. }
  354. }
  355. // --------------------------------------------------------------------------------
  356. void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
  357. {
  358. // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
  359. textBuffer.clear();
  360. // only accumulate text if we're not ignoring tags.
  361. accumulateText = !ignoringTags;
  362. if (responseDepth == 0)
  363. {
  364. isEvent = !stricmp("Event", tag);
  365. if (!stricmp("Response", tag) || isEvent)
  366. {
  367. // Grab the attributes
  368. while (*attr)
  369. {
  370. const char *key = *attr++;
  371. const char *value = *attr++;
  372. if (!stricmp("requestId", key))
  373. {
  374. requestId = value;
  375. }
  376. else if (!stricmp("action", key))
  377. {
  378. actionString = value;
  379. }
  380. else if (!stricmp("type", key))
  381. {
  382. eventTypeString = value;
  383. }
  384. }
  385. }
  386. LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
  387. }
  388. else
  389. {
  390. if (ignoringTags)
  391. {
  392. LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
  393. }
  394. else
  395. {
  396. LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
  397. // Ignore the InputXml stuff so we don't get confused
  398. if (!stricmp("InputXml", tag))
  399. {
  400. ignoringTags = true;
  401. ignoreDepth = responseDepth;
  402. accumulateText = false;
  403. LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
  404. }
  405. else if (!stricmp("CaptureDevices", tag))
  406. {
  407. gVoiceClient->clearCaptureDevices();
  408. }
  409. else if (!stricmp("RenderDevices", tag))
  410. {
  411. gVoiceClient->clearRenderDevices();
  412. }
  413. else if (!stricmp("CaptureDevice", tag))
  414. {
  415. deviceString.clear();
  416. }
  417. else if (!stricmp("RenderDevice", tag))
  418. {
  419. deviceString.clear();
  420. }
  421. else if (!stricmp("Buddies", tag))
  422. {
  423. gVoiceClient->deleteAllBuddies();
  424. }
  425. else if (!stricmp("BlockRules", tag))
  426. {
  427. gVoiceClient->deleteAllBlockRules();
  428. }
  429. else if (!stricmp("AutoAcceptRules", tag))
  430. {
  431. gVoiceClient->deleteAllAutoAcceptRules();
  432. }
  433. }
  434. }
  435. responseDepth++;
  436. }
  437. // --------------------------------------------------------------------------------
  438. void LLVivoxProtocolParser::EndTag(const char *tag)
  439. {
  440. const std::string& string = textBuffer;
  441. responseDepth--;
  442. if (ignoringTags)
  443. {
  444. if (ignoreDepth == responseDepth)
  445. {
  446. LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL;
  447. ignoringTags = false;
  448. }
  449. else
  450. {
  451. LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
  452. }
  453. }
  454. if (!ignoringTags)
  455. {
  456. LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
  457. // Closing a tag. Finalize the text we've accumulated and reset
  458. if (!stricmp("ReturnCode", tag))
  459. returnCode = strtol(string.c_str(), NULL, 10);
  460. else if (!stricmp("SessionHandle", tag))
  461. sessionHandle = string;
  462. else if (!stricmp("SessionGroupHandle", tag))
  463. sessionGroupHandle = string;
  464. else if (!stricmp("StatusCode", tag))
  465. statusCode = strtol(string.c_str(), NULL, 10);
  466. else if (!stricmp("StatusString", tag))
  467. statusString = string;
  468. else if (!stricmp("ParticipantURI", tag))
  469. uriString = string;
  470. else if (!stricmp("Volume", tag))
  471. volume = strtol(string.c_str(), NULL, 10);
  472. else if (!stricmp("Energy", tag))
  473. energy = (F32)strtod(string.c_str(), NULL);
  474. else if (!stricmp("IsModeratorMuted", tag))
  475. isModeratorMuted = !stricmp(string.c_str(), "true");
  476. else if (!stricmp("IsSpeaking", tag))
  477. isSpeaking = !stricmp(string.c_str(), "true");
  478. else if (!stricmp("Alias", tag))
  479. alias = string;
  480. else if (!stricmp("NumberOfAliases", tag))
  481. numberOfAliases = strtol(string.c_str(), NULL, 10);
  482. else if (!stricmp("Application", tag))
  483. applicationString = string;
  484. else if (!stricmp("ConnectorHandle", tag))
  485. connectorHandle = string;
  486. else if (!stricmp("VersionID", tag))
  487. versionID = string;
  488. else if (!stricmp("AccountHandle", tag))
  489. accountHandle = string;
  490. else if (!stricmp("State", tag))
  491. state = strtol(string.c_str(), NULL, 10);
  492. else if (!stricmp("URI", tag))
  493. uriString = string;
  494. else if (!stricmp("IsChannel", tag))
  495. isChannel = !stricmp(string.c_str(), "true");
  496. else if (!stricmp("Incoming", tag))
  497. incoming = !stricmp(string.c_str(), "true");
  498. else if (!stricmp("Enabled", tag))
  499. enabled = !stricmp(string.c_str(), "true");
  500. else if (!stricmp("Name", tag))
  501. nameString = string;
  502. else if (!stricmp("AudioMedia", tag))
  503. audioMediaString = string;
  504. else if (!stricmp("ChannelName", tag))
  505. nameString = string;
  506. else if (!stricmp("DisplayName", tag))
  507. displayNameString = string;
  508. else if (!stricmp("Device", tag))
  509. deviceString = string;
  510. else if (!stricmp("AccountName", tag))
  511. nameString = string;
  512. else if (!stricmp("ParticipantType", tag))
  513. participantType = strtol(string.c_str(), NULL, 10);
  514. else if (!stricmp("IsLocallyMuted", tag))
  515. isLocallyMuted = !stricmp(string.c_str(), "true");
  516. else if (!stricmp("MicEnergy", tag))
  517. energy = (F32)strtod(string.c_str(), NULL);
  518. else if (!stricmp("ChannelName", tag))
  519. nameString = string;
  520. else if (!stricmp("ChannelURI", tag))
  521. uriString = string;
  522. else if (!stricmp("BuddyURI", tag))
  523. uriString = string;
  524. else if (!stricmp("Presence", tag))
  525. statusString = string;
  526. else if (!stricmp("CaptureDevice", tag))
  527. {
  528. gVoiceClient->addCaptureDevice(deviceString);
  529. }
  530. else if (!stricmp("RenderDevice", tag))
  531. {
  532. gVoiceClient->addRenderDevice(deviceString);
  533. }
  534. else if (!stricmp("Buddy", tag))
  535. {
  536. gVoiceClient->processBuddyListEntry(uriString, displayNameString);
  537. }
  538. else if (!stricmp("BlockRule", tag))
  539. {
  540. gVoiceClient->addBlockRule(blockMask, presenceOnly);
  541. }
  542. else if (!stricmp("BlockMask", tag))
  543. blockMask = string;
  544. else if (!stricmp("PresenceOnly", tag))
  545. presenceOnly = string;
  546. else if (!stricmp("AutoAcceptRule", tag))
  547. {
  548. gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy);
  549. }
  550. else if (!stricmp("AutoAcceptMask", tag))
  551. autoAcceptMask = string;
  552. else if (!stricmp("AutoAddAsBuddy", tag))
  553. autoAddAsBuddy = string;
  554. else if (!stricmp("MessageHeader", tag))
  555. messageHeader = string;
  556. else if (!stricmp("MessageBody", tag))
  557. messageBody = string;
  558. else if (!stricmp("NotificationType", tag))
  559. notificationType = string;
  560. else if (!stricmp("HasText", tag))
  561. hasText = !stricmp(string.c_str(), "true");
  562. else if (!stricmp("HasAudio", tag))
  563. hasAudio = !stricmp(string.c_str(), "true");
  564. else if (!stricmp("HasVideo", tag))
  565. hasVideo = !stricmp(string.c_str(), "true");
  566. else if (!stricmp("Terminated", tag))
  567. terminated = !stricmp(string.c_str(), "true");
  568. else if (!stricmp("SubscriptionHandle", tag))
  569. subscriptionHandle = string;
  570. else if (!stricmp("SubscriptionType", tag))
  571. subscriptionType = string;
  572. textBuffer.clear();
  573. accumulateText= false;
  574. if (responseDepth == 0)
  575. {
  576. // We finished all of the XML, process the data
  577. processResponse(tag);
  578. }
  579. }
  580. }
  581. // --------------------------------------------------------------------------------
  582. void LLVivoxProtocolParser::CharData(const char *buffer, int length)
  583. {
  584. /*
  585. This method is called for anything that isn't a tag, which can be text you
  586. want that lies between tags, and a lot of stuff you don't want like file formatting
  587. (tabs, spaces, CR/LF, etc).
  588. Only copy text if we are in accumulate mode...
  589. */
  590. if (accumulateText)
  591. textBuffer.append(buffer, length);
  592. }
  593. // --------------------------------------------------------------------------------
  594. void LLVivoxProtocolParser::processResponse(std::string tag)
  595. {
  596. LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
  597. // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success.  This is a change vs. previous SDKs.
  598. // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
  599. // so I believe this will give correct behavior.
  600. if(returnCode == 0)
  601. statusCode = 0;
  602. if (isEvent)
  603. {
  604. const char *eventTypeCstr = eventTypeString.c_str();
  605. if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
  606. {
  607. gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
  608. }
  609. else if (!stricmp(eventTypeCstr, "SessionAddedEvent"))
  610. {
  611. /*
  612. <Event type="SessionAddedEvent">
  613. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  614. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  615. <Uri>sip:confctl-1408789@bhr.vivox.com</Uri>
  616. <IsChannel>true</IsChannel>
  617. <Incoming>false</Incoming>
  618. <ChannelName />
  619. </Event>
  620. */
  621. gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString);
  622. }
  623. else if (!stricmp(eventTypeCstr, "SessionRemovedEvent"))
  624. {
  625. gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
  626. }
  627. else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
  628. {
  629. gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle);
  630. }
  631. else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent"))
  632. {
  633. /*
  634. <Event type="MediaStreamUpdatedEvent">
  635. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  636. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  637. <StatusCode>200</StatusCode>
  638. <StatusString>OK</StatusString>
  639. <State>2</State>
  640. <Incoming>false</Incoming>
  641. </Event>
  642. */
  643. gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming);
  644. }
  645. else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
  646. {
  647. /*
  648. <Event type="TextStreamUpdatedEvent">
  649. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
  650. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
  651. <Enabled>true</Enabled>
  652. <State>1</State>
  653. <Incoming>true</Incoming>
  654. </Event>
  655. */
  656. gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
  657. }
  658. else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
  659. {
  660. /* 
  661. <Event type="ParticipantAddedEvent">
  662. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
  663. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
  664. <ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri>
  665. <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
  666. <DisplayName />
  667. <ParticipantType>0</ParticipantType>
  668. </Event>
  669. */
  670. gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
  671. }
  672. else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
  673. {
  674. /*
  675. <Event type="ParticipantRemovedEvent">
  676. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
  677. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
  678. <ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri>
  679. <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
  680. </Event>
  681. */
  682. gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
  683. }
  684. else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
  685. {
  686. /*
  687. <Event type="ParticipantUpdatedEvent">
  688. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  689. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  690. <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
  691. <IsModeratorMuted>false</IsModeratorMuted>
  692. <IsSpeaking>true</IsSpeaking>
  693. <Volume>44</Volume>
  694. <Energy>0.0879437</Energy>
  695. </Event>
  696. */
  697. // These happen so often that logging them is pretty useless.
  698. squelchDebugOutput = true;
  699. gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
  700. }
  701. else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
  702. {
  703. gVoiceClient->auxAudioPropertiesEvent(energy);
  704. }
  705. else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent"))
  706. {
  707. gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString);
  708. }
  709. else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent"))
  710. {
  711. // The buddy list was updated during parsing.
  712. // Need to recheck against the friends list.
  713. gVoiceClient->buddyListChanged();
  714. }
  715. else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
  716. {
  717. /*
  718. <Event type="BuddyChangedEvent">
  719. <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
  720. <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
  721. <DisplayName>Monroe Tester</DisplayName>
  722. <BuddyData />
  723. <GroupID>0</GroupID>
  724. <ChangeType>Set</ChangeType>
  725. </Event>
  726. */
  727. // TODO: Question: Do we need to process this at all?
  728. }
  729. else if (!stricmp(eventTypeCstr, "MessageEvent"))  
  730. {
  731. gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
  732. }
  733. else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))  
  734. {
  735. gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType);
  736. }
  737. else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))  
  738. {
  739. gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType);
  740. }
  741. else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))  
  742. {
  743. /*
  744. <Event type="SessionUpdatedEvent">
  745. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  746. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  747. <Uri>sip:confctl-9@bhd.vivox.com</Uri>
  748. <IsMuted>0</IsMuted>
  749. <Volume>50</Volume>
  750. <TransmitEnabled>1</TransmitEnabled>
  751. <IsFocused>0</IsFocused>
  752. <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
  753. <SessionFontID>0</SessionFontID>
  754. </Event>
  755. */
  756. // We don't need to process this, but we also shouldn't warn on it, since that confuses people.
  757. }
  758. else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))  
  759. {
  760. /*
  761. <Event type="SessionGroupRemovedEvent">
  762. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  763. </Event>
  764. */
  765. // We don't need to process this, but we also shouldn't warn on it, since that confuses people.
  766. }
  767. else
  768. {
  769. LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
  770. }
  771. }
  772. else
  773. {
  774. const char *actionCstr = actionString.c_str();
  775. if (!stricmp(actionCstr, "Connector.Create.1"))
  776. {
  777. gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
  778. }
  779. else if (!stricmp(actionCstr, "Account.Login.1"))
  780. {
  781. gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases);
  782. }
  783. else if (!stricmp(actionCstr, "Session.Create.1"))
  784. {
  785. gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);
  786. }
  787. else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
  788. {
  789. gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);
  790. }
  791. else if (!stricmp(actionCstr, "Session.Connect.1"))
  792. {
  793. gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString);
  794. }
  795. else if (!stricmp(actionCstr, "Account.Logout.1"))
  796. {
  797. gVoiceClient->logoutResponse(statusCode, statusString);
  798. }
  799. else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
  800. {
  801. gVoiceClient->connectorShutdownResponse(statusCode, statusString);
  802. }
  803. else if (!stricmp(actionCstr, "Account.ListBlockRules.1"))
  804. {
  805. gVoiceClient->accountListBlockRulesResponse(statusCode, statusString);
  806. }
  807. else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1"))
  808. {
  809. gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);
  810. }
  811. else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
  812. {
  813. // We don't need to process these, but they're so spammy we don't want to log them.
  814. squelchDebugOutput = true;
  815. }
  816. /*
  817. else if (!stricmp(actionCstr, "Account.ChannelGetList.1"))
  818. {
  819. gVoiceClient->channelGetListResponse(statusCode, statusString);
  820. }
  821. else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
  822. {
  823. }
  824. else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
  825. {
  826. }
  827. else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
  828. {
  829. }
  830. else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
  831. {
  832. }
  833. else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
  834. {
  835. }
  836. else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
  837. {
  838. }
  839. else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
  840. {
  841. }
  842. else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
  843. {
  844. }
  845. else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
  846. {
  847. }
  848. else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
  849. {
  850. }
  851. else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
  852. {
  853. }
  854. else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
  855. {
  856. }
  857. else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
  858. {
  859. }
  860. else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
  861. {
  862. }
  863. else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
  864. {
  865. }
  866. else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
  867. {
  868. }
  869. else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
  870. {
  871. }
  872. else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
  873. {
  874. }
  875. */
  876. }
  877. }
  878. ///////////////////////////////////////////////////////////////////////////////////////////////
  879. class LLVoiceClientMuteListObserver : public LLMuteListObserver
  880. {
  881. /* virtual */ void onChange()  { gVoiceClient->muteListChanged();}
  882. };
  883. class LLVoiceClientFriendsObserver : public LLFriendObserver
  884. {
  885. public:
  886. /* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);}
  887. };
  888. static LLVoiceClientMuteListObserver mutelist_listener;
  889. static bool sMuteListListener_listening = false;
  890. static LLVoiceClientFriendsObserver *friendslist_listener = NULL;
  891. ///////////////////////////////////////////////////////////////////////////////////////////////
  892. class LLVoiceClientCapResponder : public LLHTTPClient::Responder
  893. {
  894. public:
  895. LLVoiceClientCapResponder(void){};
  896. virtual void error(U32 status, const std::string& reason); // called with bad status codes
  897. virtual void result(const LLSD& content);
  898. private:
  899. };
  900. void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
  901. {
  902. LL_WARNS("Voice") << "LLVoiceClientCapResponder::error("
  903. << status << ": " << reason << ")"
  904. << LL_ENDL;
  905. }
  906. void LLVoiceClientCapResponder::result(const LLSD& content)
  907. {
  908. LLSD::map_const_iterator iter;
  909. LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
  910. if ( content.has("voice_credentials") )
  911. {
  912. LLSD voice_credentials = content["voice_credentials"];
  913. std::string uri;
  914. std::string credentials;
  915. if ( voice_credentials.has("channel_uri") )
  916. {
  917. uri = voice_credentials["channel_uri"].asString();
  918. }
  919. if ( voice_credentials.has("channel_credentials") )
  920. {
  921. credentials =
  922. voice_credentials["channel_credentials"].asString();
  923. }
  924. gVoiceClient->setSpatialChannel(uri, credentials);
  925. }
  926. }
  927. #if LL_WINDOWS
  928. static HANDLE sGatewayHandle = 0;
  929. static bool isGatewayRunning()
  930. {
  931. bool result = false;
  932. if(sGatewayHandle != 0)
  933. {
  934. DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
  935. if(waitresult != WAIT_OBJECT_0)
  936. {
  937. result = true;
  938. }
  939. }
  940. return result;
  941. }
  942. static void killGateway()
  943. {
  944. if(sGatewayHandle != 0)
  945. {
  946. TerminateProcess(sGatewayHandle,0);
  947. }
  948. }
  949. #else // Mac and linux
  950. static pid_t sGatewayPID = 0;
  951. static bool isGatewayRunning()
  952. {
  953. bool result = false;
  954. if(sGatewayPID != 0)
  955. {
  956. // A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists.
  957. if(kill(sGatewayPID, 0) == 0)
  958. {
  959. result = true;
  960. }
  961. }
  962. return result;
  963. }
  964. static void killGateway()
  965. {
  966. if(sGatewayPID != 0)
  967. {
  968. kill(sGatewayPID, SIGTERM);
  969. }
  970. }
  971. #endif
  972. class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage>
  973. {
  974. LOG_CLASS(LLSpeakerVolumeStorage);
  975. public:
  976. /**
  977.  * Sets internal voluem level for specified user.
  978.  *
  979.  * @param[in] speaker_id - LLUUID of user to store volume level for
  980.  * @param[in] volume - external (vivox) volume level to be stored for user.
  981.  */
  982. void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume);
  983. /**
  984.  * Gets stored external (vivox) volume level for specified speaker and
  985.  * transforms it into internal (viewer) level.
  986.  *
  987.  * If specified user is not found -1 will be returned.
  988.  * Internal level is calculated as: internal = 400 * external^2
  989.  * Maps 0.0 to 1.0 to internal values 0-400
  990.  *
  991.  * @param[in] speaker_id - LLUUID of user to get his volume level
  992.  */
  993. S32 getSpeakerVolume(const LLUUID& speaker_id);
  994. private:
  995. friend class LLSingleton<LLSpeakerVolumeStorage>;
  996. LLSpeakerVolumeStorage();
  997. ~LLSpeakerVolumeStorage();
  998. const static std::string SETTINGS_FILE_NAME;
  999. void load();
  1000. void save();
  1001. typedef std::map<LLUUID, F32> speaker_data_map_t;
  1002. speaker_data_map_t mSpeakersData;
  1003. };
  1004. const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml";
  1005. LLSpeakerVolumeStorage::LLSpeakerVolumeStorage()
  1006. {
  1007. load();
  1008. }
  1009. LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage()
  1010. {
  1011. save();
  1012. }
  1013. void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume)
  1014. {
  1015. mSpeakersData[speaker_id] = volume;
  1016. }
  1017. S32 LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id)
  1018. {
  1019. // Return value of -1 indicates no level is stored for this speaker
  1020. S32 ret_val = -1;
  1021. speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id);
  1022. if (it != mSpeakersData.end())
  1023. {
  1024. F32 f_val = it->second;
  1025. // volume can amplify by as much as 4x!
  1026. S32 ivol = (S32)(400.f * f_val * f_val);
  1027. ret_val = llclamp(ivol, 0, 400);
  1028. }
  1029. return ret_val;
  1030. }
  1031. void LLSpeakerVolumeStorage::load()
  1032. {
  1033. // load per-resident voice volume information
  1034. std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME);
  1035. LLSD settings_llsd;
  1036. llifstream file;
  1037. file.open(filename);
  1038. if (file.is_open())
  1039. {
  1040. LLSDSerialize::fromXML(settings_llsd, file);
  1041. }
  1042. for (LLSD::map_const_iterator iter = settings_llsd.beginMap();
  1043. iter != settings_llsd.endMap(); ++iter)
  1044. {
  1045. mSpeakersData.insert(std::make_pair(LLUUID(iter->first), (F32)iter->second.asReal()));
  1046. }
  1047. }
  1048. void LLSpeakerVolumeStorage::save()
  1049. {
  1050. // If we quit from the login screen we will not have an SL account
  1051. // name.  Don't try to save, otherwise we'll dump a file in
  1052. // C:Program FilesSecondLife or similar. JC
  1053. std::string user_dir = gDirUtilp->getLindenUserDir();
  1054. if (!user_dir.empty())
  1055. {
  1056. std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME);
  1057. LLSD settings_llsd;
  1058. for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter)
  1059. {
  1060. settings_llsd[iter->first.asString()] = iter->second;
  1061. }
  1062. llofstream file;
  1063. file.open(filename);
  1064. LLSDSerialize::toPrettyXML(settings_llsd, file);
  1065. }
  1066. }
  1067. ///////////////////////////////////////////////////////////////////////////////////////////////
  1068. LLVoiceClient::LLVoiceClient() :
  1069. mState(stateDisabled),
  1070. mSessionTerminateRequested(false),
  1071. mRelogRequested(false),
  1072. mConnected(false),
  1073. mPump(NULL),
  1074. mTuningMode(false),
  1075. mTuningEnergy(0.0f),
  1076. mTuningMicVolume(0),
  1077. mTuningMicVolumeDirty(true),
  1078. mTuningSpeakerVolume(0),
  1079. mTuningSpeakerVolumeDirty(true),
  1080. mTuningExitState(stateDisabled),
  1081. mAreaVoiceDisabled(false),
  1082. mAudioSession(NULL),
  1083. mAudioSessionChanged(false),
  1084. mNextAudioSession(NULL),
  1085. mCurrentParcelLocalID(0),
  1086. mNumberOfAliases(0),
  1087. mCommandCookie(0),
  1088. mLoginRetryCount(0),
  1089. mBuddyListMapPopulated(false),
  1090. mBlockRulesListReceived(false),
  1091. mAutoAcceptRulesListReceived(false),
  1092. mCaptureDeviceDirty(false),
  1093. mRenderDeviceDirty(false),
  1094. mSpatialCoordsDirty(false),
  1095. mPTTDirty(true),
  1096. mPTT(true),
  1097. mUsePTT(true),
  1098. mPTTIsMiddleMouse(false),
  1099. mPTTKey(0),
  1100. mPTTIsToggle(false),
  1101. mUserPTTState(false),
  1102. mMuteMic(false),
  1103. mFriendsListDirty(true),
  1104. mEarLocation(0),
  1105. mSpeakerVolumeDirty(true),
  1106. mSpeakerMuteDirty(true),
  1107. mMicVolume(0),
  1108. mMicVolumeDirty(true),
  1109. mVoiceEnabled(false),
  1110. mWriteInProgress(false),
  1111. mLipSyncEnabled(false)
  1112. {
  1113. gVoiceClient = this;
  1114. mAPIVersion = LLTrans::getString("NotConnected");
  1115. mSpeakerVolume = scale_speaker_volume(0);
  1116. #if LL_DARWIN || LL_LINUX || LL_SOLARIS
  1117. // HACK: THIS DOES NOT BELONG HERE
  1118. // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
  1119. // This should cause us to ignore SIGPIPE and handle the error through proper channels.
  1120. // This should really be set up elsewhere.  Where should it go?
  1121. signal(SIGPIPE, SIG_IGN);
  1122. // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
  1123. // Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that.
  1124. signal(SIGCHLD, SIG_IGN);
  1125. #endif
  1126. // set up state machine
  1127. setState(stateDisabled);
  1128. gIdleCallbacks.addFunction(idle, this);
  1129. }
  1130. //---------------------------------------------------
  1131. LLVoiceClient::~LLVoiceClient()
  1132. {
  1133. }
  1134. //----------------------------------------------
  1135. void LLVoiceClient::init(LLPumpIO *pump)
  1136. {
  1137. // constructor will set up gVoiceClient
  1138. LLVoiceClient::getInstance()->mPump = pump;
  1139. LLVoiceClient::getInstance()->updateSettings();
  1140. }
  1141. void LLVoiceClient::terminate()
  1142. {
  1143. if(gVoiceClient)
  1144. {
  1145. // gVoiceClient->leaveAudioSession();
  1146. gVoiceClient->logout();
  1147. // As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to.
  1148. // ms_sleep(2000);
  1149. gVoiceClient->connectorShutdown();
  1150. gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
  1151. // This will do unpleasant things on windows.
  1152. // killGateway();
  1153. // Don't do this anymore -- LLSingleton will take care of deleting the object.
  1154. // delete gVoiceClient;
  1155. // Hint to other code not to access the voice client anymore.
  1156. gVoiceClient = NULL;
  1157. }
  1158. }
  1159. //---------------------------------------------------
  1160. void LLVoiceClient::updateSettings()
  1161. {
  1162. setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat"));
  1163. setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled"));
  1164. std::string keyString = gSavedSettings.getString("PushToTalkButton");
  1165. setPTTKey(keyString);
  1166. setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
  1167. setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
  1168. std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
  1169. setCaptureDevice(inputDevice);
  1170. std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
  1171. setRenderDevice(outputDevice);
  1172. F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
  1173. setMicGain(mic_level);
  1174. setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled"));
  1175. }
  1176. /////////////////////////////
  1177. // utility functions
  1178. bool LLVoiceClient::writeString(const std::string &str)
  1179. {
  1180. bool result = false;
  1181. if(mConnected)
  1182. {
  1183. apr_status_t err;
  1184. apr_size_t size = (apr_size_t)str.size();
  1185. apr_size_t written = size;
  1186. //MARK: Turn this on to log outgoing XML
  1187. // LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
  1188. // check return code - sockets will fail (broken, etc.)
  1189. err = apr_socket_send(
  1190. mSocket->getSocket(),
  1191. (const char*)str.data(),
  1192. &written);
  1193. if(err == 0)
  1194. {
  1195. // Success.
  1196. result = true;
  1197. }
  1198. // TODO: handle partial writes (written is number of bytes written)
  1199. // Need to set socket to non-blocking before this will work.
  1200. // else if(APR_STATUS_IS_EAGAIN(err))
  1201. // {
  1202. // // 
  1203. // }
  1204. else
  1205. {
  1206. // Assume any socket error means something bad.  For now, just close the socket.
  1207. char buf[MAX_STRING];
  1208. LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
  1209. daemonDied();
  1210. }
  1211. }
  1212. return result;
  1213. }
  1214. /////////////////////////////
  1215. // session control messages
  1216. void LLVoiceClient::connectorCreate()
  1217. {
  1218. std::ostringstream stream;
  1219. std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
  1220. std::string loglevel = "0";
  1221. // Transition to stateConnectorStarted when the connector handle comes back.
  1222. setState(stateConnectorStarting);
  1223. std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
  1224. if(savedLogLevel != "-1")
  1225. {
  1226. LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
  1227. loglevel = "10";
  1228. }
  1229. stream 
  1230. << "<Request requestId="" << mCommandCookie++ << "" action="Connector.Create.1">"
  1231. << "<ClientName>V2 SDK</ClientName>"
  1232. << "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
  1233. << "<Mode>Normal</Mode>"
  1234. << "<Logging>"
  1235. << "<Folder>" << logpath << "</Folder>"
  1236. << "<FileNamePrefix>Connector</FileNamePrefix>"
  1237. << "<FileNameSuffix>.log</FileNameSuffix>"
  1238. << "<LogLevel>" << loglevel << "</LogLevel>"
  1239. << "</Logging>"
  1240. << "<Application>SecondLifeViewer.1</Application>"
  1241. << "</Request>nnn";
  1242. writeString(stream.str());
  1243. }
  1244. void LLVoiceClient::connectorShutdown()
  1245. {
  1246. setState(stateConnectorStopping);
  1247. if(!mConnectorHandle.empty())
  1248. {
  1249. std::ostringstream stream;
  1250. stream
  1251. << "<Request requestId="" << mCommandCookie++ << "" action="Connector.InitiateShutdown.1">"
  1252. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  1253. << "</Request>"
  1254. << "nnn";
  1255. mConnectorHandle.clear();
  1256. writeString(stream.str());
  1257. }
  1258. }
  1259. void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID)
  1260. {
  1261. mAccountFirstName = firstName;
  1262. mAccountLastName = lastName;
  1263. mAccountDisplayName = firstName;
  1264. mAccountDisplayName += " ";
  1265. mAccountDisplayName += lastName;
  1266. LL_INFOS("Voice") << "name "" << mAccountDisplayName << "" , ID " << agentID << LL_ENDL;
  1267. sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid();
  1268. mAccountName = nameFromID(agentID);
  1269. }
  1270. void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
  1271. {
  1272. if ( gAgent.getRegion() && mVoiceEnabled )
  1273. {
  1274. std::string url = 
  1275. gAgent.getRegion()->getCapability(
  1276. "ProvisionVoiceAccountRequest");
  1277. if ( url == "" ) return;
  1278. LLHTTPClient::post(
  1279. url,
  1280. LLSD(),
  1281. new LLViewerVoiceAccountProvisionResponder(retries));
  1282. }
  1283. }
  1284. void LLVoiceClient::login(
  1285. const std::string& account_name,
  1286. const std::string& password,
  1287. const std::string& voice_sip_uri_hostname,
  1288. const std::string& voice_account_server_uri)
  1289. {
  1290. mVoiceSIPURIHostName = voice_sip_uri_hostname;
  1291. mVoiceAccountServerURI = voice_account_server_uri;
  1292. if(!mAccountHandle.empty())
  1293. {
  1294. // Already logged in.
  1295. LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL;
  1296. // Don't process another login.
  1297. return;
  1298. }
  1299. else if ( account_name != mAccountName )
  1300. {
  1301. //TODO: error?
  1302. LL_WARNS("Voice") << "Wrong account name! " << account_name
  1303. << " instead of " << mAccountName << LL_ENDL;
  1304. }
  1305. else
  1306. {
  1307. mAccountPassword = password;
  1308. }
  1309. std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
  1310. if( !debugSIPURIHostName.empty() )
  1311. {
  1312. mVoiceSIPURIHostName = debugSIPURIHostName;
  1313. }
  1314. if( mVoiceSIPURIHostName.empty() )
  1315. {
  1316. // we have an empty account server name
  1317. // so we fall back to hardcoded defaults
  1318. if(sConnectingToAgni)
  1319. {
  1320. // Use the release account server
  1321. mVoiceSIPURIHostName = "bhr.vivox.com";
  1322. }
  1323. else
  1324. {
  1325. // Use the development account server
  1326. mVoiceSIPURIHostName = "bhd.vivox.com";
  1327. }
  1328. }
  1329. std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
  1330. if( !debugAccountServerURI.empty() )
  1331. {
  1332. mVoiceAccountServerURI = debugAccountServerURI;
  1333. }
  1334. if( mVoiceAccountServerURI.empty() )
  1335. {
  1336. // If the account server URI isn't specified, construct it from the SIP URI hostname
  1337. mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";
  1338. }
  1339. }
  1340. void LLVoiceClient::idle(void* user_data)
  1341. {
  1342. LLVoiceClient* self = (LLVoiceClient*)user_data;
  1343. self->stateMachine();
  1344. }
  1345. std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
  1346. {
  1347. std::string result = "UNKNOWN";
  1348. // Prevent copy-paste errors when updating this list...
  1349. #define CASE(x)  case x:  result = #x;  break
  1350. switch(inState)
  1351. {
  1352. CASE(stateDisableCleanup);
  1353. CASE(stateDisabled);
  1354. CASE(stateStart);
  1355. CASE(stateDaemonLaunched);
  1356. CASE(stateConnecting);
  1357. CASE(stateConnected);
  1358. CASE(stateIdle);
  1359. CASE(stateMicTuningStart);
  1360. CASE(stateMicTuningRunning);
  1361. CASE(stateMicTuningStop);
  1362. CASE(stateConnectorStart);
  1363. CASE(stateConnectorStarting);
  1364. CASE(stateConnectorStarted);
  1365. CASE(stateLoginRetry);
  1366. CASE(stateLoginRetryWait);
  1367. CASE(stateNeedsLogin);
  1368. CASE(stateLoggingIn);
  1369. CASE(stateLoggedIn);
  1370. CASE(stateCreatingSessionGroup);
  1371. CASE(stateNoChannel);
  1372. CASE(stateJoiningSession);
  1373. CASE(stateSessionJoined);
  1374. CASE(stateRunning);
  1375. CASE(stateLeavingSession);
  1376. CASE(stateSessionTerminated);
  1377. CASE(stateLoggingOut);
  1378. CASE(stateLoggedOut);
  1379. CASE(stateConnectorStopping);
  1380. CASE(stateConnectorStopped);
  1381. CASE(stateConnectorFailed);
  1382. CASE(stateConnectorFailedWaiting);
  1383. CASE(stateLoginFailed);
  1384. CASE(stateLoginFailedWaiting);
  1385. CASE(stateJoinSessionFailed);
  1386. CASE(stateJoinSessionFailedWaiting);
  1387. CASE(stateJail);
  1388. }
  1389. #undef CASE
  1390. return result;
  1391. }
  1392. std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
  1393. {
  1394. std::string result = "UNKNOWN";
  1395. // Prevent copy-paste errors when updating this list...
  1396. #define CASE(x)  case x:  result = #x;  break
  1397. switch(inStatus)
  1398. {
  1399. CASE(STATUS_LOGIN_RETRY);
  1400. CASE(STATUS_LOGGED_IN);
  1401. CASE(STATUS_JOINING);
  1402. CASE(STATUS_JOINED);
  1403. CASE(STATUS_LEFT_CHANNEL);
  1404. CASE(STATUS_VOICE_DISABLED);
  1405. CASE(STATUS_VOICE_ENABLED);
  1406. CASE(BEGIN_ERROR_STATUS);
  1407. CASE(ERROR_CHANNEL_FULL);
  1408. CASE(ERROR_CHANNEL_LOCKED);
  1409. CASE(ERROR_NOT_AVAILABLE);
  1410. CASE(ERROR_UNKNOWN);
  1411. default:
  1412. break;
  1413. }
  1414. #undef CASE
  1415. return result;
  1416. }
  1417. void LLVoiceClient::setState(state inState)
  1418. {
  1419. LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL;
  1420. mState = inState;
  1421. }
  1422. void LLVoiceClient::stateMachine()
  1423. {
  1424. if(gDisconnected)
  1425. {
  1426. // The viewer has been disconnected from the sim.  Disable voice.
  1427. setVoiceEnabled(false);
  1428. }
  1429. if(mVoiceEnabled)
  1430. {
  1431. updatePosition();
  1432. }
  1433. else if(mTuningMode)
  1434. {
  1435. // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled.
  1436. }
  1437. else
  1438. {
  1439. if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
  1440. {
  1441. // User turned off voice support.  Send the cleanup messages, close the socket, and reset.
  1442. if(!mConnected)
  1443. {
  1444. // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
  1445. LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
  1446. killGateway();
  1447. }
  1448. logout();
  1449. connectorShutdown();
  1450. setState(stateDisableCleanup);
  1451. }
  1452. }
  1453. // Check for parcel boundary crossing
  1454. {
  1455. LLViewerRegion *region = gAgent.getRegion();
  1456. LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
  1457. if(region && parcel)
  1458. {
  1459. S32 parcelLocalID = parcel->getLocalID();
  1460. std::string regionName = region->getName();
  1461. std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
  1462. // LL_DEBUGS("Voice") << "Region name = "" << regionName << "", parcel local ID = " << parcelLocalID << ", cap URI = "" << capURI << """ << LL_ENDL;
  1463. // The region name starts out empty and gets filled in later.  
  1464. // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
  1465. // If either is empty, wait for the next time around.
  1466. if(!regionName.empty())
  1467. {
  1468. if(!capURI.empty())
  1469. {
  1470. if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
  1471. {
  1472. // We have changed parcels.  Initiate a parcel channel lookup.
  1473. mCurrentParcelLocalID = parcelLocalID;
  1474. mCurrentRegionName = regionName;
  1475. parcelChanged();
  1476. }
  1477. }
  1478. else
  1479. {
  1480. LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability.  This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL;
  1481. }
  1482. }
  1483. }
  1484. }
  1485. switch(getState())
  1486. {
  1487. //MARK: stateDisableCleanup
  1488. case stateDisableCleanup:
  1489. // Clean up and reset everything. 
  1490. closeSocket();
  1491. deleteAllSessions();
  1492. deleteAllBuddies();
  1493. mConnectorHandle.clear();
  1494. mAccountHandle.clear();
  1495. mAccountPassword.clear();
  1496. mVoiceAccountServerURI.clear();
  1497. setState(stateDisabled);
  1498. break;
  1499. //MARK: stateDisabled
  1500. case stateDisabled:
  1501. if(mTuningMode || (mVoiceEnabled && !mAccountName.empty()))
  1502. {
  1503. setState(stateStart);
  1504. }
  1505. break;
  1506. //MARK: stateStart
  1507. case stateStart:
  1508. if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
  1509. {
  1510. // Voice is locked out, we must not launch the vivox daemon.
  1511. setState(stateJail);
  1512. }
  1513. else if(!isGatewayRunning())
  1514. {
  1515. if(true)
  1516. {
  1517. // Launch the voice daemon
  1518. // *FIX:Mani - Using the executable dir instead 
  1519. // of mAppRODataDir, the working directory from which the app
  1520. // is launched.
  1521. //std::string exe_path = gDirUtilp->getAppRODataDir();
  1522. std::string exe_path = gDirUtilp->getExecutableDir();
  1523. exe_path += gDirUtilp->getDirDelimiter();
  1524. #if LL_WINDOWS
  1525. exe_path += "SLVoice.exe";
  1526. #elif LL_DARWIN
  1527. exe_path += "../Resources/SLVoice";
  1528. #else
  1529. exe_path += "SLVoice";
  1530. #endif
  1531. // See if the vivox executable exists
  1532. llstat s;
  1533. if(!LLFile::stat(exe_path, &s))
  1534. {
  1535. // vivox executable exists.  Build the command line and launch the daemon.
  1536. // SLIM SDK: these arguments are no longer necessary.
  1537. // std::string args = " -p tcp -h -c";
  1538. std::string args;
  1539. std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
  1540. if(loglevel.empty())
  1541. {
  1542. loglevel = "-1"; // turn logging off completely
  1543. }
  1544. args += " -ll ";
  1545. args += loglevel;
  1546. LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL;
  1547. #if LL_WINDOWS
  1548. PROCESS_INFORMATION pinfo;
  1549. STARTUPINFOW sinfo;
  1550. memset(&sinfo, 0, sizeof(sinfo));
  1551. std::string exe_dir = gDirUtilp->getExecutableDir();
  1552. llutf16string exe_path16 = utf8str_to_utf16str(exe_path);
  1553. llutf16string exe_dir16 = utf8str_to_utf16str(exe_dir);
  1554. llutf16string args16 = utf8str_to_utf16str(args);
  1555. // Create a writeable copy to keep Windows happy.
  1556. U16 *argscpy_16 = new U16[args16.size() + 1];
  1557. wcscpy_s(argscpy_16,args16.size()+1,args16.c_str());
  1558. if(!CreateProcessW(exe_path16.c_str(), argscpy_16, NULL, NULL, FALSE, 0, NULL, exe_dir16.c_str(), &sinfo, &pinfo))
  1559. {
  1560. // DWORD dwErr = GetLastError();
  1561. }
  1562. else
  1563. {
  1564. // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
  1565. // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
  1566. sGatewayHandle = pinfo.hProcess;
  1567. CloseHandle(pinfo.hThread); // stops leaks - nothing else
  1568. }
  1569. delete[] argscpy_16;
  1570. #else // LL_WINDOWS
  1571. // This should be the same for mac and linux
  1572. {
  1573. std::vector<std::string> arglist;
  1574. arglist.push_back(exe_path);
  1575. // Split the argument string into separate strings for each argument
  1576. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  1577. boost::char_separator<char> sep(" ");
  1578. tokenizer tokens(args, sep);
  1579. tokenizer::iterator token_iter;
  1580. for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  1581. {
  1582. arglist.push_back(*token_iter);
  1583. }
  1584. // create an argv vector for the child process
  1585. char **fakeargv = new char*[arglist.size() + 1];
  1586. int i;
  1587. for(i=0; i < arglist.size(); i++)
  1588. fakeargv[i] = const_cast<char*>(arglist[i].c_str());
  1589. fakeargv[i] = NULL;
  1590. fflush(NULL); // flush all buffers before the child inherits them
  1591. pid_t id = vfork();
  1592. if(id == 0)
  1593. {
  1594. // child
  1595. execv(exe_path.c_str(), fakeargv);
  1596. // If we reach this point, the exec failed.
  1597. // Use _exit() instead of exit() per the vfork man page.
  1598. _exit(0);
  1599. }
  1600. // parent
  1601. delete[] fakeargv;
  1602. sGatewayPID = id;
  1603. }
  1604. #endif // LL_WINDOWS
  1605. mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort"));
  1606. }
  1607. else
  1608. {
  1609. LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
  1610. }
  1611. }
  1612. else
  1613. {
  1614. // SLIM SDK: port changed from 44124 to 44125.
  1615. // We can connect to a client gateway running on another host.  This is useful for testing.
  1616. // To do this, launch the gateway on a nearby host like this:
  1617. //  vivox-gw.exe -p tcp -i 0.0.0.0:44125
  1618. // and put that host's IP address here.
  1619. mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort"));
  1620. }
  1621. mUpdateTimer.start();
  1622. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  1623. setState(stateDaemonLaunched);
  1624. // Dirty the states we'll need to sync with the daemon when it comes up.
  1625. mPTTDirty = true;
  1626. mMicVolumeDirty = true;
  1627. mSpeakerVolumeDirty = true;
  1628. mSpeakerMuteDirty = true;
  1629. // These only need to be set if they're not default (i.e. empty string).
  1630. mCaptureDeviceDirty = !mCaptureDevice.empty();
  1631. mRenderDeviceDirty = !mRenderDevice.empty();
  1632. mMainSessionGroupHandle.clear();
  1633. }
  1634. break;
  1635. //MARK: stateDaemonLaunched
  1636. case stateDaemonLaunched:
  1637. if(mUpdateTimer.hasExpired())
  1638. {
  1639. LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
  1640. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  1641. if(!mSocket)
  1642. {
  1643. mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
  1644. }
  1645. mConnected = mSocket->blockingConnect(mDaemonHost);
  1646. if(mConnected)
  1647. {
  1648. setState(stateConnecting);
  1649. }
  1650. else
  1651. {
  1652. // If the connect failed, the socket may have been put into a bad state.  Delete it.
  1653. closeSocket();
  1654. }
  1655. }
  1656. break;
  1657. //MARK: stateConnecting
  1658. case stateConnecting:
  1659. // Can't do this until we have the pump available.
  1660. if(mPump)
  1661. {
  1662. // MBW -- Note to self: pumps and pipes examples in
  1663. //  indra/test/io.cpp
  1664. //  indra/test/llpipeutil.{cpp|h}
  1665. // Attach the pumps and pipes
  1666. LLPumpIO::chain_t readChain;
  1667. readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
  1668. readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
  1669. mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
  1670. setState(stateConnected);
  1671. }
  1672. break;
  1673. //MARK: stateConnected
  1674. case stateConnected:
  1675. // Initial devices query
  1676. getCaptureDevicesSendMessage();
  1677. getRenderDevicesSendMessage();
  1678. mLoginRetryCount = 0;
  1679. setState(stateIdle);
  1680. break;
  1681. //MARK: stateIdle
  1682. case stateIdle:
  1683. // This is the idle state where we're connected to the daemon but haven't set up a connector yet.
  1684. if(mTuningMode)
  1685. {
  1686. mTuningExitState = stateIdle;
  1687. setState(stateMicTuningStart);
  1688. }
  1689. else if(!mVoiceEnabled)
  1690. {
  1691. // We never started up the connector.  This will shut down the daemon.
  1692. setState(stateConnectorStopped);
  1693. }
  1694. else if(!mAccountName.empty())
  1695. {
  1696. LLViewerRegion *region = gAgent.getRegion();
  1697. if(region)
  1698. {
  1699. if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
  1700. {
  1701. if ( mAccountPassword.empty() )
  1702. {
  1703. requestVoiceAccountProvision();
  1704. }
  1705. setState(stateConnectorStart);
  1706. }
  1707. else
  1708. {
  1709. LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL;
  1710. }
  1711. }
  1712. }
  1713. break;
  1714. //MARK: stateMicTuningStart
  1715. case stateMicTuningStart:
  1716. if(mUpdateTimer.hasExpired())
  1717. {
  1718. if(mCaptureDeviceDirty || mRenderDeviceDirty)
  1719. {
  1720. // These can't be changed while in tuning mode.  Set them before starting.
  1721. std::ostringstream stream;
  1722. buildSetCaptureDevice(stream);
  1723. buildSetRenderDevice(stream);
  1724. if(!stream.str().empty())
  1725. {
  1726. writeString(stream.str());
  1727. }
  1728. // This will come around again in the same state and start the capture, after the timer expires.
  1729. mUpdateTimer.start();
  1730. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1731. }
  1732. else
  1733. {
  1734. // duration parameter is currently unused, per Mike S.
  1735. tuningCaptureStartSendMessage(10000);
  1736. setState(stateMicTuningRunning);
  1737. }
  1738. }
  1739. break;
  1740. //MARK: stateMicTuningRunning
  1741. case stateMicTuningRunning:
  1742. if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
  1743. {
  1744. // All of these conditions make us leave tuning mode.
  1745. setState(stateMicTuningStop);
  1746. }
  1747. else
  1748. {
  1749. // process mic/speaker volume changes
  1750. if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
  1751. {
  1752. std::ostringstream stream;
  1753. if(mTuningMicVolumeDirty)
  1754. {
  1755. LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
  1756. stream
  1757. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.SetMicLevel.1">"
  1758. << "<Level>" << mTuningMicVolume << "</Level>"
  1759. << "</Request>nnn";
  1760. }
  1761. if(mTuningSpeakerVolumeDirty)
  1762. {
  1763. stream
  1764. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.SetSpeakerLevel.1">"
  1765. << "<Level>" << mTuningSpeakerVolume << "</Level>"
  1766. << "</Request>nnn";
  1767. }
  1768. mTuningMicVolumeDirty = false;
  1769. mTuningSpeakerVolumeDirty = false;
  1770. if(!stream.str().empty())
  1771. {
  1772. writeString(stream.str());
  1773. }
  1774. }
  1775. }
  1776. break;
  1777. //MARK: stateMicTuningStop
  1778. case stateMicTuningStop:
  1779. {
  1780. // transition out of mic tuning
  1781. tuningCaptureStopSendMessage();
  1782. setState(mTuningExitState);
  1783. // if we exited just to change devices, this will keep us from re-entering too fast.
  1784. mUpdateTimer.start();
  1785. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1786. }
  1787. break;
  1788. //MARK: stateConnectorStart
  1789. case stateConnectorStart:
  1790. if(!mVoiceEnabled)
  1791. {
  1792. // We were never logged in.  This will shut down the connector.
  1793. setState(stateLoggedOut);
  1794. }
  1795. else if(!mVoiceAccountServerURI.empty())
  1796. {
  1797. connectorCreate();
  1798. }
  1799. break;
  1800. //MARK: stateConnectorStarting
  1801. case stateConnectorStarting: // waiting for connector handle
  1802. // connectorCreateResponse() will transition from here to stateConnectorStarted.
  1803. break;
  1804. //MARK: stateConnectorStarted
  1805. case stateConnectorStarted: // connector handle received
  1806. if(!mVoiceEnabled)
  1807. {
  1808. // We were never logged in.  This will shut down the connector.
  1809. setState(stateLoggedOut);
  1810. }
  1811. else
  1812. {
  1813. // The connector is started.  Send a login message.
  1814. setState(stateNeedsLogin);
  1815. }
  1816. break;
  1817. //MARK: stateLoginRetry
  1818. case stateLoginRetry:
  1819. if(mLoginRetryCount == 0)
  1820. {
  1821. // First retry -- display a message to the user
  1822. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
  1823. }
  1824. mLoginRetryCount++;
  1825. if(mLoginRetryCount > MAX_LOGIN_RETRIES)
  1826. {
  1827. LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
  1828. setState(stateLoginFailed);
  1829. }
  1830. else
  1831. {
  1832. LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
  1833. mUpdateTimer.start();
  1834. mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
  1835. setState(stateLoginRetryWait);
  1836. }
  1837. break;
  1838. //MARK: stateLoginRetryWait
  1839. case stateLoginRetryWait:
  1840. if(mUpdateTimer.hasExpired())
  1841. {
  1842. setState(stateNeedsLogin);
  1843. }
  1844. break;
  1845. //MARK: stateNeedsLogin
  1846. case stateNeedsLogin:
  1847. if(!mAccountPassword.empty())
  1848. {
  1849. setState(stateLoggingIn);
  1850. loginSendMessage();
  1851. }
  1852. break;
  1853. //MARK: stateLoggingIn
  1854. case stateLoggingIn: // waiting for account handle
  1855. // loginResponse() will transition from here to stateLoggedIn.
  1856. break;
  1857. //MARK: stateLoggedIn
  1858. case stateLoggedIn: // account handle received
  1859. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
  1860. // request the current set of block rules (we'll need them when updating the friends list)
  1861. accountListBlockRulesSendMessage();
  1862. // request the current set of auto-accept rules
  1863. accountListAutoAcceptRulesSendMessage();
  1864. // Set up the mute list observer if it hasn't been set up already.
  1865. if((!sMuteListListener_listening))
  1866. {
  1867. LLMuteList::getInstance()->addObserver(&mutelist_listener);
  1868. sMuteListListener_listening = true;
  1869. }
  1870. // Set up the friends list observer if it hasn't been set up already.
  1871. if(friendslist_listener == NULL)
  1872. {
  1873. friendslist_listener = new LLVoiceClientFriendsObserver;
  1874. LLAvatarTracker::instance().addObserver(friendslist_listener);
  1875. }
  1876. // Set the initial state of mic mute, local speaker volume, etc.
  1877. {
  1878. std::ostringstream stream;
  1879. buildLocalAudioUpdates(stream);
  1880. if(!stream.str().empty())
  1881. {
  1882. writeString(stream.str());
  1883. }
  1884. }
  1885. #if USE_SESSION_GROUPS
  1886. // create the main session group
  1887. sessionGroupCreateSendMessage();
  1888. setState(stateCreatingSessionGroup);
  1889. #else
  1890. // Not using session groups -- skip the stateCreatingSessionGroup state.
  1891. setState(stateNoChannel);
  1892. // Initial kick-off of channel lookup logic
  1893. parcelChanged();
  1894. #endif
  1895. break;
  1896. //MARK: stateCreatingSessionGroup
  1897. case stateCreatingSessionGroup:
  1898. if(mSessionTerminateRequested || !mVoiceEnabled)
  1899. {
  1900. // TODO: Question: is this the right way out of this state
  1901. setState(stateSessionTerminated);
  1902. }
  1903. else if(!mMainSessionGroupHandle.empty())
  1904. {
  1905. setState(stateNoChannel);
  1906. // Start looped recording (needed for "panic button" anti-griefing tool)
  1907. recordingLoopStart();
  1908. // Initial kick-off of channel lookup logic
  1909. parcelChanged();
  1910. }
  1911. break;
  1912. //MARK: stateNoChannel
  1913. case stateNoChannel:
  1914. // Do this here as well as inside sendPositionalUpdate().  
  1915. // 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.
  1916. sendFriendsListUpdates();
  1917. if(mSessionTerminateRequested || !mVoiceEnabled)
  1918. {
  1919. // TODO: Question: Is this the right way out of this state?
  1920. setState(stateSessionTerminated);
  1921. }
  1922. else if(mTuningMode)
  1923. {
  1924. mTuningExitState = stateNoChannel;
  1925. setState(stateMicTuningStart);
  1926. }
  1927. else if(sessionNeedsRelog(mNextAudioSession))
  1928. {
  1929. requestRelog();
  1930. setState(stateSessionTerminated);
  1931. }
  1932. else if(mNextAudioSession)
  1933. {
  1934. sessionState *oldSession = mAudioSession;
  1935. mAudioSession = mNextAudioSession;
  1936. if(!mAudioSession->mReconnect)
  1937. {
  1938. mNextAudioSession = NULL;
  1939. }
  1940. // The old session may now need to be deleted.
  1941. reapSession(oldSession);
  1942. if(!mAudioSession->mHandle.empty())
  1943. {
  1944. // Connect to a session by session handle
  1945. sessionMediaConnectSendMessage(mAudioSession);
  1946. }
  1947. else
  1948. {
  1949. // Connect to a session by URI
  1950. sessionCreateSendMessage(mAudioSession, true, false);
  1951. }
  1952. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
  1953. setState(stateJoiningSession);
  1954. }
  1955. else if(!mSpatialSessionURI.empty())
  1956. {
  1957. // If we're not headed elsewhere and have a spatial URI, return to spatial.
  1958. switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
  1959. }
  1960. break;
  1961. //MARK: stateJoiningSession
  1962. case stateJoiningSession: // waiting for session handle
  1963. // joinedAudioSession() will transition from here to stateSessionJoined.
  1964. if(!mVoiceEnabled)
  1965. {
  1966. // User bailed out during connect -- jump straight to teardown.
  1967. setState(stateSessionTerminated);
  1968. }
  1969. else if(mSessionTerminateRequested)
  1970. {
  1971. if(mAudioSession && !mAudioSession->mHandle.empty())
  1972. {
  1973. // Only allow direct exits from this state in p2p calls (for cancelling an invite).
  1974. // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
  1975. if(mAudioSession->mIsP2P)
  1976. {
  1977. sessionMediaDisconnectSendMessage(mAudioSession);
  1978. setState(stateSessionTerminated);
  1979. }
  1980. }
  1981. }
  1982. break;
  1983. //MARK: stateSessionJoined
  1984. case stateSessionJoined: // session handle received
  1985. // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
  1986. // before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
  1987. // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
  1988. // This is a cheap way to make sure both have happened before proceeding.
  1989. if(mAudioSession && mAudioSession->mVoiceEnabled)
  1990. {
  1991. // Dirty state that may need to be sync'ed with the daemon.
  1992. mPTTDirty = true;
  1993. mSpeakerVolumeDirty = true;
  1994. mSpatialCoordsDirty = true;
  1995. setState(stateRunning);
  1996. // Start the throttle timer
  1997. mUpdateTimer.start();
  1998. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1999. // Events that need to happen when a session is joined could go here.
  2000. // Maybe send initial spatial data?
  2001. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
  2002. }
  2003. else if(!mVoiceEnabled)
  2004. {
  2005. // User bailed out during connect -- jump straight to teardown.
  2006. setState(stateSessionTerminated);
  2007. }
  2008. else if(mSessionTerminateRequested)
  2009. {
  2010. // Only allow direct exits from this state in p2p calls (for cancelling an invite).
  2011. // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
  2012. if(mAudioSession && mAudioSession->mIsP2P)
  2013. {
  2014. sessionMediaDisconnectSendMessage(mAudioSession);
  2015. setState(stateSessionTerminated);
  2016. }
  2017. }
  2018. break;
  2019. //MARK: stateRunning
  2020. case stateRunning: // steady state
  2021. // Disabling voice or disconnect requested.
  2022. if(!mVoiceEnabled || mSessionTerminateRequested)
  2023. {
  2024. leaveAudioSession();
  2025. }
  2026. else
  2027. {
  2028. // Figure out whether the PTT state needs to change
  2029. {
  2030. bool newPTT;
  2031. if(mUsePTT)
  2032. {
  2033. // If configured to use PTT, track the user state.
  2034. newPTT = mUserPTTState;
  2035. }
  2036. else
  2037. {
  2038. // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
  2039. newPTT = true;
  2040. }
  2041. if(mMuteMic)
  2042. {
  2043. // This always overrides any other PTT setting.
  2044. newPTT = false;
  2045. }
  2046. // Dirty if state changed.
  2047. if(newPTT != mPTT)
  2048. {
  2049. mPTT = newPTT;
  2050. mPTTDirty = true;
  2051. }
  2052. }
  2053. if(!inSpatialChannel())
  2054. {
  2055. // When in a non-spatial channel, never send positional updates.
  2056. mSpatialCoordsDirty = false;
  2057. }
  2058. else
  2059. {
  2060. // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
  2061. enforceTether();
  2062. }
  2063. // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
  2064. // or every 10hz, whichever is sooner.
  2065. if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
  2066. {
  2067. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  2068. sendPositionalUpdate();
  2069. }
  2070. }
  2071. break;
  2072. //MARK: stateLeavingSession
  2073. case stateLeavingSession: // waiting for terminate session response
  2074. // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
  2075. break;
  2076. //MARK: stateSessionTerminated
  2077. case stateSessionTerminated:
  2078. // Must do this first, since it uses mAudioSession.
  2079. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
  2080. if(mAudioSession)
  2081. {
  2082. sessionState *oldSession = mAudioSession;
  2083. mAudioSession = NULL;
  2084. // We just notified status observers about this change.  Don't do it again.
  2085. mAudioSessionChanged = false;
  2086. // The old session may now need to be deleted.
  2087. reapSession(oldSession);
  2088. }
  2089. else
  2090. {
  2091. LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
  2092. }
  2093. // Always reset the terminate request flag when we get here.
  2094. mSessionTerminateRequested = false;
  2095. if(mVoiceEnabled && !mRelogRequested)
  2096. {
  2097. // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
  2098. setState(stateNoChannel);
  2099. }
  2100. else
  2101. {
  2102. // Shutting down voice, continue with disconnecting.
  2103. logout();
  2104. // The state machine will take it from here
  2105. mRelogRequested = false;
  2106. }
  2107. break;
  2108. //MARK: stateLoggingOut
  2109. case stateLoggingOut: // waiting for logout response
  2110. // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut.
  2111. break;
  2112. //MARK: stateLoggedOut
  2113. case stateLoggedOut: // logout response received
  2114. // Once we're logged out, all these things are invalid.
  2115. mAccountHandle.clear();
  2116. deleteAllSessions();
  2117. deleteAllBuddies();
  2118. if(mVoiceEnabled && !mRelogRequested)
  2119. {
  2120. // User was logged out, but wants to be logged in.  Send a new login request.
  2121. setState(stateNeedsLogin);
  2122. }
  2123. else
  2124. {
  2125. // shut down the connector
  2126. connectorShutdown();
  2127. }
  2128. break;
  2129. //MARK: stateConnectorStopping
  2130. case stateConnectorStopping: // waiting for connector stop
  2131. // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
  2132. break;
  2133. //MARK: stateConnectorStopped
  2134. case stateConnectorStopped: // connector stop received
  2135. setState(stateDisableCleanup);
  2136. break;
  2137. //MARK: stateConnectorFailed
  2138. case stateConnectorFailed:
  2139. setState(stateConnectorFailedWaiting);
  2140. break;
  2141. //MARK: stateConnectorFailedWaiting
  2142. case stateConnectorFailedWaiting:
  2143. if(!mVoiceEnabled)
  2144. {
  2145. setState(stateDisableCleanup);
  2146. }
  2147. break;
  2148. //MARK: stateLoginFailed
  2149. case stateLoginFailed:
  2150. setState(stateLoginFailedWaiting);
  2151. break;
  2152. //MARK: stateLoginFailedWaiting
  2153. case stateLoginFailedWaiting:
  2154. if(!mVoiceEnabled)
  2155. {
  2156. setState(stateDisableCleanup);
  2157. }
  2158. break;
  2159. //MARK: stateJoinSessionFailed
  2160. case stateJoinSessionFailed:
  2161. // Transition to error state.  Send out any notifications here.
  2162. if(mAudioSession)
  2163. {
  2164. LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
  2165. }
  2166. else
  2167. {
  2168. LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
  2169. }
  2170. notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
  2171. setState(stateJoinSessionFailedWaiting);
  2172. break;
  2173. //MARK: stateJoinSessionFailedWaiting
  2174. case stateJoinSessionFailedWaiting:
  2175. // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
  2176. // Region crossings may leave this state and try the join again.
  2177. if(mSessionTerminateRequested)
  2178. {
  2179. setState(stateSessionTerminated);
  2180. }
  2181. break;
  2182. //MARK: stateJail
  2183. case stateJail:
  2184. // We have given up.  Do nothing.
  2185. break;
  2186. }
  2187. if(mAudioSession && mAudioSession->mParticipantsChanged)
  2188. {
  2189. mAudioSession->mParticipantsChanged = false;
  2190. mAudioSessionChanged = true;
  2191. }
  2192. if(mAudioSessionChanged)
  2193. {
  2194. mAudioSessionChanged = false;
  2195. notifyParticipantObservers();
  2196. }
  2197. }
  2198. void LLVoiceClient::closeSocket(void)
  2199. {
  2200. mSocket.reset();
  2201. mConnected = false;
  2202. }
  2203. void LLVoiceClient::loginSendMessage()
  2204. {
  2205. std::ostringstream stream;
  2206. bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps");
  2207. stream
  2208. << "<Request requestId="" << mCommandCookie++ << "" action="Account.Login.1">"
  2209. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  2210. << "<AccountName>" << mAccountName << "</AccountName>"
  2211. << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
  2212. << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
  2213. << "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
  2214. << "<BuddyManagementMode>Application</BuddyManagementMode>"
  2215. << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
  2216. << (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"")
  2217. << "</Request>nnn";
  2218. writeString(stream.str());
  2219. }
  2220. void LLVoiceClient::logout()
  2221. {
  2222. // Ensure that we'll re-request provisioning before logging in again
  2223. mAccountPassword.clear();
  2224. mVoiceAccountServerURI.clear();
  2225. setState(stateLoggingOut);
  2226. logoutSendMessage();
  2227. }
  2228. void LLVoiceClient::logoutSendMessage()
  2229. {
  2230. if(!mAccountHandle.empty())
  2231. {
  2232. std::ostringstream stream;
  2233. stream
  2234. << "<Request requestId="" << mCommandCookie++ << "" action="Account.Logout.1">"
  2235. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  2236. << "</Request>"
  2237. << "nnn";
  2238. mAccountHandle.clear();
  2239. writeString(stream.str());
  2240. }
  2241. }
  2242. void LLVoiceClient::accountListBlockRulesSendMessage()
  2243. {
  2244. if(!mAccountHandle.empty())
  2245. {
  2246. std::ostringstream stream;
  2247. LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
  2248. stream
  2249. << "<Request requestId="" << mCommandCookie++ << "" action="Account.ListBlockRules.1">"
  2250. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  2251. << "</Request>"
  2252. << "nnn";
  2253. writeString(stream.str());
  2254. }
  2255. }
  2256. void LLVoiceClient::accountListAutoAcceptRulesSendMessage()
  2257. {
  2258. if(!mAccountHandle.empty())
  2259. {
  2260. std::ostringstream stream;
  2261. LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
  2262. stream
  2263. << "<Request requestId="" << mCommandCookie++ << "" action="Account.ListAutoAcceptRules.1">"
  2264. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  2265. << "</Request>"
  2266. << "nnn";
  2267. writeString(stream.str());
  2268. }
  2269. }
  2270. void LLVoiceClient::sessionGroupCreateSendMessage()
  2271. {
  2272. if(!mAccountHandle.empty())
  2273. {
  2274. std::ostringstream stream;
  2275. LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
  2276. stream
  2277. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.Create.1">"
  2278. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  2279. << "<Type>Normal</Type>"
  2280. << "</Request>"
  2281. << "nnn";
  2282. writeString(stream.str());
  2283. }
  2284. }
  2285. void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
  2286. {
  2287. LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
  2288. session->mCreateInProgress = true;
  2289. if(startAudio)
  2290. {
  2291. session->mMediaConnectInProgress = true;
  2292. }
  2293. std::ostringstream stream;
  2294. stream
  2295. << "<Request requestId="" << session->mSIPURI << "" action="Session.Create.1">"
  2296. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  2297. << "<URI>" << session->mSIPURI << "</URI>";
  2298. static const std::string allowed_chars =
  2299. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  2300. "0123456789"
  2301. "-._~";
  2302. if(!session->mHash.empty())
  2303. {
  2304. stream
  2305. << "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
  2306. << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
  2307. }
  2308. stream
  2309. << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
  2310. << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
  2311. << "<Name>" << mChannelName << "</Name>"
  2312. << "</Request>nnn";
  2313. writeString(stream.str());
  2314. }
  2315. void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
  2316. {
  2317. LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
  2318. session->mCreateInProgress = true;
  2319. if(startAudio)
  2320. {
  2321. session->mMediaConnectInProgress = true;
  2322. }
  2323. std::string password;
  2324. if(!session->mHash.empty())
  2325. {
  2326. static const std::string allowed_chars =
  2327. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  2328. "0123456789"
  2329. "-._~"
  2330. ;
  2331. password = LLURI::escape(session->mHash, allowed_chars);
  2332. }
  2333. std::ostringstream stream;
  2334. stream
  2335. << "<Request requestId="" << session->mSIPURI << "" action="SessionGroup.AddSession.1">"
  2336. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  2337. << "<URI>" << session->mSIPURI << "</URI>"
  2338. << "<Name>" << mChannelName << "</Name>"
  2339. << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
  2340. << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
  2341. << "<Password>" << password << "</Password>"
  2342. << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
  2343. << "</Request>nnn"
  2344. ;
  2345. writeString(stream.str());
  2346. }
  2347. void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
  2348. {
  2349. LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL;
  2350. session->mMediaConnectInProgress = true;
  2351. std::ostringstream stream;
  2352. stream
  2353. << "<Request requestId="" << session->mHandle << "" action="Session.MediaConnect.1">"
  2354. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  2355. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  2356. << "<Media>Audio</Media>"
  2357. << "</Request>nnn";
  2358. writeString(stream.str());
  2359. }
  2360. void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session)
  2361. {
  2362. LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
  2363. std::ostringstream stream;
  2364. stream
  2365. << "<Request requestId="" << session->mHandle << "" action="Session.TextConnect.1">"
  2366. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  2367. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  2368. << "</Request>nnn";
  2369. writeString(stream.str());
  2370. }
  2371. void LLVoiceClient::sessionTerminate()
  2372. {
  2373. mSessionTerminateRequested = true;
  2374. }
  2375. void LLVoiceClient::requestRelog()
  2376. {
  2377. mSessionTerminateRequested = true;
  2378. mRelogRequested = true;
  2379. }
  2380. void LLVoiceClient::leaveAudioSession()
  2381. {
  2382. if(mAudioSession)
  2383. {
  2384. LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
  2385. switch(getState())
  2386. {
  2387. case stateNoChannel:
  2388. // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
  2389. // Skip the join failed transition state so we don't send out error notifications.
  2390. setState(stateJoinSessionFailedWaiting);
  2391. break;
  2392. case stateJoiningSession:
  2393. case stateSessionJoined:
  2394. case stateRunning:
  2395. if(!mAudioSession->mHandle.empty())
  2396. {
  2397. #if RECORD_EVERYTHING
  2398. // HACK: for testing only
  2399. // Save looped recording
  2400. std::string savepath("/tmp/vivoxrecording");
  2401. {
  2402. time_t now = time(NULL);
  2403. const size_t BUF_SIZE = 64;
  2404. char time_str[BUF_SIZE]; /* Flawfinder: ignore */
  2405. strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
  2406. savepath += time_str;
  2407. }
  2408. recordingLoopSave(savepath);
  2409. #endif
  2410. sessionMediaDisconnectSendMessage(mAudioSession);
  2411. setState(stateLeavingSession);
  2412. }
  2413. else
  2414. {
  2415. LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
  2416. setState(stateSessionTerminated);
  2417. }
  2418. break;
  2419. case stateJoinSessionFailed:
  2420. case stateJoinSessionFailedWaiting:
  2421. setState(stateSessionTerminated);
  2422. break;
  2423. default:
  2424. LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
  2425. break;
  2426. }
  2427. }
  2428. else
  2429. {
  2430. LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
  2431. setState(stateSessionTerminated);
  2432. }
  2433. }
  2434. void LLVoiceClient::sessionTerminateSendMessage(sessionState *session)
  2435. {
  2436. std::ostringstream stream;
  2437. LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
  2438. stream
  2439. << "<Request requestId="" << mCommandCookie++ << "" action="Session.Terminate.1">"
  2440. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  2441. << "</Request>nnn";
  2442. writeString(stream.str());
  2443. }
  2444. void LLVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
  2445. {
  2446. std::ostringstream stream;
  2447. LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;
  2448. stream
  2449. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.Terminate.1">"
  2450. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  2451. << "</Request>nnn";
  2452. writeString(stream.str());
  2453. }
  2454. void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
  2455. {
  2456. std::ostringstream stream;
  2457. LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
  2458. stream
  2459. << "<Request requestId="" << mCommandCookie++ << "" action="Session.MediaDisconnect.1">"
  2460. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  2461. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  2462. << "<Media>Audio</Media>"
  2463. << "</Request>nnn";
  2464. writeString(stream.str());
  2465. }
  2466. void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
  2467. {
  2468. std::ostringstream stream;
  2469. LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;
  2470. stream
  2471. << "<Request requestId="" << mCommandCookie++ << "" action="Session.TextDisconnect.1">"
  2472. << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
  2473. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  2474. << "</Request>nnn";
  2475. writeString(stream.str());
  2476. }
  2477. void LLVoiceClient::getCaptureDevicesSendMessage()
  2478. {
  2479. std::ostringstream stream;
  2480. stream
  2481. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.GetCaptureDevices.1">"
  2482. << "</Request>nnn";
  2483. writeString(stream.str());
  2484. }
  2485. void LLVoiceClient::getRenderDevicesSendMessage()
  2486. {
  2487. std::ostringstream stream;
  2488. stream
  2489. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.GetRenderDevices.1">"
  2490. << "</Request>nnn";
  2491. writeString(stream.str());
  2492. }
  2493. void LLVoiceClient::clearCaptureDevices()
  2494. {
  2495. LL_DEBUGS("Voice") << "called" << LL_ENDL;
  2496. mCaptureDevices.clear();
  2497. }
  2498. void LLVoiceClient::addCaptureDevice(const std::string& name)
  2499. {
  2500. LL_DEBUGS("Voice") << name << LL_ENDL;
  2501. mCaptureDevices.push_back(name);
  2502. }
  2503. LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices()
  2504. {
  2505. return &mCaptureDevices;
  2506. }
  2507. void LLVoiceClient::setCaptureDevice(const std::string& name)
  2508. {
  2509. if(name == "Default")
  2510. {
  2511. if(!mCaptureDevice.empty())
  2512. {
  2513. mCaptureDevice.clear();
  2514. mCaptureDeviceDirty = true;
  2515. }
  2516. }
  2517. else
  2518. {
  2519. if(mCaptureDevice != name)
  2520. {
  2521. mCaptureDevice = name;
  2522. mCaptureDeviceDirty = true;
  2523. }
  2524. }
  2525. }
  2526. void LLVoiceClient::clearRenderDevices()
  2527. {
  2528. LL_DEBUGS("Voice") << "called" << LL_ENDL;
  2529. mRenderDevices.clear();
  2530. }
  2531. void LLVoiceClient::addRenderDevice(const std::string& name)
  2532. {
  2533. LL_DEBUGS("Voice") << name << LL_ENDL;
  2534. mRenderDevices.push_back(name);
  2535. }
  2536. LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices()
  2537. {
  2538. return &mRenderDevices;
  2539. }
  2540. void LLVoiceClient::setRenderDevice(const std::string& name)
  2541. {
  2542. if(name == "Default")
  2543. {
  2544. if(!mRenderDevice.empty())
  2545. {
  2546. mRenderDevice.clear();
  2547. mRenderDeviceDirty = true;
  2548. }
  2549. }
  2550. else
  2551. {
  2552. if(mRenderDevice != name)
  2553. {
  2554. mRenderDevice = name;
  2555. mRenderDeviceDirty = true;
  2556. }
  2557. }
  2558. }
  2559. void LLVoiceClient::tuningStart()
  2560. {
  2561. mTuningMode = true;
  2562. if(getState() >= stateNoChannel)
  2563. {
  2564. sessionTerminate();
  2565. }
  2566. }
  2567. void LLVoiceClient::tuningStop()
  2568. {
  2569. mTuningMode = false;
  2570. }
  2571. bool LLVoiceClient::inTuningMode()
  2572. {
  2573. bool result = false;
  2574. switch(getState())
  2575. {
  2576. case stateMicTuningRunning:
  2577. result = true;
  2578. break;
  2579. default:
  2580. break;
  2581. }
  2582. return result;
  2583. }
  2584. void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
  2585. {
  2586. mTuningAudioFile = name;
  2587. std::ostringstream stream;
  2588. stream
  2589. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.RenderAudioStart.1">"
  2590.     << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
  2591.     << "<Loop>" << (loop?"1":"0") << "</Loop>"
  2592. << "</Request>nnn";
  2593. writeString(stream.str());
  2594. }
  2595. void LLVoiceClient::tuningRenderStopSendMessage()
  2596. {
  2597. std::ostringstream stream;
  2598. stream
  2599. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.RenderAudioStop.1">"
  2600.     << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
  2601. << "</Request>nnn";
  2602. writeString(stream.str());
  2603. }
  2604. void LLVoiceClient::tuningCaptureStartSendMessage(int duration)
  2605. {
  2606. LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
  2607. std::ostringstream stream;
  2608. stream
  2609. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.CaptureAudioStart.1">"
  2610.     << "<Duration>" << duration << "</Duration>"
  2611. << "</Request>nnn";
  2612. writeString(stream.str());
  2613. }
  2614. void LLVoiceClient::tuningCaptureStopSendMessage()
  2615. {
  2616. LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL;
  2617. std::ostringstream stream;
  2618. stream
  2619. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.CaptureAudioStop.1">"
  2620. << "</Request>nnn";
  2621. writeString(stream.str());
  2622. mTuningEnergy = 0.0f;
  2623. }
  2624. void LLVoiceClient::tuningSetMicVolume(float volume)
  2625. {
  2626. int scaled_volume = scale_mic_volume(volume);
  2627. if(scaled_volume != mTuningMicVolume)
  2628. {
  2629. mTuningMicVolume = scaled_volume;
  2630. mTuningMicVolumeDirty = true;
  2631. }
  2632. }
  2633. void LLVoiceClient::tuningSetSpeakerVolume(float volume)
  2634. {
  2635. int scaled_volume = scale_speaker_volume(volume);
  2636. if(scaled_volume != mTuningSpeakerVolume)
  2637. {
  2638. mTuningSpeakerVolume = scaled_volume;
  2639. mTuningSpeakerVolumeDirty = true;
  2640. }
  2641. }
  2642. float LLVoiceClient::tuningGetEnergy(void)
  2643. {
  2644. return mTuningEnergy;
  2645. }
  2646. bool LLVoiceClient::deviceSettingsAvailable()
  2647. {
  2648. bool result = true;
  2649. if(!mConnected)
  2650. result = false;
  2651. if(mRenderDevices.empty())
  2652. result = false;
  2653. return result;
  2654. }
  2655. void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
  2656. {
  2657. if(clearCurrentList)
  2658. {
  2659. clearCaptureDevices();
  2660. clearRenderDevices();
  2661. }
  2662. getCaptureDevicesSendMessage();
  2663. getRenderDevicesSendMessage();
  2664. }
  2665. void LLVoiceClient::daemonDied()
  2666. {
  2667. // The daemon died, so the connection is gone.  Reset everything and start over.
  2668. LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL;
  2669. // Try to relaunch the daemon
  2670. setState(stateDisableCleanup);
  2671. }
  2672. void LLVoiceClient::giveUp()
  2673. {
  2674. // All has failed.  Clean up and stop trying.
  2675. closeSocket();
  2676. deleteAllSessions();
  2677. deleteAllBuddies();
  2678. setState(stateJail);
  2679. }
  2680. static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
  2681. {
  2682. F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the  new position and velocity
  2683. F64 npos[3];
  2684. // The original XML command was sent like this:
  2685. /*
  2686. << "<Position>"
  2687. << "<X>" << pos[VX] << "</X>"
  2688. << "<Y>" << pos[VZ] << "</Y>"
  2689. << "<Z>" << pos[VY] << "</Z>"
  2690. << "</Position>"
  2691. << "<Velocity>"
  2692. << "<X>" << mAvatarVelocity[VX] << "</X>"
  2693. << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
  2694. << "<Z>" << mAvatarVelocity[VY] << "</Z>"
  2695. << "</Velocity>"
  2696. << "<AtOrientation>"
  2697. << "<X>" << l.mV[VX] << "</X>"
  2698. << "<Y>" << u.mV[VX] << "</Y>"
  2699. << "<Z>" << a.mV[VX] << "</Z>"
  2700. << "</AtOrientation>"
  2701. << "<UpOrientation>"
  2702. << "<X>" << l.mV[VZ] << "</X>"
  2703. << "<Y>" << u.mV[VY] << "</Y>"
  2704. << "<Z>" << a.mV[VZ] << "</Z>"
  2705. << "</UpOrientation>"
  2706. << "<LeftOrientation>"
  2707. << "<X>" << l.mV [VY] << "</X>"
  2708. << "<Y>" << u.mV [VZ] << "</Y>"
  2709. << "<Z>" << a.mV [VY] << "</Z>"
  2710. << "</LeftOrientation>";
  2711. */
  2712. #if 1
  2713. // This was the original transform done when building the XML command
  2714. nat[0] = left.mV[VX];
  2715. nat[1] = up.mV[VX];
  2716. nat[2] = at.mV[VX];
  2717. nup[0] = left.mV[VZ];
  2718. nup[1] = up.mV[VY];
  2719. nup[2] = at.mV[VZ];
  2720. nl[0] = left.mV[VY];
  2721. nl[1] = up.mV[VZ];
  2722. nl[2] = at.mV[VY];
  2723. npos[0] = pos.mdV[VX];
  2724. npos[1] = pos.mdV[VZ];
  2725. npos[2] = pos.mdV[VY];
  2726. nvel[0] = vel.mV[VX];
  2727. nvel[1] = vel.mV[VZ];
  2728. nvel[2] = vel.mV[VY];
  2729. for(int i=0;i<3;++i) {
  2730. at.mV[i] = nat[i];
  2731. up.mV[i] = nup[i];
  2732. left.mV[i] = nl[i];
  2733. pos.mdV[i] = npos[i];
  2734. }
  2735. // This was the original transform done in the SDK
  2736. nat[0] = at.mV[2];
  2737. nat[1] = 0; // y component of at vector is always 0, this was up[2]
  2738. nat[2] = -1 * left.mV[2];
  2739. // We override whatever the application gives us
  2740. nup[0] = 0; // x component of up vector is always 0
  2741. nup[1] = 1; // y component of up vector is always 1
  2742. nup[2] = 0; // z component of up vector is always 0
  2743. nl[0] = at.mV[0];
  2744. nl[1] = 0;  // y component of left vector is always zero, this was up[0]
  2745. nl[2] = -1 * left.mV[0];
  2746. npos[2] = pos.mdV[2] * -1.0;
  2747. npos[1] = pos.mdV[1];
  2748. npos[0] = pos.mdV[0];
  2749. for(int i=0;i<3;++i) {
  2750. at.mV[i] = nat[i];
  2751. up.mV[i] = nup[i];
  2752. left.mV[i] = nl[i];
  2753. pos.mdV[i] = npos[i];
  2754. }
  2755. #else
  2756. // This is the compose of the two transforms (at least, that's what I'm trying for)
  2757. nat[0] = at.mV[VX];
  2758. nat[1] = 0; // y component of at vector is always 0, this was up[2]
  2759. nat[2] = -1 * up.mV[VZ];
  2760. // We override whatever the application gives us
  2761. nup[0] = 0; // x component of up vector is always 0
  2762. nup[1] = 1; // y component of up vector is always 1
  2763. nup[2] = 0; // z component of up vector is always 0
  2764. nl[0] = left.mV[VX];
  2765. nl[1] = 0;  // y component of left vector is always zero, this was up[0]
  2766. nl[2] = -1 * left.mV[VY];
  2767. npos[0] = pos.mdV[VX];
  2768. npos[1] = pos.mdV[VZ];
  2769. npos[2] = pos.mdV[VY] * -1.0;
  2770. nvel[0] = vel.mV[VX];
  2771. nvel[1] = vel.mV[VZ];
  2772. nvel[2] = vel.mV[VY];
  2773. for(int i=0;i<3;++i) {
  2774. at.mV[i] = nat[i];
  2775. up.mV[i] = nup[i];
  2776. left.mV[i] = nl[i];
  2777. pos.mdV[i] = npos[i];
  2778. }
  2779. #endif
  2780. }
  2781. void LLVoiceClient::sendPositionalUpdate(void)
  2782. {
  2783. std::ostringstream stream;
  2784. if(mSpatialCoordsDirty)
  2785. {
  2786. LLVector3 l, u, a, vel;
  2787. LLVector3d pos;
  2788. mSpatialCoordsDirty = false;
  2789. // Always send both speaker and listener positions together.
  2790. stream << "<Request requestId="" << mCommandCookie++ << "" action="Session.Set3DPosition.1">"
  2791. << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
  2792. stream << "<SpeakerPosition>";
  2793. // LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
  2794. l = mAvatarRot.getLeftRow();
  2795. u = mAvatarRot.getUpRow();
  2796. a = mAvatarRot.getFwdRow();
  2797. pos = mAvatarPosition;
  2798. vel = mAvatarVelocity;
  2799. // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
  2800. // The old transform is replicated by this function.
  2801. oldSDKTransform(l, u, a, pos, vel);
  2802. stream 
  2803. << "<Position>"
  2804. << "<X>" << pos.mdV[VX] << "</X>"
  2805. << "<Y>" << pos.mdV[VY] << "</Y>"
  2806. << "<Z>" << pos.mdV[VZ] << "</Z>"
  2807. << "</Position>"
  2808. << "<Velocity>"
  2809. << "<X>" << vel.mV[VX] << "</X>"
  2810. << "<Y>" << vel.mV[VY] << "</Y>"
  2811. << "<Z>" << vel.mV[VZ] << "</Z>"
  2812. << "</Velocity>"
  2813. << "<AtOrientation>"
  2814. << "<X>" << a.mV[VX] << "</X>"
  2815. << "<Y>" << a.mV[VY] << "</Y>"
  2816. << "<Z>" << a.mV[VZ] << "</Z>"
  2817. << "</AtOrientation>"
  2818. << "<UpOrientation>"
  2819. << "<X>" << u.mV[VX] << "</X>"
  2820. << "<Y>" << u.mV[VY] << "</Y>"
  2821. << "<Z>" << u.mV[VZ] << "</Z>"
  2822. << "</UpOrientation>"
  2823. << "<LeftOrientation>"
  2824. << "<X>" << l.mV [VX] << "</X>"
  2825. << "<Y>" << l.mV [VY] << "</Y>"
  2826. << "<Z>" << l.mV [VZ] << "</Z>"
  2827. << "</LeftOrientation>";
  2828. stream << "</SpeakerPosition>";
  2829. stream << "<ListenerPosition>";
  2830. LLVector3d earPosition;
  2831. LLVector3 earVelocity;
  2832. LLMatrix3 earRot;
  2833. switch(mEarLocation)
  2834. {
  2835. case earLocCamera:
  2836. default:
  2837. earPosition = mCameraPosition;
  2838. earVelocity = mCameraVelocity;
  2839. earRot = mCameraRot;
  2840. break;
  2841. case earLocAvatar:
  2842. earPosition = mAvatarPosition;
  2843. earVelocity = mAvatarVelocity;
  2844. earRot = mAvatarRot;
  2845. break;
  2846. case earLocMixed:
  2847. earPosition = mAvatarPosition;
  2848. earVelocity = mAvatarVelocity;
  2849. earRot = mCameraRot;
  2850. break;
  2851. }
  2852. l = earRot.getLeftRow();
  2853. u = earRot.getUpRow();
  2854. a = earRot.getFwdRow();
  2855. pos = earPosition;
  2856. vel = earVelocity;
  2857. // LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
  2858. oldSDKTransform(l, u, a, pos, vel);
  2859. stream 
  2860. << "<Position>"
  2861. << "<X>" << pos.mdV[VX] << "</X>"
  2862. << "<Y>" << pos.mdV[VY] << "</Y>"
  2863. << "<Z>" << pos.mdV[VZ] << "</Z>"
  2864. << "</Position>"
  2865. << "<Velocity>"
  2866. << "<X>" << vel.mV[VX] << "</X>"
  2867. << "<Y>" << vel.mV[VY] << "</Y>"
  2868. << "<Z>" << vel.mV[VZ] << "</Z>"
  2869. << "</Velocity>"
  2870. << "<AtOrientation>"
  2871. << "<X>" << a.mV[VX] << "</X>"
  2872. << "<Y>" << a.mV[VY] << "</Y>"
  2873. << "<Z>" << a.mV[VZ] << "</Z>"
  2874. << "</AtOrientation>"
  2875. << "<UpOrientation>"
  2876. << "<X>" << u.mV[VX] << "</X>"
  2877. << "<Y>" << u.mV[VY] << "</Y>"
  2878. << "<Z>" << u.mV[VZ] << "</Z>"
  2879. << "</UpOrientation>"
  2880. << "<LeftOrientation>"
  2881. << "<X>" << l.mV [VX] << "</X>"
  2882. << "<Y>" << l.mV [VY] << "</Y>"
  2883. << "<Z>" << l.mV [VZ] << "</Z>"
  2884. << "</LeftOrientation>";
  2885. stream << "</ListenerPosition>";
  2886. stream << "</Request>nnn";
  2887. }
  2888. if(mAudioSession && mAudioSession->mVolumeDirty)
  2889. {
  2890. participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
  2891. mAudioSession->mVolumeDirty = false;
  2892. for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
  2893. {
  2894. participantState *p = iter->second;
  2895. if(p->mVolumeDirty)
  2896. {
  2897. // Can't set volume/mute for yourself
  2898. if(!p->mIsSelf)
  2899. {
  2900. int volume = 56; // nominal default value
  2901. bool mute = p->mOnMuteList;
  2902. if(p->mUserVolume != -1)
  2903. {
  2904. // scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal")
  2905. if(p->mUserVolume < 100)
  2906. volume = (p->mUserVolume * 56) / 100;
  2907. else
  2908. volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56;
  2909. }
  2910. else if(p->mVolume != -1)
  2911. {
  2912. // Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent)
  2913. volume = p->mVolume;
  2914. }
  2915. if(mute)
  2916. {
  2917. // SetParticipantMuteForMe doesn't work in p2p sessions.
  2918. // If we want the user to be muted, set their volume to 0 as well.
  2919. // This isn't perfect, but it will at least reduce their volume to a minimum.
  2920. volume = 0;
  2921. }
  2922. if(volume == 0)
  2923. mute = true;
  2924. LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
  2925. // SLIM SDK: Send both volume and mute commands.
  2926. // Send a "volume for me" command for the user.
  2927. stream << "<Request requestId="" << mCommandCookie++ << "" action="Session.SetParticipantVolumeForMe.1">"
  2928. << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
  2929. << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
  2930. << "<Volume>" << volume << "</Volume>"
  2931. << "</Request>nnn";
  2932. if(!mAudioSession->mIsP2P)
  2933. {
  2934. // Send a "mute for me" command for the user
  2935. // Doesn't work in P2P sessions
  2936. stream << "<Request requestId="" << mCommandCookie++ << "" action="Session.SetParticipantMuteForMe.1">"
  2937. << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
  2938. << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
  2939. << "<Mute>" << (mute?"1":"0") << "</Mute>"
  2940. << "<Scope>Audio</Scope>"
  2941. << "</Request>nnn";
  2942. }
  2943. }
  2944. p->mVolumeDirty = false;
  2945. }
  2946. }
  2947. }
  2948. buildLocalAudioUpdates(stream);
  2949. if(!stream.str().empty())
  2950. {
  2951. writeString(stream.str());
  2952. }
  2953. // Friends list updates can be huge, especially on the first voice login of an account with lots of friends.
  2954. // Batching them all together can choke SLVoice, so send them in separate writes.
  2955. sendFriendsListUpdates();
  2956. }
  2957. void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
  2958. {
  2959. if(mCaptureDeviceDirty)
  2960. {
  2961. LL_DEBUGS("Voice") << "Setting input device = "" << mCaptureDevice << """ << LL_ENDL;
  2962. stream 
  2963. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.SetCaptureDevice.1">"
  2964. << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
  2965. << "</Request>"
  2966. << "nnn";
  2967. mCaptureDeviceDirty = false;
  2968. }
  2969. }
  2970. void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
  2971. {
  2972. if(mRenderDeviceDirty)
  2973. {
  2974. LL_DEBUGS("Voice") << "Setting output device = "" << mRenderDevice << """ << LL_ENDL;
  2975. stream
  2976. << "<Request requestId="" << mCommandCookie++ << "" action="Aux.SetRenderDevice.1">"
  2977. << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
  2978. << "</Request>"
  2979. << "nnn";
  2980. mRenderDeviceDirty = false;
  2981. }
  2982. }
  2983. void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
  2984. {
  2985. buildSetCaptureDevice(stream);
  2986. buildSetRenderDevice(stream);
  2987. if(mPTTDirty)
  2988. {
  2989. mPTTDirty = false;
  2990. // Send a local mute command.
  2991. // NOTE that the state of "PTT" is the inverse of "local mute".
  2992. //   (i.e. when PTT is true, we send a mute command with "false", and vice versa)
  2993. LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL;
  2994. stream << "<Request requestId="" << mCommandCookie++ << "" action="Connector.MuteLocalMic.1">"
  2995. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  2996. << "<Value>" << (mPTT?"false":"true") << "</Value>"
  2997. << "</Request>nnn";
  2998. }
  2999. if(mSpeakerMuteDirty)
  3000. {
  3001. const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false");
  3002. mSpeakerMuteDirty = false;
  3003. LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL;
  3004. stream << "<Request requestId="" << mCommandCookie++ << "" action="Connector.MuteLocalSpeaker.1">"
  3005. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  3006. << "<Value>" << muteval << "</Value>"
  3007. << "</Request>nnn";
  3008. }
  3009. if(mSpeakerVolumeDirty)
  3010. {
  3011. mSpeakerVolumeDirty = false;
  3012. LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL;
  3013. stream << "<Request requestId="" << mCommandCookie++ << "" action="Connector.SetLocalSpeakerVolume.1">"
  3014. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  3015. << "<Value>" << mSpeakerVolume << "</Value>"
  3016. << "</Request>nnn";
  3017. }
  3018. if(mMicVolumeDirty)
  3019. {
  3020. mMicVolumeDirty = false;
  3021. LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL;
  3022. stream << "<Request requestId="" << mCommandCookie++ << "" action="Connector.SetLocalMicVolume.1">"
  3023. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  3024. << "<Value>" << mMicVolume << "</Value>"
  3025. << "</Request>nnn";
  3026. }
  3027. }
  3028. void LLVoiceClient::checkFriend(const LLUUID& id)
  3029. {
  3030. std::string name;
  3031. buddyListEntry *buddy = findBuddy(id);
  3032. // Make sure we don't add a name before it's been looked up.
  3033. if(gCacheName->getFullName(id, name))
  3034. {
  3035. const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
  3036. bool canSeeMeOnline = false;
  3037. if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
  3038. canSeeMeOnline = true;
  3039. // When we get here, mNeedsSend is true and mInSLFriends is false.  Change them as necessary.
  3040. if(buddy)
  3041. {
  3042. // This buddy is already in both lists.
  3043. if(name != buddy->mDisplayName)
  3044. {
  3045. // The buddy is in the list with the wrong name.  Update it with the correct name.
  3046. LL_WARNS("Voice") << "Buddy " << id << " has wrong name ("" << buddy->mDisplayName << "" should be "" << name << ""), updating."<< LL_ENDL;
  3047. buddy->mDisplayName = name;
  3048. buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent.
  3049. }
  3050. }
  3051. else
  3052. {
  3053. // This buddy was not in the vivox list, needs to be added.
  3054. buddy = addBuddy(sipURIFromID(id), name);
  3055. buddy->mUUID = id;
  3056. }
  3057. // In all the above cases, the buddy is in the SL friends list (which is how we got here).
  3058. buddy->mInSLFriends = true;
  3059. buddy->mCanSeeMeOnline = canSeeMeOnline;
  3060. buddy->mNameResolved = true;
  3061. }
  3062. else
  3063. {
  3064. // This name hasn't been looked up yet.  Don't do anything with this buddy list entry until it has.
  3065. if(buddy)
  3066. {
  3067. buddy->mNameResolved = false;
  3068. }
  3069. // Initiate a lookup.
  3070. // The "lookup completed" callback will ensure that the friends list is rechecked after it completes.
  3071. lookupName(id);