123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388 |
- /**
- * @file llvoicevivox.cpp
- * @brief Implementation of LLVoiceVivox class which is the interface to the
- * SLVoice client process.
- *
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- * Copyright (c) 2009-2024, Henri Beauchamp.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #if LL_LINUX
- # include <stdlib.h> // For getenv()
- #endif
- #include "expat.h"
- #include "llvoicevivox.h"
- #include "llapr.h"
- #include "llbase64.h"
- #include "llbufferstream.h"
- #include "llcachename.h"
- #include "llcallbacklist.h"
- #include "llcorehttputil.h"
- #include "lldir.h"
- #include "llparcel.h"
- #include "llpumpio.h"
- #include "llprocesslauncher.h"
- #include "llsdutil.h"
- #include "llagent.h"
- #include "llappviewer.h" // For gDisconnected and gSecondLife
- #include "llfloaterchat.h" // For LLFloaterChat::addChat()
- #include "llgridmanager.h"
- #include "llimmgr.h"
- #include "llmutelist.h"
- #include "llstartup.h"
- #include "llviewercamera.h"
- #include "llviewercontrol.h"
- #include "llviewerparcelmgr.h"
- #include "llviewerregion.h"
- #include "llvoavatarself.h"
- #include "llvoicechannel.h"
- static const std::string VIVOXSTR = "vivox";
- // Helper functions.
- // Incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
- // Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75
- static S32 scale_mic_volume(F32 volume)
- {
- // Offset volume to the range [-1.0, 1.0], with 0 at the default.
- volume -= 1.f;
- S32 scaled_volume = 44; // offset scaled_volume by its default level
- if (volume < 0.f)
- {
- scaled_volume += (S32)(volume * 4.f); // (44 - 40)
- }
- else
- {
- scaled_volume += (S32)(volume * 31.f); // (75 - 44)
- }
- return scaled_volume;
- }
- // Incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
- // Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75
- static S32 scale_speaker_volume(F32 volume)
- {
- // Offset volume to the range [-0.5, 0.5], with 0 at the default.
- volume -= 0.5f;
- S32 scaled_volume = 62; // Offset scaled_volume by its default level
- if (volume < 0.f)
- {
- scaled_volume += (S32)(volume * 124.f); // (62 - 0) * 2
- }
- else
- {
- scaled_volume += (S32)(volume * 26.f); // (75 - 62) * 2
- }
- return scaled_volume;
- }
- static std::string random_handle()
- {
- LLUUID id;
- id.generate();
- return LLBase64::encode((const char*)id.mData, UUID_BYTES);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLVivoxProtocolParser class
- ///////////////////////////////////////////////////////////////////////////////
- class LLVivoxProtocolParser : public LLIOPipe
- {
- protected:
- LOG_CLASS(LLVivoxProtocolParser);
- public:
- LLVivoxProtocolParser();
- ~LLVivoxProtocolParser() override;
- protected:
- // LLIOPipe virtual implementations: process the data in buffer
- EStatus process_impl(const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer, bool& eos, LLSD& context,
- LLPumpIO* pump) override;
- void reset();
- void processResponse(std::string tag);
- static void XMLCALL ExpatStartTag(void* data, const char* el,
- const char** attr);
- static void XMLCALL ExpatEndTag(void* data, const char* el);
- static void XMLCALL ExpatCharHandler(void* data, const XML_Char* s,
- int len);
- void StartTag(const char* tag, const char** attr);
- void EndTag(const char* tag);
- void CharData(const char* buffer, int length);
- protected:
- std::string mInput;
- // Expat control members
- XML_ParserStruct* mParser;
- S32 mResponseDepth;
- S32 mIgnoreDepth;
- bool mIgnoringTags;
- bool mIsEvent;
- // Members for processing responses. The values are transient and only
- // valid within a call to processResponse().
- bool mSquelchDebugOutput;
- S32 mReturnCode;
- S32 mStatusCode;
- std::string mStatusString;
- std::string mRequestId;
- std::string mActionString;
- std::string mConnectorHandle;
- std::string mVersionId;
- std::string mAccountHandle;
- std::string mSessionHandle;
- std::string mSessionGrpHandle;
- std::string mAlias;
- // Members for processing events. The values are transient and only valid
- // within a call to processResponse().
- std::string mEventTypeString;
- std::string mUriString;
- std::string mDeviceString;
- std::string mNameString;
- std::string mDisplayNameString;
- std::string mMessageHeader;
- std::string mMessageBody;
- std::string mNotificationType;
- S32 mState;
- S32 mVolume;
- S32 mParticipantType;
- S32 mNumberOfAliases;
- F32 mEnergy;
- bool mIsModeratorMuted;
- bool mIsSpeaking;
- bool mIsChannel;
- bool mIncoming;
- bool mEnabled;
- // Members for processing text between tags
- bool mAccumulateText;
- std::string mTextBuffer;
- };
- LLVivoxProtocolParser::LLVivoxProtocolParser()
- {
- mParser = NULL;
- mParser = XML_ParserCreate(NULL);
- reset();
- }
- void LLVivoxProtocolParser::reset()
- {
- mResponseDepth = mIgnoreDepth = mParticipantType = mState = mVolume =
- mNumberOfAliases = mStatusCode = 0;
- mIgnoringTags = mAccumulateText = mIsChannel = mIsEvent = mIsSpeaking =
- mIsModeratorMuted = mSquelchDebugOutput = false;
- mEnergy = 0.f;
- mReturnCode = -1;
- mAlias.clear();
- mTextBuffer.clear();
- }
- //virtual
- LLVivoxProtocolParser::~LLVivoxProtocolParser()
- {
- if (mParser)
- {
- XML_ParserFree(mParser);
- }
- }
- // virtual
- LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer,
- bool& eos,
- LLSD& context,
- LLPumpIO* pump)
- {
- LLBufferStream istr(channels, buffer.get());
- std::ostringstream ostr;
- while (istr.good())
- {
- char buf[1024];
- istr.read(buf, sizeof(buf));
- mInput.append(buf, istr.gcount());
- }
- // Look for input delimiter(s) in the input buffer. If one is found, send
- // the message to the xml parser.
- size_t start = 0;
- size_t delim;
- while ((delim = mInput.find("\n\n\n", start)) != std::string::npos)
- {
- // Reset internal state of the LLVivoxProtocolParser (no effect on the
- // expat parser)
- reset();
- XML_ParserReset(mParser, NULL);
- XML_SetElementHandler(mParser, ExpatStartTag, ExpatEndTag);
- XML_SetCharacterDataHandler(mParser, ExpatCharHandler);
- XML_SetUserData(mParser, this);
- XML_Parse(mParser, mInput.data() + start, delim - start, false);
- // If this message is not set to be squelched, output the raw XML
- // received
- if (!mSquelchDebugOutput)
- {
- LL_DEBUGS("Voice") << "Parsing: "
- << mInput.substr(start, delim - start)
- << LL_ENDL;
- }
- start = delim + 3;
- }
- if (start)
- {
- mInput = mInput.substr(start);
- }
- LL_DEBUGS("VivoxProtocolParser") << "At end, mInput is: " << mInput
- << LL_ENDL;
- if (!gVoiceVivox.mConnected)
- {
- // If voice has been disabled, we just want to close the socket.
- // This does so.
- llinfos << "Returning STATUS_STOP" << llendl;
- return STATUS_STOP;
- }
- return STATUS_OK;
- }
- void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void* data, const char* el,
- const char** attr)
- {
- if (data)
- {
- LLVivoxProtocolParser* object = (LLVivoxProtocolParser*)data;
- object->StartTag(el, attr);
- }
- }
- void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void* data, const char* el)
- {
- if (data)
- {
- ((LLVivoxProtocolParser*)data)->EndTag(el);
- }
- }
- void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void* data,
- const XML_Char* s,
- int len)
- {
- if (data)
- {
- ((LLVivoxProtocolParser*)data)->CharData(s, len);
- }
- }
- void LLVivoxProtocolParser::StartTag(const char* tag, const char** attr)
- {
- // Reset the text accumulator. We should not have strings that are
- // interrupted by new tags
- mTextBuffer.clear();
- // Only accumulate text if we're not ignoring tags.
- mAccumulateText = !mIgnoringTags;
- if (mResponseDepth == 0)
- {
- mIsEvent = !stricmp("Event", tag);
- if (!stricmp("Response", tag) || mIsEvent)
- {
- // Grab the attributes
- while (*attr)
- {
- const char* key = *attr++;
- const char* value = *attr++;
- if (!stricmp("requestId", key))
- {
- mRequestId = value;
- }
- else if (!stricmp("action", key))
- {
- mActionString = value;
- }
- else if (!stricmp("type", key))
- {
- mEventTypeString = value;
- }
- }
- }
- LL_DEBUGS("VivoxProtocolParser") << "Tag: " << tag << " ("
- << mResponseDepth << ")" << LL_ENDL;
- }
- else if (mIgnoringTags)
- {
- LL_DEBUGS("VivoxProtocolParser") << "Ignoring tag " << tag
- << " (depth = " << mResponseDepth
- << ")" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("VivoxProtocolParser") << "Tag: " << tag << " ("
- << mResponseDepth << ")" << LL_ENDL;
- // Ignore the InputXml stuff so we do not get confused
- if (!stricmp("InputXml", tag))
- {
- mIgnoringTags = true;
- mIgnoreDepth = mResponseDepth;
- mAccumulateText = false;
- LL_DEBUGS("VivoxProtocolParser") << "Starting ignore, mIgnoreDepth is "
- << mIgnoreDepth << LL_ENDL;
- }
- else if (!stricmp("CaptureDevices", tag))
- {
- gVoiceVivox.clearCaptureDevices();
- }
- else if (!stricmp("RenderDevices", tag))
- {
- gVoiceVivox.clearRenderDevices();
- }
- else if (!stricmp("CaptureDevice", tag) ||
- !stricmp("RenderDevice", tag))
- {
- mDeviceString.clear();
- }
- }
- ++mResponseDepth;
- }
- void LLVivoxProtocolParser::EndTag(const char* tag)
- {
- const std::string& string = mTextBuffer;
- --mResponseDepth;
- if (mIgnoringTags)
- {
- if (mIgnoreDepth == mResponseDepth)
- {
- LL_DEBUGS("VivoxProtocolParser") << "End of ignore" << LL_ENDL;
- mIgnoringTags = false;
- }
- else
- {
- LL_DEBUGS("VivoxProtocolParser") << "Ignoring tag " << tag
- << " (depth = " << mResponseDepth
- << ")" << LL_ENDL;
- }
- }
- if (!mIgnoringTags)
- {
- LL_DEBUGS("VivoxProtocolParser") << "Processing tag: " << tag
- << " (depth = " << mResponseDepth
- << ")" << LL_ENDL;
- // Closing a tag. Finalize the text we have accumulated and reset
- if (!stricmp("ReturnCode", tag))
- {
- mReturnCode = strtol(string.c_str(), NULL, 10);
- }
- else if (!stricmp("SessionHandle", tag))
- {
- mSessionHandle = string;
- LL_DEBUGS("Voice") << "Received session handle: " << mSessionHandle
- << LL_ENDL;
- }
- else if (!stricmp("SessionGroupHandle", tag))
- {
- mSessionGrpHandle = string;
- LL_DEBUGS("Voice") << "Received session group handle: "
- << mSessionGrpHandle << LL_ENDL;
- }
- else if (!stricmp("StatusCode", tag))
- {
- mStatusCode = strtol(string.c_str(), NULL, 10);
- }
- else if (!stricmp("StatusString", tag))
- {
- mStatusString = string;
- }
- else if (!stricmp("ParticipantURI", tag))
- {
- mUriString = string;
- }
- else if (!stricmp("Volume", tag))
- {
- mVolume = strtol(string.c_str(), NULL, 10);
- }
- else if (!stricmp("Energy", tag))
- {
- mEnergy = (F32)strtod(string.c_str(), NULL);
- }
- else if (!stricmp("IsModeratorMuted", tag))
- {
- mIsModeratorMuted = !stricmp(string.c_str(), "true");
- }
- else if (!stricmp("IsSpeaking", tag))
- {
- mIsSpeaking = !stricmp(string.c_str(), "true");
- }
- else if (!stricmp("Alias", tag))
- {
- mAlias = string;
- }
- else if (!stricmp("NumberOfAliases", tag))
- {
- mNumberOfAliases = strtol(string.c_str(), NULL, 10);
- }
- else if (!stricmp("ConnectorHandle", tag))
- {
- mConnectorHandle = string;
- LL_DEBUGS("Voice") << "Received connector handle: "
- << mConnectorHandle << LL_ENDL;
- }
- else if (!stricmp("VersionID", tag))
- {
- mVersionId = string;
- }
- else if (!stricmp("AccountHandle", tag))
- {
- mAccountHandle = string;
- }
- else if (!stricmp("State", tag))
- {
- mState = strtol(string.c_str(), NULL, 10);
- }
- else if (!stricmp("URI", tag))
- {
- mUriString = string;
- }
- else if (!stricmp("IsChannel", tag))
- {
- mIsChannel = !stricmp(string.c_str(), "true");
- }
- else if (!stricmp("Incoming", tag))
- {
- mIncoming = !stricmp(string.c_str(), "true");
- }
- else if (!stricmp("Enabled", tag))
- {
- mEnabled = !stricmp(string.c_str(), "true");
- }
- else if (!stricmp("Name", tag))
- {
- mNameString = string;
- }
- else if (!stricmp("ChannelName", tag))
- {
- mNameString = string;
- }
- else if (!stricmp("DisplayName", tag))
- {
- mDisplayNameString = string;
- }
- else if (!stricmp("AccountName", tag))
- {
- mNameString = string;
- }
- else if (!stricmp("ParticipantType", tag))
- {
- mParticipantType = strtol(string.c_str(), NULL, 10);
- }
- else if (!stricmp("MicEnergy", tag))
- {
- mEnergy = (F32)strtod(string.c_str(), NULL);
- }
- else if (!stricmp("ChannelName", tag))
- {
- mNameString = string;
- }
- else if (!stricmp("ChannelURI", tag))
- {
- mUriString = string;
- }
- else if (!stricmp("BuddyURI", tag))
- {
- mUriString = string;
- llwarns << "Buddy feature no more supported." << llendl;
- }
- else if (!stricmp("Presence", tag))
- {
- mStatusString = string;
- }
- else if (!stricmp("Device", tag))
- {
- mDeviceString = string;
- }
- else if (!stricmp("CaptureDevice", tag))
- {
- gVoiceVivox.addCaptureDevice(mDisplayNameString, mDeviceString);
- }
- else if (!stricmp("RenderDevice", tag))
- {
- gVoiceVivox.addRenderDevice(mDisplayNameString, mDeviceString);
- }
- else if (!stricmp("MessageHeader", tag))
- {
- mMessageHeader = string;
- }
- else if (!stricmp("MessageBody", tag))
- {
- mMessageBody = string;
- }
- else if (!stricmp("NotificationType", tag))
- {
- mNotificationType = string;
- }
- else
- {
- LL_DEBUGS("VivoxProtocolParser") << "Unhandled tag; " << tag
- << LL_ENDL;
- }
- mTextBuffer.clear();
- mAccumulateText = false;
- if (mResponseDepth == 0)
- {
- // We finished all of the XML, process the data
- processResponse(tag);
- }
- }
- }
- // This method is called for anything that is not a tag, which can be text you
- // want that lies between tags, and a lot of stuff you do not want like file
- // formatting (tabs, spaces, CR/LF, etc).
- void LLVivoxProtocolParser::CharData(const char* buffer, int length)
- {
- // Only copy text if we are in accumulate mode...
- if (mAccumulateText)
- {
- mTextBuffer.append(buffer, length);
- }
- }
- void LLVivoxProtocolParser::processResponse(std::string tag)
- {
- LL_DEBUGS("VivoxProtocolParser") << "Response for tag: " << tag << LL_ENDL;
- // SLIM SDK: the SDK now returns a mStatusCode of "200" (OK) for success.
- // This is a change vs. previous SDKs.
- // According to Mike S., "The actual API convention is that responses with
- // return codes of 0 are successful, regardless of the status code
- // returned", so I believe this will give correct behavior.
- if (mReturnCode == 0)
- {
- mStatusCode = 0;
- }
- if (mIsEvent)
- {
- const char* event_type = mEventTypeString.c_str();
- if (!stricmp(event_type, "ParticipantUpdatedEvent"))
- {
- /*
- <Event type="ParticipantUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
- <ParticipantUri>sip:[email protected]</ParticipantUri>
- <IsModeratorMuted>false</IsModeratorMuted>
- <IsSpeaking>true</IsSpeaking>
- <Volume>44</Volume>
- <Energy>0.0879437</Energy>
- </Event>
- */
- // These happen so often that logging them is pretty useless.
- mSquelchDebugOutput = true;
- gVoiceVivox.participantUpdatedEvent(mSessionHandle,
- mSessionGrpHandle,
- mUriString, mAlias,
- mIsModeratorMuted,
- mIsSpeaking, mVolume, mEnergy);
- }
- else if (!stricmp(event_type, "AccountLoginStateChangeEvent"))
- {
- gVoiceVivox.accountLoginStateChangeEvent(mAccountHandle,
- mStatusCode,
- mStatusString, mState);
- }
- else if (!stricmp(event_type, "SessionAddedEvent"))
- {
- /*
- <Event type="SessionAddedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
- <Uri>sip:[email protected]</Uri>
- <IsChannel>true</IsChannel>
- <Incoming>false</Incoming>
- <ChannelName />
- </Event>
- */
- gVoiceVivox.sessionAddedEvent(mUriString, mAlias, mSessionHandle,
- mSessionGrpHandle, mIsChannel,
- mIncoming, mNameString);
- }
- else if (!stricmp(event_type, "SessionRemovedEvent"))
- {
- gVoiceVivox.sessionRemovedEvent(mSessionHandle,
- mSessionGrpHandle);
- }
- else if (!stricmp(event_type, "MediaStreamUpdatedEvent"))
- {
- /*
- <Event type="MediaStreamUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
- <StatusCode>200</StatusCode>
- <StatusString>OK</StatusString>
- <State>2</State>
- <Incoming>false</Incoming>
- </Event>
- */
- gVoiceVivox.mediaStreamUpdatedEvent(mSessionHandle,
- mSessionGrpHandle,
- mStatusCode, mStatusString,
- mState, mIncoming);
- }
- else if (!stricmp(event_type, "ParticipantAddedEvent"))
- {
- /*
- <Event type="ParticipantAddedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
- <ParticipantUri>sip:[email protected]</ParticipantUri>
- <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
- <DisplayName />
- <ParticipantType>0</ParticipantType>
- </Event>
- */
- gVoiceVivox.participantAddedEvent(mSessionHandle,
- mSessionGrpHandle,
- mUriString, mAlias,
- mNameString, mDisplayNameString,
- mParticipantType);
- }
- else if (!stricmp(event_type, "ParticipantRemovedEvent"))
- {
- /*
- <Event type="ParticipantRemovedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
- <ParticipantUri>sip:[email protected]</ParticipantUri>
- <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
- </Event>
- */
- gVoiceVivox.participantRemovedEvent(mSessionHandle,
- mSessionGrpHandle,
- mUriString, mAlias,
- mNameString);
- }
- else if (!stricmp(event_type, "AuxAudioPropertiesEvent"))
- {
- // These happen so often that logging them is pretty useless.
- mSquelchDebugOutput = true;
- gVoiceVivox.auxAudioPropertiesEvent(mEnergy);
- }
- else if (!stricmp(event_type, "MessageEvent"))
- {
- gVoiceVivox.messageEvent(mSessionHandle, mUriString, mAlias,
- mMessageHeader, mMessageBody);
- }
- else if (!stricmp(event_type, "SessionNotificationEvent"))
- {
- gVoiceVivox.sessionNotificationEvent(mSessionHandle, mUriString,
- mNotificationType);
- }
- else if (!stricmp(event_type, "SessionUpdatedEvent"))
- {
- /*
- <Event type="SessionUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
- <Uri>sip:[email protected]</Uri>
- <IsMuted>0</IsMuted>
- <Volume>50</Volume>
- <TransmitEnabled>1</TransmitEnabled>
- <IsFocused>0</IsFocused>
- <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
- <SessionFontID>0</SessionFontID>
- </Event>
- */
- // We do not need to process this, but we also should not warn on
- // it, since that confuses people.
- LL_DEBUGS("VivoxProtocolParser") << "Ignored event: "
- << mEventTypeString << LL_ENDL;
- }
- else if (!stricmp(event_type, "AudioDeviceHotSwapEvent"))
- {
- /*
- <Event type = "AudioDeviceHotSwapEvent">
- <EventType>RenderDeviceChanged</EventType>
- <RelevantDevice>
- <Device>Speakers(Turtle Beach P11 Headset)</Device>
- <DisplayName>Speakers(Turtle Beach P11 Headset)</DisplayName>
- <Type>SpecificDevice</Type>
- </RelevantDevice>
- </Event>
- */
- // An audio device was removed or added, fetch and update the local
- // list of audio devices.
- gVoiceVivox.getCaptureDevicesSendMessage();
- gVoiceVivox.getRenderDevicesSendMessage();
- }
- // Warn for all other events but those (deprecated/unused):
- else if (stricmp(event_type, "BuddyAndGroupListChangedEvent") &&
- stricmp(event_type, "SessionGroupUpdatedEvent") &&
- stricmp(event_type, "SessionGroupRemovedEvent") &&
- stricmp(event_type, "SessionGroupAddedEvent") &&
- // This one relates to voice morphing (not implemented):
- stricmp(event_type, "MediaCompletionEvent") &&
- stricmp(event_type, "VoiceServiceConnectionStateChangedEvent"))
- {
- llwarns << "Unknown event type " << mEventTypeString << llendl;
- }
- }
- else
- {
- const char* action = mActionString.c_str();
- if (!stricmp(action, "Session.Set3DPosition.1"))
- {
- // We do not need to process these, but they are so spammy we do
- // not want to log them.
- mSquelchDebugOutput = true;
- }
- else if (!stricmp(action, "Connector.Create.1"))
- {
- gVoiceVivox.connectorCreateResponse(mStatusCode, mStatusString,
- mConnectorHandle, mVersionId);
- }
- else if (!stricmp(action, "Account.Login.1"))
- {
- gVoiceVivox.loginResponse(mStatusCode, mStatusString,
- mAccountHandle, mNumberOfAliases);
- }
- else if (!stricmp(action, "Session.Create.1"))
- {
- gVoiceVivox.sessionCreateResponse(mRequestId, mStatusCode,
- mStatusString, mSessionHandle);
- }
- else if (!stricmp(action, "SessionGroup.AddSession.1"))
- {
- gVoiceVivox.sessionGroupAddSessionResponse(mRequestId,
- mStatusCode,
- mStatusString,
- mSessionHandle);
- }
- else if (!stricmp(action, "Session.Connect.1"))
- {
- gVoiceVivox.sessionConnectResponse(mRequestId, mStatusCode,
- mStatusString);
- }
- else if (!stricmp(action, "Aux.SetVadProperties.1"))
- {
- if (mStatusCode && mStatusCode != 200)
- {
- llwarns << "Aux.SetVadProperties.1 request failed with code "
- << mStatusCode << " and status string: "
- << mStatusString << llendl;
- }
- }
- else if (!stricmp(action, "Account.Logout.1"))
- {
- gVoiceVivox.logoutResponse(mStatusCode, mStatusString);
- }
- else if (!stricmp(action, "Connector.InitiateShutdown.1"))
- {
- gVoiceVivox.connectorShutdownResponse(mStatusCode, mStatusString);
- }
- else
- {
- LL_DEBUGS("VivoxProtocolParser") << "Unhandled action: " << action
- << LL_ENDL;
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLVivoxMuteListObserver class
- ///////////////////////////////////////////////////////////////////////////////
- class LLVivoxMuteListObserver : public LLMuteListObserver
- {
- LL_INLINE void onChange() override
- {
- gVoiceVivox.muteListChanged();
- }
- };
- static LLVivoxMuteListObserver sMutelistListener;
- static bool sMuteListListening = false;
- ///////////////////////////////////////////////////////////////////////////////
- // LLVoiceVivox class proper
- ///////////////////////////////////////////////////////////////////////////////
- LLVoiceVivox gVoiceVivox;
- LLVoiceVivox::LLVoiceVivox()
- : mPump(NULL),
- mProcess(NULL),
- mAudioSession(NULL),
- mNextAudioSession(NULL),
- mTerminated(false),
- mState(stateDisabled),
- mVoiceEnabled(false),
- mProcessChannels(false),
- mAccountLoggedIn(false),
- mConnectorEstablished(false),
- mSessionTerminateRequested(false),
- mRelogRequested(false),
- mConnected(false),
- #if LL_LINUX
- mDeprecatedClient(false),
- #endif
- mRetries(0),
- mTuningMode(false),
- mTuningEnergy(0.f),
- mTuningMicVolume(0),
- mTuningMicVolumeDirty(true),
- mTuningSpeakerVolume(0),
- mTuningSpeakerVolumeDirty(true),
- mTuningExitState(stateDisabled),
- mNumberOfAliases(0),
- mCommandCookie(0),
- mLoginRetryCount(0),
- mLogLevel(0),
- mCaptureDeviceDirty(false),
- mRenderDeviceDirty(false),
- mSpatialCoordsDirty(false),
- mEarLocation(0),
- mSpeakerVolume(0),
- mSpeakerVolumeDirty(true),
- mSpeakerMuteDirty(true),
- mMicVolume(0),
- mMicVolumeDirty(true),
- mPTT(true),
- mPTTDirty(false),
- mAccountHandle(random_handle()),
- mConnectorHandle(random_handle())
- {
- #if LL_DARWIN || LL_LINUX
- // *HACK: when the vivox daemon dies, the next write attempt on our socket
- // generates a SIGPIPE, which kills us. This should cause us to ignore
- // SIGPIPE and handle the error through proper channels.
- signal(SIGPIPE, SIG_IGN);
- // Since we are now launching the gateway with fork/exec instead of
- // system(), we need to deal with zombie processes.
- // Ignoring SIGCHLD should prevent zombies from being created.
- // Alternately, we could use wait(), but I would rather not do that.
- signal(SIGCHLD, SIG_IGN);
- #endif
- }
- LLVoiceVivox::~LLVoiceVivox()
- {
- killDaemon();
- }
- void LLVoiceVivox::init(LLPumpIO* pumpp)
- {
- if (mTerminated || mPump)
- {
- return;
- }
- llinfos << "Initializing Vivox voice client. Default account handle: "
- << mAccountHandle << " - Default connector handle: "
- << mConnectorHandle << llendl;
- mPump = pumpp;
- gIdleCallbacks.addFunction(LLVoiceVivox::idle, &gVoiceVivox);
- LLControlVariable* controlp = gSavedSettings.getControl("VivoxVadAuto");
- controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
- &gVoiceVivox));
- controlp = gSavedSettings.getControl("VivoxVadHangover");
- controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
- &gVoiceVivox));
- controlp = gSavedSettings.getControl("VivoxVadNoiseFloor");
- controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
- &gVoiceVivox));
- controlp = gSavedSettings.getControl("VivoxVadSensitivity");
- controlp->getSignal()->connect(boost::bind(&LLVoiceVivox::setupVADParams,
- &gVoiceVivox));
- }
- //virtual
- const std::string& LLVoiceVivox::getName() const
- {
- return VIVOXSTR;
- }
- bool LLVoiceVivox::isVoiceWorking() const
- {
- return !mTerminated && mVoiceEnabled && mProcessChannels &&
- mState != stateDisabled;
- }
- void LLVoiceVivox::killDaemon()
- {
- if (mProcess)
- {
- delete mProcess;
- mProcess = NULL;
- }
- }
- void LLVoiceVivox::terminate()
- {
- if (mTerminated)
- {
- return;
- }
- mTerminated = true;
- if (mPump)
- {
- llinfos << "Terminating Vivox voice client..." << llendl;
- gIdleCallbacks.deleteFunction(LLVoiceVivox::idle, &gVoiceVivox);
- if (mConnected)
- {
- logout();
- connectorShutdown();
- // Need to do this now: bad things happen if the destructor does it
- // later.
- closeSocket();
- }
- mPump = NULL;
- }
- }
- bool LLVoiceVivox::writeString(const std::string& str)
- {
- bool result = false;
- if (mConnected && mSocket)
- {
- apr_status_t err;
- apr_size_t size = (apr_size_t)str.size();
- apr_size_t written = size;
- // Check return code: sockets will fail (broken, etc)
- err = apr_socket_send(mSocket->getSocket(), (const char*)str.data(),
- &written);
- if (err == 0)
- {
- // Success.
- result = true;
- }
- #if 0 // *FIXME: handle partial writes (written is number of bytes written)
- // Need to set socket to non-blocking before this will work.
- else if (APR_STATUS_IS_EAGAIN(err))
- {
- }
- #endif
- else
- {
- // Assume any socket error means something bad. For now, just close
- // the socket.
- char buf[MAX_STRING];
- llwarns << "APR error " << err << " ("
- << apr_strerror(err, buf, MAX_STRING)
- << ") sending data to vivox daemon." << llendl;
- daemonDied();
- }
- }
- return result;
- }
- void LLVoiceVivox::connectorCreate()
- {
- // Transition to stateConnectorStarted when the connector handle comes back.
- setState(stateConnectorStarting);
- std::string logpath = gDirUtil.getFullPath(LL_PATH_LOGS, "");
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Connector.Create.1\">"
- << "<ClientName>V2 SDK</ClientName><AccountManagementServer>"
- << mVoiceAccountServerURI
- << "</AccountManagementServer><Mode>Normal</Mode>"
- << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
- << "<Logging><Folder>" << logpath
- << "</Folder><FileNamePrefix>Connector</FileNamePrefix>"
- << "<FileNameSuffix>.log</FileNameSuffix><LogLevel>"
- << mLogLevel << "</LogLevel></Logging><Application>" << gSecondLife
- << "</Application><MaxCalls>12</MaxCalls></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::connectorShutdown()
- {
- setState(stateConnectorStopping);
- if (!mConnectorEstablished)
- {
- return;
- }
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Connector.InitiateShutdown.1\"><ConnectorHandle>"
- << mConnectorHandle << "</ConnectorHandle></Request>\n\n\n";
- mConnectorEstablished = false;
- writeString(stream.str());
- }
- void LLVoiceVivox::userAuthorized(const std::string& first_name,
- const std::string& last_name,
- const LLUUID& agent_id)
- {
- mAccountFirstName = first_name;
- mAccountLastName = last_name;
- mAccountDisplayName = first_name + " " + last_name;
- llinfos << "Name \"" << mAccountDisplayName << "\", Id " << agent_id
- << llendl;
- mAccountName = nameFromID(agent_id);
- }
- void LLVoiceVivox::requestVoiceAccountProvision(S32 retries)
- {
- if (!mVoiceEnabled || !LLStartUp::isLoggedIn())
- {
- return;
- }
- std::string url =
- gAgent.getRegionCapability("ProvisionVoiceAccountRequest");
- if (url.empty())
- {
- LL_DEBUGS("Voice") << "Region does not have ProvisionVoiceAccountRequest capability !"
- << LL_ENDL;
- return;
- }
- gCoros.launch("LLVivoxVoiceClient::voiceAccountProvisionCoro",
- boost::bind(&LLVoiceVivox::voiceAccountProvisionCoro, url,
- retries));
- setState(stateConnectorStart);
- }
- //static
- void LLVoiceVivox::voiceAccountProvisionCoro(const std::string& url,
- S32 retries)
- {
- LLSD body;
- body["voice_server_type"] = VIVOXSTR;
- LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
- options->setRetries(retries);
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("voiceAccountProvision");
- LLSD result = adapter.postAndSuspend(url, body, options);
- if (gVoiceVivox.mTerminated)
- {
- // Voice has since been shut down
- return;
- }
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- llwarns << "Unable to provision voice account: " << status.toString()
- << llendl;
- gVoiceVivox.giveUp();
- return;
- }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response: " << result
- << LL_ENDL;
- std::string sip_uri_hostname;
- if (result.has("voice_sip_uri_hostname"))
- {
- sip_uri_hostname = result["voice_sip_uri_hostname"].asString();
- }
- // Old Vivox protocol key... Just in case (for OpenSim grids)...
- else if (result.has("sip_uri_hostname"))
- {
- sip_uri_hostname = result["sip_uri_hostname"].asString();
- }
- // This key is actually misnamed; it is an entire URI, not just a hostname.
- std::string account_server_uri;
- if (result.has("voice_account_server_name"))
- {
- account_server_uri = result["voice_account_server_name"].asString();
- }
- gVoiceVivox.login(result["username"].asString(),
- result["password"].asString(),
- sip_uri_hostname, account_server_uri);
- }
- void LLVoiceVivox::login(const std::string& account_name,
- const std::string& password,
- const std::string& sip_uri_hostname,
- const std::string& account_server_uri)
- {
- mVoiceSIPURIHostName = sip_uri_hostname;
- mVoiceAccountServerURI = account_server_uri;
- if (mAccountLoggedIn)
- {
- // Already logged in.
- llwarns << "Called while already logged in." << llendl;
- // Do not process another login.
- return;
- }
- else if (account_name != mAccountName)
- {
- // *TODO: error ?
- llwarns << "Wrong account name " << account_name << " instead of "
- << mAccountName << llendl;
- }
- else
- {
- mAccountPassword = password;
- }
- std::string sip_override = gSavedSettings.getString("VivoxSIPURIHostName");
- if (!sip_override.empty())
- {
- mVoiceSIPURIHostName = sip_override;
- }
- if (mVoiceSIPURIHostName.empty())
- {
- // We have an empty account server name so we fall back to hardcoded
- // defaults
- if (gIsInSecondLifeBetaGrid)
- {
- // Use the development account server
- mVoiceSIPURIHostName = "bhd.vivox.com";
- }
- else
- {
- // Use the release account server
- mVoiceSIPURIHostName = "bhr.vivox.com";
- }
- }
- std::string server_override =
- gSavedSettings.getString("VivoxVoiceAccountServerURI");
- if (!server_override.empty())
- {
- mVoiceAccountServerURI = server_override;
- llinfos << "Overriding account server based on VivoxVoiceAccountServerURI setting: "
- << mVoiceAccountServerURI << llendl;
- }
- if (mVoiceAccountServerURI.empty())
- {
- // If the account server URI is not specified, construct it from the
- // SIP URI hostname
- mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName +
- "/api2/";
- llinfos << "Inferring account server based on SIP URI Host name: "
- << mVoiceAccountServerURI << llendl;
- }
- }
- //static
- void LLVoiceVivox::idle(void* user_data)
- {
- LLVoiceVivox* self = (LLVoiceVivox*)user_data;
- if (self == &gVoiceVivox && !self->mTerminated && self->mPump)
- {
- self->stateMachine();
- }
- }
- std::string LLVoiceVivox::state2string(state new_state)
- {
- std::string result = "UNKNOWN";
- // Prevent copy-paste errors when updating this list...
- #define CASE(x) case x: result = #x; break
- switch (new_state)
- {
- CASE(stateDisableCleanup);
- CASE(stateDisabled);
- CASE(stateStart);
- CASE(stateDaemonLaunched);
- CASE(stateConnecting);
- CASE(stateConnected);
- CASE(stateIdle);
- CASE(stateMicTuningStart);
- CASE(stateMicTuningRunning);
- CASE(stateMicTuningStop);
- CASE(stateConnectorStart);
- CASE(stateConnectorStarting);
- CASE(stateConnectorStarted);
- CASE(stateLoginRetry);
- CASE(stateLoginRetryWait);
- CASE(stateNeedsLogin);
- CASE(stateLoggingIn);
- CASE(stateLoggedIn);
- CASE(stateNoChannel);
- CASE(stateJoiningSession);
- CASE(stateSessionJoined);
- CASE(stateRunning);
- CASE(stateLeavingSession);
- CASE(stateSessionTerminated);
- CASE(stateLoggingOut);
- CASE(stateLoggedOut);
- CASE(stateConnectorStopping);
- CASE(stateConnectorStopped);
- CASE(stateConnectorFailed);
- CASE(stateConnectorFailedWaiting);
- CASE(stateLoginFailed);
- CASE(stateLoginFailedWaiting);
- CASE(stateJoinSessionFailed);
- CASE(stateJoinSessionFailedWaiting);
- CASE(stateJail);
- }
- #undef CASE
- return result;
- }
- void LLVoiceVivox::setState(state new_state)
- {
- LL_DEBUGS("Voice") << "Entering state " << state2string(new_state)
- << LL_ENDL;
- mState = new_state;
- }
- void LLVoiceVivox::stateMachine()
- {
- // Do not retry connecting to the daemon more frequently than this:
- constexpr F32 CONNECT_THROTTLE_SECONDS = 1.f;
- // Do not send positional updates more frequently than this:
- constexpr F32 UPDATE_THROTTLE_SECONDS = 0.1f;
- // *HACK: for failed login on viewer launch; see below. HB
- static bool first_run = true;
- // SLVoice retries logic. HB
- constexpr F32 LOGIN_RETRY_SECONDS = 10.f;
- constexpr S32 MAX_LOGIN_RETRIES = 12;
- if (gDisconnected)
- {
- // The viewer has been disconnected from the sim. Disable voice.
- setVoiceEnabled(false);
- return;
- }
- if (!LLStartUp::isLoggedIn())
- {
- // Not yet logged in: nothing to do.
- return;
- }
- if (mVoiceEnabled && mProcessChannels)
- {
- updatePosition();
- }
- // NOTE: tuning mode is special: it needs to launch SLVoice even if voice
- // is disabled.
- else if (!mTuningMode)
- {
- if (mState != stateDisabled && mState != stateDisableCleanup)
- {
- // User turned off voice support. Send the cleanup messages, close
- // the socket, and reset.
- if (!mConnected)
- {
- // If voice was turned off after the daemon was launched but
- // before we could connect to it, we may need to issue a kill.
- llinfos << "Disabling voice before connection to daemon, terminating."
- << llendl;
- killDaemon();
- }
- logout();
- connectorShutdown();
- setState(stateDisableCleanup);
- }
- }
- switch (mState)
- {
- case stateDisableCleanup:
- // Clean up and reset everything.
- closeSocket();
- deleteAllSessions();
- mConnectorEstablished = false;
- mAccountLoggedIn = false;
- mAccountPassword.clear();
- mVoiceAccountServerURI.clear();
- setState(stateDisabled);
- break;
- case stateDisabled:
- if (mTuningMode ||
- (mVoiceEnabled && mProcessChannels && !mAccountName.empty()))
- {
- setState(stateStart);
- }
- break;
- case stateStart:
- if (!LLStartUp::isLoggedIn())
- {
- break;
- }
- if (gSavedSettings.getBool("CmdLineDisableVoice"))
- {
- // Voice is locked out, we must not launch the vivox daemon.
- setState(stateJail);
- }
- else if (!mProcess || !mProcess->isRunning())
- {
- killDaemon();
- // Refresh the log level
- mLogLevel =
- llmin((U32)gSavedSettings.getU32("VivoxDebugLevel"), 10U);
- // Launch the voice daemon
- std::string exe_path = gDirUtil.getExecutableDir();
- std::string full_path;
- #if LL_DARWIN
- full_path = exe_path + "/../Resources/SLVoice";
- #elif LL_WINDOWS
- full_path = exe_path + "\\SLVoice.exe";
- #elif LL_LINUX
- mDeprecatedClient = false;
- // Linux SLVoice is alas totally deprecated, so we better use
- // Wine to run the Windows binary if we want working voice in
- // SL (and most probably as well in OpenSim grids using Vivox).
- // We get the Windows binary path from the LL_WINE_SLVOICE
- // environment variable...
- // See install-wine-SLVoice.sh and viewer.conf in linux_tools/
- char* envvar = getenv("LL_WINE_SLVOICE");
- if (envvar)
- {
- full_path.assign(envvar);
- if (!full_path.empty())
- {
- size_t i = full_path.rfind('/');
- if (i == std::string::npos || i == 0)
- {
- llwarns << "Invalid LL_WINE_SLVOICE environment variable setting: '"
- << full_path
- << "' does not point to a program. Falling back to Linux SLVoice."
- << llendl;
- full_path.clear();
- }
- else
- {
- exe_path = full_path.substr(0, i - 1);
- }
- }
- }
- if (full_path.empty())
- {
- if (gIsInSecondLife)
- {
- llwarns << "Using the deprecated Linux SLVoice binary. Expect voice to be flaky..."
- << llendl;
- }
- full_path = exe_path + "/SLVoice";
- mDeprecatedClient = true;
- }
- #endif
- // See if the vivox executable exists
- if (LLFile::isfile(full_path))
- {
- std::string host =
- gSavedSettings.getString("VivoxVoiceHost");
- // Port base, clamped to non-priviledged ports and so that
- // with the added cyclic offset, we are till below the
- // highest possible port number.
- U32 port =
- llclamp((U32)gSavedSettings.getU32("VivoxVoicePort"),
- 1024U, 65435U);
- // Vivox executable exists. Build the command line and
- // launch the daemon.
- mProcess = new LLProcessLauncher();
- mProcess->setExecutable(full_path);
- mProcess->setWorkingDirectory(exe_path);
- // Add an auto-incremented offset at each new connection
- // so that the old connection port would not be reused
- // (when reconnecting at a short interval of time, such as
- // during retries) before it got time to close...
- // The offset is also randomized on first connection, so
- // to lower the risk of a port collision with other running
- // viewer sessions.
- static U32 portoffset = 49 + ll_rand(49); // 0 to 98
- port += portoffset++;
- portoffset %= 100; // Cycle the offset over 100 ports
- mProcess->addArgument("-i");
- mProcess->addArgument(llformat("%s:%d", host.c_str(),
- port));
- S32 log_level = mLogLevel;
- #if LL_LINUX
- if (mDeprecatedClient)
- {
- log_level = log_level == 0 ? -1 : 10;
- }
- #endif
- mProcess->addArgument("-ll");
- mProcess->addArgument(llformat("%d", log_level));
- #if LL_LINUX
- if (!mDeprecatedClient)
- #endif
- {
- std::string log_dir =
- gDirUtil.getFullPath(LL_PATH_LOGS, "");
- mProcess->addArgument("-lf");
- mProcess->addArgument(log_dir);
- mProcess->addArgument("-lp");
- mProcess->addArgument("SLVoice");
- mProcess->addArgument("-ls");
- mProcess->addArgument(".log");
- S32 timeout =
- gSavedSettings.getU32("VivoxShutdownTimeout");
- mProcess->addArgument("-st");
- mProcess->addArgument(llformat("%d", timeout));
- }
- if (mProcess->launch() != 0)
- {
- llwarns << "Failure to launch SLVoice. Giving up."
- << llendl;
- killDaemon();
- setState(stateJail);
- break;
- }
- mDaemonHost = LLHost(host, port);
- }
- else
- {
- llwarns << full_path << " not found. Giving up." << llendl;
- setState(stateJail);
- break;
- }
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
- setState(stateDaemonLaunched);
- // Dirty the states we will need to sync with the daemon when
- // it comes up.
- mPTTDirty = true;
- mMicVolumeDirty = true;
- mSpeakerVolumeDirty = true;
- mSpeakerMuteDirty = true;
- // These only need to be set if they are not default (i.e.
- // empty strings).
- mCaptureDeviceDirty = !mCaptureDevice.empty();
- mRenderDeviceDirty = !mRenderDevice.empty();
- // Delay the first socket connection attempt to let the process
- // deamon start and listen for the socket. HB
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
- }
- break;
- case stateDaemonLaunched:
- if (mUpdateTimer.hasExpired())
- {
- LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
- if (!mSocket)
- {
- LL_DEBUGS("Voice") << "Creating socket to vivox daemon"
- << LL_ENDL;
- mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
- }
- mConnected = mSocket->blockingConnect(mDaemonHost);
- if (mConnected)
- {
- LL_DEBUGS("Voice") << "Connected to socket" << LL_ENDL;
- setState(stateConnecting);
- break;
- }
- LL_DEBUGS("Voice") << "Failure to connect to socket" << LL_ENDL;
- // If the connection failed, the socket may have been put into
- // a bad state; delete it.
- closeSocket();
- // Give up after 12 failed attempts in total. HB
- if (mRetries >= 12)
- {
- llwarns << "Too many retries. Giving up." << llendl;
- setState(stateJail);
- break;
- }
- // Every 3 failed connection retries, try and restart the
- // daemon itself... HB
- if (++mRetries % 3 == 0)
- {
- killDaemon();
- setState(stateStart);
- }
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
- }
- break;
- case stateConnecting:
- // Cannot do this until we have the pump available.
- if (mPump)
- {
- // Attach the pumps and pipes
- LLPumpIO::chain_t read_chain;
- read_chain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
- read_chain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
- mPump->addChain(read_chain, 0.f); // 0.f = never expire
- setState(stateConnected);
- }
- break;
- case stateConnected:
- // Initial devices query
- getCaptureDevicesSendMessage();
- getRenderDevicesSendMessage();
- setupVADParams();
- mLoginRetryCount = 0;
- setState(stateIdle);
- break;
- case stateIdle:
- // This is the idle state where we are connected to the daemon but
- // have not set up a connector yet.
- if (mTuningMode)
- {
- mTuningExitState = stateIdle;
- setState(stateMicTuningStart);
- }
- else if (!mVoiceEnabled || !mProcessChannels)
- {
- // We never started up the connector. This will shut down the
- // daemon.
- setState(stateConnectorStopped);
- }
- else if (!mAccountName.empty() && mAccountPassword.empty())
- {
- requestVoiceAccountProvision();
- }
- break;
- case stateMicTuningStart:
- if (mUpdateTimer.hasExpired())
- {
- if (mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // These cannot be changed while in tuning mode. Set them
- // before starting.
- std::ostringstream stream;
- buildSetCaptureDevice(stream);
- buildSetRenderDevice(stream);
- if (!stream.str().empty())
- {
- writeString(stream.str());
- }
- // This will come around again in the same state and start
- // the capture, after the timer expires.
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- }
- else
- {
- // Duration parameter is currently unused, per Mike S.
- tuningCaptureStartSendMessage(10000);
- setState(stateMicTuningRunning);
- }
- }
- break;
- case stateMicTuningRunning:
- if (!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // All of these conditions make us leave tuning mode.
- setState(stateMicTuningStop);
- }
- // Process mic/speaker volume changes
- else if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
- {
- std::ostringstream stream;
- if (mTuningMicVolumeDirty)
- {
- llinfos << "Setting tuning mic level to "
- << mTuningMicVolume << llendl;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.SetMicLevel.1\"><Level>"
- << mTuningMicVolume << "</Level></Request>\n\n\n";
- }
- if (mTuningSpeakerVolumeDirty)
- {
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.SetSpeakerLevel.1\">"
- << "<Level>" << mTuningSpeakerVolume
- << "</Level></Request>\n\n\n";
- }
- mTuningMicVolumeDirty = false;
- mTuningSpeakerVolumeDirty = false;
- if (!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- break;
- case stateMicTuningStop:
- {
- // Transition out of mic tuning
- tuningCaptureStopSendMessage();
- setState(mTuningExitState);
- // If we exited just to change devices, this will keep us from
- // re-entering too fast.
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- break;
- }
- case stateConnectorStart:
- if (!mVoiceEnabled || !mProcessChannels)
- {
- // We were never logged in. This will shut down the connector.
- setState(stateLoggedOut);
- }
- else if (!mVoiceAccountServerURI.empty())
- {
- connectorCreate();
- }
- break;
- case stateConnectorStarting:
- // Waiting for connector handle connectorCreateResponse() will
- // transition from here to stateConnectorStarted.
- break;
- case stateConnectorStarted: // Connector handle received
- if (!mVoiceEnabled || !mProcessChannels)
- {
- // We were never logged in. This will shut down the connector.
- setState(stateLoggedOut);
- }
- else
- {
- // The connector is started. Send a login message.
- setState(stateNeedsLogin);
- }
- break;
- case stateLoginRetry:
- if (mLoginRetryCount == 0)
- {
- // First retry: display a message to the user
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
- }
- ++mLoginRetryCount;
- if (mLoginRetryCount > MAX_LOGIN_RETRIES)
- {
- llwarns << "Too many login retries, giving up." << llendl;
- setState(stateLoginFailed);
- }
- else
- {
- llinfos << "Will retry login in " << LOGIN_RETRY_SECONDS
- << " seconds." << llendl;
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
- setState(stateLoginRetryWait);
- }
- break;
- case stateLoginRetryWait:
- if (mUpdateTimer.hasExpired())
- {
- setState(stateNeedsLogin);
- }
- break;
- case stateNeedsLogin:
- if (!mAccountPassword.empty())
- {
- setState(stateLoggingIn);
- loginSendMessage();
- }
- break;
- case stateLoggingIn: // Waiting for account handle
- // loginResponse() will transition from here to stateLoggedIn.
- break;
- case stateLoggedIn: // Account handle received
- {
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
- // Set up the mute list observer if it has not been set up already.
- if (!sMuteListListening)
- {
- LLMuteList::addObserver(&sMutelistListener);
- sMuteListListening = true;
- }
- // Set the initial state of mic mute, local speaker volume, etc.
- std::ostringstream stream;
- buildLocalAudioUpdates(stream);
- if (!stream.str().empty())
- {
- writeString(stream.str());
- }
- setState(stateNoChannel);
- // Initial kick-off of channel lookup logic
- parcelChanged();
- break;
- }
- case stateNoChannel:
- if (mSessionTerminateRequested || !mVoiceEnabled ||
- !mProcessChannels)
- {
- // *TODO: is this the right way out of this state ?
- setState(stateSessionTerminated);
- }
- else if (mTuningMode)
- {
- mTuningExitState = stateNoChannel;
- setState(stateMicTuningStart);
- }
- else if (sessionNeedsRelog(mNextAudioSession))
- {
- requestRelog();
- setState(stateSessionTerminated);
- }
- else if (mNextAudioSession)
- {
- sessionState* oldSession = mAudioSession;
- mAudioSession = mNextAudioSession;
- if (!mAudioSession->mReconnect)
- {
- mNextAudioSession = NULL;
- }
- // The old session may now need to be deleted.
- reapSession(oldSession);
- if (!mAudioSession->mHandle.empty())
- {
- // Connect to a session by session handle
- sessionMediaConnectSendMessage(mAudioSession);
- }
- else
- {
- // Connect to a session by URI
- sessionCreateSendMessage(mAudioSession, true, false);
- }
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
- setState(stateJoiningSession);
- }
- else if (!mSpatialSessionURI.empty())
- {
- // If we're not headed elsewhere and have a spatial URI,
- // return to spatial.
- switchChannel(mSpatialSessionURI, true, false, false,
- mSpatialSessionCredentials);
- }
- break;
- case stateJoiningSession: // Waiting for session handle
- // joinedAudioSession() will transition from here to
- // stateSessionJoined.
- if (!mVoiceEnabled || !mProcessChannels)
- {
- // User bailed out during connect -- jump straight to teardown.
- setState(stateSessionTerminated);
- }
- else if (mSessionTerminateRequested)
- {
- if (mAudioSession && !mAudioSession->mHandle.empty())
- {
- // Only allow direct exits from this state in P2P calls
- // (for cancelling an invite).
- // Terminating a half-connected session on other types of
- // calls seems to break something in the vivox gateway.
- if (mAudioSession->mIsP2P)
- {
- sessionGroupTerminateSendMessage(mAudioSession);
- setState(stateSessionTerminated);
- }
- }
- }
- break;
- case stateSessionJoined: // Session handle received
- // It appears that we need to wait for BOTH the
- // SessionGroup.AddSession response and the
- // SessionStateChangeEvent with state 4 before continuing from
- // this state. They can happen in either order, and if we do not
- // wait for both, things can get stuck.
- // For now, the SessionGroup.AddSession response handler sets
- // mSessionHandle and the SessionStateChangeEvent handler
- // transitions to stateSessionJoined.
- // This is a cheap way to make sure both have happened before
- // proceeding.
- if (mAudioSession && mAudioSession->mVoiceEnabled)
- {
- // Dirty state that may need to be sync'ed with the daemon.
- mPTTDirty = true;
- mSpeakerVolumeDirty = true;
- mSpatialCoordsDirty = true;
- setState(stateRunning);
- // Start the throttle timer
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- // Events that need to happen when a session is joined could go
- // here. Maybe send initial spatial data ?
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
- }
- else if (!mVoiceEnabled || !mProcessChannels)
- {
- // User bailed out during connect; jump straight to teardown.
- setState(stateSessionTerminated);
- }
- else if (mSessionTerminateRequested)
- {
- // Only allow direct exits from this state in P2P calls (for
- // cancelling an invite).
- // Terminating a half-connected session on other types of calls
- // seems to break something in the vivox gateway.
- if (mAudioSession && mAudioSession->mIsP2P)
- {
- sessionGroupTerminateSendMessage(mAudioSession);
- setState(stateSessionTerminated);
- }
- }
- break;
- case stateRunning: // Steady state
- // Disabling voice or disconnect requested.
- if (!mVoiceEnabled || !mProcessChannels ||
- mSessionTerminateRequested)
- {
- leaveAudioSession();
- }
- else
- {
- // Figure out whether the PTT state needs to change
- bool new_ptt;
- if (gVoiceClient.mUsePTT)
- {
- // If configured to use PTT, track the user state.
- new_ptt = gVoiceClient.mUserPTTState;
- }
- else
- {
- // If not configured to use PTT, it should always be true
- // (otherwise the user will be unable to speak).
- new_ptt = true;
- }
- if (gVoiceClient.mMuteMic)
- {
- // This always overrides any other PTT setting.
- new_ptt = false;
- }
- // Dirty if state changed.
- if (mPTT != new_ptt)
- {
- mPTT = new_ptt;
- mPTTDirty = true;
- }
- if (!inSpatialChannel())
- {
- // When in a non-spatial channel, never send positional
- // updates.
- mSpatialCoordsDirty = false;
- }
- else
- {
- // Do the calculation that enforces the listener<->speaker
- // tether (and also updates the real camera position)
- enforceTether();
- }
- // Send an update if the ptt state has changed (which should
- // not be able to happen that often; the user can only click so
- // fast) or every 10hz, whichever is sooner.
- if ((mAudioSession && mAudioSession->mVolumeDirty) ||
- mPTTDirty || mSpeakerVolumeDirty ||
- mUpdateTimer.hasExpired())
- {
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- sendPositionalUpdate();
- }
- // Dity hack to get voice to initialize properly after login
- if (first_run)
- {
- first_run = false;
- LLVoiceChannel::suspend();
- LLVoiceChannel::resume();
- }
- }
- break;
- case stateLeavingSession: // Waiting for terminate session response
- // The handler for the Session.Terminate response will transition
- // from here to stateSessionTerminated.
- break;
- case stateSessionTerminated:
- // Must do this first, since it uses mAudioSession.
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
- if (mAudioSession)
- {
- sessionState* old_sessionp = mAudioSession;
- mAudioSession = NULL;
- // The old session may now need to be deleted.
- reapSession(old_sessionp);
- }
- else
- {
- llwarns << "stateSessionTerminated with NULL mAudioSession"
- << llendl;
- }
- // Always reset the terminate request flag when we get here.
- mSessionTerminateRequested = false;
- if ((mVoiceEnabled && mProcessChannels) && !mRelogRequested)
- {
- // Just leaving a channel, go back to stateNoChannel (the
- // "logged in but have no channel" state).
- setState(stateNoChannel);
- }
- else
- {
- // Shutting down voice, continue with disconnecting.
- logout();
- // The state machine will take it from here
- mRelogRequested = false;
- }
- break;
- case stateLoggingOut: // Waiting for logout response
- // The handler for the AccountLoginStateChangeEvent will transition
- // from here to stateLoggedOut.
- break;
- case stateLoggedOut: // Logout response received
- // Once we are logged out, all these things are invalid.
- mAccountLoggedIn = false;
- deleteAllSessions();
- if (mVoiceEnabled && mProcessChannels && !mRelogRequested)
- {
- // User was logged out, but wants to be logged in. Send a new
- // login request.
- setState(stateNeedsLogin);
- }
- else
- {
- // Shut down the connector
- connectorShutdown();
- }
- break;
- case stateConnectorStopping: // Waiting for connector stop
- // The handler for the Connector.InitiateShutdown response will
- // transition from here to stateConnectorStopped.
- break;
- case stateConnectorStopped: // Connector stop received
- setState(stateDisableCleanup);
- break;
- case stateConnectorFailed:
- setState(stateConnectorFailedWaiting);
- break;
- case stateConnectorFailedWaiting:
- if (!mVoiceEnabled || !mProcessChannels)
- {
- setState(stateDisableCleanup);
- }
- break;
- case stateLoginFailed:
- setState(stateLoginFailedWaiting);
- break;
- case stateLoginFailedWaiting:
- if (!mVoiceEnabled || !mProcessChannels)
- {
- setState(stateDisableCleanup);
- }
- break;
- case stateJoinSessionFailed:
- // Transition to error state. Send out any notifications here.
- if (mAudioSession)
- {
- llwarns << "stateJoinSessionFailed: ("
- << mAudioSession->mErrorStatusCode << "): "
- << mAudioSession->mErrorStatusString << llendl;
- }
- else
- {
- llwarns << "stateJoinSessionFailed with no current session"
- << llendl;
- }
- notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
- setState(stateJoinSessionFailedWaiting);
- break;
- case stateJoinSessionFailedWaiting:
- // Joining a channel failed, either due to a failed channel name ->
- // sip url lookup or an error from the join message.
- // Region crossings may leave this state and try the join again.
- if (mSessionTerminateRequested)
- {
- setState(stateSessionTerminated);
- }
- break;
- case stateJail:
- // We have given up. Do nothing.
- break;
- }
- }
- void LLVoiceVivox::closeSocket()
- {
- mSocket.reset();
- mConnected = mConnectorEstablished = mAccountLoggedIn = false;
- }
- void LLVoiceVivox::loginSendMessage()
- {
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Account.Login.1\"><ConnectorHandle>"
- << mConnectorHandle << "</ConnectorHandle><AccountName>"
- << mAccountName << "</AccountName><AccountPassword>"
- << mAccountPassword << "</AccountPassword>"
- << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
- << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
- << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>"
- << "<BuddyManagementMode>Application</BuddyManagementMode>"
- << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
- << "</Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::logout()
- {
- // Ensure that we will re-request provisioning before logging in again
- mAccountPassword.clear();
- mVoiceAccountServerURI.clear();
- setState(stateLoggingOut);
- logoutSendMessage();
- }
- void LLVoiceVivox::logoutSendMessage()
- {
- if (mAccountLoggedIn)
- {
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Account.Logout.1\"><AccountHandle>"
- << mAccountHandle << "</AccountHandle></Request>\n\n\n";
- mAccountLoggedIn = false;
- writeString(stream.str());
- }
- }
- void LLVoiceVivox::sessionCreateSendMessage(sessionState* session,
- bool start_audio,
- bool start_text)
- {
- LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
- session->mCreateInProgress = true;
- if (start_audio)
- {
- session->mMediaConnectInProgress = true;
- }
- std::ostringstream stream;
- stream << "<Request requestId=\"" << session->mSIPURI
- << "\" action=\"Session.Create.1\"><AccountHandle>"
- << mAccountHandle << "</AccountHandle><URI>" << session->mSIPURI
- << "</URI>";
- static const std::string allowed_chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- "0123456789"
- "-._~";
- if (!session->mHash.empty())
- {
- stream << "<Password>" << LLURI::escape(session->mHash, allowed_chars)
- << "</Password>"
- << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
- }
- stream << "<ConnectAudio>" << (start_audio ? "true" : "false")
- << "</ConnectAudio><ConnectText>" << (start_text ? "true" : "false")
- << "</ConnectText><Name>" << mChannelName
- << "</Name><VoiceFontID>0</VoiceFontID></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::sessionGroupAddSessionSendMessage(sessionState* session,
- bool start_audio,
- bool start_text)
- {
- LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL;
- session->mCreateInProgress = true;
- if (start_audio)
- {
- session->mMediaConnectInProgress = true;
- }
- std::string password;
- if (!session->mHash.empty())
- {
- static const std::string allowed_chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- "0123456789"
- "-._~";
- password = LLURI::escape(session->mHash, allowed_chars);
- }
- std::ostringstream stream;
- stream << "<Request requestId=\"" << session->mSIPURI
- << "\" action=\"SessionGroup.AddSession.1\"><SessionGroupHandle>"
- << session->mGroupHandle << "</SessionGroupHandle><URI>"
- << session->mSIPURI << "</URI><Name>" << mChannelName
- << "</Name><ConnectAudio>" << (start_audio ? "true" : "false")
- << "</ConnectAudio><ConnectText>" << (start_text ? "true" : "false")
- << "<VoiceFontID>0</VoiceFontID></ConnectText><Password>"
- << password << "</Password>"
- << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
- << "</Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::sessionMediaConnectSendMessage(sessionState* session)
- {
- LL_DEBUGS("Voice") << "Connecting audio to session handle: "
- << session->mHandle << LL_ENDL;
- session->mMediaConnectInProgress = true;
- std::ostringstream stream;
- stream << "<Request requestId=\"" << session->mHandle
- << "\" action=\"Session.MediaConnect.1\"><SessionGroupHandle>"
- << session->mGroupHandle << "</SessionGroupHandle><SessionHandle>"
- << session->mHandle << "</SessionHandle><VoiceFontID>0</VoiceFontID>"
- <<"<Media>Audio</Media></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::sessionTextConnectSendMessage(sessionState* session)
- {
- LL_DEBUGS("Voice") << "Connecting text to session handle: "
- << session->mHandle << LL_ENDL;
- std::ostringstream stream;
- stream << "<Request requestId=\"" << session->mHandle
- << "\" action=\"Session.TextConnect.1\"><SessionGroupHandle>"
- << session->mGroupHandle << "</SessionGroupHandle><SessionHandle>"
- << session->mHandle << "</SessionHandle></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::sessionTerminate()
- {
- mSessionTerminateRequested = true;
- }
- void LLVoiceVivox::requestRelog()
- {
- mSessionTerminateRequested = true;
- mRelogRequested = true;
- }
- //virtual
- void LLVoiceVivox::leaveAudioSession()
- {
- if (mAudioSession)
- {
- LL_DEBUGS("Voice") << "Leaving session: " << mAudioSession->mSIPURI
- << LL_ENDL;
- switch (mState)
- {
- case stateNoChannel:
- // In this case, we want to pretend the join failed so our
- // state machine does not get stuck.
- // Skip the join failed transition state so we do not send out
- // error notifications.
- setState(stateJoinSessionFailedWaiting);
- break;
- case stateJoiningSession:
- case stateSessionJoined:
- case stateRunning:
- if (!mAudioSession->mHandle.empty())
- {
- sessionGroupTerminateSendMessage(mAudioSession);
- setState(stateLeavingSession);
- }
- else
- {
- llwarns << "Called without session handle" << llendl;
- setState(stateSessionTerminated);
- }
- break;
- case stateJoinSessionFailed:
- case stateJoinSessionFailedWaiting:
- setState(stateSessionTerminated);
- break;
- default:
- llwarns << "Called from unknown state" << llendl;
- }
- }
- else
- {
- llwarns << "Called with no active session" << llendl;
- setState(stateSessionTerminated);
- }
- }
- void LLVoiceVivox::sessionGroupTerminateSendMessage(sessionState* session)
- {
- LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle "
- << session->mGroupHandle << LL_ENDL;
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"SessionGroup.Terminate.1\"><SessionGroupHandle>"
- << session->mGroupHandle << "</SessionGroupHandle></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::getCaptureDevicesSendMessage()
- {
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.GetCaptureDevices.1\"></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::getRenderDevicesSendMessage()
- {
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.GetRenderDevices.1\"></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox:: addCaptureDevice(const std::string& display_name,
- const std::string& device_id)
- {
- // Yep, it may happen... In this case, and since we will not be able to
- // set (and therefore use) this device, just skip it. HB
- if (device_id.empty())
- {
- return;
- }
- if (display_name.empty()) // Could happen, I suppose... HB
- {
- mCaptureDevices.emplace(device_id, device_id);
- }
- else // Normal case
- {
- mCaptureDevices.emplace(display_name, device_id);
- }
- }
- void LLVoiceVivox::setCaptureDevice(const std::string& device_id)
- {
- if (device_id == "Default")
- {
- if (!mCaptureDevice.empty())
- {
- mCaptureDevice.clear();
- mCaptureDeviceDirty = true;
- }
- return;
- }
- if (mCaptureDevice != device_id)
- {
- mCaptureDevice = device_id;
- mCaptureDeviceDirty = true;
- }
- }
- void LLVoiceVivox::addRenderDevice(const std::string& display_name,
- const std::string& device_id)
- {
- // Yep, it may happen... In this case, and since we will not be able to
- // set (and therefore use) this device, just skip it. HB
- if (device_id.empty())
- {
- return;
- }
- if (display_name.empty()) // Could happen, I suppose... HB
- {
- mRenderDevices.emplace(device_id, device_id);
- }
- else // Normal case
- {
- mRenderDevices.emplace(display_name, device_id);
- }
- }
- void LLVoiceVivox::setRenderDevice(const std::string& device_id)
- {
- if (device_id == "Default")
- {
- if (!mRenderDevice.empty())
- {
- mRenderDevice.clear();
- mRenderDeviceDirty = true;
- }
- return;
- }
- if (mRenderDevice != device_id)
- {
- mRenderDevice = device_id;
- mRenderDeviceDirty = true;
- }
- }
- void LLVoiceVivox::setTuningMode(bool tuning_on)
- {
- mTuningMode = tuning_on;
- if (tuning_on && mState >= stateNoChannel)
- {
- sessionTerminate();
- }
- }
- bool LLVoiceVivox::inTuningMode()
- {
- return mState == stateMicTuningRunning;
- }
- void LLVoiceVivox::tuningRenderStartSendMessage(const std::string& name,
- bool loop)
- {
- mTuningAudioFile = name;
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.RenderAudioStart.1\"><SoundFilePath>"
- << mTuningAudioFile << "</SoundFilePath><Loop>"
- << (loop ? "1" : "0") << "</Loop></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::tuningRenderStopSendMessage()
- {
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.RenderAudioStop.1\"><SoundFilePath>"
- << mTuningAudioFile << "</SoundFilePath></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::tuningCaptureStartSendMessage(S32 duration)
- {
- LL_DEBUGS("Voice") << "Sending CaptureAudioStart" << LL_ENDL;
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.CaptureAudioStart.1\"><Duration>"
- << duration << "</Duration></Request>\n\n\n";
- writeString(stream.str());
- }
- void LLVoiceVivox::tuningCaptureStopSendMessage()
- {
- LL_DEBUGS("Voice") << "Sending CaptureAudioStop" << LL_ENDL;
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.CaptureAudioStop.1\"></Request>\n\n\n";
- writeString(stream.str());
- mTuningEnergy = 0.f;
- }
- void LLVoiceVivox::tuningSetMicVolume(F32 volume)
- {
- S32 scaled_volume = scale_mic_volume(volume);
- if (scaled_volume != mTuningMicVolume)
- {
- mTuningMicVolume = scaled_volume;
- mTuningMicVolumeDirty = true;
- }
- }
- #if 0 // Not used
- void LLVoiceVivox::tuningSetSpeakerVolume(F32 volume)
- {
- S32 scaled_volume = scale_speaker_volume(volume);
- if (scaled_volume != mTuningSpeakerVolume)
- {
- mTuningSpeakerVolume = scaled_volume;
- mTuningSpeakerVolumeDirty = true;
- }
- }
- #endif
- bool LLVoiceVivox::deviceSettingsAvailable()
- {
- return mConnected && !mRenderDevices.empty();
- }
- void LLVoiceVivox::refreshDeviceLists(bool clear_current_list)
- {
- if (clear_current_list)
- {
- mCaptureDevices.clear();
- mRenderDevices.clear();
- }
- getCaptureDevicesSendMessage();
- getRenderDevicesSendMessage();
- }
- void LLVoiceVivox::daemonDied()
- {
- // The daemon died, so the connection is gone. Reset everything and start
- // over.
- llwarns << "Connection to Vivox daemon lost. Resetting state." << llendl;
- // Try to relaunch the daemon
- setState(stateDisableCleanup);
- }
- void LLVoiceVivox::giveUp()
- {
- // Avoid infinite loop while giving up...
- static bool giving_up = false;
- if (!giving_up)
- {
- giving_up = true;
- // All has failed. Clean up and stop trying.
- closeSocket();
- deleteAllSessions();
- setState(stateJail);
- llwarns << "Unrecoverable error: voice permanently disabled."
- << llendl;
- }
- }
- static void oldSDKTransform(LLVector3& left, LLVector3& up, LLVector3& at,
- LLVector3d& pos, LLVector3& vel)
- {
- // the new at, up, left vectors and the new position and velocity
- F32 nat[3], nup[3], nl[3];
- F64 npos[3];
- // The original XML command was sent like this:
- /*
- << "<Position>"
- << "<X>" << pos[VX] << "</X>"
- << "<Y>" << pos[VZ] << "</Y>"
- << "<Z>" << pos[VY] << "</Z>"
- << "</Position>"
- << "<Velocity>"
- << "<X>" << mAvatarVelocity[VX] << "</X>"
- << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
- << "<Z>" << mAvatarVelocity[VY] << "</Z>"
- << "</Velocity>"
- << "<AtOrientation>"
- << "<X>" << l.mV[VX] << "</X>"
- << "<Y>" << u.mV[VX] << "</Y>"
- << "<Z>" << a.mV[VX] << "</Z>"
- << "</AtOrientation>"
- << "<UpOrientation>"
- << "<X>" << l.mV[VZ] << "</X>"
- << "<Y>" << u.mV[VY] << "</Y>"
- << "<Z>" << a.mV[VZ] << "</Z>"
- << "</UpOrientation>"
- << "<LeftOrientation>"
- << "<X>" << l.mV [VY] << "</X>"
- << "<Y>" << u.mV [VZ] << "</Y>"
- << "<Z>" << a.mV [VY] << "</Z>"
- << "</LeftOrientation>";
- */
- #if 1
- // This was the original transform done when building the XML command
- nat[0] = left.mV[VX];
- nat[1] = up.mV[VX];
- nat[2] = at.mV[VX];
- nup[0] = left.mV[VZ];
- nup[1] = up.mV[VY];
- nup[2] = at.mV[VZ];
- nl[0] = left.mV[VY];
- nl[1] = up.mV[VZ];
- nl[2] = at.mV[VY];
- npos[0] = pos.mdV[VX];
- npos[1] = pos.mdV[VZ];
- npos[2] = pos.mdV[VY];
- for (S32 i = 0; i < 3; ++i)
- {
- at.mV[i] = nat[i];
- up.mV[i] = nup[i];
- left.mV[i] = nl[i];
- pos.mdV[i] = npos[i];
- }
- // This was the original transform done in the SDK
- nat[0] = at.mV[2];
- nat[1] = 0; // y component of at vector is always 0, this was up[2]
- nat[2] = -1 * left.mV[2];
- // We override whatever the application gives us
- nup[0] = 0; // x component of up vector is always 0
- nup[1] = 1; // y component of up vector is always 1
- nup[2] = 0; // z component of up vector is always 0
- nl[0] = at.mV[0];
- nl[1] = 0; // y component of left vector is always zero, this was up[0]
- nl[2] = -1 * left.mV[0];
- npos[2] = pos.mdV[2] * -1.0;
- npos[1] = pos.mdV[1];
- npos[0] = pos.mdV[0];
- for (S32 i = 0; i < 3; ++i)
- {
- at.mV[i] = nat[i];
- up.mV[i] = nup[i];
- left.mV[i] = nl[i];
- pos.mdV[i] = npos[i];
- }
- #else
- // This is the compose of the two transforms (at least, that's what I'm trying for)
- nat[0] = at.mV[VX];
- nat[1] = 0; // y component of at vector is always 0, this was up[2]
- nat[2] = -1 * up.mV[VZ];
- // We override whatever the application gives us
- nup[0] = 0; // x component of up vector is always 0
- nup[1] = 1; // y component of up vector is always 1
- nup[2] = 0; // z component of up vector is always 0
- nl[0] = left.mV[VX];
- nl[1] = 0; // y component of left vector is always zero, this was up[0]
- nl[2] = -1 * left.mV[VY];
- npos[0] = pos.mdV[VX];
- npos[1] = pos.mdV[VZ];
- npos[2] = pos.mdV[VY] * -1.0;
- for (S32 i = 0; i < 3; ++i)
- {
- at.mV[i] = nat[i];
- up.mV[i] = nup[i];
- left.mV[i] = nl[i];
- pos.mdV[i] = npos[i];
- }
- #endif
- }
- void LLVoiceVivox::sendPositionalUpdate()
- {
- std::ostringstream stream;
- if (mSpatialCoordsDirty)
- {
- LLVector3 l, u, a, vel;
- LLVector3d pos;
- mSpatialCoordsDirty = false;
- // Always send both speaker and listener positions together.
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Session.Set3DPosition.1\">"
- << "<SessionHandle>" << getAudioSessionHandle()
- << "</SessionHandle>";
- stream << "<SpeakerPosition>";
- l = mAvatarRot.getLeftRow();
- u = mAvatarRot.getUpRow();
- a = mAvatarRot.getFwdRow();
- pos = mAvatarPosition;
- vel = mAvatarVelocity;
- // SLIM SDK: the old SDK was doing a transform on the passed
- // coordinates that the new one does not do anymore. The old transform
- // is replicated by this function.
- oldSDKTransform(l, u, a, pos, vel);
- stream << "<Position><X>" << pos.mdV[VX] << "</X><Y>" << pos.mdV[VY]
- << "</Y><Z>" << pos.mdV[VZ] << "</Z></Position><Velocity><X>"
- << vel.mV[VX] << "</X><Y>" << vel.mV[VY] << "</Y><Z>"
- << vel.mV[VZ] << "</Z></Velocity><AtOrientation><X>" << a.mV[VX]
- << "</X><Y>" << a.mV[VY] << "</Y><Z>" << a.mV[VZ]
- << "</Z></AtOrientation><UpOrientation><X>" << u.mV[VX]
- << "</X><Y>" << u.mV[VY] << "</Y><Z>" << u.mV[VZ]
- << "</Z></UpOrientation><LeftOrientation><X>" << l.mV[VX]
- << "</X><Y>" << l.mV[VY] << "</Y><Z>" << l.mV[VZ]
- << "</Z></LeftOrientation>";
- stream << "</SpeakerPosition><ListenerPosition>";
- LLVector3d ear_pos;
- LLVector3 ear_vel;
- LLMatrix3 ear_rot;
- switch (mEarLocation)
- {
- case earLocCamera:
- ear_pos = mCameraPosition;
- ear_vel = mCameraVelocity;
- ear_rot = mCameraRot;
- break;
- case earLocAvatar:
- ear_pos = mAvatarPosition;
- ear_vel = mAvatarVelocity;
- ear_rot = mAvatarRot;
- break;
- case earLocMixed:
- ear_pos = mAvatarPosition;
- ear_vel = mAvatarVelocity;
- ear_rot = mCameraRot;
- break;
- default:
- llerrs << "Invalid ear location: " << mEarLocation << llendl;
- }
- l = ear_rot.getLeftRow();
- u = ear_rot.getUpRow();
- a = ear_rot.getFwdRow();
- pos = ear_pos;
- vel = ear_vel;
- oldSDKTransform(l, u, a, pos, vel);
- stream << "<Position><X>" << pos.mdV[VX] << "</X><Y>" << pos.mdV[VY]
- << "</Y><Z>" << pos.mdV[VZ] << "</Z></Position><Velocity><X>"
- << vel.mV[VX] << "</X><Y>" << vel.mV[VY] << "</Y><Z>"
- << vel.mV[VZ] << "</Z></Velocity><AtOrientation><X>" << a.mV[VX]
- << "</X><Y>" << a.mV[VY] << "</Y><Z>" << a.mV[VZ]
- << "</Z></AtOrientation><UpOrientation><X>" << u.mV[VX]
- << "</X><Y>" << u.mV[VY] << "</Y><Z>" << u.mV[VZ]
- << "</Z></UpOrientation><LeftOrientation><X>" << l.mV[VX]
- << "</X><Y>" << l.mV[VY] << "</Y><Z>" << l.mV[VZ]
- << "</Z></LeftOrientation>";
- stream << "</ListenerPosition></Request>\n\n\n";
- }
- if (mAudioSession && mAudioSession->mVolumeDirty)
- {
- particip_map_t::iterator iter =
- mAudioSession->mParticipantsByURI.begin();
- mAudioSession->mVolumeDirty = false;
- for ( ; iter != mAudioSession->mParticipantsByURI.end(); ++iter)
- {
- participantState* p = iter->second;
- if (p->mVolumeDirty)
- {
- // Cannot set volume/mute for yourself
- if (!p->mIsSelf)
- {
- S32 volume = 56; // nominal default value
- bool mute = p->mOnMuteList;
- if (p->mUserVolume != -1)
- {
- // Scale from user volume in the range 0-400 (with 100
- // as "normal") to vivox volume in the range 0-100
- // (with 56 as "normal")
- if (p->mUserVolume < 100)
- {
- volume = (p->mUserVolume * 56) / 100;
- }
- else
- {
- volume = 44 * (p->mUserVolume - 100) / 300 + 56;
- }
- }
- else if (p->mVolume != -1)
- {
- // Use the previously reported internal volume (comes
- // in with a ParticipantUpdatedEvent)
- volume = p->mVolume;
- }
- if (mute)
- {
- // SetParticipantMuteForMe does not work in P2P
- // sessions. If we want the user to be muted, set their
- // volume to 0 as well. This is not perfect, but it
- // will at least reduce their volume to a minimum.
- volume = 0;
- }
- if (volume <= 0)
- {
- mute = true;
- }
- LL_DEBUGS("Voice") << "Setting volume/mute for avatar "
- << p->mAvatarID << " to " << volume
- << (mute ? "/true" : "/false")
- << LL_ENDL;
- // SLIM SDK: Send both volume and mute commands.
- // Send a "volume for me" command for the user.
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
- << "<SessionHandle>" << getAudioSessionHandle()
- << "</SessionHandle><ParticipantURI>"
- << p->mURI << "</ParticipantURI><Volume>"
- << volume << "</Volume></Request>\n\n\n";
- // Send a "mute for me" command for the user
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Session.SetParticipantMuteForMe.1\">"
- << "<SessionHandle>" << getAudioSessionHandle()
- << "</SessionHandle><ParticipantURI>"
- << p->mURI << "</ParticipantURI><Mute>"
- << (mute ? "1" : "0") << "</Mute></Request>\n\n\n";
- }
- p->mVolumeDirty = false;
- }
- }
- }
- buildLocalAudioUpdates(stream);
- if (!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- void LLVoiceVivox::buildSetCaptureDevice(std::ostringstream& stream)
- {
- if (mCaptureDeviceDirty)
- {
- LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice
- << "\"" << LL_ENDL;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.SetCaptureDevice.1\">"
- << "<CaptureDeviceSpecifier>" << mCaptureDevice
- << "</CaptureDeviceSpecifier></Request>\n\n\n";
- mCaptureDeviceDirty = false;
- }
- }
- void LLVoiceVivox::buildSetRenderDevice(std::ostringstream& stream)
- {
- if (mRenderDeviceDirty)
- {
- LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice
- << "\"" << LL_ENDL;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.SetRenderDevice.1\"><RenderDeviceSpecifier>"
- << mRenderDevice << "</RenderDeviceSpecifier></Request>\n\n\n";
- mRenderDeviceDirty = false;
- }
- }
- void LLVoiceVivox::buildLocalAudioUpdates(std::ostringstream& stream)
- {
- buildSetCaptureDevice(stream);
- buildSetRenderDevice(stream);
- if (mPTTDirty)
- {
- mPTTDirty = false;
- // Send a local mute command.
- // NOTE: the state of "PTT" is the inverse of "local mute" (i.e. when
- // PTT is true, we send a mute command with "false", and vice versa).
- LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter "
- << (mPTT ? "false" : "true") << LL_ENDL;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Connector.MuteLocalMic.1\"><ConnectorHandle>"
- << mConnectorHandle << "</ConnectorHandle>" << "<Value>"
- << (mPTT ? "false" : "true") << "</Value></Request>\n\n\n";
- }
- if (mSpeakerMuteDirty)
- {
- const char* muteval = mSpeakerVolume == 0 ? "true" : "false";
- mSpeakerMuteDirty = false;
- llinfos << "Setting speaker mute to " << muteval << llendl;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Connector.MuteLocalSpeaker.1\">"
- << "<ConnectorHandle>" << mConnectorHandle
- << "</ConnectorHandle><Value>" << muteval
- << "</Value></Request>\n\n\n";
- }
- if (mSpeakerVolumeDirty)
- {
- mSpeakerVolumeDirty = false;
- llinfos << "Setting speaker volume to " << mSpeakerVolume << llendl;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
- << "<ConnectorHandle>" << mConnectorHandle
- << "</ConnectorHandle><Value>" << mSpeakerVolume
- << "</Value></Request>\n\n\n";
- }
- if (mMicVolumeDirty)
- {
- mMicVolumeDirty = false;
- llinfos << "Setting mic volume to " << mMicVolume << llendl;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Connector.SetLocalMicVolume.1\">"
- << "<ConnectorHandle>" << mConnectorHandle
- << "</ConnectorHandle><Value>" << mMicVolume
- << "</Value></Request>\n\n\n";
- }
- }
- /////////////////////////////
- // Response/Event handlers
- void LLVoiceVivox::connectorCreateResponse(S32 status_code,
- std::string& status_str,
- std::string& connector_handle,
- std::string& version_id)
- {
- if (status_code)
- {
- llwarns << "Connector.Create response failure: " << status_str
- << llendl;
- setState(stateConnectorFailed);
- }
- else
- {
- // Connector created, move forward.
- llinfos << "Connector.Create succeeded, Vivox SDK version is "
- << version_id << " - Connector handle: " << connector_handle
- << llendl;
- mConnectorEstablished = true;
- mConnectorHandle = connector_handle;
- if (mState == stateConnectorStarting)
- {
- setState(stateConnectorStarted);
- }
- }
- }
- void LLVoiceVivox::loginResponse(S32 status_code,
- std::string& status_str,
- std::string& account_handle,
- S32 aliases_number)
- {
- LL_DEBUGS("Voice") << "Account.Login response (" << status_code << "): "
- << status_str << " - Handle: " << account_handle
- << LL_ENDL;
- // Status code of 20200 means "bad password". We may want to special-case
- // that at some point.
- if (status_code == HTTP_UNAUTHORIZED)
- {
- // Login failure which is probably caused by the delay after a user's
- // password being updated.
- llinfos << "Account.Login response failure (" << status_code << "): "
- << status_str << llendl;
- setState(stateLoginRetry);
- }
- else if (status_code)
- {
- llwarns << "Account.Login response failure (" << status_code << "): "
- << status_str << llendl;
- setState(stateLoginFailed);
- }
- else
- {
- // Login succeeded, move forward.
- mAccountLoggedIn = true;
- mAccountHandle = account_handle;
- mNumberOfAliases = aliases_number;
- llinfos << "Account.Login succeeded. Account handle: "
- << account_handle << llendl;
- #if 0 // This needs to wait until the AccountLoginStateChangeEvent is received.
- if (mState == stateLoggingIn)
- {
- setState(stateLoggedIn);
- }
- #endif
- }
- }
- void LLVoiceVivox::sessionCreateResponse(std::string& request_id,
- S32 status_code,
- std::string& status_str,
- std::string& session_handle)
- {
- llinfos << "Got Session.Create response for request Id: " << request_id
- << " - Session handle " << session_handle << llendl;
- sessionState* sessionp = findSessionBeingCreatedByURI(request_id);
- if (sessionp)
- {
- LL_DEBUGS("Voice") << "Found session, marking as creation in progress."
- << LL_ENDL;
- sessionp->mCreateInProgress = false;
- }
- // Do not re-join a spatial session we just left after changing for a non
- // voice parcel. HB
- if (sessionp && sessionp->mIsSpatial &&
- !gViewerParcelMgr.allowAgentVoice())
- {
- // This will be set back to true by LLVoiceClient::onParcelChange() on
- // arrival in a voice-enabled parcel. HB
- mProcessChannels = false;
- return;
- }
- if (status_code)
- {
- llwarns << "Failure (" << status_code << "): " << status_str << llendl;
- if (sessionp)
- {
- sessionp->mErrorStatusCode = status_code;
- sessionp->mErrorStatusString = status_str;
- if (sessionp == mAudioSession)
- {
- setState(stateJoinSessionFailed);
- }
- else
- {
- reapSession(sessionp);
- }
- }
- }
- else
- {
- llinfos << "Session successfully created." << llendl;
- if (sessionp)
- {
- setSessionHandle(sessionp, session_handle);
- }
- }
- }
- void LLVoiceVivox::sessionGroupAddSessionResponse(std::string& request_id,
- S32 status_code,
- std::string& status_str,
- std::string& session_handle)
- {
- sessionState* sessionp = findSessionBeingCreatedByURI(request_id);
- if (sessionp)
- {
- sessionp->mCreateInProgress = false;
- }
- if (status_code)
- {
- llwarns << "SessionGroup.AddSession response failure (" << status_code
- << "): " << status_str << " - Session handle "
- << session_handle << llendl;
- if (sessionp)
- {
- sessionp->mErrorStatusCode = status_code;
- sessionp->mErrorStatusString = status_str;
- if (sessionp == mAudioSession)
- {
- setState(stateJoinSessionFailed);
- }
- else
- {
- reapSession(sessionp);
- }
- }
- }
- else
- {
- LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle: "
- << session_handle << LL_ENDL;
- if (sessionp)
- {
- setSessionHandle(sessionp, session_handle);
- }
- }
- }
- void LLVoiceVivox::sessionConnectResponse(std::string& request_id,
- S32 status_code,
- std::string& status_str)
- {
- sessionState* sessionp = findSession(request_id);
- if (status_code)
- {
- llwarns << "Session.Connect response failure (" << status_code << "): "
- << status_str << llendl;
- if (sessionp)
- {
- sessionp->mMediaConnectInProgress = false;
- sessionp->mErrorStatusCode = status_code;
- sessionp->mErrorStatusString = status_str;
- if (sessionp == mAudioSession)
- {
- setState(stateJoinSessionFailed);
- }
- }
- }
- else
- {
- LL_DEBUGS("Voice") << "Session.Connect response received (success)"
- << LL_ENDL;
- }
- }
- void LLVoiceVivox::logoutResponse(S32 status_code, std::string& status_str)
- {
- if (status_code)
- {
- llwarns << "Account.Logout response failure: " << status_str
- << llendl;
- // Should this ever fail ? Do we care if it does ?
- }
- }
- void LLVoiceVivox::connectorShutdownResponse(S32 status_code,
- std::string& status_str)
- {
- if (status_code)
- {
- llwarns << "Connector.InitiateShutdown response failure: "
- << status_str << llendl;
- // Should this ever fail ? Do we care if it does ?
- }
- mConnected = false;
- if (mState == stateConnectorStopping)
- {
- setState(stateConnectorStopped);
- }
- }
- void LLVoiceVivox::sessionAddedEvent(std::string& uri_str, std::string& alias,
- std::string& session_handle,
- std::string& session_grp_handle,
- bool is_channel, bool incoming,
- std::string& name_str)
- {
- sessionState* sessionp = NULL;
- llinfos << "Session: " << uri_str << " - Alias: " << alias << " - Name: "
- << name_str << " - Session handle: " << session_handle
- << " - Group handle: " << session_grp_handle << llendl;
- sessionp = addSession(uri_str, session_handle);
- if (sessionp)
- {
- sessionp->mGroupHandle = session_grp_handle;
- sessionp->mIsChannel = is_channel;
- sessionp->mIncoming = incoming;
- sessionp->mAlias = alias;
- // Generate a caller UUID: we do not need to do this for channels
- if (!sessionp->mIsChannel)
- {
- if (IDFromName(sessionp->mSIPURI, sessionp->mCallerID))
- {
- // Normal URI(base64-encoded UUID)
- }
- else if (!sessionp->mAlias.empty() &&
- IDFromName(sessionp->mAlias, sessionp->mCallerID))
- {
- // Wrong URI, but an alias is available. Stash the incoming URI
- // as an alternate
- sessionp->mAlternateSIPURI = sessionp->mSIPURI;
- // And generate a proper URI from the ID.
- setSessionURI(sessionp, sipURIFromID(sessionp->mCallerID));
- }
- else
- {
- llinfos << "Could not generate caller id from uri, using hash of URI "
- << sessionp->mSIPURI << llendl;
- sessionp->mCallerID.generate(sessionp->mSIPURI);
- sessionp->mSynthesizedCallerID = true;
- // Cannot look up the name in this case: we have to extract it
- // from the URI.
- std::string name_portion = nameFromsipURI(sessionp->mSIPURI);
- if (name_portion.empty())
- {
- // Did not seem to be a SIP URI, just use the whole
- // provided name.
- name_portion = name_str;
- }
- // Some incoming names may be separated with an underscore
- // instead of a space. Fix this.
- LLStringUtil::replaceChar(name_portion, '_', ' ');
- // Act like we just finished resolving the name (this stores it
- // in all the right places)
- avatarNameResolved(sessionp->mCallerID, name_portion);
- }
- llinfos << "Caller Id: " << sessionp->mCallerID << llendl;
- if (!sessionp->mSynthesizedCallerID)
- {
- // If we got here, we do not have a proper name. Initiate a
- // lookup.
- lookupName(sessionp->mCallerID);
- }
- }
- }
- }
- void LLVoiceVivox::joinedAudioSession(sessionState* sessionp)
- {
- if (mAudioSession != sessionp)
- {
- sessionState* old_sessionp = mAudioSession;
- mAudioSession = sessionp;
- // The old session may now need to be deleted.
- reapSession(old_sessionp);
- }
- // If we are not in the process of joining a session, bail out.
- if (mState != stateJoiningSession)
- {
- return;
- }
- setState(stateSessionJoined);
- // SLIM SDK: we do not always receive a participant state change for
- // ourselves when joining a channel now. Add the current user as a
- // participant here.
- participantState* participantp =
- sessionp->addParticipant(sipURIFromName(mAccountName));
- if (participantp)
- {
- participantp->mIsSelf = true;
- lookupName(participantp->mAvatarID);
- llinfos << "Added self as participant \""
- << participantp->mAccountName << "\" ("
- << participantp->mAvatarID << ")" << llendl;
- }
- if (!sessionp->mIsChannel)
- {
- // This is a P2P session. Make sure the other end is added as a
- // participant.
- participantp = sessionp->addParticipant(sessionp->mSIPURI);
- if (participantp)
- {
- if (participantp->mAvatarIDValid)
- {
- lookupName(participantp->mAvatarID);
- }
- else if (!sessionp->mName.empty())
- {
- participantp->mLegacyName = sessionp->mName;
- avatarNameResolved(participantp->mAvatarID, sessionp->mName);
- }
- // *TODO: do we need to set up mAvatarID/mAvatarIDValid here ?
- llinfos << "Added caller as participant \""
- << participantp->mAccountName << "\" ("
- << participantp->mAvatarID << ")" << llendl;
- }
- }
- }
- void LLVoiceVivox::sessionRemovedEvent(std::string& session_handle,
- std::string& session_grp_handle)
- {
- sessionState* sessionp = findSession(session_handle);
- if (!sessionp)
- {
- llwarns << "Unknown session " << session_handle << " removed"
- << llendl;
- return;
- }
- leftAudioSession(sessionp);
- // This message invalidates the session's handle. Set it to empty.
- setSessionHandle(sessionp);
- // This also means that the session's session group is now empty.
- // Terminate the session group so it does not leak.
- sessionGroupTerminateSendMessage(sessionp);
- // Conditionally delete the session
- reapSession(sessionp);
- llinfos << "Removed session. Session handle: " << session_handle
- << " - Group handle: " << session_grp_handle << llendl;
- }
- void LLVoiceVivox::reapSession(sessionState* sessionp)
- {
- if (!sessionp)
- {
- return;
- }
- if (!sessionp->mHandle.empty())
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
- << " (non-null session handle)" << LL_ENDL;
- }
- else if (sessionp->mCreateInProgress)
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
- << " (create in progress)" << LL_ENDL;
- }
- else if (sessionp->mMediaConnectInProgress)
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
- << " (connect in progress)" << LL_ENDL;
- }
- else if (sessionp == mAudioSession)
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
- << " (it is the current session)" << LL_ENDL;
- }
- else if (sessionp == mNextAudioSession)
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << sessionp->mSIPURI
- << " (it is the next session)" << LL_ENDL;
- }
- else
- {
- // *TODO: should we check for queued text messages here ?
- // We do not have a reason to keep tracking this session, so just
- // delete it.
- LL_DEBUGS("Voice") << "Deleting session " << sessionp->mSIPURI
- << LL_ENDL;
- deleteSession(sessionp);
- }
- }
- // Returns true if the session seems to indicate we have moved to a region on a
- // different voice server
- bool LLVoiceVivox::sessionNeedsRelog(sessionState* sessionp)
- {
- // Only make this check for spatial channels (so it would not happen for
- // group or P2P calls)
- if (sessionp && sessionp->mIsSpatial)
- {
- size_t i = sessionp->mSIPURI.find("@");
- if (i != std::string::npos)
- {
- std::string urihost = sessionp->mSIPURI.substr(i + 1);
- if (stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
- {
- // The hostname in this URI is different from what we expect.
- // This probably means we need to relog.
- // We could make a ProvisionVoiceAccountRequest and compare the
- // result with the current values of mVoiceSIPURIHostName and
- // mVoiceAccountServerURI to be really sure, but this is a
- // pretty good indicator.
- return true;
- }
- }
- }
- return false;
- }
- void LLVoiceVivox::leftAudioSession(sessionState* sessionp)
- {
- if (mAudioSession == sessionp)
- {
- switch (mState)
- {
- case stateJoiningSession:
- case stateSessionJoined:
- case stateRunning:
- case stateLeavingSession:
- case stateJoinSessionFailed:
- case stateJoinSessionFailedWaiting:
- // Normal transition
- LL_DEBUGS("Voice") << "Left session " << sessionp->mHandle
- << " in state " << state2string(mState)
- << LL_ENDL;
- setState(stateSessionTerminated);
- break;
- case stateSessionTerminated:
- // This will happen sometimes -- there are cases where we send
- // the terminate and then go straight to this state.
- llwarns << "Left session " << sessionp->mHandle << " in state "
- << state2string(mState) << llendl;
- break;
- default:
- llwarns << "Unexpected SessionStateChangeEvent (left session) in state "
- << state2string(mState) << llendl;
- setState(stateSessionTerminated);
- }
- }
- }
- void LLVoiceVivox::accountLoginStateChangeEvent(std::string& account_handle,
- S32 status_code,
- std::string& status_str,
- S32 state)
- {
- LL_DEBUGS("Voice") << "State is " << state << " - Handle: "
- << account_handle << LL_ENDL;
- /*
- According to Mike S., status codes for this event are:
- login_state_logged_out=0,
- login_state_logged_in = 1,
- login_state_logging_in = 2,
- login_state_logging_out = 3,
- login_state_resetting = 4,
- login_state_error=100
- */
- switch (state)
- {
- case 0:
- // The user has been logged out.
- setState(stateLoggedOut);
- break;
- case 1:
- if (mState == stateLoggingIn)
- {
- setState(stateLoggedIn);
- }
- break;
- case 3:
- // The user is in the process of logging out.
- setState(stateLoggingOut);
- break;
- default:
- // Used to be a commented out warning
- LL_DEBUGS("Voice") << "Unknown state: " << state << LL_ENDL;
- }
- }
- void LLVoiceVivox::mediaStreamUpdatedEvent(std::string& session_handle,
- std::string& session_grp_handle,
- S32 status_code,
- std::string& status_str,
- S32 state, bool incoming)
- {
- sessionState* sessionp = findSession(session_handle);
- LL_DEBUGS("Voice") << "session " << session_handle << ", status code "
- << status_code << ", string \"" << status_str << "\""
- << LL_ENDL;
- if (sessionp) // If we know about this session
- {
- switch (status_code)
- {
- case 0:
- case HTTP_OK:
- // Generic success: do not change the saved error code (it may
- // have been set elsewhere).
- break;
- default:
- // Save the status code for later
- sessionp->mErrorStatusCode = status_code;
- }
- switch (state)
- {
- case streamStateIdle:
- case streamStateDisconnecting:
- // Standard "left audio session"
- sessionp->mVoiceEnabled = false;
- sessionp->mMediaConnectInProgress = false;
- leftAudioSession(sessionp);
- break;
- case streamStateConnecting: // Nothing to do
- break;
- case streamStateConnected:
- sessionp->mVoiceEnabled = true;
- sessionp->mMediaConnectInProgress = false;
- joinedAudioSession(sessionp);
- break;
- case streamStateRinging:
- if (incoming)
- {
- // Send the voice chat invite to the GUI layer
- // *TODO: should we correlate with the mute list here ?
- sessionp->mIMSessionID =
- LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE,
- sessionp->mCallerID);
- sessionp->mVoiceInvitePending = true;
- if (sessionp->mName.empty())
- {
- lookupName(sessionp->mCallerID);
- }
- else
- {
- // Act like we just finished resolving the name
- avatarNameResolved(sessionp->mCallerID,
- sessionp->mName);
- }
- }
- break;
- default:
- llwarns << "Unknown state " << state << llendl;
- }
- }
- else
- {
- llwarns << "Session " << session_handle << " not found !" << llendl;
- }
- }
- void LLVoiceVivox::participantAddedEvent(std::string& session_handle,
- std::string& session_grp_handle,
- std::string& uri_str,
- std::string& alias,
- std::string& name_str,
- std::string& display_name_str,
- S32 participant_type)
- {
- sessionState* sessionp = findSession(session_handle);
- if (sessionp)
- {
- participantState* participant = sessionp->addParticipant(uri_str);
- if (participant)
- {
- participant->mAccountName = name_str;
- LL_DEBUGS("Voice") << "Added participant \""
- << participant->mAccountName << "\" ("
- << participant->mAvatarID << ")" << LL_ENDL;
- if (participant->mAvatarIDValid)
- {
- // Initiate a lookup
- lookupName(participant->mAvatarID);
- }
- else
- {
- // If we do not have a valid avatar UUID, we need to fill in
- // the display name to make the active speakers floater work.
- std::string name_portion = nameFromsipURI(uri_str);
- if (name_portion.empty())
- {
- // Problem with the SIP URI, fall back to the display name
- name_portion = display_name_str;
- }
- if (name_portion.empty())
- {
- // Problems with both of the above, fall back to the
- // account name
- name_portion = name_str;
- }
- // Set the display name (which is a hint to the active speakers
- // window not to do its own lookup)
- participant->mLegacyName = name_portion;
- avatarNameResolved(participant->mAvatarID, name_portion);
- }
- }
- }
- }
- void LLVoiceVivox::participantRemovedEvent(std::string& session_handle,
- std::string& session_grp_handle,
- std::string& uri_str,
- std::string& alias,
- std::string& name_str)
- {
- sessionState* sessionp = findSession(session_handle);
- if (sessionp)
- {
- participantState* participantp = sessionp->findParticipant(uri_str);
- if (participantp)
- {
- sessionp->removeParticipant(participantp);
- }
- else
- {
- LL_DEBUGS("Voice") << "Unknown participant " << uri_str << LL_ENDL;
- }
- }
- else
- {
- LL_DEBUGS("Voice") << "Unknown session " << session_handle << LL_ENDL;
- }
- }
- void LLVoiceVivox::participantUpdatedEvent(std::string& session_handle,
- std::string& session_grp_handle,
- std::string& uri_str,
- std::string& alias,
- bool muted_by_moderator,
- bool speaking, S32 volume,
- F32 energy)
- {
- sessionState* sessionp = findSession(session_handle);
- if (sessionp)
- {
- participantState* participantp = sessionp->findParticipant(uri_str);
- if (participantp)
- {
- participantp->mIsSpeaking = speaking;
- participantp->mIsModeratorMuted = muted_by_moderator;
- // SLIM SDK: convert range: ensure that energy is set to zero if
- // is_speaking is false
- if (speaking)
- {
- participantp->mSpeakingTimeout.reset();
- participantp->mPower = energy;
- }
- else
- {
- participantp->mPower = 0.f;
- }
- participantp->mVolume = volume;
- }
- else
- {
- llwarns << "Unknown participant: " << uri_str << llendl;
- }
- }
- else
- {
- llinfos << "Unknown session " << session_handle << llendl;
- }
- }
- void LLVoiceVivox::messageEvent(std::string& session_handle,
- std::string& uri_str, std::string& alias,
- std::string& msg_header,
- std::string& msg_body)
- {
- LL_DEBUGS("Voice") << "Message event, session " << session_handle
- << " from " << uri_str << LL_ENDL;
- size_t start, end;
- if (msg_header.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos)
- {
- static const std::string STARTMARKER = "<body";
- static const std::string STARTMARKER2 = ">";
- static const std::string ENDMARKER = "</body>";
- static const std::string STARTSPAN = "<span";
- static const std::string ENDSPAN = "</span>";
- // Default to displaying the raw string, so the message gets through.
- std::string raw_msg = msg_body;
- // Find the actual message text within the XML fragment
- start = msg_body.find(STARTMARKER);
- start = msg_body.find(STARTMARKER2, start);
- end = msg_body.find(ENDMARKER);
- if (start != std::string::npos)
- {
- start += STARTMARKER2.size();
- if (end != std::string::npos)
- {
- end -= start;
- }
- raw_msg.assign(msg_body, start, end);
- }
- else
- {
- // Did not find a <body>, try looking for a <span> instead.
- start = msg_body.find(STARTSPAN);
- start = msg_body.find(STARTMARKER2, start);
- end = msg_body.find(ENDSPAN);
- if (start != std::string::npos)
- {
- start += STARTMARKER2.size();
- if (end != std::string::npos)
- {
- end -= start;
- }
- raw_msg.assign(msg_body, start, end);
- }
- }
- // Strip formatting tags
- while ((start = raw_msg.find('<')) != std::string::npos)
- {
- if ((end = raw_msg.find('>', start + 1)) != std::string::npos)
- {
- // Strip out the tag
- raw_msg.erase(start, (end + 1) - start);
- }
- else
- {
- // Avoid an infinite loop
- break;
- }
- }
- // Decode ampersand-escaped chars
- // The text may contain text encoded with <, >, and &
- size_t mark = 0;
- while ((mark = raw_msg.find("<", mark)) != std::string::npos)
- {
- raw_msg.replace(mark++, 4, "<");
- }
- mark = 0;
- while ((mark = raw_msg.find(">", mark)) != std::string::npos)
- {
- raw_msg.replace(mark++, 4, ">");
- }
- mark = 0;
- while ((mark = raw_msg.find("&", mark)) != std::string::npos)
- {
- raw_msg.replace(mark++, 5, "&");
- }
- // Strip leading/trailing whitespace (since we always seem to get a
- // couple newlines)
- LLStringUtil::trim(raw_msg);
- sessionState* session = findSession(session_handle);
- if (session)
- {
- bool is_busy = gAgent.getBusy();
- bool is_muted = LLMuteList::isMuted(session->mCallerID,
- session->mName,
- LLMute::flagTextChat,
- LLMute::AGENT);
- bool is_linden = LLMuteList::isLinden(session->mName);
- bool quiet_chat = false;
- LLChat chat;
- chat.mMuted = is_muted && !is_linden;
- if (!chat.mMuted)
- {
- chat.mFromID = session->mCallerID;
- chat.mFromName = session->mName;
- chat.mSourceType = CHAT_SOURCE_AGENT;
- if (is_busy && !is_linden)
- {
- quiet_chat = true;
- // *TODO: Return busy mode response here ? Or maybe when
- // session is started instead ?
- }
- LL_DEBUGS("Voice") << "Adding message, name " << session->mName
- << ", session " << session->mIMSessionID
- << ", target " << session->mCallerID
- << LL_ENDL;
- std::string full_msg = ": " + raw_msg;
- if (gIMMgrp)
- {
- gIMMgrp->addMessage(session->mIMSessionID,
- session->mCallerID,
- session->mName.c_str(),
- full_msg.c_str(),
- LLStringUtil::null, IM_NOTHING_SPECIAL,
- 0, LLUUID::null, LLVector3::zero,
- // Prepend name and make it a link to
- // the user's profile
- true);
- }
- chat.mText = "IM: " + session->mName + full_msg;
- // If the chat should come in quietly (i.e. we are in busy
- // mode), pretend it is from a local agent.
- LLFloaterChat::addChat(chat, true, quiet_chat);
- }
- }
- }
- }
- void LLVoiceVivox::sessionNotificationEvent(std::string& session_handle,
- std::string& uri_str,
- std::string& notif_type)
- {
- sessionState* session = findSession(session_handle);
- if (!session)
- {
- LL_DEBUGS("Voice") << "Unknown session handle " << session_handle
- << LL_ENDL;
- return;
- }
- participantState* participant = session->findParticipant(uri_str);
- if (participant)
- {
- if (!stricmp(notif_type.c_str(), "Typing"))
- {
- LL_DEBUGS("Voice") << "Participant " << uri_str << " in session "
- << session->mSIPURI << " starts typing."
- << LL_ENDL;
- }
- else if (!stricmp(notif_type.c_str(), "NotTyping"))
- {
- LL_DEBUGS("Voice") << "Participant " << uri_str << " in session "
- << session->mSIPURI << " stops typing."
- << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("Voice") << "Unknown notification type "
- << notif_type << "for participant "
- << uri_str << " in session "
- << session->mSIPURI << LL_ENDL;
- }
- }
- else
- {
- LL_DEBUGS("Voice") << "Unknown participant " << uri_str
- << " in session " << session->mSIPURI
- << LL_ENDL;
- }
- }
- // The user's mute list has been updated. This method goes through the current
- // participants list and syncs it with the mute list.
- void LLVoiceVivox::muteListChanged()
- {
- if (!mAudioSession)
- {
- return;
- }
- for (particip_map_t::iterator
- it = mAudioSession->mParticipantsByURI.begin(),
- end = mAudioSession->mParticipantsByURI.end();
- it != end; ++it)
- {
- participantState* p = it->second;
- if (p && p->updateMuteState())
- {
- mAudioSession->mVolumeDirty = true;
- }
- }
- }
- /////////////////////////////
- // Managing list of participants
- LLVoiceVivox::participantState::participantState(const std::string& uri)
- : mURI(uri),
- mPTT(false),
- mIsSpeaking(false),
- mIsModeratorMuted(false),
- mLastSpokeTimestamp(0.f),
- mPower(0.f),
- mVolume(-1),
- mOnMuteList(false),
- mUserVolume(-1),
- mVolumeDirty(false),
- mAvatarIDValid(false),
- mIsSelf(false)
- {
- }
- LLVoiceVivox::participantState* LLVoiceVivox::sessionState::addParticipant(const std::string& uri)
- {
- participantState* result = NULL;
- bool useAlternateURI = false;
- // Note: this is mostly the body of
- // LLVoiceVivox::sessionState::findParticipant(), but since we need to
- // know if it matched the alternate SIP URI (so we can add it properly), we
- // need to reproduce it here.
- {
- particip_map_t::iterator iter = mParticipantsByURI.find(&uri);
- if (iter == mParticipantsByURI.end())
- {
- if (!mAlternateSIPURI.empty() && uri == mAlternateSIPURI)
- {
- // This is a P2P session (probably with the SLIM client) with
- // an alternate URI for the other participant.
- // Use mSIPURI instead, since it will be properly encoded.
- iter = mParticipantsByURI.find(&(mSIPURI));
- useAlternateURI = true;
- }
- }
- if (iter != mParticipantsByURI.end())
- {
- result = iter->second;
- }
- }
- if (!result)
- {
- // Participant is not already in one list or the other.
- result = new participantState(useAlternateURI?mSIPURI:uri);
- mParticipantsByURI.emplace(&(result->mURI), result);
- // Try to do a reverse transform on the URI to get the GUID back.
- LLUUID id;
- if (IDFromName(result->mURI, id))
- {
- result->mAvatarIDValid = true;
- result->mAvatarID = id;
- if (result->updateMuteState())
- {
- mVolumeDirty = true;
- }
- }
- else
- {
- // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
- // This tells both code in LLVoiceVivox and code in
- // llfloateractivespeakers.cpp that the ID will not be in the name
- // cache.
- result->mAvatarID.generate(uri);
- }
- mParticipantsByUUID.emplace(&(result->mAvatarID), result);
- LL_DEBUGS("Voice") << "Participant \"" << result->mURI << "\" added."
- << LL_ENDL;
- }
- return result;
- }
- bool LLVoiceVivox::participantState::updateMuteState()
- {
- bool result = false;
- if (mAvatarIDValid)
- {
- bool muted = LLMuteList::isMuted(mAvatarID, LLMute::flagVoiceChat);
- if (mOnMuteList != muted)
- {
- mOnMuteList = muted;
- mVolumeDirty = true;
- result = true;
- }
- }
- return result;
- }
- void LLVoiceVivox::sessionState::removeParticipant(LLVoiceVivox::participantState* participant)
- {
- if (participant)
- {
- particip_map_t::iterator iter =
- mParticipantsByURI.find(&(participant->mURI));
- particip_id_map_t::iterator iter2 =
- mParticipantsByUUID.find(&(participant->mAvatarID));
- LL_DEBUGS("Voice") << "Participant \"" << participant->mURI
- << "\" (" << participant->mAvatarID
- << ") removed." << LL_ENDL;
- if (iter == mParticipantsByURI.end())
- {
- llwarns << "Internal error: participant " << participant->mURI
- << " not in URI map" << llendl;
- gVoiceVivox.giveUp();
- }
- else if (iter2 == mParticipantsByUUID.end())
- {
- llwarns << "Internal error: participant ID "
- << participant->mAvatarID << " not in UUID map" << llendl;
- gVoiceVivox.giveUp();
- }
- else if (iter->second != iter2->second)
- {
- llwarns << "Internal error: participant mismatch !" << llendl;
- gVoiceVivox.giveUp();
- }
- else
- {
- mParticipantsByURI.erase(iter);
- mParticipantsByUUID.erase(iter2);
- delete participant;
- }
- }
- }
- void LLVoiceVivox::sessionState::removeAllParticipants()
- {
- while (!mParticipantsByURI.empty())
- {
- removeParticipant(mParticipantsByURI.begin()->second);
- }
- if (!mParticipantsByUUID.empty())
- {
- llwarns << "Internal error: empty URI map, non-empty UUID map"
- << llendl;
- gVoiceVivox.giveUp();
- }
- }
- LLVoiceVivox::particip_map_t* LLVoiceVivox::getParticipantList()
- {
- particip_map_t* result = NULL;
- if (mAudioSession)
- {
- result = &(mAudioSession->mParticipantsByURI);
- }
- return result;
- }
- LLVoiceVivox::participantState* LLVoiceVivox::sessionState::findParticipant(const std::string& uri)
- {
- participantState* result = NULL;
- particip_map_t::iterator iter = mParticipantsByURI.find(&uri);
- if (iter == mParticipantsByURI.end())
- {
- if (!mAlternateSIPURI.empty() && uri == mAlternateSIPURI)
- {
- // This is a P2P session (probably with the SLIM client) with an
- // alternate URI for the other participant.
- // Look up the other URI
- iter = mParticipantsByURI.find(&(mSIPURI));
- }
- }
- if (iter != mParticipantsByURI.end())
- {
- result = iter->second;
- }
- return result;
- }
- LLVoiceVivox::participantState* LLVoiceVivox::sessionState::findParticipantByID(const LLUUID& id)
- {
- participantState* result = NULL;
- particip_id_map_t::iterator iter = mParticipantsByUUID.find(&id);
- if (iter != mParticipantsByUUID.end())
- {
- result = iter->second;
- }
- return result;
- }
- LLVoiceVivox::participantState* LLVoiceVivox::findParticipantByID(const LLUUID& id)
- {
- participantState* result = NULL;
- if (mAudioSession)
- {
- result = mAudioSession->findParticipantByID(id);
- }
- return result;
- }
- void LLVoiceVivox::parcelChanged()
- {
- if (!gViewerParcelMgr.getAgentParcel())
- {
- llinfos << "Not logged in yet, deferring..." << llendl;
- return;
- }
- if (!gViewerParcelMgr.allowAgentVoice())
- {
- return;
- }
- // If the user is logged in, start a channel lookup.
- const std::string& url =
- gAgent.getRegionCapability("ParcelVoiceInfoRequest");
- if (url.empty())
- {
- LL_DEBUGS("Voice") << "No ParcelVoiceInfoRequest capability for agent region."
- << LL_ENDL;
- return;
- }
- LL_DEBUGS("Voice") << "Sending ParcelVoiceInfoRequest" << LL_ENDL;
- gCoros.launch("LLVivoxVoiceClient::parcelVoiceInfoRequestCoro",
- boost::bind(&LLVoiceVivox::parcelVoiceInfoRequestCoro,
- url));
- }
- //static
- void LLVoiceVivox::parcelVoiceInfoRequestCoro(const std::string& url)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("parcelVoiceInfoRequest");
- LLSD result = adapter.postAndSuspend(url, LLSD());
- if (gVoiceVivox.mTerminated) return; // Voice has since been shut down
- LL_DEBUGS("Voice") << "Received voice info reply..." << LL_ENDL;
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- llwarns << "No voice on parcel: " << status.toString() << llendl;
- gVoiceVivox.sessionTerminate();
- return;
- }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- if (result.has("voice_credentials"))
- {
- gVoiceVivox.setSpatialChannel(result["voice_credentials"]);
- }
- else
- {
- LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL;
- }
- }
- void LLVoiceVivox::switchChannel(std::string uri, bool spatial,
- bool no_reconnect, bool is_p2p,
- std::string hash)
- {
- bool needs_switch = false;
- LL_DEBUGS("Voice") << "Called in state " << state2string(mState)
- << " with uri \"" << uri << "\", spatial is "
- << (spatial ? "true" : "false")
- << LL_ENDL;
- switch (mState)
- {
- case stateJoinSessionFailed:
- case stateJoinSessionFailedWaiting:
- case stateNoChannel:
- {
- // Always switch to the new URI from these states.
- needs_switch = true;
- break;
- }
- default:
- {
- if (mSessionTerminateRequested)
- {
- // If a terminate has been requested, we need to compare
- // against where the URI we are already headed to.
- if (mNextAudioSession)
- {
- if (mNextAudioSession->mSIPURI != uri)
- {
- needs_switch = true;
- }
- }
- else
- {
- // mNextAudioSession is NULL; this probably means we are
- // on our way back to spatial.
- if (!uri.empty())
- {
- // We do want to process a switch in this case.
- needs_switch = true;
- }
- }
- }
- // Otherwise, compare against the URI we are in now.
- else if (mAudioSession && mAudioSession->mSIPURI != uri)
- {
- needs_switch = true;
- }
- break;
- }
- }
- if (!needs_switch)
- {
- return;
- }
- if (uri.empty())
- {
- // Leave any channel we may be in
- LL_DEBUGS("Voice") << "Leaving channel" << LL_ENDL;
- sessionState* old_sessionp = mNextAudioSession;
- mNextAudioSession = NULL;
- // The old session may now need to be deleted.
- reapSession(old_sessionp);
- // Make sure voice is turned off
- gVoiceClient.mUserPTTState = false;
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
- }
- else
- {
- LL_DEBUGS("Voice") << "Switching to channel " << uri << LL_ENDL;
- mNextAudioSession = addSession(uri);
- mNextAudioSession->mHash = hash;
- mNextAudioSession->mIsSpatial = spatial;
- mNextAudioSession->mReconnect = !no_reconnect;
- mNextAudioSession->mIsP2P = is_p2p;
- }
- if (mState > stateNoChannel)
- {
- // State machine will come around and rejoin if URI/handle is not empty
- sessionTerminate();
- }
- }
- void LLVoiceVivox::joinSession(sessionState* sessionp)
- {
- mNextAudioSession = sessionp;
- if (mState > stateNoChannel)
- {
- // State machine will come around and rejoin if URI/handle is not empty
- sessionTerminate();
- }
- }
- //virtual
- void LLVoiceVivox::setNonSpatialChannel(const LLSD& channel_info, bool, bool)
- {
- switchChannel(channel_info["channel_uri"].asString(), false, false, false,
- channel_info["channel_credentials"].asString());
- }
- //virtual
- void LLVoiceVivox::setSpatialChannel(const LLSD& channel_info)
- {
- if (channel_info.has("channel_uri"))
- {
- mSpatialSessionURI = channel_info["channel_uri"].asString();
- LL_DEBUGS("Voice") << "Got spatial channel uri: " << mSpatialSessionURI
- << LL_ENDL;
- }
- else
- {
- mSpatialSessionURI.clear();
- LL_DEBUGS("Voice") << "No voice channel URI" << LL_ENDL;
- }
- if (channel_info.has("channel_credentials"))
- {
- mSpatialSessionCredentials =
- channel_info["channel_credentials"].asString();
- LL_DEBUGS("Voice") << "Got spatial session credentials." << LL_ENDL;
- }
- else
- {
- mSpatialSessionCredentials.clear();
- LL_DEBUGS("Voice") << "No voice channel credentials" << LL_ENDL;
- }
- if (!mProcessChannels)
- {
- // We are not even processing channels (another provider is) so save
- // the credentials aside and exit.
- return;
- }
- if ((mAudioSession && !mAudioSession->mIsSpatial) ||
- (mNextAudioSession && !mNextAudioSession->mIsSpatial))
- {
- // User is in a non-spatial chat or joining a non-spatial chat. Do not
- // switch channels.
- llinfos << "In non-spatial chat, not switching channels" << llendl;
- }
- else
- {
- switchChannel(mSpatialSessionURI, true, false, false,
- mSpatialSessionCredentials);
- }
- }
- void LLVoiceVivox::callUser(const LLUUID& id)
- {
- mProcessChannels = true;
- switchChannel(sipURIFromID(id), false, true, true);
- }
- void LLVoiceVivox::leaveChannel()
- {
- if (mState >= stateNoChannel)
- {
- LL_DEBUGS("Voice") << "Leaving channel for teleport/logout" << LL_ENDL;
- mChannelName.clear();
- sessionTerminate();
- }
- }
- // This is only ever used to answer incoming P2P call invites.
- bool LLVoiceVivox::answerInvite(std::string& session_handle)
- {
- sessionState* sessionp = findSession(session_handle);
- if (sessionp)
- {
- sessionp->mIsSpatial = false;
- sessionp->mReconnect = false;
- sessionp->mIsP2P = true;
- mProcessChannels = true;
- joinSession(sessionp);
- return true;
- }
- return false;
- }
- void LLVoiceVivox::declineInvite(std::string& session_handle)
- {
- sessionState* sessionp = findSession(session_handle);
- if (sessionp)
- {
- sessionGroupTerminateSendMessage(sessionp);
- }
- }
- //virtual
- void LLVoiceVivox::leaveNonSpatialChannel()
- {
- LL_DEBUGS("Voice") << "Called in state " << state2string(mState)
- << LL_ENDL;
- // Make sure we do not rejoin the current session.
- sessionState* old_next_sessionp = mNextAudioSession;
- mNextAudioSession = NULL;
- // Most likely this will still be the current session at this point, but
- // check it anyway.
- reapSession(old_next_sessionp);
- verifySessionState();
- sessionTerminate();
- }
- //virtual
- bool LLVoiceVivox::inProximalChannel() const
- {
- return !mSessionTerminateRequested && mState == stateRunning &&
- inSpatialChannel();
- }
- //virtual
- std::string LLVoiceVivox::sipURIFromID(const LLUUID& id) const
- {
- return "sip:" + nameFromID(id) + "@" + mVoiceSIPURIHostName;
- }
- // If you need to transform a GUID to this form on the Mac OS X command line,
- // this will do so:
- // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 | xxd -r -p | openssl base64 | tr '/+' '_-')
- //
- // The reverse transform can be done with:
- // echo 'x5mkTKmxDTuGnjWyC__WfMg==' | cut -b 2- - | tr '_-' '/+' | openssl base64 -d | xxd -p
- std::string LLVoiceVivox::nameFromID(const LLUUID& id)
- {
- if (id.isNull())
- {
- return "";
- }
- // Prepending this apparently prevents conflicts with reserved names inside
- // the vivox and diamondware code.
- std::string result = "x";
- // Base64 encode and replace the pieces of base64 that are less compatible
- // with e-mail local-parts.
- // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
- result += LLBase64::encode((const char*)id.mData, UUID_BYTES);
- LLStringUtil::replaceChar(result, '+', '-');
- LLStringUtil::replaceChar(result, '/', '_');
- return result;
- }
- bool LLVoiceVivox::IDFromName(const std::string in_name, LLUUID& id)
- {
- bool result = false;
- // SLIM SDK: The "name" may actually be a SIP URI such as:
- // "sip:[email protected]"
- // If it is, convert to a bare name before doing the transform.
- std::string name = nameFromsipURI(in_name);
- // Does not look like a SIP URI, assume it is an actual name.
- if (name.empty())
- {
- name = in_name;
- }
- // This will only work if the name is of the proper form.
- // As an example, the account name for Monroe Linden
- // (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
- // "xFnPP04IpREWNkuw1cOXlhw=="
- if (name.size() == 25 && name[0] == 'x' && name[23] == '=' &&
- name[24] == '=')
- {
- // The name appears to have the right form.
- // Reverse the transforms done by nameFromID
- std::string temp = name;
- LLStringUtil::replaceChar(temp, '-', '+');
- LLStringUtil::replaceChar(temp, '_', '/');
- std::string buffer = LLBase64::decode(temp.c_str() + 1);
- if (buffer.size() == (size_t)UUID_BYTES)
- {
- // The decode succeeded. Stuff the bits into the UUID
- memcpy(id.mData, buffer.c_str(), UUID_BYTES);
- result = true;
- LL_DEBUGS("Voice") << "Decoded UUID: " << id << LL_ENDL;
- }
- else
- {
- llwarns << "Invalid UUID encoding" << llendl;
- }
- }
- if (!result)
- {
- // VIVOX: not a standard account name, just copy the URI name
- // mURIString field and hope for the best. bpj
- id.setNull(); // VIVOX, set the uuid field to nulls
- }
- return result;
- }
- std::string LLVoiceVivox::sipURIFromName(std::string& name) const
- {
- return "sip:" + name + "@" + mVoiceSIPURIHostName;
- }
- std::string LLVoiceVivox::nameFromsipURI(const std::string& uri)
- {
- std::string result;
- size_t sip_offset = uri.find("sip:");
- size_t at_offset = uri.find("@");
- if (sip_offset != std::string::npos && at_offset != std::string::npos)
- {
- result = uri.substr(sip_offset + 4, at_offset - sip_offset - 4);
- }
- return result;
- }
- bool LLVoiceVivox::inSpatialChannel() const
- {
- return mAudioSession && mAudioSession->mIsSpatial;
- }
- LLSD LLVoiceVivox::getAudioSessionChannelInfo() const
- {
- return mAudioSession ? mAudioSession->getVoiceChannelInfo() : LLSD();
- }
- std::string LLVoiceVivox::getAudioSessionHandle() const
- {
- return mAudioSession ? mAudioSession->mHandle : "";
- }
- // Because of the recurring voice cutout issues (SL-15072) we are going to try
- // to disable the automatic VAD (Voice Activity Detection) and set the
- // associated parameters directly. We will expose them via Debug Settings and
- // that should let us iterate on a collection of values that work for us.
- //
- // From the VIVOX docs:
- //
- // VadAuto: flag to enable (1) or disable (0) automatic VAD.
- //
- // VadHangover: the time (in milliseconds) that it takes for the VAD to switch
- // back to silence from speech mode after the last speech frame
- // has been detected.
- //
- // VadNoiseFloor: dimensionless value between 0 and 20000 (default 576) that
- // controls the maximum level at which the noise floor may be
- // set at by the VAD's noise tracking. Too low of a value will
- // make noise tracking ineffective (a value of 0 disables noise
- // tracking and the VAD then relies purely on the sensitivity
- // property). Too high of a value will make long speech
- // classifiable as noise.
- //
- // VadSensitivity: dimensionless value between 0 and 100, indicating the
- // 'sensitivity of the VAD'. Increasing this value corresponds
- // to decreasing the sensitivity of the VAD (i.e. 0 is most
- // sensitive, while 100 is least sensitive).
- void LLVoiceVivox::setupVADParams()
- {
- #if LL_LINUX
- if (mDeprecatedClient)
- {
- return;
- }
- #endif
- U32 vad_auto = gSavedSettings.getBool("VivoxVadAuto") ? 1 : 0;
- U32 vad_hangover = gSavedSettings.getU32("VivoxVadHangover");
- U32 vad_noise_floor = gSavedSettings.getU32("VivoxVadNoiseFloor");
- if (vad_noise_floor > 20000)
- {
- vad_noise_floor = 20000;
- }
- U32 vad_sensitivity = gSavedSettings.getU32("VivoxVadSensitivity");
- if (vad_sensitivity > 100)
- {
- vad_sensitivity = 100;
- }
- if (vad_auto)
- {
- llinfos << "Enabling the automatic VAD." << llendl;
- }
- else
- {
- llinfos << "Disabling the automatic VAD. Setting fixed values: VadHangover = "
- << vad_hangover << " - VadSensitivity = " << vad_sensitivity
- << " - VadNoiseFloor = " << vad_noise_floor << llendl;
- }
- std::ostringstream stream;
- stream << "<Request requestId=\"" << mCommandCookie++
- << "\" action=\"Aux.SetVadProperties.1\">"
- << "<VadAuto>" << vad_auto << "</VadAuto>"
- << "<VadHangover>" << vad_hangover << "</VadHangover>"
- << "<VadSensitivity>" << vad_sensitivity << "</VadSensitivity>"
- << "<VadNoiseFloor>" << vad_noise_floor << "</VadNoiseFloor>"
- << "</Request>\n\n\n";
- writeString(stream.str());
- }
- /////////////////////////////
- // Sending updates of current state
- void LLVoiceVivox::enforceTether()
- {
- LLVector3d tethered = mCameraRequestedPosition;
- // Constrain 'tethered' to within 50m of mAvatarPosition.
- constexpr F32 max_dist = 50.f;
- LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
- F32 camera_distance = (F32)camera_offset.length();
- if (camera_distance > max_dist)
- {
- tethered = mAvatarPosition +
- (max_dist / camera_distance) * camera_offset;
- }
- if (dist_vec_squared(mCameraPosition, tethered) > 0.01)
- {
- mCameraPosition = tethered;
- mSpatialCoordsDirty = true;
- }
- }
- void LLVoiceVivox::updatePosition()
- {
- if (mTerminated || !mPump || !mProcessChannels)
- {
- return;
- }
- LLViewerRegion* regionp = gAgent.getRegion();
- if (!regionp || !isAgentAvatarValid())
- {
- return;
- }
- // Send the current camera position to the voice code
- LLMatrix3 rot;
- rot.setRows(gViewerCamera.getAtAxis(), gViewerCamera.getLeftAxis(),
- gViewerCamera.getUpAxis());
- LLVector3d pos =
- regionp->getPosGlobalFromRegion(gViewerCamera.getOrigin());
- setCameraPosition(pos, LLVector3::zero, rot);
- // Send the current avatar position to the voice code
- rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3();
- pos = gAgentAvatarp->getPositionGlobal();
- pos.mdV[VZ] += 1.0; // Bump it up to head height
- setAvatarPosition(pos, LLVector3::zero, rot);
- }
- void LLVoiceVivox::setCameraPosition(const LLVector3d& position,
- const LLVector3& velocity,
- const LLMatrix3& rot)
- {
- mCameraRequestedPosition = position;
- if (mCameraVelocity != velocity)
- {
- mCameraVelocity = velocity;
- mSpatialCoordsDirty = true;
- }
- if (mCameraRot != rot)
- {
- mCameraRot = rot;
- mSpatialCoordsDirty = true;
- }
- }
- void LLVoiceVivox::setAvatarPosition(const LLVector3d& position,
- const LLVector3& velocity,
- const LLMatrix3& rot)
- {
- if (dist_vec(mAvatarPosition, position) > 0.1)
- {
- mAvatarPosition = position;
- mSpatialCoordsDirty = true;
- }
- if (mAvatarVelocity != velocity)
- {
- mAvatarVelocity = velocity;
- mSpatialCoordsDirty = true;
- }
- if (mAvatarRot != rot)
- {
- mAvatarRot = rot;
- mSpatialCoordsDirty = true;
- }
- }
- bool LLVoiceVivox::isCurrentChannel(const LLSD& channel_info)
- {
- if (!mProcessChannels ||
- (channel_info.has("voice_server_type") &&
- channel_info["voice_server_type"].asString() != VIVOXSTR))
- {
- return false;
- }
- // Favor the next audio session, as this is the one we are bringing up.
- sessionState* sessionp = mNextAudioSession;
- if (!sessionp)
- {
- if (!mAudioSession)
- {
- return false;
- }
- sessionp = mAudioSession;
- }
- std::string handle = channel_info["session_handle"].asString();
- if (!handle.empty())
- {
- return sessionp->mHandle == handle;
- }
- return channel_info["channel_uri"].asString() == mAudioSession->mSIPURI;
- }
- bool LLVoiceVivox::compareChannels(const LLSD& info1, const LLSD& info2)
- {
- return (!info1.has("voice_server_type") ||
- info1["voice_server_type"].asString() == VIVOXSTR) &&
- (!info2.has("voice_server_type") ||
- info2["voice_server_type"].asString() == VIVOXSTR) &&
- info1["channel_uri"].asString() == info2["channel_uri"].asString();
- }
- void LLVoiceVivox::setVoiceEnabled(bool enabled)
- {
- if (mVoiceEnabled == enabled)
- {
- return;
- }
- mVoiceEnabled = enabled;
- LLVoiceClientStatusObserver::EStatusType status;
- if (enabled)
- {
- LLVoiceChannel::getCurrentVoiceChannel()->activate();
- status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
- }
- else
- {
- // Turning voice off looses your current channel: this makes sure the
- // UI is not out of sync when you re-enable it.
- LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
- status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
- mRetries = 0;
- }
- notifyStatusObservers(status);
- }
- //virtual
- void LLVoiceVivox::processChannels(bool enabled)
- {
- if (mProcessChannels == enabled)
- {
- return;
- }
- mProcessChannels = enabled;
- // Note: each time channels processing is disabled, the daemon is stopped,
- // so we must reset this each time we restart the channels processing. HB
- if (enabled)
- {
- mRetries = 0;
- }
- }
- void LLVoiceVivox::setEarLocation(S32 loc)
- {
- if (mEarLocation != loc && loc >= 0 && loc <= (S32)earLocMixed)
- {
- LL_DEBUGS("Voice") << "Setting location to " << loc << LL_ENDL;
- mEarLocation = loc;
- mSpatialCoordsDirty = true;
- }
- }
- void LLVoiceVivox::setVoiceVolume(F32 volume)
- {
- S32 scaled_volume = scale_speaker_volume(volume);
- if (scaled_volume != mSpeakerVolume)
- {
- if (scaled_volume == 0 || mSpeakerVolume == 0)
- {
- mSpeakerMuteDirty = true;
- }
- mSpeakerVolume = scaled_volume;
- mSpeakerVolumeDirty = true;
- }
- }
- void LLVoiceVivox::setMicGain(F32 volume)
- {
- S32 scaled_volume = scale_mic_volume(volume);
- if (scaled_volume != mMicVolume)
- {
- mMicVolume = scaled_volume;
- mMicVolumeDirty = true;
- }
- }
- bool LLVoiceVivox::isParticipant(const LLUUID& id)
- {
- return mAudioSession && findParticipantByID(id) != NULL;
- }
- bool LLVoiceVivox::getIsSpeaking(const LLUUID& id)
- {
- if (!mProcessChannels)
- {
- return false;
- }
- constexpr F32 SPEAKING_TIMEOUT = 1.f;
- participantState* participant = findParticipantByID(id);
- if (participant)
- {
- if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
- {
- participant->mIsSpeaking = false;
- }
- return participant->mIsSpeaking;
- }
- return false;
- }
- bool LLVoiceVivox::getIsModeratorMuted(const LLUUID& id)
- {
- if (!mProcessChannels)
- {
- return false;
- }
- participantState* participantp = findParticipantByID(id);
- return participantp && participantp->mIsModeratorMuted;
- }
- F32 LLVoiceVivox::getCurrentPower(const LLUUID& id)
- {
- participantState* participantp = findParticipantByID(id);
- return participantp ? participantp->mPower : -1.f;
- }
- // External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
- // internal = 400 * external^2
- F32 LLVoiceVivox::getUserVolume(const LLUUID& id)
- {
- F32 result = -1.f; // Returned when user is not a participant.
- participantState* participantp = findParticipantByID(id);
- if (participantp)
- {
- S32 ires = 100; // Nominal default volume
- if (participantp->mIsSelf)
- {
- // Always make it look like the user's own volume is set at the
- // default.
- }
- else if (participantp->mUserVolume != -1)
- {
- // Use the internal volume
- ires = participantp->mUserVolume;
- }
- else if (participantp->mVolume != -1)
- {
- // Map backwards from vivox volume
- if (participantp->mVolume < 56)
- {
- ires = (participantp->mVolume * 100) / 56;
- }
- else
- {
- ires = 300 * (participantp->mVolume - 56) / 44 + 100;
- }
- }
- result = sqrtf((F32)ires / 400.f);
- }
- return result;
- }
- void LLVoiceVivox::setUserVolume(const LLUUID& id, F32 volume)
- {
- if (mAudioSession)
- {
- participantState* participantp = findParticipantByID(id);
- if (participantp)
- {
- // Volume can amplify by as much as 4x !
- S32 ivol = (S32)(400.f * volume * volume);
- participantp->mUserVolume = llclamp(ivol, 0, 400);
- participantp->mVolumeDirty = true;
- mAudioSession->mVolumeDirty = true;
- }
- }
- }
- LLVoiceVivox::sessionState::sessionState()
- : mCreateInProgress(false),
- mMediaConnectInProgress(false),
- mVoiceInvitePending(false),
- mSynthesizedCallerID(false),
- mIsChannel(false),
- mIsSpatial(false),
- mIsP2P(false),
- mIncoming(false),
- mVoiceEnabled(false),
- mReconnect(false),
- mVolumeDirty(false)
- {
- }
- LLVoiceVivox::sessionState::~sessionState()
- {
- removeAllParticipants();
- }
- LLSD LLVoiceVivox::sessionState::getVoiceChannelInfo() const
- {
- LLSD channel_info;
- channel_info["voice_server_type"] = VIVOXSTR;
- channel_info["channel_credentials"] = mHash;
- channel_info["channel_uri"] = mSIPURI;
- channel_info["session_handle"] = mHandle;
- return channel_info;
- }
- bool LLVoiceVivox::sessionState::isCallBackPossible()
- {
- // This may change to be explicitly specified by Vivox in the future...
- // Currently, only PSTN P2P calls cannot be returned.
- // Conveniently, this is also the only case where we synthesize a caller
- // UUID.
- return !mSynthesizedCallerID;
- }
- bool LLVoiceVivox::sessionState::isTextIMPossible()
- {
- // This may change to be explicitly specified by vivox in the future...
- return !mSynthesizedCallerID;
- }
- LLVoiceVivox::sessionState* LLVoiceVivox::findSession(const std::string& handle)
- {
- session_map_t::iterator iter = mSessionsByHandle.find(&handle);
- return iter != mSessionsByHandle.end() ? iter->second : NULL;
- }
- LLVoiceVivox::sessionState* LLVoiceVivox::findSessionBeingCreatedByURI(const std::string& uri)
- {
- for (session_set_it_t iter = mSessions.begin(); iter != mSessions.end();
- ++iter)
- {
- sessionState* sessionp = *iter;
- if (sessionp->mCreateInProgress && sessionp->mSIPURI == uri)
- {
- return sessionp;
- }
- }
- return NULL;
- }
- LLVoiceVivox::sessionState* LLVoiceVivox::findSession(const LLUUID& participant_id)
- {
- for (session_set_it_t iter = mSessions.begin(); iter != mSessions.end();
- ++iter)
- {
- sessionState* sessionp = *iter;
- if (sessionp->mCallerID == participant_id ||
- sessionp->mIMSessionID == participant_id)
- {
- return sessionp;
- }
- }
- return NULL;
- }
- LLVoiceVivox::sessionState* LLVoiceVivox::addSession(const std::string& uri,
- const std::string& handle)
- {
- sessionState* sessionp = NULL;
- if (handle.empty())
- {
- // No handle supplied: check whether there is already a session with
- // this URI
- for (session_set_it_t iter = mSessions.begin();
- iter != mSessions.end(); ++iter)
- {
- sessionState* s = *iter;
- if (s->mSIPURI == uri || s->mAlternateSIPURI == uri)
- {
- // *TODO: it is possible that this case we should raise an
- // Internal error.
- sessionp = s;
- break;
- }
- }
- }
- else
- {
- // Check for an existing session with this handle
- session_map_t::iterator iter = mSessionsByHandle.find(&handle);
- if (iter != mSessionsByHandle.end())
- {
- sessionp = iter->second;
- }
- }
- if (!sessionp)
- {
- // No existing session found.
- LL_DEBUGS("Voice") << "Adding new session: handle " << handle
- << " URI " << uri << LL_ENDL;
- sessionp = new sessionState();
- sessionp->mSIPURI = uri;
- sessionp->mHandle = handle;
- mSessions.insert(sessionp);
- if (!sessionp->mHandle.empty())
- {
- mSessionsByHandle.emplace(&(sessionp->mHandle), sessionp);
- }
- }
- else
- {
- // Found an existing session
- if (uri != sessionp->mSIPURI)
- {
- // TODO: Should this be an Internal error?
- LL_DEBUGS("Voice") << "Changing uri from " << sessionp->mSIPURI
- << " to " << uri << LL_ENDL;
- setSessionURI(sessionp, uri);
- }
- if (handle != sessionp->mHandle)
- {
- if (handle.empty())
- {
- // There is at least one race condition where where addSession
- // was clearing an existing session handle, which caused things
- // to break.
- LL_DEBUGS("Voice") << "NOT clearing handle "
- << sessionp->mHandle << LL_ENDL;
- }
- else
- {
- // TODO: Should this be an Internal error ?
- LL_DEBUGS("Voice") << "Changing handle from "
- << sessionp->mHandle << " to " << handle
- << LL_ENDL;
- setSessionHandle(sessionp, handle);
- }
- }
- LL_DEBUGS("Voice") << "Returning existing session: handle " << handle
- << " URI " << uri << LL_ENDL;
- }
- verifySessionState();
- return sessionp;
- }
- void LLVoiceVivox::setSessionHandle(sessionState* sessionp,
- const std::string& handle)
- {
- if (!sessionp) return;
- // We have to remove the session from the handle-indexed map before
- // changing the handle, or things will break badly.
- if (!sessionp->mHandle.empty())
- {
- // Remove session from the map if it should have been there.
- session_map_t::iterator iter =
- mSessionsByHandle.find(&(sessionp->mHandle));
- if (iter != mSessionsByHandle.end())
- {
- if (iter->second != sessionp)
- {
- llwarns << "Internal error: session mismatch !" << llendl;
- giveUp();
- return;
- }
- mSessionsByHandle.erase(iter);
- }
- else
- {
- llwarns << "Internal error: session handle not found in map !"
- << llendl;
- giveUp();
- return;
- }
- }
- sessionp->mHandle = handle;
- if (!handle.empty())
- {
- mSessionsByHandle.emplace(&(sessionp->mHandle), sessionp);
- }
- verifySessionState();
- }
- void LLVoiceVivox::setSessionURI(sessionState* sessionp,
- const std::string& uri)
- {
- if (sessionp)
- {
- // There used to be a map of session URIs to sessions, which made this
- // complex....
- sessionp->mSIPURI = uri;
- }
- verifySessionState();
- }
- void LLVoiceVivox::deleteSession(sessionState* sessionp)
- {
- if (!sessionp) return;
- // Remove the session from the handle map
- if (!sessionp->mHandle.empty())
- {
- session_map_t::iterator iter =
- mSessionsByHandle.find(&(sessionp->mHandle));
- if (iter != mSessionsByHandle.end())
- {
- if (iter->second != sessionp)
- {
- llwarns << "Internal error: session mismatch !" << llendl;
- giveUp();
- return;
- }
- mSessionsByHandle.erase(iter);
- }
- }
- // Remove the session from the URI map
- mSessions.erase(sessionp);
- // At this point, the session should be unhooked from all lists and all
- // states should be consistent.
- verifySessionState();
- // If this is the current audio session, clean up the pointer which will
- // soon be dangling.
- if (mAudioSession == sessionp)
- {
- mAudioSession = NULL;
- }
- // Ditto for the next audio session
- if (mNextAudioSession == sessionp)
- {
- mNextAudioSession = NULL;
- }
- // Delete the session
- delete sessionp;
- }
- void LLVoiceVivox::deleteAllSessions()
- {
- while (!mSessions.empty())
- {
- deleteSession(*(mSessions.begin()));
- }
- if (!mSessionsByHandle.empty())
- {
- llwarns << "Internal error: empty session map, non-empty handle map"
- << llendl;
- giveUp();
- }
- }
- void LLVoiceVivox::verifySessionState()
- {
- // This is mostly intended for debugging problems with session state
- // management.
- LL_DEBUGS("Voice") << "Total session count: " << mSessions.size()
- << " , session handle map size: "
- << mSessionsByHandle.size() << LL_ENDL;
- session_map_t::iterator map_end = mSessionsByHandle.end();
- session_set_it_t end = mSessions.end();
- for (session_set_it_t iter = mSessions.begin(); iter != end; ++iter)
- {
- sessionState* sessionp = *iter;
- LL_DEBUGS("Voice") << "Session " << sessionp << ": handle "
- << sessionp->mHandle << ", URI "
- << sessionp->mSIPURI << LL_ENDL;
- if (!sessionp->mHandle.empty())
- {
- // Every session with a non-empty handle needs to be in the handle
- // map
- session_map_t::iterator i2 =
- mSessionsByHandle.find(&(sessionp->mHandle));
- if (i2 == map_end)
- {
- llwarns << "Internal error (handle " << sessionp->mHandle
- << " not found in session map)" << llendl;
- giveUp();
- return;
- }
- else if (i2->second != sessionp)
- {
- llwarns << "Internal error (handle " << sessionp->mHandle
- << " in session map points to another session)"
- << llendl;
- giveUp();
- return;
- }
- }
- }
- // Check that every entry in the handle map points to a valid session in
- // the session set
- for (session_map_t::iterator iter = mSessionsByHandle.begin();
- iter != map_end; ++iter)
- {
- sessionState* sessionp = iter->second;
- session_set_it_t i2 = mSessions.find(sessionp);
- if (i2 == mSessions.end())
- {
- llwarns << "Internal error (session for handle "
- << sessionp->mHandle << " not found in session map)"
- << llendl;
- giveUp();
- return;
- }
- else if (sessionp->mHandle != (*i2)->mHandle)
- {
- llwarns << "Internal error (session for handle "
- << sessionp->mHandle
- << " points to session with different handle "
- << (*i2)->mHandle << ")" << llendl;
- giveUp();
- return;
- }
- }
- }
- void LLVoiceVivox::addObserver(LLVoiceClientStatusObserver* observerp)
- {
- mStatusObservers.insert(observerp);
- }
- void LLVoiceVivox::removeObserver(LLVoiceClientStatusObserver* observerp)
- {
- mStatusObservers.erase(observerp);
- }
- void LLVoiceVivox::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
- {
- if (mAudioSession)
- {
- if (status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
- {
- switch (mAudioSession->mErrorStatusCode)
- {
- case 20713:
- status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL;
- break;
- case 20714:
- status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED;
- break;
- case 20715:
- // Invalid channel, we may be using a set of poorly cached
- // info
- status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
- break;
- case 1009:
- // Invalid username and password
- status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
- }
- // Reset the error code to make sure it would not be reused later
- // by accident.
- mAudioSession->mErrorStatusCode = 0;
- }
- else if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
- {
- switch (mAudioSession->mErrorStatusCode)
- {
- case HTTP_NOT_FOUND: // 404
- // *TODO: Should this be 503 ?
- case 480: // TEMPORARILY_UNAVAILABLE
- case HTTP_REQUEST_TIME_OUT: // 408
- // Call failed because other user was not available treat
- // this as an error case
- status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
- // Reset the error code to make sure it would not be reused
- // later by accident.
- mAudioSession->mErrorStatusCode = 0;
- }
- }
- }
- LL_DEBUGS("Voice") << LLVoiceClientStatusObserver::status2string(status)
- << " - Session channel info "
- << getAudioSessionChannelInfo() << " - Proximal is "
- << (inSpatialChannel() ? "true" : "false") << LL_ENDL;
- if (!mProcessChannels)
- {
- return;
- }
- for (status_observer_set_t::iterator it = mStatusObservers.begin();
- it != mStatusObservers.end(); )
- {
- LLVoiceClientStatusObserver* observerp = *it;
- observerp->onChange(status, getAudioSessionChannelInfo(),
- inSpatialChannel());
- // In case onError() deleted an entry.
- it = mStatusObservers.upper_bound(observerp);
- }
- }
- void LLVoiceVivox::lookupName(const LLUUID& id)
- {
- if (gCacheNamep)
- {
- gCacheNamep->get(id, false, onAvatarNameLookup);
- }
- }
- //static
- void LLVoiceVivox::onAvatarNameLookup(const LLUUID& id,
- const std::string& fullname, bool)
- {
- if (!gVoiceVivox.mTerminated) // If Voice has not since been shut down
- {
- gVoiceVivox.avatarNameResolved(id, fullname);
- }
- }
- void LLVoiceVivox::avatarNameResolved(const LLUUID& id,
- const std::string& name)
- {
- // Iterate over all sessions.
- for (session_set_it_t iter = mSessions.begin(); iter != mSessions.end();
- ++iter)
- {
- sessionState* sessionp = *iter;
- // Check for this user as a participant in this session
- participantState* participantp = sessionp->findParticipantByID(id);
- if (participantp)
- {
- // Found: fill in the name
- participantp->mAccountName = name;
- }
- // Check whether this is a P2P session whose caller name just resolved
- if (sessionp->mCallerID == id)
- {
- // This session's "caller ID" just resolved. Fill in the name.
- sessionp->mName = name;
- if (sessionp->mVoiceInvitePending)
- {
- sessionp->mVoiceInvitePending = false;
- gIMMgrp->inviteToSession(sessionp->mIMSessionID,
- sessionp->mName,
- sessionp->mCallerID,
- sessionp->mName,
- IM_SESSION_P2P_INVITE,
- LLIMMgr::INVITATION_TYPE_VOICE,
- sessionp->getVoiceChannelInfo());
- }
- }
- }
- }
|