llvoicevivox.cpp 140 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388
  1. /**
  2. * @file llvoicevivox.cpp
  3. * @brief Implementation of LLVoiceVivox class which is the interface to the
  4. * SLVoice client process.
  5. *
  6. * $LicenseInfo:firstyear=2007&license=viewergpl$
  7. *
  8. * Copyright (c) 2007-2009, Linden Research, Inc.
  9. * Copyright (c) 2009-2024, Henri Beauchamp.
  10. *
  11. * Second Life Viewer Source Code
  12. * The source code in this file ("Source Code") is provided by Linden Lab
  13. * to you under the terms of the GNU General Public License, version 2.0
  14. * ("GPL"), unless you have obtained a separate licensing agreement
  15. * ("Other License"), formally executed by you and Linden Lab. Terms of
  16. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18. *
  19. * There are special exceptions to the terms and conditions of the GPL as
  20. * it is applied to this Source Code. View the full text of the exception
  21. * in the file doc/FLOSS-exception.txt in this software distribution, or
  22. * online at
  23. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24. *
  25. * By copying, modifying or distributing this software, you acknowledge
  26. * that you have read and understood your obligations described above,
  27. * and agree to abide by those obligations.
  28. *
  29. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31. * COMPLETENESS OR PERFORMANCE.
  32. * $/LicenseInfo$
  33. */
  34. #include "llviewerprecompiledheaders.h"
  35. #if LL_LINUX
  36. # include <stdlib.h> // For getenv()
  37. #endif
  38. #include "expat.h"
  39. #include "llvoicevivox.h"
  40. #include "llapr.h"
  41. #include "llbase64.h"
  42. #include "llbufferstream.h"
  43. #include "llcachename.h"
  44. #include "llcallbacklist.h"
  45. #include "llcorehttputil.h"
  46. #include "lldir.h"
  47. #include "llparcel.h"
  48. #include "llpumpio.h"
  49. #include "llprocesslauncher.h"
  50. #include "llsdutil.h"
  51. #include "llagent.h"
  52. #include "llappviewer.h" // For gDisconnected and gSecondLife
  53. #include "llfloaterchat.h" // For LLFloaterChat::addChat()
  54. #include "llgridmanager.h"
  55. #include "llimmgr.h"
  56. #include "llmutelist.h"
  57. #include "llstartup.h"
  58. #include "llviewercamera.h"
  59. #include "llviewercontrol.h"
  60. #include "llviewerparcelmgr.h"
  61. #include "llviewerregion.h"
  62. #include "llvoavatarself.h"
  63. #include "llvoicechannel.h"
  64. static const std::string VIVOXSTR = "vivox";
  65. // Helper functions.
  66. // Incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
  67. // Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75
  68. static S32 scale_mic_volume(F32 volume)
  69. {
  70. // Offset volume to the range [-1.0, 1.0], with 0 at the default.
  71. volume -= 1.f;
  72. S32 scaled_volume = 44; // offset scaled_volume by its default level
  73. if (volume < 0.f)
  74. {
  75. scaled_volume += (S32)(volume * 4.f); // (44 - 40)
  76. }
  77. else
  78. {
  79. scaled_volume += (S32)(volume * 31.f); // (75 - 44)
  80. }
  81. return scaled_volume;
  82. }
  83. // Incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
  84. // Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75
  85. static S32 scale_speaker_volume(F32 volume)
  86. {
  87. // Offset volume to the range [-0.5, 0.5], with 0 at the default.
  88. volume -= 0.5f;
  89. S32 scaled_volume = 62; // Offset scaled_volume by its default level
  90. if (volume < 0.f)
  91. {
  92. scaled_volume += (S32)(volume * 124.f); // (62 - 0) * 2
  93. }
  94. else
  95. {
  96. scaled_volume += (S32)(volume * 26.f); // (75 - 62) * 2
  97. }
  98. return scaled_volume;
  99. }
  100. static std::string random_handle()
  101. {
  102. LLUUID id;
  103. id.generate();
  104. return LLBase64::encode((const char*)id.mData, UUID_BYTES);
  105. }
  106. ///////////////////////////////////////////////////////////////////////////////
  107. // LLVivoxProtocolParser class
  108. ///////////////////////////////////////////////////////////////////////////////
  109. class LLVivoxProtocolParser : public LLIOPipe
  110. {
  111. protected:
  112. LOG_CLASS(LLVivoxProtocolParser);
  113. public:
  114. LLVivoxProtocolParser();
  115. ~LLVivoxProtocolParser() override;
  116. protected:
  117. // LLIOPipe virtual implementations: process the data in buffer
  118. EStatus process_impl(const LLChannelDescriptors& channels,
  119. buffer_ptr_t& buffer, bool& eos, LLSD& context,
  120. LLPumpIO* pump) override;
  121. void reset();
  122. void processResponse(std::string tag);
  123. static void XMLCALL ExpatStartTag(void* data, const char* el,
  124. const char** attr);
  125. static void XMLCALL ExpatEndTag(void* data, const char* el);
  126. static void XMLCALL ExpatCharHandler(void* data, const XML_Char* s,
  127. int len);
  128. void StartTag(const char* tag, const char** attr);
  129. void EndTag(const char* tag);
  130. void CharData(const char* buffer, int length);
  131. protected:
  132. std::string mInput;
  133. // Expat control members
  134. XML_ParserStruct* mParser;
  135. S32 mResponseDepth;
  136. S32 mIgnoreDepth;
  137. bool mIgnoringTags;
  138. bool mIsEvent;
  139. // Members for processing responses. The values are transient and only
  140. // valid within a call to processResponse().
  141. bool mSquelchDebugOutput;
  142. S32 mReturnCode;
  143. S32 mStatusCode;
  144. std::string mStatusString;
  145. std::string mRequestId;
  146. std::string mActionString;
  147. std::string mConnectorHandle;
  148. std::string mVersionId;
  149. std::string mAccountHandle;
  150. std::string mSessionHandle;
  151. std::string mSessionGrpHandle;
  152. std::string mAlias;
  153. // Members for processing events. The values are transient and only valid
  154. // within a call to processResponse().
  155. std::string mEventTypeString;
  156. std::string mUriString;
  157. std::string mDeviceString;
  158. std::string mNameString;
  159. std::string mDisplayNameString;
  160. std::string mMessageHeader;
  161. std::string mMessageBody;
  162. std::string mNotificationType;
  163. S32 mState;
  164. S32 mVolume;
  165. S32 mParticipantType;
  166. S32 mNumberOfAliases;
  167. F32 mEnergy;
  168. bool mIsModeratorMuted;
  169. bool mIsSpeaking;
  170. bool mIsChannel;
  171. bool mIncoming;
  172. bool mEnabled;
  173. // Members for processing text between tags
  174. bool mAccumulateText;
  175. std::string mTextBuffer;
  176. };
  177. LLVivoxProtocolParser::LLVivoxProtocolParser()
  178. {
  179. mParser = NULL;
  180. mParser = XML_ParserCreate(NULL);
  181. reset();
  182. }
  183. void LLVivoxProtocolParser::reset()
  184. {
  185. mResponseDepth = mIgnoreDepth = mParticipantType = mState = mVolume =
  186. mNumberOfAliases = mStatusCode = 0;
  187. mIgnoringTags = mAccumulateText = mIsChannel = mIsEvent = mIsSpeaking =
  188. mIsModeratorMuted = mSquelchDebugOutput = false;
  189. mEnergy = 0.f;
  190. mReturnCode = -1;
  191. mAlias.clear();
  192. mTextBuffer.clear();
  193. }
  194. //virtual
  195. LLVivoxProtocolParser::~LLVivoxProtocolParser()
  196. {
  197. if (mParser)
  198. {
  199. XML_ParserFree(mParser);
  200. }
  201. }
  202. // virtual
  203. LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(const LLChannelDescriptors& channels,
  204. buffer_ptr_t& buffer,
  205. bool& eos,
  206. LLSD& context,
  207. LLPumpIO* pump)
  208. {
  209. LLBufferStream istr(channels, buffer.get());
  210. std::ostringstream ostr;
  211. while (istr.good())
  212. {
  213. char buf[1024];
  214. istr.read(buf, sizeof(buf));
  215. mInput.append(buf, istr.gcount());
  216. }
  217. // Look for input delimiter(s) in the input buffer. If one is found, send
  218. // the message to the xml parser.
  219. size_t start = 0;
  220. size_t delim;
  221. while ((delim = mInput.find("\n\n\n", start)) != std::string::npos)
  222. {
  223. // Reset internal state of the LLVivoxProtocolParser (no effect on the
  224. // expat parser)
  225. reset();
  226. XML_ParserReset(mParser, NULL);
  227. XML_SetElementHandler(mParser, ExpatStartTag, ExpatEndTag);
  228. XML_SetCharacterDataHandler(mParser, ExpatCharHandler);
  229. XML_SetUserData(mParser, this);
  230. XML_Parse(mParser, mInput.data() + start, delim - start, false);
  231. // If this message is not set to be squelched, output the raw XML
  232. // received
  233. if (!mSquelchDebugOutput)
  234. {
  235. LL_DEBUGS("Voice") << "Parsing: "
  236. << mInput.substr(start, delim - start)
  237. << LL_ENDL;
  238. }
  239. start = delim + 3;
  240. }
  241. if (start)
  242. {
  243. mInput = mInput.substr(start);
  244. }
  245. LL_DEBUGS("VivoxProtocolParser") << "At end, mInput is: " << mInput
  246. << LL_ENDL;
  247. if (!gVoiceVivox.mConnected)
  248. {
  249. // If voice has been disabled, we just want to close the socket.
  250. // This does so.
  251. llinfos << "Returning STATUS_STOP" << llendl;
  252. return STATUS_STOP;
  253. }
  254. return STATUS_OK;
  255. }
  256. void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void* data, const char* el,
  257. const char** attr)
  258. {
  259. if (data)
  260. {
  261. LLVivoxProtocolParser* object = (LLVivoxProtocolParser*)data;
  262. object->StartTag(el, attr);
  263. }
  264. }
  265. void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void* data, const char* el)
  266. {
  267. if (data)
  268. {
  269. ((LLVivoxProtocolParser*)data)->EndTag(el);
  270. }
  271. }
  272. void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void* data,
  273. const XML_Char* s,
  274. int len)
  275. {
  276. if (data)
  277. {
  278. ((LLVivoxProtocolParser*)data)->CharData(s, len);
  279. }
  280. }
  281. void LLVivoxProtocolParser::StartTag(const char* tag, const char** attr)
  282. {
  283. // Reset the text accumulator. We should not have strings that are
  284. // interrupted by new tags
  285. mTextBuffer.clear();
  286. // Only accumulate text if we're not ignoring tags.
  287. mAccumulateText = !mIgnoringTags;
  288. if (mResponseDepth == 0)
  289. {
  290. mIsEvent = !stricmp("Event", tag);
  291. if (!stricmp("Response", tag) || mIsEvent)
  292. {
  293. // Grab the attributes
  294. while (*attr)
  295. {
  296. const char* key = *attr++;
  297. const char* value = *attr++;
  298. if (!stricmp("requestId", key))
  299. {
  300. mRequestId = value;
  301. }
  302. else if (!stricmp("action", key))
  303. {
  304. mActionString = value;
  305. }
  306. else if (!stricmp("type", key))
  307. {
  308. mEventTypeString = value;
  309. }
  310. }
  311. }
  312. LL_DEBUGS("VivoxProtocolParser") << "Tag: " << tag << " ("
  313. << mResponseDepth << ")" << LL_ENDL;
  314. }
  315. else if (mIgnoringTags)
  316. {
  317. LL_DEBUGS("VivoxProtocolParser") << "Ignoring tag " << tag
  318. << " (depth = " << mResponseDepth
  319. << ")" << LL_ENDL;
  320. }
  321. else
  322. {
  323. LL_DEBUGS("VivoxProtocolParser") << "Tag: " << tag << " ("
  324. << mResponseDepth << ")" << LL_ENDL;
  325. // Ignore the InputXml stuff so we do not get confused
  326. if (!stricmp("InputXml", tag))
  327. {
  328. mIgnoringTags = true;
  329. mIgnoreDepth = mResponseDepth;
  330. mAccumulateText = false;
  331. LL_DEBUGS("VivoxProtocolParser") << "Starting ignore, mIgnoreDepth is "
  332. << mIgnoreDepth << LL_ENDL;
  333. }
  334. else if (!stricmp("CaptureDevices", tag))
  335. {
  336. gVoiceVivox.clearCaptureDevices();
  337. }
  338. else if (!stricmp("RenderDevices", tag))
  339. {
  340. gVoiceVivox.clearRenderDevices();
  341. }
  342. else if (!stricmp("CaptureDevice", tag) ||
  343. !stricmp("RenderDevice", tag))
  344. {
  345. mDeviceString.clear();
  346. }
  347. }
  348. ++mResponseDepth;
  349. }
  350. void LLVivoxProtocolParser::EndTag(const char* tag)
  351. {
  352. const std::string& string = mTextBuffer;
  353. --mResponseDepth;
  354. if (mIgnoringTags)
  355. {
  356. if (mIgnoreDepth == mResponseDepth)
  357. {
  358. LL_DEBUGS("VivoxProtocolParser") << "End of ignore" << LL_ENDL;
  359. mIgnoringTags = false;
  360. }
  361. else
  362. {
  363. LL_DEBUGS("VivoxProtocolParser") << "Ignoring tag " << tag
  364. << " (depth = " << mResponseDepth
  365. << ")" << LL_ENDL;
  366. }
  367. }
  368. if (!mIgnoringTags)
  369. {
  370. LL_DEBUGS("VivoxProtocolParser") << "Processing tag: " << tag
  371. << " (depth = " << mResponseDepth
  372. << ")" << LL_ENDL;
  373. // Closing a tag. Finalize the text we have accumulated and reset
  374. if (!stricmp("ReturnCode", tag))
  375. {
  376. mReturnCode = strtol(string.c_str(), NULL, 10);
  377. }
  378. else if (!stricmp("SessionHandle", tag))
  379. {
  380. mSessionHandle = string;
  381. LL_DEBUGS("Voice") << "Received session handle: " << mSessionHandle
  382. << LL_ENDL;
  383. }
  384. else if (!stricmp("SessionGroupHandle", tag))
  385. {
  386. mSessionGrpHandle = string;
  387. LL_DEBUGS("Voice") << "Received session group handle: "
  388. << mSessionGrpHandle << LL_ENDL;
  389. }
  390. else if (!stricmp("StatusCode", tag))
  391. {
  392. mStatusCode = strtol(string.c_str(), NULL, 10);
  393. }
  394. else if (!stricmp("StatusString", tag))
  395. {
  396. mStatusString = string;
  397. }
  398. else if (!stricmp("ParticipantURI", tag))
  399. {
  400. mUriString = string;
  401. }
  402. else if (!stricmp("Volume", tag))
  403. {
  404. mVolume = strtol(string.c_str(), NULL, 10);
  405. }
  406. else if (!stricmp("Energy", tag))
  407. {
  408. mEnergy = (F32)strtod(string.c_str(), NULL);
  409. }
  410. else if (!stricmp("IsModeratorMuted", tag))
  411. {
  412. mIsModeratorMuted = !stricmp(string.c_str(), "true");
  413. }
  414. else if (!stricmp("IsSpeaking", tag))
  415. {
  416. mIsSpeaking = !stricmp(string.c_str(), "true");
  417. }
  418. else if (!stricmp("Alias", tag))
  419. {
  420. mAlias = string;
  421. }
  422. else if (!stricmp("NumberOfAliases", tag))
  423. {
  424. mNumberOfAliases = strtol(string.c_str(), NULL, 10);
  425. }
  426. else if (!stricmp("ConnectorHandle", tag))
  427. {
  428. mConnectorHandle = string;
  429. LL_DEBUGS("Voice") << "Received connector handle: "
  430. << mConnectorHandle << LL_ENDL;
  431. }
  432. else if (!stricmp("VersionID", tag))
  433. {
  434. mVersionId = string;
  435. }
  436. else if (!stricmp("AccountHandle", tag))
  437. {
  438. mAccountHandle = string;
  439. }
  440. else if (!stricmp("State", tag))
  441. {
  442. mState = strtol(string.c_str(), NULL, 10);
  443. }
  444. else if (!stricmp("URI", tag))
  445. {
  446. mUriString = string;
  447. }
  448. else if (!stricmp("IsChannel", tag))
  449. {
  450. mIsChannel = !stricmp(string.c_str(), "true");
  451. }
  452. else if (!stricmp("Incoming", tag))
  453. {
  454. mIncoming = !stricmp(string.c_str(), "true");
  455. }
  456. else if (!stricmp("Enabled", tag))
  457. {
  458. mEnabled = !stricmp(string.c_str(), "true");
  459. }
  460. else if (!stricmp("Name", tag))
  461. {
  462. mNameString = string;
  463. }
  464. else if (!stricmp("ChannelName", tag))
  465. {
  466. mNameString = string;
  467. }
  468. else if (!stricmp("DisplayName", tag))
  469. {
  470. mDisplayNameString = string;
  471. }
  472. else if (!stricmp("AccountName", tag))
  473. {
  474. mNameString = string;
  475. }
  476. else if (!stricmp("ParticipantType", tag))
  477. {
  478. mParticipantType = strtol(string.c_str(), NULL, 10);
  479. }
  480. else if (!stricmp("MicEnergy", tag))
  481. {
  482. mEnergy = (F32)strtod(string.c_str(), NULL);
  483. }
  484. else if (!stricmp("ChannelName", tag))
  485. {
  486. mNameString = string;
  487. }
  488. else if (!stricmp("ChannelURI", tag))
  489. {
  490. mUriString = string;
  491. }
  492. else if (!stricmp("BuddyURI", tag))
  493. {
  494. mUriString = string;
  495. llwarns << "Buddy feature no more supported." << llendl;
  496. }
  497. else if (!stricmp("Presence", tag))
  498. {
  499. mStatusString = string;
  500. }
  501. else if (!stricmp("Device", tag))
  502. {
  503. mDeviceString = string;
  504. }
  505. else if (!stricmp("CaptureDevice", tag))
  506. {
  507. gVoiceVivox.addCaptureDevice(mDisplayNameString, mDeviceString);
  508. }
  509. else if (!stricmp("RenderDevice", tag))
  510. {
  511. gVoiceVivox.addRenderDevice(mDisplayNameString, mDeviceString);
  512. }
  513. else if (!stricmp("MessageHeader", tag))
  514. {
  515. mMessageHeader = string;
  516. }
  517. else if (!stricmp("MessageBody", tag))
  518. {
  519. mMessageBody = string;
  520. }
  521. else if (!stricmp("NotificationType", tag))
  522. {
  523. mNotificationType = string;
  524. }
  525. else
  526. {
  527. LL_DEBUGS("VivoxProtocolParser") << "Unhandled tag; " << tag
  528. << LL_ENDL;
  529. }
  530. mTextBuffer.clear();
  531. mAccumulateText = false;
  532. if (mResponseDepth == 0)
  533. {
  534. // We finished all of the XML, process the data
  535. processResponse(tag);
  536. }
  537. }
  538. }
  539. // This method is called for anything that is not a tag, which can be text you
  540. // want that lies between tags, and a lot of stuff you do not want like file
  541. // formatting (tabs, spaces, CR/LF, etc).
  542. void LLVivoxProtocolParser::CharData(const char* buffer, int length)
  543. {
  544. // Only copy text if we are in accumulate mode...
  545. if (mAccumulateText)
  546. {
  547. mTextBuffer.append(buffer, length);
  548. }
  549. }
  550. void LLVivoxProtocolParser::processResponse(std::string tag)
  551. {
  552. LL_DEBUGS("VivoxProtocolParser") << "Response for tag: " << tag << LL_ENDL;
  553. // SLIM SDK: the SDK now returns a mStatusCode of "200" (OK) for success.
  554. // This is a change vs. previous SDKs.
  555. // According to Mike S., "The actual API convention is that responses with
  556. // return codes of 0 are successful, regardless of the status code
  557. // returned", so I believe this will give correct behavior.
  558. if (mReturnCode == 0)
  559. {
  560. mStatusCode = 0;
  561. }
  562. if (mIsEvent)
  563. {
  564. const char* event_type = mEventTypeString.c_str();
  565. if (!stricmp(event_type, "ParticipantUpdatedEvent"))
  566. {
  567. /*
  568. <Event type="ParticipantUpdatedEvent">
  569. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  570. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  571. <ParticipantUri>sip:[email protected]</ParticipantUri>
  572. <IsModeratorMuted>false</IsModeratorMuted>
  573. <IsSpeaking>true</IsSpeaking>
  574. <Volume>44</Volume>
  575. <Energy>0.0879437</Energy>
  576. </Event>
  577. */
  578. // These happen so often that logging them is pretty useless.
  579. mSquelchDebugOutput = true;
  580. gVoiceVivox.participantUpdatedEvent(mSessionHandle,
  581. mSessionGrpHandle,
  582. mUriString, mAlias,
  583. mIsModeratorMuted,
  584. mIsSpeaking, mVolume, mEnergy);
  585. }
  586. else if (!stricmp(event_type, "AccountLoginStateChangeEvent"))
  587. {
  588. gVoiceVivox.accountLoginStateChangeEvent(mAccountHandle,
  589. mStatusCode,
  590. mStatusString, mState);
  591. }
  592. else if (!stricmp(event_type, "SessionAddedEvent"))
  593. {
  594. /*
  595. <Event type="SessionAddedEvent">
  596. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  597. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  598. <Uri>sip:[email protected]</Uri>
  599. <IsChannel>true</IsChannel>
  600. <Incoming>false</Incoming>
  601. <ChannelName />
  602. </Event>
  603. */
  604. gVoiceVivox.sessionAddedEvent(mUriString, mAlias, mSessionHandle,
  605. mSessionGrpHandle, mIsChannel,
  606. mIncoming, mNameString);
  607. }
  608. else if (!stricmp(event_type, "SessionRemovedEvent"))
  609. {
  610. gVoiceVivox.sessionRemovedEvent(mSessionHandle,
  611. mSessionGrpHandle);
  612. }
  613. else if (!stricmp(event_type, "MediaStreamUpdatedEvent"))
  614. {
  615. /*
  616. <Event type="MediaStreamUpdatedEvent">
  617. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  618. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  619. <StatusCode>200</StatusCode>
  620. <StatusString>OK</StatusString>
  621. <State>2</State>
  622. <Incoming>false</Incoming>
  623. </Event>
  624. */
  625. gVoiceVivox.mediaStreamUpdatedEvent(mSessionHandle,
  626. mSessionGrpHandle,
  627. mStatusCode, mStatusString,
  628. mState, mIncoming);
  629. }
  630. else if (!stricmp(event_type, "ParticipantAddedEvent"))
  631. {
  632. /*
  633. <Event type="ParticipantAddedEvent">
  634. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
  635. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
  636. <ParticipantUri>sip:[email protected]</ParticipantUri>
  637. <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
  638. <DisplayName />
  639. <ParticipantType>0</ParticipantType>
  640. </Event>
  641. */
  642. gVoiceVivox.participantAddedEvent(mSessionHandle,
  643. mSessionGrpHandle,
  644. mUriString, mAlias,
  645. mNameString, mDisplayNameString,
  646. mParticipantType);
  647. }
  648. else if (!stricmp(event_type, "ParticipantRemovedEvent"))
  649. {
  650. /*
  651. <Event type="ParticipantRemovedEvent">
  652. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
  653. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
  654. <ParticipantUri>sip:[email protected]</ParticipantUri>
  655. <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
  656. </Event>
  657. */
  658. gVoiceVivox.participantRemovedEvent(mSessionHandle,
  659. mSessionGrpHandle,
  660. mUriString, mAlias,
  661. mNameString);
  662. }
  663. else if (!stricmp(event_type, "AuxAudioPropertiesEvent"))
  664. {
  665. // These happen so often that logging them is pretty useless.
  666. mSquelchDebugOutput = true;
  667. gVoiceVivox.auxAudioPropertiesEvent(mEnergy);
  668. }
  669. else if (!stricmp(event_type, "MessageEvent"))
  670. {
  671. gVoiceVivox.messageEvent(mSessionHandle, mUriString, mAlias,
  672. mMessageHeader, mMessageBody);
  673. }
  674. else if (!stricmp(event_type, "SessionNotificationEvent"))
  675. {
  676. gVoiceVivox.sessionNotificationEvent(mSessionHandle, mUriString,
  677. mNotificationType);
  678. }
  679. else if (!stricmp(event_type, "SessionUpdatedEvent"))
  680. {
  681. /*
  682. <Event type="SessionUpdatedEvent">
  683. <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
  684. <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
  685. <Uri>sip:[email protected]</Uri>
  686. <IsMuted>0</IsMuted>
  687. <Volume>50</Volume>
  688. <TransmitEnabled>1</TransmitEnabled>
  689. <IsFocused>0</IsFocused>
  690. <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
  691. <SessionFontID>0</SessionFontID>
  692. </Event>
  693. */
  694. // We do not need to process this, but we also should not warn on
  695. // it, since that confuses people.
  696. LL_DEBUGS("VivoxProtocolParser") << "Ignored event: "
  697. << mEventTypeString << LL_ENDL;
  698. }
  699. else if (!stricmp(event_type, "AudioDeviceHotSwapEvent"))
  700. {
  701. /*
  702. <Event type = "AudioDeviceHotSwapEvent">
  703. <EventType>RenderDeviceChanged</EventType>
  704. <RelevantDevice>
  705. <Device>Speakers(Turtle Beach P11 Headset)</Device>
  706. <DisplayName>Speakers(Turtle Beach P11 Headset)</DisplayName>
  707. <Type>SpecificDevice</Type>
  708. </RelevantDevice>
  709. </Event>
  710. */
  711. // An audio device was removed or added, fetch and update the local
  712. // list of audio devices.
  713. gVoiceVivox.getCaptureDevicesSendMessage();
  714. gVoiceVivox.getRenderDevicesSendMessage();
  715. }
  716. // Warn for all other events but those (deprecated/unused):
  717. else if (stricmp(event_type, "BuddyAndGroupListChangedEvent") &&
  718. stricmp(event_type, "SessionGroupUpdatedEvent") &&
  719. stricmp(event_type, "SessionGroupRemovedEvent") &&
  720. stricmp(event_type, "SessionGroupAddedEvent") &&
  721. // This one relates to voice morphing (not implemented):
  722. stricmp(event_type, "MediaCompletionEvent") &&
  723. stricmp(event_type, "VoiceServiceConnectionStateChangedEvent"))
  724. {
  725. llwarns << "Unknown event type " << mEventTypeString << llendl;
  726. }
  727. }
  728. else
  729. {
  730. const char* action = mActionString.c_str();
  731. if (!stricmp(action, "Session.Set3DPosition.1"))
  732. {
  733. // We do not need to process these, but they are so spammy we do
  734. // not want to log them.
  735. mSquelchDebugOutput = true;
  736. }
  737. else if (!stricmp(action, "Connector.Create.1"))
  738. {
  739. gVoiceVivox.connectorCreateResponse(mStatusCode, mStatusString,
  740. mConnectorHandle, mVersionId);
  741. }
  742. else if (!stricmp(action, "Account.Login.1"))
  743. {
  744. gVoiceVivox.loginResponse(mStatusCode, mStatusString,
  745. mAccountHandle, mNumberOfAliases);
  746. }
  747. else if (!stricmp(action, "Session.Create.1"))
  748. {
  749. gVoiceVivox.sessionCreateResponse(mRequestId, mStatusCode,
  750. mStatusString, mSessionHandle);
  751. }
  752. else if (!stricmp(action, "SessionGroup.AddSession.1"))
  753. {
  754. gVoiceVivox.sessionGroupAddSessionResponse(mRequestId,
  755. mStatusCode,
  756. mStatusString,
  757. mSessionHandle);
  758. }
  759. else if (!stricmp(action, "Session.Connect.1"))
  760. {
  761. gVoiceVivox.sessionConnectResponse(mRequestId, mStatusCode,
  762. mStatusString);
  763. }
  764. else if (!stricmp(action, "Aux.SetVadProperties.1"))
  765. {
  766. if (mStatusCode && mStatusCode != 200)
  767. {
  768. llwarns << "Aux.SetVadProperties.1 request failed with code "
  769. << mStatusCode << " and status string: "
  770. << mStatusString << llendl;
  771. }
  772. }
  773. else if (!stricmp(action, "Account.Logout.1"))
  774. {
  775. gVoiceVivox.logoutResponse(mStatusCode, mStatusString);
  776. }
  777. else if (!stricmp(action, "Connector.InitiateShutdown.1"))
  778. {
  779. gVoiceVivox.connectorShutdownResponse(mStatusCode, mStatusString);
  780. }
  781. else
  782. {
  783. LL_DEBUGS("VivoxProtocolParser") << "Unhandled action: " << action
  784. << LL_ENDL;
  785. }
  786. }
  787. }
  788. ///////////////////////////////////////////////////////////////////////////////
  789. // LLVivoxMuteListObserver class
  790. ///////////////////////////////////////////////////////////////////////////////
  791. class LLVivoxMuteListObserver : public LLMuteListObserver
  792. {
  793. LL_INLINE void onChange() override
  794. {
  795. gVoiceVivox.muteListChanged();
  796. }
  797. };
  798. static LLVivoxMuteListObserver sMutelistListener;
  799. static bool sMuteListListening = false;
  800. ///////////////////////////////////////////////////////////////////////////////
  801. // LLVoiceVivox class proper
  802. ///////////////////////////////////////////////////////////////////////////////
  803. LLVoiceVivox gVoiceVivox;
  804. LLVoiceVivox::LLVoiceVivox()
  805. : mPump(NULL),
  806. mProcess(NULL),
  807. mAudioSession(NULL),
  808. mNextAudioSession(NULL),
  809. mTerminated(false),
  810. mState(stateDisabled),
  811. mVoiceEnabled(false),
  812. mProcessChannels(false),
  813. mAccountLoggedIn(false),
  814. mConnectorEstablished(false),
  815. mSessionTerminateRequested(false),
  816. mRelogRequested(false),
  817. mConnected(false),
  818. #if LL_LINUX
  819. mDeprecatedClient(false),
  820. #endif
  821. mRetries(0),
  822. mTuningMode(false),
  823. mTuningEnergy(0.f),
  824. mTuningMicVolume(0),
  825. mTuningMicVolumeDirty(true),
  826. mTuningSpeakerVolume(0),
  827. mTuningSpeakerVolumeDirty(true),
  828. mTuningExitState(stateDisabled),
  829. mNumberOfAliases(0),
  830. mCommandCookie(0),
  831. mLoginRetryCount(0),
  832. mLogLevel(0),
  833. mCaptureDeviceDirty(false),
  834. mRenderDeviceDirty(false),
  835. mSpatialCoordsDirty(false),
  836. mEarLocation(0),
  837. mSpeakerVolume(0),
  838. mSpeakerVolumeDirty(true),
  839. mSpeakerMuteDirty(true),
  840. mMicVolume(0),
  841. mMicVolumeDirty(true),
  842. mPTT(true),
  843. mPTTDirty(false),
  844. mAccountHandle(random_handle()),
  845. mConnectorHandle(random_handle())
  846. {
  847. #if LL_DARWIN || LL_LINUX
  848. // *HACK: when the vivox daemon dies, the next write attempt on our socket
  849. // generates a SIGPIPE, which kills us. This should cause us to ignore
  850. // SIGPIPE and handle the error through proper channels.
  851. signal(SIGPIPE, SIG_IGN);
  852. // Since we are now launching the gateway with fork/exec instead of
  853. // system(), we need to deal with zombie processes.
  854. // Ignoring SIGCHLD should prevent zombies from being created.
  855. // Alternately, we could use wait(), but I would rather not do that.
  856. signal(SIGCHLD, SIG_IGN);
  857. #endif
  858. }
  859. LLVoiceVivox::~LLVoiceVivox()
  860. {
  861. killDaemon();
  862. }
  863. void LLVoiceVivox::init(LLPumpIO* pumpp)
  864. {
  865. if (mTerminated || mPump)
  866. {
  867. return;
  868. }
  869. llinfos << "Initializing Vivox voice client. Default account handle: "
  870. << mAccountHandle << " - Default connector handle: "
  871. << mConnectorHandle << llendl;
  872. mPump = pumpp;
  873. gIdleCallbacks.addFunction(LLVoiceVivox::idle, &gVoiceVivox);
  874. LLControlVariable* controlp = gSavedSettings.getControl("VivoxVadAuto");
  875. controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
  876. &gVoiceVivox));
  877. controlp = gSavedSettings.getControl("VivoxVadHangover");
  878. controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
  879. &gVoiceVivox));
  880. controlp = gSavedSettings.getControl("VivoxVadNoiseFloor");
  881. controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
  882. &gVoiceVivox));
  883. controlp = gSavedSettings.getControl("VivoxVadSensitivity");
  884. controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
  885. &gVoiceVivox));
  886. }
  887. //virtual
  888. const std::string& LLVoiceVivox::getName() const
  889. {
  890. return VIVOXSTR;
  891. }
  892. bool LLVoiceVivox::isVoiceWorking() const
  893. {
  894. return !mTerminated && mVoiceEnabled && mProcessChannels &&
  895. mState != stateDisabled;
  896. }
  897. void LLVoiceVivox::killDaemon()
  898. {
  899. if (mProcess)
  900. {
  901. delete mProcess;
  902. mProcess = NULL;
  903. }
  904. }
  905. void LLVoiceVivox::terminate()
  906. {
  907. if (mTerminated)
  908. {
  909. return;
  910. }
  911. mTerminated = true;
  912. if (mPump)
  913. {
  914. llinfos << "Terminating Vivox voice client..." << llendl;
  915. gIdleCallbacks.deleteFunction(LLVoiceVivox::idle, &gVoiceVivox);
  916. if (mConnected)
  917. {
  918. logout();
  919. connectorShutdown();
  920. // Need to do this now: bad things happen if the destructor does it
  921. // later.
  922. closeSocket();
  923. }
  924. mPump = NULL;
  925. }
  926. }
  927. bool LLVoiceVivox::writeString(const std::string& str)
  928. {
  929. bool result = false;
  930. if (mConnected && mSocket)
  931. {
  932. apr_status_t err;
  933. apr_size_t size = (apr_size_t)str.size();
  934. apr_size_t written = size;
  935. // Check return code: sockets will fail (broken, etc)
  936. err = apr_socket_send(mSocket->getSocket(), (const char*)str.data(),
  937. &written);
  938. if (err == 0)
  939. {
  940. // Success.
  941. result = true;
  942. }
  943. #if 0 // *FIXME: handle partial writes (written is number of bytes written)
  944. // Need to set socket to non-blocking before this will work.
  945. else if (APR_STATUS_IS_EAGAIN(err))
  946. {
  947. }
  948. #endif
  949. else
  950. {
  951. // Assume any socket error means something bad. For now, just close
  952. // the socket.
  953. char buf[MAX_STRING];
  954. llwarns << "APR error " << err << " ("
  955. << apr_strerror(err, buf, MAX_STRING)
  956. << ") sending data to vivox daemon." << llendl;
  957. daemonDied();
  958. }
  959. }
  960. return result;
  961. }
  962. void LLVoiceVivox::connectorCreate()
  963. {
  964. // Transition to stateConnectorStarted when the connector handle comes back.
  965. setState(stateConnectorStarting);
  966. std::string logpath = gDirUtil.getFullPath(LL_PATH_LOGS, "");
  967. std::ostringstream stream;
  968. stream << "<Request requestId=\"" << mCommandCookie++
  969. << "\" action=\"Connector.Create.1\">"
  970. << "<ClientName>V2 SDK</ClientName><AccountManagementServer>"
  971. << mVoiceAccountServerURI
  972. << "</AccountManagementServer><Mode>Normal</Mode>"
  973. << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
  974. << "<Logging><Folder>" << logpath
  975. << "</Folder><FileNamePrefix>Connector</FileNamePrefix>"
  976. << "<FileNameSuffix>.log</FileNameSuffix><LogLevel>"
  977. << mLogLevel << "</LogLevel></Logging><Application>" << gSecondLife
  978. << "</Application><MaxCalls>12</MaxCalls></Request>\n\n\n";
  979. writeString(stream.str());
  980. }
  981. void LLVoiceVivox::connectorShutdown()
  982. {
  983. setState(stateConnectorStopping);
  984. if (!mConnectorEstablished)
  985. {
  986. return;
  987. }
  988. std::ostringstream stream;
  989. stream << "<Request requestId=\"" << mCommandCookie++
  990. << "\" action=\"Connector.InitiateShutdown.1\"><ConnectorHandle>"
  991. << mConnectorHandle << "</ConnectorHandle></Request>\n\n\n";
  992. mConnectorEstablished = false;
  993. writeString(stream.str());
  994. }
  995. void LLVoiceVivox::userAuthorized(const std::string& first_name,
  996. const std::string& last_name,
  997. const LLUUID& agent_id)
  998. {
  999. mAccountFirstName = first_name;
  1000. mAccountLastName = last_name;
  1001. mAccountDisplayName = first_name + " " + last_name;
  1002. llinfos << "Name \"" << mAccountDisplayName << "\", Id " << agent_id
  1003. << llendl;
  1004. mAccountName = nameFromID(agent_id);
  1005. }
  1006. void LLVoiceVivox::requestVoiceAccountProvision(S32 retries)
  1007. {
  1008. if (!mVoiceEnabled || !LLStartUp::isLoggedIn())
  1009. {
  1010. return;
  1011. }
  1012. std::string url =
  1013. gAgent.getRegionCapability("ProvisionVoiceAccountRequest");
  1014. if (url.empty())
  1015. {
  1016. LL_DEBUGS("Voice") << "Region does not have ProvisionVoiceAccountRequest capability !"
  1017. << LL_ENDL;
  1018. return;
  1019. }
  1020. gCoros.launch("LLVivoxVoiceClient::voiceAccountProvisionCoro",
  1021. boost::bind(&LLVoiceVivox::voiceAccountProvisionCoro, url,
  1022. retries));
  1023. setState(stateConnectorStart);
  1024. }
  1025. //static
  1026. void LLVoiceVivox::voiceAccountProvisionCoro(const std::string& url,
  1027. S32 retries)
  1028. {
  1029. LLSD body;
  1030. body["voice_server_type"] = VIVOXSTR;
  1031. LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
  1032. options->setRetries(retries);
  1033. LLCoreHttpUtil::HttpCoroutineAdapter adapter("voiceAccountProvision");
  1034. LLSD result = adapter.postAndSuspend(url, body, options);
  1035. if (gVoiceVivox.mTerminated)
  1036. {
  1037. // Voice has since been shut down
  1038. return;
  1039. }
  1040. LLCore::HttpStatus status =
  1041. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  1042. if (!status)
  1043. {
  1044. llwarns << "Unable to provision voice account: " << status.toString()
  1045. << llendl;
  1046. gVoiceVivox.giveUp();
  1047. return;
  1048. }
  1049. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  1050. LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response: " << result
  1051. << LL_ENDL;
  1052. std::string sip_uri_hostname;
  1053. if (result.has("voice_sip_uri_hostname"))
  1054. {
  1055. sip_uri_hostname = result["voice_sip_uri_hostname"].asString();
  1056. }
  1057. // Old Vivox protocol key... Just in case (for OpenSim grids)...
  1058. else if (result.has("sip_uri_hostname"))
  1059. {
  1060. sip_uri_hostname = result["sip_uri_hostname"].asString();
  1061. }
  1062. // This key is actually misnamed; it is an entire URI, not just a hostname.
  1063. std::string account_server_uri;
  1064. if (result.has("voice_account_server_name"))
  1065. {
  1066. account_server_uri = result["voice_account_server_name"].asString();
  1067. }
  1068. gVoiceVivox.login(result["username"].asString(),
  1069. result["password"].asString(),
  1070. sip_uri_hostname, account_server_uri);
  1071. }
  1072. void LLVoiceVivox::login(const std::string& account_name,
  1073. const std::string& password,
  1074. const std::string& sip_uri_hostname,
  1075. const std::string& account_server_uri)
  1076. {
  1077. mVoiceSIPURIHostName = sip_uri_hostname;
  1078. mVoiceAccountServerURI = account_server_uri;
  1079. if (mAccountLoggedIn)
  1080. {
  1081. // Already logged in.
  1082. llwarns << "Called while already logged in." << llendl;
  1083. // Do not process another login.
  1084. return;
  1085. }
  1086. else if (account_name != mAccountName)
  1087. {
  1088. // *TODO: error ?
  1089. llwarns << "Wrong account name " << account_name << " instead of "
  1090. << mAccountName << llendl;
  1091. }
  1092. else
  1093. {
  1094. mAccountPassword = password;
  1095. }
  1096. std::string sip_override = gSavedSettings.getString("VivoxSIPURIHostName");
  1097. if (!sip_override.empty())
  1098. {
  1099. mVoiceSIPURIHostName = sip_override;
  1100. }
  1101. if (mVoiceSIPURIHostName.empty())
  1102. {
  1103. // We have an empty account server name so we fall back to hardcoded
  1104. // defaults
  1105. if (gIsInSecondLifeBetaGrid)
  1106. {
  1107. // Use the development account server
  1108. mVoiceSIPURIHostName = "bhd.vivox.com";
  1109. }
  1110. else
  1111. {
  1112. // Use the release account server
  1113. mVoiceSIPURIHostName = "bhr.vivox.com";
  1114. }
  1115. }
  1116. std::string server_override =
  1117. gSavedSettings.getString("VivoxVoiceAccountServerURI");
  1118. if (!server_override.empty())
  1119. {
  1120. mVoiceAccountServerURI = server_override;
  1121. llinfos << "Overriding account server based on VivoxVoiceAccountServerURI setting: "
  1122. << mVoiceAccountServerURI << llendl;
  1123. }
  1124. if (mVoiceAccountServerURI.empty())
  1125. {
  1126. // If the account server URI is not specified, construct it from the
  1127. // SIP URI hostname
  1128. mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName +
  1129. "/api2/";
  1130. llinfos << "Inferring account server based on SIP URI Host name: "
  1131. << mVoiceAccountServerURI << llendl;
  1132. }
  1133. }
  1134. //static
  1135. void LLVoiceVivox::idle(void* user_data)
  1136. {
  1137. LLVoiceVivox* self = (LLVoiceVivox*)user_data;
  1138. if (self == &gVoiceVivox && !self->mTerminated && self->mPump)
  1139. {
  1140. self->stateMachine();
  1141. }
  1142. }
  1143. std::string LLVoiceVivox::state2string(state new_state)
  1144. {
  1145. std::string result = "UNKNOWN";
  1146. // Prevent copy-paste errors when updating this list...
  1147. #define CASE(x) case x: result = #x; break
  1148. switch (new_state)
  1149. {
  1150. CASE(stateDisableCleanup);
  1151. CASE(stateDisabled);
  1152. CASE(stateStart);
  1153. CASE(stateDaemonLaunched);
  1154. CASE(stateConnecting);
  1155. CASE(stateConnected);
  1156. CASE(stateIdle);
  1157. CASE(stateMicTuningStart);
  1158. CASE(stateMicTuningRunning);
  1159. CASE(stateMicTuningStop);
  1160. CASE(stateConnectorStart);
  1161. CASE(stateConnectorStarting);
  1162. CASE(stateConnectorStarted);
  1163. CASE(stateLoginRetry);
  1164. CASE(stateLoginRetryWait);
  1165. CASE(stateNeedsLogin);
  1166. CASE(stateLoggingIn);
  1167. CASE(stateLoggedIn);
  1168. CASE(stateNoChannel);
  1169. CASE(stateJoiningSession);
  1170. CASE(stateSessionJoined);
  1171. CASE(stateRunning);
  1172. CASE(stateLeavingSession);
  1173. CASE(stateSessionTerminated);
  1174. CASE(stateLoggingOut);
  1175. CASE(stateLoggedOut);
  1176. CASE(stateConnectorStopping);
  1177. CASE(stateConnectorStopped);
  1178. CASE(stateConnectorFailed);
  1179. CASE(stateConnectorFailedWaiting);
  1180. CASE(stateLoginFailed);
  1181. CASE(stateLoginFailedWaiting);
  1182. CASE(stateJoinSessionFailed);
  1183. CASE(stateJoinSessionFailedWaiting);
  1184. CASE(stateJail);
  1185. }
  1186. #undef CASE
  1187. return result;
  1188. }
  1189. void LLVoiceVivox::setState(state new_state)
  1190. {
  1191. LL_DEBUGS("Voice") << "Entering state " << state2string(new_state)
  1192. << LL_ENDL;
  1193. mState = new_state;
  1194. }
  1195. void LLVoiceVivox::stateMachine()
  1196. {
  1197. // Do not retry connecting to the daemon more frequently than this:
  1198. constexpr F32 CONNECT_THROTTLE_SECONDS = 1.f;
  1199. // Do not send positional updates more frequently than this:
  1200. constexpr F32 UPDATE_THROTTLE_SECONDS = 0.1f;
  1201. // *HACK: for failed login on viewer launch; see below. HB
  1202. static bool first_run = true;
  1203. // SLVoice retries logic. HB
  1204. constexpr F32 LOGIN_RETRY_SECONDS = 10.f;
  1205. constexpr S32 MAX_LOGIN_RETRIES = 12;
  1206. if (gDisconnected)
  1207. {
  1208. // The viewer has been disconnected from the sim. Disable voice.
  1209. setVoiceEnabled(false);
  1210. return;
  1211. }
  1212. if (!LLStartUp::isLoggedIn())
  1213. {
  1214. // Not yet logged in: nothing to do.
  1215. return;
  1216. }
  1217. if (mVoiceEnabled && mProcessChannels)
  1218. {
  1219. updatePosition();
  1220. }
  1221. // NOTE: tuning mode is special: it needs to launch SLVoice even if voice
  1222. // is disabled.
  1223. else if (!mTuningMode)
  1224. {
  1225. if (mState != stateDisabled && mState != stateDisableCleanup)
  1226. {
  1227. // User turned off voice support. Send the cleanup messages, close
  1228. // the socket, and reset.
  1229. if (!mConnected)
  1230. {
  1231. // If voice was turned off after the daemon was launched but
  1232. // before we could connect to it, we may need to issue a kill.
  1233. llinfos << "Disabling voice before connection to daemon, terminating."
  1234. << llendl;
  1235. killDaemon();
  1236. }
  1237. logout();
  1238. connectorShutdown();
  1239. setState(stateDisableCleanup);
  1240. }
  1241. }
  1242. switch (mState)
  1243. {
  1244. case stateDisableCleanup:
  1245. // Clean up and reset everything.
  1246. closeSocket();
  1247. deleteAllSessions();
  1248. mConnectorEstablished = false;
  1249. mAccountLoggedIn = false;
  1250. mAccountPassword.clear();
  1251. mVoiceAccountServerURI.clear();
  1252. setState(stateDisabled);
  1253. break;
  1254. case stateDisabled:
  1255. if (mTuningMode ||
  1256. (mVoiceEnabled && mProcessChannels && !mAccountName.empty()))
  1257. {
  1258. setState(stateStart);
  1259. }
  1260. break;
  1261. case stateStart:
  1262. if (!LLStartUp::isLoggedIn())
  1263. {
  1264. break;
  1265. }
  1266. if (gSavedSettings.getBool("CmdLineDisableVoice"))
  1267. {
  1268. // Voice is locked out, we must not launch the vivox daemon.
  1269. setState(stateJail);
  1270. }
  1271. else if (!mProcess || !mProcess->isRunning())
  1272. {
  1273. killDaemon();
  1274. // Refresh the log level
  1275. mLogLevel =
  1276. llmin((U32)gSavedSettings.getU32("VivoxDebugLevel"), 10U);
  1277. // Launch the voice daemon
  1278. std::string exe_path = gDirUtil.getExecutableDir();
  1279. std::string full_path;
  1280. #if LL_DARWIN
  1281. full_path = exe_path + "/../Resources/SLVoice";
  1282. #elif LL_WINDOWS
  1283. full_path = exe_path + "\\SLVoice.exe";
  1284. #elif LL_LINUX
  1285. mDeprecatedClient = false;
  1286. // Linux SLVoice is alas totally deprecated, so we better use
  1287. // Wine to run the Windows binary if we want working voice in
  1288. // SL (and most probably as well in OpenSim grids using Vivox).
  1289. // We get the Windows binary path from the LL_WINE_SLVOICE
  1290. // environment variable...
  1291. // See install-wine-SLVoice.sh and viewer.conf in linux_tools/
  1292. char* envvar = getenv("LL_WINE_SLVOICE");
  1293. if (envvar)
  1294. {
  1295. full_path.assign(envvar);
  1296. if (!full_path.empty())
  1297. {
  1298. size_t i = full_path.rfind('/');
  1299. if (i == std::string::npos || i == 0)
  1300. {
  1301. llwarns << "Invalid LL_WINE_SLVOICE environment variable setting: '"
  1302. << full_path
  1303. << "' does not point to a program. Falling back to Linux SLVoice."
  1304. << llendl;
  1305. full_path.clear();
  1306. }
  1307. else
  1308. {
  1309. exe_path = full_path.substr(0, i - 1);
  1310. }
  1311. }
  1312. }
  1313. if (full_path.empty())
  1314. {
  1315. if (gIsInSecondLife)
  1316. {
  1317. llwarns << "Using the deprecated Linux SLVoice binary. Expect voice to be flaky..."
  1318. << llendl;
  1319. }
  1320. full_path = exe_path + "/SLVoice";
  1321. mDeprecatedClient = true;
  1322. }
  1323. #endif
  1324. // See if the vivox executable exists
  1325. if (LLFile::isfile(full_path))
  1326. {
  1327. std::string host =
  1328. gSavedSettings.getString("VivoxVoiceHost");
  1329. // Port base, clamped to non-priviledged ports and so that
  1330. // with the added cyclic offset, we are till below the
  1331. // highest possible port number.
  1332. U32 port =
  1333. llclamp((U32)gSavedSettings.getU32("VivoxVoicePort"),
  1334. 1024U, 65435U);
  1335. // Vivox executable exists. Build the command line and
  1336. // launch the daemon.
  1337. mProcess = new LLProcessLauncher();
  1338. mProcess->setExecutable(full_path);
  1339. mProcess->setWorkingDirectory(exe_path);
  1340. // Add an auto-incremented offset at each new connection
  1341. // so that the old connection port would not be reused
  1342. // (when reconnecting at a short interval of time, such as
  1343. // during retries) before it got time to close...
  1344. // The offset is also randomized on first connection, so
  1345. // to lower the risk of a port collision with other running
  1346. // viewer sessions.
  1347. static U32 portoffset = 49 + ll_rand(49); // 0 to 98
  1348. port += portoffset++;
  1349. portoffset %= 100; // Cycle the offset over 100 ports
  1350. mProcess->addArgument("-i");
  1351. mProcess->addArgument(llformat("%s:%d", host.c_str(),
  1352. port));
  1353. S32 log_level = mLogLevel;
  1354. #if LL_LINUX
  1355. if (mDeprecatedClient)
  1356. {
  1357. log_level = log_level == 0 ? -1 : 10;
  1358. }
  1359. #endif
  1360. mProcess->addArgument("-ll");
  1361. mProcess->addArgument(llformat("%d", log_level));
  1362. #if LL_LINUX
  1363. if (!mDeprecatedClient)
  1364. #endif
  1365. {
  1366. std::string log_dir =
  1367. gDirUtil.getFullPath(LL_PATH_LOGS, "");
  1368. mProcess->addArgument("-lf");
  1369. mProcess->addArgument(log_dir);
  1370. mProcess->addArgument("-lp");
  1371. mProcess->addArgument("SLVoice");
  1372. mProcess->addArgument("-ls");
  1373. mProcess->addArgument(".log");
  1374. S32 timeout =
  1375. gSavedSettings.getU32("VivoxShutdownTimeout");
  1376. mProcess->addArgument("-st");
  1377. mProcess->addArgument(llformat("%d", timeout));
  1378. }
  1379. if (mProcess->launch() != 0)
  1380. {
  1381. llwarns << "Failure to launch SLVoice. Giving up."
  1382. << llendl;
  1383. killDaemon();
  1384. setState(stateJail);
  1385. break;
  1386. }
  1387. mDaemonHost = LLHost(host, port);
  1388. }
  1389. else
  1390. {
  1391. llwarns << full_path << " not found. Giving up." << llendl;
  1392. setState(stateJail);
  1393. break;
  1394. }
  1395. mUpdateTimer.start();
  1396. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  1397. setState(stateDaemonLaunched);
  1398. // Dirty the states we will need to sync with the daemon when
  1399. // it comes up.
  1400. mPTTDirty = true;
  1401. mMicVolumeDirty = true;
  1402. mSpeakerVolumeDirty = true;
  1403. mSpeakerMuteDirty = true;
  1404. // These only need to be set if they are not default (i.e.
  1405. // empty strings).
  1406. mCaptureDeviceDirty = !mCaptureDevice.empty();
  1407. mRenderDeviceDirty = !mRenderDevice.empty();
  1408. // Delay the first socket connection attempt to let the process
  1409. // deamon start and listen for the socket. HB
  1410. mUpdateTimer.start();
  1411. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  1412. }
  1413. break;
  1414. case stateDaemonLaunched:
  1415. if (mUpdateTimer.hasExpired())
  1416. {
  1417. LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
  1418. if (!mSocket)
  1419. {
  1420. LL_DEBUGS("Voice") << "Creating socket to vivox daemon"
  1421. << LL_ENDL;
  1422. mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
  1423. }
  1424. mConnected = mSocket->blockingConnect(mDaemonHost);
  1425. if (mConnected)
  1426. {
  1427. LL_DEBUGS("Voice") << "Connected to socket" << LL_ENDL;
  1428. setState(stateConnecting);
  1429. break;
  1430. }
  1431. LL_DEBUGS("Voice") << "Failure to connect to socket" << LL_ENDL;
  1432. // If the connection failed, the socket may have been put into
  1433. // a bad state; delete it.
  1434. closeSocket();
  1435. // Give up after 12 failed attempts in total. HB
  1436. if (mRetries >= 12)
  1437. {
  1438. llwarns << "Too many retries. Giving up." << llendl;
  1439. setState(stateJail);
  1440. break;
  1441. }
  1442. // Every 3 failed connection retries, try and restart the
  1443. // daemon itself... HB
  1444. if (++mRetries % 3 == 0)
  1445. {
  1446. killDaemon();
  1447. setState(stateStart);
  1448. }
  1449. mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
  1450. }
  1451. break;
  1452. case stateConnecting:
  1453. // Cannot do this until we have the pump available.
  1454. if (mPump)
  1455. {
  1456. // Attach the pumps and pipes
  1457. LLPumpIO::chain_t read_chain;
  1458. read_chain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
  1459. read_chain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
  1460. mPump->addChain(read_chain, 0.f); // 0.f = never expire
  1461. setState(stateConnected);
  1462. }
  1463. break;
  1464. case stateConnected:
  1465. // Initial devices query
  1466. getCaptureDevicesSendMessage();
  1467. getRenderDevicesSendMessage();
  1468. setupVADParams();
  1469. mLoginRetryCount = 0;
  1470. setState(stateIdle);
  1471. break;
  1472. case stateIdle:
  1473. // This is the idle state where we are connected to the daemon but
  1474. // have not set up a connector yet.
  1475. if (mTuningMode)
  1476. {
  1477. mTuningExitState = stateIdle;
  1478. setState(stateMicTuningStart);
  1479. }
  1480. else if (!mVoiceEnabled || !mProcessChannels)
  1481. {
  1482. // We never started up the connector. This will shut down the
  1483. // daemon.
  1484. setState(stateConnectorStopped);
  1485. }
  1486. else if (!mAccountName.empty() && mAccountPassword.empty())
  1487. {
  1488. requestVoiceAccountProvision();
  1489. }
  1490. break;
  1491. case stateMicTuningStart:
  1492. if (mUpdateTimer.hasExpired())
  1493. {
  1494. if (mCaptureDeviceDirty || mRenderDeviceDirty)
  1495. {
  1496. // These cannot be changed while in tuning mode. Set them
  1497. // before starting.
  1498. std::ostringstream stream;
  1499. buildSetCaptureDevice(stream);
  1500. buildSetRenderDevice(stream);
  1501. if (!stream.str().empty())
  1502. {
  1503. writeString(stream.str());
  1504. }
  1505. // This will come around again in the same state and start
  1506. // the capture, after the timer expires.
  1507. mUpdateTimer.start();
  1508. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1509. }
  1510. else
  1511. {
  1512. // Duration parameter is currently unused, per Mike S.
  1513. tuningCaptureStartSendMessage(10000);
  1514. setState(stateMicTuningRunning);
  1515. }
  1516. }
  1517. break;
  1518. case stateMicTuningRunning:
  1519. if (!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
  1520. {
  1521. // All of these conditions make us leave tuning mode.
  1522. setState(stateMicTuningStop);
  1523. }
  1524. // Process mic/speaker volume changes
  1525. else if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
  1526. {
  1527. std::ostringstream stream;
  1528. if (mTuningMicVolumeDirty)
  1529. {
  1530. llinfos << "Setting tuning mic level to "
  1531. << mTuningMicVolume << llendl;
  1532. stream << "<Request requestId=\"" << mCommandCookie++
  1533. << "\" action=\"Aux.SetMicLevel.1\"><Level>"
  1534. << mTuningMicVolume << "</Level></Request>\n\n\n";
  1535. }
  1536. if (mTuningSpeakerVolumeDirty)
  1537. {
  1538. stream << "<Request requestId=\"" << mCommandCookie++
  1539. << "\" action=\"Aux.SetSpeakerLevel.1\">"
  1540. << "<Level>" << mTuningSpeakerVolume
  1541. << "</Level></Request>\n\n\n";
  1542. }
  1543. mTuningMicVolumeDirty = false;
  1544. mTuningSpeakerVolumeDirty = false;
  1545. if (!stream.str().empty())
  1546. {
  1547. writeString(stream.str());
  1548. }
  1549. }
  1550. break;
  1551. case stateMicTuningStop:
  1552. {
  1553. // Transition out of mic tuning
  1554. tuningCaptureStopSendMessage();
  1555. setState(mTuningExitState);
  1556. // If we exited just to change devices, this will keep us from
  1557. // re-entering too fast.
  1558. mUpdateTimer.start();
  1559. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1560. break;
  1561. }
  1562. case stateConnectorStart:
  1563. if (!mVoiceEnabled || !mProcessChannels)
  1564. {
  1565. // We were never logged in. This will shut down the connector.
  1566. setState(stateLoggedOut);
  1567. }
  1568. else if (!mVoiceAccountServerURI.empty())
  1569. {
  1570. connectorCreate();
  1571. }
  1572. break;
  1573. case stateConnectorStarting:
  1574. // Waiting for connector handle connectorCreateResponse() will
  1575. // transition from here to stateConnectorStarted.
  1576. break;
  1577. case stateConnectorStarted: // Connector handle received
  1578. if (!mVoiceEnabled || !mProcessChannels)
  1579. {
  1580. // We were never logged in. This will shut down the connector.
  1581. setState(stateLoggedOut);
  1582. }
  1583. else
  1584. {
  1585. // The connector is started. Send a login message.
  1586. setState(stateNeedsLogin);
  1587. }
  1588. break;
  1589. case stateLoginRetry:
  1590. if (mLoginRetryCount == 0)
  1591. {
  1592. // First retry: display a message to the user
  1593. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
  1594. }
  1595. ++mLoginRetryCount;
  1596. if (mLoginRetryCount > MAX_LOGIN_RETRIES)
  1597. {
  1598. llwarns << "Too many login retries, giving up." << llendl;
  1599. setState(stateLoginFailed);
  1600. }
  1601. else
  1602. {
  1603. llinfos << "Will retry login in " << LOGIN_RETRY_SECONDS
  1604. << " seconds." << llendl;
  1605. mUpdateTimer.start();
  1606. mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
  1607. setState(stateLoginRetryWait);
  1608. }
  1609. break;
  1610. case stateLoginRetryWait:
  1611. if (mUpdateTimer.hasExpired())
  1612. {
  1613. setState(stateNeedsLogin);
  1614. }
  1615. break;
  1616. case stateNeedsLogin:
  1617. if (!mAccountPassword.empty())
  1618. {
  1619. setState(stateLoggingIn);
  1620. loginSendMessage();
  1621. }
  1622. break;
  1623. case stateLoggingIn: // Waiting for account handle
  1624. // loginResponse() will transition from here to stateLoggedIn.
  1625. break;
  1626. case stateLoggedIn: // Account handle received
  1627. {
  1628. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
  1629. // Set up the mute list observer if it has not been set up already.
  1630. if (!sMuteListListening)
  1631. {
  1632. LLMuteList::addObserver(&sMutelistListener);
  1633. sMuteListListening = true;
  1634. }
  1635. // Set the initial state of mic mute, local speaker volume, etc.
  1636. std::ostringstream stream;
  1637. buildLocalAudioUpdates(stream);
  1638. if (!stream.str().empty())
  1639. {
  1640. writeString(stream.str());
  1641. }
  1642. setState(stateNoChannel);
  1643. // Initial kick-off of channel lookup logic
  1644. parcelChanged();
  1645. break;
  1646. }
  1647. case stateNoChannel:
  1648. if (mSessionTerminateRequested || !mVoiceEnabled ||
  1649. !mProcessChannels)
  1650. {
  1651. // *TODO: is this the right way out of this state ?
  1652. setState(stateSessionTerminated);
  1653. }
  1654. else if (mTuningMode)
  1655. {
  1656. mTuningExitState = stateNoChannel;
  1657. setState(stateMicTuningStart);
  1658. }
  1659. else if (sessionNeedsRelog(mNextAudioSession))
  1660. {
  1661. requestRelog();
  1662. setState(stateSessionTerminated);
  1663. }
  1664. else if (mNextAudioSession)
  1665. {
  1666. sessionState* oldSession = mAudioSession;
  1667. mAudioSession = mNextAudioSession;
  1668. if (!mAudioSession->mReconnect)
  1669. {
  1670. mNextAudioSession = NULL;
  1671. }
  1672. // The old session may now need to be deleted.
  1673. reapSession(oldSession);
  1674. if (!mAudioSession->mHandle.empty())
  1675. {
  1676. // Connect to a session by session handle
  1677. sessionMediaConnectSendMessage(mAudioSession);
  1678. }
  1679. else
  1680. {
  1681. // Connect to a session by URI
  1682. sessionCreateSendMessage(mAudioSession, true, false);
  1683. }
  1684. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
  1685. setState(stateJoiningSession);
  1686. }
  1687. else if (!mSpatialSessionURI.empty())
  1688. {
  1689. // If we're not headed elsewhere and have a spatial URI,
  1690. // return to spatial.
  1691. switchChannel(mSpatialSessionURI, true, false, false,
  1692. mSpatialSessionCredentials);
  1693. }
  1694. break;
  1695. case stateJoiningSession: // Waiting for session handle
  1696. // joinedAudioSession() will transition from here to
  1697. // stateSessionJoined.
  1698. if (!mVoiceEnabled || !mProcessChannels)
  1699. {
  1700. // User bailed out during connect -- jump straight to teardown.
  1701. setState(stateSessionTerminated);
  1702. }
  1703. else if (mSessionTerminateRequested)
  1704. {
  1705. if (mAudioSession && !mAudioSession->mHandle.empty())
  1706. {
  1707. // Only allow direct exits from this state in P2P calls
  1708. // (for cancelling an invite).
  1709. // Terminating a half-connected session on other types of
  1710. // calls seems to break something in the vivox gateway.
  1711. if (mAudioSession->mIsP2P)
  1712. {
  1713. sessionGroupTerminateSendMessage(mAudioSession);
  1714. setState(stateSessionTerminated);
  1715. }
  1716. }
  1717. }
  1718. break;
  1719. case stateSessionJoined: // Session handle received
  1720. // It appears that we need to wait for BOTH the
  1721. // SessionGroup.AddSession response and the
  1722. // SessionStateChangeEvent with state 4 before continuing from
  1723. // this state. They can happen in either order, and if we do not
  1724. // wait for both, things can get stuck.
  1725. // For now, the SessionGroup.AddSession response handler sets
  1726. // mSessionHandle and the SessionStateChangeEvent handler
  1727. // transitions to stateSessionJoined.
  1728. // This is a cheap way to make sure both have happened before
  1729. // proceeding.
  1730. if (mAudioSession && mAudioSession->mVoiceEnabled)
  1731. {
  1732. // Dirty state that may need to be sync'ed with the daemon.
  1733. mPTTDirty = true;
  1734. mSpeakerVolumeDirty = true;
  1735. mSpatialCoordsDirty = true;
  1736. setState(stateRunning);
  1737. // Start the throttle timer
  1738. mUpdateTimer.start();
  1739. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1740. // Events that need to happen when a session is joined could go
  1741. // here. Maybe send initial spatial data ?
  1742. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
  1743. }
  1744. else if (!mVoiceEnabled || !mProcessChannels)
  1745. {
  1746. // User bailed out during connect; jump straight to teardown.
  1747. setState(stateSessionTerminated);
  1748. }
  1749. else if (mSessionTerminateRequested)
  1750. {
  1751. // Only allow direct exits from this state in P2P calls (for
  1752. // cancelling an invite).
  1753. // Terminating a half-connected session on other types of calls
  1754. // seems to break something in the vivox gateway.
  1755. if (mAudioSession && mAudioSession->mIsP2P)
  1756. {
  1757. sessionGroupTerminateSendMessage(mAudioSession);
  1758. setState(stateSessionTerminated);
  1759. }
  1760. }
  1761. break;
  1762. case stateRunning: // Steady state
  1763. // Disabling voice or disconnect requested.
  1764. if (!mVoiceEnabled || !mProcessChannels ||
  1765. mSessionTerminateRequested)
  1766. {
  1767. leaveAudioSession();
  1768. }
  1769. else
  1770. {
  1771. // Figure out whether the PTT state needs to change
  1772. bool new_ptt;
  1773. if (gVoiceClient.mUsePTT)
  1774. {
  1775. // If configured to use PTT, track the user state.
  1776. new_ptt = gVoiceClient.mUserPTTState;
  1777. }
  1778. else
  1779. {
  1780. // If not configured to use PTT, it should always be true
  1781. // (otherwise the user will be unable to speak).
  1782. new_ptt = true;
  1783. }
  1784. if (gVoiceClient.mMuteMic)
  1785. {
  1786. // This always overrides any other PTT setting.
  1787. new_ptt = false;
  1788. }
  1789. // Dirty if state changed.
  1790. if (mPTT != new_ptt)
  1791. {
  1792. mPTT = new_ptt;
  1793. mPTTDirty = true;
  1794. }
  1795. if (!inSpatialChannel())
  1796. {
  1797. // When in a non-spatial channel, never send positional
  1798. // updates.
  1799. mSpatialCoordsDirty = false;
  1800. }
  1801. else
  1802. {
  1803. // Do the calculation that enforces the listener<->speaker
  1804. // tether (and also updates the real camera position)
  1805. enforceTether();
  1806. }
  1807. // Send an update if the ptt state has changed (which should
  1808. // not be able to happen that often; the user can only click so
  1809. // fast) or every 10hz, whichever is sooner.
  1810. if ((mAudioSession && mAudioSession->mVolumeDirty) ||
  1811. mPTTDirty || mSpeakerVolumeDirty ||
  1812. mUpdateTimer.hasExpired())
  1813. {
  1814. mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
  1815. sendPositionalUpdate();
  1816. }
  1817. // Dity hack to get voice to initialize properly after login
  1818. if (first_run)
  1819. {
  1820. first_run = false;
  1821. LLVoiceChannel::suspend();
  1822. LLVoiceChannel::resume();
  1823. }
  1824. }
  1825. break;
  1826. case stateLeavingSession: // Waiting for terminate session response
  1827. // The handler for the Session.Terminate response will transition
  1828. // from here to stateSessionTerminated.
  1829. break;
  1830. case stateSessionTerminated:
  1831. // Must do this first, since it uses mAudioSession.
  1832. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
  1833. if (mAudioSession)
  1834. {
  1835. sessionState* old_sessionp = mAudioSession;
  1836. mAudioSession = NULL;
  1837. // The old session may now need to be deleted.
  1838. reapSession(old_sessionp);
  1839. }
  1840. else
  1841. {
  1842. llwarns << "stateSessionTerminated with NULL mAudioSession"
  1843. << llendl;
  1844. }
  1845. // Always reset the terminate request flag when we get here.
  1846. mSessionTerminateRequested = false;
  1847. if ((mVoiceEnabled && mProcessChannels) && !mRelogRequested)
  1848. {
  1849. // Just leaving a channel, go back to stateNoChannel (the
  1850. // "logged in but have no channel" state).
  1851. setState(stateNoChannel);
  1852. }
  1853. else
  1854. {
  1855. // Shutting down voice, continue with disconnecting.
  1856. logout();
  1857. // The state machine will take it from here
  1858. mRelogRequested = false;
  1859. }
  1860. break;
  1861. case stateLoggingOut: // Waiting for logout response
  1862. // The handler for the AccountLoginStateChangeEvent will transition
  1863. // from here to stateLoggedOut.
  1864. break;
  1865. case stateLoggedOut: // Logout response received
  1866. // Once we are logged out, all these things are invalid.
  1867. mAccountLoggedIn = false;
  1868. deleteAllSessions();
  1869. if (mVoiceEnabled && mProcessChannels && !mRelogRequested)
  1870. {
  1871. // User was logged out, but wants to be logged in. Send a new
  1872. // login request.
  1873. setState(stateNeedsLogin);
  1874. }
  1875. else
  1876. {
  1877. // Shut down the connector
  1878. connectorShutdown();
  1879. }
  1880. break;
  1881. case stateConnectorStopping: // Waiting for connector stop
  1882. // The handler for the Connector.InitiateShutdown response will
  1883. // transition from here to stateConnectorStopped.
  1884. break;
  1885. case stateConnectorStopped: // Connector stop received
  1886. setState(stateDisableCleanup);
  1887. break;
  1888. case stateConnectorFailed:
  1889. setState(stateConnectorFailedWaiting);
  1890. break;
  1891. case stateConnectorFailedWaiting:
  1892. if (!mVoiceEnabled || !mProcessChannels)
  1893. {
  1894. setState(stateDisableCleanup);
  1895. }
  1896. break;
  1897. case stateLoginFailed:
  1898. setState(stateLoginFailedWaiting);
  1899. break;
  1900. case stateLoginFailedWaiting:
  1901. if (!mVoiceEnabled || !mProcessChannels)
  1902. {
  1903. setState(stateDisableCleanup);
  1904. }
  1905. break;
  1906. case stateJoinSessionFailed:
  1907. // Transition to error state. Send out any notifications here.
  1908. if (mAudioSession)
  1909. {
  1910. llwarns << "stateJoinSessionFailed: ("
  1911. << mAudioSession->mErrorStatusCode << "): "
  1912. << mAudioSession->mErrorStatusString << llendl;
  1913. }
  1914. else
  1915. {
  1916. llwarns << "stateJoinSessionFailed with no current session"
  1917. << llendl;
  1918. }
  1919. notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
  1920. setState(stateJoinSessionFailedWaiting);
  1921. break;
  1922. case stateJoinSessionFailedWaiting:
  1923. // Joining a channel failed, either due to a failed channel name ->
  1924. // sip url lookup or an error from the join message.
  1925. // Region crossings may leave this state and try the join again.
  1926. if (mSessionTerminateRequested)
  1927. {
  1928. setState(stateSessionTerminated);
  1929. }
  1930. break;
  1931. case stateJail:
  1932. // We have given up. Do nothing.
  1933. break;
  1934. }
  1935. }
  1936. void LLVoiceVivox::closeSocket()
  1937. {
  1938. mSocket.reset();
  1939. mConnected = mConnectorEstablished = mAccountLoggedIn = false;
  1940. }
  1941. void LLVoiceVivox::loginSendMessage()
  1942. {
  1943. std::ostringstream stream;
  1944. stream << "<Request requestId=\"" << mCommandCookie++
  1945. << "\" action=\"Account.Login.1\"><ConnectorHandle>"
  1946. << mConnectorHandle << "</ConnectorHandle><AccountName>"
  1947. << mAccountName << "</AccountName><AccountPassword>"
  1948. << mAccountPassword << "</AccountPassword>"
  1949. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1950. << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
  1951. << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>"
  1952. << "<BuddyManagementMode>Application</BuddyManagementMode>"
  1953. << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
  1954. << "</Request>\n\n\n";
  1955. writeString(stream.str());
  1956. }
  1957. void LLVoiceVivox::logout()
  1958. {
  1959. // Ensure that we will re-request provisioning before logging in again
  1960. mAccountPassword.clear();
  1961. mVoiceAccountServerURI.clear();
  1962. setState(stateLoggingOut);
  1963. logoutSendMessage();
  1964. }
  1965. void LLVoiceVivox::logoutSendMessage()
  1966. {
  1967. if (mAccountLoggedIn)
  1968. {
  1969. std::ostringstream stream;
  1970. stream << "<Request requestId=\"" << mCommandCookie++
  1971. << "\" action=\"Account.Logout.1\"><AccountHandle>"
  1972. << mAccountHandle << "</AccountHandle></Request>\n\n\n";
  1973. mAccountLoggedIn = false;
  1974. writeString(stream.str());
  1975. }
  1976. }
  1977. void LLVoiceVivox::sessionCreateSendMessage(sessionState* session,
  1978. bool start_audio,
  1979. bool start_text)
  1980. {
  1981. LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
  1982. session->mCreateInProgress = true;
  1983. if (start_audio)
  1984. {
  1985. session->mMediaConnectInProgress = true;
  1986. }
  1987. std::ostringstream stream;
  1988. stream << "<Request requestId=\"" << session->mSIPURI
  1989. << "\" action=\"Session.Create.1\"><AccountHandle>"
  1990. << mAccountHandle << "</AccountHandle><URI>" << session->mSIPURI
  1991. << "</URI>";
  1992. static const std::string allowed_chars =
  1993. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  1994. "0123456789"
  1995. "-._~";
  1996. if (!session->mHash.empty())
  1997. {
  1998. stream << "<Password>" << LLURI::escape(session->mHash, allowed_chars)
  1999. << "</Password>"
  2000. << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
  2001. }
  2002. stream << "<ConnectAudio>" << (start_audio ? "true" : "false")
  2003. << "</ConnectAudio><ConnectText>" << (start_text ? "true" : "false")
  2004. << "</ConnectText><Name>" << mChannelName
  2005. << "</Name><VoiceFontID>0</VoiceFontID></Request>\n\n\n";
  2006. writeString(stream.str());
  2007. }
  2008. void LLVoiceVivox::sessionGroupAddSessionSendMessage(sessionState* session,
  2009. bool start_audio,
  2010. bool start_text)
  2011. {
  2012. LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
  2013. session->mCreateInProgress = true;
  2014. if (start_audio)
  2015. {
  2016. session->mMediaConnectInProgress = true;
  2017. }
  2018. std::string password;
  2019. if (!session->mHash.empty())
  2020. {
  2021. static const std::string allowed_chars =
  2022. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  2023. "0123456789"
  2024. "-._~";
  2025. password = LLURI::escape(session->mHash, allowed_chars);
  2026. }
  2027. std::ostringstream stream;
  2028. stream << "<Request requestId=\"" << session->mSIPURI
  2029. << "\" action=\"SessionGroup.AddSession.1\"><SessionGroupHandle>"
  2030. << session->mGroupHandle << "</SessionGroupHandle><URI>"
  2031. << session->mSIPURI << "</URI><Name>" << mChannelName
  2032. << "</Name><ConnectAudio>" << (start_audio ? "true" : "false")
  2033. << "</ConnectAudio><ConnectText>" << (start_text ? "true" : "false")
  2034. << "<VoiceFontID>0</VoiceFontID></ConnectText><Password>"
  2035. << password << "</Password>"
  2036. << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
  2037. << "</Request>\n\n\n";
  2038. writeString(stream.str());
  2039. }
  2040. void LLVoiceVivox::sessionMediaConnectSendMessage(sessionState* session)
  2041. {
  2042. LL_DEBUGS("Voice") << "Connecting audio to session handle: "
  2043. << session->mHandle << LL_ENDL;
  2044. session->mMediaConnectInProgress = true;
  2045. std::ostringstream stream;
  2046. stream << "<Request requestId=\"" << session->mHandle
  2047. << "\" action=\"Session.MediaConnect.1\"><SessionGroupHandle>"
  2048. << session->mGroupHandle << "</SessionGroupHandle><SessionHandle>"
  2049. << session->mHandle << "</SessionHandle><VoiceFontID>0</VoiceFontID>"
  2050. <<"<Media>Audio</Media></Request>\n\n\n";
  2051. writeString(stream.str());
  2052. }
  2053. void LLVoiceVivox::sessionTextConnectSendMessage(sessionState* session)
  2054. {
  2055. LL_DEBUGS("Voice") << "Connecting text to session handle: "
  2056. << session->mHandle << LL_ENDL;
  2057. std::ostringstream stream;
  2058. stream << "<Request requestId=\"" << session->mHandle
  2059. << "\" action=\"Session.TextConnect.1\"><SessionGroupHandle>"
  2060. << session->mGroupHandle << "</SessionGroupHandle><SessionHandle>"
  2061. << session->mHandle << "</SessionHandle></Request>\n\n\n";
  2062. writeString(stream.str());
  2063. }
  2064. void LLVoiceVivox::sessionTerminate()
  2065. {
  2066. mSessionTerminateRequested = true;
  2067. }
  2068. void LLVoiceVivox::requestRelog()
  2069. {
  2070. mSessionTerminateRequested = true;
  2071. mRelogRequested = true;
  2072. }
  2073. //virtual
  2074. void LLVoiceVivox::leaveAudioSession()
  2075. {
  2076. if (mAudioSession)
  2077. {
  2078. LL_DEBUGS("Voice") << "Leaving session: " << mAudioSession->mSIPURI
  2079. << LL_ENDL;
  2080. switch (mState)
  2081. {
  2082. case stateNoChannel:
  2083. // In this case, we want to pretend the join failed so our
  2084. // state machine does not get stuck.
  2085. // Skip the join failed transition state so we do not send out
  2086. // error notifications.
  2087. setState(stateJoinSessionFailedWaiting);
  2088. break;
  2089. case stateJoiningSession:
  2090. case stateSessionJoined:
  2091. case stateRunning:
  2092. if (!mAudioSession->mHandle.empty())
  2093. {
  2094. sessionGroupTerminateSendMessage(mAudioSession);
  2095. setState(stateLeavingSession);
  2096. }
  2097. else
  2098. {
  2099. llwarns << "Called without session handle" << llendl;
  2100. setState(stateSessionTerminated);
  2101. }
  2102. break;
  2103. case stateJoinSessionFailed:
  2104. case stateJoinSessionFailedWaiting:
  2105. setState(stateSessionTerminated);
  2106. break;
  2107. default:
  2108. llwarns << "Called from unknown state" << llendl;
  2109. }
  2110. }
  2111. else
  2112. {
  2113. llwarns << "Called with no active session" << llendl;
  2114. setState(stateSessionTerminated);
  2115. }
  2116. }
  2117. void LLVoiceVivox::sessionGroupTerminateSendMessage(sessionState* session)
  2118. {
  2119. LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle "
  2120. << session->mGroupHandle << LL_ENDL;
  2121. std::ostringstream stream;
  2122. stream << "<Request requestId=\"" << mCommandCookie++
  2123. << "\" action=\"SessionGroup.Terminate.1\"><SessionGroupHandle>"
  2124. << session->mGroupHandle << "</SessionGroupHandle></Request>\n\n\n";
  2125. writeString(stream.str());
  2126. }
  2127. void LLVoiceVivox::getCaptureDevicesSendMessage()
  2128. {
  2129. std::ostringstream stream;
  2130. stream << "<Request requestId=\"" << mCommandCookie++
  2131. << "\" action=\"Aux.GetCaptureDevices.1\"></Request>\n\n\n";
  2132. writeString(stream.str());
  2133. }
  2134. void LLVoiceVivox::getRenderDevicesSendMessage()
  2135. {
  2136. std::ostringstream stream;
  2137. stream << "<Request requestId=\"" << mCommandCookie++
  2138. << "\" action=\"Aux.GetRenderDevices.1\"></Request>\n\n\n";
  2139. writeString(stream.str());
  2140. }
  2141. void LLVoiceVivox:: addCaptureDevice(const std::string& display_name,
  2142. const std::string& device_id)
  2143. {
  2144. // Yep, it may happen... In this case, and since we will not be able to
  2145. // set (and therefore use) this device, just skip it. HB
  2146. if (device_id.empty())
  2147. {
  2148. return;
  2149. }
  2150. if (display_name.empty()) // Could happen, I suppose... HB
  2151. {
  2152. mCaptureDevices.emplace(device_id, device_id);
  2153. }
  2154. else // Normal case
  2155. {
  2156. mCaptureDevices.emplace(display_name, device_id);
  2157. }
  2158. }
  2159. void LLVoiceVivox::setCaptureDevice(const std::string& device_id)
  2160. {
  2161. if (device_id == "Default")
  2162. {
  2163. if (!mCaptureDevice.empty())
  2164. {
  2165. mCaptureDevice.clear();
  2166. mCaptureDeviceDirty = true;
  2167. }
  2168. return;
  2169. }
  2170. if (mCaptureDevice != device_id)
  2171. {
  2172. mCaptureDevice = device_id;
  2173. mCaptureDeviceDirty = true;
  2174. }
  2175. }
  2176. void LLVoiceVivox::addRenderDevice(const std::string& display_name,
  2177. const std::string& device_id)
  2178. {
  2179. // Yep, it may happen... In this case, and since we will not be able to
  2180. // set (and therefore use) this device, just skip it. HB
  2181. if (device_id.empty())
  2182. {
  2183. return;
  2184. }
  2185. if (display_name.empty()) // Could happen, I suppose... HB
  2186. {
  2187. mRenderDevices.emplace(device_id, device_id);
  2188. }
  2189. else // Normal case
  2190. {
  2191. mRenderDevices.emplace(display_name, device_id);
  2192. }
  2193. }
  2194. void LLVoiceVivox::setRenderDevice(const std::string& device_id)
  2195. {
  2196. if (device_id == "Default")
  2197. {
  2198. if (!mRenderDevice.empty())
  2199. {
  2200. mRenderDevice.clear();
  2201. mRenderDeviceDirty = true;
  2202. }
  2203. return;
  2204. }
  2205. if (mRenderDevice != device_id)
  2206. {
  2207. mRenderDevice = device_id;
  2208. mRenderDeviceDirty = true;
  2209. }
  2210. }
  2211. void LLVoiceVivox::setTuningMode(bool tuning_on)
  2212. {
  2213. mTuningMode = tuning_on;
  2214. if (tuning_on && mState >= stateNoChannel)
  2215. {
  2216. sessionTerminate();
  2217. }
  2218. }
  2219. bool LLVoiceVivox::inTuningMode()
  2220. {
  2221. return mState == stateMicTuningRunning;
  2222. }
  2223. void LLVoiceVivox::tuningRenderStartSendMessage(const std::string& name,
  2224. bool loop)
  2225. {
  2226. mTuningAudioFile = name;
  2227. std::ostringstream stream;
  2228. stream << "<Request requestId=\"" << mCommandCookie++
  2229. << "\" action=\"Aux.RenderAudioStart.1\"><SoundFilePath>"
  2230. << mTuningAudioFile << "</SoundFilePath><Loop>"
  2231. << (loop ? "1" : "0") << "</Loop></Request>\n\n\n";
  2232. writeString(stream.str());
  2233. }
  2234. void LLVoiceVivox::tuningRenderStopSendMessage()
  2235. {
  2236. std::ostringstream stream;
  2237. stream << "<Request requestId=\"" << mCommandCookie++
  2238. << "\" action=\"Aux.RenderAudioStop.1\"><SoundFilePath>"
  2239. << mTuningAudioFile << "</SoundFilePath></Request>\n\n\n";
  2240. writeString(stream.str());
  2241. }
  2242. void LLVoiceVivox::tuningCaptureStartSendMessage(S32 duration)
  2243. {
  2244. LL_DEBUGS("Voice") << "Sending CaptureAudioStart" << LL_ENDL;
  2245. std::ostringstream stream;
  2246. stream << "<Request requestId=\"" << mCommandCookie++
  2247. << "\" action=\"Aux.CaptureAudioStart.1\"><Duration>"
  2248. << duration << "</Duration></Request>\n\n\n";
  2249. writeString(stream.str());
  2250. }
  2251. void LLVoiceVivox::tuningCaptureStopSendMessage()
  2252. {
  2253. LL_DEBUGS("Voice") << "Sending CaptureAudioStop" << LL_ENDL;
  2254. std::ostringstream stream;
  2255. stream << "<Request requestId=\"" << mCommandCookie++
  2256. << "\" action=\"Aux.CaptureAudioStop.1\"></Request>\n\n\n";
  2257. writeString(stream.str());
  2258. mTuningEnergy = 0.f;
  2259. }
  2260. void LLVoiceVivox::tuningSetMicVolume(F32 volume)
  2261. {
  2262. S32 scaled_volume = scale_mic_volume(volume);
  2263. if (scaled_volume != mTuningMicVolume)
  2264. {
  2265. mTuningMicVolume = scaled_volume;
  2266. mTuningMicVolumeDirty = true;
  2267. }
  2268. }
  2269. #if 0 // Not used
  2270. void LLVoiceVivox::tuningSetSpeakerVolume(F32 volume)
  2271. {
  2272. S32 scaled_volume = scale_speaker_volume(volume);
  2273. if (scaled_volume != mTuningSpeakerVolume)
  2274. {
  2275. mTuningSpeakerVolume = scaled_volume;
  2276. mTuningSpeakerVolumeDirty = true;
  2277. }
  2278. }
  2279. #endif
  2280. bool LLVoiceVivox::deviceSettingsAvailable()
  2281. {
  2282. return mConnected && !mRenderDevices.empty();
  2283. }
  2284. void LLVoiceVivox::refreshDeviceLists(bool clear_current_list)
  2285. {
  2286. if (clear_current_list)
  2287. {
  2288. mCaptureDevices.clear();
  2289. mRenderDevices.clear();
  2290. }
  2291. getCaptureDevicesSendMessage();
  2292. getRenderDevicesSendMessage();
  2293. }
  2294. void LLVoiceVivox::daemonDied()
  2295. {
  2296. // The daemon died, so the connection is gone. Reset everything and start
  2297. // over.
  2298. llwarns << "Connection to Vivox daemon lost. Resetting state." << llendl;
  2299. // Try to relaunch the daemon
  2300. setState(stateDisableCleanup);
  2301. }
  2302. void LLVoiceVivox::giveUp()
  2303. {
  2304. // Avoid infinite loop while giving up...
  2305. static bool giving_up = false;
  2306. if (!giving_up)
  2307. {
  2308. giving_up = true;
  2309. // All has failed. Clean up and stop trying.
  2310. closeSocket();
  2311. deleteAllSessions();
  2312. setState(stateJail);
  2313. llwarns << "Unrecoverable error: voice permanently disabled."
  2314. << llendl;
  2315. }
  2316. }
  2317. static void oldSDKTransform(LLVector3& left, LLVector3& up, LLVector3& at,
  2318. LLVector3d& pos, LLVector3& vel)
  2319. {
  2320. // the new at, up, left vectors and the new position and velocity
  2321. F32 nat[3], nup[3], nl[3];
  2322. F64 npos[3];
  2323. // The original XML command was sent like this:
  2324. /*
  2325. << "<Position>"
  2326. << "<X>" << pos[VX] << "</X>"
  2327. << "<Y>" << pos[VZ] << "</Y>"
  2328. << "<Z>" << pos[VY] << "</Z>"
  2329. << "</Position>"
  2330. << "<Velocity>"
  2331. << "<X>" << mAvatarVelocity[VX] << "</X>"
  2332. << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
  2333. << "<Z>" << mAvatarVelocity[VY] << "</Z>"
  2334. << "</Velocity>"
  2335. << "<AtOrientation>"
  2336. << "<X>" << l.mV[VX] << "</X>"
  2337. << "<Y>" << u.mV[VX] << "</Y>"
  2338. << "<Z>" << a.mV[VX] << "</Z>"
  2339. << "</AtOrientation>"
  2340. << "<UpOrientation>"
  2341. << "<X>" << l.mV[VZ] << "</X>"
  2342. << "<Y>" << u.mV[VY] << "</Y>"
  2343. << "<Z>" << a.mV[VZ] << "</Z>"
  2344. << "</UpOrientation>"
  2345. << "<LeftOrientation>"
  2346. << "<X>" << l.mV [VY] << "</X>"
  2347. << "<Y>" << u.mV [VZ] << "</Y>"
  2348. << "<Z>" << a.mV [VY] << "</Z>"
  2349. << "</LeftOrientation>";
  2350. */
  2351. #if 1
  2352. // This was the original transform done when building the XML command
  2353. nat[0] = left.mV[VX];
  2354. nat[1] = up.mV[VX];
  2355. nat[2] = at.mV[VX];
  2356. nup[0] = left.mV[VZ];
  2357. nup[1] = up.mV[VY];
  2358. nup[2] = at.mV[VZ];
  2359. nl[0] = left.mV[VY];
  2360. nl[1] = up.mV[VZ];
  2361. nl[2] = at.mV[VY];
  2362. npos[0] = pos.mdV[VX];
  2363. npos[1] = pos.mdV[VZ];
  2364. npos[2] = pos.mdV[VY];
  2365. for (S32 i = 0; i < 3; ++i)
  2366. {
  2367. at.mV[i] = nat[i];
  2368. up.mV[i] = nup[i];
  2369. left.mV[i] = nl[i];
  2370. pos.mdV[i] = npos[i];
  2371. }
  2372. // This was the original transform done in the SDK
  2373. nat[0] = at.mV[2];
  2374. nat[1] = 0; // y component of at vector is always 0, this was up[2]
  2375. nat[2] = -1 * left.mV[2];
  2376. // We override whatever the application gives us
  2377. nup[0] = 0; // x component of up vector is always 0
  2378. nup[1] = 1; // y component of up vector is always 1
  2379. nup[2] = 0; // z component of up vector is always 0
  2380. nl[0] = at.mV[0];
  2381. nl[1] = 0; // y component of left vector is always zero, this was up[0]
  2382. nl[2] = -1 * left.mV[0];
  2383. npos[2] = pos.mdV[2] * -1.0;
  2384. npos[1] = pos.mdV[1];
  2385. npos[0] = pos.mdV[0];
  2386. for (S32 i = 0; i < 3; ++i)
  2387. {
  2388. at.mV[i] = nat[i];
  2389. up.mV[i] = nup[i];
  2390. left.mV[i] = nl[i];
  2391. pos.mdV[i] = npos[i];
  2392. }
  2393. #else
  2394. // This is the compose of the two transforms (at least, that's what I'm trying for)
  2395. nat[0] = at.mV[VX];
  2396. nat[1] = 0; // y component of at vector is always 0, this was up[2]
  2397. nat[2] = -1 * up.mV[VZ];
  2398. // We override whatever the application gives us
  2399. nup[0] = 0; // x component of up vector is always 0
  2400. nup[1] = 1; // y component of up vector is always 1
  2401. nup[2] = 0; // z component of up vector is always 0
  2402. nl[0] = left.mV[VX];
  2403. nl[1] = 0; // y component of left vector is always zero, this was up[0]
  2404. nl[2] = -1 * left.mV[VY];
  2405. npos[0] = pos.mdV[VX];
  2406. npos[1] = pos.mdV[VZ];
  2407. npos[2] = pos.mdV[VY] * -1.0;
  2408. for (S32 i = 0; i < 3; ++i)
  2409. {
  2410. at.mV[i] = nat[i];
  2411. up.mV[i] = nup[i];
  2412. left.mV[i] = nl[i];
  2413. pos.mdV[i] = npos[i];
  2414. }
  2415. #endif
  2416. }
  2417. void LLVoiceVivox::sendPositionalUpdate()
  2418. {
  2419. std::ostringstream stream;
  2420. if (mSpatialCoordsDirty)
  2421. {
  2422. LLVector3 l, u, a, vel;
  2423. LLVector3d pos;
  2424. mSpatialCoordsDirty = false;
  2425. // Always send both speaker and listener positions together.
  2426. stream << "<Request requestId=\"" << mCommandCookie++
  2427. << "\" action=\"Session.Set3DPosition.1\">"
  2428. << "<SessionHandle>" << getAudioSessionHandle()
  2429. << "</SessionHandle>";
  2430. stream << "<SpeakerPosition>";
  2431. l = mAvatarRot.getLeftRow();
  2432. u = mAvatarRot.getUpRow();
  2433. a = mAvatarRot.getFwdRow();
  2434. pos = mAvatarPosition;
  2435. vel = mAvatarVelocity;
  2436. // SLIM SDK: the old SDK was doing a transform on the passed
  2437. // coordinates that the new one does not do anymore. The old transform
  2438. // is replicated by this function.
  2439. oldSDKTransform(l, u, a, pos, vel);
  2440. stream << "<Position><X>" << pos.mdV[VX] << "</X><Y>" << pos.mdV[VY]
  2441. << "</Y><Z>" << pos.mdV[VZ] << "</Z></Position><Velocity><X>"
  2442. << vel.mV[VX] << "</X><Y>" << vel.mV[VY] << "</Y><Z>"
  2443. << vel.mV[VZ] << "</Z></Velocity><AtOrientation><X>" << a.mV[VX]
  2444. << "</X><Y>" << a.mV[VY] << "</Y><Z>" << a.mV[VZ]
  2445. << "</Z></AtOrientation><UpOrientation><X>" << u.mV[VX]
  2446. << "</X><Y>" << u.mV[VY] << "</Y><Z>" << u.mV[VZ]
  2447. << "</Z></UpOrientation><LeftOrientation><X>" << l.mV[VX]
  2448. << "</X><Y>" << l.mV[VY] << "</Y><Z>" << l.mV[VZ]
  2449. << "</Z></LeftOrientation>";
  2450. stream << "</SpeakerPosition><ListenerPosition>";
  2451. LLVector3d ear_pos;
  2452. LLVector3 ear_vel;
  2453. LLMatrix3 ear_rot;
  2454. switch (mEarLocation)
  2455. {
  2456. case earLocCamera:
  2457. ear_pos = mCameraPosition;
  2458. ear_vel = mCameraVelocity;
  2459. ear_rot = mCameraRot;
  2460. break;
  2461. case earLocAvatar:
  2462. ear_pos = mAvatarPosition;
  2463. ear_vel = mAvatarVelocity;
  2464. ear_rot = mAvatarRot;
  2465. break;
  2466. case earLocMixed:
  2467. ear_pos = mAvatarPosition;
  2468. ear_vel = mAvatarVelocity;
  2469. ear_rot = mCameraRot;
  2470. break;
  2471. default:
  2472. llerrs << "Invalid ear location: " << mEarLocation << llendl;
  2473. }
  2474. l = ear_rot.getLeftRow();
  2475. u = ear_rot.getUpRow();
  2476. a = ear_rot.getFwdRow();
  2477. pos = ear_pos;
  2478. vel = ear_vel;
  2479. oldSDKTransform(l, u, a, pos, vel);
  2480. stream << "<Position><X>" << pos.mdV[VX] << "</X><Y>" << pos.mdV[VY]
  2481. << "</Y><Z>" << pos.mdV[VZ] << "</Z></Position><Velocity><X>"
  2482. << vel.mV[VX] << "</X><Y>" << vel.mV[VY] << "</Y><Z>"
  2483. << vel.mV[VZ] << "</Z></Velocity><AtOrientation><X>" << a.mV[VX]
  2484. << "</X><Y>" << a.mV[VY] << "</Y><Z>" << a.mV[VZ]
  2485. << "</Z></AtOrientation><UpOrientation><X>" << u.mV[VX]
  2486. << "</X><Y>" << u.mV[VY] << "</Y><Z>" << u.mV[VZ]
  2487. << "</Z></UpOrientation><LeftOrientation><X>" << l.mV[VX]
  2488. << "</X><Y>" << l.mV[VY] << "</Y><Z>" << l.mV[VZ]
  2489. << "</Z></LeftOrientation>";
  2490. stream << "</ListenerPosition></Request>\n\n\n";
  2491. }
  2492. if (mAudioSession && mAudioSession->mVolumeDirty)
  2493. {
  2494. particip_map_t::iterator iter =
  2495. mAudioSession->mParticipantsByURI.begin();
  2496. mAudioSession->mVolumeDirty = false;
  2497. for ( ; iter != mAudioSession->mParticipantsByURI.end(); ++iter)
  2498. {
  2499. participantState* p = iter->second;
  2500. if (p->mVolumeDirty)
  2501. {
  2502. // Cannot set volume/mute for yourself
  2503. if (!p->mIsSelf)
  2504. {
  2505. S32 volume = 56; // nominal default value
  2506. bool mute = p->mOnMuteList;
  2507. if (p->mUserVolume != -1)
  2508. {
  2509. // Scale from user volume in the range 0-400 (with 100
  2510. // as "normal") to vivox volume in the range 0-100
  2511. // (with 56 as "normal")
  2512. if (p->mUserVolume < 100)
  2513. {
  2514. volume = (p->mUserVolume * 56) / 100;
  2515. }
  2516. else
  2517. {
  2518. volume = 44 * (p->mUserVolume - 100) / 300 + 56;
  2519. }
  2520. }
  2521. else if (p->mVolume != -1)
  2522. {
  2523. // Use the previously reported internal volume (comes
  2524. // in with a ParticipantUpdatedEvent)
  2525. volume = p->mVolume;
  2526. }
  2527. if (mute)
  2528. {
  2529. // SetParticipantMuteForMe does not work in P2P
  2530. // sessions. If we want the user to be muted, set their
  2531. // volume to 0 as well. This is not perfect, but it
  2532. // will at least reduce their volume to a minimum.
  2533. volume = 0;
  2534. }
  2535. if (volume <= 0)
  2536. {
  2537. mute = true;
  2538. }
  2539. LL_DEBUGS("Voice") << "Setting volume/mute for avatar "
  2540. << p->mAvatarID << " to " << volume
  2541. << (mute ? "/true" : "/false")
  2542. << LL_ENDL;
  2543. // SLIM SDK: Send both volume and mute commands.
  2544. // Send a "volume for me" command for the user.
  2545. stream << "<Request requestId=\"" << mCommandCookie++
  2546. << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
  2547. << "<SessionHandle>" << getAudioSessionHandle()
  2548. << "</SessionHandle><ParticipantURI>"
  2549. << p->mURI << "</ParticipantURI><Volume>"
  2550. << volume << "</Volume></Request>\n\n\n";
  2551. // Send a "mute for me" command for the user
  2552. stream << "<Request requestId=\"" << mCommandCookie++
  2553. << "\" action=\"Session.SetParticipantMuteForMe.1\">"
  2554. << "<SessionHandle>" << getAudioSessionHandle()
  2555. << "</SessionHandle><ParticipantURI>"
  2556. << p->mURI << "</ParticipantURI><Mute>"
  2557. << (mute ? "1" : "0") << "</Mute></Request>\n\n\n";
  2558. }
  2559. p->mVolumeDirty = false;
  2560. }
  2561. }
  2562. }
  2563. buildLocalAudioUpdates(stream);
  2564. if (!stream.str().empty())
  2565. {
  2566. writeString(stream.str());
  2567. }
  2568. }
  2569. void LLVoiceVivox::buildSetCaptureDevice(std::ostringstream& stream)
  2570. {
  2571. if (mCaptureDeviceDirty)
  2572. {
  2573. LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice
  2574. << "\"" << LL_ENDL;
  2575. stream << "<Request requestId=\"" << mCommandCookie++
  2576. << "\" action=\"Aux.SetCaptureDevice.1\">"
  2577. << "<CaptureDeviceSpecifier>" << mCaptureDevice
  2578. << "</CaptureDeviceSpecifier></Request>\n\n\n";
  2579. mCaptureDeviceDirty = false;
  2580. }
  2581. }
  2582. void LLVoiceVivox::buildSetRenderDevice(std::ostringstream& stream)
  2583. {
  2584. if (mRenderDeviceDirty)
  2585. {
  2586. LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice
  2587. << "\"" << LL_ENDL;
  2588. stream << "<Request requestId=\"" << mCommandCookie++
  2589. << "\" action=\"Aux.SetRenderDevice.1\"><RenderDeviceSpecifier>"
  2590. << mRenderDevice << "</RenderDeviceSpecifier></Request>\n\n\n";
  2591. mRenderDeviceDirty = false;
  2592. }
  2593. }
  2594. void LLVoiceVivox::buildLocalAudioUpdates(std::ostringstream& stream)
  2595. {
  2596. buildSetCaptureDevice(stream);
  2597. buildSetRenderDevice(stream);
  2598. if (mPTTDirty)
  2599. {
  2600. mPTTDirty = false;
  2601. // Send a local mute command.
  2602. // NOTE: the state of "PTT" is the inverse of "local mute" (i.e. when
  2603. // PTT is true, we send a mute command with "false", and vice versa).
  2604. LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter "
  2605. << (mPTT ? "false" : "true") << LL_ENDL;
  2606. stream << "<Request requestId=\"" << mCommandCookie++
  2607. << "\" action=\"Connector.MuteLocalMic.1\"><ConnectorHandle>"
  2608. << mConnectorHandle << "</ConnectorHandle>" << "<Value>"
  2609. << (mPTT ? "false" : "true") << "</Value></Request>\n\n\n";
  2610. }
  2611. if (mSpeakerMuteDirty)
  2612. {
  2613. const char* muteval = mSpeakerVolume == 0 ? "true" : "false";
  2614. mSpeakerMuteDirty = false;
  2615. llinfos << "Setting speaker mute to " << muteval << llendl;
  2616. stream << "<Request requestId=\"" << mCommandCookie++
  2617. << "\" action=\"Connector.MuteLocalSpeaker.1\">"
  2618. << "<ConnectorHandle>" << mConnectorHandle
  2619. << "</ConnectorHandle><Value>" << muteval
  2620. << "</Value></Request>\n\n\n";
  2621. }
  2622. if (mSpeakerVolumeDirty)
  2623. {
  2624. mSpeakerVolumeDirty = false;
  2625. llinfos << "Setting speaker volume to " << mSpeakerVolume << llendl;
  2626. stream << "<Request requestId=\"" << mCommandCookie++
  2627. << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
  2628. << "<ConnectorHandle>" << mConnectorHandle
  2629. << "</ConnectorHandle><Value>" << mSpeakerVolume
  2630. << "</Value></Request>\n\n\n";
  2631. }
  2632. if (mMicVolumeDirty)
  2633. {
  2634. mMicVolumeDirty = false;
  2635. llinfos << "Setting mic volume to " << mMicVolume << llendl;
  2636. stream << "<Request requestId=\"" << mCommandCookie++
  2637. << "\" action=\"Connector.SetLocalMicVolume.1\">"
  2638. << "<ConnectorHandle>" << mConnectorHandle
  2639. << "</ConnectorHandle><Value>" << mMicVolume
  2640. << "</Value></Request>\n\n\n";
  2641. }
  2642. }
  2643. /////////////////////////////
  2644. // Response/Event handlers
  2645. void LLVoiceVivox::connectorCreateResponse(S32 status_code,
  2646. std::string& status_str,
  2647. std::string& connector_handle,
  2648. std::string& version_id)
  2649. {
  2650. if (status_code)
  2651. {
  2652. llwarns << "Connector.Create response failure: " << status_str
  2653. << llendl;
  2654. setState(stateConnectorFailed);
  2655. }
  2656. else
  2657. {
  2658. // Connector created, move forward.
  2659. llinfos << "Connector.Create succeeded, Vivox SDK version is "
  2660. << version_id << " - Connector handle: " << connector_handle
  2661. << llendl;
  2662. mConnectorEstablished = true;
  2663. mConnectorHandle = connector_handle;
  2664. if (mState == stateConnectorStarting)
  2665. {
  2666. setState(stateConnectorStarted);
  2667. }
  2668. }
  2669. }
  2670. void LLVoiceVivox::loginResponse(S32 status_code,
  2671. std::string& status_str,
  2672. std::string& account_handle,
  2673. S32 aliases_number)
  2674. {
  2675. LL_DEBUGS("Voice") << "Account.Login response (" << status_code << "): "
  2676. << status_str << " - Handle: " << account_handle
  2677. << LL_ENDL;
  2678. // Status code of 20200 means "bad password". We may want to special-case
  2679. // that at some point.
  2680. if (status_code == HTTP_UNAUTHORIZED)
  2681. {
  2682. // Login failure which is probably caused by the delay after a user's
  2683. // password being updated.
  2684. llinfos << "Account.Login response failure (" << status_code << "): "
  2685. << status_str << llendl;
  2686. setState(stateLoginRetry);
  2687. }
  2688. else if (status_code)
  2689. {
  2690. llwarns << "Account.Login response failure (" << status_code << "): "
  2691. << status_str << llendl;
  2692. setState(stateLoginFailed);
  2693. }
  2694. else
  2695. {
  2696. // Login succeeded, move forward.
  2697. mAccountLoggedIn = true;
  2698. mAccountHandle = account_handle;
  2699. mNumberOfAliases = aliases_number;
  2700. llinfos << "Account.Login succeeded. Account handle: "
  2701. << account_handle << llendl;
  2702. #if 0 // This needs to wait until the AccountLoginStateChangeEvent is received.
  2703. if (mState == stateLoggingIn)
  2704. {
  2705. setState(stateLoggedIn);
  2706. }
  2707. #endif
  2708. }
  2709. }
  2710. void LLVoiceVivox::sessionCreateResponse(std::string& request_id,
  2711. S32 status_code,
  2712. std::string& status_str,
  2713. std::string& session_handle)
  2714. {
  2715. llinfos << "Got Session.Create response for request Id: " << request_id
  2716. << " - Session handle " << session_handle << llendl;
  2717. sessionState* sessionp = findSessionBeingCreatedByURI(request_id);
  2718. if (sessionp)
  2719. {
  2720. LL_DEBUGS("Voice") << "Found session, marking as creation in progress."
  2721. << LL_ENDL;
  2722. sessionp->mCreateInProgress = false;
  2723. }
  2724. // Do not re-join a spatial session we just left after changing for a non
  2725. // voice parcel. HB
  2726. if (sessionp && sessionp->mIsSpatial &&
  2727. !gViewerParcelMgr.allowAgentVoice())
  2728. {
  2729. // This will be set back to true by LLVoiceClient::onParcelChange() on
  2730. // arrival in a voice-enabled parcel. HB
  2731. mProcessChannels = false;
  2732. return;
  2733. }
  2734. if (status_code)
  2735. {
  2736. llwarns << "Failure (" << status_code << "): " << status_str << llendl;
  2737. if (sessionp)
  2738. {
  2739. sessionp->mErrorStatusCode = status_code;
  2740. sessionp->mErrorStatusString = status_str;
  2741. if (sessionp == mAudioSession)
  2742. {
  2743. setState(stateJoinSessionFailed);
  2744. }
  2745. else
  2746. {
  2747. reapSession(sessionp);
  2748. }
  2749. }
  2750. }
  2751. else
  2752. {
  2753. llinfos << "Session successfully created." << llendl;
  2754. if (sessionp)
  2755. {
  2756. setSessionHandle(sessionp, session_handle);
  2757. }
  2758. }
  2759. }
  2760. void LLVoiceVivox::sessionGroupAddSessionResponse(std::string& request_id,
  2761. S32 status_code,
  2762. std::string& status_str,
  2763. std::string& session_handle)
  2764. {
  2765. sessionState* sessionp = findSessionBeingCreatedByURI(request_id);
  2766. if (sessionp)
  2767. {
  2768. sessionp->mCreateInProgress = false;
  2769. }
  2770. if (status_code)
  2771. {
  2772. llwarns << "SessionGroup.AddSession response failure (" << status_code
  2773. << "): " << status_str << " - Session handle "
  2774. << session_handle << llendl;
  2775. if (sessionp)
  2776. {
  2777. sessionp->mErrorStatusCode = status_code;
  2778. sessionp->mErrorStatusString = status_str;
  2779. if (sessionp == mAudioSession)
  2780. {
  2781. setState(stateJoinSessionFailed);
  2782. }
  2783. else
  2784. {
  2785. reapSession(sessionp);
  2786. }
  2787. }
  2788. }
  2789. else
  2790. {
  2791. LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle: "
  2792. << session_handle << LL_ENDL;
  2793. if (sessionp)
  2794. {
  2795. setSessionHandle(sessionp, session_handle);
  2796. }
  2797. }
  2798. }
  2799. void LLVoiceVivox::sessionConnectResponse(std::string& request_id,
  2800. S32 status_code,
  2801. std::string& status_str)
  2802. {
  2803. sessionState* sessionp = findSession(request_id);
  2804. if (status_code)
  2805. {
  2806. llwarns << "Session.Connect response failure (" << status_code << "): "
  2807. << status_str << llendl;
  2808. if (sessionp)
  2809. {
  2810. sessionp->mMediaConnectInProgress = false;
  2811. sessionp->mErrorStatusCode = status_code;
  2812. sessionp->mErrorStatusString = status_str;
  2813. if (sessionp == mAudioSession)
  2814. {
  2815. setState(stateJoinSessionFailed);
  2816. }
  2817. }
  2818. }
  2819. else
  2820. {
  2821. LL_DEBUGS("Voice") << "Session.Connect response received (success)"
  2822. << LL_ENDL;
  2823. }
  2824. }
  2825. void LLVoiceVivox::logoutResponse(S32 status_code, std::string& status_str)
  2826. {
  2827. if (status_code)
  2828. {
  2829. llwarns << "Account.Logout response failure: " << status_str
  2830. << llendl;
  2831. // Should this ever fail ? Do we care if it does ?
  2832. }
  2833. }
  2834. void LLVoiceVivox::connectorShutdownResponse(S32 status_code,
  2835. std::string& status_str)
  2836. {
  2837. if (status_code)
  2838. {
  2839. llwarns << "Connector.InitiateShutdown response failure: "
  2840. << status_str << llendl;
  2841. // Should this ever fail ? Do we care if it does ?
  2842. }
  2843. mConnected = false;
  2844. if (mState == stateConnectorStopping)
  2845. {
  2846. setState(stateConnectorStopped);
  2847. }
  2848. }
  2849. void LLVoiceVivox::sessionAddedEvent(std::string& uri_str, std::string& alias,
  2850. std::string& session_handle,
  2851. std::string& session_grp_handle,
  2852. bool is_channel, bool incoming,
  2853. std::string& name_str)
  2854. {
  2855. sessionState* sessionp = NULL;
  2856. llinfos << "Session: " << uri_str << " - Alias: " << alias << " - Name: "
  2857. << name_str << " - Session handle: " << session_handle
  2858. << " - Group handle: " << session_grp_handle << llendl;
  2859. sessionp = addSession(uri_str, session_handle);
  2860. if (sessionp)
  2861. {
  2862. sessionp->mGroupHandle = session_grp_handle;
  2863. sessionp->mIsChannel = is_channel;
  2864. sessionp->mIncoming = incoming;
  2865. sessionp->mAlias = alias;
  2866. // Generate a caller UUID: we do not need to do this for channels
  2867. if (!sessionp->mIsChannel)
  2868. {
  2869. if (IDFromName(sessionp->mSIPURI, sessionp->mCallerID))
  2870. {
  2871. // Normal URI(base64-encoded UUID)
  2872. }
  2873. else if (!sessionp->mAlias.empty() &&
  2874. IDFromName(sessionp->mAlias, sessionp->mCallerID))
  2875. {
  2876. // Wrong URI, but an alias is available. Stash the incoming URI
  2877. // as an alternate
  2878. sessionp->mAlternateSIPURI = sessionp->mSIPURI;
  2879. // And generate a proper URI from the ID.
  2880. setSessionURI(sessionp, sipURIFromID(sessionp->mCallerID));
  2881. }
  2882. else
  2883. {
  2884. llinfos << "Could not generate caller id from uri, using hash of URI "
  2885. << sessionp->mSIPURI << llendl;
  2886. sessionp->mCallerID.generate(sessionp->mSIPURI);
  2887. sessionp->mSynthesizedCallerID = true;
  2888. // Cannot look up the name in this case: we have to extract it
  2889. // from the URI.
  2890. std::string name_portion = nameFromsipURI(sessionp->mSIPURI);
  2891. if (name_portion.empty())
  2892. {
  2893. // Did not seem to be a SIP URI, just use the whole
  2894. // provided name.
  2895. name_portion = name_str;
  2896. }
  2897. // Some incoming names may be separated with an underscore
  2898. // instead of a space. Fix this.
  2899. LLStringUtil::replaceChar(name_portion, '_', ' ');
  2900. // Act like we just finished resolving the name (this stores it
  2901. // in all the right places)
  2902. avatarNameResolved(sessionp->mCallerID, name_portion);
  2903. }
  2904. llinfos << "Caller Id: " << sessionp->mCallerID << llendl;
  2905. if (!sessionp->mSynthesizedCallerID)
  2906. {
  2907. // If we got here, we do not have a proper name. Initiate a
  2908. // lookup.
  2909. lookupName(sessionp->mCallerID);
  2910. }
  2911. }
  2912. }
  2913. }
  2914. void LLVoiceVivox::joinedAudioSession(sessionState* sessionp)
  2915. {
  2916. if (mAudioSession != sessionp)
  2917. {
  2918. sessionState* old_sessionp = mAudioSession;
  2919. mAudioSession = sessionp;
  2920. // The old session may now need to be deleted.
  2921. reapSession(old_sessionp);
  2922. }
  2923. // If we are not in the process of joining a session, bail out.
  2924. if (mState != stateJoiningSession)
  2925. {
  2926. return;
  2927. }
  2928. setState(stateSessionJoined);
  2929. // SLIM SDK: we do not always receive a participant state change for
  2930. // ourselves when joining a channel now. Add the current user as a
  2931. // participant here.
  2932. participantState* participantp =
  2933. sessionp->addParticipant(sipURIFromName(mAccountName));
  2934. if (participantp)
  2935. {
  2936. participantp->mIsSelf = true;
  2937. lookupName(participantp->mAvatarID);
  2938. llinfos << "Added self as participant \""
  2939. << participantp->mAccountName << "\" ("
  2940. << participantp->mAvatarID << ")" << llendl;
  2941. }
  2942. if (!sessionp->mIsChannel)
  2943. {
  2944. // This is a P2P session. Make sure the other end is added as a
  2945. // participant.
  2946. participantp = sessionp->addParticipant(sessionp->mSIPURI);
  2947. if (participantp)
  2948. {
  2949. if (participantp->mAvatarIDValid)
  2950. {
  2951. lookupName(participantp->mAvatarID);
  2952. }
  2953. else if (!sessionp->mName.empty())
  2954. {
  2955. participantp->mLegacyName = sessionp->mName;
  2956. avatarNameResolved(participantp->mAvatarID, sessionp->mName);
  2957. }
  2958. // *TODO: do we need to set up mAvatarID/mAvatarIDValid here ?
  2959. llinfos << "Added caller as participant \""
  2960. << participantp->mAccountName << "\" ("
  2961. << participantp->mAvatarID << ")" << llendl;
  2962. }
  2963. }
  2964. }
  2965. void LLVoiceVivox::sessionRemovedEvent(std::string& session_handle,
  2966. std::string& session_grp_handle)
  2967. {
  2968. sessionState* sessionp = findSession(session_handle);
  2969. if (!sessionp)
  2970. {
  2971. llwarns << "Unknown session " << session_handle << " removed"
  2972. << llendl;
  2973. return;
  2974. }
  2975. leftAudioSession(sessionp);
  2976. // This message invalidates the session's handle. Set it to empty.
  2977. setSessionHandle(sessionp);
  2978. // This also means that the session's session group is now empty.
  2979. // Terminate the session group so it does not leak.
  2980. sessionGroupTerminateSendMessage(sessionp);
  2981. // Conditionally delete the session
  2982. reapSession(sessionp);
  2983. llinfos << "Removed session. Session handle: " << session_handle
  2984. << " - Group handle: " << session_grp_handle << llendl;
  2985. }
  2986. void LLVoiceVivox::reapSession(sessionState* sessionp)
  2987. {
  2988. if (!sessionp)
  2989. {
  2990. return;
  2991. }
  2992. if (!sessionp->mHandle.empty())
  2993. {
  2994. LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
  2995. << " (non-null session handle)" << LL_ENDL;
  2996. }
  2997. else if (sessionp->mCreateInProgress)
  2998. {
  2999. LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
  3000. << " (create in progress)" << LL_ENDL;
  3001. }
  3002. else if (sessionp->mMediaConnectInProgress)
  3003. {
  3004. LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
  3005. << " (connect in progress)" << LL_ENDL;
  3006. }
  3007. else if (sessionp == mAudioSession)
  3008. {
  3009. LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
  3010. << " (it is the current session)" << LL_ENDL;
  3011. }
  3012. else if (sessionp == mNextAudioSession)
  3013. {
  3014. LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
  3015. << " (it is the next session)" << LL_ENDL;
  3016. }
  3017. else
  3018. {
  3019. // *TODO: should we check for queued text messages here ?
  3020. // We do not have a reason to keep tracking this session, so just
  3021. // delete it.
  3022. LL_DEBUGS("Voice") << "Deleting session " << sessionp->mSIPURI
  3023. << LL_ENDL;
  3024. deleteSession(sessionp);
  3025. }
  3026. }
  3027. // Returns true if the session seems to indicate we have moved to a region on a
  3028. // different voice server
  3029. bool LLVoiceVivox::sessionNeedsRelog(sessionState* sessionp)
  3030. {
  3031. // Only make this check for spatial channels (so it would not happen for
  3032. // group or P2P calls)
  3033. if (sessionp && sessionp->mIsSpatial)
  3034. {
  3035. size_t i = sessionp->mSIPURI.find("@");
  3036. if (i != std::string::npos)
  3037. {
  3038. std::string urihost = sessionp->mSIPURI.substr(i + 1);
  3039. if (stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
  3040. {
  3041. // The hostname in this URI is different from what we expect.
  3042. // This probably means we need to relog.
  3043. // We could make a ProvisionVoiceAccountRequest and compare the
  3044. // result with the current values of mVoiceSIPURIHostName and
  3045. // mVoiceAccountServerURI to be really sure, but this is a
  3046. // pretty good indicator.
  3047. return true;
  3048. }
  3049. }
  3050. }
  3051. return false;
  3052. }
  3053. void LLVoiceVivox::leftAudioSession(sessionState* sessionp)
  3054. {
  3055. if (mAudioSession == sessionp)
  3056. {
  3057. switch (mState)
  3058. {
  3059. case stateJoiningSession:
  3060. case stateSessionJoined:
  3061. case stateRunning:
  3062. case stateLeavingSession:
  3063. case stateJoinSessionFailed:
  3064. case stateJoinSessionFailedWaiting:
  3065. // Normal transition
  3066. LL_DEBUGS("Voice") << "Left session " << sessionp->mHandle
  3067. << " in state " << state2string(mState)
  3068. << LL_ENDL;
  3069. setState(stateSessionTerminated);
  3070. break;
  3071. case stateSessionTerminated:
  3072. // This will happen sometimes -- there are cases where we send
  3073. // the terminate and then go straight to this state.
  3074. llwarns << "Left session " << sessionp->mHandle << " in state "
  3075. << state2string(mState) << llendl;
  3076. break;
  3077. default:
  3078. llwarns << "Unexpected SessionStateChangeEvent (left session) in state "
  3079. << state2string(mState) << llendl;
  3080. setState(stateSessionTerminated);
  3081. }
  3082. }
  3083. }
  3084. void LLVoiceVivox::accountLoginStateChangeEvent(std::string& account_handle,
  3085. S32 status_code,
  3086. std::string& status_str,
  3087. S32 state)
  3088. {
  3089. LL_DEBUGS("Voice") << "State is " << state << " - Handle: "
  3090. << account_handle << LL_ENDL;
  3091. /*
  3092. According to Mike S., status codes for this event are:
  3093. login_state_logged_out=0,
  3094. login_state_logged_in = 1,
  3095. login_state_logging_in = 2,
  3096. login_state_logging_out = 3,
  3097. login_state_resetting = 4,
  3098. login_state_error=100
  3099. */
  3100. switch (state)
  3101. {
  3102. case 0:
  3103. // The user has been logged out.
  3104. setState(stateLoggedOut);
  3105. break;
  3106. case 1:
  3107. if (mState == stateLoggingIn)
  3108. {
  3109. setState(stateLoggedIn);
  3110. }
  3111. break;
  3112. case 3:
  3113. // The user is in the process of logging out.
  3114. setState(stateLoggingOut);
  3115. break;
  3116. default:
  3117. // Used to be a commented out warning
  3118. LL_DEBUGS("Voice") << "Unknown state: " << state << LL_ENDL;
  3119. }
  3120. }
  3121. void LLVoiceVivox::mediaStreamUpdatedEvent(std::string& session_handle,
  3122. std::string& session_grp_handle,
  3123. S32 status_code,
  3124. std::string& status_str,
  3125. S32 state, bool incoming)
  3126. {
  3127. sessionState* sessionp = findSession(session_handle);
  3128. LL_DEBUGS("Voice") << "session " << session_handle << ", status code "
  3129. << status_code << ", string \"" << status_str << "\""
  3130. << LL_ENDL;
  3131. if (sessionp) // If we know about this session
  3132. {
  3133. switch (status_code)
  3134. {
  3135. case 0:
  3136. case HTTP_OK:
  3137. // Generic success: do not change the saved error code (it may
  3138. // have been set elsewhere).
  3139. break;
  3140. default:
  3141. // Save the status code for later
  3142. sessionp->mErrorStatusCode = status_code;
  3143. }
  3144. switch (state)
  3145. {
  3146. case streamStateIdle:
  3147. case streamStateDisconnecting:
  3148. // Standard "left audio session"
  3149. sessionp->mVoiceEnabled = false;
  3150. sessionp->mMediaConnectInProgress = false;
  3151. leftAudioSession(sessionp);
  3152. break;
  3153. case streamStateConnecting: // Nothing to do
  3154. break;
  3155. case streamStateConnected:
  3156. sessionp->mVoiceEnabled = true;
  3157. sessionp->mMediaConnectInProgress = false;
  3158. joinedAudioSession(sessionp);
  3159. break;
  3160. case streamStateRinging:
  3161. if (incoming)
  3162. {
  3163. // Send the voice chat invite to the GUI layer
  3164. // *TODO: should we correlate with the mute list here ?
  3165. sessionp->mIMSessionID =
  3166. LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE,
  3167. sessionp->mCallerID);
  3168. sessionp->mVoiceInvitePending = true;
  3169. if (sessionp->mName.empty())
  3170. {
  3171. lookupName(sessionp->mCallerID);
  3172. }
  3173. else
  3174. {
  3175. // Act like we just finished resolving the name
  3176. avatarNameResolved(sessionp->mCallerID,
  3177. sessionp->mName);
  3178. }
  3179. }
  3180. break;
  3181. default:
  3182. llwarns << "Unknown state " << state << llendl;
  3183. }
  3184. }
  3185. else
  3186. {
  3187. llwarns << "Session " << session_handle << " not found !" << llendl;
  3188. }
  3189. }
  3190. void LLVoiceVivox::participantAddedEvent(std::string& session_handle,
  3191. std::string& session_grp_handle,
  3192. std::string& uri_str,
  3193. std::string& alias,
  3194. std::string& name_str,
  3195. std::string& display_name_str,
  3196. S32 participant_type)
  3197. {
  3198. sessionState* sessionp = findSession(session_handle);
  3199. if (sessionp)
  3200. {
  3201. participantState* participant = sessionp->addParticipant(uri_str);
  3202. if (participant)
  3203. {
  3204. participant->mAccountName = name_str;
  3205. LL_DEBUGS("Voice") << "Added participant \""
  3206. << participant->mAccountName << "\" ("
  3207. << participant->mAvatarID << ")" << LL_ENDL;
  3208. if (participant->mAvatarIDValid)
  3209. {
  3210. // Initiate a lookup
  3211. lookupName(participant->mAvatarID);
  3212. }
  3213. else
  3214. {
  3215. // If we do not have a valid avatar UUID, we need to fill in
  3216. // the display name to make the active speakers floater work.
  3217. std::string name_portion = nameFromsipURI(uri_str);
  3218. if (name_portion.empty())
  3219. {
  3220. // Problem with the SIP URI, fall back to the display name
  3221. name_portion = display_name_str;
  3222. }
  3223. if (name_portion.empty())
  3224. {
  3225. // Problems with both of the above, fall back to the
  3226. // account name
  3227. name_portion = name_str;
  3228. }
  3229. // Set the display name (which is a hint to the active speakers
  3230. // window not to do its own lookup)
  3231. participant->mLegacyName = name_portion;
  3232. avatarNameResolved(participant->mAvatarID, name_portion);
  3233. }
  3234. }
  3235. }
  3236. }
  3237. void LLVoiceVivox::participantRemovedEvent(std::string& session_handle,
  3238. std::string& session_grp_handle,
  3239. std::string& uri_str,
  3240. std::string& alias,
  3241. std::string& name_str)
  3242. {
  3243. sessionState* sessionp = findSession(session_handle);
  3244. if (sessionp)
  3245. {
  3246. participantState* participantp = sessionp->findParticipant(uri_str);
  3247. if (participantp)
  3248. {
  3249. sessionp->removeParticipant(participantp);
  3250. }
  3251. else
  3252. {
  3253. LL_DEBUGS("Voice") << "Unknown participant " << uri_str << LL_ENDL;
  3254. }
  3255. }
  3256. else
  3257. {
  3258. LL_DEBUGS("Voice") << "Unknown session " << session_handle << LL_ENDL;
  3259. }
  3260. }
  3261. void LLVoiceVivox::participantUpdatedEvent(std::string& session_handle,
  3262. std::string& session_grp_handle,
  3263. std::string& uri_str,
  3264. std::string& alias,
  3265. bool muted_by_moderator,
  3266. bool speaking, S32 volume,
  3267. F32 energy)
  3268. {
  3269. sessionState* sessionp = findSession(session_handle);
  3270. if (sessionp)
  3271. {
  3272. participantState* participantp = sessionp->findParticipant(uri_str);
  3273. if (participantp)
  3274. {
  3275. participantp->mIsSpeaking = speaking;
  3276. participantp->mIsModeratorMuted = muted_by_moderator;
  3277. // SLIM SDK: convert range: ensure that energy is set to zero if
  3278. // is_speaking is false
  3279. if (speaking)
  3280. {
  3281. participantp->mSpeakingTimeout.reset();
  3282. participantp->mPower = energy;
  3283. }
  3284. else
  3285. {
  3286. participantp->mPower = 0.f;
  3287. }
  3288. participantp->mVolume = volume;
  3289. }
  3290. else
  3291. {
  3292. llwarns << "Unknown participant: " << uri_str << llendl;
  3293. }
  3294. }
  3295. else
  3296. {
  3297. llinfos << "Unknown session " << session_handle << llendl;
  3298. }
  3299. }
  3300. void LLVoiceVivox::messageEvent(std::string& session_handle,
  3301. std::string& uri_str, std::string& alias,
  3302. std::string& msg_header,
  3303. std::string& msg_body)
  3304. {
  3305. LL_DEBUGS("Voice") << "Message event, session " << session_handle
  3306. << " from " << uri_str << LL_ENDL;
  3307. size_t start, end;
  3308. if (msg_header.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos)
  3309. {
  3310. static const std::string STARTMARKER = "<body";
  3311. static const std::string STARTMARKER2 = ">";
  3312. static const std::string ENDMARKER = "</body>";
  3313. static const std::string STARTSPAN = "<span";
  3314. static const std::string ENDSPAN = "</span>";
  3315. // Default to displaying the raw string, so the message gets through.
  3316. std::string raw_msg = msg_body;
  3317. // Find the actual message text within the XML fragment
  3318. start = msg_body.find(STARTMARKER);
  3319. start = msg_body.find(STARTMARKER2, start);
  3320. end = msg_body.find(ENDMARKER);
  3321. if (start != std::string::npos)
  3322. {
  3323. start += STARTMARKER2.size();
  3324. if (end != std::string::npos)
  3325. {
  3326. end -= start;
  3327. }
  3328. raw_msg.assign(msg_body, start, end);
  3329. }
  3330. else
  3331. {
  3332. // Did not find a <body>, try looking for a <span> instead.
  3333. start = msg_body.find(STARTSPAN);
  3334. start = msg_body.find(STARTMARKER2, start);
  3335. end = msg_body.find(ENDSPAN);
  3336. if (start != std::string::npos)
  3337. {
  3338. start += STARTMARKER2.size();
  3339. if (end != std::string::npos)
  3340. {
  3341. end -= start;
  3342. }
  3343. raw_msg.assign(msg_body, start, end);
  3344. }
  3345. }
  3346. // Strip formatting tags
  3347. while ((start = raw_msg.find('<')) != std::string::npos)
  3348. {
  3349. if ((end = raw_msg.find('>', start + 1)) != std::string::npos)
  3350. {
  3351. // Strip out the tag
  3352. raw_msg.erase(start, (end + 1) - start);
  3353. }
  3354. else
  3355. {
  3356. // Avoid an infinite loop
  3357. break;
  3358. }
  3359. }
  3360. // Decode ampersand-escaped chars
  3361. // The text may contain text encoded with &lt;, &gt;, and &amp;
  3362. size_t mark = 0;
  3363. while ((mark = raw_msg.find("&lt;", mark)) != std::string::npos)
  3364. {
  3365. raw_msg.replace(mark++, 4, "<");
  3366. }
  3367. mark = 0;
  3368. while ((mark = raw_msg.find("&gt;", mark)) != std::string::npos)
  3369. {
  3370. raw_msg.replace(mark++, 4, ">");
  3371. }
  3372. mark = 0;
  3373. while ((mark = raw_msg.find("&amp;", mark)) != std::string::npos)
  3374. {
  3375. raw_msg.replace(mark++, 5, "&");
  3376. }
  3377. // Strip leading/trailing whitespace (since we always seem to get a
  3378. // couple newlines)
  3379. LLStringUtil::trim(raw_msg);
  3380. sessionState* session = findSession(session_handle);
  3381. if (session)
  3382. {
  3383. bool is_busy = gAgent.getBusy();
  3384. bool is_muted = LLMuteList::isMuted(session->mCallerID,
  3385. session->mName,
  3386. LLMute::flagTextChat,
  3387. LLMute::AGENT);
  3388. bool is_linden = LLMuteList::isLinden(session->mName);
  3389. bool quiet_chat = false;
  3390. LLChat chat;
  3391. chat.mMuted = is_muted && !is_linden;
  3392. if (!chat.mMuted)
  3393. {
  3394. chat.mFromID = session->mCallerID;
  3395. chat.mFromName = session->mName;
  3396. chat.mSourceType = CHAT_SOURCE_AGENT;
  3397. if (is_busy && !is_linden)
  3398. {
  3399. quiet_chat = true;
  3400. // *TODO: Return busy mode response here ? Or maybe when
  3401. // session is started instead ?
  3402. }
  3403. LL_DEBUGS("Voice") << "Adding message, name " << session->mName
  3404. << ", session " << session->mIMSessionID
  3405. << ", target " << session->mCallerID
  3406. << LL_ENDL;
  3407. std::string full_msg = ": " + raw_msg;
  3408. if (gIMMgrp)
  3409. {
  3410. gIMMgrp->addMessage(session->mIMSessionID,
  3411. session->mCallerID,
  3412. session->mName.c_str(),
  3413. full_msg.c_str(),
  3414. LLStringUtil::null, IM_NOTHING_SPECIAL,
  3415. 0, LLUUID::null, LLVector3::zero,
  3416. // Prepend name and make it a link to
  3417. // the user's profile
  3418. true);
  3419. }
  3420. chat.mText = "IM: " + session->mName + full_msg;
  3421. // If the chat should come in quietly (i.e. we are in busy
  3422. // mode), pretend it is from a local agent.
  3423. LLFloaterChat::addChat(chat, true, quiet_chat);
  3424. }
  3425. }
  3426. }
  3427. }
  3428. void LLVoiceVivox::sessionNotificationEvent(std::string& session_handle,
  3429. std::string& uri_str,
  3430. std::string& notif_type)
  3431. {
  3432. sessionState* session = findSession(session_handle);
  3433. if (!session)
  3434. {
  3435. LL_DEBUGS("Voice") << "Unknown session handle " << session_handle
  3436. << LL_ENDL;
  3437. return;
  3438. }
  3439. participantState* participant = session->findParticipant(uri_str);
  3440. if (participant)
  3441. {
  3442. if (!stricmp(notif_type.c_str(), "Typing"))
  3443. {
  3444. LL_DEBUGS("Voice") << "Participant " << uri_str << " in session "
  3445. << session->mSIPURI << " starts typing."
  3446. << LL_ENDL;
  3447. }
  3448. else if (!stricmp(notif_type.c_str(), "NotTyping"))
  3449. {
  3450. LL_DEBUGS("Voice") << "Participant " << uri_str << " in session "
  3451. << session->mSIPURI << " stops typing."
  3452. << LL_ENDL;
  3453. }
  3454. else
  3455. {
  3456. LL_DEBUGS("Voice") << "Unknown notification type "
  3457. << notif_type << "for participant "
  3458. << uri_str << " in session "
  3459. << session->mSIPURI << LL_ENDL;
  3460. }
  3461. }
  3462. else
  3463. {
  3464. LL_DEBUGS("Voice") << "Unknown participant " << uri_str
  3465. << " in session " << session->mSIPURI
  3466. << LL_ENDL;
  3467. }
  3468. }
  3469. // The user's mute list has been updated. This method goes through the current
  3470. // participants list and syncs it with the mute list.
  3471. void LLVoiceVivox::muteListChanged()
  3472. {
  3473. if (!mAudioSession)
  3474. {
  3475. return;
  3476. }
  3477. for (particip_map_t::iterator
  3478. it = mAudioSession->mParticipantsByURI.begin(),
  3479. end = mAudioSession->mParticipantsByURI.end();
  3480. it != end; ++it)
  3481. {
  3482. participantState* p = it->second;
  3483. if (p && p->updateMuteState())
  3484. {
  3485. mAudioSession->mVolumeDirty = true;
  3486. }
  3487. }
  3488. }
  3489. /////////////////////////////
  3490. // Managing list of participants
  3491. LLVoiceVivox::participantState::participantState(const std::string& uri)
  3492. : mURI(uri),
  3493. mPTT(false),
  3494. mIsSpeaking(false),
  3495. mIsModeratorMuted(false),
  3496. mLastSpokeTimestamp(0.f),
  3497. mPower(0.f),
  3498. mVolume(-1),
  3499. mOnMuteList(false),
  3500. mUserVolume(-1),
  3501. mVolumeDirty(false),
  3502. mAvatarIDValid(false),
  3503. mIsSelf(false)
  3504. {
  3505. }
  3506. LLVoiceVivox::participantState* LLVoiceVivox::sessionState::addParticipant(const std::string& uri)
  3507. {
  3508. participantState* result = NULL;
  3509. bool useAlternateURI = false;
  3510. // Note: this is mostly the body of
  3511. // LLVoiceVivox::sessionState::findParticipant(), but since we need to
  3512. // know if it matched the alternate SIP URI (so we can add it properly), we
  3513. // need to reproduce it here.
  3514. {
  3515. particip_map_t::iterator iter = mParticipantsByURI.find(&uri);
  3516. if (iter == mParticipantsByURI.end())
  3517. {
  3518. if (!mAlternateSIPURI.empty() && uri == mAlternateSIPURI)
  3519. {
  3520. // This is a P2P session (probably with the SLIM client) with
  3521. // an alternate URI for the other participant.
  3522. // Use mSIPURI instead, since it will be properly encoded.
  3523. iter = mParticipantsByURI.find(&(mSIPURI));
  3524. useAlternateURI = true;
  3525. }
  3526. }
  3527. if (iter != mParticipantsByURI.end())
  3528. {
  3529. result = iter->second;
  3530. }
  3531. }
  3532. if (!result)
  3533. {
  3534. // Participant is not already in one list or the other.
  3535. result = new participantState(useAlternateURI?mSIPURI:uri);
  3536. mParticipantsByURI.emplace(&(result->mURI), result);
  3537. // Try to do a reverse transform on the URI to get the GUID back.
  3538. LLUUID id;
  3539. if (IDFromName(result->mURI, id))
  3540. {
  3541. result->mAvatarIDValid = true;
  3542. result->mAvatarID = id;
  3543. if (result->updateMuteState())
  3544. {
  3545. mVolumeDirty = true;
  3546. }
  3547. }
  3548. else
  3549. {
  3550. // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
  3551. // This tells both code in LLVoiceVivox and code in
  3552. // llfloateractivespeakers.cpp that the ID will not be in the name
  3553. // cache.
  3554. result->mAvatarID.generate(uri);
  3555. }
  3556. mParticipantsByUUID.emplace(&(result->mAvatarID), result);
  3557. LL_DEBUGS("Voice") << "Participant \"" << result->mURI << "\" added."
  3558. << LL_ENDL;
  3559. }
  3560. return result;
  3561. }
  3562. bool LLVoiceVivox::participantState::updateMuteState()
  3563. {
  3564. bool result = false;
  3565. if (mAvatarIDValid)
  3566. {
  3567. bool muted = LLMuteList::isMuted(mAvatarID, LLMute::flagVoiceChat);
  3568. if (mOnMuteList != muted)
  3569. {
  3570. mOnMuteList = muted;
  3571. mVolumeDirty = true;
  3572. result = true;
  3573. }
  3574. }
  3575. return result;
  3576. }
  3577. void LLVoiceVivox::sessionState::removeParticipant(LLVoiceVivox::participantState* participant)
  3578. {
  3579. if (participant)
  3580. {
  3581. particip_map_t::iterator iter =
  3582. mParticipantsByURI.find(&(participant->mURI));
  3583. particip_id_map_t::iterator iter2 =
  3584. mParticipantsByUUID.find(&(participant->mAvatarID));
  3585. LL_DEBUGS("Voice") << "Participant \"" << participant->mURI
  3586. << "\" (" << participant->mAvatarID
  3587. << ") removed." << LL_ENDL;
  3588. if (iter == mParticipantsByURI.end())
  3589. {
  3590. llwarns << "Internal error: participant " << participant->mURI
  3591. << " not in URI map" << llendl;
  3592. gVoiceVivox.giveUp();
  3593. }
  3594. else if (iter2 == mParticipantsByUUID.end())
  3595. {
  3596. llwarns << "Internal error: participant ID "
  3597. << participant->mAvatarID << " not in UUID map" << llendl;
  3598. gVoiceVivox.giveUp();
  3599. }
  3600. else if (iter->second != iter2->second)
  3601. {
  3602. llwarns << "Internal error: participant mismatch !" << llendl;
  3603. gVoiceVivox.giveUp();
  3604. }
  3605. else
  3606. {
  3607. mParticipantsByURI.erase(iter);
  3608. mParticipantsByUUID.erase(iter2);
  3609. delete participant;
  3610. }
  3611. }
  3612. }
  3613. void LLVoiceVivox::sessionState::removeAllParticipants()
  3614. {
  3615. while (!mParticipantsByURI.empty())
  3616. {
  3617. removeParticipant(mParticipantsByURI.begin()->second);
  3618. }
  3619. if (!mParticipantsByUUID.empty())
  3620. {
  3621. llwarns << "Internal error: empty URI map, non-empty UUID map"
  3622. << llendl;
  3623. gVoiceVivox.giveUp();
  3624. }
  3625. }
  3626. LLVoiceVivox::particip_map_t* LLVoiceVivox::getParticipantList()
  3627. {
  3628. particip_map_t* result = NULL;
  3629. if (mAudioSession)
  3630. {
  3631. result = &(mAudioSession->mParticipantsByURI);
  3632. }
  3633. return result;
  3634. }
  3635. LLVoiceVivox::participantState* LLVoiceVivox::sessionState::findParticipant(const std::string& uri)
  3636. {
  3637. participantState* result = NULL;
  3638. particip_map_t::iterator iter = mParticipantsByURI.find(&uri);
  3639. if (iter == mParticipantsByURI.end())
  3640. {
  3641. if (!mAlternateSIPURI.empty() && uri == mAlternateSIPURI)
  3642. {
  3643. // This is a P2P session (probably with the SLIM client) with an
  3644. // alternate URI for the other participant.
  3645. // Look up the other URI
  3646. iter = mParticipantsByURI.find(&(mSIPURI));
  3647. }
  3648. }
  3649. if (iter != mParticipantsByURI.end())
  3650. {
  3651. result = iter->second;
  3652. }
  3653. return result;
  3654. }
  3655. LLVoiceVivox::participantState* LLVoiceVivox::sessionState::findParticipantByID(const LLUUID& id)
  3656. {
  3657. participantState* result = NULL;
  3658. particip_id_map_t::iterator iter = mParticipantsByUUID.find(&id);
  3659. if (iter != mParticipantsByUUID.end())
  3660. {
  3661. result = iter->second;
  3662. }
  3663. return result;
  3664. }
  3665. LLVoiceVivox::participantState* LLVoiceVivox::findParticipantByID(const LLUUID& id)
  3666. {
  3667. participantState* result = NULL;
  3668. if (mAudioSession)
  3669. {
  3670. result = mAudioSession->findParticipantByID(id);
  3671. }
  3672. return result;
  3673. }
  3674. void LLVoiceVivox::parcelChanged()
  3675. {
  3676. if (!gViewerParcelMgr.getAgentParcel())
  3677. {
  3678. llinfos << "Not logged in yet, deferring..." << llendl;
  3679. return;
  3680. }
  3681. if (!gViewerParcelMgr.allowAgentVoice())
  3682. {
  3683. return;
  3684. }
  3685. // If the user is logged in, start a channel lookup.
  3686. const std::string& url =
  3687. gAgent.getRegionCapability("ParcelVoiceInfoRequest");
  3688. if (url.empty())
  3689. {
  3690. LL_DEBUGS("Voice") << "No ParcelVoiceInfoRequest capability for agent region."
  3691. << LL_ENDL;
  3692. return;
  3693. }
  3694. LL_DEBUGS("Voice") << "Sending ParcelVoiceInfoRequest" << LL_ENDL;
  3695. gCoros.launch("LLVivoxVoiceClient::parcelVoiceInfoRequestCoro",
  3696. boost::bind(&LLVoiceVivox::parcelVoiceInfoRequestCoro,
  3697. url));
  3698. }
  3699. //static
  3700. void LLVoiceVivox::parcelVoiceInfoRequestCoro(const std::string& url)
  3701. {
  3702. LLCoreHttpUtil::HttpCoroutineAdapter adapter("parcelVoiceInfoRequest");
  3703. LLSD result = adapter.postAndSuspend(url, LLSD());
  3704. if (gVoiceVivox.mTerminated) return; // Voice has since been shut down
  3705. LL_DEBUGS("Voice") << "Received voice info reply..." << LL_ENDL;
  3706. LLCore::HttpStatus status =
  3707. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  3708. if (!status)
  3709. {
  3710. llwarns << "No voice on parcel: " << status.toString() << llendl;
  3711. gVoiceVivox.sessionTerminate();
  3712. return;
  3713. }
  3714. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  3715. if (result.has("voice_credentials"))
  3716. {
  3717. gVoiceVivox.setSpatialChannel(result["voice_credentials"]);
  3718. }
  3719. else
  3720. {
  3721. LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL;
  3722. }
  3723. }
  3724. void LLVoiceVivox::switchChannel(std::string uri, bool spatial,
  3725. bool no_reconnect, bool is_p2p,
  3726. std::string hash)
  3727. {
  3728. bool needs_switch = false;
  3729. LL_DEBUGS("Voice") << "Called in state " << state2string(mState)
  3730. << " with uri \"" << uri << "\", spatial is "
  3731. << (spatial ? "true" : "false")
  3732. << LL_ENDL;
  3733. switch (mState)
  3734. {
  3735. case stateJoinSessionFailed:
  3736. case stateJoinSessionFailedWaiting:
  3737. case stateNoChannel:
  3738. {
  3739. // Always switch to the new URI from these states.
  3740. needs_switch = true;
  3741. break;
  3742. }
  3743. default:
  3744. {
  3745. if (mSessionTerminateRequested)
  3746. {
  3747. // If a terminate has been requested, we need to compare
  3748. // against where the URI we are already headed to.
  3749. if (mNextAudioSession)
  3750. {
  3751. if (mNextAudioSession->mSIPURI != uri)
  3752. {
  3753. needs_switch = true;
  3754. }
  3755. }
  3756. else
  3757. {
  3758. // mNextAudioSession is NULL; this probably means we are
  3759. // on our way back to spatial.
  3760. if (!uri.empty())
  3761. {
  3762. // We do want to process a switch in this case.
  3763. needs_switch = true;
  3764. }
  3765. }
  3766. }
  3767. // Otherwise, compare against the URI we are in now.
  3768. else if (mAudioSession && mAudioSession->mSIPURI != uri)
  3769. {
  3770. needs_switch = true;
  3771. }
  3772. break;
  3773. }
  3774. }
  3775. if (!needs_switch)
  3776. {
  3777. return;
  3778. }
  3779. if (uri.empty())
  3780. {
  3781. // Leave any channel we may be in
  3782. LL_DEBUGS("Voice") << "Leaving channel" << LL_ENDL;
  3783. sessionState* old_sessionp = mNextAudioSession;
  3784. mNextAudioSession = NULL;
  3785. // The old session may now need to be deleted.
  3786. reapSession(old_sessionp);
  3787. // Make sure voice is turned off
  3788. gVoiceClient.mUserPTTState = false;
  3789. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
  3790. }
  3791. else
  3792. {
  3793. LL_DEBUGS("Voice") << "Switching to channel " << uri << LL_ENDL;
  3794. mNextAudioSession = addSession(uri);
  3795. mNextAudioSession->mHash = hash;
  3796. mNextAudioSession->mIsSpatial = spatial;
  3797. mNextAudioSession->mReconnect = !no_reconnect;
  3798. mNextAudioSession->mIsP2P = is_p2p;
  3799. }
  3800. if (mState > stateNoChannel)
  3801. {
  3802. // State machine will come around and rejoin if URI/handle is not empty
  3803. sessionTerminate();
  3804. }
  3805. }
  3806. void LLVoiceVivox::joinSession(sessionState* sessionp)
  3807. {
  3808. mNextAudioSession = sessionp;
  3809. if (mState > stateNoChannel)
  3810. {
  3811. // State machine will come around and rejoin if URI/handle is not empty
  3812. sessionTerminate();
  3813. }
  3814. }
  3815. //virtual
  3816. void LLVoiceVivox::setNonSpatialChannel(const LLSD& channel_info, bool, bool)
  3817. {
  3818. switchChannel(channel_info["channel_uri"].asString(), false, false, false,
  3819. channel_info["channel_credentials"].asString());
  3820. }
  3821. //virtual
  3822. void LLVoiceVivox::setSpatialChannel(const LLSD& channel_info)
  3823. {
  3824. if (channel_info.has("channel_uri"))
  3825. {
  3826. mSpatialSessionURI = channel_info["channel_uri"].asString();
  3827. LL_DEBUGS("Voice") << "Got spatial channel uri: " << mSpatialSessionURI
  3828. << LL_ENDL;
  3829. }
  3830. else
  3831. {
  3832. mSpatialSessionURI.clear();
  3833. LL_DEBUGS("Voice") << "No voice channel URI" << LL_ENDL;
  3834. }
  3835. if (channel_info.has("channel_credentials"))
  3836. {
  3837. mSpatialSessionCredentials =
  3838. channel_info["channel_credentials"].asString();
  3839. LL_DEBUGS("Voice") << "Got spatial session credentials." << LL_ENDL;
  3840. }
  3841. else
  3842. {
  3843. mSpatialSessionCredentials.clear();
  3844. LL_DEBUGS("Voice") << "No voice channel credentials" << LL_ENDL;
  3845. }
  3846. if (!mProcessChannels)
  3847. {
  3848. // We are not even processing channels (another provider is) so save
  3849. // the credentials aside and exit.
  3850. return;
  3851. }
  3852. if ((mAudioSession && !mAudioSession->mIsSpatial) ||
  3853. (mNextAudioSession && !mNextAudioSession->mIsSpatial))
  3854. {
  3855. // User is in a non-spatial chat or joining a non-spatial chat. Do not
  3856. // switch channels.
  3857. llinfos << "In non-spatial chat, not switching channels" << llendl;
  3858. }
  3859. else
  3860. {
  3861. switchChannel(mSpatialSessionURI, true, false, false,
  3862. mSpatialSessionCredentials);
  3863. }
  3864. }
  3865. void LLVoiceVivox::callUser(const LLUUID& id)
  3866. {
  3867. mProcessChannels = true;
  3868. switchChannel(sipURIFromID(id), false, true, true);
  3869. }
  3870. void LLVoiceVivox::leaveChannel()
  3871. {
  3872. if (mState >= stateNoChannel)
  3873. {
  3874. LL_DEBUGS("Voice") << "Leaving channel for teleport/logout" << LL_ENDL;
  3875. mChannelName.clear();
  3876. sessionTerminate();
  3877. }
  3878. }
  3879. // This is only ever used to answer incoming P2P call invites.
  3880. bool LLVoiceVivox::answerInvite(std::string& session_handle)
  3881. {
  3882. sessionState* sessionp = findSession(session_handle);
  3883. if (sessionp)
  3884. {
  3885. sessionp->mIsSpatial = false;
  3886. sessionp->mReconnect = false;
  3887. sessionp->mIsP2P = true;
  3888. mProcessChannels = true;
  3889. joinSession(sessionp);
  3890. return true;
  3891. }
  3892. return false;
  3893. }
  3894. void LLVoiceVivox::declineInvite(std::string& session_handle)
  3895. {
  3896. sessionState* sessionp = findSession(session_handle);
  3897. if (sessionp)
  3898. {
  3899. sessionGroupTerminateSendMessage(sessionp);
  3900. }
  3901. }
  3902. //virtual
  3903. void LLVoiceVivox::leaveNonSpatialChannel()
  3904. {
  3905. LL_DEBUGS("Voice") << "Called in state " << state2string(mState)
  3906. << LL_ENDL;
  3907. // Make sure we do not rejoin the current session.
  3908. sessionState* old_next_sessionp = mNextAudioSession;
  3909. mNextAudioSession = NULL;
  3910. // Most likely this will still be the current session at this point, but
  3911. // check it anyway.
  3912. reapSession(old_next_sessionp);
  3913. verifySessionState();
  3914. sessionTerminate();
  3915. }
  3916. //virtual
  3917. bool LLVoiceVivox::inProximalChannel() const
  3918. {
  3919. return !mSessionTerminateRequested && mState == stateRunning &&
  3920. inSpatialChannel();
  3921. }
  3922. //virtual
  3923. std::string LLVoiceVivox::sipURIFromID(const LLUUID& id) const
  3924. {
  3925. return "sip:" + nameFromID(id) + "@" + mVoiceSIPURIHostName;
  3926. }
  3927. // If you need to transform a GUID to this form on the Mac OS X command line,
  3928. // this will do so:
  3929. // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 | xxd -r -p | openssl base64 | tr '/+' '_-')
  3930. //
  3931. // The reverse transform can be done with:
  3932. // echo 'x5mkTKmxDTuGnjWyC__WfMg==' | cut -b 2- - | tr '_-' '/+' | openssl base64 -d | xxd -p
  3933. std::string LLVoiceVivox::nameFromID(const LLUUID& id)
  3934. {
  3935. if (id.isNull())
  3936. {
  3937. return "";
  3938. }
  3939. // Prepending this apparently prevents conflicts with reserved names inside
  3940. // the vivox and diamondware code.
  3941. std::string result = "x";
  3942. // Base64 encode and replace the pieces of base64 that are less compatible
  3943. // with e-mail local-parts.
  3944. // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
  3945. result += LLBase64::encode((const char*)id.mData, UUID_BYTES);
  3946. LLStringUtil::replaceChar(result, '+', '-');
  3947. LLStringUtil::replaceChar(result, '/', '_');
  3948. return result;
  3949. }
  3950. bool LLVoiceVivox::IDFromName(const std::string in_name, LLUUID& id)
  3951. {
  3952. bool result = false;
  3953. // SLIM SDK: The "name" may actually be a SIP URI such as:
  3954. // "sip:[email protected]"
  3955. // If it is, convert to a bare name before doing the transform.
  3956. std::string name = nameFromsipURI(in_name);
  3957. // Does not look like a SIP URI, assume it is an actual name.
  3958. if (name.empty())
  3959. {
  3960. name = in_name;
  3961. }
  3962. // This will only work if the name is of the proper form.
  3963. // As an example, the account name for Monroe Linden
  3964. // (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
  3965. // "xFnPP04IpREWNkuw1cOXlhw=="
  3966. if (name.size() == 25 && name[0] == 'x' && name[23] == '=' &&
  3967. name[24] == '=')
  3968. {
  3969. // The name appears to have the right form.
  3970. // Reverse the transforms done by nameFromID
  3971. std::string temp = name;
  3972. LLStringUtil::replaceChar(temp, '-', '+');
  3973. LLStringUtil::replaceChar(temp, '_', '/');
  3974. std::string buffer = LLBase64::decode(temp.c_str() + 1);
  3975. if (buffer.size() == (size_t)UUID_BYTES)
  3976. {
  3977. // The decode succeeded. Stuff the bits into the UUID
  3978. memcpy(id.mData, buffer.c_str(), UUID_BYTES);
  3979. result = true;
  3980. LL_DEBUGS("Voice") << "Decoded UUID: " << id << LL_ENDL;
  3981. }
  3982. else
  3983. {
  3984. llwarns << "Invalid UUID encoding" << llendl;
  3985. }
  3986. }
  3987. if (!result)
  3988. {
  3989. // VIVOX: not a standard account name, just copy the URI name
  3990. // mURIString field and hope for the best. bpj
  3991. id.setNull(); // VIVOX, set the uuid field to nulls
  3992. }
  3993. return result;
  3994. }
  3995. std::string LLVoiceVivox::sipURIFromName(std::string& name) const
  3996. {
  3997. return "sip:" + name + "@" + mVoiceSIPURIHostName;
  3998. }
  3999. std::string LLVoiceVivox::nameFromsipURI(const std::string& uri)
  4000. {
  4001. std::string result;
  4002. size_t sip_offset = uri.find("sip:");
  4003. size_t at_offset = uri.find("@");
  4004. if (sip_offset != std::string::npos && at_offset != std::string::npos)
  4005. {
  4006. result = uri.substr(sip_offset + 4, at_offset - sip_offset - 4);
  4007. }
  4008. return result;
  4009. }
  4010. bool LLVoiceVivox::inSpatialChannel() const
  4011. {
  4012. return mAudioSession && mAudioSession->mIsSpatial;
  4013. }
  4014. LLSD LLVoiceVivox::getAudioSessionChannelInfo() const
  4015. {
  4016. return mAudioSession ? mAudioSession->getVoiceChannelInfo() : LLSD();
  4017. }
  4018. std::string LLVoiceVivox::getAudioSessionHandle() const
  4019. {
  4020. return mAudioSession ? mAudioSession->mHandle : "";
  4021. }
  4022. // Because of the recurring voice cutout issues (SL-15072) we are going to try
  4023. // to disable the automatic VAD (Voice Activity Detection) and set the
  4024. // associated parameters directly. We will expose them via Debug Settings and
  4025. // that should let us iterate on a collection of values that work for us.
  4026. //
  4027. // From the VIVOX docs:
  4028. //
  4029. // VadAuto: flag to enable (1) or disable (0) automatic VAD.
  4030. //
  4031. // VadHangover: the time (in milliseconds) that it takes for the VAD to switch
  4032. // back to silence from speech mode after the last speech frame
  4033. // has been detected.
  4034. //
  4035. // VadNoiseFloor: dimensionless value between 0 and 20000 (default 576) that
  4036. // controls the maximum level at which the noise floor may be
  4037. // set at by the VAD's noise tracking. Too low of a value will
  4038. // make noise tracking ineffective (a value of 0 disables noise
  4039. // tracking and the VAD then relies purely on the sensitivity
  4040. // property). Too high of a value will make long speech
  4041. // classifiable as noise.
  4042. //
  4043. // VadSensitivity: dimensionless value between 0 and 100, indicating the
  4044. // 'sensitivity of the VAD'. Increasing this value corresponds
  4045. // to decreasing the sensitivity of the VAD (i.e. 0 is most
  4046. // sensitive, while 100 is least sensitive).
  4047. void LLVoiceVivox::setupVADParams()
  4048. {
  4049. #if LL_LINUX
  4050. if (mDeprecatedClient)
  4051. {
  4052. return;
  4053. }
  4054. #endif
  4055. U32 vad_auto = gSavedSettings.getBool("VivoxVadAuto") ? 1 : 0;
  4056. U32 vad_hangover = gSavedSettings.getU32("VivoxVadHangover");
  4057. U32 vad_noise_floor = gSavedSettings.getU32("VivoxVadNoiseFloor");
  4058. if (vad_noise_floor > 20000)
  4059. {
  4060. vad_noise_floor = 20000;
  4061. }
  4062. U32 vad_sensitivity = gSavedSettings.getU32("VivoxVadSensitivity");
  4063. if (vad_sensitivity > 100)
  4064. {
  4065. vad_sensitivity = 100;
  4066. }
  4067. if (vad_auto)
  4068. {
  4069. llinfos << "Enabling the automatic VAD." << llendl;
  4070. }
  4071. else
  4072. {
  4073. llinfos << "Disabling the automatic VAD. Setting fixed values: VadHangover = "
  4074. << vad_hangover << " - VadSensitivity = " << vad_sensitivity
  4075. << " - VadNoiseFloor = " << vad_noise_floor << llendl;
  4076. }
  4077. std::ostringstream stream;
  4078. stream << "<Request requestId=\"" << mCommandCookie++
  4079. << "\" action=\"Aux.SetVadProperties.1\">"
  4080. << "<VadAuto>" << vad_auto << "</VadAuto>"
  4081. << "<VadHangover>" << vad_hangover << "</VadHangover>"
  4082. << "<VadSensitivity>" << vad_sensitivity << "</VadSensitivity>"
  4083. << "<VadNoiseFloor>" << vad_noise_floor << "</VadNoiseFloor>"
  4084. << "</Request>\n\n\n";
  4085. writeString(stream.str());
  4086. }
  4087. /////////////////////////////
  4088. // Sending updates of current state
  4089. void LLVoiceVivox::enforceTether()
  4090. {
  4091. LLVector3d tethered = mCameraRequestedPosition;
  4092. // Constrain 'tethered' to within 50m of mAvatarPosition.
  4093. constexpr F32 max_dist = 50.f;
  4094. LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
  4095. F32 camera_distance = (F32)camera_offset.length();
  4096. if (camera_distance > max_dist)
  4097. {
  4098. tethered = mAvatarPosition +
  4099. (max_dist / camera_distance) * camera_offset;
  4100. }
  4101. if (dist_vec_squared(mCameraPosition, tethered) > 0.01)
  4102. {
  4103. mCameraPosition = tethered;
  4104. mSpatialCoordsDirty = true;
  4105. }
  4106. }
  4107. void LLVoiceVivox::updatePosition()
  4108. {
  4109. if (mTerminated || !mPump || !mProcessChannels)
  4110. {
  4111. return;
  4112. }
  4113. LLViewerRegion* regionp = gAgent.getRegion();
  4114. if (!regionp || !isAgentAvatarValid())
  4115. {
  4116. return;
  4117. }
  4118. // Send the current camera position to the voice code
  4119. LLMatrix3 rot;
  4120. rot.setRows(gViewerCamera.getAtAxis(), gViewerCamera.getLeftAxis(),
  4121. gViewerCamera.getUpAxis());
  4122. LLVector3d pos =
  4123. regionp->getPosGlobalFromRegion(gViewerCamera.getOrigin());
  4124. setCameraPosition(pos, LLVector3::zero, rot);
  4125. // Send the current avatar position to the voice code
  4126. rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3();
  4127. pos = gAgentAvatarp->getPositionGlobal();
  4128. pos.mdV[VZ] += 1.0; // Bump it up to head height
  4129. setAvatarPosition(pos, LLVector3::zero, rot);
  4130. }
  4131. void LLVoiceVivox::setCameraPosition(const LLVector3d& position,
  4132. const LLVector3& velocity,
  4133. const LLMatrix3& rot)
  4134. {
  4135. mCameraRequestedPosition = position;
  4136. if (mCameraVelocity != velocity)
  4137. {
  4138. mCameraVelocity = velocity;
  4139. mSpatialCoordsDirty = true;
  4140. }
  4141. if (mCameraRot != rot)
  4142. {
  4143. mCameraRot = rot;
  4144. mSpatialCoordsDirty = true;
  4145. }
  4146. }
  4147. void LLVoiceVivox::setAvatarPosition(const LLVector3d& position,
  4148. const LLVector3& velocity,
  4149. const LLMatrix3& rot)
  4150. {
  4151. if (dist_vec(mAvatarPosition, position) > 0.1)
  4152. {
  4153. mAvatarPosition = position;
  4154. mSpatialCoordsDirty = true;
  4155. }
  4156. if (mAvatarVelocity != velocity)
  4157. {
  4158. mAvatarVelocity = velocity;
  4159. mSpatialCoordsDirty = true;
  4160. }
  4161. if (mAvatarRot != rot)
  4162. {
  4163. mAvatarRot = rot;
  4164. mSpatialCoordsDirty = true;
  4165. }
  4166. }
  4167. bool LLVoiceVivox::isCurrentChannel(const LLSD& channel_info)
  4168. {
  4169. if (!mProcessChannels ||
  4170. (channel_info.has("voice_server_type") &&
  4171. channel_info["voice_server_type"].asString() != VIVOXSTR))
  4172. {
  4173. return false;
  4174. }
  4175. // Favor the next audio session, as this is the one we are bringing up.
  4176. sessionState* sessionp = mNextAudioSession;
  4177. if (!sessionp)
  4178. {
  4179. if (!mAudioSession)
  4180. {
  4181. return false;
  4182. }
  4183. sessionp = mAudioSession;
  4184. }
  4185. std::string handle = channel_info["session_handle"].asString();
  4186. if (!handle.empty())
  4187. {
  4188. return sessionp->mHandle == handle;
  4189. }
  4190. return channel_info["channel_uri"].asString() == mAudioSession->mSIPURI;
  4191. }
  4192. bool LLVoiceVivox::compareChannels(const LLSD& info1, const LLSD& info2)
  4193. {
  4194. return (!info1.has("voice_server_type") ||
  4195. info1["voice_server_type"].asString() == VIVOXSTR) &&
  4196. (!info2.has("voice_server_type") ||
  4197. info2["voice_server_type"].asString() == VIVOXSTR) &&
  4198. info1["channel_uri"].asString() == info2["channel_uri"].asString();
  4199. }
  4200. void LLVoiceVivox::setVoiceEnabled(bool enabled)
  4201. {
  4202. if (mVoiceEnabled == enabled)
  4203. {
  4204. return;
  4205. }
  4206. mVoiceEnabled = enabled;
  4207. LLVoiceClientStatusObserver::EStatusType status;
  4208. if (enabled)
  4209. {
  4210. LLVoiceChannel::getCurrentVoiceChannel()->activate();
  4211. status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
  4212. }
  4213. else
  4214. {
  4215. // Turning voice off looses your current channel: this makes sure the
  4216. // UI is not out of sync when you re-enable it.
  4217. LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
  4218. status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
  4219. mRetries = 0;
  4220. }
  4221. notifyStatusObservers(status);
  4222. }
  4223. //virtual
  4224. void LLVoiceVivox::processChannels(bool enabled)
  4225. {
  4226. if (mProcessChannels == enabled)
  4227. {
  4228. return;
  4229. }
  4230. mProcessChannels = enabled;
  4231. // Note: each time channels processing is disabled, the daemon is stopped,
  4232. // so we must reset this each time we restart the channels processing. HB
  4233. if (enabled)
  4234. {
  4235. mRetries = 0;
  4236. }
  4237. }
  4238. void LLVoiceVivox::setEarLocation(S32 loc)
  4239. {
  4240. if (mEarLocation != loc && loc >= 0 && loc <= (S32)earLocMixed)
  4241. {
  4242. LL_DEBUGS("Voice") << "Setting location to " << loc << LL_ENDL;
  4243. mEarLocation = loc;
  4244. mSpatialCoordsDirty = true;
  4245. }
  4246. }
  4247. void LLVoiceVivox::setVoiceVolume(F32 volume)
  4248. {
  4249. S32 scaled_volume = scale_speaker_volume(volume);
  4250. if (scaled_volume != mSpeakerVolume)
  4251. {
  4252. if (scaled_volume == 0 || mSpeakerVolume == 0)
  4253. {
  4254. mSpeakerMuteDirty = true;
  4255. }
  4256. mSpeakerVolume = scaled_volume;
  4257. mSpeakerVolumeDirty = true;
  4258. }
  4259. }
  4260. void LLVoiceVivox::setMicGain(F32 volume)
  4261. {
  4262. S32 scaled_volume = scale_mic_volume(volume);
  4263. if (scaled_volume != mMicVolume)
  4264. {
  4265. mMicVolume = scaled_volume;
  4266. mMicVolumeDirty = true;
  4267. }
  4268. }
  4269. bool LLVoiceVivox::isParticipant(const LLUUID& id)
  4270. {
  4271. return mAudioSession && findParticipantByID(id) != NULL;
  4272. }
  4273. bool LLVoiceVivox::getIsSpeaking(const LLUUID& id)
  4274. {
  4275. if (!mProcessChannels)
  4276. {
  4277. return false;
  4278. }
  4279. constexpr F32 SPEAKING_TIMEOUT = 1.f;
  4280. participantState* participant = findParticipantByID(id);
  4281. if (participant)
  4282. {
  4283. if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
  4284. {
  4285. participant->mIsSpeaking = false;
  4286. }
  4287. return participant->mIsSpeaking;
  4288. }
  4289. return false;
  4290. }
  4291. bool LLVoiceVivox::getIsModeratorMuted(const LLUUID& id)
  4292. {
  4293. if (!mProcessChannels)
  4294. {
  4295. return false;
  4296. }
  4297. participantState* participantp = findParticipantByID(id);
  4298. return participantp && participantp->mIsModeratorMuted;
  4299. }
  4300. F32 LLVoiceVivox::getCurrentPower(const LLUUID& id)
  4301. {
  4302. participantState* participantp = findParticipantByID(id);
  4303. return participantp ? participantp->mPower : -1.f;
  4304. }
  4305. // External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
  4306. // internal = 400 * external^2
  4307. F32 LLVoiceVivox::getUserVolume(const LLUUID& id)
  4308. {
  4309. F32 result = -1.f; // Returned when user is not a participant.
  4310. participantState* participantp = findParticipantByID(id);
  4311. if (participantp)
  4312. {
  4313. S32 ires = 100; // Nominal default volume
  4314. if (participantp->mIsSelf)
  4315. {
  4316. // Always make it look like the user's own volume is set at the
  4317. // default.
  4318. }
  4319. else if (participantp->mUserVolume != -1)
  4320. {
  4321. // Use the internal volume
  4322. ires = participantp->mUserVolume;
  4323. }
  4324. else if (participantp->mVolume != -1)
  4325. {
  4326. // Map backwards from vivox volume
  4327. if (participantp->mVolume < 56)
  4328. {
  4329. ires = (participantp->mVolume * 100) / 56;
  4330. }
  4331. else
  4332. {
  4333. ires = 300 * (participantp->mVolume - 56) / 44 + 100;
  4334. }
  4335. }
  4336. result = sqrtf((F32)ires / 400.f);
  4337. }
  4338. return result;
  4339. }
  4340. void LLVoiceVivox::setUserVolume(const LLUUID& id, F32 volume)
  4341. {
  4342. if (mAudioSession)
  4343. {
  4344. participantState* participantp = findParticipantByID(id);
  4345. if (participantp)
  4346. {
  4347. // Volume can amplify by as much as 4x !
  4348. S32 ivol = (S32)(400.f * volume * volume);
  4349. participantp->mUserVolume = llclamp(ivol, 0, 400);
  4350. participantp->mVolumeDirty = true;
  4351. mAudioSession->mVolumeDirty = true;
  4352. }
  4353. }
  4354. }
  4355. LLVoiceVivox::sessionState::sessionState()
  4356. : mCreateInProgress(false),
  4357. mMediaConnectInProgress(false),
  4358. mVoiceInvitePending(false),
  4359. mSynthesizedCallerID(false),
  4360. mIsChannel(false),
  4361. mIsSpatial(false),
  4362. mIsP2P(false),
  4363. mIncoming(false),
  4364. mVoiceEnabled(false),
  4365. mReconnect(false),
  4366. mVolumeDirty(false)
  4367. {
  4368. }
  4369. LLVoiceVivox::sessionState::~sessionState()
  4370. {
  4371. removeAllParticipants();
  4372. }
  4373. LLSD LLVoiceVivox::sessionState::getVoiceChannelInfo() const
  4374. {
  4375. LLSD channel_info;
  4376. channel_info["voice_server_type"] = VIVOXSTR;
  4377. channel_info["channel_credentials"] = mHash;
  4378. channel_info["channel_uri"] = mSIPURI;
  4379. channel_info["session_handle"] = mHandle;
  4380. return channel_info;
  4381. }
  4382. bool LLVoiceVivox::sessionState::isCallBackPossible()
  4383. {
  4384. // This may change to be explicitly specified by Vivox in the future...
  4385. // Currently, only PSTN P2P calls cannot be returned.
  4386. // Conveniently, this is also the only case where we synthesize a caller
  4387. // UUID.
  4388. return !mSynthesizedCallerID;
  4389. }
  4390. bool LLVoiceVivox::sessionState::isTextIMPossible()
  4391. {
  4392. // This may change to be explicitly specified by vivox in the future...
  4393. return !mSynthesizedCallerID;
  4394. }
  4395. LLVoiceVivox::sessionState* LLVoiceVivox::findSession(const std::string& handle)
  4396. {
  4397. session_map_t::iterator iter = mSessionsByHandle.find(&handle);
  4398. return iter != mSessionsByHandle.end() ? iter->second : NULL;
  4399. }
  4400. LLVoiceVivox::sessionState* LLVoiceVivox::findSessionBeingCreatedByURI(const std::string& uri)
  4401. {
  4402. for (session_set_it_t iter = mSessions.begin(); iter != mSessions.end();
  4403. ++iter)
  4404. {
  4405. sessionState* sessionp = *iter;
  4406. if (sessionp->mCreateInProgress && sessionp->mSIPURI == uri)
  4407. {
  4408. return sessionp;
  4409. }
  4410. }
  4411. return NULL;
  4412. }
  4413. LLVoiceVivox::sessionState* LLVoiceVivox::findSession(const LLUUID& participant_id)
  4414. {
  4415. for (session_set_it_t iter = mSessions.begin(); iter != mSessions.end();
  4416. ++iter)
  4417. {
  4418. sessionState* sessionp = *iter;
  4419. if (sessionp->mCallerID == participant_id ||
  4420. sessionp->mIMSessionID == participant_id)
  4421. {
  4422. return sessionp;
  4423. }
  4424. }
  4425. return NULL;
  4426. }
  4427. LLVoiceVivox::sessionState* LLVoiceVivox::addSession(const std::string& uri,
  4428. const std::string& handle)
  4429. {
  4430. sessionState* sessionp = NULL;
  4431. if (handle.empty())
  4432. {
  4433. // No handle supplied: check whether there is already a session with
  4434. // this URI
  4435. for (session_set_it_t iter = mSessions.begin();
  4436. iter != mSessions.end(); ++iter)
  4437. {
  4438. sessionState* s = *iter;
  4439. if (s->mSIPURI == uri || s->mAlternateSIPURI == uri)
  4440. {
  4441. // *TODO: it is possible that this case we should raise an
  4442. // Internal error.
  4443. sessionp = s;
  4444. break;
  4445. }
  4446. }
  4447. }
  4448. else
  4449. {
  4450. // Check for an existing session with this handle
  4451. session_map_t::iterator iter = mSessionsByHandle.find(&handle);
  4452. if (iter != mSessionsByHandle.end())
  4453. {
  4454. sessionp = iter->second;
  4455. }
  4456. }
  4457. if (!sessionp)
  4458. {
  4459. // No existing session found.
  4460. LL_DEBUGS("Voice") << "Adding new session: handle " << handle
  4461. << " URI " << uri << LL_ENDL;
  4462. sessionp = new sessionState();
  4463. sessionp->mSIPURI = uri;
  4464. sessionp->mHandle = handle;
  4465. mSessions.insert(sessionp);
  4466. if (!sessionp->mHandle.empty())
  4467. {
  4468. mSessionsByHandle.emplace(&(sessionp->mHandle), sessionp);
  4469. }
  4470. }
  4471. else
  4472. {
  4473. // Found an existing session
  4474. if (uri != sessionp->mSIPURI)
  4475. {
  4476. // TODO: Should this be an Internal error?
  4477. LL_DEBUGS("Voice") << "Changing uri from " << sessionp->mSIPURI
  4478. << " to " << uri << LL_ENDL;
  4479. setSessionURI(sessionp, uri);
  4480. }
  4481. if (handle != sessionp->mHandle)
  4482. {
  4483. if (handle.empty())
  4484. {
  4485. // There is at least one race condition where where addSession
  4486. // was clearing an existing session handle, which caused things
  4487. // to break.
  4488. LL_DEBUGS("Voice") << "NOT clearing handle "
  4489. << sessionp->mHandle << LL_ENDL;
  4490. }
  4491. else
  4492. {
  4493. // TODO: Should this be an Internal error ?
  4494. LL_DEBUGS("Voice") << "Changing handle from "
  4495. << sessionp->mHandle << " to " << handle
  4496. << LL_ENDL;
  4497. setSessionHandle(sessionp, handle);
  4498. }
  4499. }
  4500. LL_DEBUGS("Voice") << "Returning existing session: handle " << handle
  4501. << " URI " << uri << LL_ENDL;
  4502. }
  4503. verifySessionState();
  4504. return sessionp;
  4505. }
  4506. void LLVoiceVivox::setSessionHandle(sessionState* sessionp,
  4507. const std::string& handle)
  4508. {
  4509. if (!sessionp) return;
  4510. // We have to remove the session from the handle-indexed map before
  4511. // changing the handle, or things will break badly.
  4512. if (!sessionp->mHandle.empty())
  4513. {
  4514. // Remove session from the map if it should have been there.
  4515. session_map_t::iterator iter =
  4516. mSessionsByHandle.find(&(sessionp->mHandle));
  4517. if (iter != mSessionsByHandle.end())
  4518. {
  4519. if (iter->second != sessionp)
  4520. {
  4521. llwarns << "Internal error: session mismatch !" << llendl;
  4522. giveUp();
  4523. return;
  4524. }
  4525. mSessionsByHandle.erase(iter);
  4526. }
  4527. else
  4528. {
  4529. llwarns << "Internal error: session handle not found in map !"
  4530. << llendl;
  4531. giveUp();
  4532. return;
  4533. }
  4534. }
  4535. sessionp->mHandle = handle;
  4536. if (!handle.empty())
  4537. {
  4538. mSessionsByHandle.emplace(&(sessionp->mHandle), sessionp);
  4539. }
  4540. verifySessionState();
  4541. }
  4542. void LLVoiceVivox::setSessionURI(sessionState* sessionp,
  4543. const std::string& uri)
  4544. {
  4545. if (sessionp)
  4546. {
  4547. // There used to be a map of session URIs to sessions, which made this
  4548. // complex....
  4549. sessionp->mSIPURI = uri;
  4550. }
  4551. verifySessionState();
  4552. }
  4553. void LLVoiceVivox::deleteSession(sessionState* sessionp)
  4554. {
  4555. if (!sessionp) return;
  4556. // Remove the session from the handle map
  4557. if (!sessionp->mHandle.empty())
  4558. {
  4559. session_map_t::iterator iter =
  4560. mSessionsByHandle.find(&(sessionp->mHandle));
  4561. if (iter != mSessionsByHandle.end())
  4562. {
  4563. if (iter->second != sessionp)
  4564. {
  4565. llwarns << "Internal error: session mismatch !" << llendl;
  4566. giveUp();
  4567. return;
  4568. }
  4569. mSessionsByHandle.erase(iter);
  4570. }
  4571. }
  4572. // Remove the session from the URI map
  4573. mSessions.erase(sessionp);
  4574. // At this point, the session should be unhooked from all lists and all
  4575. // states should be consistent.
  4576. verifySessionState();
  4577. // If this is the current audio session, clean up the pointer which will
  4578. // soon be dangling.
  4579. if (mAudioSession == sessionp)
  4580. {
  4581. mAudioSession = NULL;
  4582. }
  4583. // Ditto for the next audio session
  4584. if (mNextAudioSession == sessionp)
  4585. {
  4586. mNextAudioSession = NULL;
  4587. }
  4588. // Delete the session
  4589. delete sessionp;
  4590. }
  4591. void LLVoiceVivox::deleteAllSessions()
  4592. {
  4593. while (!mSessions.empty())
  4594. {
  4595. deleteSession(*(mSessions.begin()));
  4596. }
  4597. if (!mSessionsByHandle.empty())
  4598. {
  4599. llwarns << "Internal error: empty session map, non-empty handle map"
  4600. << llendl;
  4601. giveUp();
  4602. }
  4603. }
  4604. void LLVoiceVivox::verifySessionState()
  4605. {
  4606. // This is mostly intended for debugging problems with session state
  4607. // management.
  4608. LL_DEBUGS("Voice") << "Total session count: " << mSessions.size()
  4609. << " , session handle map size: "
  4610. << mSessionsByHandle.size() << LL_ENDL;
  4611. session_map_t::iterator map_end = mSessionsByHandle.end();
  4612. session_set_it_t end = mSessions.end();
  4613. for (session_set_it_t iter = mSessions.begin(); iter != end; ++iter)
  4614. {
  4615. sessionState* sessionp = *iter;
  4616. LL_DEBUGS("Voice") << "Session " << sessionp << ": handle "
  4617. << sessionp->mHandle << ", URI "
  4618. << sessionp->mSIPURI << LL_ENDL;
  4619. if (!sessionp->mHandle.empty())
  4620. {
  4621. // Every session with a non-empty handle needs to be in the handle
  4622. // map
  4623. session_map_t::iterator i2 =
  4624. mSessionsByHandle.find(&(sessionp->mHandle));
  4625. if (i2 == map_end)
  4626. {
  4627. llwarns << "Internal error (handle " << sessionp->mHandle
  4628. << " not found in session map)" << llendl;
  4629. giveUp();
  4630. return;
  4631. }
  4632. else if (i2->second != sessionp)
  4633. {
  4634. llwarns << "Internal error (handle " << sessionp->mHandle
  4635. << " in session map points to another session)"
  4636. << llendl;
  4637. giveUp();
  4638. return;
  4639. }
  4640. }
  4641. }
  4642. // Check that every entry in the handle map points to a valid session in
  4643. // the session set
  4644. for (session_map_t::iterator iter = mSessionsByHandle.begin();
  4645. iter != map_end; ++iter)
  4646. {
  4647. sessionState* sessionp = iter->second;
  4648. session_set_it_t i2 = mSessions.find(sessionp);
  4649. if (i2 == mSessions.end())
  4650. {
  4651. llwarns << "Internal error (session for handle "
  4652. << sessionp->mHandle << " not found in session map)"
  4653. << llendl;
  4654. giveUp();
  4655. return;
  4656. }
  4657. else if (sessionp->mHandle != (*i2)->mHandle)
  4658. {
  4659. llwarns << "Internal error (session for handle "
  4660. << sessionp->mHandle
  4661. << " points to session with different handle "
  4662. << (*i2)->mHandle << ")" << llendl;
  4663. giveUp();
  4664. return;
  4665. }
  4666. }
  4667. }
  4668. void LLVoiceVivox::addObserver(LLVoiceClientStatusObserver* observerp)
  4669. {
  4670. mStatusObservers.insert(observerp);
  4671. }
  4672. void LLVoiceVivox::removeObserver(LLVoiceClientStatusObserver* observerp)
  4673. {
  4674. mStatusObservers.erase(observerp);
  4675. }
  4676. void LLVoiceVivox::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
  4677. {
  4678. if (mAudioSession)
  4679. {
  4680. if (status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
  4681. {
  4682. switch (mAudioSession->mErrorStatusCode)
  4683. {
  4684. case 20713:
  4685. status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL;
  4686. break;
  4687. case 20714:
  4688. status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED;
  4689. break;
  4690. case 20715:
  4691. // Invalid channel, we may be using a set of poorly cached
  4692. // info
  4693. status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  4694. break;
  4695. case 1009:
  4696. // Invalid username and password
  4697. status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  4698. }
  4699. // Reset the error code to make sure it would not be reused later
  4700. // by accident.
  4701. mAudioSession->mErrorStatusCode = 0;
  4702. }
  4703. else if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
  4704. {
  4705. switch (mAudioSession->mErrorStatusCode)
  4706. {
  4707. case HTTP_NOT_FOUND: // 404
  4708. // *TODO: Should this be 503 ?
  4709. case 480: // TEMPORARILY_UNAVAILABLE
  4710. case HTTP_REQUEST_TIME_OUT: // 408
  4711. // Call failed because other user was not available treat
  4712. // this as an error case
  4713. status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  4714. // Reset the error code to make sure it would not be reused
  4715. // later by accident.
  4716. mAudioSession->mErrorStatusCode = 0;
  4717. }
  4718. }
  4719. }
  4720. LL_DEBUGS("Voice") << LLVoiceClientStatusObserver::status2string(status)
  4721. << " - Session channel info "
  4722. << getAudioSessionChannelInfo() << " - Proximal is "
  4723. << (inSpatialChannel() ? "true" : "false") << LL_ENDL;
  4724. if (!mProcessChannels)
  4725. {
  4726. return;
  4727. }
  4728. for (status_observer_set_t::iterator it = mStatusObservers.begin();
  4729. it != mStatusObservers.end(); )
  4730. {
  4731. LLVoiceClientStatusObserver* observerp = *it;
  4732. observerp->onChange(status, getAudioSessionChannelInfo(),
  4733. inSpatialChannel());
  4734. // In case onError() deleted an entry.
  4735. it = mStatusObservers.upper_bound(observerp);
  4736. }
  4737. }
  4738. void LLVoiceVivox::lookupName(const LLUUID& id)
  4739. {
  4740. if (gCacheNamep)
  4741. {
  4742. gCacheNamep->get(id, false, onAvatarNameLookup);
  4743. }
  4744. }
  4745. //static
  4746. void LLVoiceVivox::onAvatarNameLookup(const LLUUID& id,
  4747. const std::string& fullname, bool)
  4748. {
  4749. if (!gVoiceVivox.mTerminated) // If Voice has not since been shut down
  4750. {
  4751. gVoiceVivox.avatarNameResolved(id, fullname);
  4752. }
  4753. }
  4754. void LLVoiceVivox::avatarNameResolved(const LLUUID& id,
  4755. const std::string& name)
  4756. {
  4757. // Iterate over all sessions.
  4758. for (session_set_it_t iter = mSessions.begin(); iter != mSessions.end();
  4759. ++iter)
  4760. {
  4761. sessionState* sessionp = *iter;
  4762. // Check for this user as a participant in this session
  4763. participantState* participantp = sessionp->findParticipantByID(id);
  4764. if (participantp)
  4765. {
  4766. // Found: fill in the name
  4767. participantp->mAccountName = name;
  4768. }
  4769. // Check whether this is a P2P session whose caller name just resolved
  4770. if (sessionp->mCallerID == id)
  4771. {
  4772. // This session's "caller ID" just resolved. Fill in the name.
  4773. sessionp->mName = name;
  4774. if (sessionp->mVoiceInvitePending)
  4775. {
  4776. sessionp->mVoiceInvitePending = false;
  4777. gIMMgrp->inviteToSession(sessionp->mIMSessionID,
  4778. sessionp->mName,
  4779. sessionp->mCallerID,
  4780. sessionp->mName,
  4781. IM_SESSION_P2P_INVITE,
  4782. LLIMMgr::INVITATION_TYPE_VOICE,
  4783. sessionp->getVoiceChannelInfo());
  4784. }
  4785. }
  4786. }
  4787. }