llvoicewebrtc.cpp 84 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256
  1. /**
  2. * @file llvoicewebrtc.cpp
  3. * @brief Implementation of LLVoiceWebRTC class.
  4. *
  5. * $LicenseInfo:firstyear=2023&license=viewergpl$
  6. *
  7. * Copyright (C) 2023-2024, Linden Research, Inc.
  8. * Copyright (C) 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 "json.hpp"
  35. #include "llvoicewebrtc.h"
  36. #include "llcachename.h"
  37. #include "llcorehttputil.h"
  38. #include "lldir.h"
  39. #include "llcallbacklist.h"
  40. #include "llpluginclassmedia.h"
  41. #include "llparcel.h"
  42. #include "llrand.h" // For ll_frand()
  43. #include "llsdutil.h"
  44. #include "hbtracy.h"
  45. #include "llagent.h"
  46. #include "llappviewer.h"
  47. #include "hbfloaterdebugtags.h" // For HBFloaterDebugTags::setTag()
  48. #include "llgridmanager.h" // For gIsInSecondLife*
  49. #include "llimmgr.h"
  50. #include "llmutelist.h"
  51. #include "llstartup.h"
  52. #include "llviewercamera.h"
  53. #include "llviewercontrol.h"
  54. #include "llviewerparcelmgr.h"
  55. #include "llviewerregion.h"
  56. #include "llvoavatarself.h"
  57. #include "llvoicechannel.h"
  58. #include "llworld.h"
  59. using lljson = nlohmann::json;
  60. static const std::string WEBRTCSTR = "webrtc";
  61. ///////////////////////////////////////////////////////////////////////////////
  62. // LLVoiceConnection class
  63. ///////////////////////////////////////////////////////////////////////////////
  64. // LLVoiceConnection and its derived classes manage state transitions,
  65. // negotiating WebRTC connections and other such things for a single connection
  66. // to a WebRTC server. Multiple of these connections may be active at once, in
  67. // the case of cross-region voice, or when a new connection is being created
  68. // before the old one has a chance to shut down.
  69. class LLVoiceConnection
  70. {
  71. LOG_CLASS(LLVoiceConnection);
  72. public:
  73. LLVoiceConnection(const LLUUID& region_id, const std::string& channel_id);
  74. virtual ~LLVoiceConnection();
  75. LL_INLINE const LLUUID& getRegionID() const { return mRegionID; }
  76. LL_INLINE const std::string& getRegionName() const { return mRegionName; }
  77. LL_INLINE void shutDown() { mShutDown = true; }
  78. bool pluginCreateSession();
  79. bool processPluginData();
  80. void sendJoin();
  81. void sendData(const std::string& data);
  82. bool connectionStateMachine();
  83. void setUserVolume(const LLUUID& id, F32 volume);
  84. void setUserMute(const LLUUID& id, bool mute);
  85. // New virtual methods
  86. virtual void setMuteMic(bool muted);
  87. virtual void setSpeakerVolume(F32 volume);
  88. virtual bool isSpatial() = 0;
  89. protected:
  90. void onVoiceConnectionRequestSuccess(const LLSD& body);
  91. void onDataReceivedImpl(const std::string& data);
  92. void processIceUpdatesCoro();
  93. typedef enum e_voice_connection_state : U32
  94. {
  95. VS_ERROR = 0x0,
  96. VS_START_SESSION = 0x1,
  97. VS_WAIT_FOR_SESSION_START = 0x2,
  98. VS_REQUEST_CONNECTION = 0x4,
  99. VS_CONNECTION_WAIT = 0x8,
  100. VS_SESSION_ESTABLISHED = 0x10,
  101. VS_WAIT_FOR_DATA_CHANNEL = 0x20,
  102. VS_SESSION_UP = 0x40,
  103. VS_SESSION_RETRY = 0x80,
  104. VS_DISCONNECT = 0x100,
  105. VS_WAIT_FOR_EXIT = 0x200,
  106. VS_SESSION_EXIT = 0x400,
  107. VS_WAIT_FOR_CLOSE = 0x800,
  108. VS_CLOSED = 0x1000,
  109. VS_SESSION_STOPPING = 0x1F80,
  110. // Used when no WebRTC server is available for a particular sim, to
  111. // avoid infinite connection retries. HB
  112. VS_SESSION_JAIL = 0x2000,
  113. } EVoiceConnectionState;
  114. std::string state2string(EVoiceConnectionState status);
  115. void setVoiceConnectionState(EVoiceConnectionState state,
  116. bool force = false);
  117. LL_INLINE EVoiceConnectionState getVoiceConnectionState() const
  118. {
  119. return mVoiceConnectionState;
  120. }
  121. virtual void requestVoiceConnection() = 0;
  122. void breakVoiceConnectionCoro();
  123. protected:
  124. LLUUID mID;
  125. LLSD mIceCandidates;
  126. LLCore::HttpOptions::ptr_t mHttpOptions;
  127. LLUUID mViewerSession;
  128. LLUUID mRegionID;
  129. std::string mRegionName;
  130. std::string mChannelID;
  131. std::string mChannelSDP;
  132. LLTimer mStateTransitionTimer;
  133. EVoiceConnectionState mVoiceConnectionState;
  134. S32 mOutstandingRequests;
  135. // Number of seconds to wait before next retry.
  136. F32 mRetryWaitSecs;
  137. // Number of UPDATE_THROTTLE_SECONDS we have waited since our last attempt
  138. // to connect.
  139. S32 mRetryWaitPeriod;
  140. LLVoiceClientStatusObserver::EStatusType mCurrentStatus;
  141. F32 mSpeakerVolume;
  142. bool mMuted;
  143. // *HACK: to get the audio devices to properly be taken into account by
  144. // WebRTC after a voice channel change. For a full explanation of how this
  145. // hack works, see the comment in connectionStateMachine() inside the
  146. // VS_SESSION_UP state. HB
  147. bool mNeedsTuning;
  148. bool mPluginSessionCreated;
  149. bool mHasDataInterface;
  150. bool mIceCompleted;
  151. bool mShutDown;
  152. };
  153. // Since LLVoiceConnection uses coroutines and each instance may get removed
  154. // from memory by the main coroutine, we must check on each return from a yield
  155. // in the coroutines that the connection still exists ! This vector holds a
  156. // list of the live LLVoiceConnection internal UUIDs for this purpose. HB
  157. static uuid_list_t sVoiceConnections;
  158. // Do not auto-tune when the channel Id did not change since last time. HB
  159. static std::string sLastTunedChannelId;
  160. LLVoiceConnection::LLVoiceConnection(const LLUUID& region_id,
  161. const std::string& channel_id)
  162. : mRegionID(region_id),
  163. mChannelID(channel_id),
  164. mHasDataInterface(false),
  165. mHttpOptions(new LLCore::HttpOptions),
  166. mVoiceConnectionState(VS_START_SESSION),
  167. mCurrentStatus(LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED),
  168. mMuted(true),
  169. mNeedsTuning(false),
  170. mShutDown(false),
  171. mIceCompleted(false),
  172. mSpeakerVolume(0.f),
  173. mOutstandingRequests(0),
  174. mRetryWaitPeriod(0),
  175. mRetryWaitSecs(ll_frand() + 0.5f)
  176. {
  177. mHttpOptions->setWantHeaders(true);
  178. LLViewerRegion* regionp = gWorld.getRegionFromID(mRegionID);
  179. if (regionp)
  180. {
  181. mRegionName = regionp->getIdentity();
  182. }
  183. mID.generate();
  184. sVoiceConnections.emplace(mID);
  185. mPluginSessionCreated = pluginCreateSession();
  186. LL_DEBUGS("Voice") << "New connection (" << mID << ") for region: "
  187. << mRegionName << LL_ENDL;
  188. mStateTransitionTimer.start();
  189. }
  190. //virtual
  191. LLVoiceConnection::~LLVoiceConnection()
  192. {
  193. sVoiceConnections.erase(mID);
  194. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  195. if (pluginp)
  196. {
  197. pluginp->sendVoiceCommand(mID, "delete_connection");
  198. }
  199. }
  200. std::string LLVoiceConnection::state2string(EVoiceConnectionState status)
  201. {
  202. std::string result = "UNKNOWN";
  203. // Prevent copy-paste errors when updating this list...
  204. #define CASE(x) case x: result = #x; break
  205. switch (status)
  206. {
  207. CASE(VS_ERROR);
  208. CASE(VS_START_SESSION);
  209. CASE(VS_WAIT_FOR_SESSION_START);
  210. CASE(VS_REQUEST_CONNECTION);
  211. CASE(VS_CONNECTION_WAIT);
  212. CASE(VS_SESSION_ESTABLISHED);
  213. CASE(VS_WAIT_FOR_DATA_CHANNEL);
  214. CASE(VS_SESSION_UP);
  215. CASE(VS_SESSION_RETRY);
  216. CASE(VS_DISCONNECT);
  217. CASE(VS_WAIT_FOR_EXIT);
  218. CASE(VS_SESSION_EXIT);
  219. CASE(VS_WAIT_FOR_CLOSE);
  220. CASE(VS_CLOSED);
  221. CASE(VS_SESSION_STOPPING);
  222. CASE(VS_SESSION_JAIL);
  223. default:
  224. break;
  225. }
  226. #undef CASE
  227. return result;
  228. }
  229. void LLVoiceConnection::setVoiceConnectionState(EVoiceConnectionState state,
  230. bool force)
  231. {
  232. // Never change state after jailing. HB
  233. if (mVoiceConnectionState == VS_SESSION_JAIL)
  234. {
  235. return;
  236. }
  237. if (force || (state & VS_SESSION_STOPPING) || // Shutdown or restart
  238. // Ignore state changes *while* shutting down or restarting.
  239. !(mVoiceConnectionState & VS_SESSION_STOPPING))
  240. {
  241. LL_DEBUGS("Voice") << (isSpatial() ? "Spatial" : "Ad-hoc")
  242. << " connection for region '" << mRegionName
  243. << "' entering state " << state2string(state)
  244. << " - Transition time from previous state "
  245. << state2string(mVoiceConnectionState) << ": "
  246. << mStateTransitionTimer.getElapsedTimeF32() << "s"
  247. << LL_ENDL;
  248. mVoiceConnectionState = state;
  249. mStateTransitionTimer.reset();
  250. if (state == VS_WAIT_FOR_SESSION_START ||
  251. state == VS_CONNECTION_WAIT || state == VS_WAIT_FOR_CLOSE ||
  252. state == VS_DISCONNECT)
  253. {
  254. static LLCachedControl<U32> timeout(gSavedSettings,
  255. "WebRTCVoiceTimeout");
  256. if (timeout >= 10) // Clamp to a realistic value
  257. {
  258. // Let's guard against a stuck connection process... HB
  259. mStateTransitionTimer.setTimerExpirySec(timeout);
  260. }
  261. }
  262. }
  263. }
  264. bool LLVoiceConnection::pluginCreateSession()
  265. {
  266. // Wait for the WebRTC plugin to reach the running state so that it can
  267. // accept commands. HB
  268. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  269. if (!pluginp || !pluginp->isPluginRunning())
  270. {
  271. return false;
  272. }
  273. if (pluginp)
  274. {
  275. pluginp->sendVoiceCommand(mID, "add_connection");
  276. if (mVoiceConnectionState != VS_START_SESSION)
  277. {
  278. // This is a re-creation after a plugin failure: reset the
  279. // connection steps flags and update the state. HB
  280. mHasDataInterface = mIceCompleted = false;
  281. setVoiceConnectionState(VS_SESSION_EXIT);
  282. }
  283. mPluginSessionCreated = true;
  284. return true;
  285. }
  286. return false;
  287. }
  288. // ICE candidates may be streamed in before or after the SDP offer is available
  289. // (see below). This method determines whether candidates are available to send
  290. // to the WebRTC server via the simulator. If so, and there are no more
  291. // candidates, this code will make the capability call to the server sending up
  292. // the ICE candidates.
  293. void LLVoiceConnection::processIceUpdatesCoro()
  294. {
  295. if (gDisconnected || LLApp::isQuitting() ||gVoiceWebRTC.isTerminated() ||
  296. mShutDown)
  297. {
  298. return;
  299. }
  300. LLViewerRegion* regionp = gWorld.getRegionFromID(mRegionID);
  301. if (!regionp)
  302. {
  303. LL_DEBUGS("Voice") << "Region is gone. Ignoring." << LL_ENDL;
  304. return;
  305. }
  306. if (!regionp->capabilitiesReceived())
  307. {
  308. LL_DEBUGS("Voice") << "Capabilities not yet received for region "
  309. << regionp->getIdentity() << LL_ENDL;
  310. return;
  311. }
  312. const std::string& url = regionp->getCapability("VoiceSignalingRequest");
  313. if (url.empty())
  314. {
  315. LL_DEBUGS("Voice") << "Missing VoiceSignalingRequest capability for region "
  316. << regionp->getIdentity() << LL_ENDL;
  317. return;
  318. }
  319. LL_DEBUGS("Voice") << "Sending candidates data" << LL_ENDL;
  320. LLSD body;
  321. body["viewer_session"] = mViewerSession;
  322. body["voice_server_type"] = WEBRTCSTR;
  323. if (mIceCandidates.isDefined())
  324. {
  325. body["candidates"] = mIceCandidates;
  326. mIceCandidates.clear();
  327. }
  328. else if (mIceCompleted)
  329. {
  330. LLSD data;
  331. data["completed"] = true;
  332. body["candidate"] = data;
  333. mIceCompleted = false;
  334. }
  335. else // This should never happen. HB
  336. {
  337. llwarns << "Incoherent state: mIceCandidates empty and mIceCompleted false"
  338. << llendl;
  339. return;
  340. }
  341. LL_DEBUGS("Voice") << "Posting for " << regionp->getIdentity()
  342. << " - URL: " << url << " - Body: " << body << LL_ENDL;
  343. ++mOutstandingRequests;
  344. LLUUID connection_id = mID; // Keep a copy on the stack. HB
  345. LLCoreHttpUtil::HttpCoroutineAdapter adapter("processIceUpdatesCoro");
  346. LLSD result = adapter.postAndSuspend(url, body, mHttpOptions);
  347. if (!sVoiceConnections.count(connection_id))
  348. {
  349. return;
  350. }
  351. --mOutstandingRequests;
  352. if (gDisconnected || LLApp::isQuitting() || gVoiceWebRTC.isTerminated())
  353. {
  354. return;
  355. }
  356. LLCore::HttpStatus status =
  357. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  358. if (!status)
  359. {
  360. // Could not trickle the candidates, so restart the session.
  361. llwarns << "Could not trickle the candidates. HTTP error "
  362. << status.getType() << ": " << status.toString() << llendl;
  363. setVoiceConnectionState(VS_SESSION_RETRY);
  364. }
  365. }
  366. //virtual
  367. void LLVoiceConnection::setMuteMic(bool muted)
  368. {
  369. mMuted = muted;
  370. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  371. if (pluginp)
  372. {
  373. pluginp->sendVoiceCmdWithBool(mID, "mute", "muted", muted);
  374. }
  375. }
  376. //virtual
  377. void LLVoiceConnection::setSpeakerVolume(F32 volume)
  378. {
  379. mSpeakerVolume = volume;
  380. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  381. if (pluginp)
  382. {
  383. pluginp->sendVoiceCmdWithReal(mID, "receive_volume", "volume", volume);
  384. }
  385. }
  386. void LLVoiceConnection::setUserVolume(const LLUUID& id, F32 volume)
  387. {
  388. lljson root = lljson::object();
  389. lljson user_gain = lljson::object();
  390. // Give it two decimal places with a range from 0-200, where 100 is normal
  391. user_gain[id.asString()] = (U32)(volume * 200.f);
  392. root["ug"] = user_gain;
  393. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  394. if (pluginp)
  395. {
  396. pluginp->sendVoiceCmdWithString(mID, "send_data", "json_data",
  397. to_string(root));
  398. }
  399. }
  400. void LLVoiceConnection::setUserMute(const LLUUID& id, bool mute)
  401. {
  402. lljson root = lljson::object();
  403. lljson muted = lljson::object();
  404. muted[id.asString()] = mute;
  405. root["m"] = muted;
  406. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  407. if (pluginp)
  408. {
  409. pluginp->sendVoiceCmdWithString(mID, "send_data", "json_data",
  410. to_string(root));
  411. }
  412. }
  413. // Sends data to the WebRTC server via the webrtc data channel.
  414. void LLVoiceConnection::sendData(const std::string& data)
  415. {
  416. if (getVoiceConnectionState() == VS_SESSION_UP)
  417. {
  418. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  419. if (pluginp)
  420. {
  421. pluginp->sendVoiceCmdWithString(mID, "send_data", "json_data",
  422. data);
  423. }
  424. }
  425. }
  426. // Tells the simulator that we are shutting down a voice connection. The
  427. // simulator will pass this on to the WebRTC server.
  428. void LLVoiceConnection::breakVoiceConnectionCoro()
  429. {
  430. LL_DEBUGS("Voice") << "Disconnecting voice." << LL_ENDL;
  431. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  432. if (pluginp)
  433. {
  434. pluginp->sendVoiceCommand(mID, "close_data_interface");
  435. }
  436. mHasDataInterface = false;
  437. LLViewerRegion* regionp = gWorld.getRegionFromID(mRegionID);
  438. if (!regionp || !regionp->capabilitiesReceived())
  439. {
  440. LL_DEBUGS("Voice") << "Still waiting for region capabilities..."
  441. << LL_ENDL;
  442. setVoiceConnectionState(VS_SESSION_RETRY);
  443. return;
  444. }
  445. const std::string& url =
  446. regionp->getCapability("ProvisionVoiceAccountRequest");
  447. if (url.empty())
  448. {
  449. llwarns_once << "No ProvisionVoiceAccountRequest capability for agent region; will retry."
  450. << llendl;
  451. setVoiceConnectionState(VS_SESSION_RETRY);
  452. return;
  453. }
  454. LL_DEBUGS("Voice") << "Returning to wait state and loging out voice server..."
  455. << LL_ENDL;
  456. setVoiceConnectionState(VS_WAIT_FOR_EXIT);
  457. LLSD body;
  458. body["logout"] = true;
  459. body["viewer_session"] = mViewerSession;
  460. body["voice_server_type"] = WEBRTCSTR;
  461. ++mOutstandingRequests;
  462. LLUUID connection_id = mID; // Keep a copy on the stack. HB
  463. LLCoreHttpUtil::HttpCoroutineAdapter adapter("breakVoiceConnection");
  464. LLSD result = adapter.postAndSuspend(url, body, mHttpOptions);
  465. if (!sVoiceConnections.count(connection_id))
  466. {
  467. return;
  468. }
  469. --mOutstandingRequests;
  470. if (!gDisconnected && !LLApp::isQuitting() && !gVoiceWebRTC.isTerminated())
  471. {
  472. setVoiceConnectionState(VS_SESSION_EXIT);
  473. }
  474. }
  475. void LLVoiceConnection::onVoiceConnectionRequestSuccess(const LLSD& result)
  476. {
  477. if (gDisconnected || LLApp::isQuitting() || gVoiceWebRTC.isTerminated())
  478. {
  479. return;
  480. }
  481. if (!result.has("viewer_session") || !result.has("jsep") ||
  482. !result["jsep"].has("sdp") || !result["jsep"].has("type") ||
  483. result["jsep"]["type"].asString() != "answer")
  484. {
  485. llwarns << "Invalid voice provision request result: " << result
  486. << llendl;
  487. setVoiceConnectionState(VS_SESSION_EXIT);
  488. return;
  489. }
  490. mViewerSession = result["viewer_session"];
  491. std::string sdp = result["jsep"]["sdp"].asString();
  492. LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response: channel sdp "
  493. << sdp << LL_ENDL;
  494. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  495. if (pluginp)
  496. {
  497. pluginp->sendVoiceCmdWithString(mID, "answer", "remote_sdp", sdp);
  498. }
  499. }
  500. static LLSD get_connection_urls()
  501. {
  502. if (!gIsInSecondLife)
  503. {
  504. // Note: when empty (i.e. for OpenSim grids), the URLs of the Google
  505. // servers are automatically added by llwebrtc.
  506. return LLSD();
  507. }
  508. LLSD urls = LLSD::emptyArray();
  509. std::string grid;
  510. U32 num_servers;
  511. if (gIsInSecondLifeBetaGrid)
  512. {
  513. grid = "aditi";
  514. num_servers = 2;
  515. }
  516. else
  517. {
  518. grid = "agni";
  519. num_servers = 3;
  520. }
  521. static const char* url_format = "stun:stun%d.%s.secondlife.io:3478";
  522. std::string url;
  523. for (U32 i = 1; i <= num_servers; ++i)
  524. {
  525. url = llformat(url_format, i, grid.c_str());
  526. urls.append(LLSD::String(url));
  527. LL_DEBUGS("Voice") << "Added WebRTC server URI: " << url << LL_ENDL;
  528. }
  529. return urls;
  530. }
  531. bool LLVoiceConnection::processPluginData()
  532. {
  533. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  534. if (!pluginp)
  535. {
  536. return false;
  537. }
  538. pluginp->lockWebRTCData();
  539. LLSD& webrtc_data = pluginp->getVoiceData(mID);
  540. if (webrtc_data.isDefined() && webrtc_data.isArray())
  541. {
  542. for (LLSD::array_const_iterator it = webrtc_data.beginArray(),
  543. end = webrtc_data.endArray();
  544. it != end; ++it)
  545. {
  546. const LLSD& data = *it;
  547. if (data.has("ice_completed"))
  548. {
  549. mIceCompleted = true;
  550. LL_DEBUGS("Voice") << "ICE complete" << LL_ENDL;
  551. }
  552. if (data.has("ice_candidates") && data["ice_candidates"].isArray())
  553. {
  554. if (mIceCandidates.isUndefined())
  555. {
  556. mIceCandidates = LLSD::emptyArray();
  557. }
  558. U32 count = 0;
  559. const LLSD& candidates = data["ice_candidates"];
  560. for (LLSD::array_const_iterator iter = candidates.beginArray(),
  561. end = candidates.endArray();
  562. iter != end; ++iter)
  563. {
  564. mIceCandidates.append(*iter);
  565. ++count;
  566. }
  567. LL_DEBUGS("Voice") << "Got " << count
  568. << " new ICE candidate(s)." << LL_ENDL;
  569. }
  570. if (data.has("channel_sdp"))
  571. {
  572. mChannelSDP = data["channel_sdp"].asString();
  573. LL_DEBUGS("Voice") << "Received offer: " << mChannelSDP
  574. << LL_ENDL;
  575. if (mVoiceConnectionState == VS_WAIT_FOR_SESSION_START)
  576. {
  577. setVoiceConnectionState(VS_REQUEST_CONNECTION, true);
  578. }
  579. }
  580. if (data.has("got_data_interface"))
  581. {
  582. LL_DEBUGS("Voice") << "Data interface ready." << LL_ENDL;
  583. mHasDataInterface = true;
  584. }
  585. if (data.has("channel_data"))
  586. {
  587. const LLSD& chan_data = data["channel_data"];
  588. if (chan_data.isArray())
  589. {
  590. for (LLSD::array_const_iterator
  591. iter = chan_data.beginArray(),
  592. end = chan_data.endArray();
  593. iter != end; ++iter)
  594. {
  595. onDataReceivedImpl(iter->asString());
  596. }
  597. }
  598. else
  599. {
  600. llwarns << "Channel data LLSD is not an array: "
  601. << chan_data << llendl;
  602. }
  603. }
  604. if (data.has("established"))
  605. {
  606. setVoiceConnectionState(VS_SESSION_ESTABLISHED);
  607. }
  608. if (data.has("closed"))
  609. {
  610. setVoiceConnectionState(VS_CLOSED);
  611. }
  612. if (data.has("renegotiate"))
  613. {
  614. setVoiceConnectionState(VS_SESSION_RETRY);
  615. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_UNKNOWN;
  616. }
  617. }
  618. }
  619. webrtc_data.clear();
  620. pluginp->unlockWebRTCData();
  621. return true;
  622. }
  623. // Primary state machine for negotiating a single voice connection to the
  624. // WebRTC server.
  625. bool LLVoiceConnection::connectionStateMachine()
  626. {
  627. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  628. if (!processPluginData())
  629. {
  630. llwarns << "Failed to process plugin data. Aborting." << llendl;
  631. setVoiceConnectionState(VS_SESSION_EXIT);
  632. return false;
  633. }
  634. // Process ICE updates if needed.
  635. if (mIceCompleted || mIceCandidates.isDefined())
  636. {
  637. gCoros.launch("processIceUpdates",
  638. boost::bind(&LLVoiceConnection::processIceUpdatesCoro,
  639. this));
  640. }
  641. if (mStateTransitionTimer.hasExpiration() &&
  642. mStateTransitionTimer.hasExpired())
  643. {
  644. LL_DEBUGS("Voice") << "Timeout waiting for state transition."
  645. << LL_ENDL;
  646. setVoiceConnectionState(VS_SESSION_RETRY, true);
  647. }
  648. // *HACK: see below in VS_SESSION_UP case for explanation. HB
  649. static LLCachedControl<F32> tune_duration(gSavedSettings,
  650. "VoiceWebRTCAutoTuneDuration");
  651. switch (getVoiceConnectionState())
  652. {
  653. case VS_START_SESSION:
  654. {
  655. mNeedsTuning = false;
  656. if (mShutDown)
  657. {
  658. LL_DEBUGS("Voice") << "Shutting voice down." << LL_ENDL;
  659. setVoiceConnectionState(VS_SESSION_EXIT);
  660. break;
  661. }
  662. // Wait for our session to be declared to the running WebRTC plugin
  663. if (!mPluginSessionCreated)
  664. {
  665. pluginCreateSession();
  666. break;
  667. }
  668. mIceCompleted = false;
  669. setVoiceConnectionState(VS_WAIT_FOR_SESSION_START);
  670. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  671. if (pluginp)
  672. {
  673. pluginp->sendVoiceCmdWithLLSD(mID, "connect", "urls",
  674. get_connection_urls());
  675. // Note: in case of failure to connect, the plugin would queue
  676. // "renegociate" request for this connection, which will also
  677. // trigger a change to VS_SESSION_RETRY. HB
  678. }
  679. break;
  680. }
  681. case VS_WAIT_FOR_SESSION_START:
  682. case VS_CONNECTION_WAIT:
  683. if (mShutDown)
  684. {
  685. LL_DEBUGS("Voice") << "Shutting voice down." << LL_ENDL;
  686. setVoiceConnectionState(VS_SESSION_EXIT);
  687. }
  688. break;
  689. case VS_REQUEST_CONNECTION:
  690. if (mShutDown)
  691. {
  692. LL_DEBUGS("Voice") << "Shutting voice down." << LL_ENDL;
  693. setVoiceConnectionState(VS_SESSION_EXIT);
  694. break;
  695. }
  696. // Ask the sim to relay to the WebRTC server our request for a
  697. // connection to a given voice channel. On completion, we will move
  698. // on to VS_SESSION_ESTABLISHED via a callback on a webrtc thread.
  699. setVoiceConnectionState(VS_CONNECTION_WAIT);
  700. gCoros.launch("requestVoiceConnectionCoro",
  701. boost::bind(&LLVoiceConnection::requestVoiceConnection,
  702. this));
  703. break;
  704. case VS_SESSION_ESTABLISHED:
  705. if (mShutDown)
  706. {
  707. LL_DEBUGS("Voice") << "Shutting voice down." << LL_ENDL;
  708. setVoiceConnectionState(VS_DISCONNECT);
  709. break;
  710. }
  711. // Update the peer connection with the various characteristics of
  712. // this connection.
  713. setMuteMic(mMuted);
  714. setSpeakerVolume(mSpeakerVolume);
  715. gVoiceWebRTC.onConnectionEstablished(mChannelID, mRegionID);
  716. setVoiceConnectionState(VS_WAIT_FOR_DATA_CHANNEL);
  717. break;
  718. case VS_WAIT_FOR_DATA_CHANNEL:
  719. if (mShutDown)
  720. {
  721. LL_DEBUGS("Voice") << "Shutting voice down." << LL_ENDL;
  722. setVoiceConnectionState(VS_DISCONNECT);
  723. break;
  724. }
  725. if (mHasDataInterface) // Wait for data interface
  726. {
  727. // *HACK: see below in VS_SESSION_UP case for explanation. HB
  728. mNeedsTuning = tune_duration > 0.f &&
  729. // Do not do this on login or after (re)enabling
  730. // voice...
  731. !sLastTunedChannelId.empty() &&
  732. #if 0 // YES: do it too in this case... So glitchy !
  733. // Do not do this on returning to the same
  734. // channel Id
  735. sLastTunedChannelId != mChannelID &&
  736. #endif
  737. !gVoiceWebRTC.inTuningMode();
  738. // Tuned or not, remember this channel.
  739. sLastTunedChannelId = mChannelID;
  740. // Tell the WebRTC server we are here via the data channel.
  741. sendJoin();
  742. setVoiceConnectionState(VS_SESSION_UP);
  743. if (isSpatial())
  744. {
  745. gVoiceWebRTC.updatePosition();
  746. gVoiceWebRTC.sendPositionUpdate(true);
  747. }
  748. }
  749. break;
  750. case VS_SESSION_UP:
  751. mRetryWaitPeriod = 0;
  752. mRetryWaitSecs = ll_frand() + 0.5f;
  753. // We stay in this sate for as long as the session remains up.
  754. if (mShutDown)
  755. {
  756. LL_DEBUGS("Voice") << "Shutting voice down." << LL_ENDL;
  757. setVoiceConnectionState(VS_DISCONNECT);
  758. }
  759. // *HACK: auto re-tune after bringing up the channel so that audio
  760. // devices are properly accounted for by WebRTC; this basically
  761. // mimics what happens when opening and closing the voice audio
  762. // devices settings floater, which also fixes such issues... HB
  763. if (mNeedsTuning)
  764. {
  765. F32 delay = llclamp(F32(tune_duration), 0.5f, 3.f);
  766. if (!gVoiceWebRTC.inTuningMode())
  767. {
  768. LL_DEBUGS("Voice") << "Auto-tuning..." << LL_ENDL;
  769. gVoiceWebRTC.refreshDeviceLists(false);
  770. gVoiceWebRTC.setTuningMode(true);
  771. LLVoiceChannel::suspend();
  772. // Re-use this timer (not covering VS_SESSION_UP since the
  773. // latter is not subject to timeouts) to delay the
  774. // simulated closing of the device settings floater. HB
  775. mStateTransitionTimer.reset();
  776. }
  777. else if (mStateTransitionTimer.getElapsedTimeF32() > delay)
  778. {
  779. gVoiceWebRTC.setTuningMode(false);
  780. LLVoiceChannel::resume();
  781. mNeedsTuning = false;
  782. LL_DEBUGS("Voice") << "Auto-tuning done." << LL_ENDL;
  783. }
  784. }
  785. break;
  786. case VS_SESSION_RETRY:
  787. {
  788. constexpr F32 UPDATE_THROTTLE_SECONDS = 0.1f;
  789. constexpr F32 MAX_RETRY_WAIT_SECONDS = 10.f;
  790. if (mRetryWaitPeriod++ * UPDATE_THROTTLE_SECONDS <= mRetryWaitSecs)
  791. {
  792. break;
  793. }
  794. // Something went wrong, so notify that the connection has failed.
  795. gVoiceWebRTC.onConnectionFailure(mChannelID, mRegionID,
  796. mCurrentStatus);
  797. setVoiceConnectionState(VS_DISCONNECT);
  798. mRetryWaitPeriod = 0;
  799. if (mRetryWaitSecs < MAX_RETRY_WAIT_SECONDS)
  800. {
  801. // Back off the retry period, and do it by a small random bit
  802. // so all clients do not reconnect at once.
  803. mRetryWaitSecs += ll_frand() + 0.5f;
  804. }
  805. break;
  806. }
  807. case VS_DISCONNECT:
  808. LL_DEBUGS("Voice") << "Disconnecting..." << LL_ENDL;
  809. gCoros.launch("breakVoiceConnectionCoro",
  810. boost::bind(&LLVoiceConnection::breakVoiceConnectionCoro,
  811. this));
  812. break;
  813. case VS_SESSION_EXIT:
  814. {
  815. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  816. if (pluginp)
  817. {
  818. setVoiceConnectionState(VS_WAIT_FOR_CLOSE);
  819. pluginp->sendVoiceCommand(mID, "disconnect");
  820. LL_DEBUGS("Voice") << "Log out message sent to voice server."
  821. << LL_ENDL;
  822. }
  823. break;
  824. }
  825. case VS_CLOSED:
  826. if (mShutDown)
  827. {
  828. // If we still have outstanding HTTP or webrtc calls, wait for
  829. // them to complete so we do not delete objects while they
  830. // still may be used.
  831. if (mOutstandingRequests <= 0)
  832. {
  833. gVoiceWebRTC.onConnectionShutDown(mChannelID, mRegionID);
  834. return false;
  835. }
  836. LL_DEBUGS_SPARSE("Voice") << "Waiting for outstanding requests before exiting."
  837. << LL_ENDL;
  838. }
  839. else
  840. {
  841. setVoiceConnectionState(VS_START_SESSION, true);
  842. }
  843. break;
  844. case VS_WAIT_FOR_EXIT:
  845. case VS_WAIT_FOR_CLOSE:
  846. case VS_SESSION_JAIL:
  847. break;
  848. default: // This should never happen
  849. llwarns_sparse << "Unknown control state: "
  850. << getVoiceConnectionState() << llendl;
  851. llassert(false);
  852. return false;
  853. }
  854. return true;
  855. }
  856. void LLVoiceConnection::onDataReceivedImpl(const std::string& data)
  857. {
  858. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  859. if (gDisconnected || LLApp::isQuitting() || mShutDown)
  860. {
  861. return;
  862. }
  863. // 'false' = no throw, 'true' = ignore comments.
  864. lljson voice_data = lljson::parse(data, NULL, false, true);
  865. if (voice_data.is_discarded())
  866. {
  867. llwarns << "Failed to parse JSON data: " << data << llendl;
  868. return;
  869. }
  870. if (!voice_data.is_object())
  871. {
  872. llwarns << "Malformed JSON data (missing object): " << data << llendl;
  873. return;
  874. }
  875. bool has_mute = false;
  876. bool has_gain = false;
  877. lljson mute = lljson::object();
  878. lljson user_gain = lljson::object();
  879. for (const auto& entry : voice_data.items())
  880. {
  881. LLUUID av_id(entry.key(), false);
  882. LL_DEBUGS("Voice") << "Processing: " << av_id << LL_ENDL;
  883. if (av_id.isNull())
  884. {
  885. // Likely a test client: ignore.
  886. continue;
  887. }
  888. const lljson& val = entry.value();
  889. LLVoiceWebRTC::pstate_ptr_t participantp =
  890. gVoiceWebRTC.findParticipantByID(mChannelID, av_id);
  891. bool muted = false;
  892. // We ignore any 'joins' reported about participants that come from
  893. // voice servers which are not their primary voice server; this may
  894. // happen with cross-region voice where a participant on a neighboring
  895. // region may be connected to multiple servers. We do not want to add
  896. // new identical participants from all of those servers.
  897. bool primary = false;
  898. bool joined = val.contains("j") && val["j"].is_object();
  899. if (joined)
  900. {
  901. LL_DEBUGS("Voice") << "New participant: " << av_id << LL_ENDL;
  902. const lljson& jval = val["j"];
  903. // A new participant has announced that they are joining.
  904. if (jval.contains("p") && jval["p"].is_boolean())
  905. {
  906. jval["p"].get_to(primary);
  907. }
  908. muted = LLMuteList::isMuted(av_id, LLMute::flagVoiceChat);
  909. if (muted)
  910. {
  911. mute[entry.key()] = true;
  912. has_mute = true;
  913. }
  914. // Default to an average volume.
  915. // *TODO: _maybe_ backport LLSpeakerVolumeStorage ? HB
  916. user_gain[entry.key()] = 100; // Note: max is 200.
  917. has_gain = true;
  918. }
  919. if (!participantp && joined && (primary || !isSpatial()))
  920. {
  921. participantp = gVoiceWebRTC.addParticipantByID(mChannelID, av_id,
  922. mRegionID);
  923. }
  924. if (!participantp)
  925. {
  926. continue;
  927. }
  928. // Keep a cached value of the muted voice chat flag. HB
  929. if (joined && muted)
  930. {
  931. LL_DEBUGS("Voice") << "Muted participant: " << av_id << LL_ENDL;
  932. participantp->mIsMuted = true;
  933. }
  934. bool leaving = false;
  935. if (val.contains("l") && val["l"].is_boolean())
  936. {
  937. val["l"].get_to(leaving);
  938. }
  939. if (leaving)
  940. {
  941. // An existing participant is leaving
  942. if (av_id != gAgentID)
  943. {
  944. LL_DEBUGS("Voice") << "Leaving participant: " << av_id
  945. << LL_ENDL;
  946. gVoiceWebRTC.removeParticipantByID(mChannelID, av_id,
  947. mRegionID);
  948. }
  949. continue;
  950. }
  951. // We got a 'power' update.
  952. if (val.contains("p") && val["p"].is_number_integer())
  953. {
  954. S32 level;
  955. val["p"].get_to(level);
  956. participantp->mLevel = F32(level) / 128.f;
  957. }
  958. if (val.contains("v") && val["v"].is_boolean())
  959. {
  960. val["v"].get_to(participantp->mIsSpeaking);
  961. }
  962. if (val.contains("m") && val["m"].is_boolean())
  963. {
  964. val["m"].get_to(participantp->mIsModeratorMuted);
  965. if (participantp->mIsModeratorMuted)
  966. {
  967. LL_DEBUGS("Voice") << "Moderator-muted participant: " << av_id
  968. << LL_ENDL;
  969. }
  970. }
  971. }
  972. // Tell the simulator to set the mute and volume data for these
  973. // participants, if there are any updates.
  974. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  975. if (!pluginp || (!has_mute && !has_gain))
  976. {
  977. return;
  978. }
  979. lljson root = lljson::object();
  980. if (has_mute)
  981. {
  982. root["m"] = mute;
  983. }
  984. if (has_gain)
  985. {
  986. root["ug"] = user_gain;
  987. }
  988. pluginp->sendVoiceCmdWithString(mID, "send_data", "json_data",
  989. to_string(root));
  990. }
  991. // Tells the WebRTC server that we are joining and whether we are joining a
  992. // server associated with the the region we currently occupy or not (primary).
  993. // The WebRTC voice server will pass this info to peers.
  994. void LLVoiceConnection::sendJoin()
  995. {
  996. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  997. LLViewerRegion* regionp = gAgent.getRegion();
  998. if (!regionp) // Disconnected
  999. {
  1000. return;
  1001. }
  1002. LL_DEBUGS("Voice") << "Sending join request." << LL_ENDL;
  1003. lljson root = lljson::object();
  1004. lljson join_obj = lljson::object();
  1005. const LLUUID& region_id = regionp->getRegionID();
  1006. if (region_id == mRegionID || !isSpatial())
  1007. {
  1008. join_obj["p"] = true;
  1009. }
  1010. root["j"] = join_obj;
  1011. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  1012. if (pluginp)
  1013. {
  1014. pluginp->sendVoiceCmdWithString(mID, "send_data", "json_data",
  1015. to_string(root));
  1016. }
  1017. }
  1018. ///////////////////////////////////////////////////////////////////////////////
  1019. // LLVoiceConnectionSpatial class
  1020. ///////////////////////////////////////////////////////////////////////////////
  1021. class LLVoiceConnectionSpatial final : public LLVoiceConnection
  1022. {
  1023. LOG_CLASS(LLVoiceConnectionSpatial);
  1024. public:
  1025. LLVoiceConnectionSpatial(const LLUUID& region_id, S32 parcel_id,
  1026. const std::string& channel_id)
  1027. : LLVoiceConnection(region_id, channel_id),
  1028. mParcelLocalID(parcel_id)
  1029. {
  1030. }
  1031. LL_INLINE bool isSpatial() override { return true; }
  1032. void setMuteMic(bool muted) override;
  1033. protected:
  1034. void requestVoiceConnection() override;
  1035. private:
  1036. S32 mParcelLocalID;
  1037. };
  1038. //virtual
  1039. void LLVoiceConnectionSpatial::setMuteMic(bool muted)
  1040. {
  1041. if (mMuted == muted)
  1042. {
  1043. return;
  1044. }
  1045. mMuted = muted;
  1046. LLViewerRegion* regionp = gAgent.getRegion();
  1047. if (!regionp || mRegionID != regionp->getRegionID())
  1048. {
  1049. // Always mute this agent with respect to neighboring regions: peers do
  1050. // not want to hear this agent from multiple regions which would just
  1051. // be echos.
  1052. muted = true;
  1053. }
  1054. LLPluginClassMedia* pluginp = gVoiceWebRTC.getPlugin();
  1055. if (pluginp)
  1056. {
  1057. pluginp->sendVoiceCmdWithBool(mID, "mute", "muted", muted);
  1058. }
  1059. }
  1060. // Asks the simulator to forward our request to the WebRTC server for a voice
  1061. // connection. The SDP is sent up as part of this, and the simulator will
  1062. // respond with an 'answer' which is in the form of another SDP. The webrtc
  1063. // library will use the offer and answer to negotiate the session.
  1064. //virtual
  1065. void LLVoiceConnectionSpatial::requestVoiceConnection()
  1066. {
  1067. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1068. LLViewerRegion* regionp = gWorld.getRegionFromID(mRegionID);
  1069. if (!regionp || !regionp->capabilitiesReceived())
  1070. {
  1071. setVoiceConnectionState(VS_SESSION_RETRY);
  1072. return;
  1073. }
  1074. const std::string& url =
  1075. regionp->getCapability("ProvisionVoiceAccountRequest");
  1076. if (url.empty())
  1077. {
  1078. llwarns_once << "No ProvisionVoiceAccountRequest capability for agent region; will retry."
  1079. << llendl;
  1080. setVoiceConnectionState(VS_SESSION_RETRY);
  1081. return;
  1082. }
  1083. LL_DEBUGS("Voice") << "Requesting spatial voice connection." << LL_ENDL;
  1084. LLSD body;
  1085. body["channel_type"] = "local";
  1086. body["voice_server_type"] = WEBRTCSTR;
  1087. if (mParcelLocalID != INVALID_PARCEL_ID)
  1088. {
  1089. body["parcel_local_id"] = mParcelLocalID;
  1090. }
  1091. LLSD data;
  1092. data["type"] = "offer";
  1093. data["sdp"] = mChannelSDP;
  1094. body["jsep"] = data;
  1095. ++mOutstandingRequests;
  1096. LLUUID connection_id = mID; // Keep a copy on the stack. HB
  1097. LLCoreHttpUtil::HttpCoroutineAdapter adapter("requestVoiceConnection");
  1098. LLSD result = adapter.postAndSuspend(url, body, mHttpOptions);
  1099. if (!sVoiceConnections.count(connection_id))
  1100. {
  1101. return;
  1102. }
  1103. --mOutstandingRequests;
  1104. if (gDisconnected || LLApp::isQuitting() || gVoiceWebRTC.isTerminated())
  1105. {
  1106. return;
  1107. }
  1108. LLCore::HttpStatus status =
  1109. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  1110. if (status)
  1111. {
  1112. onVoiceConnectionRequestSuccess(result);
  1113. return;
  1114. }
  1115. LL_DEBUGS("Voice") << "Region: " << mRegionID << ". Status: "
  1116. << status.toString() << LL_ENDL;
  1117. switch (status.getType())
  1118. {
  1119. case HTTP_CONFLICT:
  1120. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL;
  1121. break;
  1122. case HTTP_UNAUTHORIZED:
  1123. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED;
  1124. break;
  1125. case HTTP_NOT_FOUND: // The sim does not have WebRTC. HB
  1126. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  1127. // We must jail the state machine, else the connection is retried
  1128. // indefinitely ! HB
  1129. setVoiceConnectionState(VS_SESSION_JAIL, true);
  1130. return;
  1131. default:
  1132. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_UNKNOWN;
  1133. }
  1134. setVoiceConnectionState(VS_SESSION_EXIT);
  1135. }
  1136. ///////////////////////////////////////////////////////////////////////////////
  1137. // LLVoiceConnectionAdHoc class
  1138. ///////////////////////////////////////////////////////////////////////////////
  1139. class LLVoiceConnectionAdHoc final : public LLVoiceConnection
  1140. {
  1141. LOG_CLASS(LLVoiceConnectionAdHoc);
  1142. public:
  1143. LLVoiceConnectionAdHoc(const LLUUID& region_id,
  1144. const std::string& channel_id,
  1145. const std::string& credentials)
  1146. : LLVoiceConnection(region_id, channel_id),
  1147. mCredentials(credentials)
  1148. {
  1149. }
  1150. LL_INLINE bool isSpatial() override { return false; }
  1151. protected:
  1152. void requestVoiceConnection() override;
  1153. private:
  1154. std::string mCredentials;
  1155. };
  1156. // Add-hoc connections require a different channel type as they go to a
  1157. // different set of WebRTC servers. They also require credentials for the given
  1158. // channels.
  1159. //virtual
  1160. void LLVoiceConnectionAdHoc::requestVoiceConnection()
  1161. {
  1162. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1163. LLViewerRegion* regionp = gWorld.getRegionFromID(mRegionID);
  1164. if (!regionp)
  1165. {
  1166. LL_DEBUGS("Voice") << "Region " << mRegionID << " not yet live."
  1167. << LL_ENDL;
  1168. setVoiceConnectionState(VS_SESSION_RETRY);
  1169. return;
  1170. }
  1171. if (!regionp->capabilitiesReceived())
  1172. {
  1173. LL_DEBUGS("Voice") << "Capabilities not yet received for region "
  1174. << regionp->getIdentity() << LL_ENDL;
  1175. setVoiceConnectionState(VS_REQUEST_CONNECTION);
  1176. return;
  1177. }
  1178. const std::string& url = regionp->getCapability("VoiceSignalingRequest");
  1179. if (url.empty())
  1180. {
  1181. LL_DEBUGS("Voice") << "VoiceSignalingRequest capability not yet received for region "
  1182. << regionp->getIdentity() << LL_ENDL;
  1183. setVoiceConnectionState(VS_SESSION_RETRY);
  1184. return;
  1185. }
  1186. LLSD body;
  1187. body["credentials"] = mCredentials;
  1188. body["channel"] = mChannelID;
  1189. body["channel_type"] = "multiagent";
  1190. body["voice_server_type"] = WEBRTCSTR;
  1191. LLSD data;
  1192. data["type"] = "offer";
  1193. data["sdp"] = mChannelSDP;
  1194. body["jsep"] = data;
  1195. ++mOutstandingRequests;
  1196. LLUUID connection_id = mID; // Keep a copy on the stack. HB
  1197. LLCoreHttpUtil::HttpCoroutineAdapter adapter("requestVoiceConnection");
  1198. LLSD result = adapter.postAndSuspend(url, body, mHttpOptions);
  1199. if (!sVoiceConnections.count(connection_id))
  1200. {
  1201. return;
  1202. }
  1203. --mOutstandingRequests;
  1204. if (gDisconnected || LLApp::isQuitting() || gVoiceWebRTC.isTerminated())
  1205. {
  1206. return;
  1207. }
  1208. LLCore::HttpStatus status =
  1209. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  1210. if (status)
  1211. {
  1212. onVoiceConnectionRequestSuccess(result);
  1213. return;
  1214. }
  1215. LL_DEBUGS("Voice") << "Region: " << mRegionID << ". Status: "
  1216. << status.toString() << LL_ENDL;
  1217. switch (status.getType())
  1218. {
  1219. case HTTP_CONFLICT:
  1220. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL;
  1221. break;
  1222. case HTTP_UNAUTHORIZED:
  1223. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED;
  1224. break;
  1225. case HTTP_NOT_FOUND: // The sim does not have WebRTC. HB
  1226. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  1227. // We must jail the state machine, else the connection is retried
  1228. // indefinitely ! HB
  1229. setVoiceConnectionState(VS_SESSION_JAIL, true);
  1230. return;
  1231. default:
  1232. mCurrentStatus = LLVoiceClientStatusObserver::ERROR_UNKNOWN;
  1233. }
  1234. setVoiceConnectionState(VS_SESSION_EXIT);
  1235. }
  1236. ///////////////////////////////////////////////////////////////////////////////
  1237. // LLVivoxMuteListObserver class
  1238. ///////////////////////////////////////////////////////////////////////////////
  1239. class LLWebRTCMuteListObserver : public LLMuteListObserver
  1240. {
  1241. LL_INLINE void onChange() override
  1242. {
  1243. gVoiceWebRTC.muteListChanged();
  1244. }
  1245. };
  1246. static LLWebRTCMuteListObserver sMutelistListener;
  1247. ///////////////////////////////////////////////////////////////////////////////
  1248. // LLVoiceWebRTC class proper
  1249. ///////////////////////////////////////////////////////////////////////////////
  1250. LLVoiceWebRTC gVoiceWebRTC;
  1251. LLVoiceWebRTC::LLVoiceWebRTC()
  1252. : mTerminated(false),
  1253. mInitDone(false),
  1254. mIsInTuningMode(false),
  1255. mTuningMicGain(0.f),
  1256. mTuningSpeakerVolume(50),
  1257. mSpatialCoordsDirty(false),
  1258. mMuteMic(false),
  1259. mEarLocation(0),
  1260. mMicGain(0.f),
  1261. mSpeakerVolume(0.f),
  1262. mVoiceEnabled(false),
  1263. mProcessChannels(false),
  1264. mIsProcessingChannels(false),
  1265. mIsCoroutineActive(false),
  1266. mPluginHasDied(false),
  1267. mWebRTCPlugin(NULL)
  1268. {
  1269. }
  1270. LLVoiceWebRTC::~LLVoiceWebRTC()
  1271. {
  1272. // Note: since gVoiceWebRTC is a global instance and not a singleton,
  1273. // this member variable can be tested until the program _exit() call
  1274. // without needing it to be static. HB
  1275. mTerminated = true;
  1276. }
  1277. //virtual
  1278. const std::string& LLVoiceWebRTC::getName() const
  1279. {
  1280. return WEBRTCSTR;
  1281. }
  1282. void LLVoiceWebRTC::updateSettings()
  1283. {
  1284. static LLCachedControl<bool> echo_cancel(gSavedSettings,
  1285. "VoiceWebRTCEchoCancellation");
  1286. static LLCachedControl<bool> agc(gSavedSettings,
  1287. "VoiceWebRTCAutomaticGainControl");
  1288. static LLCachedControl<U32> level(gSavedSettings,
  1289. "VoiceWebRTCNoiseSuppression");
  1290. S32 l = llmin(4, level);
  1291. LLSD config;
  1292. config["cancel_echo"] = LLSD::Boolean(echo_cancel);
  1293. config["agc"] = LLSD::Boolean(agc);
  1294. config["noise_suppression"] = LLSD::Integer(l);
  1295. if (mWebRTCPlugin)
  1296. {
  1297. mWebRTCPlugin->setConfiguration(config);
  1298. }
  1299. }
  1300. void LLVoiceWebRTC::init()
  1301. {
  1302. if (mTerminated || mInitDone)
  1303. {
  1304. return;
  1305. }
  1306. mInitDone = true;
  1307. llinfos << "Initializing WebRTC voice." << llendl;
  1308. if (gSavedSettings.getBool("DebugWebRTCVoice"))
  1309. {
  1310. HBFloaterDebugTags::setTag("Voice", true);
  1311. HBFloaterDebugTags::setTag("Plugin", true);
  1312. }
  1313. gIdleCallbacks.addFunction(LLVoiceWebRTC::idle, &gVoiceWebRTC);
  1314. }
  1315. void LLVoiceWebRTC::terminate()
  1316. {
  1317. if (mTerminated)
  1318. {
  1319. return;
  1320. }
  1321. if (mInitDone)
  1322. {
  1323. if (mSession)
  1324. {
  1325. llinfos << "Leaving WebRTC voice session..." << llendl;
  1326. mSession->shutdownAllConnections();
  1327. }
  1328. gIdleCallbacks.deleteFunction(LLVoiceWebRTC::idle, &gVoiceWebRTC);
  1329. killPlugin();
  1330. mVoiceEnabled = false;
  1331. llinfos << "WebRTC voice terminated." << llendl;
  1332. }
  1333. mTerminated = true;
  1334. }
  1335. void LLVoiceWebRTC::cleanUp()
  1336. {
  1337. LL_DEBUGS("Voice") << "Cleaning up..." << LL_ENDL;
  1338. mNextSession.reset();
  1339. mSession.reset();
  1340. mNeighboringRegions.clear();
  1341. sessionState::forEach(boost::bind(predShutdownSession, _1));
  1342. killPlugin();
  1343. // We simply removed the plugin: no need to ask sessions to reconnect
  1344. // after we will relaunch another. HB
  1345. mPluginHasDied = false;
  1346. }
  1347. bool LLVoiceWebRTC::launchPlugin()
  1348. {
  1349. // Remove any (dead) plugin media instance from memory. HB
  1350. killPlugin();
  1351. std::string launcher_name = gDirUtil.getLLPluginLauncher();
  1352. std::string plugin_name =
  1353. gDirUtil.getLLPluginFilename("voice_plugin_webrtc");
  1354. if (!LLFile::isfile(launcher_name) || !LLFile::isfile(plugin_name))
  1355. {
  1356. llwarns << "Missing plugin or launcher executable: cannot procceed."
  1357. << llendl;
  1358. terminate();
  1359. return false;
  1360. }
  1361. // We specify a NULL owner because we do not care about media events
  1362. // which are not used by our plugin. HB
  1363. mWebRTCPlugin = new LLPluginClassMedia(NULL);
  1364. const std::string& plugin_dir = gDirUtil.getLLPluginDir();
  1365. if (!mWebRTCPlugin->init(launcher_name, plugin_dir, plugin_name, false))
  1366. {
  1367. llwarns << "Failed to initialize the voice plugin: cannot procceed."
  1368. << llendl;
  1369. mWebRTCPlugin->setDeleteOK(true);
  1370. delete mWebRTCPlugin;
  1371. mWebRTCPlugin = NULL;
  1372. terminate();
  1373. return false;
  1374. }
  1375. mWebRTCPlugin->setLockupTimeout(3.f); // Timeout after 3 seconds freezes
  1376. return true;
  1377. }
  1378. void LLVoiceWebRTC::killPlugin()
  1379. {
  1380. if (mWebRTCPlugin)
  1381. {
  1382. LLPluginClassMedia* old_pluginp = mWebRTCPlugin;
  1383. mWebRTCPlugin = NULL;
  1384. old_pluginp->setDeleteOK(true);
  1385. delete old_pluginp;
  1386. }
  1387. }
  1388. //static
  1389. void LLVoiceWebRTC::idle(void* userdatap)
  1390. {
  1391. LLVoiceWebRTC* self = (LLVoiceWebRTC*)userdatap;
  1392. if (self != &gVoiceWebRTC || !self->mVoiceEnabled)
  1393. {
  1394. return;
  1395. }
  1396. // At this point, check that a plugin has been launched and if not, launch
  1397. // one (this will happen when voice has just been (re-)enabled). HB
  1398. if (!self->mWebRTCPlugin && !self->launchPlugin())
  1399. {
  1400. return; // Failure to launch. Voice terminated at this point. HB
  1401. }
  1402. // Check to see if the current plugin has exited (crash or timeout due to a
  1403. // deadlock); if yes, relaunch another. HB
  1404. if (self->mWebRTCPlugin->isPluginExited())
  1405. {
  1406. llwarns << "WebRTC plugin died: relaunching another..." << llendl;
  1407. if (!self->launchPlugin())
  1408. {
  1409. return; // Failure to launch. Voice terminated at this point. HB
  1410. }
  1411. self->mPluginHasDied = true;
  1412. }
  1413. // Here, we know we have a healthy plugin, but it might not yet be ready to
  1414. // process commands, so check it does have reached the "running" state. HB
  1415. if (self->mWebRTCPlugin->isPluginRunning())
  1416. {
  1417. static F32 last_ping = 0.f;
  1418. if (gFrameTimeSeconds - last_ping >= 1.f)
  1419. {
  1420. last_ping = gFrameTimeSeconds;
  1421. // Note: this command will be ignored by the plugin, but allows to
  1422. // detect a frozen plugin when no voice command has been sent to it
  1423. // for a while. HB
  1424. self->mWebRTCPlugin->sendVoiceCommand(LLUUID::null, "ping");
  1425. }
  1426. if (self->mPluginHasDied)
  1427. {
  1428. self->mPluginHasDied = false;
  1429. // Signal to all sessions that they need to reconnect with our new
  1430. // plugin. HB
  1431. sessionState::forEach(boost::bind(predReconnect, _1));
  1432. }
  1433. // Refresh the debug flag (this is a no operation when it has not
  1434. // changed since plugin start). HB
  1435. static LLCachedControl<bool> debug(gSavedSettings, "DebugVoicePlugin");
  1436. self->mWebRTCPlugin->setVoiceDebug(debug);
  1437. }
  1438. // This call allows the plugin to process commands and send back its data.
  1439. self->mWebRTCPlugin->idle();
  1440. }
  1441. //static
  1442. void LLVoiceWebRTC::predReconnect(const sessionState::ptr_t& sessionp)
  1443. {
  1444. sessionp->reconnectAllSessions();
  1445. }
  1446. //static
  1447. void LLVoiceWebRTC::predShutdownSession(const sessionState::ptr_t& sessionp)
  1448. {
  1449. sessionp->shutdownAllConnections();
  1450. }
  1451. bool LLVoiceWebRTC::isVoiceWorking() const
  1452. {
  1453. return !mTerminated && mVoiceEnabled && mIsProcessingChannels;
  1454. }
  1455. #if 0 // Not used in the Cool VL Viewer
  1456. void LLVoiceWebRTC::addObserver(LLVoiceClientParticipantObserver* observerp)
  1457. {
  1458. mParticipantObservers.insert(observerp);
  1459. }
  1460. void LLVoiceWebRTC::removeObserver(LLVoiceClientParticipantObserver* observerp)
  1461. {
  1462. mParticipantObservers.erase(observerp);
  1463. }
  1464. void LLVoiceWebRTC::notifyParticipantObservers()
  1465. {
  1466. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1467. for (observer_set_t::iterator it = mParticipantObservers.begin();
  1468. it != mParticipantObservers.end(); )
  1469. {
  1470. LLVoiceClientParticipantObserver* observerp = *it;
  1471. observerp->onParticipantsChanged();
  1472. // In case onParticipantsChanged() deleted an entry.
  1473. it = mParticipantObservers.upper_bound(observerp);
  1474. }
  1475. }
  1476. #endif
  1477. void LLVoiceWebRTC::addObserver(LLVoiceClientStatusObserver* observerp)
  1478. {
  1479. mStatusObservers.insert(observerp);
  1480. }
  1481. void LLVoiceWebRTC::removeObserver(LLVoiceClientStatusObserver* observerp)
  1482. {
  1483. mStatusObservers.erase(observerp);
  1484. }
  1485. void LLVoiceWebRTC::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
  1486. {
  1487. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1488. mIsProcessingChannels =
  1489. status == LLVoiceClientStatusObserver::STATUS_JOINED;
  1490. LL_DEBUGS("Voice") << LLVoiceClientStatusObserver::status2string(status)
  1491. << " - Channel info: " << getAudioSessionChannelInfo()
  1492. << " - Proximal is "
  1493. << (inSpatialChannel() ? "true" : "false") << LL_ENDL;
  1494. #if 0
  1495. if (!mProcessChannels)
  1496. {
  1497. return;
  1498. }
  1499. #endif
  1500. LLSD channel_info = getAudioSessionChannelInfo();
  1501. for (status_observer_set_t::iterator it = mStatusObservers.begin();
  1502. it != mStatusObservers.end(); )
  1503. {
  1504. LLVoiceClientStatusObserver* observerp = *it;
  1505. observerp->onChange(status, channel_info, inSpatialChannel());
  1506. // In case onParticipantsChanged() deleted an entry.
  1507. it = mStatusObservers.upper_bound(observerp);
  1508. }
  1509. }
  1510. // Primary voice loop: once launched in a coroutine, it loops every 100ms plus
  1511. // the time it takes to process the various functions called in the loop. It
  1512. // does the following:
  1513. // - It gates whether we do channel processing depending on whether we are
  1514. // running a WebRTC voice channel or one from another voice provider.
  1515. void LLVoiceWebRTC::voiceConnectionCoro()
  1516. {
  1517. mIsCoroutineActive = true;
  1518. LLCoros::set_consuming(true);
  1519. std::string channel_id;
  1520. while (!mTerminated)
  1521. {
  1522. llcoro::suspendUntilTimeout(0.1f);
  1523. if (gDisconnected || LLApp::isQuitting() || mTerminated)
  1524. {
  1525. break;
  1526. }
  1527. LLViewerRegion* regionp = gAgent.getRegion();
  1528. if (!regionp || regionp->getRegionID().isNull() ||
  1529. !isAgentAvatarValid())
  1530. {
  1531. continue;
  1532. }
  1533. checkDevicesChanged();
  1534. bool voice_enabled = mVoiceEnabled;
  1535. if (!mProcessChannels)
  1536. {
  1537. // We have switched away from webrtc voice, so shut all channels
  1538. // down. Note that leaveChannel() can be called again and again
  1539. // without adverse effects: it merely tells channels to shut down
  1540. // if they are not already doing so.
  1541. leaveChannel(false);
  1542. }
  1543. else if (inSpatialChannel())
  1544. {
  1545. bool use_estate_voice = true;
  1546. // Add session for region or parcel voice.
  1547. if (!regionp->isVoiceEnabled())
  1548. {
  1549. voice_enabled = false;
  1550. }
  1551. if (voice_enabled)
  1552. {
  1553. LLParcel* parcelp = gViewerParcelMgr.getAgentParcel();
  1554. if (parcelp && parcelp->getLocalID() != INVALID_PARCEL_ID)
  1555. {
  1556. if (!parcelp->getParcelFlagAllowVoice())
  1557. {
  1558. voice_enabled = false;
  1559. }
  1560. else if (!parcelp->getParcelFlagUseEstateVoiceChannel())
  1561. {
  1562. // Use the parcel-specific voice channel.
  1563. use_estate_voice = false;
  1564. S32 parcel_id = parcelp->getLocalID();
  1565. channel_id = llformat("%s-%d",
  1566. regionp->getRegionID().asString().c_str(),
  1567. parcel_id);
  1568. if (!inOrJoiningChannel(channel_id))
  1569. {
  1570. startParcelSession(channel_id, parcel_id);
  1571. }
  1572. }
  1573. if (voice_enabled && use_estate_voice &&
  1574. !inEstateChannel())
  1575. {
  1576. startEstateSession();
  1577. }
  1578. }
  1579. if (voice_enabled)
  1580. {
  1581. // We are in spatial voice, and voice is enabled, so
  1582. // determine positions in order to send position updates.
  1583. updatePosition();
  1584. }
  1585. else
  1586. {
  1587. // Voice is disabled, so leave and disable PTT
  1588. leaveChannel(true);
  1589. // Also stop processing channels. HB
  1590. mProcessChannels = false;
  1591. }
  1592. }
  1593. sessionState::processSessionStates();
  1594. #if 0 // mHidden is not used in the Cool VL Viewer
  1595. if (voice_enabled && mProcessChannels && !mHidden)
  1596. #else
  1597. if (voice_enabled && mProcessChannels)
  1598. #endif
  1599. {
  1600. sendPositionUpdate(false);
  1601. updateOwnVolume();
  1602. }
  1603. }
  1604. }
  1605. cleanUp();
  1606. }
  1607. void LLVoiceWebRTC::updateNeighboringRegions()
  1608. {
  1609. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1610. constexpr F64 SQRT2BY2 = 0.5 * F_SQRT2;
  1611. static const std::vector<LLVector3d> neighbors
  1612. {
  1613. LLVector3d(0.0, 1.0, 0.0), LLVector3d(SQRT2BY2, SQRT2BY2, 0.0),
  1614. LLVector3d(1.0, 0.0, 0.0), LLVector3d(SQRT2BY2, -SQRT2BY2, 0.0),
  1615. LLVector3d(0.0, -1.0, 0.0), LLVector3d(-SQRT2BY2, -SQRT2BY2, 0.0),
  1616. LLVector3d(-1.0, 0.0, 0.0), LLVector3d(-SQRT2BY2, SQRT2BY2, 0.0)
  1617. };
  1618. // Estate voice requires connection to neighboring regions.
  1619. mNeighboringRegions.clear();
  1620. mNeighboringRegions.insert(gAgent.getRegion()->getRegionID());
  1621. // Base off of speaker position as it will move more slowly than camera
  1622. // position. Once we have hysteresis, we may be able to track off of
  1623. // speaker and camera position at 50m.
  1624. // *TODO: add hysteresis so we do not flip-flop connections to neighbors.
  1625. LLVector3d pos;
  1626. for (size_t i = 0, count = neighbors.size(); i < count; ++i)
  1627. {
  1628. const LLVector3d& neighbor_pos = neighbors[i];
  1629. // Include every region within 100m to deal with the fact that the
  1630. // camera can stray 50m away from the avatar.
  1631. pos = mAvatarPosition + 100 * neighbor_pos;
  1632. LLViewerRegion* regionp = gWorld.getRegionFromPosGlobal(pos);
  1633. if (regionp && regionp->getRegionID().notNull())
  1634. {
  1635. mNeighboringRegions.insert(regionp->getRegionID());
  1636. }
  1637. }
  1638. }
  1639. //virtual
  1640. void LLVoiceWebRTC::leaveAudioSession()
  1641. {
  1642. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1643. if (mSession)
  1644. {
  1645. LL_DEBUGS("Voice") << "Leaving session: " << mSession->mChannelID
  1646. << LL_ENDL;
  1647. mSession->shutdownAllConnections();
  1648. }
  1649. else
  1650. {
  1651. llwarns << "Called with no active session" << llendl;
  1652. }
  1653. }
  1654. void LLVoiceWebRTC::checkDevicesChanged()
  1655. {
  1656. if (!mWebRTCPlugin)
  1657. {
  1658. return;
  1659. }
  1660. mWebRTCPlugin->lockWebRTCData();
  1661. LLSD& capture = mWebRTCPlugin->getCaptureDevices();
  1662. if (capture.isMap())
  1663. {
  1664. clearCaptureDevices();
  1665. for (LLSD::map_const_iterator it = capture.beginMap(),
  1666. end = capture.endMap();
  1667. it != end; ++it)
  1668. {
  1669. addCaptureDevice(it->first, it->second.asString());
  1670. }
  1671. capture.clear();
  1672. }
  1673. LLSD& render = mWebRTCPlugin->getRenderDevices();
  1674. if (render.isMap())
  1675. {
  1676. clearRenderDevices();
  1677. for (LLSD::map_const_iterator it = render.beginMap(),
  1678. end = render.endMap();
  1679. it != end; ++it)
  1680. {
  1681. addRenderDevice(it->first, it->second.asString());
  1682. }
  1683. render.clear();
  1684. }
  1685. mWebRTCPlugin->unlockWebRTCData();
  1686. }
  1687. void LLVoiceWebRTC:: addCaptureDevice(const std::string& display_name,
  1688. const std::string& device_id)
  1689. {
  1690. // Yep, it may happen... In this case, and since we will not be able to
  1691. // set (and therefore use) this device, just skip it. HB
  1692. if (device_id.empty())
  1693. {
  1694. llwarns << "Got an empty device Id for render device name: "
  1695. << display_name << llendl;
  1696. return;
  1697. }
  1698. if (display_name.empty()) // Could happen, I suppose... HB
  1699. {
  1700. mCaptureDevices.emplace(device_id, device_id);
  1701. }
  1702. else // Normal case
  1703. {
  1704. mCaptureDevices.emplace(display_name, device_id);
  1705. }
  1706. }
  1707. void LLVoiceWebRTC::setCaptureDevice(const std::string& device_id)
  1708. {
  1709. if (!mWebRTCPlugin)
  1710. {
  1711. return;
  1712. }
  1713. LL_DEBUGS("Voice") << "Setting audio capture device to: " << device_id
  1714. << LL_ENDL;
  1715. if (device_id.empty())
  1716. {
  1717. // Interpret as "Default", like what Vivox is doing. HB
  1718. mWebRTCPlugin->setCaptureDevice("Default");
  1719. }
  1720. else
  1721. {
  1722. mWebRTCPlugin->setCaptureDevice(device_id);
  1723. }
  1724. }
  1725. void LLVoiceWebRTC::addRenderDevice(const std::string& display_name,
  1726. const std::string& device_id)
  1727. {
  1728. // Yep, it may happen... In this case, and since we will not be able to
  1729. // set (and therefore use) this device, just skip it. HB
  1730. if (device_id.empty())
  1731. {
  1732. llwarns << "Got an empty device Id for render device name: "
  1733. << display_name << llendl;
  1734. return;
  1735. }
  1736. if (display_name.empty()) // Could happen, I suppose... HB
  1737. {
  1738. mRenderDevices.emplace(device_id, device_id);
  1739. }
  1740. else // Normal case
  1741. {
  1742. mRenderDevices.emplace(display_name, device_id);
  1743. }
  1744. }
  1745. void LLVoiceWebRTC::setRenderDevice(const std::string& device_id)
  1746. {
  1747. if (!mWebRTCPlugin)
  1748. {
  1749. return;
  1750. }
  1751. LL_DEBUGS("Voice") << "Setting audio render device to: " << device_id
  1752. << LL_ENDL;
  1753. if (device_id.empty())
  1754. {
  1755. // Interpret as "Default", like what Vivox is doing. HB
  1756. mWebRTCPlugin->setRenderDevice("Default");
  1757. }
  1758. else
  1759. {
  1760. mWebRTCPlugin->setRenderDevice(device_id);
  1761. }
  1762. }
  1763. bool LLVoiceWebRTC::deviceSettingsAvailable()
  1764. {
  1765. return !mRenderDevices.empty() && !mCaptureDevices.empty();
  1766. }
  1767. void LLVoiceWebRTC::refreshDeviceLists(bool clear_current_list)
  1768. {
  1769. if (clear_current_list)
  1770. {
  1771. LL_DEBUGS("Voice") << "Clearing current list of audio devices"
  1772. << LL_ENDL;
  1773. mCaptureDevices.clear();
  1774. mRenderDevices.clear();
  1775. }
  1776. if (mWebRTCPlugin)
  1777. {
  1778. LL_DEBUGS("Voice") << "Requesting audio devices list..." << LL_ENDL;
  1779. mWebRTCPlugin->refreshDevices();
  1780. }
  1781. }
  1782. void LLVoiceWebRTC::setTuningMode(bool tuning_on)
  1783. {
  1784. if (mIsInTuningMode != tuning_on && mWebRTCPlugin)
  1785. {
  1786. mIsInTuningMode = tuning_on;
  1787. mWebRTCPlugin->setTuningMode(tuning_on);
  1788. }
  1789. }
  1790. F32 LLVoiceWebRTC::getAudioLevel()
  1791. {
  1792. constexpr F32 LEVEL_SCALE = 0.008f;
  1793. if (!mWebRTCPlugin)
  1794. {
  1795. return 0.f;
  1796. }
  1797. F32 level = mWebRTCPlugin->getAudioLevel();
  1798. F32 gain = mIsInTuningMode ? mTuningMicGain : mMicGain;
  1799. return (1.f - level * LEVEL_SCALE) * gain / 2.1f;
  1800. }
  1801. // The user's mute list has been updated. This method goes through the current
  1802. // participants list and syncs it with the mute list.
  1803. void LLVoiceWebRTC::muteListChanged()
  1804. {
  1805. sessionState::forEach(boost::bind(predMuteListChanged, _1));
  1806. }
  1807. //static
  1808. void LLVoiceWebRTC::predMuteListChanged(const sessionState::ptr_t& sessionp)
  1809. {
  1810. sessionp->muteListChanged();
  1811. }
  1812. void LLVoiceWebRTC::onConnectionEstablished(const std::string& channel_id,
  1813. const LLUUID& region_id)
  1814. {
  1815. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1816. LLViewerRegion* regionp = gAgent.getRegion();
  1817. if (!regionp || regionp->getRegionID() != region_id)
  1818. {
  1819. return;
  1820. }
  1821. if (mNextSession && mNextSession->mChannelID == channel_id)
  1822. {
  1823. if (mSession)
  1824. {
  1825. mSession->shutdownAllConnections();
  1826. }
  1827. mSession = mNextSession;
  1828. mNextSession.reset();
  1829. // Add ourselves as a participant.
  1830. if (mSession)
  1831. {
  1832. LLViewerRegion* regionp = gAgent.getRegion();
  1833. if (regionp)
  1834. {
  1835. mSession->addParticipant(gAgentID, regionp->getRegionID());
  1836. }
  1837. }
  1838. }
  1839. if (mSession && mSession->mChannelID == channel_id)
  1840. {
  1841. // The current session was established.
  1842. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
  1843. // Only set status to joined if asked to. This will happen in the case
  1844. // where we are not doing an ad-hoc based p2p session. Those sessions
  1845. // expect a STATUS_JOINED when the peer has, in fact, joined, which we
  1846. // detect elsewhere.
  1847. if (!mSession->mNotifyOnFirstJoin)
  1848. {
  1849. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
  1850. }
  1851. }
  1852. }
  1853. void LLVoiceWebRTC::onConnectionShutDown(const std::string& channel_id,
  1854. const LLUUID& region_id)
  1855. {
  1856. if (mSession && mSession->mChannelID == channel_id)
  1857. {
  1858. LLViewerRegion* regionp = gAgent.getRegion();
  1859. if (regionp && regionp->getRegionID() == region_id)
  1860. {
  1861. mSession->removeAllParticipants(region_id);
  1862. }
  1863. }
  1864. }
  1865. void LLVoiceWebRTC::onConnectionFailure(const std::string& channel_id,
  1866. const LLUUID& region_id,
  1867. LLVoiceClientStatusObserver::EStatusType status)
  1868. {
  1869. LLViewerRegion* regionp = gAgent.getRegion();
  1870. if (!regionp || regionp->getRegionID() != region_id)
  1871. {
  1872. return;
  1873. }
  1874. if ((mNextSession && mNextSession->mChannelID == channel_id) ||
  1875. (mSession && mSession->mChannelID == channel_id))
  1876. {
  1877. notifyStatusObservers(status);
  1878. }
  1879. }
  1880. LLVoiceWebRTC::particip_map_t* LLVoiceWebRTC::getParticipantList()
  1881. {
  1882. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1883. particip_map_t* result = NULL;
  1884. if (mProcessChannels && mSession)
  1885. {
  1886. result = &(mSession->mParticipantsByUUID);
  1887. }
  1888. return result;
  1889. }
  1890. bool LLVoiceWebRTC::isParticipant(const LLUUID& id)
  1891. {
  1892. return mProcessChannels && mSession &&
  1893. mSession->mParticipantsByUUID.count(id);
  1894. }
  1895. LLVoiceWebRTC::pstate_ptr_t LLVoiceWebRTC::findParticipantByID(const std::string& channel_id,
  1896. const LLUUID& id)
  1897. {
  1898. pstate_ptr_t result;
  1899. sessionState::ptr_t sessionp =
  1900. sessionState::matchSessionByChannelID(channel_id);
  1901. if (sessionp)
  1902. {
  1903. result = sessionp->findParticipantByID(id);
  1904. }
  1905. return result;
  1906. }
  1907. LLVoiceWebRTC::pstate_ptr_t LLVoiceWebRTC::addParticipantByID(const std::string& channel_id,
  1908. const LLUUID& id,
  1909. const LLUUID& region_id)
  1910. {
  1911. pstate_ptr_t result;
  1912. sessionState::ptr_t sessionp =
  1913. sessionState::matchSessionByChannelID(channel_id);
  1914. if (sessionp)
  1915. {
  1916. LL_DEBUGS("Voice") << "Adding participant " << id << " to channel "
  1917. << channel_id << LL_ENDL;
  1918. result = sessionp->addParticipant(id, region_id);
  1919. if (sessionp->mNotifyOnFirstJoin && id != gAgentID)
  1920. {
  1921. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
  1922. }
  1923. }
  1924. return result;
  1925. }
  1926. void LLVoiceWebRTC::removeParticipantByID(const std::string& channel_id,
  1927. const LLUUID& id,
  1928. const LLUUID& region_id)
  1929. {
  1930. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  1931. pstate_ptr_t result;
  1932. sessionState::ptr_t sessionp =
  1933. sessionState::matchSessionByChannelID(channel_id);
  1934. if (!sessionp)
  1935. {
  1936. return;
  1937. }
  1938. pstate_ptr_t participantp = sessionp->findParticipantByID(id);
  1939. if (!participantp || participantp->mRegion != region_id)
  1940. {
  1941. return;
  1942. }
  1943. LL_DEBUGS("Voice") << "Removing participant " << id << " from channel "
  1944. << channel_id << LL_ENDL;
  1945. sessionp->removeParticipant(participantp);
  1946. }
  1947. void LLVoiceWebRTC::startEstateSession()
  1948. {
  1949. leaveChannel(false);
  1950. mNextSession = addSession("Estate",
  1951. sessionState::ptr_t(new estateSessionState()));
  1952. }
  1953. void LLVoiceWebRTC::startParcelSession(const std::string& channel_id,
  1954. S32 parcel_id)
  1955. {
  1956. leaveChannel(false);
  1957. mNextSession =
  1958. addSession(channel_id,
  1959. sessionState::ptr_t(new parcelSessionState(channel_id,
  1960. parcel_id)));
  1961. }
  1962. void LLVoiceWebRTC::setSpatialChannel(const LLSD& channel_info)
  1963. {
  1964. if (!channel_info.isMap() || !channel_info.has("channel_uri"))
  1965. {
  1966. return;
  1967. }
  1968. bool allow_voice = !channel_info["channel_uri"].asString().empty();
  1969. LLViewerRegion* regionp = gAgent.getRegion();
  1970. if (!regionp || !isAgentAvatarValid())
  1971. {
  1972. return;
  1973. }
  1974. LLParcel* parcelp = gViewerParcelMgr.getAgentParcel();
  1975. if (parcelp)
  1976. {
  1977. parcelp->setParcelFlag(PF_ALLOW_VOICE_CHAT, allow_voice);
  1978. bool use_estate =
  1979. channel_info["channel_uri"].asUUID() == regionp->getRegionID();
  1980. parcelp->setParcelFlag(PF_USE_ESTATE_VOICE_CHAN, use_estate);
  1981. }
  1982. else
  1983. {
  1984. regionp->setRegionFlag(REGION_FLAGS_ALLOW_VOICE, allow_voice);
  1985. }
  1986. }
  1987. // This is synonymous to startAdHocSession() in LL's sources. HB
  1988. //virtual
  1989. void LLVoiceWebRTC::setNonSpatialChannel(const LLSD& channel_info,
  1990. bool notify_join,
  1991. bool hangup_on_last)
  1992. {
  1993. leaveChannel(false);
  1994. LL_DEBUGS("Voice") << "Starting AdHoc session: " << channel_info
  1995. << LL_ENDL;
  1996. std::string channel_id = channel_info["channel_uri"];
  1997. std::string credentials = channel_info["channel_credentials"];
  1998. mNextSession =
  1999. addSession(channel_id,
  2000. sessionState::ptr_t(new adhocSessionState(channel_id,
  2001. credentials,
  2002. notify_join,
  2003. hangup_on_last)));
  2004. }
  2005. //virtual
  2006. void LLVoiceWebRTC::leaveNonSpatialChannel()
  2007. {
  2008. LL_DEBUGS("Voice") << "Request to leave non-spatial channel." << LL_ENDL;
  2009. deleteSession(mNextSession);
  2010. leaveChannel(true);
  2011. }
  2012. bool LLVoiceWebRTC::inOrJoiningChannel(const std::string& channel_id)
  2013. {
  2014. return (mSession && mSession->mChannelID == channel_id) ||
  2015. (mNextSession && mNextSession->mChannelID == channel_id);
  2016. }
  2017. bool LLVoiceWebRTC::inEstateChannel()
  2018. {
  2019. return (mSession && mSession->isEstate()) ||
  2020. (mNextSession && mNextSession->isEstate());
  2021. }
  2022. bool LLVoiceWebRTC::inSpatialChannel() const
  2023. {
  2024. if (mNextSession)
  2025. {
  2026. return mNextSession->isSpatial();
  2027. }
  2028. if (mSession)
  2029. {
  2030. return mSession->isSpatial();
  2031. }
  2032. return true;
  2033. }
  2034. LLSD LLVoiceWebRTC::getAudioSessionChannelInfo()
  2035. {
  2036. LLSD result;
  2037. if (mSession)
  2038. {
  2039. result["voice_server_type"] = WEBRTCSTR;
  2040. result["channel_uri"] = mSession->mChannelID;
  2041. }
  2042. return result;
  2043. }
  2044. void LLVoiceWebRTC::leaveChannel(bool stop_talking)
  2045. {
  2046. if (mSession)
  2047. {
  2048. LL_DEBUGS("Voice") << "Leaving channel for teleport/logout" << LL_ENDL;
  2049. deleteSession(mSession);
  2050. }
  2051. if (mNextSession)
  2052. {
  2053. LL_DEBUGS("Voice") << "Leaving next channel for teleport/logout"
  2054. << LL_ENDL;
  2055. deleteSession(mNextSession);
  2056. }
  2057. if (stop_talking && gVoiceClient.getUserPTTState())
  2058. {
  2059. gVoiceClient.setUserPTTState(false);
  2060. }
  2061. }
  2062. bool LLVoiceWebRTC::isCurrentChannel(const LLSD& channel_info)
  2063. {
  2064. if (!mProcessChannels || !mSession ||
  2065. channel_info["voice_server_type"].asString() != WEBRTCSTR)
  2066. {
  2067. return false;
  2068. }
  2069. if (mSession)
  2070. {
  2071. if (!channel_info["session_handle"].asString().empty())
  2072. {
  2073. return mSession->mHandle == channel_info["session_handle"].asString();
  2074. }
  2075. return channel_info["channel_uri"].asString() == mSession->mChannelID;
  2076. }
  2077. return false;
  2078. }
  2079. bool LLVoiceWebRTC::compareChannels(const LLSD& info1, const LLSD& info2)
  2080. {
  2081. return info1["voice_server_type"].asString() == WEBRTCSTR &&
  2082. info2["voice_server_type"].asString() == WEBRTCSTR &&
  2083. info1["sip_uri"].asString() == info2["sip_uri"].asString();
  2084. }
  2085. LLVoiceWebRTC::sessionState::ptr_t
  2086. LLVoiceWebRTC::addSession(const std::string& channel_id,
  2087. sessionState::ptr_t sessionp)
  2088. {
  2089. #if 0 // Reviving does not properly work... Any old session will then be
  2090. // deleted when the call to sessionState::addSession() will overwrite
  2091. // the smart pointer in sSessions. HB
  2092. sessionState::ptr_t old_sessionp =
  2093. sessionState::matchSessionByChannelID(channel_id);
  2094. if (old_sessionp)
  2095. {
  2096. LL_DEBUGS("Voice") << "Reviving existing session for channel: "
  2097. << channel_id << LL_ENDL;
  2098. old_sessionp->revive();
  2099. return old_sessionp;
  2100. }
  2101. #endif
  2102. LL_DEBUGS("Voice") << "Adding a new session for channel: " << channel_id
  2103. << LL_ENDL;
  2104. sessionp->setMuteMic(mMuteMic);
  2105. sessionp->setSpeakerVolume(mSpeakerVolume);
  2106. sessionState::addSession(channel_id, sessionp);
  2107. return sessionp;
  2108. }
  2109. LLVoiceWebRTC::sessionState::ptr_t LLVoiceWebRTC::findP2PSession(const LLUUID& id)
  2110. {
  2111. sessionState::ptr_t sessionp =
  2112. sessionState::matchSessionByChannelID(id.asString());
  2113. if (sessionp && !sessionp->isSpatial())
  2114. {
  2115. return sessionp;
  2116. }
  2117. sessionp.reset();
  2118. return sessionp;
  2119. }
  2120. void LLVoiceWebRTC::deleteSession(const sessionState::ptr_t& sessionp)
  2121. {
  2122. if (!sessionp) return;
  2123. // At this point, the session should be unhooked from all lists and all
  2124. // states should be consistent.
  2125. sessionp->shutdownAllConnections();
  2126. if (sessionp == mSession)
  2127. {
  2128. mSession.reset();
  2129. }
  2130. else if (sessionp == mNextSession)
  2131. {
  2132. mNextSession.reset();
  2133. }
  2134. }
  2135. #if 0 // Not used in the Cool VL Viewer
  2136. bool LLVoiceWebRTC::setHidden(bool hidden)
  2137. {
  2138. if (mHidden == hidden)
  2139. {
  2140. return;
  2141. }
  2142. if (inSpatialChannel())
  2143. {
  2144. if (mHidden)
  2145. {
  2146. // Get out of the channel entirely; mute the microphone.
  2147. sessionState::forEach(boost::bind(predSetMuteMic, _1, true));
  2148. }
  2149. else // Put it back
  2150. {
  2151. sessionState::forEach(boost::bind(predSetMuteMic, _1, mMuteMic));
  2152. updatePosition();
  2153. sendPositionUpdate(true);
  2154. }
  2155. }
  2156. }
  2157. #endif
  2158. void LLVoiceWebRTC::setMuteMic(bool muted)
  2159. {
  2160. #if 0 // mHidden not used in the Cool VL Viewer
  2161. if (mMuteMic != muted && !mHidden)
  2162. #else
  2163. if (mMuteMic != muted)
  2164. #endif
  2165. {
  2166. sessionState::forEach(boost::bind(predSetMuteMic, _1, muted));
  2167. }
  2168. }
  2169. //static
  2170. void LLVoiceWebRTC::predSetMuteMic(const sessionState::ptr_t& sessionp,
  2171. bool muted)
  2172. {
  2173. pstate_ptr_t participantp = sessionp->findParticipantByID(gAgentID);
  2174. if (participantp)
  2175. {
  2176. participantp->mLevel = 0.f;
  2177. }
  2178. sessionp->setMuteMic(muted);
  2179. }
  2180. void LLVoiceWebRTC::setVoiceVolume(F32 volume)
  2181. {
  2182. if (mSpeakerVolume != volume)
  2183. {
  2184. mSpeakerVolume = volume;
  2185. sessionState::forEach(boost::bind(predSetSpeakerVolume, _1, volume));
  2186. }
  2187. }
  2188. //static
  2189. void LLVoiceWebRTC::predSetSpeakerVolume(const sessionState::ptr_t& sessionp,
  2190. F32 volume)
  2191. {
  2192. sessionp->setSpeakerVolume(volume);
  2193. }
  2194. void LLVoiceWebRTC::setMicGain(F32 gain)
  2195. {
  2196. if (mMicGain != gain)
  2197. {
  2198. mMicGain = gain;
  2199. if (mWebRTCPlugin)
  2200. {
  2201. mWebRTCPlugin->setMicGain(gain);
  2202. }
  2203. }
  2204. }
  2205. void LLVoiceWebRTC::setEarLocation(S32 loc)
  2206. {
  2207. if (mEarLocation != loc && loc >= 0 && loc <= (S32)earLocMixed)
  2208. {
  2209. LL_DEBUGS("Voice") << "Setting ear location to " << loc << LL_ENDL;
  2210. mEarLocation = loc;
  2211. mSpatialCoordsDirty = true;
  2212. }
  2213. }
  2214. void LLVoiceWebRTC::updatePosition()
  2215. {
  2216. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2217. if (mTerminated || !mInitDone)
  2218. {
  2219. return;
  2220. }
  2221. LLViewerRegion* regionp = gAgent.getRegion();
  2222. if (!regionp || !isAgentAvatarValid())
  2223. {
  2224. return;
  2225. }
  2226. LLVector3d agent_pos = gAgentAvatarp->getPositionGlobal();
  2227. agent_pos.mdV[VZ] += 1.0; // Bump it up to head height
  2228. LLQuaternion agent_rot = gAgentAvatarp->getRootJoint()->getWorldRotation();
  2229. LLVector3d ear_pos;
  2230. LLQuaternion ear_rot;
  2231. switch (mEarLocation)
  2232. {
  2233. case earLocCamera:
  2234. ear_pos =
  2235. regionp->getPosGlobalFromRegion(gViewerCamera.getOrigin());
  2236. ear_rot = gViewerCamera.getQuaternion();
  2237. break;
  2238. case earLocAvatar:
  2239. ear_pos = mAvatarPosition;
  2240. ear_rot = mAvatarRot;
  2241. break;
  2242. case earLocMixed:
  2243. ear_pos = mAvatarPosition;
  2244. ear_rot = gViewerCamera.getQuaternion();
  2245. break;
  2246. default:
  2247. llerrs << "Invalid ear location: " << mEarLocation << llendl;
  2248. }
  2249. setListenerPosition(ear_pos, LLVector3::zero, ear_rot);
  2250. setAvatarPosition(agent_pos, LLVector3::zero, agent_rot);
  2251. enforceTether();
  2252. updateNeighboringRegions();
  2253. // Update own region Id to be the region Id avatar is currently in.
  2254. pstate_ptr_t participantp = findParticipantByID("Estate", gAgentID);
  2255. if (participantp)
  2256. {
  2257. LLViewerRegion* regionp = gAgent.getRegion();
  2258. if (regionp)
  2259. {
  2260. participantp->mRegion = regionp->getRegionID();
  2261. }
  2262. }
  2263. }
  2264. void LLVoiceWebRTC::setListenerPosition(const LLVector3d& position,
  2265. const LLVector3& velocity,
  2266. const LLQuaternion& rot)
  2267. {
  2268. mListenerRequestedPosition = position;
  2269. if (mListenerVelocity != velocity)
  2270. {
  2271. mListenerVelocity = velocity;
  2272. mSpatialCoordsDirty = true;
  2273. }
  2274. if (mListenerRot != rot)
  2275. {
  2276. mListenerRot = rot;
  2277. mSpatialCoordsDirty = true;
  2278. }
  2279. }
  2280. void LLVoiceWebRTC::setAvatarPosition(const LLVector3d& position,
  2281. const LLVector3& velocity,
  2282. const LLQuaternion& rot)
  2283. {
  2284. if (dist_vec(mAvatarPosition, position) > 0.1)
  2285. {
  2286. mAvatarPosition = position;
  2287. mSpatialCoordsDirty = true;
  2288. }
  2289. if (mAvatarVelocity != velocity)
  2290. {
  2291. mAvatarVelocity = velocity;
  2292. mSpatialCoordsDirty = true;
  2293. }
  2294. // If the two rotations are not exactly equal test their dot product to get
  2295. // the cosinus of the angle between them. If too small, do not update.
  2296. if (mAvatarRot != rot)
  2297. {
  2298. static const F32 minuscule_angle_cos = cosf(2.f * DEG_TO_RAD);
  2299. if (fabs(dot(mAvatarRot, rot)) < minuscule_angle_cos)
  2300. {
  2301. mAvatarRot = rot;
  2302. mSpatialCoordsDirty = true;
  2303. }
  2304. }
  2305. }
  2306. void LLVoiceWebRTC::enforceTether()
  2307. {
  2308. LLVector3d tethered = mListenerRequestedPosition;
  2309. // Constrain 'tethered' to within 50m of mAvatarPosition.
  2310. constexpr F32 max_dist = 50.f;
  2311. LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition;
  2312. F32 camera_distance = (F32)camera_offset.length();
  2313. if (camera_distance > max_dist)
  2314. {
  2315. tethered = mAvatarPosition +
  2316. (max_dist / camera_distance) * camera_offset;
  2317. }
  2318. if (dist_vec_squared(mListenerPosition, tethered) > 0.01)
  2319. {
  2320. mListenerPosition = tethered;
  2321. mSpatialCoordsDirty = true;
  2322. }
  2323. }
  2324. void LLVoiceWebRTC::sendPositionUpdate(bool force)
  2325. {
  2326. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2327. if (force || mSpatialCoordsDirty)
  2328. {
  2329. lljson spatial = lljson::object();
  2330. spatial["sp"] = lljson::object();
  2331. spatial["sp"]["x"] = (S32)(mAvatarPosition[0] * 100.0);
  2332. spatial["sp"]["y"] = (S32)(mAvatarPosition[1] * 100.0);
  2333. spatial["sp"]["z"] = (S32)(mAvatarPosition[2] * 100.0);
  2334. spatial["sh"] = lljson::object();
  2335. spatial["sh"]["x"] = (S32)(mAvatarRot[0] * 100.0);
  2336. spatial["sh"]["y"] = (S32)(mAvatarRot[1] * 100.0);
  2337. spatial["sh"]["z"] = (S32)(mAvatarRot[2] * 100.0);
  2338. spatial["sh"]["w"] = (S32)(mAvatarRot[3] * 100.0);
  2339. spatial["lp"] = lljson::object();
  2340. spatial["lp"]["x"] = (S32)(mListenerPosition[0] * 100.0);
  2341. spatial["lp"]["y"] = (S32)(mListenerPosition[1] * 100.0);
  2342. spatial["lp"]["z"] = (S32)(mListenerPosition[2] * 100.0);
  2343. spatial["lh"] = lljson::object();
  2344. spatial["lh"]["x"] = (S32)(mListenerRot[0] * 100.0);
  2345. spatial["lh"]["y"] = (S32)(mListenerRot[1] * 100.0);
  2346. spatial["lh"]["z"] = (S32)(mListenerRot[2] * 100.0);
  2347. spatial["lh"]["w"] = (S32)(mListenerRot[3] * 100.0);
  2348. std::string spatial_data = to_string(spatial);
  2349. sessionState::forEach(boost::bind(predSendData, _1, spatial_data));
  2350. mSpatialCoordsDirty = false;
  2351. }
  2352. }
  2353. void LLVoiceWebRTC::updateOwnVolume()
  2354. {
  2355. F32 audio_level = 0.f;
  2356. if (!mMuteMic && !mIsInTuningMode)
  2357. {
  2358. audio_level = getAudioLevel();
  2359. }
  2360. sessionState::forEach(boost::bind(predUpdateOwnVolume, _1, audio_level));
  2361. }
  2362. void LLVoiceWebRTC::setVoiceEnabled(bool enabled)
  2363. {
  2364. if (mVoiceEnabled == enabled)
  2365. {
  2366. return;
  2367. }
  2368. mVoiceEnabled = enabled;
  2369. sLastTunedChannelId.clear();
  2370. LLVoiceClientStatusObserver::EStatusType status;
  2371. if (enabled)
  2372. {
  2373. LLVoiceChannel::getCurrentVoiceChannel()->activate();
  2374. status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
  2375. mSpatialCoordsDirty = true;
  2376. updatePosition();
  2377. if (!mIsCoroutineActive)
  2378. {
  2379. gCoros.launch("voiceConnectionCoro",
  2380. boost::bind(&LLVoiceWebRTC::voiceConnectionCoro,
  2381. this));
  2382. }
  2383. }
  2384. else
  2385. {
  2386. // Turning voice off looses your current channel: this makes sure the
  2387. // UI is not out of sync when you re-enable it.
  2388. LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
  2389. status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
  2390. cleanUp();
  2391. }
  2392. notifyStatusObservers(status);
  2393. }
  2394. bool LLVoiceWebRTC::getIsSpeaking(const LLUUID& id)
  2395. {
  2396. if (mProcessChannels && mSession)
  2397. {
  2398. // Since WebRTC accounts only for the mic input level for the agent
  2399. // (and not the level of the transmitted voice), we must ignore the
  2400. // speaking flag when the mic toggle is off (no voice transmitted). HB
  2401. if (id == gAgentID && !gVoiceClient.isAgentMicOpen())
  2402. {
  2403. return false;
  2404. }
  2405. pstate_ptr_t participantp = mSession->findParticipantByID(id);
  2406. if (participantp)
  2407. {
  2408. return participantp->mIsSpeaking;
  2409. }
  2410. }
  2411. return false;
  2412. }
  2413. bool LLVoiceWebRTC::getIsModeratorMuted(const LLUUID& id)
  2414. {
  2415. if (mProcessChannels && mSession)
  2416. {
  2417. pstate_ptr_t participantp = mSession->findParticipantByID(id);
  2418. if (participantp)
  2419. {
  2420. return participantp->mIsModeratorMuted;
  2421. }
  2422. }
  2423. return false;
  2424. }
  2425. F32 LLVoiceWebRTC::getCurrentPower(const LLUUID& id)
  2426. {
  2427. if (!mProcessChannels || !mSession)
  2428. {
  2429. return -1.f; // Id not in session
  2430. }
  2431. pstate_ptr_t participantp = mSession->findParticipantByID(id);
  2432. if (!participantp)
  2433. {
  2434. return -1.f; // Id not in session
  2435. }
  2436. // Since WebRTC reports the mic input level for the agent (and not the
  2437. // level of the transmitted voice), we must set the power to zero when the
  2438. // mic toggle is off (no voice transmitted). HB
  2439. if (id == gAgentID && !gVoiceClient.isAgentMicOpen())
  2440. {
  2441. return 0.f;
  2442. }
  2443. return participantp->mIsSpeaking ? participantp->mLevel : 0.f;
  2444. }
  2445. // External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
  2446. // internal = 400 * external^2
  2447. F32 LLVoiceWebRTC::getUserVolume(const LLUUID& id)
  2448. {
  2449. if (!mSession)
  2450. {
  2451. return -1.f;
  2452. }
  2453. pstate_ptr_t participantp = mSession->findParticipantByID(id);
  2454. if (!participantp)
  2455. {
  2456. return -1.f; // Id not in session
  2457. }
  2458. // Since WebRTC reports the mic input level for the agent (and not the
  2459. // level of the transmitted voice), we must set the volume to zero when the
  2460. // mic toggle is off (no voice transmitted). HB
  2461. if (id == gAgentID && !gVoiceClient.isAgentMicOpen())
  2462. {
  2463. return 0.f;
  2464. }
  2465. return participantp->mVolume;
  2466. }
  2467. void LLVoiceWebRTC::setUserVolume(const LLUUID& id, F32 volume)
  2468. {
  2469. if (mSession)
  2470. {
  2471. pstate_ptr_t participantp = mSession->findParticipantByID(id);
  2472. if (participantp)
  2473. {
  2474. participantp->mVolume = volume;
  2475. }
  2476. }
  2477. sessionState::forEach(boost::bind(predSetUserVolume, _1, id, volume));
  2478. }
  2479. //static
  2480. void LLVoiceWebRTC::predSetUserVolume(const sessionState::ptr_t& sessionp,
  2481. const LLUUID& id, F32 volume)
  2482. {
  2483. sessionp->setUserVolume(id, volume);
  2484. }
  2485. //static
  2486. void LLVoiceWebRTC::predUpdateOwnVolume(const sessionState::ptr_t& sessionp,
  2487. F32 audio_level)
  2488. {
  2489. pstate_ptr_t participantp = sessionp->findParticipantByID(gAgentID);
  2490. if (participantp)
  2491. {
  2492. participantp->mLevel = audio_level;
  2493. // *TODO: add VAD for our own voice.
  2494. constexpr F32 SPEAKING_AUDIO_LEVEL = 0.3f;
  2495. participantp->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL;
  2496. }
  2497. }
  2498. //static
  2499. void LLVoiceWebRTC::predSendData(const sessionState::ptr_t& sessionp,
  2500. const std::string& spatial_data)
  2501. {
  2502. if (sessionp->isSpatial() && !spatial_data.empty())
  2503. {
  2504. sessionp->sendData(spatial_data);
  2505. }
  2506. }
  2507. void LLVoiceWebRTC::lookupName(const LLUUID& id)
  2508. {
  2509. if (gCacheNamep)
  2510. {
  2511. gCacheNamep->get(id, false, onAvatarNameLookup);
  2512. }
  2513. }
  2514. //static
  2515. void LLVoiceWebRTC::onAvatarNameLookup(const LLUUID& id,
  2516. const std::string& fullname, bool)
  2517. {
  2518. if (!gVoiceWebRTC.mTerminated)
  2519. {
  2520. gVoiceWebRTC.avatarNameResolved(id, fullname);
  2521. }
  2522. }
  2523. void LLVoiceWebRTC::avatarNameResolved(const LLUUID& id,
  2524. const std::string& name)
  2525. {
  2526. sessionState::forEach(boost::bind(predAvatarName, _1, id, name));
  2527. }
  2528. //static
  2529. void LLVoiceWebRTC::predAvatarName(const sessionState::ptr_t& sessionp,
  2530. const LLUUID& id, const std::string& name)
  2531. {
  2532. auto participantp = sessionp->findParticipantByID(id);
  2533. if (participantp)
  2534. {
  2535. participantp->mLegacyName = name;
  2536. #if 0 // Not used in the Cool VL Viewer
  2537. gVoiceWebRTC.notifyParticipantObservers();
  2538. #endif
  2539. }
  2540. }
  2541. ///////////////////////////////////////////////////////////////////////////////
  2542. // LLVoiceWebRTC::sessionState sub-class
  2543. ///////////////////////////////////////////////////////////////////////////////
  2544. LLVoiceWebRTC::sessionState::map_t LLVoiceWebRTC::sessionState::sSessions;
  2545. LLVoiceWebRTC::sessionState::sessionState()
  2546. : mShuttingDown(false),
  2547. mHangupOnLastLeave(false),
  2548. mNotifyOnFirstJoin(false),
  2549. mSpeakerVolume(1.f),
  2550. mMuted(false)
  2551. {
  2552. }
  2553. LLVoiceWebRTC::sessionState::~sessionState()
  2554. {
  2555. removeAllParticipants(LLUUID::null); // Remove in all regions
  2556. }
  2557. //static
  2558. void LLVoiceWebRTC::sessionState::forEachPredicate(const std::pair<std::string,
  2559. wptr_t>& a,
  2560. func_t fn)
  2561. {
  2562. ptr_t a_lck(a.second.lock());
  2563. if (a_lck)
  2564. {
  2565. fn(a_lck);
  2566. }
  2567. else
  2568. {
  2569. llwarns << "Stale handle in session map." << llendl;
  2570. }
  2571. }
  2572. //static
  2573. void LLVoiceWebRTC::sessionState::forEach(func_t fn)
  2574. {
  2575. std::for_each(sSessions.begin(), sSessions.end(),
  2576. boost::bind(forEachPredicate, _1, fn));
  2577. }
  2578. bool LLVoiceWebRTC::sessionState::isEmpty()
  2579. {
  2580. return mConnections.empty();
  2581. }
  2582. //static
  2583. void LLVoiceWebRTC::sessionState::addSession(const std::string& channel_id,
  2584. ptr_t& sessionp)
  2585. {
  2586. sSessions[channel_id] = sessionp;
  2587. }
  2588. //static
  2589. LLVoiceWebRTC::sessionState::ptr_t
  2590. LLVoiceWebRTC::sessionState::matchSessionByChannelID(const std::string& chan_id)
  2591. {
  2592. ptr_t result;
  2593. map_t::iterator it = sSessions.find(chan_id);
  2594. if (it != sSessions.end())
  2595. {
  2596. result = it->second;
  2597. }
  2598. return result;
  2599. }
  2600. LLVoiceWebRTC::pstate_ptr_t LLVoiceWebRTC::sessionState::findParticipantByID(const LLUUID& id)
  2601. {
  2602. pstate_ptr_t result;
  2603. auto iter = mParticipantsByUUID.find(id);
  2604. if (iter != mParticipantsByUUID.end())
  2605. {
  2606. result = iter->second;
  2607. }
  2608. return result;
  2609. }
  2610. LLVoiceWebRTC::pstate_ptr_t LLVoiceWebRTC::sessionState::addParticipant(const LLUUID& id,
  2611. const LLUUID& region_id)
  2612. {
  2613. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2614. if (gVoiceWebRTC.isTerminated())
  2615. {
  2616. llwarns << "Trying to add a parcipant after voice shutdown." << llendl;
  2617. return NULL;
  2618. }
  2619. pstate_ptr_t participantp;
  2620. particip_map_t::iterator it = mParticipantsByUUID.find(id);
  2621. if (it != mParticipantsByUUID.end())
  2622. {
  2623. participantp = it->second;
  2624. participantp->mRegion = region_id;
  2625. }
  2626. else
  2627. {
  2628. participantp.reset(new participantState(id, region_id));
  2629. mParticipantsByUUID.emplace(id, participantp);
  2630. if (!gVoiceWebRTC.isTerminated())
  2631. {
  2632. gVoiceWebRTC.lookupName(id);
  2633. }
  2634. }
  2635. LL_DEBUGS("Voice") << "Participant: " << participantp->mURI << LL_ENDL;
  2636. #if 0 // Not used in the Cool VL Viewer
  2637. gVoiceWebRTC.notifyParticipantObservers();
  2638. #endif
  2639. return participantp;
  2640. }
  2641. void LLVoiceWebRTC::sessionState::removeParticipant(pstate_ptr_t participantp)
  2642. {
  2643. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2644. if (gVoiceWebRTC.isTerminated() || !participantp)
  2645. {
  2646. return;
  2647. }
  2648. LLUUID part_id = participantp->mAvatarID;
  2649. auto iter = mParticipantsByUUID.find(part_id);
  2650. if (iter == mParticipantsByUUID.end())
  2651. {
  2652. llwarns << "Internal error: participant "
  2653. << part_id << " not in UUID map" << llendl;
  2654. }
  2655. else
  2656. {
  2657. LL_DEBUGS("Voice") << "Participant \"" << participantp->mURI << "\" ("
  2658. << part_id << ") removed." << LL_ENDL;
  2659. mParticipantsByUUID.erase(iter);
  2660. #if 0 // Not used in the Cool VL Viewer
  2661. gVoiceWebRTC.notifyParticipantObservers();
  2662. #endif
  2663. }
  2664. if (mHangupOnLastLeave && part_id != gAgentID &&
  2665. mParticipantsByUUID.size() <= 1)
  2666. {
  2667. gVoiceWebRTC.notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
  2668. }
  2669. }
  2670. void LLVoiceWebRTC::sessionState::removeAllParticipants(const LLUUID& region_id)
  2671. {
  2672. if (gVoiceWebRTC.isTerminated())
  2673. {
  2674. return;
  2675. }
  2676. bool all_regions = region_id.isNull();
  2677. std::vector<pstate_ptr_t> to_remove;
  2678. for (particip_map_t::iterator it = mParticipantsByUUID.begin(),
  2679. end = mParticipantsByUUID.end();
  2680. it != end; ++it)
  2681. {
  2682. if (all_regions || it->second->mRegion == region_id)
  2683. {
  2684. to_remove.emplace_back(it->second);
  2685. }
  2686. }
  2687. for (size_t i = 0, count = to_remove.size(); i < count; ++i)
  2688. {
  2689. removeParticipant(to_remove[i]);
  2690. }
  2691. }
  2692. void LLVoiceWebRTC::sessionState::sendData(const std::string& data)
  2693. {
  2694. if (!gVoiceWebRTC.isTerminated())
  2695. {
  2696. for (auto& connectionp : mConnections)
  2697. {
  2698. connectionp->sendData(data);
  2699. }
  2700. }
  2701. }
  2702. void LLVoiceWebRTC::sessionState::setMuteMic(bool muted)
  2703. {
  2704. if (!gVoiceWebRTC.isTerminated())
  2705. {
  2706. mMuted = muted;
  2707. for (auto& connectionp : mConnections)
  2708. {
  2709. connectionp->setMuteMic(muted);
  2710. }
  2711. }
  2712. }
  2713. void LLVoiceWebRTC::sessionState::setSpeakerVolume(F32 volume)
  2714. {
  2715. if (!gVoiceWebRTC.isTerminated())
  2716. {
  2717. mSpeakerVolume = volume;
  2718. for (auto& connectionp : mConnections)
  2719. {
  2720. connectionp->setSpeakerVolume(volume);
  2721. }
  2722. }
  2723. }
  2724. void LLVoiceWebRTC::sessionState::setUserVolume(const LLUUID& id, F32 volume)
  2725. {
  2726. if (!gVoiceWebRTC.isTerminated() && mParticipantsByUUID.count(id))
  2727. {
  2728. for (auto& connectionp : mConnections)
  2729. {
  2730. connectionp->setUserVolume(id, volume);
  2731. }
  2732. }
  2733. }
  2734. void LLVoiceWebRTC::sessionState::setUserMute(const LLUUID& id, bool muted)
  2735. {
  2736. if (!gVoiceWebRTC.isTerminated() && mParticipantsByUUID.count(id))
  2737. {
  2738. for (auto& connectionp : mConnections)
  2739. {
  2740. connectionp->setUserMute(id, muted);
  2741. }
  2742. }
  2743. }
  2744. void LLVoiceWebRTC::sessionState:: muteListChanged()
  2745. {
  2746. for (particip_map_t::iterator it = mParticipantsByUUID.begin(),
  2747. end = mParticipantsByUUID.end();
  2748. it != end; ++it)
  2749. {
  2750. bool muted = LLMuteList::isMuted(it->first, LLMute::flagVoiceChat);
  2751. if (it->second->mIsMuted != muted)
  2752. {
  2753. setUserMute(it->first, muted);
  2754. }
  2755. }
  2756. }
  2757. //static
  2758. void LLVoiceWebRTC::sessionState::processSessionStates()
  2759. {
  2760. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2761. auto it = sSessions.begin();
  2762. while (it != sSessions.end())
  2763. {
  2764. if (!it->second->processConnectionStates() &&
  2765. it->second->mShuttingDown)
  2766. {
  2767. // If the connections associated with a session are gone, and this
  2768. // session is shutting down, remove it.
  2769. it = sSessions.erase(it);
  2770. }
  2771. else
  2772. {
  2773. ++it;
  2774. }
  2775. }
  2776. }
  2777. //virtual
  2778. bool LLVoiceWebRTC::sessionState::processConnectionStates()
  2779. {
  2780. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2781. auto it = mConnections.begin();
  2782. while (it != mConnections.end())
  2783. {
  2784. if (!it->get()->connectionStateMachine())
  2785. {
  2786. // If the state machine returns false, the connection is shut down
  2787. // so delete it.
  2788. it = mConnections.erase(it);
  2789. }
  2790. else
  2791. {
  2792. ++it;
  2793. }
  2794. }
  2795. return !mConnections.empty();
  2796. }
  2797. void LLVoiceWebRTC::sessionState::reconnectAllSessions()
  2798. {
  2799. for (auto&& connectionp : mConnections)
  2800. {
  2801. connectionp->pluginCreateSession();
  2802. }
  2803. }
  2804. void LLVoiceWebRTC::sessionState::shutdownAllConnections()
  2805. {
  2806. mShuttingDown = true;
  2807. for (auto&& connectionp : mConnections)
  2808. {
  2809. connectionp->shutDown();
  2810. }
  2811. }
  2812. //static
  2813. void LLVoiceWebRTC::sessionState::reapEmptySessions()
  2814. {
  2815. LL_TRACY_TIMER(TRC_WEBRTC_VOICE);
  2816. auto it = sSessions.begin();
  2817. while (it != sSessions.end())
  2818. {
  2819. if (it->second->isEmpty())
  2820. {
  2821. it = sSessions.erase(it);
  2822. }
  2823. else
  2824. {
  2825. ++it;
  2826. }
  2827. }
  2828. }
  2829. ///////////////////////////////////////////////////////////////////////////////
  2830. // LLVoiceWebRTC::estateSessionState sub-class
  2831. ///////////////////////////////////////////////////////////////////////////////
  2832. LLVoiceWebRTC::estateSessionState::estateSessionState()
  2833. {
  2834. mNotifyOnFirstJoin = mHangupOnLastLeave = false;
  2835. mChannelID = "Estate";
  2836. LLViewerRegion* regionp = gAgent.getRegion();
  2837. if (!regionp) // Disconnected
  2838. {
  2839. return;
  2840. }
  2841. const LLUUID& region_id = regionp->getRegionID();
  2842. mConnections.emplace_back(
  2843. new LLVoiceConnectionSpatial(region_id, INVALID_PARCEL_ID,
  2844. mChannelID));
  2845. }
  2846. //virtual
  2847. bool LLVoiceWebRTC::estateSessionState::processConnectionStates()
  2848. {
  2849. if (!mShuttingDown)
  2850. {
  2851. // Estate voice requires connection to neighboring regions.
  2852. uuid_list_t neighbors = gVoiceWebRTC.getNeighboringRegions();
  2853. for (auto& connectionp : mConnections)
  2854. {
  2855. // Do check this is a spatial connection first. HB
  2856. if (!connectionp->isSpatial())
  2857. {
  2858. continue;
  2859. }
  2860. const LLUUID& region_id = connectionp.get()->getRegionID();
  2861. uuid_list_t::iterator it = neighbors.find(region_id);
  2862. if (it == neighbors.end())
  2863. {
  2864. // Shut down connections to neighbors that are too far away.
  2865. connectionp.get()->shutDown();
  2866. }
  2867. else
  2868. {
  2869. neighbors.erase(it);
  2870. }
  2871. }
  2872. // Add new connections for active neighbors
  2873. typedef std::shared_ptr<LLVoiceConnection> con_ptr_t;
  2874. for (auto& region_id : neighbors)
  2875. {
  2876. con_ptr_t connectp(new LLVoiceConnectionSpatial(region_id,
  2877. INVALID_PARCEL_ID,
  2878. mChannelID));
  2879. mConnections.push_back(connectp);
  2880. connectp->setMuteMic(mMuted);
  2881. connectp->setSpeakerVolume(mSpeakerVolume);
  2882. }
  2883. }
  2884. return sessionState::processConnectionStates();
  2885. }
  2886. ///////////////////////////////////////////////////////////////////////////////
  2887. // LLVoiceWebRTC::parcelSessionState sub-class
  2888. ///////////////////////////////////////////////////////////////////////////////
  2889. LLVoiceWebRTC::parcelSessionState::parcelSessionState(const std::string& cid,
  2890. S32 parcel_id)
  2891. {
  2892. mNotifyOnFirstJoin = mHangupOnLastLeave = false;
  2893. mChannelID = cid;
  2894. LLViewerRegion* regionp = gAgent.getRegion();
  2895. if (!regionp) // Disconnected
  2896. {
  2897. return;
  2898. }
  2899. const LLUUID& region_id = regionp->getRegionID();
  2900. mConnections.emplace_back(new LLVoiceConnectionSpatial(region_id,
  2901. parcel_id, cid));
  2902. }
  2903. ///////////////////////////////////////////////////////////////////////////////
  2904. // LLVoiceWebRTC::adhocSessionState sub-class
  2905. ///////////////////////////////////////////////////////////////////////////////
  2906. LLVoiceWebRTC::adhocSessionState::adhocSessionState(const std::string& cid,
  2907. const std::string& cred,
  2908. bool notify_on_first_join,
  2909. bool hangup_on_last_leave)
  2910. {
  2911. mNotifyOnFirstJoin = notify_on_first_join;
  2912. mHangupOnLastLeave = hangup_on_last_leave;
  2913. mChannelID = cid;
  2914. LLViewerRegion* regionp = gAgent.getRegion();
  2915. if (!regionp) // Disconnected
  2916. {
  2917. return;
  2918. }
  2919. const LLUUID& region_id = regionp->getRegionID();
  2920. mConnections.emplace_back(new LLVoiceConnectionAdHoc(region_id, cid,
  2921. cred));
  2922. }