llvoiceclient.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. /**
  2. * @file llvoiceclient.cpp
  3. * @brief Implementation of LLVoiceClient class.
  4. *
  5. * $LicenseInfo:firstyear=2007&license=viewergpl$
  6. *
  7. * Copyright (c) 2007-2009, Linden Research, Inc.
  8. * Copyright (c) 2009-2024, Henri Beauchamp.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include "llvoiceclient.h"
  35. #include "llhttpnode.h"
  36. #include "llkeyboard.h"
  37. #include "llsdutil.h"
  38. #include "llagent.h"
  39. #include "llmutelist.h"
  40. #include "llviewercontrol.h"
  41. #include "llviewerparcelmgr.h"
  42. #include "llviewerregion.h"
  43. #include "llvoicevivox.h"
  44. #include "llvoicewebrtc.h"
  45. // Global
  46. LLVoiceClient gVoiceClient;
  47. ///////////////////////////////////////////////////////////////////////////////
  48. // LLVoiceClientStatusObserver class
  49. ///////////////////////////////////////////////////////////////////////////////
  50. std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType status)
  51. {
  52. std::string result = "UNKNOWN";
  53. // Prevent copy-paste errors when updating this list...
  54. #define CASE(x) case x: result = #x; break
  55. switch (status)
  56. {
  57. CASE(STATUS_LOGIN_RETRY);
  58. CASE(STATUS_LOGGED_IN);
  59. CASE(STATUS_JOINING);
  60. CASE(STATUS_JOINED);
  61. CASE(STATUS_LEFT_CHANNEL);
  62. CASE(STATUS_VOICE_DISABLED);
  63. CASE(STATUS_VOICE_ENABLED);
  64. CASE(BEGIN_ERROR_STATUS);
  65. CASE(ERROR_CHANNEL_FULL);
  66. CASE(ERROR_CHANNEL_LOCKED);
  67. CASE(ERROR_NOT_AVAILABLE);
  68. CASE(ERROR_UNKNOWN);
  69. default:
  70. break;
  71. }
  72. #undef CASE
  73. return result;
  74. }
  75. ///////////////////////////////////////////////////////////////////////////////
  76. // LLVoiceClient class
  77. ///////////////////////////////////////////////////////////////////////////////
  78. LLVoiceClient::LLVoiceClient()
  79. : mSpatialVoiceModulep(NULL),
  80. mNonSpatialVoiceModulep(NULL),
  81. mReady(false),
  82. mUserPTTState(false),
  83. mUsePTT(true),
  84. mPTTIsToggle(false),
  85. mMuteMic(false)
  86. {
  87. }
  88. LLVoiceClient::~LLVoiceClient()
  89. {
  90. terminate(); // Just in case it was forgotten... HB
  91. }
  92. void LLVoiceClient::init(LLPumpIO* pumpp)
  93. {
  94. if (mReady)
  95. {
  96. return;
  97. }
  98. mReady = true;
  99. gVoiceVivox.init(pumpp);
  100. gVoiceWebRTC.init();
  101. #if 0 // Do not do this here: wait for full login before enabling voice, else
  102. // race conditions would happen in the WebRTC connection process and
  103. // would result in failures to bring up voice on login. Instead, an
  104. // explicit call to LLVoiceClient::updateSettings() is now performed
  105. // from llstartup.cpp on STATE_CLEANUP stage. HB
  106. updateSettings();
  107. #endif
  108. // Register or parcel manager observer for agent pracle changes. HB
  109. mParcelChangedConnection =
  110. gViewerParcelMgr.addAgentParcelChangedCB(boost::bind(&LLVoiceClient::onParcelChange,
  111. this));
  112. }
  113. void LLVoiceClient::terminate()
  114. {
  115. if (mReady)
  116. {
  117. if (mParcelChangedConnection.connected())
  118. {
  119. mParcelChangedConnection.disconnect();
  120. }
  121. gVoiceVivox.terminate();
  122. gVoiceWebRTC.terminate();
  123. mReady = false;
  124. }
  125. }
  126. void LLVoiceClient::updateSettings()
  127. {
  128. setVoiceEnabled(gSavedSettings.getBool("EnableVoiceChat"));
  129. setUsePTT(gSavedSettings.getBool("PTTCurrentlyEnabled"));
  130. if (!setPTTKey(gSavedSettings.getString("PushToTalkButton")))
  131. {
  132. llwarns << "Invalid push-to-talk key: trigger set to none" << llendl;
  133. }
  134. setPTTIsToggle(gSavedSettings.getBool("PushToTalkToggle"));
  135. setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
  136. setMicGain(gSavedSettings.getF32("AudioLevelMic"));
  137. setCaptureDevice(gSavedSettings.getString("VoiceInputAudioDevice"), false);
  138. setRenderDevice(gSavedSettings.getString("VoiceOutputAudioDevice"), false);
  139. setCaptureDevice(gSavedSettings.getString("VoiceWebRTCInputAudioDevice"),
  140. true);
  141. setRenderDevice(gSavedSettings.getString("VoiceWebRTCOutputAudioDevice"),
  142. true);
  143. gVoiceWebRTC.updateSettings();
  144. }
  145. LLVoiceModule* LLVoiceClient::getModuleFromType(const std::string& server_type)
  146. {
  147. if (server_type.empty() || server_type == "vivox")
  148. {
  149. return &gVoiceVivox;
  150. }
  151. if (server_type == "webrtc")
  152. {
  153. return &gVoiceWebRTC;
  154. }
  155. return NULL;
  156. }
  157. LLVoiceModule* LLVoiceClient::getModuleFromChannelInfo(const LLSD& info)
  158. {
  159. // When not server type is given, use Vivox.
  160. if (!info.has("voice_server_type"))
  161. {
  162. return &gVoiceVivox;
  163. }
  164. return getModuleFromType(info["voice_server_type"].asString());
  165. }
  166. U32 LLVoiceClient::getVoiceServerType(const LLSD& channel_info)
  167. {
  168. if (!channel_info.has("voice_server_type"))
  169. {
  170. return VIVOX_SERVER;
  171. }
  172. std::string type = channel_info["voice_server_type"].asString();
  173. if (type == "vivox")
  174. {
  175. return VIVOX_SERVER;
  176. }
  177. if (type == "webrtc")
  178. {
  179. return WEBRTC_SERVER;
  180. }
  181. return UNKNOWN_SERVER;
  182. }
  183. //static
  184. LLVoiceModule* LLVoiceClient::getModuleFromSimFeatures(const LLSD& features,
  185. bool save_to_settings)
  186. {
  187. // Default is Vivox when no voice type feature exists (old sim versions).
  188. LLVoiceModule* modulep = &gVoiceVivox;
  189. std::string type_str;
  190. if (features.has("VoiceServerType"))
  191. {
  192. type_str = features["VoiceServerType"].asString();
  193. if (type_str == "webrtc")
  194. {
  195. modulep = &gVoiceWebRTC;
  196. }
  197. else if (type_str != "vivox")
  198. {
  199. modulep = NULL;
  200. type_str = "UNSUPPORTED";
  201. }
  202. }
  203. if (save_to_settings)
  204. {
  205. if (type_str.empty())
  206. {
  207. llinfos << " No voice server type given for agent region: Vivox assumed."
  208. << llendl;
  209. type_str = "vivox";
  210. }
  211. else
  212. {
  213. llinfos << "Voice server type set for agent region: " << type_str
  214. << llendl;
  215. }
  216. gSavedSettings.setString("VoiceServerType", type_str);
  217. }
  218. return modulep;
  219. }
  220. void LLVoiceClient::setSpatialVoiceModule(LLVoiceModule* modulep)
  221. {
  222. if (!modulep)
  223. {
  224. return;
  225. }
  226. if (modulep != mSpatialVoiceModulep)
  227. {
  228. if (mSpatialVoiceModulep && mSpatialVoiceModulep->inProximalChannel())
  229. {
  230. mSpatialVoiceModulep->processChannels(false);
  231. }
  232. mSpatialVoiceModulep = modulep;
  233. mSpatialVoiceModulep->processChannels(true);
  234. LL_DEBUGS("Voice") << "Spatial voice module changed for: "
  235. << mSpatialVoiceModulep->getName() << LL_ENDL;
  236. }
  237. }
  238. void LLVoiceClient::setNonSpatialVoiceModule(LLVoiceModule* modulep)
  239. {
  240. mNonSpatialVoiceModulep = modulep;
  241. // If do not have a non-spatial module, revert to spatial when possible.
  242. if (!mNonSpatialVoiceModulep && mSpatialVoiceModulep)
  243. {
  244. mSpatialVoiceModulep->processChannels(true);
  245. }
  246. }
  247. void LLVoiceClient::handleSimFeaturesReceived(const LLSD& features)
  248. {
  249. LL_DEBUGS("Voice") << "Processing simulator features for agent region"
  250. << LL_ENDL;
  251. LLVoiceModule* modulep = getModuleFromSimFeatures(features, true);
  252. if (!modulep)
  253. {
  254. return; // Unknown server type: nothing to do !
  255. }
  256. if (mSpatialVoiceModulep && !mNonSpatialVoiceModulep)
  257. {
  258. // Stop processing if we are going to change voice clients and we are
  259. // not currently in non-spatial.
  260. if (mSpatialVoiceModulep != modulep)
  261. {
  262. llinfos << "Disabling " << mSpatialVoiceModulep->getName()
  263. << " voice processing." << llendl;
  264. mSpatialVoiceModulep->processChannels(false);
  265. }
  266. }
  267. // Switch to spatial voice to the new module if needed.
  268. setSpatialVoiceModule(modulep);
  269. // If we should be in spatial voice, switch to it and set the credentials
  270. if (mSpatialVoiceModulep && !mNonSpatialVoiceModulep)
  271. {
  272. LL_DEBUGS("Voice") << "Using spatial voice module." << LL_ENDL;
  273. if (!mSpatialCredentials.isUndefined())
  274. {
  275. mSpatialVoiceModulep->setSpatialChannel(mSpatialCredentials);
  276. }
  277. mSpatialVoiceModulep->processChannels(true);
  278. }
  279. }
  280. void LLVoiceClient::onParcelChange()
  281. {
  282. LL_DEBUGS("Voice") << "Parcel change detected." << LL_ENDL;
  283. if (!mSpatialVoiceModulep || mNonSpatialVoiceModulep)
  284. {
  285. // Not in parcel/estate voice channel currently. HB
  286. return;
  287. }
  288. // Check for parcel voice permissions and act accordingly. HB
  289. if (gViewerParcelMgr.allowAgentVoice())
  290. {
  291. LL_DEBUGS("Voice") << "Parcel voice allowed." << LL_ENDL;
  292. mSpatialVoiceModulep->processChannels(true);
  293. setSpatialChannel(mSpatialCredentials);
  294. if (mSpatialVoiceModulep->isVivox())
  295. {
  296. gVoiceVivox.parcelChanged();
  297. }
  298. return;
  299. }
  300. // Parcel flag does not permit voice: make sure the spatial channel will
  301. // be shut down and will stay as such. HB
  302. if (mSpatialVoiceModulep && mSpatialVoiceModulep->inProximalChannel())
  303. {
  304. LL_DEBUGS("Voice") << "Parcel voice disabled. Switching off spatial voice."
  305. << LL_ENDL;
  306. mSpatialVoiceModulep->processChannels(false);
  307. }
  308. }
  309. void LLVoiceClient::setVoiceEnabled(bool enabled)
  310. {
  311. gVoiceVivox.setVoiceEnabled(enabled);
  312. gVoiceWebRTC.setVoiceEnabled(enabled);
  313. }
  314. void LLVoiceClient::userAuthorized(const std::string& first_name,
  315. const std::string& last_name,
  316. const LLUUID& agent_id)
  317. {
  318. gVoiceVivox.userAuthorized(first_name, last_name, agent_id);
  319. // Kick up a parcel change since the one which occurred at login was done
  320. // too soon and got ignored. HB
  321. onParcelChange();
  322. // Note: no such method for LLVoiceWebRTC
  323. }
  324. void LLVoiceClient::setCaptureDevice(const std::string& device_id, bool webrtc)
  325. {
  326. if (webrtc)
  327. {
  328. gVoiceWebRTC.setCaptureDevice(device_id);
  329. }
  330. else
  331. {
  332. gVoiceVivox.setCaptureDevice(device_id);
  333. }
  334. }
  335. void LLVoiceClient::setRenderDevice(const std::string& device_id, bool webrtc)
  336. {
  337. if (webrtc)
  338. {
  339. gVoiceWebRTC.setRenderDevice(device_id);
  340. }
  341. else
  342. {
  343. gVoiceVivox.setRenderDevice(device_id);
  344. }
  345. }
  346. const LLVoiceClient::device_map_t& LLVoiceClient::getCaptureDevices(bool webrtc) const
  347. {
  348. if (webrtc)
  349. {
  350. return gVoiceWebRTC.getCaptureDevices();
  351. }
  352. return gVoiceVivox.getCaptureDevices();
  353. }
  354. const LLVoiceClient::device_map_t& LLVoiceClient::getRenderDevices(bool webrtc) const
  355. {
  356. if (webrtc)
  357. {
  358. return gVoiceWebRTC.getRenderDevices();
  359. }
  360. return gVoiceVivox.getRenderDevices();
  361. }
  362. void LLVoiceClient::tuningStart(bool webrtc)
  363. {
  364. if (webrtc)
  365. {
  366. gVoiceWebRTC.setTuningMode(true);
  367. }
  368. else
  369. {
  370. gVoiceVivox.setTuningMode(true);
  371. }
  372. }
  373. void LLVoiceClient::tuningStop(bool webrtc)
  374. {
  375. if (webrtc)
  376. {
  377. gVoiceWebRTC.setTuningMode(false);
  378. }
  379. else
  380. {
  381. gVoiceVivox.setTuningMode(false);
  382. }
  383. }
  384. bool LLVoiceClient::inTuningMode(bool webrtc)
  385. {
  386. if (webrtc)
  387. {
  388. return gVoiceWebRTC.inTuningMode();
  389. }
  390. return gVoiceVivox.inTuningMode();
  391. }
  392. bool LLVoiceClient::tuningModeActive()
  393. {
  394. static LLCachedControl<std::string> enable_voice(gSavedSettings,
  395. "VoiceServerType");
  396. if (enable_voice() == "webrtc")
  397. {
  398. return gVoiceWebRTC.inTuningMode();
  399. }
  400. return gVoiceVivox.inTuningMode();
  401. }
  402. void LLVoiceClient::tuningSetMicVolume(F32 volume, bool webrtc)
  403. {
  404. if (webrtc)
  405. {
  406. gVoiceWebRTC.tuningSetMicVolume(volume);
  407. }
  408. else
  409. {
  410. gVoiceVivox.tuningSetMicVolume(volume);
  411. }
  412. }
  413. #if 0 // Not used
  414. void LLVoiceClient::tuningSetSpeakerVolume(F32 volume, bool webrtc)
  415. {
  416. if (webrtc)
  417. {
  418. gVoiceWebRTC.tuningSetSpeakerVolume(volume);
  419. }
  420. else
  421. {
  422. gVoiceVivox.tuningSetSpeakerVolume(volume);
  423. }
  424. }
  425. #endif
  426. F32 LLVoiceClient::tuningGetEnergy(bool webrtc)
  427. {
  428. if (webrtc)
  429. {
  430. return gVoiceWebRTC.tuningGetEnergy();
  431. }
  432. return gVoiceVivox.tuningGetEnergy();
  433. }
  434. bool LLVoiceClient::deviceSettingsAvailable(bool webrtc)
  435. {
  436. if (webrtc)
  437. {
  438. return gVoiceWebRTC.deviceSettingsAvailable();
  439. }
  440. return gVoiceVivox.deviceSettingsAvailable();
  441. }
  442. void LLVoiceClient::refreshDeviceLists(bool clear_current_list, bool webrtc)
  443. {
  444. if (webrtc)
  445. {
  446. gVoiceWebRTC.refreshDeviceLists(clear_current_list);
  447. }
  448. else
  449. {
  450. gVoiceVivox.refreshDeviceLists(clear_current_list);
  451. }
  452. }
  453. // This is only ever used to answer incoming P2P call invites.
  454. bool LLVoiceClient::answerInvite(const LLSD& channel_info)
  455. {
  456. // Note: no such method for LLVoiceWebRTC
  457. std::string handle = channel_info["session_handle"].asString();
  458. return gVoiceVivox.answerInvite(handle);
  459. }
  460. void LLVoiceClient::declineInvite(const LLSD& channel_info)
  461. {
  462. // Note: no such method for LLVoiceWebRTC
  463. std::string handle = channel_info["session_handle"].asString();
  464. gVoiceVivox.declineInvite(handle);
  465. }
  466. bool LLVoiceClient::getParticipants(participants_vec_t& participants)
  467. {
  468. participants.clear();
  469. bool result = false;
  470. LLVoiceVivox::particip_map_t* listp = gVoiceVivox.getParticipantList();
  471. if (listp)
  472. {
  473. result = true;
  474. for (LLVoiceVivox::particip_map_t::const_iterator it = listp->begin(),
  475. end = listp->end();
  476. it != end; ++it)
  477. {
  478. LLVoiceVivox::participantState* participantp = it->second;
  479. participants.emplace_back(participantp->mAvatarID,
  480. participantp->mLegacyName,
  481. participantp->isAvatar());
  482. }
  483. }
  484. LLVoiceWebRTC::particip_map_t* list2p = gVoiceWebRTC.getParticipantList();
  485. if (list2p)
  486. {
  487. result = true;
  488. for (LLVoiceWebRTC::particip_map_t::const_iterator it = list2p->begin(),
  489. end = list2p->end();
  490. it != end; ++it)
  491. {
  492. LLVoiceWebRTC::pstate_ptr_t participantp = it->second;
  493. participants.emplace_back(participantp->mAvatarID,
  494. participantp->mLegacyName, true);
  495. }
  496. }
  497. return result;
  498. }
  499. void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observerp)
  500. {
  501. gVoiceVivox.addObserver(observerp);
  502. gVoiceWebRTC.addObserver(observerp);
  503. }
  504. void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observerp)
  505. {
  506. gVoiceVivox.removeObserver(observerp);
  507. gVoiceWebRTC.removeObserver(observerp);
  508. }
  509. void LLVoiceClient::setSpatialChannel(const LLSD& channel_info)
  510. {
  511. mSpatialCredentials = channel_info;
  512. LLViewerRegion* regionp = gAgent.getRegion();
  513. if (!regionp || !regionp->getFeaturesReceived())
  514. {
  515. return;
  516. }
  517. const LLSD& features = regionp->getSimulatorFeatures();
  518. setSpatialVoiceModule(getModuleFromSimFeatures(features));
  519. if (mSpatialVoiceModulep)
  520. {
  521. mSpatialVoiceModulep->setSpatialChannel(channel_info);
  522. }
  523. }
  524. void LLVoiceClient::setNonSpatialChannel(const LLSD& channel_info,
  525. bool notify_on_first_join,
  526. bool hangup_on_last_leave)
  527. {
  528. LLVoiceModule* modulep = getModuleFromChannelInfo(channel_info);
  529. setNonSpatialVoiceModule(modulep);
  530. if (mSpatialVoiceModulep &&
  531. mSpatialVoiceModulep != mNonSpatialVoiceModulep)
  532. {
  533. mSpatialVoiceModulep->processChannels(false);
  534. }
  535. if (mNonSpatialVoiceModulep)
  536. {
  537. mNonSpatialVoiceModulep->processChannels(true);
  538. mNonSpatialVoiceModulep->setNonSpatialChannel(channel_info,
  539. notify_on_first_join,
  540. hangup_on_last_leave);
  541. }
  542. }
  543. void LLVoiceClient::leaveNonSpatialChannel()
  544. {
  545. if (mNonSpatialVoiceModulep)
  546. {
  547. mNonSpatialVoiceModulep->leaveNonSpatialChannel();
  548. mNonSpatialVoiceModulep->processChannels(false);
  549. mNonSpatialVoiceModulep = NULL;
  550. }
  551. }
  552. void LLVoiceClient::activateSpatialChannel(bool activate)
  553. {
  554. if (mSpatialVoiceModulep)
  555. {
  556. if (activate && !gViewerParcelMgr.allowAgentVoice())
  557. {
  558. LL_DEBUGS("Voice") << "Not activating due to parcel no-voice flag"
  559. << LL_ENDL;
  560. activate = false;
  561. }
  562. mSpatialVoiceModulep->processChannels(activate);
  563. }
  564. }
  565. bool LLVoiceClient::isCurrentChannel(const LLSD& channel_info)
  566. {
  567. return gVoiceVivox.isCurrentChannel(channel_info) ||
  568. gVoiceWebRTC.isCurrentChannel(channel_info);
  569. }
  570. bool LLVoiceClient::compareChannels(const LLSD& info1, const LLSD& info2)
  571. {
  572. return gVoiceVivox.compareChannels(info1, info2) ||
  573. gVoiceWebRTC.compareChannels(info1, info2);
  574. }
  575. bool LLVoiceClient::inProximalChannel() const
  576. {
  577. return mSpatialVoiceModulep && mSpatialVoiceModulep->inProximalChannel();
  578. }
  579. void LLVoiceClient::callUser(const LLUUID& id, U32 server_type)
  580. {
  581. // Note: there is no callUser() equivalent for WebRTC
  582. if (server_type == VIVOX_SERVER)
  583. {
  584. LL_DEBUGS("Voice") << "Calling user " << id << " via Viviox."
  585. << LL_ENDL;
  586. gVoiceVivox.callUser(id);
  587. }
  588. }
  589. void LLVoiceClient::hangup(U32 server_type)
  590. {
  591. // Note: there is no hangup() equivalent for WebRTC
  592. if (server_type == VIVOX_SERVER)
  593. {
  594. LL_DEBUGS("Voice") << "Leaving Vivox session" << LL_ENDL;
  595. // Note: in LL's viewer code, LLVoiceVivox::hangup() is an alias to
  596. // LLVoiceVivox::leaveChannel(). HB
  597. gVoiceVivox.leaveChannel();
  598. }
  599. }
  600. std::string LLVoiceClient::sipURIFromID(const LLUUID& id) const
  601. {
  602. if (mNonSpatialVoiceModulep)
  603. {
  604. return mNonSpatialVoiceModulep->sipURIFromID(id);
  605. }
  606. if (mSpatialVoiceModulep)
  607. {
  608. return mSpatialVoiceModulep->sipURIFromID(id);
  609. }
  610. return "";
  611. }
  612. bool LLVoiceClient::isVoiceWorking()
  613. {
  614. return voiceEnabled() &&
  615. (gVoiceVivox.isVoiceWorking() || gVoiceWebRTC.isVoiceWorking());
  616. }
  617. bool LLVoiceClient::voiceEnabled()
  618. {
  619. static LLCachedControl<bool> enable_voice(gSavedSettings,
  620. "EnableVoiceChat");
  621. static LLCachedControl<bool> disable_voice(gSavedSettings,
  622. "CmdLineDisableVoice");
  623. return enable_voice && !disable_voice;
  624. }
  625. bool LLVoiceClient::getVoiceEnabled(const LLUUID& id)
  626. {
  627. return gVoiceVivox.isParticipant(id) || gVoiceWebRTC.isParticipant(id);
  628. }
  629. bool LLVoiceClient::getIsSpeaking(const LLUUID& id)
  630. {
  631. return gVoiceVivox.getIsSpeaking(id) || gVoiceWebRTC.getIsSpeaking(id);
  632. }
  633. bool LLVoiceClient::getIsModeratorMuted(const LLUUID& id)
  634. {
  635. return gVoiceVivox.getIsModeratorMuted(id) ||
  636. gVoiceWebRTC.getIsModeratorMuted(id);
  637. }
  638. F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
  639. {
  640. F32 power = gVoiceVivox.getCurrentPower(id);
  641. if (power < 0.f) // 'id' is not in a Vivox session
  642. {
  643. // Try WebRTC then...
  644. power = gVoiceWebRTC.getCurrentPower(id);
  645. }
  646. return llmax(power, 0.f);
  647. }
  648. void LLVoiceClient::setEarLocation(S32 loc)
  649. {
  650. gVoiceVivox.setEarLocation(loc);
  651. gVoiceWebRTC.setEarLocation(loc);
  652. }
  653. void LLVoiceClient::setVoiceVolume(F32 volume)
  654. {
  655. gVoiceVivox.setVoiceVolume(volume);
  656. gVoiceWebRTC.setVoiceVolume(volume);
  657. }
  658. void LLVoiceClient::setMicGain(F32 volume)
  659. {
  660. gVoiceVivox.setMicGain(volume);
  661. gVoiceWebRTC.setMicGain(volume);
  662. }
  663. F32 LLVoiceClient::getUserVolume(const LLUUID& id)
  664. {
  665. F32 volume = gVoiceVivox.getUserVolume(id);
  666. if (volume < 0.f) // 'id' is not in a Vivox session
  667. {
  668. volume = gVoiceWebRTC.getUserVolume(id);
  669. }
  670. return llmax(volume, 0.f);
  671. }
  672. void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
  673. {
  674. volume = llclamp(volume, 0.f, 1.f);
  675. gVoiceVivox.setUserVolume(id, volume);
  676. gVoiceWebRTC.setUserVolume(id, volume);
  677. }
  678. bool LLVoiceClient::getOnMuteList(const LLUUID& id)
  679. {
  680. return LLMuteList::isMuted(id, LLMute::flagVoiceChat);
  681. }
  682. // PTT related methods
  683. bool LLVoiceClient::isAgentMicOpen() const
  684. {
  685. // Not in push to talk mode, or push to talk is active means currently
  686. // talking.
  687. static LLCachedControl<bool> ptt_enabled(gSavedSettings,
  688. "PTTCurrentlyEnabled");
  689. return mUserPTTState || !ptt_enabled;
  690. }
  691. void LLVoiceClient::setUsePTT(bool use_it)
  692. {
  693. if (use_it && !mUsePTT)
  694. {
  695. // When the user turns on PTT, reset the current state.
  696. mUserPTTState = false;
  697. }
  698. mUsePTT = use_it;
  699. }
  700. void LLVoiceClient::setPTTIsToggle(bool set_as_toggle)
  701. {
  702. if (!set_as_toggle && mPTTIsToggle)
  703. {
  704. // When the user turns off toggle, reset the current state.
  705. mUserPTTState = false;
  706. }
  707. mPTTIsToggle = set_as_toggle;
  708. }
  709. bool LLVoiceClient::setPTTKey(const std::string& key)
  710. {
  711. if (key == "MiddleMouse")
  712. {
  713. mPTTIsMiddleMouse = true;
  714. return true;
  715. }
  716. mPTTIsMiddleMouse = false;
  717. return LLKeyboard::keyFromString(key.c_str(), &mPTTKey);
  718. }
  719. void LLVoiceClient::keyDown(KEY key, MASK mask)
  720. {
  721. if (!gKeyboardp || gKeyboardp->getKeyRepeated(key))
  722. {
  723. return; // Ignore auto-repeat keys
  724. }
  725. if (!mPTTIsMiddleMouse)
  726. {
  727. if (mPTTIsToggle)
  728. {
  729. if (key == mPTTKey)
  730. {
  731. toggleUserPTTState();
  732. }
  733. }
  734. else if (mPTTKey != KEY_NONE)
  735. {
  736. setUserPTTState(gKeyboardp->getKeyDown(mPTTKey));
  737. }
  738. }
  739. }
  740. void LLVoiceClient::keyUp(KEY key, MASK mask)
  741. {
  742. if (!mPTTIsMiddleMouse)
  743. {
  744. if (!mPTTIsToggle && (mPTTKey != KEY_NONE) && gKeyboardp)
  745. {
  746. setUserPTTState(gKeyboardp->getKeyDown(mPTTKey));
  747. }
  748. }
  749. }
  750. void LLVoiceClient::inputUserControlState(bool down)
  751. {
  752. if (!mPTTIsToggle)
  753. {
  754. // Set open-mic state as an absolute
  755. setUserPTTState(down);
  756. }
  757. else if (down) // Toggle open-mic state on 'down'
  758. {
  759. toggleUserPTTState();
  760. }
  761. }
  762. void LLVoiceClient::middleMouseState(bool down)
  763. {
  764. if (mPTTIsMiddleMouse)
  765. {
  766. inputUserControlState(down);
  767. }
  768. }
  769. ///////////////////////////////////////////////////////////////////////////////
  770. // LLViewerParcelVoiceInfo class
  771. ///////////////////////////////////////////////////////////////////////////////
  772. class LLViewerParcelVoiceInfo final : public LLHTTPNode
  773. {
  774. void post(LLHTTPNode::ResponsePtr response, const LLSD& context,
  775. const LLSD& input) const override
  776. {
  777. // The parcel you are in has changed something about its voice
  778. // information. This is a misnomer, as it can also be when you are not
  779. // in a parcel at all. Should really be something like
  780. // LLViewerVoiceInfoChanged...
  781. if (input.has("body"))
  782. {
  783. const LLSD& body = input["body"];
  784. // body has "region_name" (str), "parcel_local_id"(int),
  785. // "voice_credentials" (map).
  786. // body["voice_credentials"] has "channel_uri" (str),
  787. // body["voice_credentials"] has "channel_credentials" (str)
  788. // If we really wanted to be extra careful, we would check the
  789. // supplied local parcel id to make sure it is for the same
  790. // parcel we believe we are in.
  791. if (body.has("voice_credentials"))
  792. {
  793. LL_DEBUGS("Voice") << "Got spatial voice channel info"
  794. << LL_ENDL;
  795. gVoiceClient.setSpatialChannel(body["voice_credentials"]);
  796. }
  797. }
  798. }
  799. };
  800. LLHTTPRegistration<LLViewerParcelVoiceInfo>
  801. gHTTPRegistrationMessageParcelVoiceInfo("/message/ParcelVoiceInfo");
  802. ///////////////////////////////////////////////////////////////////////////////
  803. // LLViewerRequiredVoiceVersion class
  804. ///////////////////////////////////////////////////////////////////////////////
  805. class LLViewerRequiredVoiceVersion final : public LLHTTPNode
  806. {
  807. void post(LLHTTPNode::ResponsePtr response, const LLSD& context,
  808. const LLSD& input) const override
  809. {
  810. // You received this messsage (most likely on region cross or teleport)
  811. if (!gVoiceClient.ready() || !input.has("body"))
  812. {
  813. return;
  814. }
  815. LLViewerRegion* regionp = gAgent.getRegion();
  816. if (!regionp || !regionp->getFeaturesReceived())
  817. {
  818. return;
  819. }
  820. const LLSD& body = input["body"];
  821. if (!body.has("major_version"))
  822. {
  823. return;
  824. }
  825. LL_DEBUGS("Voice") << "Got Voice version info: "
  826. << ll_pretty_print_sd(body) << LL_ENDL;
  827. // Check for the voice server version, based on its type.
  828. LLVoiceModule* modulep = gVoiceClient.getModuleFromChannelInfo(body);
  829. // Default to -1 to cause failure for unknown server type. HB
  830. S32 max_version = -1;
  831. if (modulep && modulep->isVivox())
  832. {
  833. constexpr S32 VIVOX_MAJOR_VERSION = 1;
  834. max_version = VIVOX_MAJOR_VERSION;
  835. }
  836. else if (modulep && modulep->isWebRTC())
  837. {
  838. constexpr S32 WEBRTC_MAJOR_VERSION = 2;
  839. max_version = WEBRTC_MAJOR_VERSION;
  840. }
  841. if (body["major_version"].asInteger() > max_version)
  842. {
  843. gNotifications.add("VoiceVersionMismatch");
  844. // Toggles the listener
  845. gSavedSettings.setBool("EnableVoiceChat", false);
  846. }
  847. }
  848. };
  849. LLHTTPRegistration<LLViewerRequiredVoiceVersion>
  850. gHTTPRegistrationMessageRequiredVoiceVersion("/message/RequiredVoiceVersion");