aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llvoiceclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llvoiceclient.cpp4614
1 files changed, 3620 insertions, 994 deletions
diff --git a/linden/indra/newview/llvoiceclient.cpp b/linden/indra/newview/llvoiceclient.cpp
index 64a17ae..76ef87d 100644
--- a/linden/indra/newview/llvoiceclient.cpp
+++ b/linden/indra/newview/llvoiceclient.cpp
@@ -4,7 +4,7 @@
4 * 4 *
5 * $LicenseInfo:firstyear=2001&license=viewergpl$ 5 * $LicenseInfo:firstyear=2001&license=viewergpl$
6 * 6 *
7 * Copyright (c) 2001-2008, Linden Research, Inc. 7 * Copyright (c) 2001-2009, Linden Research, Inc.
8 * 8 *
9 * Second Life Viewer Source Code 9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab 10 * The source code in this file ("Source Code") is provided by Linden Lab
@@ -62,15 +62,20 @@
62#include "llviewerwindow.h" 62#include "llviewerwindow.h"
63#include "llviewercamera.h" 63#include "llviewercamera.h"
64 64
65#include "llfloaterfriends.h" //VIVOX, inorder to refresh communicate panel
66#include "llfloaterchat.h" // for LLFloaterChat::addChat()
67
65// for base64 decoding 68// for base64 decoding
66#include "apr_base64.h" 69#include "apr_base64.h"
67 70
68// for SHA1 hash 71// for SHA1 hash
69#include "apr_sha1.h" 72#include "apr_sha1.h"
70 73
71// If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name. 74// for MD5 hash
72// If we are connecting to agni and the user's last name is NOT "Linden", disable voice. 75#include "llmd5.h"
73#define AGNI_LINDENS_ONLY_CHANNEL "SL" 76
77#define USE_SESSION_GROUPS 0
78
74static bool sConnectingToAgni = false; 79static bool sConnectingToAgni = false;
75F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; 80F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
76 81
@@ -90,6 +95,44 @@ const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
90const F32 LOGIN_RETRY_SECONDS = 10.0f; 95const F32 LOGIN_RETRY_SECONDS = 10.0f;
91const int MAX_LOGIN_RETRIES = 12; 96const int MAX_LOGIN_RETRIES = 12;
92 97
98static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
99{
100 LLMD5 md5_uuid;
101 md5_uuid.update((const unsigned char*)str.data(), str.size());
102 md5_uuid.finalize();
103 md5_uuid.raw_digest(uuid.mData);
104}
105
106static int scale_mic_volume(float volume)
107{
108 // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
109 // Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75
110
111 volume -= 1.0f; // offset volume to the range [-1.0 ... 1.0], with 0 at the default.
112 int scaled_volume = 44; // offset scaled_volume by its default level
113 if(volume < 0.0f)
114 scaled_volume += ((int)(volume * 4.0f)); // (44 - 40)
115 else
116 scaled_volume += ((int)(volume * 31.0f)); // (75 - 44)
117
118 return scaled_volume;
119}
120
121static int scale_speaker_volume(float volume)
122{
123 // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
124 // Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75
125
126 volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
127 int scaled_volume = 62; // offset scaled_volume by its default level
128 if(volume < 0.0f)
129 scaled_volume += ((int)(volume * 124.0f)); // (62 - 0) * 2
130 else
131 scaled_volume += ((int)(volume * 26.0f)); // (75 - 62) * 2
132
133 return scaled_volume;
134}
135
93class LLViewerVoiceAccountProvisionResponder : 136class LLViewerVoiceAccountProvisionResponder :
94 public LLHTTPClient::Responder 137 public LLHTTPClient::Responder
95{ 138{
@@ -103,12 +146,13 @@ public:
103 { 146 {
104 if ( mRetries > 0 ) 147 if ( mRetries > 0 )
105 { 148 {
149 LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying. status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
106 if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision( 150 if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
107 mRetries - 1); 151 mRetries - 1);
108 } 152 }
109 else 153 else
110 { 154 {
111 //TODO: throw an error message? 155 LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up). status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
112 if ( gVoiceClient ) gVoiceClient->giveUp(); 156 if ( gVoiceClient ) gVoiceClient->giveUp();
113 } 157 }
114 } 158 }
@@ -117,9 +161,23 @@ public:
117 { 161 {
118 if ( gVoiceClient ) 162 if ( gVoiceClient )
119 { 163 {
164 std::string voice_sip_uri_hostname;
165 std::string voice_account_server_uri;
166
167 LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
168
169 if(content.has("voice_sip_uri_hostname"))
170 voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
171
172 // this key is actually misnamed -- it will be an entire URI, not just a hostname.
173 if(content.has("voice_account_server_name"))
174 voice_account_server_uri = content["voice_account_server_name"].asString();
175
120 gVoiceClient->login( 176 gVoiceClient->login(
121 content["username"].asString(), 177 content["username"].asString(),
122 content["password"].asString()); 178 content["password"].asString(),
179 voice_sip_uri_hostname,
180 voice_account_server_uri);
123 } 181 }
124 } 182 }
125 183
@@ -166,21 +224,27 @@ protected:
166 int ignoreDepth; 224 int ignoreDepth;
167 225
168 // Members for processing responses. The values are transient and only valid within a call to processResponse(). 226 // Members for processing responses. The values are transient and only valid within a call to processResponse().
227 bool squelchDebugOutput;
169 int returnCode; 228 int returnCode;
170 int statusCode; 229 int statusCode;
171 std::string statusString; 230 std::string statusString;
172 std::string uuidString; 231 std::string requestId;
173 std::string actionString; 232 std::string actionString;
174 std::string connectorHandle; 233 std::string connectorHandle;
234 std::string versionID;
175 std::string accountHandle; 235 std::string accountHandle;
176 std::string sessionHandle; 236 std::string sessionHandle;
177 std::string eventSessionHandle; 237 std::string sessionGroupHandle;
238 std::string alias;
239 std::string applicationString;
178 240
179 // Members for processing events. The values are transient and only valid within a call to processResponse(). 241 // Members for processing events. The values are transient and only valid within a call to processResponse().
180 std::string eventTypeString; 242 std::string eventTypeString;
181 int state; 243 int state;
182 std::string uriString; 244 std::string uriString;
183 bool isChannel; 245 bool isChannel;
246 bool incoming;
247 bool enabled;
184 std::string nameString; 248 std::string nameString;
185 std::string audioMediaString; 249 std::string audioMediaString;
186 std::string displayNameString; 250 std::string displayNameString;
@@ -190,6 +254,21 @@ protected:
190 bool isSpeaking; 254 bool isSpeaking;
191 int volume; 255 int volume;
192 F32 energy; 256 F32 energy;
257 std::string messageHeader;
258 std::string messageBody;
259 std::string notificationType;
260 bool hasText;
261 bool hasAudio;
262 bool hasVideo;
263 bool terminated;
264 std::string blockMask;
265 std::string presenceOnly;
266 std::string autoAcceptMask;
267 std::string autoAddAsBuddy;
268 int numberOfAliases;
269 std::string subscriptionHandle;
270 std::string subscriptionType;
271
193 272
194 // Members for processing text between tags 273 // Members for processing text between tags
195 std::string textBuffer; 274 std::string textBuffer;
@@ -222,8 +301,6 @@ void LLVivoxProtocolParser::reset()
222 responseDepth = 0; 301 responseDepth = 0;
223 ignoringTags = false; 302 ignoringTags = false;
224 accumulateText = false; 303 accumulateText = false;
225 textBuffer.clear();
226
227 energy = 0.f; 304 energy = 0.f;
228 ignoreDepth = 0; 305 ignoreDepth = 0;
229 isChannel = false; 306 isChannel = false;
@@ -232,10 +309,15 @@ void LLVivoxProtocolParser::reset()
232 isModeratorMuted = false; 309 isModeratorMuted = false;
233 isSpeaking = false; 310 isSpeaking = false;
234 participantType = 0; 311 participantType = 0;
235 returnCode = 0; 312 squelchDebugOutput = false;
313 returnCode = -1;
236 state = 0; 314 state = 0;
237 statusCode = 0; 315 statusCode = 0;
238 volume = 0; 316 volume = 0;
317 textBuffer.clear();
318 alias.clear();
319 numberOfAliases = 0;
320 applicationString.clear();
239} 321}
240 322
241//virtual 323//virtual
@@ -262,33 +344,11 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
262 mInput.append(buf, istr.gcount()); 344 mInput.append(buf, istr.gcount());
263 } 345 }
264 346
265 // MBW -- XXX -- This should no longer be necessary. Or even possible.
266 // We've read all the data out of the buffer. Make sure it doesn't accumulate.
267// buffer->clear();
268
269 // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. 347 // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser.
270 int start = 0; 348 int start = 0;
271 int delim; 349 int delim;
272 while((delim = mInput.find("\n\n\n", start)) != std::string::npos) 350 while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
273 { 351 {
274 // Turn this on to log incoming XML
275 if(0)
276 {
277 int foo = mInput.find("Set3DPosition", start);
278 int bar = mInput.find("ParticipantPropertiesEvent", start);
279 if(foo != std::string::npos && (foo < delim))
280 {
281 // This is a Set3DPosition response. Don't print it, since these are way too spammy.
282 }
283 else if(bar != std::string::npos && (bar < delim))
284 {
285 // This is a ParticipantPropertiesEvent response. Don't print it, since these are way too spammy.
286 }
287 else
288 {
289 LL_INFOS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
290 }
291 }
292 352
293 // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) 353 // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
294 reset(); 354 reset();
@@ -299,13 +359,19 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
299 XML_SetUserData(parser, this); 359 XML_SetUserData(parser, this);
300 XML_Parse(parser, mInput.data() + start, delim - start, false); 360 XML_Parse(parser, mInput.data() + start, delim - start, false);
301 361
362 // If this message isn't set to be squelched, output the raw XML received.
363 if(!squelchDebugOutput)
364 {
365 LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
366 }
367
302 start = delim + 3; 368 start = delim + 3;
303 } 369 }
304 370
305 if(start != 0) 371 if(start != 0)
306 mInput = mInput.substr(start); 372 mInput = mInput.substr(start);
307 373
308 LL_DEBUGS("Voice") << "at end, mInput is: " << mInput << LL_ENDL; 374 LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
309 375
310 if(!gVoiceClient->mConnected) 376 if(!gVoiceClient->mConnected)
311 { 377 {
@@ -360,9 +426,9 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
360 426
361 if (responseDepth == 0) 427 if (responseDepth == 0)
362 { 428 {
363 isEvent = strcmp("Event", tag) == 0; 429 isEvent = !stricmp("Event", tag);
364 430
365 if (strcmp("Response", tag) == 0 || isEvent) 431 if (!stricmp("Response", tag) || isEvent)
366 { 432 {
367 // Grab the attributes 433 // Grab the attributes
368 while (*attr) 434 while (*attr)
@@ -370,49 +436,62 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
370 const char *key = *attr++; 436 const char *key = *attr++;
371 const char *value = *attr++; 437 const char *value = *attr++;
372 438
373 if (strcmp("requestId", key) == 0) 439 if (!stricmp("requestId", key))
374 { 440 {
375 uuidString = value; 441 requestId = value;
376 } 442 }
377 else if (strcmp("action", key) == 0) 443 else if (!stricmp("action", key))
378 { 444 {
379 actionString = value; 445 actionString = value;
380 } 446 }
381 else if (strcmp("type", key) == 0) 447 else if (!stricmp("type", key))
382 { 448 {
383 eventTypeString = value; 449 eventTypeString = value;
384 } 450 }
385 } 451 }
386 } 452 }
387 LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")" << LL_ENDL; 453 LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL;
388 } 454 }
389 else 455 else
390 { 456 {
391 if (ignoringTags) 457 if (ignoringTags)
392 { 458 {
393 LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; 459 LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
394 } 460 }
395 else 461 else
396 { 462 {
397 LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")" << LL_ENDL; 463 LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL;
398 464
399 // Ignore the InputXml stuff so we don't get confused 465 // Ignore the InputXml stuff so we don't get confused
400 if (strcmp("InputXml", tag) == 0) 466 if (!stricmp("InputXml", tag))
401 { 467 {
402 ignoringTags = true; 468 ignoringTags = true;
403 ignoreDepth = responseDepth; 469 ignoreDepth = responseDepth;
404 accumulateText = false; 470 accumulateText = false;
405 471
406 LL_DEBUGS("Voice") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; 472 LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
407 } 473 }
408 else if (strcmp("CaptureDevices", tag) == 0) 474 else if (!stricmp("CaptureDevices", tag))
409 { 475 {
410 gVoiceClient->clearCaptureDevices(); 476 gVoiceClient->clearCaptureDevices();
411 } 477 }
412 else if (strcmp("RenderDevices", tag) == 0) 478 else if (!stricmp("RenderDevices", tag))
413 { 479 {
414 gVoiceClient->clearRenderDevices(); 480 gVoiceClient->clearRenderDevices();
415 } 481 }
482 else if (!stricmp("Buddies", tag))
483 {
484 gVoiceClient->deleteAllBuddies();
485 }
486 else if (!stricmp("BlockRules", tag))
487 {
488 gVoiceClient->deleteAllBlockRules();
489 }
490 else if (!stricmp("AutoAcceptRules", tag))
491 {
492 gVoiceClient->deleteAllAutoAcceptRules();
493 }
494
416 } 495 }
417 } 496 }
418 responseDepth++; 497 responseDepth++;
@@ -431,90 +510,138 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
431 { 510 {
432 if (ignoreDepth == responseDepth) 511 if (ignoreDepth == responseDepth)
433 { 512 {
434 LL_DEBUGS("Voice") << "end of ignore" << LL_ENDL; 513 LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL;
435 ignoringTags = false; 514 ignoringTags = false;
436 } 515 }
437 else 516 else
438 { 517 {
439 LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; 518 LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
440 } 519 }
441 } 520 }
442 521
443 if (!ignoringTags) 522 if (!ignoringTags)
444 { 523 {
445 LL_DEBUGS("Voice") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; 524 LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
446 525
447 // Closing a tag. Finalize the text we've accumulated and reset 526 // Closing a tag. Finalize the text we've accumulated and reset
448 if (strcmp("ReturnCode", tag) == 0) 527 if (!stricmp("ReturnCode", tag))
449 returnCode = strtol(string.c_str(), NULL, 10); 528 returnCode = strtol(string.c_str(), NULL, 10);
450 else if (strcmp("StatusCode", tag) == 0) 529 else if (!stricmp("SessionHandle", tag))
530 sessionHandle = string;
531 else if (!stricmp("SessionGroupHandle", tag))
532 sessionGroupHandle = string;
533 else if (!stricmp("StatusCode", tag))
451 statusCode = strtol(string.c_str(), NULL, 10); 534 statusCode = strtol(string.c_str(), NULL, 10);
452 else if (strcmp("ConnectorHandle", tag) == 0) 535 else if (!stricmp("StatusString", tag))
536 statusString = string;
537 else if (!stricmp("ParticipantURI", tag))
538 uriString = string;
539 else if (!stricmp("Volume", tag))
540 volume = strtol(string.c_str(), NULL, 10);
541 else if (!stricmp("Energy", tag))
542 energy = (F32)strtod(string.c_str(), NULL);
543 else if (!stricmp("IsModeratorMuted", tag))
544 isModeratorMuted = !stricmp(string.c_str(), "true");
545 else if (!stricmp("IsSpeaking", tag))
546 isSpeaking = !stricmp(string.c_str(), "true");
547 else if (!stricmp("Alias", tag))
548 alias = string;
549 else if (!stricmp("NumberOfAliases", tag))
550 numberOfAliases = strtol(string.c_str(), NULL, 10);
551 else if (!stricmp("Application", tag))
552 applicationString = string;
553 else if (!stricmp("ConnectorHandle", tag))
453 connectorHandle = string; 554 connectorHandle = string;
454 else if (strcmp("AccountHandle", tag) == 0) 555 else if (!stricmp("VersionID", tag))
556 versionID = string;
557 else if (!stricmp("AccountHandle", tag))
455 accountHandle = string; 558 accountHandle = string;
456 else if (strcmp("SessionHandle", tag) == 0) 559 else if (!stricmp("State", tag))
457 {
458 if (isEvent)
459 eventSessionHandle = string;
460 else
461 sessionHandle = string;
462 }
463 else if (strcmp("StatusString", tag) == 0)
464 statusString = string;
465 else if (strcmp("State", tag) == 0)
466 state = strtol(string.c_str(), NULL, 10); 560 state = strtol(string.c_str(), NULL, 10);
467 else if (strcmp("URI", tag) == 0) 561 else if (!stricmp("URI", tag))
468 uriString = string; 562 uriString = string;
469 else if (strcmp("IsChannel", tag) == 0) 563 else if (!stricmp("IsChannel", tag))
470 isChannel = string == "true" ? true : false; 564 isChannel = !stricmp(string.c_str(), "true");
471 else if (strcmp("Name", tag) == 0) 565 else if (!stricmp("Incoming", tag))
566 incoming = !stricmp(string.c_str(), "true");
567 else if (!stricmp("Enabled", tag))
568 enabled = !stricmp(string.c_str(), "true");
569 else if (!stricmp("Name", tag))
472 nameString = string; 570 nameString = string;
473 else if (strcmp("AudioMedia", tag) == 0) 571 else if (!stricmp("AudioMedia", tag))
474 audioMediaString = string; 572 audioMediaString = string;
475 else if (strcmp("ChannelName", tag) == 0) 573 else if (!stricmp("ChannelName", tag))
476 nameString = string; 574 nameString = string;
477 else if (strcmp("ParticipantURI", tag) == 0) 575 else if (!stricmp("DisplayName", tag))
478 uriString = string;
479 else if (strcmp("DisplayName", tag) == 0)
480 displayNameString = string; 576 displayNameString = string;
481 else if (strcmp("AccountName", tag) == 0) 577 else if (!stricmp("AccountName", tag))
482 nameString = string; 578 nameString = string;
483 else if (strcmp("ParticipantTyppe", tag) == 0) 579 else if (!stricmp("ParticipantType", tag))
484 participantType = strtol(string.c_str(), NULL, 10); 580 participantType = strtol(string.c_str(), NULL, 10);
485 else if (strcmp("IsLocallyMuted", tag) == 0) 581 else if (!stricmp("IsLocallyMuted", tag))
486 isLocallyMuted = string == "true" ? true : false; 582 isLocallyMuted = !stricmp(string.c_str(), "true");
487 else if (strcmp("IsModeratorMuted", tag) == 0) 583 else if (!stricmp("MicEnergy", tag))
488 isModeratorMuted = string == "true" ? true : false;
489 else if (strcmp("IsSpeaking", tag) == 0)
490 isSpeaking = string == "true" ? true : false;
491 else if (strcmp("Volume", tag) == 0)
492 volume = strtol(string.c_str(), NULL, 10);
493 else if (strcmp("Energy", tag) == 0)
494 energy = (F32)strtod(string.c_str(), NULL); 584 energy = (F32)strtod(string.c_str(), NULL);
495 else if (strcmp("MicEnergy", tag) == 0) 585 else if (!stricmp("ChannelName", tag))
496 energy = (F32)strtod(string.c_str(), NULL);
497 else if (strcmp("ChannelName", tag) == 0)
498 nameString = string; 586 nameString = string;
499 else if (strcmp("ChannelURI", tag) == 0) 587 else if (!stricmp("ChannelURI", tag))
500 uriString = string; 588 uriString = string;
501 else if (strcmp("ChannelListResult", tag) == 0) 589 else if (!stricmp("BuddyURI", tag))
502 { 590 uriString = string;
503 gVoiceClient->addChannelMapEntry(nameString, uriString); 591 else if (!stricmp("Presence", tag))
504 } 592 statusString = string;
505 else if (strcmp("Device", tag) == 0) 593 else if (!stricmp("Device", tag))
506 { 594 {
507 // This closing tag shouldn't clear the accumulated text. 595 // This closing tag shouldn't clear the accumulated text.
508 clearbuffer = false; 596 clearbuffer = false;
509 } 597 }
510 else if (strcmp("CaptureDevice", tag) == 0) 598 else if (!stricmp("CaptureDevice", tag))
511 { 599 {
512 gVoiceClient->addCaptureDevice(textBuffer); 600 gVoiceClient->addCaptureDevice(textBuffer);
513 } 601 }
514 else if (strcmp("RenderDevice", tag) == 0) 602 else if (!stricmp("RenderDevice", tag))
515 { 603 {
516 gVoiceClient->addRenderDevice(textBuffer); 604 gVoiceClient->addRenderDevice(textBuffer);
517 } 605 }
606 else if (!stricmp("Buddy", tag))
607 {
608 gVoiceClient->processBuddyListEntry(uriString, displayNameString);
609 }
610 else if (!stricmp("BlockRule", tag))
611 {
612 gVoiceClient->addBlockRule(blockMask, presenceOnly);
613 }
614 else if (!stricmp("BlockMask", tag))
615 blockMask = string;
616 else if (!stricmp("PresenceOnly", tag))
617 presenceOnly = string;
618 else if (!stricmp("AutoAcceptRule", tag))
619 {
620 gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy);
621 }
622 else if (!stricmp("AutoAcceptMask", tag))
623 autoAcceptMask = string;
624 else if (!stricmp("AutoAddAsBuddy", tag))
625 autoAddAsBuddy = string;
626 else if (!stricmp("MessageHeader", tag))
627 messageHeader = string;
628 else if (!stricmp("MessageBody", tag))
629 messageBody = string;
630 else if (!stricmp("NotificationType", tag))
631 notificationType = string;
632 else if (!stricmp("HasText", tag))
633 hasText = !stricmp(string.c_str(), "true");
634 else if (!stricmp("HasAudio", tag))
635 hasAudio = !stricmp(string.c_str(), "true");
636 else if (!stricmp("HasVideo", tag))
637 hasVideo = !stricmp(string.c_str(), "true");
638 else if (!stricmp("Terminated", tag))
639 terminated = !stricmp(string.c_str(), "true");
640 else if (!stricmp("SubscriptionHandle", tag))
641 subscriptionHandle = string;
642 else if (!stricmp("SubscriptionType", tag))
643 subscriptionType = string;
644
518 645
519 if(clearbuffer) 646 if(clearbuffer)
520 { 647 {
@@ -549,144 +676,296 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length)
549 676
550void LLVivoxProtocolParser::processResponse(std::string tag) 677void LLVivoxProtocolParser::processResponse(std::string tag)
551{ 678{
552 LL_DEBUGS("Voice") << tag << LL_ENDL; 679 LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
553 680
681 // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs.
682 // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
683 // so I believe this will give correct behavior.
684
685 if(returnCode == 0)
686 statusCode = 0;
687
554 if (isEvent) 688 if (isEvent)
555 { 689 {
556 if (eventTypeString == "LoginStateChangeEvent") 690 const char *eventTypeCstr = eventTypeString.c_str();
691 if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
557 { 692 {
558 gVoiceClient->loginStateChangeEvent(accountHandle, statusCode, statusString, state); 693 gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
559 } 694 }
560 else if (eventTypeString == "SessionNewEvent") 695 else if (!stricmp(eventTypeCstr, "SessionAddedEvent"))
561 { 696 {
562 gVoiceClient->sessionNewEvent(accountHandle, eventSessionHandle, state, nameString, uriString); 697 /*
698 <Event type="SessionAddedEvent">
699 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
700 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
701 <Uri>sip:confctl-1408789@bhr.vivox.com</Uri>
702 <IsChannel>true</IsChannel>
703 <Incoming>false</Incoming>
704 <ChannelName />
705 </Event>
706 */
707 gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString);
563 } 708 }
564 else if (eventTypeString == "SessionStateChangeEvent") 709 else if (!stricmp(eventTypeCstr, "SessionRemovedEvent"))
565 { 710 {
566 gVoiceClient->sessionStateChangeEvent(uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString); 711 gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
567 } 712 }
568 else if (eventTypeString == "ParticipantStateChangeEvent") 713 else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
569 { 714 {
570 gVoiceClient->participantStateChangeEvent(uriString, statusCode, statusString, state, nameString, displayNameString, participantType); 715 gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle);
571 716 }
717 else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent"))
718 {
719 /*
720 <Event type="MediaStreamUpdatedEvent">
721 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
722 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
723 <StatusCode>200</StatusCode>
724 <StatusString>OK</StatusString>
725 <State>2</State>
726 <Incoming>false</Incoming>
727 </Event>
728 */
729 gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming);
730 }
731 else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
732 {
733 /*
734 <Event type="TextStreamUpdatedEvent">
735 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
736 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
737 <Enabled>true</Enabled>
738 <State>1</State>
739 <Incoming>true</Incoming>
740 </Event>
741 */
742 gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
572 } 743 }
573 else if (eventTypeString == "ParticipantPropertiesEvent") 744 else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
574 { 745 {
575 gVoiceClient->participantPropertiesEvent(uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy); 746 /*
747 <Event type="ParticipantAddedEvent">
748 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
749 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
750 <ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri>
751 <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
752 <DisplayName />
753 <ParticipantType>0</ParticipantType>
754 </Event>
755 */
756 gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
576 } 757 }
577 else if (eventTypeString == "AuxAudioPropertiesEvent") 758 else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
759 {
760 /*
761 <Event type="ParticipantRemovedEvent">
762 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
763 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
764 <ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri>
765 <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
766 </Event>
767 */
768 gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
769 }
770 else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
771 {
772 /*
773 <Event type="ParticipantUpdatedEvent">
774 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
775 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
776 <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
777 <IsModeratorMuted>false</IsModeratorMuted>
778 <IsSpeaking>true</IsSpeaking>
779 <Volume>44</Volume>
780 <Energy>0.0879437</Energy>
781 </Event>
782 */
783
784 // These happen so often that logging them is pretty useless.
785 squelchDebugOutput = true;
786
787 gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
788 }
789 else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
578 { 790 {
579 gVoiceClient->auxAudioPropertiesEvent(energy); 791 gVoiceClient->auxAudioPropertiesEvent(energy);
580 } 792 }
793 else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent"))
794 {
795 gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString);
796 }
797 else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent"))
798 {
799 // The buddy list was updated during parsing.
800 // Need to recheck against the friends list.
801 gVoiceClient->buddyListChanged();
802 }
803 else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
804 {
805 /*
806 <Event type="BuddyChangedEvent">
807 <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
808 <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
809 <DisplayName>Monroe Tester</DisplayName>
810 <BuddyData />
811 <GroupID>0</GroupID>
812 <ChangeType>Set</ChangeType>
813 </Event>
814 */
815 // TODO: Question: Do we need to process this at all?
816 }
817 else if (!stricmp(eventTypeCstr, "MessageEvent"))
818 {
819 gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
820 }
821 else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))
822 {
823 gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType);
824 }
825 else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))
826 {
827 gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType);
828 }
829 else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))
830 {
831 /*
832 <Event type="SessionUpdatedEvent">
833 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
834 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
835 <Uri>sip:confctl-9@bhd.vivox.com</Uri>
836 <IsMuted>0</IsMuted>
837 <Volume>50</Volume>
838 <TransmitEnabled>1</TransmitEnabled>
839 <IsFocused>0</IsFocused>
840 <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
841 <SessionFontID>0</SessionFontID>
842 </Event>
843 */
844 // We don't need to process this, but we also shouldn't warn on it, since that confuses people.
845 }
846 else
847 {
848 LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
849 }
581 } 850 }
582 else 851 else
583 { 852 {
584 if (actionString == "Connector.Create.1") 853 const char *actionCstr = actionString.c_str();
854 if (!stricmp(actionCstr, "Connector.Create.1"))
585 { 855 {
586 gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle); 856 gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
587 } 857 }
588 else if (actionString == "Account.Login.1") 858 else if (!stricmp(actionCstr, "Account.Login.1"))
589 { 859 {
590 gVoiceClient->loginResponse(statusCode, statusString, accountHandle); 860 gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases);
591 } 861 }
592 else if (actionString == "Session.Create.1") 862 else if (!stricmp(actionCstr, "Session.Create.1"))
593 { 863 {
594 gVoiceClient->sessionCreateResponse(statusCode, statusString, sessionHandle); 864 gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);
595 } 865 }
596 else if (actionString == "Session.Connect.1") 866 else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
597 { 867 {
598 gVoiceClient->sessionConnectResponse(statusCode, statusString); 868 gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);
599 } 869 }
600 else if (actionString == "Session.Terminate.1") 870 else if (!stricmp(actionCstr, "Session.Connect.1"))
601 { 871 {
602 gVoiceClient->sessionTerminateResponse(statusCode, statusString); 872 gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString);
603 } 873 }
604 else if (actionString == "Account.Logout.1") 874 else if (!stricmp(actionCstr, "Account.Logout.1"))
605 { 875 {
606 gVoiceClient->logoutResponse(statusCode, statusString); 876 gVoiceClient->logoutResponse(statusCode, statusString);
607 } 877 }
608 else if (actionString == "Connector.InitiateShutdown.1") 878 else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
609 { 879 {
610 gVoiceClient->connectorShutdownResponse(statusCode, statusString); 880 gVoiceClient->connectorShutdownResponse(statusCode, statusString);
611 } 881 }
612 else if (actionString == "Account.ChannelGetList.1") 882 else if (!stricmp(actionCstr, "Account.ListBlockRules.1"))
613 { 883 {
614 gVoiceClient->channelGetListResponse(statusCode, statusString); 884 gVoiceClient->accountListBlockRulesResponse(statusCode, statusString);
885 }
886 else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1"))
887 {
888 gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);
889 }
890 else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
891 {
892 // We don't need to process these, but they're so spammy we don't want to log them.
893 squelchDebugOutput = true;
615 } 894 }
616/* 895/*
617 else if (actionString == "Connector.AccountCreate.1") 896 else if (!stricmp(actionCstr, "Account.ChannelGetList.1"))
618 { 897 {
619 898 gVoiceClient->channelGetListResponse(statusCode, statusString);
620 } 899 }
621 else if (actionString == "Connector.MuteLocalMic.1") 900 else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
622 { 901 {
623 902
624 } 903 }
625 else if (actionString == "Connector.MuteLocalSpeaker.1") 904 else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
626 { 905 {
627 906
628 } 907 }
629 else if (actionString == "Connector.SetLocalMicVolume.1") 908 else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
630 { 909 {
631 910
632 } 911 }
633 else if (actionString == "Connector.SetLocalSpeakerVolume.1") 912 else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
634 { 913 {
635 914
636 } 915 }
637 else if (actionString == "Session.ListenerSetPosition.1") 916 else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
638 { 917 {
639 918
640 } 919 }
641 else if (actionString == "Session.SpeakerSetPosition.1") 920 else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
642 { 921 {
643 922
644 } 923 }
645 else if (actionString == "Session.Set3DPosition.1") 924 else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
646 { 925 {
647 926
648 } 927 }
649 else if (actionString == "Session.AudioSourceSetPosition.1") 928 else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
650 { 929 {
651 930
652 } 931 }
653 else if (actionString == "Session.GetChannelParticipants.1") 932 else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
654 { 933 {
655 934
656 } 935 }
657 else if (actionString == "Account.ChannelCreate.1") 936 else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
658 { 937 {
659 938
660 } 939 }
661 else if (actionString == "Account.ChannelUpdate.1") 940 else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
662 { 941 {
663 942
664 } 943 }
665 else if (actionString == "Account.ChannelDelete.1") 944 else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
666 { 945 {
667 946
668 } 947 }
669 else if (actionString == "Account.ChannelCreateAndInvite.1") 948 else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
670 { 949 {
671 950
672 } 951 }
673 else if (actionString == "Account.ChannelFolderCreate.1") 952 else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
674 { 953 {
675 954
676 } 955 }
677 else if (actionString == "Account.ChannelFolderUpdate.1") 956 else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
678 { 957 {
679 958
680 } 959 }
681 else if (actionString == "Account.ChannelFolderDelete.1") 960 else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
682 { 961 {
683 962
684 } 963 }
685 else if (actionString == "Account.ChannelAddModerator.1") 964 else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
686 { 965 {
687 966
688 } 967 }
689 else if (actionString == "Account.ChannelDeleteModerator.1") 968 else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
690 { 969 {
691 970
692 } 971 }
@@ -700,9 +979,18 @@ class LLVoiceClientMuteListObserver : public LLMuteListObserver
700{ 979{
701 /* virtual */ void onChange() { gVoiceClient->muteListChanged();} 980 /* virtual */ void onChange() { gVoiceClient->muteListChanged();}
702}; 981};
982
983class LLVoiceClientFriendsObserver : public LLFriendObserver
984{
985public:
986 /* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);}
987};
988
703static LLVoiceClientMuteListObserver mutelist_listener; 989static LLVoiceClientMuteListObserver mutelist_listener;
704static bool sMuteListListener_listening = false; 990static bool sMuteListListener_listening = false;
705 991
992static LLVoiceClientFriendsObserver *friendslist_listener = NULL;
993
706/////////////////////////////////////////////////////////////////////////////////////////////// 994///////////////////////////////////////////////////////////////////////////////////////////////
707 995
708class LLVoiceClientCapResponder : public LLHTTPClient::Responder 996class LLVoiceClientCapResponder : public LLHTTPClient::Responder
@@ -726,11 +1014,8 @@ void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
726void LLVoiceClientCapResponder::result(const LLSD& content) 1014void LLVoiceClientCapResponder::result(const LLSD& content)
727{ 1015{
728 LLSD::map_const_iterator iter; 1016 LLSD::map_const_iterator iter;
729 for(iter = content.beginMap(); iter != content.endMap(); ++iter) 1017
730 { 1018 LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
731 LL_DEBUGS("Voice") << "LLVoiceClientCapResponder::result got "
732 << iter->first << LL_ENDL;
733 }
734 1019
735 if ( content.has("voice_credentials") ) 1020 if ( content.has("voice_credentials") )
736 { 1021 {
@@ -816,26 +1101,26 @@ LLVoiceClient::LLVoiceClient()
816 mUserPTTState = false; 1101 mUserPTTState = false;
817 mMuteMic = false; 1102 mMuteMic = false;
818 mSessionTerminateRequested = false; 1103 mSessionTerminateRequested = false;
1104 mRelogRequested = false;
819 mCommandCookie = 0; 1105 mCommandCookie = 0;
820 mNonSpatialChannel = false;
821 mNextSessionSpatial = true;
822 mNextSessionNoReconnect = false;
823 mSessionP2P = false;
824 mCurrentParcelLocalID = 0; 1106 mCurrentParcelLocalID = 0;
825 mLoginRetryCount = 0; 1107 mLoginRetryCount = 0;
826 mVivoxErrorStatusCode = 0;
827 1108
828 mNextSessionResetOnClose = false;
829 mSessionResetOnClose = false;
830 mSpeakerVolume = 0; 1109 mSpeakerVolume = 0;
831 mMicVolume = 0; 1110 mMicVolume = 0;
832 1111
1112 mAudioSession = NULL;
1113 mAudioSessionChanged = false;
1114
833 // Initial dirty state 1115 // Initial dirty state
834 mSpatialCoordsDirty = false; 1116 mSpatialCoordsDirty = false;
835 mPTTDirty = true; 1117 mPTTDirty = true;
836 mVolumeDirty = true; 1118 mFriendsListDirty = true;
837 mSpeakerVolumeDirty = true; 1119 mSpeakerVolumeDirty = true;
838 mMicVolumeDirty = true; 1120 mMicVolumeDirty = true;
1121 mBuddyListMapPopulated = false;
1122 mBlockRulesListReceived = false;
1123 mAutoAcceptRulesListReceived = false;
839 mCaptureDeviceDirty = false; 1124 mCaptureDeviceDirty = false;
840 mRenderDeviceDirty = false; 1125 mRenderDeviceDirty = false;
841 1126
@@ -856,14 +1141,12 @@ LLVoiceClient::LLVoiceClient()
856 // gMuteListp isn't set up at this point, so we defer this until later. 1141 // gMuteListp isn't set up at this point, so we defer this until later.
857// gMuteListp->addObserver(&mutelist_listener); 1142// gMuteListp->addObserver(&mutelist_listener);
858 1143
859 mParticipantMapChanged = false;
860
861 // stash the pump for later use 1144 // stash the pump for later use
862 // This now happens when init() is called instead. 1145 // This now happens when init() is called instead.
863 mPump = NULL; 1146 mPump = NULL;
864 1147
865#if LL_DARWIN || LL_LINUX 1148#if LL_DARWIN || LL_LINUX
866 // MBW -- XXX -- THIS DOES NOT BELONG HERE 1149 // HACK: THIS DOES NOT BELONG HERE
867 // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. 1150 // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
868 // This should cause us to ignore SIGPIPE and handle the error through proper channels. 1151 // This should cause us to ignore SIGPIPE and handle the error through proper channels.
869 // This should really be set up elsewhere. Where should it go? 1152 // This should really be set up elsewhere. Where should it go?
@@ -899,8 +1182,10 @@ void LLVoiceClient::terminate()
899{ 1182{
900 if(gVoiceClient) 1183 if(gVoiceClient)
901 { 1184 {
902 gVoiceClient->sessionTerminateSendMessage(); 1185// gVoiceClient->leaveAudioSession();
903 gVoiceClient->logout(); 1186 gVoiceClient->logout();
1187 // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to.
1188 // ms_sleep(2000);
904 gVoiceClient->connectorShutdown(); 1189 gVoiceClient->connectorShutdown();
905 gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. 1190 gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
906 1191
@@ -925,13 +1210,13 @@ void LLVoiceClient::updateSettings()
925 setPTTKey(keyString); 1210 setPTTKey(keyString);
926 setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); 1211 setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
927 setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); 1212 setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
928 std::string serverName = gSavedSettings.getString("VivoxDebugServerName");
929 setVivoxDebugServerName(serverName);
930 1213
931 std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); 1214 std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
932 setCaptureDevice(inputDevice); 1215 setCaptureDevice(inputDevice);
933 std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); 1216 std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
934 setRenderDevice(outputDevice); 1217 setRenderDevice(outputDevice);
1218 F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
1219 setMicGain(mic_level);
935 setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); 1220 setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled"));
936} 1221}
937 1222
@@ -947,9 +1232,10 @@ bool LLVoiceClient::writeString(const std::string &str)
947 apr_size_t size = (apr_size_t)str.size(); 1232 apr_size_t size = (apr_size_t)str.size();
948 apr_size_t written = size; 1233 apr_size_t written = size;
949 1234
950 LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; 1235 //MARK: Turn this on to log outgoing XML
1236// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
951 1237
952 // MBW -- XXX -- check return code - sockets will fail (broken, etc.) 1238 // check return code - sockets will fail (broken, etc.)
953 err = apr_socket_send( 1239 err = apr_socket_send(
954 mSocket->getSocket(), 1240 mSocket->getSocket(),
955 (const char*)str.data(), 1241 (const char*)str.data(),
@@ -960,7 +1246,7 @@ bool LLVoiceClient::writeString(const std::string &str)
960 // Success. 1246 // Success.
961 result = true; 1247 result = true;
962 } 1248 }
963 // MBW -- XXX -- handle partial writes (written is number of bytes written) 1249 // TODO: handle partial writes (written is number of bytes written)
964 // Need to set socket to non-blocking before this will work. 1250 // Need to set socket to non-blocking before this will work.
965// else if(APR_STATUS_IS_EAGAIN(err)) 1251// else if(APR_STATUS_IS_EAGAIN(err))
966// { 1252// {
@@ -984,7 +1270,7 @@ bool LLVoiceClient::writeString(const std::string &str)
984void LLVoiceClient::connectorCreate() 1270void LLVoiceClient::connectorCreate()
985{ 1271{
986 std::ostringstream stream; 1272 std::ostringstream stream;
987 std::string logpath; 1273 std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
988 std::string loglevel = "0"; 1274 std::string loglevel = "0";
989 1275
990 // Transition to stateConnectorStarted when the connector handle comes back. 1276 // Transition to stateConnectorStarted when the connector handle comes back.
@@ -996,20 +1282,20 @@ void LLVoiceClient::connectorCreate()
996 { 1282 {
997 LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; 1283 LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
998 loglevel = "10"; 1284 loglevel = "10";
999 logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
1000 } 1285 }
1001 1286
1002 stream 1287 stream
1003 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">" 1288 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
1004 << "<ClientName>V2 SDK</ClientName>" 1289 << "<ClientName>V2 SDK</ClientName>"
1005 << "<AccountManagementServer>" << mAccountServerURI << "</AccountManagementServer>" 1290 << "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
1291 << "<Mode>Normal</Mode>"
1006 << "<Logging>" 1292 << "<Logging>"
1007 << "<Enabled>false</Enabled>"
1008 << "<Folder>" << logpath << "</Folder>" 1293 << "<Folder>" << logpath << "</Folder>"
1009 << "<FileNamePrefix>Connector</FileNamePrefix>" 1294 << "<FileNamePrefix>Connector</FileNamePrefix>"
1010 << "<FileNameSuffix>.log</FileNameSuffix>" 1295 << "<FileNameSuffix>.log</FileNameSuffix>"
1011 << "<LogLevel>" << loglevel << "</LogLevel>" 1296 << "<LogLevel>" << loglevel << "</LogLevel>"
1012 << "</Logging>" 1297 << "</Logging>"
1298 << "<Application>SecondLifeViewer.1</Application>"
1013 << "</Request>\n\n\n"; 1299 << "</Request>\n\n\n";
1014 1300
1015 writeString(stream.str()); 1301 writeString(stream.str());
@@ -1047,20 +1333,6 @@ void LLVoiceClient::userAuthorized(const std::string& firstName, const std::stri
1047 1333
1048 sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid(); 1334 sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid();
1049 1335
1050 // MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.
1051 if(sConnectingToAgni)
1052 {
1053 // Use the release account server
1054 mAccountServerName = "bhr.vivox.com";
1055 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
1056 }
1057 else
1058 {
1059 // Use the development account server
1060 mAccountServerName = gSavedSettings.getString("VivoxDebugServerName");
1061 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
1062 }
1063
1064 mAccountName = nameFromID(agentID); 1336 mAccountName = nameFromID(agentID);
1065} 1337}
1066 1338
@@ -1082,24 +1354,69 @@ void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
1082} 1354}
1083 1355
1084void LLVoiceClient::login( 1356void LLVoiceClient::login(
1085 const std::string& accountName, 1357 const std::string& account_name,
1086 const std::string &password) 1358 const std::string& password,
1359 const std::string& voice_sip_uri_hostname,
1360 const std::string& voice_account_server_uri)
1087{ 1361{
1088 if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut)) 1362 mVoiceSIPURIHostName = voice_sip_uri_hostname;
1363 mVoiceAccountServerURI = voice_account_server_uri;
1364
1365 if(!mAccountHandle.empty())
1089 { 1366 {
1090 // Already logged in. This is an internal error. 1367 // Already logged in.
1091 LL_ERRS("Voice") << "Can't login again. Called from wrong state." << LL_ENDL; 1368 LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL;
1369
1370 // Don't process another login.
1371 return;
1092 } 1372 }
1093 else if ( accountName != mAccountName ) 1373 else if ( account_name != mAccountName )
1094 { 1374 {
1095 //TODO: error? 1375 //TODO: error?
1096 LL_WARNS("Voice") << "Wrong account name! " << accountName 1376 LL_WARNS("Voice") << "Wrong account name! " << account_name
1097 << " instead of " << mAccountName << LL_ENDL; 1377 << " instead of " << mAccountName << LL_ENDL;
1098 } 1378 }
1099 else 1379 else
1100 { 1380 {
1101 mAccountPassword = password; 1381 mAccountPassword = password;
1102 } 1382 }
1383
1384 std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
1385
1386 if( !debugSIPURIHostName.empty() )
1387 {
1388 mVoiceSIPURIHostName = debugSIPURIHostName;
1389 }
1390
1391 if( mVoiceSIPURIHostName.empty() )
1392 {
1393 // we have an empty account server name
1394 // so we fall back to hardcoded defaults
1395
1396 if(sConnectingToAgni)
1397 {
1398 // Use the release account server
1399 mVoiceSIPURIHostName = "bhr.vivox.com";
1400 }
1401 else
1402 {
1403 // Use the development account server
1404 mVoiceSIPURIHostName = "bhd.vivox.com";
1405 }
1406 }
1407
1408 std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
1409
1410 if( !debugAccountServerURI.empty() )
1411 {
1412 mVoiceAccountServerURI = debugAccountServerURI;
1413 }
1414
1415 if( mVoiceAccountServerURI.empty() )
1416 {
1417 // If the account server URI isn't specified, construct it from the SIP URI hostname
1418 mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";
1419 }
1103} 1420}
1104 1421
1105void LLVoiceClient::idle(void* user_data) 1422void LLVoiceClient::idle(void* user_data)
@@ -1117,11 +1434,16 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
1117 1434
1118 switch(inState) 1435 switch(inState)
1119 { 1436 {
1437 CASE(stateDisableCleanup);
1120 CASE(stateDisabled); 1438 CASE(stateDisabled);
1121 CASE(stateStart); 1439 CASE(stateStart);
1122 CASE(stateDaemonLaunched); 1440 CASE(stateDaemonLaunched);
1123 CASE(stateConnecting); 1441 CASE(stateConnecting);
1442 CASE(stateConnected);
1124 CASE(stateIdle); 1443 CASE(stateIdle);
1444 CASE(stateMicTuningStart);
1445 CASE(stateMicTuningRunning);
1446 CASE(stateMicTuningStop);
1125 CASE(stateConnectorStart); 1447 CASE(stateConnectorStart);
1126 CASE(stateConnectorStarting); 1448 CASE(stateConnectorStarting);
1127 CASE(stateConnectorStarted); 1449 CASE(stateConnectorStarted);
@@ -1130,12 +1452,8 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
1130 CASE(stateNeedsLogin); 1452 CASE(stateNeedsLogin);
1131 CASE(stateLoggingIn); 1453 CASE(stateLoggingIn);
1132 CASE(stateLoggedIn); 1454 CASE(stateLoggedIn);
1455 CASE(stateCreatingSessionGroup);
1133 CASE(stateNoChannel); 1456 CASE(stateNoChannel);
1134 CASE(stateMicTuningStart);
1135 CASE(stateMicTuningRunning);
1136 CASE(stateMicTuningStop);
1137 CASE(stateSessionCreate);
1138 CASE(stateSessionConnect);
1139 CASE(stateJoiningSession); 1457 CASE(stateJoiningSession);
1140 CASE(stateSessionJoined); 1458 CASE(stateSessionJoined);
1141 CASE(stateRunning); 1459 CASE(stateRunning);
@@ -1152,7 +1470,6 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
1152 CASE(stateJoinSessionFailed); 1470 CASE(stateJoinSessionFailed);
1153 CASE(stateJoinSessionFailedWaiting); 1471 CASE(stateJoinSessionFailedWaiting);
1154 CASE(stateJail); 1472 CASE(stateJail);
1155 CASE(stateMicTuningNoLogin);
1156 } 1473 }
1157 1474
1158#undef CASE 1475#undef CASE
@@ -1174,6 +1491,7 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv
1174 CASE(STATUS_JOINING); 1491 CASE(STATUS_JOINING);
1175 CASE(STATUS_JOINED); 1492 CASE(STATUS_JOINED);
1176 CASE(STATUS_LEFT_CHANNEL); 1493 CASE(STATUS_LEFT_CHANNEL);
1494 CASE(STATUS_VOICE_DISABLED);
1177 CASE(BEGIN_ERROR_STATUS); 1495 CASE(BEGIN_ERROR_STATUS);
1178 CASE(ERROR_CHANNEL_FULL); 1496 CASE(ERROR_CHANNEL_FULL);
1179 CASE(ERROR_CHANNEL_LOCKED); 1497 CASE(ERROR_CHANNEL_LOCKED);
@@ -1207,9 +1525,13 @@ void LLVoiceClient::stateMachine()
1207 { 1525 {
1208 updatePosition(); 1526 updatePosition();
1209 } 1527 }
1528 else if(mTuningMode)
1529 {
1530 // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled.
1531 }
1210 else 1532 else
1211 { 1533 {
1212 if(getState() != stateDisabled) 1534 if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
1213 { 1535 {
1214 // User turned off voice support. Send the cleanup messages, close the socket, and reset. 1536 // User turned off voice support. Send the cleanup messages, close the socket, and reset.
1215 if(!mConnected) 1537 if(!mConnected)
@@ -1219,13 +1541,10 @@ void LLVoiceClient::stateMachine()
1219 killGateway(); 1541 killGateway();
1220 } 1542 }
1221 1543
1222 sessionTerminateSendMessage();
1223 logout(); 1544 logout();
1224 connectorShutdown(); 1545 connectorShutdown();
1225 closeSocket(); 1546
1226 removeAllParticipants(); 1547 setState(stateDisableCleanup);
1227
1228 setState(stateDisabled);
1229 } 1548 }
1230 } 1549 }
1231 1550
@@ -1240,7 +1559,7 @@ void LLVoiceClient::stateMachine()
1240 std::string regionName = region->getName(); 1559 std::string regionName = region->getName();
1241 std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); 1560 std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
1242 1561
1243 LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL; 1562// LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL;
1244 1563
1245 // The region name starts out empty and gets filled in later. 1564 // The region name starts out empty and gets filled in later.
1246 // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. 1565 // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
@@ -1261,13 +1580,30 @@ void LLVoiceClient::stateMachine()
1261 1580
1262 switch(getState()) 1581 switch(getState())
1263 { 1582 {
1583 //MARK: stateDisableCleanup
1584 case stateDisableCleanup:
1585 // Clean up and reset everything.
1586 closeSocket();
1587 deleteAllSessions();
1588 deleteAllBuddies();
1589
1590 mConnectorHandle.clear();
1591 mAccountHandle.clear();
1592 mAccountPassword.clear();
1593 mVoiceAccountServerURI.clear();
1594
1595 setState(stateDisabled);
1596 break;
1597
1598 //MARK: stateDisabled
1264 case stateDisabled: 1599 case stateDisabled:
1265 if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode)) 1600 if(mTuningMode || (mVoiceEnabled && !mAccountName.empty()))
1266 { 1601 {
1267 setState(stateStart); 1602 setState(stateStart);
1268 } 1603 }
1269 break; 1604 break;
1270 1605
1606 //MARK: stateStart
1271 case stateStart: 1607 case stateStart:
1272 if(gSavedSettings.getBOOL("CmdLineDisableVoice")) 1608 if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
1273 { 1609 {
@@ -1298,7 +1634,9 @@ void LLVoiceClient::stateMachine()
1298 if(!LLFile::stat(exe_path, &s)) 1634 if(!LLFile::stat(exe_path, &s))
1299 { 1635 {
1300 // vivox executable exists. Build the command line and launch the daemon. 1636 // vivox executable exists. Build the command line and launch the daemon.
1301 std::string args = " -p tcp -h -c"; 1637 // SLIM SDK: these arguments are no longer necessary.
1638// std::string args = " -p tcp -h -c";
1639 std::string args;
1302 std::string cmd; 1640 std::string cmd;
1303 std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); 1641 std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
1304 1642
@@ -1383,14 +1721,15 @@ void LLVoiceClient::stateMachine()
1383 } 1721 }
1384 else 1722 else
1385 { 1723 {
1386 LL_INFOS("Voice") << exe_path << "not found." << LL_ENDL; 1724 LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
1387 } 1725 }
1388 } 1726 }
1389 else 1727 else
1390 { 1728 {
1729 // SLIM SDK: port changed from 44124 to 44125.
1391 // We can connect to a client gateway running on another host. This is useful for testing. 1730 // We can connect to a client gateway running on another host. This is useful for testing.
1392 // To do this, launch the gateway on a nearby host like this: 1731 // To do this, launch the gateway on a nearby host like this:
1393 // vivox-gw.exe -p tcp -i 0.0.0.0:44124 1732 // vivox-gw.exe -p tcp -i 0.0.0.0:44125
1394 // and put that host's IP address here. 1733 // and put that host's IP address here.
1395 mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort")); 1734 mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort"));
1396 } 1735 }
@@ -1402,17 +1741,23 @@ void LLVoiceClient::stateMachine()
1402 1741
1403 // Dirty the states we'll need to sync with the daemon when it comes up. 1742 // Dirty the states we'll need to sync with the daemon when it comes up.
1404 mPTTDirty = true; 1743 mPTTDirty = true;
1744 mMicVolumeDirty = true;
1405 mSpeakerVolumeDirty = true; 1745 mSpeakerVolumeDirty = true;
1746 mSpeakerMuteDirty = true;
1406 // These only need to be set if they're not default (i.e. empty string). 1747 // These only need to be set if they're not default (i.e. empty string).
1407 mCaptureDeviceDirty = !mCaptureDevice.empty(); 1748 mCaptureDeviceDirty = !mCaptureDevice.empty();
1408 mRenderDeviceDirty = !mRenderDevice.empty(); 1749 mRenderDeviceDirty = !mRenderDevice.empty();
1750
1751 mMainSessionGroupHandle.clear();
1409 } 1752 }
1410 break; 1753 break;
1411 1754
1755 //MARK: stateDaemonLaunched
1412 case stateDaemonLaunched: 1756 case stateDaemonLaunched:
1413 LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
1414 if(mUpdateTimer.hasExpired()) 1757 if(mUpdateTimer.hasExpired())
1415 { 1758 {
1759 LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
1760
1416 mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); 1761 mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
1417 1762
1418 if(!mSocket) 1763 if(!mSocket)
@@ -1433,6 +1778,7 @@ void LLVoiceClient::stateMachine()
1433 } 1778 }
1434 break; 1779 break;
1435 1780
1781 //MARK: stateConnecting
1436 case stateConnecting: 1782 case stateConnecting:
1437 // Can't do this until we have the pump available. 1783 // Can't do this until we have the pump available.
1438 if(mPump) 1784 if(mPump)
@@ -1450,48 +1796,34 @@ void LLVoiceClient::stateMachine()
1450 1796
1451 mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); 1797 mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
1452 1798
1453 setState(stateIdle); 1799 setState(stateConnected);
1454 } 1800 }
1455 1801
1456 break; 1802 break;
1457 1803
1458 case stateIdle: 1804 //MARK: stateConnected
1805 case stateConnected:
1459 // Initial devices query 1806 // Initial devices query
1460 getCaptureDevicesSendMessage(); 1807 getCaptureDevicesSendMessage();
1461 getRenderDevicesSendMessage(); 1808 getRenderDevicesSendMessage();
1462 1809
1463 mLoginRetryCount = 0; 1810 mLoginRetryCount = 0;
1464 1811
1465 setState(stateConnectorStart); 1812 setState(stateIdle);
1466
1467 break; 1813 break;
1468 1814
1469 case stateConnectorStart: 1815 //MARK: stateIdle
1470 if(!mVoiceEnabled) 1816 case stateIdle:
1471 { 1817 // This is the idle state where we're connected to the daemon but haven't set up a connector yet.
1472 // We were never logged in. This will shut down the connector. 1818 if(mTuningMode)
1473 setState(stateLoggedOut);
1474 }
1475 else if(!mAccountServerURI.empty())
1476 {
1477 connectorCreate();
1478 }
1479 else if(mTuningMode)
1480 { 1819 {
1481 mTuningExitState = stateConnectorStart; 1820 mTuningExitState = stateIdle;
1482 setState(stateMicTuningStart); 1821 setState(stateMicTuningStart);
1483 } 1822 }
1484 break; 1823 else if(!mVoiceEnabled)
1485
1486 case stateConnectorStarting: // waiting for connector handle
1487 // connectorCreateResponse() will transition from here to stateConnectorStarted.
1488 break;
1489
1490 case stateConnectorStarted: // connector handle received
1491 if(!mVoiceEnabled)
1492 { 1824 {
1493 // We were never logged in. This will shut down the connector. 1825 // We never started up the connector. This will shut down the daemon.
1494 setState(stateLoggedOut); 1826 setState(stateConnectorStopped);
1495 } 1827 }
1496 else if(!mAccountName.empty()) 1828 else if(!mAccountName.empty())
1497 { 1829 {
@@ -1505,12 +1837,13 @@ void LLVoiceClient::stateMachine()
1505 { 1837 {
1506 requestVoiceAccountProvision(); 1838 requestVoiceAccountProvision();
1507 } 1839 }
1508 setState(stateNeedsLogin); 1840 setState(stateConnectorStart);
1509 } 1841 }
1510 } 1842 }
1511 } 1843 }
1512 break; 1844 break;
1513 1845
1846 //MARK: stateMicTuningStart
1514 case stateMicTuningStart: 1847 case stateMicTuningStart:
1515 if(mUpdateTimer.hasExpired()) 1848 if(mUpdateTimer.hasExpired())
1516 { 1849 {
@@ -1518,19 +1851,9 @@ void LLVoiceClient::stateMachine()
1518 { 1851 {
1519 // These can't be changed while in tuning mode. Set them before starting. 1852 // These can't be changed while in tuning mode. Set them before starting.
1520 std::ostringstream stream; 1853 std::ostringstream stream;
1521 1854
1522 if(mCaptureDeviceDirty) 1855 buildSetCaptureDevice(stream);
1523 { 1856 buildSetRenderDevice(stream);
1524 buildSetCaptureDevice(stream);
1525 }
1526
1527 if(mRenderDeviceDirty)
1528 {
1529 buildSetRenderDevice(stream);
1530 }
1531
1532 mCaptureDeviceDirty = false;
1533 mRenderDeviceDirty = false;
1534 1857
1535 if(!stream.str().empty()) 1858 if(!stream.str().empty())
1536 { 1859 {
@@ -1552,8 +1875,9 @@ void LLVoiceClient::stateMachine()
1552 1875
1553 break; 1876 break;
1554 1877
1878 //MARK: stateMicTuningRunning
1555 case stateMicTuningRunning: 1879 case stateMicTuningRunning:
1556 if(!mTuningMode || !mVoiceEnabled || mSessionTerminateRequested || mCaptureDeviceDirty || mRenderDeviceDirty) 1880 if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
1557 { 1881 {
1558 // All of these conditions make us leave tuning mode. 1882 // All of these conditions make us leave tuning mode.
1559 setState(stateMicTuningStop); 1883 setState(stateMicTuningStop);
@@ -1593,6 +1917,7 @@ void LLVoiceClient::stateMachine()
1593 } 1917 }
1594 break; 1918 break;
1595 1919
1920 //MARK: stateMicTuningStop
1596 case stateMicTuningStop: 1921 case stateMicTuningStop:
1597 { 1922 {
1598 // transition out of mic tuning 1923 // transition out of mic tuning
@@ -1606,7 +1931,40 @@ void LLVoiceClient::stateMachine()
1606 1931
1607 } 1932 }
1608 break; 1933 break;
1609 1934
1935 //MARK: stateConnectorStart
1936 case stateConnectorStart:
1937 if(!mVoiceEnabled)
1938 {
1939 // We were never logged in. This will shut down the connector.
1940 setState(stateLoggedOut);
1941 }
1942 else if(!mVoiceAccountServerURI.empty())
1943 {
1944 connectorCreate();
1945 }
1946 break;
1947
1948 //MARK: stateConnectorStarting
1949 case stateConnectorStarting: // waiting for connector handle
1950 // connectorCreateResponse() will transition from here to stateConnectorStarted.
1951 break;
1952
1953 //MARK: stateConnectorStarted
1954 case stateConnectorStarted: // connector handle received
1955 if(!mVoiceEnabled)
1956 {
1957 // We were never logged in. This will shut down the connector.
1958 setState(stateLoggedOut);
1959 }
1960 else
1961 {
1962 // The connector is started. Send a login message.
1963 setState(stateNeedsLogin);
1964 }
1965 break;
1966
1967 //MARK: stateLoginRetry
1610 case stateLoginRetry: 1968 case stateLoginRetry:
1611 if(mLoginRetryCount == 0) 1969 if(mLoginRetryCount == 0)
1612 { 1970 {
@@ -1630,6 +1988,7 @@ void LLVoiceClient::stateMachine()
1630 } 1988 }
1631 break; 1989 break;
1632 1990
1991 //MARK: stateLoginRetryWait
1633 case stateLoginRetryWait: 1992 case stateLoginRetryWait:
1634 if(mUpdateTimer.hasExpired()) 1993 if(mUpdateTimer.hasExpired())
1635 { 1994 {
@@ -1637,6 +1996,7 @@ void LLVoiceClient::stateMachine()
1637 } 1996 }
1638 break; 1997 break;
1639 1998
1999 //MARK: stateNeedsLogin
1640 case stateNeedsLogin: 2000 case stateNeedsLogin:
1641 if(!mAccountPassword.empty()) 2001 if(!mAccountPassword.empty())
1642 { 2002 {
@@ -1645,16 +2005,22 @@ void LLVoiceClient::stateMachine()
1645 } 2005 }
1646 break; 2006 break;
1647 2007
2008 //MARK: stateLoggingIn
1648 case stateLoggingIn: // waiting for account handle 2009 case stateLoggingIn: // waiting for account handle
1649 // loginResponse() will transition from here to stateLoggedIn. 2010 // loginResponse() will transition from here to stateLoggedIn.
1650 break; 2011 break;
1651 2012
2013 //MARK: stateLoggedIn
1652 case stateLoggedIn: // account handle received 2014 case stateLoggedIn: // account handle received
1653 // Initial kick-off of channel lookup logic
1654 parcelChanged();
1655 2015
1656 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); 2016 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
1657 2017
2018 // request the current set of block rules (we'll need them when updating the friends list)
2019 accountListBlockRulesSendMessage();
2020
2021 // request the current set of auto-accept rules
2022 accountListAutoAcceptRulesSendMessage();
2023
1658 // Set up the mute list observer if it hasn't been set up already. 2024 // Set up the mute list observer if it hasn't been set up already.
1659 if((!sMuteListListener_listening)) 2025 if((!sMuteListListener_listening))
1660 { 2026 {
@@ -1662,13 +2028,67 @@ void LLVoiceClient::stateMachine()
1662 sMuteListListener_listening = true; 2028 sMuteListListener_listening = true;
1663 } 2029 }
1664 2030
2031 // Set up the friends list observer if it hasn't been set up already.
2032 if(friendslist_listener == NULL)
2033 {
2034 friendslist_listener = new LLVoiceClientFriendsObserver;
2035 LLAvatarTracker::instance().addObserver(friendslist_listener);
2036 }
2037
2038 // Set the initial state of mic mute, local speaker volume, etc.
2039 {
2040 std::ostringstream stream;
2041
2042 buildLocalAudioUpdates(stream);
2043
2044 if(!stream.str().empty())
2045 {
2046 writeString(stream.str());
2047 }
2048 }
2049
2050#if USE_SESSION_GROUPS
2051 // create the main session group
2052 sessionGroupCreateSendMessage();
2053
2054 setState(stateCreatingSessionGroup);
2055#else
2056 // Not using session groups -- skip the stateCreatingSessionGroup state.
1665 setState(stateNoChannel); 2057 setState(stateNoChannel);
2058
2059 // Initial kick-off of channel lookup logic
2060 parcelChanged();
2061#endif
2062 break;
2063
2064 //MARK: stateCreatingSessionGroup
2065 case stateCreatingSessionGroup:
2066 if(mSessionTerminateRequested || !mVoiceEnabled)
2067 {
2068 // TODO: Question: is this the right way out of this state
2069 setState(stateSessionTerminated);
2070 }
2071 else if(!mMainSessionGroupHandle.empty())
2072 {
2073 setState(stateNoChannel);
2074
2075 // Start looped recording (needed for "panic button" anti-griefing tool)
2076 recordingLoopStart();
2077
2078 // Initial kick-off of channel lookup logic
2079 parcelChanged();
2080 }
1666 break; 2081 break;
1667 2082
2083 //MARK: stateNoChannel
1668 case stateNoChannel: 2084 case stateNoChannel:
2085 // Do this here as well as inside sendPositionalUpdate().
2086 // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync.
2087 sendFriendsListUpdates();
2088
1669 if(mSessionTerminateRequested || !mVoiceEnabled) 2089 if(mSessionTerminateRequested || !mVoiceEnabled)
1670 { 2090 {
1671 // MBW -- XXX -- Is this the right way out of this state? 2091 // TODO: Question: Is this the right way out of this state?
1672 setState(stateSessionTerminated); 2092 setState(stateSessionTerminated);
1673 } 2093 }
1674 else if(mTuningMode) 2094 else if(mTuningMode)
@@ -1676,30 +2096,49 @@ void LLVoiceClient::stateMachine()
1676 mTuningExitState = stateNoChannel; 2096 mTuningExitState = stateNoChannel;
1677 setState(stateMicTuningStart); 2097 setState(stateMicTuningStart);
1678 } 2098 }
1679 else if(!mNextSessionHandle.empty()) 2099 else if(sessionNeedsRelog(mNextAudioSession))
1680 { 2100 {
1681 setState(stateSessionConnect); 2101 requestRelog();
2102 setState(stateSessionTerminated);
2103 }
2104 else if(mNextAudioSession)
2105 {
2106 sessionState *oldSession = mAudioSession;
2107
2108 mAudioSession = mNextAudioSession;
2109 if(!mAudioSession->mReconnect)
2110 {
2111 mNextAudioSession = NULL;
2112 }
2113
2114 // The old session may now need to be deleted.
2115 reapSession(oldSession);
2116
2117 if(!mAudioSession->mHandle.empty())
2118 {
2119 // Connect to a session by session handle
2120
2121 sessionMediaConnectSendMessage(mAudioSession);
2122 }
2123 else
2124 {
2125 // Connect to a session by URI
2126 sessionCreateSendMessage(mAudioSession, true, false);
2127 }
2128
2129 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
2130 setState(stateJoiningSession);
1682 } 2131 }
1683 else if(!mNextSessionURI.empty()) 2132 else if(!mSpatialSessionURI.empty())
1684 { 2133 {
1685 setState(stateSessionCreate); 2134 // If we're not headed elsewhere and have a spatial URI, return to spatial.
2135 switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
1686 } 2136 }
1687 break; 2137 break;
1688 2138
1689 case stateSessionCreate: 2139 //MARK: stateJoiningSession
1690 sessionCreateSendMessage();
1691 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
1692 setState(stateJoiningSession);
1693 break;
1694
1695 case stateSessionConnect:
1696 sessionConnectSendMessage();
1697 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
1698 setState(stateJoiningSession);
1699 break;
1700
1701 case stateJoiningSession: // waiting for session handle 2140 case stateJoiningSession: // waiting for session handle
1702 // sessionCreateResponse() will transition from here to stateSessionJoined. 2141 // joinedAudioSession() will transition from here to stateSessionJoined.
1703 if(!mVoiceEnabled) 2142 if(!mVoiceEnabled)
1704 { 2143 {
1705 // User bailed out during connect -- jump straight to teardown. 2144 // User bailed out during connect -- jump straight to teardown.
@@ -1707,30 +2146,27 @@ void LLVoiceClient::stateMachine()
1707 } 2146 }
1708 else if(mSessionTerminateRequested) 2147 else if(mSessionTerminateRequested)
1709 { 2148 {
1710 if(!mSessionHandle.empty()) 2149 if(mAudioSession && !mAudioSession->mHandle.empty())
1711 { 2150 {
1712 // Only allow direct exits from this state in p2p calls (for cancelling an invite). 2151 // Only allow direct exits from this state in p2p calls (for cancelling an invite).
1713 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. 2152 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
1714 if(mSessionP2P) 2153 if(mAudioSession->mIsP2P)
1715 { 2154 {
1716 sessionTerminateSendMessage(); 2155 sessionMediaDisconnectSendMessage(mAudioSession);
1717 setState(stateSessionTerminated); 2156 setState(stateSessionTerminated);
1718 } 2157 }
1719 } 2158 }
1720 } 2159 }
1721 break; 2160 break;
1722 2161
2162 //MARK: stateSessionJoined
1723 case stateSessionJoined: // session handle received 2163 case stateSessionJoined: // session handle received
1724 // MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4 2164 // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
1725 // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. 2165 // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
1726 // For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. 2166 // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
1727 // This is a cheap way to make sure both have happened before proceeding. 2167 // This is a cheap way to make sure both have happened before proceeding.
1728 if(!mSessionHandle.empty()) 2168 if(mAudioSession && mAudioSession->mVoiceEnabled)
1729 { 2169 {
1730 // Events that need to happen when a session is joined could go here.
1731 // Maybe send initial spatial data?
1732 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
1733
1734 // Dirty state that may need to be sync'ed with the daemon. 2170 // Dirty state that may need to be sync'ed with the daemon.
1735 mPTTDirty = true; 2171 mPTTDirty = true;
1736 mSpeakerVolumeDirty = true; 2172 mSpeakerVolumeDirty = true;
@@ -1741,6 +2177,11 @@ void LLVoiceClient::stateMachine()
1741 // Start the throttle timer 2177 // Start the throttle timer
1742 mUpdateTimer.start(); 2178 mUpdateTimer.start();
1743 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); 2179 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
2180
2181 // Events that need to happen when a session is joined could go here.
2182 // Maybe send initial spatial data?
2183 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
2184
1744 } 2185 }
1745 else if(!mVoiceEnabled) 2186 else if(!mVoiceEnabled)
1746 { 2187 {
@@ -1751,21 +2192,20 @@ void LLVoiceClient::stateMachine()
1751 { 2192 {
1752 // Only allow direct exits from this state in p2p calls (for cancelling an invite). 2193 // Only allow direct exits from this state in p2p calls (for cancelling an invite).
1753 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. 2194 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
1754 if(mSessionP2P) 2195 if(mAudioSession && mAudioSession->mIsP2P)
1755 { 2196 {
1756 sessionTerminateSendMessage(); 2197 sessionMediaDisconnectSendMessage(mAudioSession);
1757 setState(stateSessionTerminated); 2198 setState(stateSessionTerminated);
1758 } 2199 }
1759 } 2200 }
1760 break; 2201 break;
1761 2202
2203 //MARK: stateRunning
1762 case stateRunning: // steady state 2204 case stateRunning: // steady state
1763 // sessionTerminateSendMessage() will transition from here to stateLeavingSession
1764
1765 // Disabling voice or disconnect requested. 2205 // Disabling voice or disconnect requested.
1766 if(!mVoiceEnabled || mSessionTerminateRequested) 2206 if(!mVoiceEnabled || mSessionTerminateRequested)
1767 { 2207 {
1768 sessionTerminateSendMessage(); 2208 leaveAudioSession();
1769 } 2209 }
1770 else 2210 else
1771 { 2211 {
@@ -1798,7 +2238,7 @@ void LLVoiceClient::stateMachine()
1798 } 2238 }
1799 } 2239 }
1800 2240
1801 if(mNonSpatialChannel) 2241 if(!inSpatialChannel())
1802 { 2242 {
1803 // When in a non-spatial channel, never send positional updates. 2243 // When in a non-spatial channel, never send positional updates.
1804 mSpatialCoordsDirty = false; 2244 mSpatialCoordsDirty = false;
@@ -1811,7 +2251,7 @@ void LLVoiceClient::stateMachine()
1811 2251
1812 // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast) 2252 // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
1813 // or every 10hz, whichever is sooner. 2253 // or every 10hz, whichever is sooner.
1814 if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired()) 2254 if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
1815 { 2255 {
1816 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); 2256 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
1817 sendPositionalUpdate(); 2257 sendPositionalUpdate();
@@ -1819,25 +2259,38 @@ void LLVoiceClient::stateMachine()
1819 } 2259 }
1820 break; 2260 break;
1821 2261
2262 //MARK: stateLeavingSession
1822 case stateLeavingSession: // waiting for terminate session response 2263 case stateLeavingSession: // waiting for terminate session response
1823 // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. 2264 // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
1824 break; 2265 break;
1825 2266
2267 //MARK: stateSessionTerminated
1826 case stateSessionTerminated: 2268 case stateSessionTerminated:
1827 // Always reset the terminate request flag when we get here.
1828 mSessionTerminateRequested = false;
1829 2269
2270 // Must do this first, since it uses mAudioSession.
1830 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); 2271 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
2272
2273 if(mAudioSession)
2274 {
2275 sessionState *oldSession = mAudioSession;
2276
2277 mAudioSession = NULL;
2278 // We just notified status observers about this change. Don't do it again.
2279 mAudioSessionChanged = false;
1831 2280
1832 if(mVoiceEnabled) 2281 // The old session may now need to be deleted.
2282 reapSession(oldSession);
2283 }
2284 else
1833 { 2285 {
1834 // SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now. 2286 LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
1835 // This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter. 2287 }
1836 if(mNextSessionSpatial && mNextSessionURI.empty()) 2288
1837 { 2289 // Always reset the terminate request flag when we get here.
1838 mNonSpatialChannel = !mNextSessionSpatial; 2290 mSessionTerminateRequested = false;
1839 } 2291
1840 2292 if(mVoiceEnabled && !mRelogRequested)
2293 {
1841 // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). 2294 // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
1842 setState(stateNoChannel); 2295 setState(stateNoChannel);
1843 } 2296 }
@@ -1845,49 +2298,67 @@ void LLVoiceClient::stateMachine()
1845 { 2298 {
1846 // Shutting down voice, continue with disconnecting. 2299 // Shutting down voice, continue with disconnecting.
1847 logout(); 2300 logout();
2301
2302 // The state machine will take it from here
2303 mRelogRequested = false;
1848 } 2304 }
1849 2305
1850 break; 2306 break;
1851 2307
2308 //MARK: stateLoggingOut
1852 case stateLoggingOut: // waiting for logout response 2309 case stateLoggingOut: // waiting for logout response
1853 // The handler for the Account.Logout response will transition from here to stateLoggedOut. 2310 // The handler for the Account.Logout response will transition from here to stateLoggedOut.
1854 break; 2311 break;
2312 //MARK: stateLoggedOut
1855 case stateLoggedOut: // logout response received 2313 case stateLoggedOut: // logout response received
1856 // shut down the connector 2314 // shut down the connector
1857 connectorShutdown(); 2315 connectorShutdown();
1858 break; 2316 break;
1859 2317
2318 //MARK: stateConnectorStopping
1860 case stateConnectorStopping: // waiting for connector stop 2319 case stateConnectorStopping: // waiting for connector stop
1861 // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. 2320 // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
1862 break; 2321 break;
1863 2322
2323 //MARK: stateConnectorStopped
1864 case stateConnectorStopped: // connector stop received 2324 case stateConnectorStopped: // connector stop received
1865 // Clean up and reset everything. 2325 setState(stateDisableCleanup);
1866 closeSocket();
1867 removeAllParticipants();
1868 setState(stateDisabled);
1869 break; 2326 break;
1870 2327
2328 //MARK: stateConnectorFailed
1871 case stateConnectorFailed: 2329 case stateConnectorFailed:
1872 setState(stateConnectorFailedWaiting); 2330 setState(stateConnectorFailedWaiting);
1873 break; 2331 break;
2332 //MARK: stateConnectorFailedWaiting
1874 case stateConnectorFailedWaiting: 2333 case stateConnectorFailedWaiting:
1875 break; 2334 break;
1876 2335
2336 //MARK: stateLoginFailed
1877 case stateLoginFailed: 2337 case stateLoginFailed:
1878 setState(stateLoginFailedWaiting); 2338 setState(stateLoginFailedWaiting);
1879 break; 2339 break;
2340 //MARK: stateLoginFailedWaiting
1880 case stateLoginFailedWaiting: 2341 case stateLoginFailedWaiting:
1881 // No way to recover from these. Yet. 2342 // No way to recover from these. Yet.
1882 break; 2343 break;
1883 2344
2345 //MARK: stateJoinSessionFailed
1884 case stateJoinSessionFailed: 2346 case stateJoinSessionFailed:
1885 // Transition to error state. Send out any notifications here. 2347 // Transition to error state. Send out any notifications here.
1886 LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << LL_ENDL; 2348 if(mAudioSession)
2349 {
2350 LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
2351 }
2352 else
2353 {
2354 LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
2355 }
2356
1887 notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); 2357 notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
1888 setState(stateJoinSessionFailedWaiting); 2358 setState(stateJoinSessionFailedWaiting);
1889 break; 2359 break;
1890 2360
2361 //MARK: stateJoinSessionFailedWaiting
1891 case stateJoinSessionFailedWaiting: 2362 case stateJoinSessionFailedWaiting:
1892 // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. 2363 // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
1893 // Region crossings may leave this state and try the join again. 2364 // Region crossings may leave this state and try the join again.
@@ -1897,22 +2368,24 @@ void LLVoiceClient::stateMachine()
1897 } 2368 }
1898 break; 2369 break;
1899 2370
2371 //MARK: stateJail
1900 case stateJail: 2372 case stateJail:
1901 // We have given up. Do nothing. 2373 // We have given up. Do nothing.
1902 break; 2374 break;
1903 2375
1904 case stateMicTuningNoLogin:
1905 // *TODO: Implement me.
1906 LL_WARNS("Voice") << "stateMicTuningNoLogin not handled" << LL_ENDL;
1907 break;
1908 } 2376 }
1909 2377
1910 if(mParticipantMapChanged) 2378 if(mAudioSession && mAudioSession->mParticipantsChanged)
1911 { 2379 {
1912 mParticipantMapChanged = false; 2380 mAudioSession->mParticipantsChanged = false;
1913 notifyObservers(); 2381 mAudioSessionChanged = true;
2382 }
2383
2384 if(mAudioSessionChanged)
2385 {
2386 mAudioSessionChanged = false;
2387 notifyParticipantObservers();
1914 } 2388 }
1915
1916} 2389}
1917 2390
1918void LLVoiceClient::closeSocket(void) 2391void LLVoiceClient::closeSocket(void)
@@ -1930,6 +2403,9 @@ void LLVoiceClient::loginSendMessage()
1930 << "<AccountName>" << mAccountName << "</AccountName>" 2403 << "<AccountName>" << mAccountName << "</AccountName>"
1931 << "<AccountPassword>" << mAccountPassword << "</AccountPassword>" 2404 << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
1932 << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>" 2405 << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
2406 << "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
2407 << "<BuddyManagementMode>Application</BuddyManagementMode>"
2408 << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
1933 << "</Request>\n\n\n"; 2409 << "</Request>\n\n\n";
1934 2410
1935 writeString(stream.str()); 2411 writeString(stream.str());
@@ -1937,7 +2413,10 @@ void LLVoiceClient::loginSendMessage()
1937 2413
1938void LLVoiceClient::logout() 2414void LLVoiceClient::logout()
1939{ 2415{
1940 mAccountPassword = ""; 2416 // Ensure that we'll re-request provisioning before logging in again
2417 mAccountPassword.clear();
2418 mVoiceAccountServerURI.clear();
2419
1941 setState(stateLoggingOut); 2420 setState(stateLoggingOut);
1942 logoutSendMessage(); 2421 logoutSendMessage();
1943} 2422}
@@ -1959,78 +2438,164 @@ void LLVoiceClient::logoutSendMessage()
1959 } 2438 }
1960} 2439}
1961 2440
1962void LLVoiceClient::channelGetListSendMessage() 2441void LLVoiceClient::accountListBlockRulesSendMessage()
1963{ 2442{
1964 std::ostringstream stream; 2443 if(!mAccountHandle.empty())
1965 stream 2444 {
1966 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ChannelGetList.1\">" 2445 std::ostringstream stream;
1967 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
1968 << "</Request>\n\n\n";
1969 2446
1970 writeString(stream.str()); 2447 LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
2448
2449 stream
2450 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">"
2451 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
2452 << "</Request>"
2453 << "\n\n\n";
2454
2455 writeString(stream.str());
2456 }
1971} 2457}
1972 2458
1973void LLVoiceClient::sessionCreateSendMessage() 2459void LLVoiceClient::accountListAutoAcceptRulesSendMessage()
1974{ 2460{
1975 LL_DEBUGS("Voice") << "requesting join: " << mNextSessionURI << LL_ENDL; 2461 if(!mAccountHandle.empty())
2462 {
2463 std::ostringstream stream;
1976 2464
1977 mSessionURI = mNextSessionURI; 2465 LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
1978 mNonSpatialChannel = !mNextSessionSpatial; 2466
1979 mSessionResetOnClose = mNextSessionResetOnClose; 2467 stream
1980 mNextSessionResetOnClose = false; 2468 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">"
1981 if(mNextSessionNoReconnect) 2469 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
2470 << "</Request>"
2471 << "\n\n\n";
2472
2473 writeString(stream.str());
2474 }
2475}
2476
2477void LLVoiceClient::sessionGroupCreateSendMessage()
2478{
2479 if(!mAccountHandle.empty())
2480 {
2481 std::ostringstream stream;
2482
2483 LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
2484
2485 stream
2486 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">"
2487 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
2488 << "<Type>Normal</Type>"
2489 << "</Request>"
2490 << "\n\n\n";
2491
2492 writeString(stream.str());
2493 }
2494}
2495
2496void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
2497{
2498 LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
2499
2500 session->mCreateInProgress = true;
2501 if(startAudio)
1982 { 2502 {
1983 // Clear the stashed URI so it can't reconnect 2503 session->mMediaConnectInProgress = true;
1984 mNextSessionURI.clear();
1985 } 2504 }
1986 // Only p2p sessions are created with "no reconnect".
1987 mSessionP2P = mNextSessionNoReconnect;
1988 2505
1989 std::ostringstream stream; 2506 std::ostringstream stream;
1990 stream 2507 stream
1991 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Create.1\">" 2508 << "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">"
1992 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" 2509 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
1993 << "<URI>" << mSessionURI << "</URI>"; 2510 << "<URI>" << session->mSIPURI << "</URI>";
1994 2511
1995 static const std::string allowed_chars = 2512 static const std::string allowed_chars =
1996 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 2513 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
1997 "0123456789" 2514 "0123456789"
1998 "-._~"; 2515 "-._~";
1999 2516
2000 if(!mNextSessionHash.empty()) 2517 if(!session->mHash.empty())
2001 { 2518 {
2002 stream 2519 stream
2003 << "<Password>" << LLURI::escape(mNextSessionHash, allowed_chars) << "</Password>" 2520 << "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
2004 << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"; 2521 << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
2005 } 2522 }
2006 2523
2007 stream 2524 stream
2525 << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
2526 << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
2008 << "<Name>" << mChannelName << "</Name>" 2527 << "<Name>" << mChannelName << "</Name>"
2009 << "</Request>\n\n\n"; 2528 << "</Request>\n\n\n";
2010 writeString(stream.str()); 2529 writeString(stream.str());
2011} 2530}
2012 2531
2013void LLVoiceClient::sessionConnectSendMessage() 2532void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
2014{ 2533{
2015 LL_DEBUGS("Voice") << "connecting to session handle: " << mNextSessionHandle << LL_ENDL; 2534 LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
2535
2536 session->mCreateInProgress = true;
2537 if(startAudio)
2538 {
2539 session->mMediaConnectInProgress = true;
2540 }
2541
2542 std::string password;
2543 if(!session->mHash.empty())
2544 {
2545 static const std::string allowed_chars =
2546 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
2547 "0123456789"
2548 "-._~"
2549 ;
2550 password = LLURI::escape(session->mHash, allowed_chars);
2551 }
2552
2553 std::ostringstream stream;
2554 stream
2555 << "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">"
2556 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2557 << "<URI>" << session->mSIPURI << "</URI>"
2558 << "<Name>" << mChannelName << "</Name>"
2559 << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
2560 << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
2561 << "<Password>" << password << "</Password>"
2562 << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
2563 << "</Request>\n\n\n"
2564 ;
2016 2565
2017 mSessionHandle = mNextSessionHandle; 2566 writeString(stream.str());
2018 mSessionURI = mNextP2PSessionURI; 2567}
2019 mNextSessionHandle.clear(); // never want to re-use these. 2568
2020 mNextP2PSessionURI.clear(); 2569void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
2021 mNonSpatialChannel = !mNextSessionSpatial; 2570{
2022 mSessionResetOnClose = mNextSessionResetOnClose; 2571 LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL;
2023 mNextSessionResetOnClose = false; 2572
2024 // Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session. 2573 session->mMediaConnectInProgress = true;
2025 mSessionP2P = true;
2026 2574
2027 std::ostringstream stream; 2575 std::ostringstream stream;
2576
2577 stream
2578 << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">"
2579 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2580 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2581 << "<Media>Audio</Media>"
2582 << "</Request>\n\n\n";
2583
2584 writeString(stream.str());
2585}
2586
2587void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session)
2588{
2589 LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
2028 2590
2591 std::ostringstream stream;
2592
2029 stream 2593 stream
2030 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Connect.1\">" 2594 << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">"
2031 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>" 2595 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2032 << "<AudioMedia>default</AudioMedia>" 2596 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2033 << "</Request>\n\n\n"; 2597 << "</Request>\n\n\n";
2598
2034 writeString(stream.str()); 2599 writeString(stream.str());
2035} 2600}
2036 2601
@@ -2039,52 +2604,112 @@ void LLVoiceClient::sessionTerminate()
2039 mSessionTerminateRequested = true; 2604 mSessionTerminateRequested = true;
2040} 2605}
2041 2606
2042void LLVoiceClient::sessionTerminateSendMessage() 2607void LLVoiceClient::requestRelog()
2043{ 2608{
2044 LL_DEBUGS("Voice") << "leaving session: " << mSessionURI << LL_ENDL; 2609 mSessionTerminateRequested = true;
2610 mRelogRequested = true;
2611}
2045 2612
2046 switch(getState()) 2613
2614void LLVoiceClient::leaveAudioSession()
2615{
2616 if(mAudioSession)
2047 { 2617 {
2048 case stateNoChannel: 2618 LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
2049 // In this case, we want to pretend the join failed so our state machine doesn't get stuck. 2619
2050 // Skip the join failed transition state so we don't send out error notifications. 2620 switch(getState())
2051 setState(stateJoinSessionFailedWaiting); 2621 {
2052 break; 2622 case stateNoChannel:
2053 case stateJoiningSession: 2623 // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
2054 case stateSessionJoined: 2624 // Skip the join failed transition state so we don't send out error notifications.
2055 case stateRunning: 2625 setState(stateJoinSessionFailedWaiting);
2056 if(!mSessionHandle.empty()) 2626 break;
2057 { 2627 case stateJoiningSession:
2058 sessionTerminateByHandle(mSessionHandle); 2628 case stateSessionJoined:
2059 setState(stateLeavingSession); 2629 case stateRunning:
2060 } 2630 if(!mAudioSession->mHandle.empty())
2061 else 2631 {
2062 { 2632
2063 LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; 2633#if RECORD_EVERYTHING
2634 // HACK: for testing only
2635 // Save looped recording
2636 std::string savepath("/tmp/vivoxrecording");
2637 {
2638 time_t now = time(NULL);
2639 const size_t BUF_SIZE = 64;
2640 char time_str[BUF_SIZE]; /* Flawfinder: ignore */
2641
2642 strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
2643 savepath += time_str;
2644 }
2645 recordingLoopSave(savepath);
2646#endif
2647
2648 sessionMediaDisconnectSendMessage(mAudioSession);
2649 setState(stateLeavingSession);
2650 }
2651 else
2652 {
2653 LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
2654 setState(stateSessionTerminated);
2655 }
2656 break;
2657 case stateJoinSessionFailed:
2658 case stateJoinSessionFailedWaiting:
2064 setState(stateSessionTerminated); 2659 setState(stateSessionTerminated);
2065 } 2660 break;
2066 break; 2661
2067 case stateJoinSessionFailed: 2662 default:
2068 case stateJoinSessionFailedWaiting: 2663 LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
2069 setState(stateSessionTerminated); 2664 break;
2070 break; 2665 }
2071 2666 }
2072 default: 2667 else
2073 LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; 2668 {
2074 break; 2669 LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
2670 setState(stateSessionTerminated);
2075 } 2671 }
2076} 2672}
2077 2673
2078void LLVoiceClient::sessionTerminateByHandle(std::string &sessionHandle) 2674void LLVoiceClient::sessionTerminateSendMessage(sessionState *session)
2079{ 2675{
2080 LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << sessionHandle << LL_ENDL;
2081
2082 std::ostringstream stream; 2676 std::ostringstream stream;
2677
2678 LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
2083 stream 2679 stream
2084 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">" 2680 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
2085 << "<SessionHandle>" << sessionHandle << "</SessionHandle>" 2681 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2086 << "</Request>" 2682 << "</Request>\n\n\n";
2087 << "\n\n\n"; 2683
2684 writeString(stream.str());
2685}
2686
2687void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
2688{
2689 std::ostringstream stream;
2690
2691 LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
2692 stream
2693 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
2694 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2695 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2696 << "<Media>Audio</Media>"
2697 << "</Request>\n\n\n";
2698
2699 writeString(stream.str());
2700
2701}
2702
2703void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
2704{
2705 std::ostringstream stream;
2706
2707 LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;
2708 stream
2709 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
2710 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2711 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2712 << "</Request>\n\n\n";
2088 2713
2089 writeString(stream.str()); 2714 writeString(stream.str());
2090} 2715}
@@ -2111,14 +2736,12 @@ void LLVoiceClient::getRenderDevicesSendMessage()
2111 2736
2112void LLVoiceClient::clearCaptureDevices() 2737void LLVoiceClient::clearCaptureDevices()
2113{ 2738{
2114 // MBW -- XXX -- do something here
2115 LL_DEBUGS("Voice") << "called" << LL_ENDL; 2739 LL_DEBUGS("Voice") << "called" << LL_ENDL;
2116 mCaptureDevices.clear(); 2740 mCaptureDevices.clear();
2117} 2741}
2118 2742
2119void LLVoiceClient::addCaptureDevice(const std::string& name) 2743void LLVoiceClient::addCaptureDevice(const std::string& name)
2120{ 2744{
2121 // MBW -- XXX -- do something here
2122 LL_DEBUGS("Voice") << name << LL_ENDL; 2745 LL_DEBUGS("Voice") << name << LL_ENDL;
2123 2746
2124 mCaptureDevices.push_back(name); 2747 mCaptureDevices.push_back(name);
@@ -2150,15 +2773,13 @@ void LLVoiceClient::setCaptureDevice(const std::string& name)
2150} 2773}
2151 2774
2152void LLVoiceClient::clearRenderDevices() 2775void LLVoiceClient::clearRenderDevices()
2153{ 2776{
2154 // MBW -- XXX -- do something here
2155 LL_DEBUGS("Voice") << "called" << LL_ENDL; 2777 LL_DEBUGS("Voice") << "called" << LL_ENDL;
2156 mRenderDevices.clear(); 2778 mRenderDevices.clear();
2157} 2779}
2158 2780
2159void LLVoiceClient::addRenderDevice(const std::string& name) 2781void LLVoiceClient::addRenderDevice(const std::string& name)
2160{ 2782{
2161 // MBW -- XXX -- do something here
2162 LL_DEBUGS("Voice") << name << LL_ENDL; 2783 LL_DEBUGS("Voice") << name << LL_ENDL;
2163 mRenderDevices.push_back(name); 2784 mRenderDevices.push_back(name);
2164} 2785}
@@ -2270,29 +2891,22 @@ void LLVoiceClient::tuningCaptureStopSendMessage()
2270 2891
2271void LLVoiceClient::tuningSetMicVolume(float volume) 2892void LLVoiceClient::tuningSetMicVolume(float volume)
2272{ 2893{
2273 int scaledVolume = ((int)(volume * 100.0f)) - 100; 2894 int scaled_volume = scale_mic_volume(volume);
2274 if(scaledVolume != mTuningMicVolume) 2895
2896 if(scaled_volume != mTuningMicVolume)
2275 { 2897 {
2276 mTuningMicVolume = scaledVolume; 2898 mTuningMicVolume = scaled_volume;
2277 mTuningMicVolumeDirty = true; 2899 mTuningMicVolumeDirty = true;
2278 } 2900 }
2279} 2901}
2280 2902
2281void LLVoiceClient::tuningSetSpeakerVolume(float volume) 2903void LLVoiceClient::tuningSetSpeakerVolume(float volume)
2282{ 2904{
2283 // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. 2905 int scaled_volume = scale_speaker_volume(volume);
2284 // Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
2285
2286 volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
2287 int scaledVolume = 24; // offset scaledVolume by its default level
2288 if(volume < 0.0f)
2289 scaledVolume += ((int)(volume * 248.0f)); // (24 - (-100)) * 2
2290 else
2291 scaledVolume += ((int)(volume * 52.0f)); // (50 - 24) * 2
2292 2906
2293 if(scaledVolume != mTuningSpeakerVolume) 2907 if(scaled_volume != mTuningSpeakerVolume)
2294 { 2908 {
2295 mTuningSpeakerVolume = scaledVolume; 2909 mTuningSpeakerVolume = scaled_volume;
2296 mTuningSpeakerVolumeDirty = true; 2910 mTuningSpeakerVolumeDirty = true;
2297 } 2911 }
2298} 2912}
@@ -2331,67 +2945,193 @@ void LLVoiceClient::daemonDied()
2331 // The daemon died, so the connection is gone. Reset everything and start over. 2945 // The daemon died, so the connection is gone. Reset everything and start over.
2332 LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; 2946 LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL;
2333 2947
2334 closeSocket();
2335 removeAllParticipants();
2336
2337 // Try to relaunch the daemon 2948 // Try to relaunch the daemon
2338 setState(stateDisabled); 2949 setState(stateDisableCleanup);
2339} 2950}
2340 2951
2341void LLVoiceClient::giveUp() 2952void LLVoiceClient::giveUp()
2342{ 2953{
2343 // All has failed. Clean up and stop trying. 2954 // All has failed. Clean up and stop trying.
2344 closeSocket(); 2955 closeSocket();
2345 removeAllParticipants(); 2956 deleteAllSessions();
2957 deleteAllBuddies();
2346 2958
2347 setState(stateJail); 2959 setState(stateJail);
2348} 2960}
2349 2961
2962static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
2963{
2964 F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the new position and velocity
2965 F64 npos[3];
2966
2967 // The original XML command was sent like this:
2968 /*
2969 << "<Position>"
2970 << "<X>" << pos[VX] << "</X>"
2971 << "<Y>" << pos[VZ] << "</Y>"
2972 << "<Z>" << pos[VY] << "</Z>"
2973 << "</Position>"
2974 << "<Velocity>"
2975 << "<X>" << mAvatarVelocity[VX] << "</X>"
2976 << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
2977 << "<Z>" << mAvatarVelocity[VY] << "</Z>"
2978 << "</Velocity>"
2979 << "<AtOrientation>"
2980 << "<X>" << l.mV[VX] << "</X>"
2981 << "<Y>" << u.mV[VX] << "</Y>"
2982 << "<Z>" << a.mV[VX] << "</Z>"
2983 << "</AtOrientation>"
2984 << "<UpOrientation>"
2985 << "<X>" << l.mV[VZ] << "</X>"
2986 << "<Y>" << u.mV[VY] << "</Y>"
2987 << "<Z>" << a.mV[VZ] << "</Z>"
2988 << "</UpOrientation>"
2989 << "<LeftOrientation>"
2990 << "<X>" << l.mV [VY] << "</X>"
2991 << "<Y>" << u.mV [VZ] << "</Y>"
2992 << "<Z>" << a.mV [VY] << "</Z>"
2993 << "</LeftOrientation>";
2994 */
2995
2996#if 1
2997 // This was the original transform done when building the XML command
2998 nat[0] = left.mV[VX];
2999 nat[1] = up.mV[VX];
3000 nat[2] = at.mV[VX];
3001
3002 nup[0] = left.mV[VZ];
3003 nup[1] = up.mV[VY];
3004 nup[2] = at.mV[VZ];
3005
3006 nl[0] = left.mV[VY];
3007 nl[1] = up.mV[VZ];
3008 nl[2] = at.mV[VY];
3009
3010 npos[0] = pos.mdV[VX];
3011 npos[1] = pos.mdV[VZ];
3012 npos[2] = pos.mdV[VY];
3013
3014 nvel[0] = vel.mV[VX];
3015 nvel[1] = vel.mV[VZ];
3016 nvel[2] = vel.mV[VY];
3017
3018 for(int i=0;i<3;++i) {
3019 at.mV[i] = nat[i];
3020 up.mV[i] = nup[i];
3021 left.mV[i] = nl[i];
3022 pos.mdV[i] = npos[i];
3023 }
3024
3025 // This was the original transform done in the SDK
3026 nat[0] = at.mV[2];
3027 nat[1] = 0; // y component of at vector is always 0, this was up[2]
3028 nat[2] = -1 * left.mV[2];
3029
3030 // We override whatever the application gives us
3031 nup[0] = 0; // x component of up vector is always 0
3032 nup[1] = 1; // y component of up vector is always 1
3033 nup[2] = 0; // z component of up vector is always 0
3034
3035 nl[0] = at.mV[0];
3036 nl[1] = 0; // y component of left vector is always zero, this was up[0]
3037 nl[2] = -1 * left.mV[0];
3038
3039 npos[2] = pos.mdV[2] * -1.0;
3040 npos[1] = pos.mdV[1];
3041 npos[0] = pos.mdV[0];
3042
3043 for(int i=0;i<3;++i) {
3044 at.mV[i] = nat[i];
3045 up.mV[i] = nup[i];
3046 left.mV[i] = nl[i];
3047 pos.mdV[i] = npos[i];
3048 }
3049#else
3050 // This is the compose of the two transforms (at least, that's what I'm trying for)
3051 nat[0] = at.mV[VX];
3052 nat[1] = 0; // y component of at vector is always 0, this was up[2]
3053 nat[2] = -1 * up.mV[VZ];
3054
3055 // We override whatever the application gives us
3056 nup[0] = 0; // x component of up vector is always 0
3057 nup[1] = 1; // y component of up vector is always 1
3058 nup[2] = 0; // z component of up vector is always 0
3059
3060 nl[0] = left.mV[VX];
3061 nl[1] = 0; // y component of left vector is always zero, this was up[0]
3062 nl[2] = -1 * left.mV[VY];
3063
3064 npos[0] = pos.mdV[VX];
3065 npos[1] = pos.mdV[VZ];
3066 npos[2] = pos.mdV[VY] * -1.0;
3067
3068 nvel[0] = vel.mV[VX];
3069 nvel[1] = vel.mV[VZ];
3070 nvel[2] = vel.mV[VY];
3071
3072 for(int i=0;i<3;++i) {
3073 at.mV[i] = nat[i];
3074 up.mV[i] = nup[i];
3075 left.mV[i] = nl[i];
3076 pos.mdV[i] = npos[i];
3077 }
3078
3079#endif
3080}
3081
2350void LLVoiceClient::sendPositionalUpdate(void) 3082void LLVoiceClient::sendPositionalUpdate(void)
2351{ 3083{
2352 std::ostringstream stream; 3084 std::ostringstream stream;
2353 3085
2354 if(mSpatialCoordsDirty) 3086 if(mSpatialCoordsDirty)
2355 { 3087 {
2356 LLVector3 l, u, a; 3088 LLVector3 l, u, a, vel;
3089 LLVector3d pos;
3090
3091 mSpatialCoordsDirty = false;
2357 3092
2358 // Always send both speaker and listener positions together. 3093 // Always send both speaker and listener positions together.
2359 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">" 3094 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"
2360 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"; 3095 << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
2361 3096
2362 stream << "<SpeakerPosition>"; 3097 stream << "<SpeakerPosition>";
2363 3098
3099// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
2364 l = mAvatarRot.getLeftRow(); 3100 l = mAvatarRot.getLeftRow();
2365 u = mAvatarRot.getUpRow(); 3101 u = mAvatarRot.getUpRow();
2366 a = mAvatarRot.getFwdRow(); 3102 a = mAvatarRot.getFwdRow();
2367 3103 pos = mAvatarPosition;
2368 LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; 3104 vel = mAvatarVelocity;
2369 3105
3106 // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
3107 // The old transform is replicated by this function.
3108 oldSDKTransform(l, u, a, pos, vel);
3109
2370 stream 3110 stream
2371 << "<Position>" 3111 << "<Position>"
2372 << "<X>" << mAvatarPosition[VX] << "</X>" 3112 << "<X>" << pos.mdV[VX] << "</X>"
2373 << "<Y>" << mAvatarPosition[VZ] << "</Y>" 3113 << "<Y>" << pos.mdV[VY] << "</Y>"
2374 << "<Z>" << mAvatarPosition[VY] << "</Z>" 3114 << "<Z>" << pos.mdV[VZ] << "</Z>"
2375 << "</Position>" 3115 << "</Position>"
2376 << "<Velocity>" 3116 << "<Velocity>"
2377 << "<X>" << mAvatarVelocity[VX] << "</X>" 3117 << "<X>" << vel.mV[VX] << "</X>"
2378 << "<Y>" << mAvatarVelocity[VZ] << "</Y>" 3118 << "<Y>" << vel.mV[VY] << "</Y>"
2379 << "<Z>" << mAvatarVelocity[VY] << "</Z>" 3119 << "<Z>" << vel.mV[VZ] << "</Z>"
2380 << "</Velocity>" 3120 << "</Velocity>"
2381 << "<AtOrientation>" 3121 << "<AtOrientation>"
2382 << "<X>" << l.mV[VX] << "</X>" 3122 << "<X>" << a.mV[VX] << "</X>"
2383 << "<Y>" << u.mV[VX] << "</Y>" 3123 << "<Y>" << a.mV[VY] << "</Y>"
2384 << "<Z>" << a.mV[VX] << "</Z>" 3124 << "<Z>" << a.mV[VZ] << "</Z>"
2385 << "</AtOrientation>" 3125 << "</AtOrientation>"
2386 << "<UpOrientation>" 3126 << "<UpOrientation>"
2387 << "<X>" << l.mV[VZ] << "</X>" 3127 << "<X>" << u.mV[VX] << "</X>"
2388 << "<Y>" << u.mV[VY] << "</Y>" 3128 << "<Y>" << u.mV[VY] << "</Y>"
2389 << "<Z>" << a.mV[VZ] << "</Z>" 3129 << "<Z>" << u.mV[VZ] << "</Z>"
2390 << "</UpOrientation>" 3130 << "</UpOrientation>"
2391 << "<LeftOrientation>" 3131 << "<LeftOrientation>"
2392 << "<X>" << l.mV [VY] << "</X>" 3132 << "<X>" << l.mV [VX] << "</X>"
2393 << "<Y>" << u.mV [VZ] << "</Y>" 3133 << "<Y>" << l.mV [VY] << "</Y>"
2394 << "<Z>" << a.mV [VY] << "</Z>" 3134 << "<Z>" << l.mV [VZ] << "</Z>"
2395 << "</LeftOrientation>"; 3135 << "</LeftOrientation>";
2396 3136
2397 stream << "</SpeakerPosition>"; 3137 stream << "</SpeakerPosition>";
@@ -2427,43 +3167,158 @@ void LLVoiceClient::sendPositionalUpdate(void)
2427 l = earRot.getLeftRow(); 3167 l = earRot.getLeftRow();
2428 u = earRot.getUpRow(); 3168 u = earRot.getUpRow();
2429 a = earRot.getFwdRow(); 3169 a = earRot.getFwdRow();
3170 pos = earPosition;
3171 vel = earVelocity;
2430 3172
2431 LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; 3173// LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
2432 3174
3175 oldSDKTransform(l, u, a, pos, vel);
3176
2433 stream 3177 stream
2434 << "<Position>" 3178 << "<Position>"
2435 << "<X>" << earPosition[VX] << "</X>" 3179 << "<X>" << pos.mdV[VX] << "</X>"
2436 << "<Y>" << earPosition[VZ] << "</Y>" 3180 << "<Y>" << pos.mdV[VY] << "</Y>"
2437 << "<Z>" << earPosition[VY] << "</Z>" 3181 << "<Z>" << pos.mdV[VZ] << "</Z>"
2438 << "</Position>" 3182 << "</Position>"
2439 << "<Velocity>" 3183 << "<Velocity>"
2440 << "<X>" << earVelocity[VX] << "</X>" 3184 << "<X>" << vel.mV[VX] << "</X>"
2441 << "<Y>" << earVelocity[VZ] << "</Y>" 3185 << "<Y>" << vel.mV[VY] << "</Y>"
2442 << "<Z>" << earVelocity[VY] << "</Z>" 3186 << "<Z>" << vel.mV[VZ] << "</Z>"
2443 << "</Velocity>" 3187 << "</Velocity>"
2444 << "<AtOrientation>" 3188 << "<AtOrientation>"
2445 << "<X>" << l.mV[VX] << "</X>" 3189 << "<X>" << a.mV[VX] << "</X>"
2446 << "<Y>" << u.mV[VX] << "</Y>" 3190 << "<Y>" << a.mV[VY] << "</Y>"
2447 << "<Z>" << a.mV[VX] << "</Z>" 3191 << "<Z>" << a.mV[VZ] << "</Z>"
2448 << "</AtOrientation>" 3192 << "</AtOrientation>"
2449 << "<UpOrientation>" 3193 << "<UpOrientation>"
2450 << "<X>" << l.mV[VZ] << "</X>" 3194 << "<X>" << u.mV[VX] << "</X>"
2451 << "<Y>" << u.mV[VY] << "</Y>" 3195 << "<Y>" << u.mV[VY] << "</Y>"
2452 << "<Z>" << a.mV[VZ] << "</Z>" 3196 << "<Z>" << u.mV[VZ] << "</Z>"
2453 << "</UpOrientation>" 3197 << "</UpOrientation>"
2454 << "<LeftOrientation>" 3198 << "<LeftOrientation>"
2455 << "<X>" << l.mV [VY] << "</X>" 3199 << "<X>" << l.mV [VX] << "</X>"
2456 << "<Y>" << u.mV [VZ] << "</Y>" 3200 << "<Y>" << l.mV [VY] << "</Y>"
2457 << "<Z>" << a.mV [VY] << "</Z>" 3201 << "<Z>" << l.mV [VZ] << "</Z>"
2458 << "</LeftOrientation>"; 3202 << "</LeftOrientation>";
2459 3203
3204
2460 stream << "</ListenerPosition>"; 3205 stream << "</ListenerPosition>";
2461 3206
2462 stream << "</Request>\n\n\n"; 3207 stream << "</Request>\n\n\n";
2463 } 3208 }
3209
3210 if(mAudioSession && mAudioSession->mVolumeDirty)
3211 {
3212 participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
3213
3214 mAudioSession->mVolumeDirty = false;
3215
3216 for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
3217 {
3218 participantState *p = iter->second;
3219
3220 if(p->mVolumeDirty)
3221 {
3222 // Can't set volume/mute for yourself
3223 if(!p->mIsSelf)
3224 {
3225 int volume = p->mUserVolume;
3226 bool mute = p->mOnMuteList;
3227
3228 // SLIM SDK: scale volume from 0-400 (with 100 as "normal") to 0-100 (with 56 as "normal")
3229 if(volume < 100)
3230 volume = (volume * 56) / 100;
3231 else
3232 volume = (((volume - 100) * (100 - 56)) / 300) + 56;
3233
3234 if(mute)
3235 {
3236 // SetParticipantMuteForMe doesn't work in p2p sessions.
3237 // If we want the user to be muted, set their volume to 0 as well.
3238 // This isn't perfect, but it will at least reduce their volume to a minimum.
3239 volume = 0;
3240 }
3241
3242 if(volume == 0)
3243 mute = true;
3244
3245 LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
3246
3247 // SLIM SDK: Send both volume and mute commands.
3248
3249 // Send a "volume for me" command for the user.
3250 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
3251 << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
3252 << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
3253 << "<Volume>" << volume << "</Volume>"
3254 << "</Request>\n\n\n";
3255
3256 // Send a "mute for me" command for the user
3257 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
3258 << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
3259 << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
3260 << "<Mute>" << (mute?"1":"0") << "</Mute>"
3261 << "</Request>\n\n\n";
3262 }
3263
3264 p->mVolumeDirty = false;
3265 }
3266 }
3267 }
3268
3269 buildLocalAudioUpdates(stream);
3270
3271 if(!stream.str().empty())
3272 {
3273 writeString(stream.str());
3274 }
3275
3276 // Friends list updates can be huge, especially on the first voice login of an account with lots of friends.
3277 // Batching them all together can choke SLVoice, so send them in separate writes.
3278 sendFriendsListUpdates();
3279}
3280
3281void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
3282{
3283 if(mCaptureDeviceDirty)
3284 {
3285 LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
3286
3287 stream
3288 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
3289 << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
3290 << "</Request>"
3291 << "\n\n\n";
3292
3293 mCaptureDeviceDirty = false;
3294 }
3295}
3296
3297void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
3298{
3299 if(mRenderDeviceDirty)
3300 {
3301 LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
3302
3303 stream
3304 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
3305 << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
3306 << "</Request>"
3307 << "\n\n\n";
3308 mRenderDeviceDirty = false;
3309 }
3310}
3311
3312void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
3313{
3314 buildSetCaptureDevice(stream);
3315
3316 buildSetRenderDevice(stream);
2464 3317
2465 if(mPTTDirty) 3318 if(mPTTDirty)
2466 { 3319 {
3320 mPTTDirty = false;
3321
2467 // Send a local mute command. 3322 // Send a local mute command.
2468 // NOTE that the state of "PTT" is the inverse of "local mute". 3323 // NOTE that the state of "PTT" is the inverse of "local mute".
2469 // (i.e. when PTT is true, we send a mute command with "false", and vice versa) 3324 // (i.e. when PTT is true, we send a mute command with "false", and vice versa)
@@ -2474,119 +3329,337 @@ void LLVoiceClient::sendPositionalUpdate(void)
2474 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3329 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2475 << "<Value>" << (mPTT?"false":"true") << "</Value>" 3330 << "<Value>" << (mPTT?"false":"true") << "</Value>"
2476 << "</Request>\n\n\n"; 3331 << "</Request>\n\n\n";
2477
2478 }
2479
2480 if(mVolumeDirty)
2481 {
2482 participantMap::iterator iter = mParticipantMap.begin();
2483 3332
2484 for(; iter != mParticipantMap.end(); iter++)
2485 {
2486 participantState *p = iter->second;
2487
2488 if(p->mVolumeDirty)
2489 {
2490 int volume = p->mOnMuteList?0:p->mUserVolume;
2491
2492 LL_INFOS("Voice") << "Setting volume for avatar " << p->mAvatarID << " to " << volume << LL_ENDL;
2493
2494 // Send a mute/unumte command for the user (actually "volume for me").
2495 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
2496 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
2497 << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
2498 << "<Volume>" << volume << "</Volume>"
2499 << "</Request>\n\n\n";
2500
2501 p->mVolumeDirty = false;
2502 }
2503 }
2504 } 3333 }
2505 3334
2506 if(mSpeakerMuteDirty) 3335 if(mSpeakerMuteDirty)
2507 { 3336 {
2508 const char *muteval = ((mSpeakerVolume == -100)?"true":"false"); 3337 const char *muteval = ((mSpeakerVolume == 0)?"true":"false");
3338
3339 mSpeakerMuteDirty = false;
3340
2509 LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; 3341 LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
2510 3342
2511 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">" 3343 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
2512 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3344 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2513 << "<Value>" << muteval << "</Value>" 3345 << "<Value>" << muteval << "</Value>"
2514 << "</Request>\n\n\n"; 3346 << "</Request>\n\n\n";
3347
2515 } 3348 }
2516 3349
2517 if(mSpeakerVolumeDirty) 3350 if(mSpeakerVolumeDirty)
2518 { 3351 {
3352 mSpeakerVolumeDirty = false;
3353
2519 LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; 3354 LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
2520 3355
2521 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">" 3356 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
2522 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3357 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2523 << "<Value>" << mSpeakerVolume << "</Value>" 3358 << "<Value>" << mSpeakerVolume << "</Value>"
2524 << "</Request>\n\n\n"; 3359 << "</Request>\n\n\n";
3360
2525 } 3361 }
2526 3362
2527 if(mMicVolumeDirty) 3363 if(mMicVolumeDirty)
2528 { 3364 {
3365 mMicVolumeDirty = false;
3366
2529 LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; 3367 LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
2530 3368
2531 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">" 3369 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
2532 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3370 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2533 << "<Value>" << mMicVolume << "</Value>" 3371 << "<Value>" << mMicVolume << "</Value>"
2534 << "</Request>\n\n\n"; 3372 << "</Request>\n\n\n";
2535 } 3373 }
2536 3374
2537 3375
2538 // MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here? 3376}
2539 if(mCaptureDeviceDirty)
2540 {
2541 buildSetCaptureDevice(stream);
2542 }
2543 3377
2544 if(mRenderDeviceDirty) 3378void LLVoiceClient::checkFriend(const LLUUID& id)
3379{
3380 std::string name;
3381 buddyListEntry *buddy = findBuddy(id);
3382
3383 // Make sure we don't add a name before it's been looked up.
3384 if(gCacheName->getFullName(id, name))
2545 { 3385 {
2546 buildSetRenderDevice(stream); 3386
3387 const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
3388 bool canSeeMeOnline = false;
3389 if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
3390 canSeeMeOnline = true;
3391
3392 // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary.
3393
3394 if(buddy)
3395 {
3396 // This buddy is already in both lists.
3397
3398 if(name != buddy->mDisplayName)
3399 {
3400 // The buddy is in the list with the wrong name. Update it with the correct name.
3401 LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL;
3402 buddy->mDisplayName = name;
3403 buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent.
3404 }
3405 }
3406 else
3407 {
3408 // This buddy was not in the vivox list, needs to be added.
3409 buddy = addBuddy(sipURIFromID(id), name);
3410 buddy->mUUID = id;
3411 }
3412
3413 // In all the above cases, the buddy is in the SL friends list (which is how we got here).
3414 buddy->mInSLFriends = true;
3415 buddy->mCanSeeMeOnline = canSeeMeOnline;
3416 buddy->mNameResolved = true;
3417
2547 } 3418 }
2548 3419 else
2549 mSpatialCoordsDirty = false;
2550 mPTTDirty = false;
2551 mVolumeDirty = false;
2552 mSpeakerVolumeDirty = false;
2553 mMicVolumeDirty = false;
2554 mSpeakerMuteDirty = false;
2555 mCaptureDeviceDirty = false;
2556 mRenderDeviceDirty = false;
2557
2558 if(!stream.str().empty())
2559 { 3420 {
2560 writeString(stream.str()); 3421 // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has.
3422 if(buddy)
3423 {
3424 buddy->mNameResolved = false;
3425 }
3426
3427 // Initiate a lookup.
3428 // The "lookup completed" callback will ensure that the friends list is rechecked after it completes.
3429 lookupName(id);
2561 } 3430 }
2562} 3431}
2563 3432
2564void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) 3433void LLVoiceClient::clearAllLists()
2565{ 3434{
2566 LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; 3435 // FOR TESTING ONLY
2567 3436
2568 stream 3437 // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about.
2569 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">" 3438 buddyListMap::iterator buddy_it;
2570 << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>" 3439 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
2571 << "</Request>" 3440 {
2572 << "\n\n\n"; 3441 buddyListEntry *buddy = buddy_it->second;
3442 buddy_it++;
3443
3444 std::ostringstream stream;
3445
3446 if(buddy->mInVivoxBuddies)
3447 {
3448 // delete this entry from the vivox buddy list
3449 buddy->mInVivoxBuddies = false;
3450 LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
3451 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
3452 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3453 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
3454 << "</Request>\n\n\n";
3455 }
3456
3457 if(buddy->mHasBlockListEntry)
3458 {
3459 // Delete the associated block list entry (so the block list doesn't fill up with junk)
3460 buddy->mHasBlockListEntry = false;
3461 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
3462 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3463 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3464 << "</Request>\n\n\n";
3465 }
3466 if(buddy->mHasAutoAcceptListEntry)
3467 {
3468 // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk)
3469 buddy->mHasAutoAcceptListEntry = false;
3470 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
3471 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3472 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3473 << "</Request>\n\n\n";
3474 }
3475
3476 writeString(stream.str());
3477
3478 }
2573} 3479}
2574 3480
2575void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream) 3481void LLVoiceClient::sendFriendsListUpdates()
2576{ 3482{
2577 LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; 3483 if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty)
3484 {
3485 mFriendsListDirty = false;
3486
3487 if(0)
3488 {
3489 // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries.
3490 clearAllLists();
3491 return;
3492 }
3493
3494 LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL;
3495
3496 buddyListMap::iterator buddy_it;
3497 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
3498 {
3499 // reset the temp flags in the local buddy list
3500 buddy_it->second->mInSLFriends = false;
3501 }
3502
3503 // correlate with the friends list
3504 {
3505 LLCollectAllBuddies collect;
3506 LLAvatarTracker::instance().applyFunctor(collect);
3507 LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
3508 LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
3509
3510 for ( ; it != end; ++it)
3511 {
3512 checkFriend(it->second);
3513 }
3514 it = collect.mOffline.begin();
3515 end = collect.mOffline.end();
3516 for ( ; it != end; ++it)
3517 {
3518 checkFriend(it->second);
3519 }
3520 }
3521
3522 LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL;
2578 3523
2579 stream 3524 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
2580 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">" 3525 {
2581 << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>" 3526 buddyListEntry *buddy = buddy_it->second;
2582 << "</Request>" 3527 buddy_it++;
2583 << "\n\n\n"; 3528
3529 // Ignore entries that aren't resolved yet.
3530 if(buddy->mNameResolved)
3531 {
3532 std::ostringstream stream;
3533
3534 if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate))
3535 {
3536 if(mNumberOfAliases > 0)
3537 {
3538 // Add (or update) this entry in the vivox buddy list
3539 buddy->mInVivoxBuddies = true;
3540 buddy->mNeedsNameUpdate = false;
3541 LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
3542 stream
3543 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">"
3544 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3545 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
3546 << "<DisplayName>" << buddy->mDisplayName << "</DisplayName>"
3547 << "<BuddyData></BuddyData>" // Without this, SLVoice doesn't seem to parse the command.
3548 << "<GroupID>0</GroupID>"
3549 << "</Request>\n\n\n";
3550 }
3551 }
3552 else if(!buddy->mInSLFriends)
3553 {
3554 // This entry no longer exists in your SL friends list. Remove all traces of it from the Vivox buddy list.
3555 if(buddy->mInVivoxBuddies)
3556 {
3557 // delete this entry from the vivox buddy list
3558 buddy->mInVivoxBuddies = false;
3559 LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
3560 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
3561 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3562 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
3563 << "</Request>\n\n\n";
3564 }
3565
3566 if(buddy->mHasBlockListEntry)
3567 {
3568 // Delete the associated block list entry, if any
3569 buddy->mHasBlockListEntry = false;
3570 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
3571 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3572 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3573 << "</Request>\n\n\n";
3574 }
3575 if(buddy->mHasAutoAcceptListEntry)
3576 {
3577 // Delete the associated auto-accept list entry, if any
3578 buddy->mHasAutoAcceptListEntry = false;
3579 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
3580 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3581 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3582 << "</Request>\n\n\n";
3583 }
3584 }
3585
3586 if(buddy->mInSLFriends)
3587 {
3588
3589 if(buddy->mCanSeeMeOnline)
3590 {
3591 // Buddy should not be blocked.
3592
3593 // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
3594
3595 // If the buddy has a block list entry, delete it.
3596 if(buddy->mHasBlockListEntry)
3597 {
3598 buddy->mHasBlockListEntry = false;
3599 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
3600 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3601 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3602 << "</Request>\n\n\n";
3603
3604
3605 // If we just deleted a block list entry, add an auto-accept entry.
3606 if(!buddy->mHasAutoAcceptListEntry)
3607 {
3608 buddy->mHasAutoAcceptListEntry = true;
3609 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">"
3610 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3611 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3612 << "<AutoAddAsBuddy>0</AutoAddAsBuddy>"
3613 << "</Request>\n\n\n";
3614 }
3615 }
3616 }
3617 else
3618 {
3619 // Buddy should be blocked.
3620
3621 // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
3622
3623 // If this buddy has an autoaccept entry, delete it
3624 if(buddy->mHasAutoAcceptListEntry)
3625 {
3626 buddy->mHasAutoAcceptListEntry = false;
3627 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
3628 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3629 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3630 << "</Request>\n\n\n";
3631
3632 // If we just deleted an auto-accept entry, add a block list entry.
3633 if(!buddy->mHasBlockListEntry)
3634 {
3635 buddy->mHasBlockListEntry = true;
3636 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">"
3637 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3638 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3639 << "<PresenceOnly>1</PresenceOnly>"
3640 << "</Request>\n\n\n";
3641 }
3642 }
3643 }
3644
3645 if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies)
3646 {
3647 // Delete this entry from the local buddy list. This should NOT invalidate the iterator,
3648 // since it has already been incremented to the next entry.
3649 deleteBuddy(buddy->mURI);
3650 }
3651
3652 }
3653 writeString(stream.str());
3654 }
3655 }
3656 }
2584} 3657}
2585 3658
2586///////////////////////////// 3659/////////////////////////////
2587// Response/Event handlers 3660// Response/Event handlers
2588 3661
2589void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle) 3662void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
2590{ 3663{
2591 if(statusCode != 0) 3664 if(statusCode != 0)
2592 { 3665 {
@@ -2596,6 +3669,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
2596 else 3669 else
2597 { 3670 {
2598 // Connector created, move forward. 3671 // Connector created, move forward.
3672 LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
2599 mConnectorHandle = connectorHandle; 3673 mConnectorHandle = connectorHandle;
2600 if(getState() == stateConnectorStarting) 3674 if(getState() == stateConnectorStarting)
2601 { 3675 {
@@ -2604,7 +3678,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
2604 } 3678 }
2605} 3679}
2606 3680
2607void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle) 3681void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
2608{ 3682{
2609 LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; 3683 LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
2610 3684
@@ -2625,7 +3699,8 @@ void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std
2625 { 3699 {
2626 // Login succeeded, move forward. 3700 // Login succeeded, move forward.
2627 mAccountHandle = accountHandle; 3701 mAccountHandle = accountHandle;
2628 // MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received. 3702 mNumberOfAliases = numberOfAliases;
3703 // This needs to wait until the AccountLoginStateChangeEvent is received.
2629// if(getState() == stateLoggingIn) 3704// if(getState() == stateLoggingIn)
2630// { 3705// {
2631// setState(stateLoggedIn); 3706// setState(stateLoggedIn);
@@ -2633,106 +3708,91 @@ void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std
2633 } 3708 }
2634} 3709}
2635 3710
2636void LLVoiceClient::channelGetListResponse(int statusCode, std::string &statusString) 3711void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
2637{ 3712{
3713 sessionState *session = findSessionBeingCreatedByURI(requestId);
3714
3715 if(session)
3716 {
3717 session->mCreateInProgress = false;
3718 }
3719
2638 if(statusCode != 0) 3720 if(statusCode != 0)
2639 { 3721 {
2640 LL_WARNS("Voice") << "Account.ChannelGetList response failure: " << statusString << LL_ENDL; 3722 LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
2641 switchChannel(); 3723 if(session)
3724 {
3725 session->mErrorStatusCode = statusCode;
3726 session->mErrorStatusString = statusString;
3727 if(session == mAudioSession)
3728 {
3729 setState(stateJoinSessionFailed);
3730 }
3731 else
3732 {
3733 reapSession(session);
3734 }
3735 }
2642 } 3736 }
2643 else 3737 else
2644 { 3738 {
2645 // Got the channel list, try to do a lookup. 3739 LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
2646 std::string uri = findChannelURI(mChannelName); 3740 if(session)
2647 if(uri.empty())
2648 {
2649 // Lookup failed, can't join a channel for this area.
2650 LL_INFOS("Voice") << "failed to map channel name: " << mChannelName << LL_ENDL;
2651 }
2652 else
2653 { 3741 {
2654 // We have a sip URL for this area. 3742 setSessionHandle(session, sessionHandle);
2655 LL_INFOS("Voice") << "mapped channel " << mChannelName << " to URI "<< uri << LL_ENDL;
2656 } 3743 }
2657
2658 // switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
2659 switchChannel(uri);
2660 } 3744 }
2661} 3745}
2662 3746
2663void LLVoiceClient::sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle) 3747void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
2664{ 3748{
3749 sessionState *session = findSessionBeingCreatedByURI(requestId);
3750
3751 if(session)
3752 {
3753 session->mCreateInProgress = false;
3754 }
3755
2665 if(statusCode != 0) 3756 if(statusCode != 0)
2666 { 3757 {
2667 LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; 3758 LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
2668// if(statusCode == 1015) 3759 if(session)
2669// {
2670// if(getState() == stateJoiningSession)
2671// {
2672// // this happened during a real join. Going to sessionTerminated should cause a retry in appropriate cases.
2673// LL_WARNS("Voice") << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< LL_ENDL;
2674// if(!sessionHandle.empty())
2675// {
2676// // This session is bad. Terminate it.
2677// mSessionHandle = sessionHandle;
2678// sessionTerminateByHandle(sessionHandle);
2679// setState(stateLeavingSession);
2680// }
2681// else if(!mSessionStateEventHandle.empty())
2682// {
2683// mSessionHandle = mSessionStateEventHandle;
2684// sessionTerminateByHandle(mSessionStateEventHandle);
2685// setState(stateLeavingSession);
2686// }
2687// else
2688// {
2689// setState(stateSessionTerminated);
2690// }
2691// }
2692// else
2693// {
2694// // We didn't think we were in the middle of a join. Don't change state.
2695// LL_WARNS("Voice") << "Not in stateJoiningSession, ignoring" << LL_ENDL;
2696// }
2697// }
2698// else
2699 { 3760 {
2700 mVivoxErrorStatusCode = statusCode; 3761 session->mErrorStatusCode = statusCode;
2701 mVivoxErrorStatusString = statusString; 3762 session->mErrorStatusString = statusString;
2702 setState(stateJoinSessionFailed); 3763 if(session == mAudioSession)
3764 {
3765 setState(stateJoinSessionFailed);
3766 }
3767 else
3768 {
3769 reapSession(session);
3770 }
2703 } 3771 }
2704 } 3772 }
2705 else 3773 else
2706 { 3774 {
2707 LL_DEBUGS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; 3775 LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL;
2708 if(getState() == stateJoiningSession) 3776 if(session)
2709 {
2710 // This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early...
2711 mSessionHandle = sessionHandle;
2712 }
2713 else
2714 { 3777 {
2715 // We should never get a session.create response in any state except stateJoiningSession. Things are out of sync. Kill this session. 3778 setSessionHandle(session, sessionHandle);
2716 sessionTerminateByHandle(sessionHandle);
2717 } 3779 }
2718 } 3780 }
2719} 3781}
2720 3782
2721void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusString) 3783void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
2722{ 3784{
3785 sessionState *session = findSession(requestId);
2723 if(statusCode != 0) 3786 if(statusCode != 0)
2724 { 3787 {
2725 LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; 3788 LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
2726// if(statusCode == 1015) 3789 if(session)
2727// {
2728// LL_WARNS("Voice") << "terminating existing session" << LL_ENDL;
2729// sessionTerminate();
2730// }
2731// else
2732 { 3790 {
2733 mVivoxErrorStatusCode = statusCode; 3791 session->mMediaConnectInProgress = false;
2734 mVivoxErrorStatusString = statusString; 3792 session->mErrorStatusCode = statusCode;
2735 setState(stateJoinSessionFailed); 3793 session->mErrorStatusString = statusString;
3794 if(session == mAudioSession)
3795 setState(stateJoinSessionFailed);
2736 } 3796 }
2737 } 3797 }
2738 else 3798 else
@@ -2741,27 +3801,12 @@ void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusSt
2741 } 3801 }
2742} 3802}
2743 3803
2744void LLVoiceClient::sessionTerminateResponse(int statusCode, std::string &statusString)
2745{
2746 if(statusCode != 0)
2747 {
2748 LL_WARNS("Voice") << "Session.Terminate response failure: (" << statusCode << "): " << statusString << LL_ENDL;
2749 if(getState() == stateLeavingSession)
2750 {
2751 // This is probably "(404): Server reporting Failure. Not a member of this conference."
2752 // Do this so we don't get stuck.
2753 setState(stateSessionTerminated);
2754 }
2755 }
2756
2757}
2758
2759void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString) 3804void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
2760{ 3805{
2761 if(statusCode != 0) 3806 if(statusCode != 0)
2762 { 3807 {
2763 LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; 3808 LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
2764 // MBW -- XXX -- Should this ever fail? do we care if it does? 3809 // Should this ever fail? do we care if it does?
2765 } 3810 }
2766 3811
2767 if(getState() == stateLoggingOut) 3812 if(getState() == stateLoggingOut)
@@ -2775,7 +3820,7 @@ void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statu
2775 if(statusCode != 0) 3820 if(statusCode != 0)
2776 { 3821 {
2777 LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; 3822 LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
2778 // MBW -- XXX -- Should this ever fail? do we care if it does? 3823 // Should this ever fail? do we care if it does?
2779 } 3824 }
2780 3825
2781 mConnected = false; 3826 mConnected = false;
@@ -2786,109 +3831,277 @@ void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statu
2786 } 3831 }
2787} 3832}
2788 3833
2789void LLVoiceClient::sessionStateChangeEvent( 3834void LLVoiceClient::sessionAddedEvent(
2790 std::string &uriString, 3835 std::string &uriString,
2791 int statusCode, 3836 std::string &alias,
2792 std::string &statusString, 3837 std::string &sessionHandle,
2793 std::string &sessionHandle, 3838 std::string &sessionGroupHandle,
2794 int state,
2795 bool isChannel, 3839 bool isChannel,
2796 std::string &nameString) 3840 bool incoming,
3841 std::string &nameString,
3842 std::string &applicationString)
2797{ 3843{
2798 switch(state) 3844 sessionState *session = NULL;
2799 {
2800 case 4: // I see this when joining the session
2801 LL_INFOS("Voice") << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
2802 3845
2803 // Session create succeeded, move forward. 3846 LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
2804 mSessionStateEventHandle = sessionHandle; 3847
2805 mSessionStateEventURI = uriString; 3848 session = addSession(uriString, sessionHandle);
2806 if(sessionHandle == mSessionHandle) 3849 if(session)
3850 {
3851 session->mGroupHandle = sessionGroupHandle;
3852 session->mIsChannel = isChannel;
3853 session->mIncoming = incoming;
3854 session->mAlias = alias;
3855
3856 // Generate a caller UUID -- don't need to do this for channels
3857 if(!session->mIsChannel)
3858 {
3859 if(IDFromName(session->mSIPURI, session->mCallerID))
2807 { 3860 {
2808 // This is the session we're joining. 3861 // Normal URI(base64-encoded UUID)
2809 if(getState() == stateJoiningSession)
2810 {
2811 setState(stateSessionJoined);
2812 //RN: the uriString being returned by vivox here is actually your account uri, not the channel
2813 // you are attempting to join, so ignore it
2814 //LL_DEBUGS("Voice") << "received URI " << uriString << "(previously " << mSessionURI << ")" << LL_ENDL;
2815 //mSessionURI = uriString;
2816 }
2817 } 3862 }
2818 else if(sessionHandle == mNextSessionHandle) 3863 else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
2819 { 3864 {
2820// LL_DEBUGS("Voice") << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << LL_ENDL; 3865 // Wrong URI, but an alias is available. Stash the incoming URI as an alternate
3866 session->mAlternateSIPURI = session->mSIPURI;
3867
3868 // and generate a proper URI from the ID.
3869 setSessionURI(session, sipURIFromID(session->mCallerID));
2821 } 3870 }
2822 else 3871 else
2823 { 3872 {
2824 LL_WARNS("Voice") << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL; 3873 LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
2825 // MBW -- XXX -- Should we send a Session.Terminate here? 3874 setUUIDFromStringHash(session->mCallerID, session->mSIPURI);
3875 session->mSynthesizedCallerID = true;
3876
3877 // Can't look up the name in this case -- we have to extract it from the URI.
3878 std::string namePortion = nameFromsipURI(session->mSIPURI);
3879 if(namePortion.empty())
3880 {
3881 // Didn't seem to be a SIP URI, just use the whole provided name.
3882 namePortion = nameString;
3883 }
3884
3885 // Some incoming names may be separated with an underscore instead of a space. Fix this.
3886 LLStringUtil::replaceChar(namePortion, '_', ' ');
3887
3888 // Act like we just finished resolving the name (this stores it in all the right places)
3889 avatarNameResolved(session->mCallerID, namePortion);
2826 } 3890 }
2827 3891
2828 break; 3892 LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
2829 case 5: // I see this when leaving the session
2830 LL_INFOS("Voice") << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
2831 3893
2832 // Set the session handle to the empty string. If we get back to stateJoiningSession, we'll want to wait for the new session handle. 3894 if(!session->mSynthesizedCallerID)
2833 if(sessionHandle == mSessionHandle)
2834 { 3895 {
2835 // MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle 3896 // If we got here, we don't have a proper name. Initiate a lookup.
2836 // mSessionURI.clear(); 3897 lookupName(session->mCallerID);
2837 // clear the session handle here just for sanity. 3898 }
2838 mSessionHandle.clear(); 3899 }
2839 if(mSessionResetOnClose) 3900 }
3901}
3902
3903void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
3904{
3905 LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
3906
3907#if USE_SESSION_GROUPS
3908 if(mMainSessionGroupHandle.empty())
3909 {
3910 // This is the first (i.e. "main") session group. Save its handle.
3911 mMainSessionGroupHandle = sessionGroupHandle;
3912 }
3913 else
3914 {
3915 LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL;
3916 }
3917#endif
3918}
3919
3920void LLVoiceClient::joinedAudioSession(sessionState *session)
3921{
3922 if(mAudioSession != session)
3923 {
3924 sessionState *oldSession = mAudioSession;
3925
3926 mAudioSession = session;
3927 mAudioSessionChanged = true;
3928
3929 // The old session may now need to be deleted.
3930 reapSession(oldSession);
3931 }
3932
3933 // This is the session we're joining.
3934 if(getState() == stateJoiningSession)
3935 {
3936 setState(stateSessionJoined);
3937
3938 // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
3939 // Add the current user as a participant here.
3940 participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
3941 if(participant)
3942 {
3943 participant->mIsSelf = true;
3944 lookupName(participant->mAvatarID);
3945
3946 LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName
3947 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
3948 }
3949
3950 if(!session->mIsChannel)
3951 {
3952 // this is a p2p session. Make sure the other end is added as a participant.
3953 participantState *participant = session->addParticipant(session->mSIPURI);
3954 if(participant)
3955 {
3956 if(participant->mAvatarIDValid)
2840 { 3957 {
2841 mSessionResetOnClose = false; 3958 lookupName(participant->mAvatarID);
2842 mNonSpatialChannel = false; 3959 }
2843 mNextSessionSpatial = true; 3960 else if(!session->mName.empty())
2844 parcelChanged(); 3961 {
3962 participant->mDisplayName = session->mName;
3963 avatarNameResolved(participant->mAvatarID, session->mName);
2845 } 3964 }
2846
2847 removeAllParticipants();
2848 3965
2849 switch(getState()) 3966 // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
3967 LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName
3968 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
3969 }
3970 }
3971 }
3972}
3973
3974void LLVoiceClient::sessionRemovedEvent(
3975 std::string &sessionHandle,
3976 std::string &sessionGroupHandle)
3977{
3978 LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
3979
3980 sessionState *session = findSession(sessionHandle);
3981 if(session)
3982 {
3983 leftAudioSession(session);
3984
3985 // This message invalidates the session's handle. Set it to empty.
3986 setSessionHandle(session);
3987
3988 // Reset the media state (we now have no info)
3989 session->mMediaStreamState = streamStateUnknown;
3990 session->mTextStreamState = streamStateUnknown;
3991
3992 // Conditionally delete the session
3993 reapSession(session);
3994 }
3995 else
3996 {
3997 LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
3998 }
3999}
4000
4001void LLVoiceClient::reapSession(sessionState *session)
4002{
4003 if(session)
4004 {
4005 if(!session->mHandle.empty())
4006 {
4007 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
4008 }
4009 else if(session->mCreateInProgress)
4010 {
4011 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
4012 }
4013 else if(session->mMediaConnectInProgress)
4014 {
4015 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL;
4016 }
4017 else if(session == mAudioSession)
4018 {
4019 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL;
4020 }
4021 else if(session == mNextAudioSession)
4022 {
4023 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL;
4024 }
4025 else
4026 {
4027 // TODO: Question: Should we check for queued text messages here?
4028 // We don't have a reason to keep tracking this session, so just delete it.
4029 LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
4030 deleteSession(session);
4031 session = NULL;
4032 }
4033 }
4034 else
4035 {
4036// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL;
4037 }
4038}
4039
4040// Returns true if the session seems to indicate we've moved to a region on a different voice server
4041bool LLVoiceClient::sessionNeedsRelog(sessionState *session)
4042{
4043 bool result = false;
4044
4045 if(session != NULL)
4046 {
4047 // Only make this check for spatial channels (so it won't happen for group or p2p calls)
4048 if(session->mIsSpatial)
4049 {
4050 std::string::size_type atsign;
4051
4052 atsign = session->mSIPURI.find("@");
4053
4054 if(atsign != std::string::npos)
4055 {
4056 std::string urihost = session->mSIPURI.substr(atsign + 1);
4057 if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
2850 { 4058 {
2851 case stateJoiningSession: 4059 // The hostname in this URI is different from what we expect. This probably means we need to relog.
2852 case stateSessionJoined:
2853 case stateRunning:
2854 case stateLeavingSession:
2855 case stateJoinSessionFailed:
2856 case stateJoinSessionFailedWaiting:
2857 // normal transition
2858 LL_INFOS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
2859 setState(stateSessionTerminated);
2860 break;
2861 4060
2862 case stateSessionTerminated: 4061 // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
2863 // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. 4062 // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
2864 LL_WARNS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
2865 break;
2866 4063
2867 default: 4064 result = true;
2868 LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
2869 setState(stateSessionTerminated);
2870 break;
2871 } 4065 }
2872
2873 // store status values for later notification of observers
2874 mVivoxErrorStatusCode = statusCode;
2875 mVivoxErrorStatusString = statusString;
2876 }
2877 else
2878 {
2879 LL_INFOS("Voice") << "leaving unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL;
2880 } 4066 }
4067 }
4068 }
4069
4070 return result;
4071}
2881 4072
2882 mSessionStateEventHandle.clear(); 4073void LLVoiceClient::leftAudioSession(
2883 mSessionStateEventURI.clear(); 4074 sessionState *session)
2884 break; 4075{
2885 default: 4076 if(mAudioSession == session)
2886 LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL; 4077 {
2887 break; 4078 switch(getState())
4079 {
4080 case stateJoiningSession:
4081 case stateSessionJoined:
4082 case stateRunning:
4083 case stateLeavingSession:
4084 case stateJoinSessionFailed:
4085 case stateJoinSessionFailedWaiting:
4086 // normal transition
4087 LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
4088 setState(stateSessionTerminated);
4089 break;
4090
4091 case stateSessionTerminated:
4092 // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
4093 LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
4094 break;
4095
4096 default:
4097 LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
4098 setState(stateSessionTerminated);
4099 break;
4100 }
2888 } 4101 }
2889} 4102}
2890 4103
2891void LLVoiceClient::loginStateChangeEvent( 4104void LLVoiceClient::accountLoginStateChangeEvent(
2892 std::string &accountHandle, 4105 std::string &accountHandle,
2893 int statusCode, 4106 int statusCode,
2894 std::string &statusString, 4107 std::string &statusString,
@@ -2921,110 +4134,571 @@ void LLVoiceClient::loginStateChangeEvent(
2921 } 4134 }
2922} 4135}
2923 4136
2924void LLVoiceClient::sessionNewEvent( 4137void LLVoiceClient::mediaStreamUpdatedEvent(
2925 std::string &accountHandle, 4138 std::string &sessionHandle,
2926 std::string &eventSessionHandle, 4139 std::string &sessionGroupHandle,
2927 int state, 4140 int statusCode,
2928 std::string &nameString, 4141 std::string &statusString,
2929 std::string &uriString) 4142 int state,
4143 bool incoming)
2930{ 4144{
2931 LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; 4145 sessionState *session = findSession(sessionHandle);
2932 4146
2933 switch(state) 4147 LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
4148
4149 if(session)
2934 { 4150 {
2935 case 0: 4151 // We know about this session
2936 { 4152
2937 LL_DEBUGS("Voice") << "session handle = " << eventSessionHandle << ", name = " << nameString << ", uri = " << uriString << LL_ENDL; 4153 // Save the state for later use
4154 session->mMediaStreamState = state;
4155
4156 switch(statusCode)
4157 {
4158 case 0:
4159 case 200:
4160 // generic success
4161 // Don't change the saved error code (it may have been set elsewhere)
4162 break;
4163 default:
4164 // save the status code for later
4165 session->mErrorStatusCode = statusCode;
4166 break;
4167 }
4168
4169 switch(state)
4170 {
4171 case streamStateIdle:
4172 // Standard "left audio session"
4173 session->mVoiceEnabled = false;
4174 session->mMediaConnectInProgress = false;
4175 leftAudioSession(session);
4176 break;
2938 4177
2939 LLUUID caller_id; 4178 case streamStateConnected:
2940 if(IDFromName(nameString, caller_id)) 4179 session->mVoiceEnabled = true;
4180 session->mMediaConnectInProgress = false;
4181 joinedAudioSession(session);
4182 break;
4183
4184 case streamStateRinging:
4185 if(incoming)
2941 { 4186 {
2942 gIMMgr->inviteToSession( 4187 // Send the voice chat invite to the GUI layer
2943 LLIMMgr::computeSessionID( 4188 // TODO: Question: Should we correlate with the mute list here?
2944 IM_SESSION_P2P_INVITE, 4189 session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
2945 caller_id), 4190 session->mVoiceInvitePending = true;
2946 LLStringUtil::null, 4191 if(session->mName.empty())
2947 caller_id, 4192 {
2948 LLStringUtil::null, 4193 lookupName(session->mCallerID);
2949 IM_SESSION_P2P_INVITE, 4194 }
2950 LLIMMgr::INVITATION_TYPE_VOICE, 4195 else
2951 eventSessionHandle); 4196 {
4197 // Act like we just finished resolving the name
4198 avatarNameResolved(session->mCallerID, session->mName);
4199 }
4200 }
4201 break;
4202
4203 default:
4204 LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
4205 break;
4206
4207 }
4208
4209 }
4210 else
4211 {
4212 LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
4213 }
4214}
4215
4216void LLVoiceClient::textStreamUpdatedEvent(
4217 std::string &sessionHandle,
4218 std::string &sessionGroupHandle,
4219 bool enabled,
4220 int state,
4221 bool incoming)
4222{
4223 sessionState *session = findSession(sessionHandle);
4224
4225 if(session)
4226 {
4227 // Save the state for later use
4228 session->mTextStreamState = state;
4229
4230 // We know about this session
4231 switch(state)
4232 {
4233 case 0: // We see this when the text stream closes
4234 LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
4235 break;
4236
4237 case 1: // We see this on an incoming call from the Connector
4238 // Try to send any text messages queued for this session.
4239 sendQueuedTextMessages(session);
4240
4241 // Send the text chat invite to the GUI layer
4242 // TODO: Question: Should we correlate with the mute list here?
4243 session->mTextInvitePending = true;
4244 if(session->mName.empty())
4245 {
4246 lookupName(session->mCallerID);
2952 } 4247 }
2953 else 4248 else
2954 { 4249 {
2955 LL_WARNS("Voice") << "Could not generate caller id from uri " << uriString << LL_ENDL; 4250 // Act like we just finished resolving the name
4251 avatarNameResolved(session->mCallerID, session->mName);
2956 } 4252 }
2957 } 4253 break;
2958 break; 4254
2959 4255 default:
2960 default: 4256 LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
2961 LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL; 4257 break;
2962 break; 4258
4259 }
2963 } 4260 }
2964} 4261}
2965 4262
2966void LLVoiceClient::participantStateChangeEvent( 4263void LLVoiceClient::participantAddedEvent(
4264 std::string &sessionHandle,
4265 std::string &sessionGroupHandle,
2967 std::string &uriString, 4266 std::string &uriString,
2968 int statusCode, 4267 std::string &alias,
2969 std::string &statusString,
2970 int state,
2971 std::string &nameString, 4268 std::string &nameString,
2972 std::string &displayNameString, 4269 std::string &displayNameString,
2973 int participantType) 4270 int participantType)
2974{ 4271{
2975 participantState *participant = NULL; 4272 sessionState *session = findSession(sessionHandle);
2976 LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; 4273 if(session)
2977
2978 switch(state)
2979 { 4274 {
2980 case 7: // I see this when a participant joins 4275 participantState *participant = session->addParticipant(uriString);
2981 participant = addParticipant(uriString); 4276 if(participant)
2982 if(participant) 4277 {
4278 participant->mAccountName = nameString;
4279
4280 LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName
4281 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
4282
4283 if(participant->mAvatarIDValid)
2983 { 4284 {
2984 participant->mName = nameString; 4285 // Initiate a lookup
2985 LL_DEBUGS("Voice") << "added participant \"" << participant->mName 4286 lookupName(participant->mAvatarID);
2986 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
2987 } 4287 }
2988 break; 4288 else
2989 case 9: // I see this when a participant leaves
2990 participant = findParticipant(uriString);
2991 if(participant)
2992 { 4289 {
2993 removeParticipant(participant); 4290 // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work.
4291 std::string namePortion = nameFromsipURI(uriString);
4292 if(namePortion.empty())
4293 {
4294 // Problem with the SIP URI, fall back to the display name
4295 namePortion = displayNameString;
4296 }
4297 if(namePortion.empty())
4298 {
4299 // Problems with both of the above, fall back to the account name
4300 namePortion = nameString;
4301 }
4302
4303 // Set the display name (which is a hint to the active speakers window not to do its own lookup)
4304 participant->mDisplayName = namePortion;
4305 avatarNameResolved(participant->mAvatarID, namePortion);
2994 } 4306 }
2995 break; 4307 }
2996 default:
2997 LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
2998 break;
2999 } 4308 }
3000} 4309}
3001 4310
3002void LLVoiceClient::participantPropertiesEvent( 4311void LLVoiceClient::participantRemovedEvent(
4312 std::string &sessionHandle,
4313 std::string &sessionGroupHandle,
3003 std::string &uriString, 4314 std::string &uriString,
3004 int statusCode, 4315 std::string &alias,
3005 std::string &statusString, 4316 std::string &nameString)
3006 bool isLocallyMuted, 4317{
4318 sessionState *session = findSession(sessionHandle);
4319 if(session)
4320 {
4321 participantState *participant = session->findParticipant(uriString);
4322 if(participant)
4323 {
4324 session->removeParticipant(participant);
4325 }
4326 else
4327 {
4328 LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL;
4329 }
4330 }
4331 else
4332 {
4333 LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
4334 }
4335}
4336
4337
4338void LLVoiceClient::participantUpdatedEvent(
4339 std::string &sessionHandle,
4340 std::string &sessionGroupHandle,
4341 std::string &uriString,
4342 std::string &alias,
3007 bool isModeratorMuted, 4343 bool isModeratorMuted,
3008 bool isSpeaking, 4344 bool isSpeaking,
3009 int volume, 4345 int volume,
3010 F32 energy) 4346 F32 energy)
3011{ 4347{
3012 participantState *participant = findParticipant(uriString); 4348 sessionState *session = findSession(sessionHandle);
3013 if(participant) 4349 if(session)
4350 {
4351 participantState *participant = session->findParticipant(uriString);
4352
4353 if(participant)
4354 {
4355 participant->mIsSpeaking = isSpeaking;
4356 participant->mIsModeratorMuted = isModeratorMuted;
4357
4358 // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false
4359 if (isSpeaking)
4360 {
4361 participant->mSpeakingTimeout.reset();
4362 participant->mPower = energy;
4363 }
4364 else
4365 {
4366 participant->mPower = 0.0f;
4367 }
4368 participant->mVolume = volume;
4369 }
4370 else
4371 {
4372 LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
4373 }
4374 }
4375 else
4376 {
4377 LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
4378 }
4379}
4380
4381void LLVoiceClient::buddyPresenceEvent(
4382 std::string &uriString,
4383 std::string &alias,
4384 std::string &statusString,
4385 std::string &applicationString)
4386{
4387 buddyListEntry *buddy = findBuddy(uriString);
4388
4389 if(buddy)
4390 {
4391 LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL;
4392 LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
4393
4394 if(applicationString.empty())
4395 {
4396 // This presence event is from a client that doesn't set up the Application string. Do things the old-skool way.
4397 // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet.
4398
4399 if ( stricmp("Unknown", statusString.c_str())== 0)
4400 {
4401 // User went offline with a non-SLim-enabled viewer.
4402 buddy->mOnlineSL = false;
4403 }
4404 else if ( stricmp("Online", statusString.c_str())== 0)
4405 {
4406 // User came online with a non-SLim-enabled viewer.
4407 buddy->mOnlineSL = true;
4408 }
4409 else
4410 {
4411 // If the user is online through SLim, their status will be "Online-slc", "Away", or something else.
4412 // NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string.
4413 buddy->mOnlineSLim = true;
4414 }
4415 }
4416 else if(applicationString.find("SecondLifeViewer") != std::string::npos)
4417 {
4418 // This presence event is from a viewer that sets the application string
4419 if ( stricmp("Unknown", statusString.c_str())== 0)
4420 {
4421 // Viewer says they're offline
4422 buddy->mOnlineSL = false;
4423 }
4424 else
4425 {
4426 // Viewer says they're online
4427 buddy->mOnlineSL = true;
4428 }
4429 }
4430 else
4431 {
4432 // This presence event is from something which is NOT the SL viewer (assume it's SLim).
4433 if ( stricmp("Unknown", statusString.c_str())== 0)
4434 {
4435 // SLim says they're offline
4436 buddy->mOnlineSLim = false;
4437 }
4438 else
4439 {
4440 // SLim says they're online
4441 buddy->mOnlineSLim = true;
4442 }
4443 }
4444
4445 LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
4446
4447 // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change.
4448 LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID));
4449
4450 notifyFriendObservers();
4451 }
4452 else
4453 {
4454 LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL;
4455 }
4456}
4457
4458void LLVoiceClient::messageEvent(
4459 std::string &sessionHandle,
4460 std::string &uriString,
4461 std::string &alias,
4462 std::string &messageHeader,
4463 std::string &messageBody,
4464 std::string &applicationString)
4465{
4466 LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
4467// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL;
4468
4469 if(messageHeader.find("text/html") != std::string::npos)
4470 {
4471 std::string rawMessage;
4472
4473 {
4474 const std::string startMarker = "<body";
4475 const std::string startMarker2 = ">";
4476 const std::string endMarker = "</body>";
4477 const std::string startSpan = "<span";
4478 const std::string endSpan = "</span>";
4479 std::string::size_type start;
4480 std::string::size_type end;
4481
4482 // Default to displaying the raw string, so the message gets through.
4483 rawMessage = messageBody;
4484
4485 // Find the actual message text within the XML fragment
4486 start = messageBody.find(startMarker);
4487 start = messageBody.find(startMarker2, start);
4488 end = messageBody.find(endMarker);
4489
4490 if(start != std::string::npos)
4491 {
4492 start += startMarker2.size();
4493
4494 if(end != std::string::npos)
4495 end -= start;
4496
4497 rawMessage.assign(messageBody, start, end);
4498 }
4499 else
4500 {
4501 // Didn't find a <body>, try looking for a <span> instead.
4502 start = messageBody.find(startSpan);
4503 start = messageBody.find(startMarker2, start);
4504 end = messageBody.find(endSpan);
4505
4506 if(start != std::string::npos)
4507 {
4508 start += startMarker2.size();
4509
4510 if(end != std::string::npos)
4511 end -= start;
4512
4513 rawMessage.assign(messageBody, start, end);
4514 }
4515 }
4516 }
4517
4518// LL_DEBUGS("Voice") << " raw message = \n" << rawMessage << LL_ENDL;
4519
4520 // strip formatting tags
4521 {
4522 std::string::size_type start;
4523 std::string::size_type end;
4524
4525 while((start = rawMessage.find('<')) != std::string::npos)
4526 {
4527 if((end = rawMessage.find('>', start + 1)) != std::string::npos)
4528 {
4529 // Strip out the tag
4530 rawMessage.erase(start, (end + 1) - start);
4531 }
4532 else
4533 {
4534 // Avoid an infinite loop
4535 break;
4536 }
4537 }
4538 }
4539
4540 // Decode ampersand-escaped chars
4541 {
4542 std::string::size_type mark = 0;
4543
4544 // The text may contain text encoded with &lt;, &gt;, and &amp;
4545 mark = 0;
4546 while((mark = rawMessage.find("&lt;", mark)) != std::string::npos)
4547 {
4548 rawMessage.replace(mark, 4, "<");
4549 mark += 1;
4550 }
4551
4552 mark = 0;
4553 while((mark = rawMessage.find("&gt;", mark)) != std::string::npos)
4554 {
4555 rawMessage.replace(mark, 4, ">");
4556 mark += 1;
4557 }
4558
4559 mark = 0;
4560 while((mark = rawMessage.find("&amp;", mark)) != std::string::npos)
4561 {
4562 rawMessage.replace(mark, 5, "&");
4563 mark += 1;
4564 }
4565 }
4566
4567 // strip leading/trailing whitespace (since we always seem to get a couple newlines)
4568 LLStringUtil::trim(rawMessage);
4569
4570// LL_DEBUGS("Voice") << " stripped message = \n" << rawMessage << LL_ENDL;
4571
4572 sessionState *session = findSession(sessionHandle);
4573 if(session)
4574 {
4575 bool is_busy = gAgent.getBusy();
4576 bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
4577 bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
4578 bool quiet_chat = false;
4579 LLChat chat;
4580
4581 chat.mMuted = is_muted && !is_linden;
4582
4583 if(!chat.mMuted)
4584 {
4585 chat.mFromID = session->mCallerID;
4586 chat.mFromName = session->mName;
4587 chat.mSourceType = CHAT_SOURCE_AGENT;
4588
4589 if(is_busy && !is_linden)
4590 {
4591 quiet_chat = true;
4592 // TODO: Question: Return busy mode response here? Or maybe when session is started instead?
4593 }
4594
4595 std::string fullMessage = std::string(": ") + rawMessage;
4596
4597 LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
4598 gIMMgr->addMessage(session->mIMSessionID,
4599 session->mCallerID,
4600 session->mName.c_str(),
4601 fullMessage.c_str(),
4602 LLStringUtil::null, // default arg
4603 IM_NOTHING_SPECIAL, // default arg
4604 0, // default arg
4605 LLUUID::null, // default arg
4606 LLVector3::zero, // default arg
4607 true); // prepend name and make it a link to the user's profile
4608
4609 chat.mText = std::string("IM: ") + session->mName + std::string(": ") + rawMessage;
4610 // If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent.
4611 LLFloaterChat::addChat( chat, TRUE, quiet_chat );
4612 }
4613 }
4614 }
4615}
4616
4617void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
4618{
4619 sessionState *session = findSession(sessionHandle);
4620
4621 if(session)
3014 { 4622 {
3015 participant->mPTT = !isLocallyMuted; 4623 participantState *participant = session->findParticipant(uriString);
3016 participant->mIsSpeaking = isSpeaking; 4624 if(participant)
3017 participant->mIsModeratorMuted = isModeratorMuted; 4625 {
3018 if (isSpeaking) 4626 if (!stricmp(notificationType.c_str(), "Typing"))
4627 {
4628 // Other end started typing
4629 // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart().
4630 // It requires an LLIMInfo for the message, which we don't have here.
4631 }
4632 else if (!stricmp(notificationType.c_str(), "NotTyping"))
4633 {
4634 // Other end stopped typing
4635 // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop().
4636 // It requires an LLIMInfo for the message, which we don't have here.
4637 }
4638 else
4639 {
4640 LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
4641 }
4642 }
4643 else
3019 { 4644 {
3020 participant->mSpeakingTimeout.reset(); 4645 LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
3021 } 4646 }
3022 participant->mPower = energy;
3023 participant->mVolume = volume;
3024 } 4647 }
3025 else 4648 else
3026 { 4649 {
3027 LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; 4650 LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL;
4651 }
4652}
4653
4654void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType)
4655{
4656 buddyListEntry *buddy = findBuddy(buddyURI);
4657
4658 if(!buddy)
4659 {
4660 // Couldn't find buddy by URI, try converting the alias...
4661 if(!alias.empty())
4662 {
4663 LLUUID id;
4664 if(IDFromName(alias, id))
4665 {
4666 buddy = findBuddy(id);
4667 }
4668 }
4669 }
4670
4671 if(buddy)
4672 {
4673 std::ostringstream stream;
4674
4675 if(buddy->mCanSeeMeOnline)
4676 {
4677 // Sending the response will create an auto-accept rule
4678 buddy->mHasAutoAcceptListEntry = true;
4679 }
4680 else
4681 {
4682 // Sending the response will create a block rule
4683 buddy->mHasBlockListEntry = true;
4684 }
4685
4686 if(buddy->mInSLFriends)
4687 {
4688 buddy->mInVivoxBuddies = true;
4689 }
4690
4691 stream
4692 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">"
4693 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
4694 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
4695 << "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>"
4696 << "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>"
4697 << "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>"
4698 << "</Request>"
4699 << "\n\n\n";
4700
4701 writeString(stream.str());
3028 } 4702 }
3029} 4703}
3030 4704
@@ -3034,155 +4708,226 @@ void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
3034 mTuningEnergy = energy; 4708 mTuningEnergy = energy;
3035} 4709}
3036 4710
4711void LLVoiceClient::buddyListChanged()
4712{
4713 // This is called after we receive a BuddyAndGroupListChangedEvent.
4714 mBuddyListMapPopulated = true;
4715 mFriendsListDirty = true;
4716}
4717
3037void LLVoiceClient::muteListChanged() 4718void LLVoiceClient::muteListChanged()
3038{ 4719{
3039 // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. 4720 // The user's mute list has been updated. Go through the current participant list and sync it with the mute list.
3040 4721 if(mAudioSession)
3041 participantMap::iterator iter = mParticipantMap.begin();
3042
3043 for(; iter != mParticipantMap.end(); iter++)
3044 { 4722 {
3045 participantState *p = iter->second; 4723 participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
3046 4724
3047 // Check to see if this participant is on the mute list already 4725 for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
3048 updateMuteState(p); 4726 {
4727 participantState *p = iter->second;
4728
4729 // Check to see if this participant is on the mute list already
4730 if(p->updateMuteState())
4731 mAudioSession->mVolumeDirty = true;
4732 }
4733 }
4734}
4735
4736void LLVoiceClient::updateFriends(U32 mask)
4737{
4738 if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS))
4739 {
4740 // Just resend the whole friend list to the daemon
4741 mFriendsListDirty = true;
3049 } 4742 }
3050} 4743}
3051 4744
3052///////////////////////////// 4745/////////////////////////////
3053// Managing list of participants 4746// Managing list of participants
3054LLVoiceClient::participantState::participantState(const std::string &uri) : 4747LLVoiceClient::participantState::participantState(const std::string &uri) :
3055 mURI(uri), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), mLastSpokeTimestamp(0.f), mPower(0.f), mVolume(0), mServiceType(serviceTypeUnknown), 4748 mURI(uri),
3056 mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false) 4749 mPTT(false),
4750 mIsSpeaking(false),
4751 mIsModeratorMuted(false),
4752 mLastSpokeTimestamp(0.f),
4753 mPower(0.f),
4754 mVolume(0),
4755 mOnMuteList(false),
4756 mUserVolume(100),
4757 mVolumeDirty(false),
4758 mAvatarIDValid(false),
4759 mIsSelf(false)
3057{ 4760{
3058} 4761}
3059 4762
3060LLVoiceClient::participantState *LLVoiceClient::addParticipant(const std::string &uri) 4763LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri)
3061{ 4764{
3062 participantState *result = NULL; 4765 participantState *result = NULL;
3063 4766 bool useAlternateURI = false;
3064 participantMap::iterator iter = mParticipantMap.find(uri);
3065 4767
3066 if(iter != mParticipantMap.end()) 4768 // Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it
4769 // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
3067 { 4770 {
3068 // Found a matching participant already in the map. 4771 participantMap::iterator iter = mParticipantsByURI.find(&uri);
3069 result = iter->second; 4772
3070 } 4773 if(iter == mParticipantsByURI.end())
4774 {
4775 if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
4776 {
4777 // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
4778 // Use mSIPURI instead, since it will be properly encoded.
4779 iter = mParticipantsByURI.find(&(mSIPURI));
4780 useAlternateURI = true;
4781 }
4782 }
3071 4783
4784 if(iter != mParticipantsByURI.end())
4785 {
4786 result = iter->second;
4787 }
4788 }
4789
3072 if(!result) 4790 if(!result)
3073 { 4791 {
3074 // participant isn't already in one list or the other. 4792 // participant isn't already in one list or the other.
3075 result = new participantState(uri); 4793 result = new participantState(useAlternateURI?mSIPURI:uri);
3076 mParticipantMap.insert(participantMap::value_type(uri, result)); 4794 mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result));
3077 mParticipantMapChanged = true; 4795 mParticipantsChanged = true;
3078 4796
3079 // Try to do a reverse transform on the URI to get the GUID back. 4797 // Try to do a reverse transform on the URI to get the GUID back.
3080 { 4798 {
3081 LLUUID id; 4799 LLUUID id;
3082 if(IDFromName(uri, id)) 4800 if(IDFromName(result->mURI, id))
3083 { 4801 {
3084 result->mAvatarIDValid = true; 4802 result->mAvatarIDValid = true;
3085 result->mAvatarID = id; 4803 result->mAvatarID = id;
3086 4804
3087 updateMuteState(result); 4805 if(result->updateMuteState())
4806 mVolumeDirty = true;
4807 }
4808 else
4809 {
4810 // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
4811 // This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache.
4812 setUUIDFromStringHash(result->mAvatarID, uri);
3088 } 4813 }
3089 } 4814 }
3090 4815
4816 mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
4817
3091 LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; 4818 LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
3092 } 4819 }
3093 4820
3094 return result; 4821 return result;
3095} 4822}
3096 4823
3097void LLVoiceClient::updateMuteState(participantState *p) 4824bool LLVoiceClient::participantState::updateMuteState()
3098{ 4825{
3099 if(p->mAvatarIDValid) 4826 bool result = false;
4827
4828 if(mAvatarIDValid)
3100 { 4829 {
3101 bool isMuted = LLMuteList::getInstance()->isMuted(p->mAvatarID, LLMute::flagVoiceChat); 4830 bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
3102 if(p->mOnMuteList != isMuted) 4831 if(mOnMuteList != isMuted)
3103 { 4832 {
3104 p->mOnMuteList = isMuted; 4833 mOnMuteList = isMuted;
3105 p->mVolumeDirty = true;
3106 mVolumeDirty = true; 4834 mVolumeDirty = true;
4835 result = true;
3107 } 4836 }
3108 } 4837 }
4838 return result;
3109} 4839}
3110 4840
3111void LLVoiceClient::removeParticipant(LLVoiceClient::participantState *participant) 4841void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant)
3112{ 4842{
3113 if(participant) 4843 if(participant)
3114 { 4844 {
3115 participantMap::iterator iter = mParticipantMap.find(participant->mURI); 4845 participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI));
3116 4846 participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID));
4847
3117 LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; 4848 LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
3118 4849
3119 mParticipantMap.erase(iter); 4850 if(iter == mParticipantsByURI.end())
3120 delete participant; 4851 {
3121 mParticipantMapChanged = true; 4852 LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
4853 }
4854 else if(iter2 == mParticipantsByUUID.end())
4855 {
4856 LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
4857 }
4858 else if(iter->second != iter2->second)
4859 {
4860 LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL;
4861 }
4862 else
4863 {
4864 mParticipantsByURI.erase(iter);
4865 mParticipantsByUUID.erase(iter2);
4866
4867 delete participant;
4868 mParticipantsChanged = true;
4869 }
3122 } 4870 }
3123} 4871}
3124 4872
3125void LLVoiceClient::removeAllParticipants() 4873void LLVoiceClient::sessionState::removeAllParticipants()
3126{ 4874{
3127 LL_DEBUGS("Voice") << "called" << LL_ENDL; 4875 LL_DEBUGS("Voice") << "called" << LL_ENDL;
3128 4876
3129 while(!mParticipantMap.empty()) 4877 while(!mParticipantsByURI.empty())
3130 { 4878 {
3131 removeParticipant(mParticipantMap.begin()->second); 4879 removeParticipant(mParticipantsByURI.begin()->second);
4880 }
4881
4882 if(!mParticipantsByUUID.empty())
4883 {
4884 LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL
3132 } 4885 }
3133} 4886}
3134 4887
3135LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void) 4888LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
3136{ 4889{
3137 return &mParticipantMap; 4890 participantMap *result = NULL;
4891 if(mAudioSession)
4892 {
4893 result = &(mAudioSession->mParticipantsByURI);
4894 }
4895 return result;
3138} 4896}
3139 4897
3140 4898
3141LLVoiceClient::participantState *LLVoiceClient::findParticipant(const std::string &uri) 4899LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
3142{ 4900{
3143 participantState *result = NULL; 4901 participantState *result = NULL;
3144 4902
3145 // Don't find any participants if we're not connected. This is so that we don't continue to get stale data 4903 participantMap::iterator iter = mParticipantsByURI.find(&uri);
3146 // after the daemon dies. 4904
3147 if(mConnected) 4905 if(iter == mParticipantsByURI.end())
3148 { 4906 {
3149 participantMap::iterator iter = mParticipantMap.find(uri); 4907 if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
3150
3151 if(iter != mParticipantMap.end())
3152 { 4908 {
3153 result = iter->second; 4909 // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
4910 // Look up the other URI
4911 iter = mParticipantsByURI.find(&(mSIPURI));
3154 } 4912 }
3155 } 4913 }
3156 4914
4915 if(iter != mParticipantsByURI.end())
4916 {
4917 result = iter->second;
4918 }
4919
3157 return result; 4920 return result;
3158} 4921}
3159 4922
3160 4923LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
3161LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvatar *avatar)
3162{ 4924{
3163 participantState * result = NULL; 4925 participantState * result = NULL;
4926 participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id);
3164 4927
3165 // You'd think this would work, but it doesn't... 4928 if(iter != mParticipantsByUUID.end())
3166// std::string uri = sipURIFromAvatar(avatar);
3167
3168 // Currently, the URI is just the account name.
3169 std::string loginName = nameFromAvatar(avatar);
3170 result = findParticipant(loginName);
3171
3172 if(result != NULL)
3173 { 4929 {
3174 if(!result->mAvatarIDValid) 4930 result = iter->second;
3175 {
3176 result->mAvatarID = avatar->getID();
3177 result->mAvatarIDValid = true;
3178
3179 // We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
3180 mParticipantMapChanged = true;
3181
3182 updateMuteState(result);
3183 }
3184
3185
3186 } 4931 }
3187 4932
3188 return result; 4933 return result;
@@ -3191,43 +4936,19 @@ LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvat
3191LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id) 4936LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
3192{ 4937{
3193 participantState * result = NULL; 4938 participantState * result = NULL;
3194
3195 // Currently, the URI is just the account name.
3196 std::string loginName = nameFromID(id);
3197 result = findParticipant(loginName);
3198
3199 return result;
3200}
3201
3202
3203void LLVoiceClient::clearChannelMap(void)
3204{
3205 mChannelMap.clear();
3206}
3207
3208void LLVoiceClient::addChannelMapEntry(std::string &name, std::string &uri)
3209{
3210 LL_DEBUGS("Voice") << "Adding channel name mapping: " << name << " -> " << uri << LL_ENDL;
3211 mChannelMap.insert(channelMap::value_type(name, uri));
3212}
3213
3214std::string LLVoiceClient::findChannelURI(std::string &name)
3215{
3216 std::string result;
3217 4939
3218 channelMap::iterator iter = mChannelMap.find(name); 4940 if(mAudioSession)
3219
3220 if(iter != mChannelMap.end())
3221 { 4941 {
3222 result = iter->second; 4942 result = mAudioSession->findParticipantByID(id);
3223 } 4943 }
3224 4944
3225 return result; 4945 return result;
3226} 4946}
3227 4947
4948
3228void LLVoiceClient::parcelChanged() 4949void LLVoiceClient::parcelChanged()
3229{ 4950{
3230 if(getState() >= stateLoggedIn) 4951 if(getState() >= stateNoChannel)
3231 { 4952 {
3232 // If the user is logged in, start a channel lookup. 4953 // If the user is logged in, start a channel lookup.
3233 LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; 4954 LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
@@ -3241,7 +4962,7 @@ void LLVoiceClient::parcelChanged()
3241 } 4962 }
3242 else 4963 else
3243 { 4964 {
3244 // The transition to stateLoggedIn needs to kick this off again. 4965 // The transition to stateNoChannel needs to kick this off again.
3245 LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; 4966 LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
3246 } 4967 }
3247} 4968}
@@ -3249,12 +4970,17 @@ void LLVoiceClient::parcelChanged()
3249void LLVoiceClient::switchChannel( 4970void LLVoiceClient::switchChannel(
3250 std::string uri, 4971 std::string uri,
3251 bool spatial, 4972 bool spatial,
3252 bool noReconnect, 4973 bool no_reconnect,
4974 bool is_p2p,
3253 std::string hash) 4975 std::string hash)
3254{ 4976{
3255 bool needsSwitch = false; 4977 bool needsSwitch = false;
3256 4978
3257 LL_DEBUGS("Voice") << "called in state " << state2string(getState()) << " with uri \"" << uri << "\"" << LL_ENDL; 4979 LL_DEBUGS("Voice")
4980 << "called in state " << state2string(getState())
4981 << " with uri \"" << uri << "\""
4982 << (spatial?", spatial is true":", spatial is false")
4983 << LL_ENDL;
3258 4984
3259 switch(getState()) 4985 switch(getState())
3260 { 4986 {
@@ -3264,18 +4990,18 @@ void LLVoiceClient::switchChannel(
3264 // Always switch to the new URI from these states. 4990 // Always switch to the new URI from these states.
3265 needsSwitch = true; 4991 needsSwitch = true;
3266 break; 4992 break;
3267 4993
3268 default: 4994 default:
3269 if(mSessionTerminateRequested) 4995 if(mSessionTerminateRequested)
3270 { 4996 {
3271 // If a terminate has been requested, we need to compare against where the URI we're already headed to. 4997 // If a terminate has been requested, we need to compare against where the URI we're already headed to.
3272 if(mNextSessionURI != uri) 4998 if(mNextAudioSession && (mNextAudioSession->mSIPURI != uri))
3273 needsSwitch = true; 4999 needsSwitch = true;
3274 } 5000 }
3275 else 5001 else
3276 { 5002 {
3277 // Otherwise, compare against the URI we're in now. 5003 // Otherwise, compare against the URI we're in now.
3278 if(mSessionURI != uri) 5004 if(mAudioSession && (mAudioSession->mSIPURI != uri))
3279 needsSwitch = true; 5005 needsSwitch = true;
3280 } 5006 }
3281 break; 5007 break;
@@ -3283,22 +5009,28 @@ void LLVoiceClient::switchChannel(
3283 5009
3284 if(needsSwitch) 5010 if(needsSwitch)
3285 { 5011 {
3286 mNextSessionURI = uri;
3287 mNextSessionHash = hash;
3288 mNextSessionHandle.clear();
3289 mNextP2PSessionURI.clear();
3290 mNextSessionSpatial = spatial;
3291 mNextSessionNoReconnect = noReconnect;
3292
3293 if(uri.empty()) 5012 if(uri.empty())
3294 { 5013 {
3295 // Leave any channel we may be in 5014 // Leave any channel we may be in
3296 LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; 5015 LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
5016
5017 sessionState *oldSession = mNextAudioSession;
5018 mNextAudioSession = NULL;
5019
5020 // The old session may now need to be deleted.
5021 reapSession(oldSession);
5022
3297 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); 5023 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
3298 } 5024 }
3299 else 5025 else
3300 { 5026 {
3301 LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; 5027 LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
5028
5029 mNextAudioSession = addSession(uri);
5030 mNextAudioSession->mHash = hash;
5031 mNextAudioSession->mIsSpatial = spatial;
5032 mNextAudioSession->mReconnect = !no_reconnect;
5033 mNextAudioSession->mIsP2P = is_p2p;
3302 } 5034 }
3303 5035
3304 if(getState() <= stateNoChannel) 5036 if(getState() <= stateNoChannel)
@@ -3313,15 +5045,10 @@ void LLVoiceClient::switchChannel(
3313 } 5045 }
3314} 5046}
3315 5047
3316void LLVoiceClient::joinSession(std::string handle, std::string uri) 5048void LLVoiceClient::joinSession(sessionState *session)
3317{ 5049{
3318 mNextSessionURI.clear(); 5050 mNextAudioSession = session;
3319 mNextSessionHash.clear(); 5051
3320 mNextP2PSessionURI = uri;
3321 mNextSessionHandle = handle;
3322 mNextSessionSpatial = false;
3323 mNextSessionNoReconnect = false;
3324
3325 if(getState() <= stateNoChannel) 5052 if(getState() <= stateNoChannel)
3326 { 5053 {
3327 // We're already set up to join a channel, just needed to fill in the session handle 5054 // We're already set up to join a channel, just needed to fill in the session handle
@@ -3337,7 +5064,7 @@ void LLVoiceClient::setNonSpatialChannel(
3337 const std::string &uri, 5064 const std::string &uri,
3338 const std::string &credentials) 5065 const std::string &credentials)
3339{ 5066{
3340 switchChannel(uri, false, false, credentials); 5067 switchChannel(uri, false, false, false, credentials);
3341} 5068}
3342 5069
3343void LLVoiceClient::setSpatialChannel( 5070void LLVoiceClient::setSpatialChannel(
@@ -3345,51 +5072,216 @@ void LLVoiceClient::setSpatialChannel(
3345 const std::string &credentials) 5072 const std::string &credentials)
3346{ 5073{
3347 mSpatialSessionURI = uri; 5074 mSpatialSessionURI = uri;
5075 mSpatialSessionCredentials = credentials;
3348 mAreaVoiceDisabled = mSpatialSessionURI.empty(); 5076 mAreaVoiceDisabled = mSpatialSessionURI.empty();
3349 5077
3350 LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; 5078 LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
3351 5079
3352 if(mNonSpatialChannel || !mNextSessionSpatial) 5080 if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
3353 { 5081 {
3354 // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. 5082 // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
3355 LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; 5083 LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
3356 } 5084 }
3357 else 5085 else
3358 { 5086 {
3359 switchChannel(mSpatialSessionURI, true, false, credentials); 5087 switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
3360 } 5088 }
3361} 5089}
3362 5090
3363void LLVoiceClient::callUser(LLUUID &uuid) 5091void LLVoiceClient::callUser(const LLUUID &uuid)
3364{ 5092{
3365 std::string userURI = sipURIFromID(uuid); 5093 std::string userURI = sipURIFromID(uuid);
3366 5094
3367 switchChannel(userURI, false, true); 5095 switchChannel(userURI, false, true, true);
5096}
5097
5098LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid)
5099{
5100 // Figure out if a session with the user already exists
5101 sessionState *session = findSession(uuid);
5102 if(!session)
5103 {
5104 // No session with user, need to start one.
5105 std::string uri = sipURIFromID(uuid);
5106 session = addSession(uri);
5107 session->mIsSpatial = false;
5108 session->mReconnect = false;
5109 session->mIsP2P = true;
5110 session->mCallerID = uuid;
5111 }
5112
5113 if(session)
5114 {
5115 if(session->mHandle.empty())
5116 {
5117 // Session isn't active -- start it up.
5118 sessionCreateSendMessage(session, false, true);
5119 }
5120 else
5121 {
5122 // Session is already active -- start up text.
5123 sessionTextConnectSendMessage(session);
5124 }
5125 }
5126
5127 return session;
5128}
5129
5130bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
5131{
5132 bool result = false;
5133
5134 // Attempt to locate the indicated session
5135 sessionState *session = startUserIMSession(participant_id);
5136 if(session)
5137 {
5138 // found the session, attempt to send the message
5139 session->mTextMsgQueue.push(message);
5140
5141 // Try to send queued messages (will do nothing if the session is not open yet)
5142 sendQueuedTextMessages(session);
5143
5144 // The message is queued, so we succeed.
5145 result = true;
5146 }
5147 else
5148 {
5149 LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
5150 }
5151
5152 return result;
5153}
5154
5155void LLVoiceClient::sendQueuedTextMessages(sessionState *session)
5156{
5157 if(session->mTextStreamState == 1)
5158 {
5159 if(!session->mTextMsgQueue.empty())
5160 {
5161 std::ostringstream stream;
5162
5163 while(!session->mTextMsgQueue.empty())
5164 {
5165 std::string message = session->mTextMsgQueue.front();
5166 session->mTextMsgQueue.pop();
5167 stream
5168 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
5169 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
5170 << "<MessageHeader>text/HTML</MessageHeader>"
5171 << "<MessageBody>" << message << "</MessageBody>"
5172 << "</Request>"
5173 << "\n\n\n";
5174 }
5175 writeString(stream.str());
5176 }
5177 }
5178 else
5179 {
5180 // Session isn't connected yet, defer until later.
5181 }
5182}
5183
5184void LLVoiceClient::endUserIMSession(const LLUUID &uuid)
5185{
5186 // Figure out if a session with the user exists
5187 sessionState *session = findSession(uuid);
5188 if(session)
5189 {
5190 // found the session
5191 if(!session->mHandle.empty())
5192 {
5193 sessionTextDisconnectSendMessage(session);
5194 }
5195 }
5196 else
5197 {
5198 LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
5199 }
3368} 5200}
3369 5201
3370void LLVoiceClient::answerInvite(std::string &sessionHandle, LLUUID& other_user_id) 5202bool LLVoiceClient::answerInvite(std::string &sessionHandle)
3371{ 5203{
3372 joinSession(sessionHandle, sipURIFromID(other_user_id)); 5204 // this is only ever used to answer incoming p2p call invites.
5205
5206 sessionState *session = findSession(sessionHandle);
5207 if(session)
5208 {
5209 session->mIsSpatial = false;
5210 session->mReconnect = false;
5211 session->mIsP2P = true;
5212
5213 joinSession(session);
5214 return true;
5215 }
5216
5217 return false;
5218}
5219
5220bool LLVoiceClient::isOnlineSIP(const LLUUID &id)
5221{
5222 bool result = false;
5223 buddyListEntry *buddy = findBuddy(id);
5224 if(buddy)
5225 {
5226 result = buddy->mOnlineSLim;
5227 LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL;
5228 }
5229
5230 if(!result)
5231 {
5232 // This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM.
5233 sessionState *session = findSession(id);
5234 if(session && !session->mHandle.empty())
5235 {
5236 if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle))
5237 {
5238 LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL;
5239 // we have a p2p text session open with this user, so by definition they're online.
5240 result = true;
5241 }
5242 }
5243 }
5244
5245 return result;
3373} 5246}
3374 5247
3375void LLVoiceClient::declineInvite(std::string &sessionHandle) 5248void LLVoiceClient::declineInvite(std::string &sessionHandle)
3376{ 5249{
3377 sessionTerminateByHandle(sessionHandle); 5250 sessionState *session = findSession(sessionHandle);
5251 if(session)
5252 {
5253 sessionMediaDisconnectSendMessage(session);
5254 }
3378} 5255}
3379 5256
3380void LLVoiceClient::leaveNonSpatialChannel() 5257void LLVoiceClient::leaveNonSpatialChannel()
3381{ 5258{
3382 switchChannel(mSpatialSessionURI); 5259 LL_DEBUGS("Voice")
5260 << "called in state " << state2string(getState())
5261 << LL_ENDL;
5262
5263 // Make sure we don't rejoin the current session.
5264 sessionState *oldNextSession = mNextAudioSession;
5265 mNextAudioSession = NULL;
5266
5267 // Most likely this will still be the current session at this point, but check it anyway.
5268 reapSession(oldNextSession);
5269
5270 verifySessionState();
5271
5272 sessionTerminate();
3383} 5273}
3384 5274
3385std::string LLVoiceClient::getCurrentChannel() 5275std::string LLVoiceClient::getCurrentChannel()
3386{ 5276{
5277 std::string result;
5278
3387 if((getState() == stateRunning) && !mSessionTerminateRequested) 5279 if((getState() == stateRunning) && !mSessionTerminateRequested)
3388 { 5280 {
3389 return mSessionURI; 5281 result = getAudioSessionURI();
3390 } 5282 }
3391 5283
3392 return ""; 5284 return result;
3393} 5285}
3394 5286
3395bool LLVoiceClient::inProximalChannel() 5287bool LLVoiceClient::inProximalChannel()
@@ -3398,7 +5290,7 @@ bool LLVoiceClient::inProximalChannel()
3398 5290
3399 if((getState() == stateRunning) && !mSessionTerminateRequested) 5291 if((getState() == stateRunning) && !mSessionTerminateRequested)
3400 { 5292 {
3401 result = !mNonSpatialChannel; 5293 result = inSpatialChannel();
3402 } 5294 }
3403 5295
3404 return result; 5296 return result;
@@ -3410,7 +5302,7 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
3410 result = "sip:"; 5302 result = "sip:";
3411 result += nameFromID(id); 5303 result += nameFromID(id);
3412 result += "@"; 5304 result += "@";
3413 result += mAccountServerName; 5305 result += mVoiceSIPURIHostName;
3414 5306
3415 return result; 5307 return result;
3416} 5308}
@@ -3423,7 +5315,7 @@ std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
3423 result = "sip:"; 5315 result = "sip:";
3424 result += nameFromID(avatar->getID()); 5316 result += nameFromID(avatar->getID());
3425 result += "@"; 5317 result += "@";
3426 result += mAccountServerName; 5318 result += mVoiceSIPURIHostName;
3427 } 5319 }
3428 5320
3429 return result; 5321 return result;
@@ -3442,6 +5334,13 @@ std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
3442std::string LLVoiceClient::nameFromID(const LLUUID &uuid) 5334std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
3443{ 5335{
3444 std::string result; 5336 std::string result;
5337
5338 if (uuid.isNull()) {
5339 //VIVOX, the uuid emtpy look for the mURIString and return that instead.
5340 //result.assign(uuid.mURIStringName);
5341 LLStringUtil::replaceChar(result, '_', ' ');
5342 return result;
5343 }
3445 // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. 5344 // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
3446 result = "x"; 5345 result = "x";
3447 5346
@@ -3455,13 +5354,24 @@ std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
3455 // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: 5354 // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
3456 // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') 5355 // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
3457 5356
5357 // The reverse transform can be done with:
5358 // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
5359
3458 return result; 5360 return result;
3459} 5361}
3460 5362
3461bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid) 5363bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
3462{ 5364{
3463 bool result = false; 5365 bool result = false;
3464 5366
5367 // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
5368 // If it is, convert to a bare name before doing the transform.
5369 std::string name = nameFromsipURI(inName);
5370
5371 // Doesn't look like a SIP URI, assume it's an actual name.
5372 if(name.empty())
5373 name = inName;
5374
3465 // This will only work if the name is of the proper form. 5375 // This will only work if the name is of the proper form.
3466 // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: 5376 // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
3467 // "xFnPP04IpREWNkuw1cOXlhw==" 5377 // "xFnPP04IpREWNkuw1cOXlhw=="
@@ -3483,6 +5393,13 @@ bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
3483 memcpy(uuid.mData, rawuuid, UUID_BYTES); 5393 memcpy(uuid.mData, rawuuid, UUID_BYTES);
3484 result = true; 5394 result = true;
3485 } 5395 }
5396 }
5397
5398 if(!result)
5399 {
5400 // VIVOX: not a standard account name, just copy the URI name mURIString field
5401 // and hope for the best. bpj
5402 uuid.setNull(); // VIVOX, set the uuid field to nulls
3486 } 5403 }
3487 5404
3488 return result; 5405 return result;
@@ -3499,13 +5416,59 @@ std::string LLVoiceClient::sipURIFromName(std::string &name)
3499 result = "sip:"; 5416 result = "sip:";
3500 result += name; 5417 result += name;
3501 result += "@"; 5418 result += "@";
3502 result += mAccountServerName; 5419 result += mVoiceSIPURIHostName;
3503 5420
3504// LLStringUtil::toLower(result); 5421// LLStringUtil::toLower(result);
3505 5422
3506 return result; 5423 return result;
3507} 5424}
3508 5425
5426std::string LLVoiceClient::nameFromsipURI(const std::string &uri)
5427{
5428 std::string result;
5429
5430 std::string::size_type sipOffset, atOffset;
5431 sipOffset = uri.find("sip:");
5432 atOffset = uri.find("@");
5433 if((sipOffset != std::string::npos) && (atOffset != std::string::npos))
5434 {
5435 result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
5436 }
5437
5438 return result;
5439}
5440
5441bool LLVoiceClient::inSpatialChannel(void)
5442{
5443 bool result = false;
5444
5445 if(mAudioSession)
5446 result = mAudioSession->mIsSpatial;
5447
5448 return result;
5449}
5450
5451std::string LLVoiceClient::getAudioSessionURI()
5452{
5453 std::string result;
5454
5455 if(mAudioSession)
5456 result = mAudioSession->mSIPURI;
5457
5458 return result;
5459}
5460
5461std::string LLVoiceClient::getAudioSessionHandle()
5462{
5463 std::string result;
5464
5465 if(mAudioSession)
5466 result = mAudioSession->mHandle;
5467
5468 return result;
5469}
5470
5471
3509///////////////////////////// 5472/////////////////////////////
3510// Sending updates of current state 5473// Sending updates of current state
3511 5474
@@ -3544,7 +5507,8 @@ void LLVoiceClient::updatePosition(void)
3544 LLMatrix3 rot; 5507 LLMatrix3 rot;
3545 LLVector3d pos; 5508 LLVector3d pos;
3546 5509
3547 // MBW -- XXX -- Setting both camera and avatar velocity to 0 for now. May figure it out later... 5510 // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
5511 // They're currently always set to zero.
3548 5512
3549 // Send the current camera position to the voice code 5513 // Send the current camera position to the voice code
3550 rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); 5514 rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis());
@@ -3559,7 +5523,7 @@ void LLVoiceClient::updatePosition(void)
3559 rot = agent->getRootJoint()->getWorldRotation().getMatrix3(); 5523 rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
3560 5524
3561 pos = agent->getPositionGlobal(); 5525 pos = agent->getPositionGlobal();
3562 // MBW -- XXX -- Can we get the head offset from outside the LLVOAvatar? 5526 // TODO: Can we get the head offset from outside the LLVOAvatar?
3563// pos += LLVector3d(mHeadOffset); 5527// pos += LLVector3d(mHeadOffset);
3564 pos += LLVector3d(0.f, 0.f, 1.f); 5528 pos += LLVector3d(0.f, 0.f, 1.f);
3565 5529
@@ -3665,8 +5629,8 @@ void LLVoiceClient::setVoiceEnabled(bool enabled)
3665 } 5629 }
3666 else 5630 else
3667 { 5631 {
3668 // for now, leave active channel, to auto join when turning voice back on 5632 // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it.
3669 //LLVoiceChannel::getCurrentVoiceChannel->deactivate(); 5633 LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
3670 } 5634 }
3671 } 5635 }
3672} 5636}
@@ -3746,56 +5710,34 @@ void LLVoiceClient::setEarLocation(S32 loc)
3746 5710
3747void LLVoiceClient::setVoiceVolume(F32 volume) 5711void LLVoiceClient::setVoiceVolume(F32 volume)
3748{ 5712{
3749 LL_DEBUGS("Voice") << "volume is " << volume << LL_ENDL; 5713 int scaled_volume = scale_speaker_volume(volume);
3750 5714
3751 // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. 5715 if(scaled_volume != mSpeakerVolume)
3752 // Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
3753
3754 volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
3755 int scaledVolume = 24; // offset scaledVolume by its default level
3756 if(volume < 0.0f)
3757 scaledVolume += ((int)(volume * 248.0f)); // (24 - (-100)) * 2
3758 else
3759 scaledVolume += ((int)(volume * 52.0f)); // (50 - 24) * 2
3760
3761 if(scaledVolume != mSpeakerVolume)
3762 { 5716 {
3763 if((scaledVolume == -100) || (mSpeakerVolume == -100)) 5717 if((scaled_volume == 0) || (mSpeakerVolume == 0))
3764 { 5718 {
3765 mSpeakerMuteDirty = true; 5719 mSpeakerMuteDirty = true;
3766 } 5720 }
3767 5721
3768 mSpeakerVolume = scaledVolume; 5722 mSpeakerVolume = scaled_volume;
3769 mSpeakerVolumeDirty = true; 5723 mSpeakerVolumeDirty = true;
3770 } 5724 }
3771} 5725}
3772 5726
3773void LLVoiceClient::setMicGain(F32 volume) 5727void LLVoiceClient::setMicGain(F32 volume)
3774{ 5728{
3775 int scaledVolume = ((int)(volume * 100.0f)) - 100; 5729 int scaled_volume = scale_mic_volume(volume);
3776 if(scaledVolume != mMicVolume) 5730
5731 if(scaled_volume != mMicVolume)
3777 { 5732 {
3778 mMicVolume = scaledVolume; 5733 mMicVolume = scaled_volume;
3779 mMicVolumeDirty = true; 5734 mMicVolumeDirty = true;
3780 } 5735 }
3781} 5736}
3782 5737
3783void LLVoiceClient::setVivoxDebugServerName(std::string &serverName)
3784{
3785 if(!mAccountServerName.empty())
3786 {
3787 // The name has been filled in already, which means we know whether we're connecting to agni or not.
3788 if(!sConnectingToAgni)
3789 {
3790 // Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
3791 mAccountServerName = serverName;
3792 }
3793 }
3794}
3795
3796void LLVoiceClient::keyDown(KEY key, MASK mask) 5738void LLVoiceClient::keyDown(KEY key, MASK mask)
3797{ 5739{
3798 LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL; 5740// LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL;
3799 5741
3800 if (gKeyboard->getKeyRepeated(key)) 5742 if (gKeyboard->getKeyRepeated(key))
3801 { 5743 {
@@ -3933,19 +5875,6 @@ BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
3933 return result; 5875 return result;
3934} 5876}
3935 5877
3936BOOL LLVoiceClient::getPTTPressed(const LLUUID& id)
3937{
3938 BOOL result = FALSE;
3939
3940 participantState *participant = findParticipantByID(id);
3941 if(participant)
3942 {
3943 result = participant->mPTT;
3944 }
3945
3946 return result;
3947}
3948
3949BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) 5878BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
3950{ 5879{
3951 BOOL result = FALSE; 5880 BOOL result = FALSE;
@@ -3977,144 +5906,841 @@ F32 LLVoiceClient::getUserVolume(const LLUUID& id)
3977 5906
3978void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) 5907void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
3979{ 5908{
5909 if(mAudioSession)
5910 {
5911 participantState *participant = findParticipantByID(id);
5912 if (participant)
5913 {
5914 // volume can amplify by as much as 4x!
5915 S32 ivol = (S32)(400.f * volume * volume);
5916 participant->mUserVolume = llclamp(ivol, 0, 400);
5917 participant->mVolumeDirty = TRUE;
5918 mAudioSession->mVolumeDirty = TRUE;
5919 }
5920 }
5921}
5922
5923std::string LLVoiceClient::getGroupID(const LLUUID& id)
5924{
5925 std::string result;
5926
3980 participantState *participant = findParticipantByID(id); 5927 participantState *participant = findParticipantByID(id);
3981 if (participant) 5928 if(participant)
3982 { 5929 {
3983 // volume can amplify by as much as 4x! 5930 result = participant->mGroupID;
3984 S32 ivol = (S32)(400.f * volume * volume);
3985 participant->mUserVolume = llclamp(ivol, 0, 400);
3986 participant->mVolumeDirty = TRUE;
3987 mVolumeDirty = TRUE;
3988 } 5931 }
5932
5933 return result;
3989} 5934}
3990 5935
5936BOOL LLVoiceClient::getAreaVoiceDisabled()
5937{
5938 return mAreaVoiceDisabled;
5939}
5940
5941void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
5942{
5943// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
5944
5945 if(!mMainSessionGroupHandle.empty())
5946 {
5947 std::ostringstream stream;
5948 stream
5949 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
5950 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
5951 << "<RecordingControlType>Start</RecordingControlType>"
5952 << "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
5953 << "<Filename>" << "" << "</Filename>"
5954 << "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
5955 << "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>"
5956 << "</Request>\n\n\n";
3991 5957
3992 5958
3993LLVoiceClient::serviceType LLVoiceClient::getServiceType(const LLUUID& id) 5959 writeString(stream.str());
5960 }
5961}
5962
5963void LLVoiceClient::recordingLoopSave(const std::string& filename)
3994{ 5964{
3995 serviceType result = serviceTypeUnknown; 5965// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL;
3996 5966
3997 participantState *participant = findParticipantByID(id); 5967 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
3998 if(participant) 5968 {
5969 std::ostringstream stream;
5970 stream
5971 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
5972 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
5973 << "<RecordingControlType>Flush</RecordingControlType>"
5974 << "<Filename>" << filename << "</Filename>"
5975 << "</Request>\n\n\n";
5976
5977 writeString(stream.str());
5978 }
5979}
5980
5981void LLVoiceClient::recordingStop()
5982{
5983// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL;
5984
5985 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
5986 {
5987 std::ostringstream stream;
5988 stream
5989 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
5990 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
5991 << "<RecordingControlType>Stop</RecordingControlType>"
5992 << "</Request>\n\n\n";
5993
5994 writeString(stream.str());
5995 }
5996}
5997
5998void LLVoiceClient::filePlaybackStart(const std::string& filename)
5999{
6000// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL;
6001
6002 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
6003 {
6004 std::ostringstream stream;
6005 stream
6006 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
6007 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
6008 << "<RecordingControlType>Start</RecordingControlType>"
6009 << "<Filename>" << filename << "</Filename>"
6010 << "</Request>\n\n\n";
6011
6012 writeString(stream.str());
6013 }
6014}
6015
6016void LLVoiceClient::filePlaybackStop()
6017{
6018// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL;
6019
6020 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
3999 { 6021 {
4000 result = participant->mServiceType; 6022 std::ostringstream stream;
6023 stream
6024 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
6025 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
6026 << "<RecordingControlType>Stop</RecordingControlType>"
6027 << "</Request>\n\n\n";
6028
6029 writeString(stream.str());
6030 }
6031}
6032
6033void LLVoiceClient::filePlaybackSetPaused(bool paused)
6034{
6035 // TODO: Implement once Vivox gives me a sample
6036}
6037
6038void LLVoiceClient::filePlaybackSetMode(bool vox, float speed)
6039{
6040 // TODO: Implement once Vivox gives me a sample
6041}
6042
6043LLVoiceClient::sessionState::sessionState() :
6044 mMediaStreamState(streamStateUnknown),
6045 mTextStreamState(streamStateUnknown),
6046 mCreateInProgress(false),
6047 mMediaConnectInProgress(false),
6048 mVoiceInvitePending(false),
6049 mTextInvitePending(false),
6050 mSynthesizedCallerID(false),
6051 mIsChannel(false),
6052 mIsSpatial(false),
6053 mIsP2P(false),
6054 mIncoming(false),
6055 mVoiceEnabled(false),
6056 mReconnect(false),
6057 mVolumeDirty(false),
6058 mParticipantsChanged(false)
6059{
6060}
6061
6062LLVoiceClient::sessionState::~sessionState()
6063{
6064 removeAllParticipants();
6065}
6066
6067LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void)
6068{
6069 return mSessions.begin();
6070}
6071
6072LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void)
6073{
6074 return mSessions.end();
6075}
6076
6077
6078LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle)
6079{
6080 sessionState *result = NULL;
6081 sessionMap::iterator iter = mSessionsByHandle.find(&handle);
6082 if(iter != mSessionsByHandle.end())
6083 {
6084 result = iter->second;
4001 } 6085 }
4002 6086
4003 return result; 6087 return result;
4004} 6088}
4005 6089
4006std::string LLVoiceClient::getGroupID(const LLUUID& id) 6090LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
6091{
6092 sessionState *result = NULL;
6093 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6094 {
6095 sessionState *session = *iter;
6096 if(session->mCreateInProgress && (session->mSIPURI == uri))
6097 {
6098 result = session;
6099 break;
6100 }
6101 }
6102
6103 return result;
6104}
6105
6106LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id)
4007{ 6107{
4008 std::string result; 6108 sessionState *result = NULL;
6109
6110 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6111 {
6112 sessionState *session = *iter;
6113 if(session->mCallerID == participant_id)
6114 {
6115 result = session;
6116 break;
6117 }
6118 }
6119
6120 return result;
6121}
4009 6122
4010 participantState *participant = findParticipantByID(id); 6123LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle)
4011 if(participant) 6124{
6125 sessionState *result = NULL;
6126
6127 if(handle.empty())
4012 { 6128 {
4013 result = participant->mGroupID; 6129 // No handle supplied.
6130 // Check whether there's already a session with this URI
6131 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6132 {
6133 sessionState *s = *iter;
6134 if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
6135 {
6136 // TODO: I need to think about this logic... it's possible that this case should raise an internal error.
6137 result = s;
6138 break;
6139 }
6140 }
6141 }
6142 else // (!handle.empty())
6143 {
6144 // Check for an existing session with this handle
6145 sessionMap::iterator iter = mSessionsByHandle.find(&handle);
6146
6147 if(iter != mSessionsByHandle.end())
6148 {
6149 result = iter->second;
6150 }
6151 }
6152
6153 if(!result)
6154 {
6155 // No existing session found.
6156
6157 LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
6158 result = new sessionState();
6159 result->mSIPURI = uri;
6160 result->mHandle = handle;
6161
6162 mSessions.insert(result);
6163
6164 if(!result->mHandle.empty())
6165 {
6166 mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result));
6167 }
6168 }
6169 else
6170 {
6171 // Found an existing session
6172
6173 if(uri != result->mSIPURI)
6174 {
6175 // TODO: Should this be an internal error?
6176 LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL;
6177 setSessionURI(result, uri);
6178 }
6179
6180 if(handle != result->mHandle)
6181 {
6182 // TODO: Should this be an internal error?
6183 LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL;
6184 setSessionHandle(result, handle);
6185 }
6186
6187 LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
6188 }
6189
6190 verifySessionState();
6191
6192 return result;
6193}
6194
6195void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
6196{
6197 // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
6198
6199 if(!session->mHandle.empty())
6200 {
6201 // Remove session from the map if it should have been there.
6202 sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
6203 if(iter != mSessionsByHandle.end())
6204 {
6205 if(iter->second != session)
6206 {
6207 LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL;
6208 }
6209
6210 mSessionsByHandle.erase(iter);
6211 }
6212 else
6213 {
6214 LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL;
6215 }
6216 }
6217
6218 session->mHandle = handle;
6219
6220 if(!handle.empty())
6221 {
6222 mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session));
6223 }
6224
6225 verifySessionState();
6226}
6227
6228void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
6229{
6230 // There used to be a map of session URIs to sessions, which made this complex....
6231 session->mSIPURI = uri;
6232
6233 verifySessionState();
6234}
6235
6236void LLVoiceClient::deleteSession(sessionState *session)
6237{
6238 // Remove the session from the handle map
6239 if(!session->mHandle.empty())
6240 {
6241 sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
6242 if(iter != mSessionsByHandle.end())
6243 {
6244 if(iter->second != session)
6245 {
6246 LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL
6247 }
6248 mSessionsByHandle.erase(iter);
6249 }
6250 }
6251
6252 // Remove the session from the URI map
6253 mSessions.erase(session);
6254
6255 // At this point, the session should be unhooked from all lists and all state should be consistent.
6256 verifySessionState();
6257
6258 // If this is the current audio session, clean up the pointer which will soon be dangling.
6259 if(mAudioSession == session)
6260 {
6261 mAudioSession = NULL;
6262 mAudioSessionChanged = true;
6263 }
6264
6265 // ditto for the next audio session
6266 if(mNextAudioSession == session)
6267 {
6268 mNextAudioSession = NULL;
6269 }
6270
6271 // delete the session
6272 delete session;
6273}
6274
6275void LLVoiceClient::deleteAllSessions()
6276{
6277 LL_DEBUGS("Voice") << "called" << LL_ENDL;
6278
6279 while(!mSessions.empty())
6280 {
6281 deleteSession(*(sessionsBegin()));
6282 }
6283
6284 if(!mSessionsByHandle.empty())
6285 {
6286 LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL
6287 }
6288}
6289
6290void LLVoiceClient::verifySessionState(void)
6291{
6292 // This is mostly intended for debugging problems with session state management.
6293 LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL;
6294
6295 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6296 {
6297 sessionState *session = *iter;
6298
6299 LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
6300
6301 if(!session->mHandle.empty())
6302 {
6303 // every session with a non-empty handle needs to be in the handle map
6304 sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle));
6305 if(i2 == mSessionsByHandle.end())
6306 {
6307 LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL;
6308 }
6309 else
6310 {
6311 if(i2->second != session)
6312 {
6313 LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL;
6314 }
6315 }
6316 }
6317 }
6318
6319 // check that every entry in the handle map points to a valid session in the session set
6320 for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
6321 {
6322 sessionState *session = iter->second;
6323 sessionIterator i2 = mSessions.find(session);
6324 if(i2 == mSessions.end())
6325 {
6326 LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL;
6327 }
6328 else
6329 {
6330 if(session->mHandle != (*i2)->mHandle)
6331 {
6332 LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL;
6333 }
6334 }
6335 }
6336}
6337
6338LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) :
6339 mURI(uri)
6340{
6341 mOnlineSL = false;
6342 mOnlineSLim = false;
6343 mCanSeeMeOnline = true;
6344 mHasBlockListEntry = false;
6345 mHasAutoAcceptListEntry = false;
6346 mNameResolved = false;
6347 mInVivoxBuddies = false;
6348 mInSLFriends = false;
6349 mNeedsNameUpdate = false;
6350}
6351
6352void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName)
6353{
6354 buddyListEntry *buddy = addBuddy(uri, displayName);
6355 buddy->mInVivoxBuddies = true;
6356}
6357
6358LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri)
6359{
6360 std::string empty;
6361 buddyListEntry *buddy = addBuddy(uri, empty);
6362 if(buddy->mDisplayName.empty())
6363 {
6364 buddy->mNameResolved = false;
6365 }
6366 return buddy;
6367}
6368
6369LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName)
6370{
6371 buddyListEntry *result = NULL;
6372 buddyListMap::iterator iter = mBuddyListMap.find(&uri);
6373
6374 if(iter != mBuddyListMap.end())
6375 {
6376 // Found a matching buddy already in the map.
6377 LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL;
6378 result = iter->second;
6379 }
6380
6381 if(!result)
6382 {
6383 // participant isn't already in one list or the other.
6384 LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL;
6385 result = new buddyListEntry(uri);
6386 result->mDisplayName = displayName;
6387
6388 if(IDFromName(uri, result->mUUID))
6389 {
6390 // Extracted UUID from name successfully.
6391 }
6392 else
6393 {
6394 LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL;
6395 }
6396
6397 mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result));
4014 } 6398 }
4015 6399
4016 return result; 6400 return result;
4017} 6401}
4018 6402
4019BOOL LLVoiceClient::getAreaVoiceDisabled() 6403LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri)
4020{ 6404{
4021 return mAreaVoiceDisabled; 6405 buddyListEntry *result = NULL;
6406 buddyListMap::iterator iter = mBuddyListMap.find(&uri);
6407 if(iter != mBuddyListMap.end())
6408 {
6409 result = iter->second;
6410 }
6411
6412 return result;
6413}
6414
6415LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id)
6416{
6417 buddyListEntry *result = NULL;
6418 buddyListMap::iterator iter;
6419
6420 for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
6421 {
6422 if(iter->second->mUUID == id)
6423 {
6424 result = iter->second;
6425 break;
6426 }
6427 }
6428
6429 return result;
6430}
6431
6432LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name)
6433{
6434 buddyListEntry *result = NULL;
6435 buddyListMap::iterator iter;
6436
6437 for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
6438 {
6439 if(iter->second->mDisplayName == name)
6440 {
6441 result = iter->second;
6442 break;
6443 }
6444 }
6445
6446 return result;
6447}
6448
6449void LLVoiceClient::deleteBuddy(const std::string &uri)
6450{
6451 buddyListMap::iterator iter = mBuddyListMap.find(&uri);
6452 if(iter != mBuddyListMap.end())
6453 {
6454 LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL;
6455 buddyListEntry *buddy = iter->second;
6456 mBuddyListMap.erase(iter);
6457 delete buddy;
6458 }
6459 else
6460 {
6461 LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL;
6462 }
6463
6464}
6465
6466void LLVoiceClient::deleteAllBuddies(void)
6467{
6468 while(!mBuddyListMap.empty())
6469 {
6470 deleteBuddy(*(mBuddyListMap.begin()->first));
6471 }
6472
6473 // Don't want to correlate with friends list when we've emptied the buddy list.
6474 mBuddyListMapPopulated = false;
6475
6476 // Don't want to correlate with friends list when we've reset the block rules.
6477 mBlockRulesListReceived = false;
6478 mAutoAcceptRulesListReceived = false;
6479}
6480
6481void LLVoiceClient::deleteAllBlockRules(void)
6482{
6483 // Clear the block list entry flags from all local buddy list entries
6484 buddyListMap::iterator buddy_it;
6485 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
6486 {
6487 buddy_it->second->mHasBlockListEntry = false;
6488 }
6489}
6490
6491void LLVoiceClient::deleteAllAutoAcceptRules(void)
6492{
6493 // Clear the auto-accept list entry flags from all local buddy list entries
6494 buddyListMap::iterator buddy_it;
6495 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
6496 {
6497 buddy_it->second->mHasAutoAcceptListEntry = false;
6498 }
6499}
6500
6501void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly)
6502{
6503 buddyListEntry *buddy = NULL;
6504
6505 // blockMask is the SIP URI of a friends list entry
6506 buddyListMap::iterator iter = mBuddyListMap.find(&blockMask);
6507 if(iter != mBuddyListMap.end())
6508 {
6509 LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL;
6510 buddy = iter->second;
6511 }
6512
6513 if(buddy == NULL)
6514 {
6515 LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL;
6516 buddy = addBuddy(blockMask);
6517 }
6518
6519 if(buddy != NULL)
6520 {
6521 buddy->mHasBlockListEntry = true;
6522 }
6523}
6524
6525void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy)
6526{
6527 buddyListEntry *buddy = NULL;
6528
6529 // blockMask is the SIP URI of a friends list entry
6530 buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask);
6531 if(iter != mBuddyListMap.end())
6532 {
6533 LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL;
6534 buddy = iter->second;
6535 }
6536
6537 if(buddy == NULL)
6538 {
6539 LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL;
6540 buddy = addBuddy(autoAcceptMask);
6541 }
6542
6543 if(buddy != NULL)
6544 {
6545 buddy->mHasAutoAcceptListEntry = true;
6546 }
6547}
6548
6549void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
6550{
6551 // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done.
6552 mBlockRulesListReceived = true;
6553}
6554
6555void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString)
6556{
6557 // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done.
6558 mAutoAcceptRulesListReceived = true;
4022} 6559}
4023 6560
4024void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) 6561void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
4025{ 6562{
4026 mObservers.insert(observer); 6563 mParticipantObservers.insert(observer);
4027} 6564}
4028 6565
4029void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) 6566void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
4030{ 6567{
4031 mObservers.erase(observer); 6568 mParticipantObservers.erase(observer);
4032} 6569}
4033 6570
4034void LLVoiceClient::notifyObservers() 6571void LLVoiceClient::notifyParticipantObservers()
4035{ 6572{
4036 for (observer_set_t::iterator it = mObservers.begin(); 6573 for (observer_set_t::iterator it = mParticipantObservers.begin();
4037 it != mObservers.end(); 6574 it != mParticipantObservers.end();
4038 ) 6575 )
4039 { 6576 {
4040 LLVoiceClientParticipantObserver* observer = *it; 6577 LLVoiceClientParticipantObserver* observer = *it;
4041 observer->onChange(); 6578 observer->onChange();
4042 // In case onChange() deleted an entry. 6579 // In case onChange() deleted an entry.
4043 it = mObservers.upper_bound(observer); 6580 it = mParticipantObservers.upper_bound(observer);
4044 } 6581 }
4045} 6582}
4046 6583
4047void LLVoiceClient::addStatusObserver(LLVoiceClientStatusObserver* observer) 6584void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
4048{ 6585{
4049 mStatusObservers.insert(observer); 6586 mStatusObservers.insert(observer);
4050} 6587}
4051 6588
4052void LLVoiceClient::removeStatusObserver(LLVoiceClientStatusObserver* observer) 6589void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
4053{ 6590{
4054 mStatusObservers.erase(observer); 6591 mStatusObservers.erase(observer);
4055} 6592}
4056 6593
4057void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) 6594void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
4058{ 6595{
4059 if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) 6596 if(mAudioSession)
4060 { 6597 {
4061 switch(mVivoxErrorStatusCode) 6598 if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
4062 { 6599 {
4063 case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; 6600 switch(mAudioSession->mErrorStatusCode)
4064 case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; 6601 {
4065 case 20715: 6602 case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break;
4066 //invalid channel, we may be using a set of poorly cached 6603 case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break;
4067 //info 6604 case 20715:
4068 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; 6605 //invalid channel, we may be using a set of poorly cached
4069 break; 6606 //info
4070 case 1009: 6607 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
4071 //invalid username and password 6608 break;
4072 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; 6609 case 1009:
4073 break; 6610 //invalid username and password
4074 } 6611 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
4075 6612 break;
4076 // Reset the error code to make sure it won't be reused later by accident. 6613 }
4077 mVivoxErrorStatusCode = 0;
4078 }
4079
4080 if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
4081 //NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
4082 && (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408))
4083 {
4084 // call failed because other user was not available
4085 // treat this as an error case
4086 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
4087 6614
4088 // Reset the error code to make sure it won't be reused later by accident. 6615 // Reset the error code to make sure it won't be reused later by accident.
4089 mVivoxErrorStatusCode = 0; 6616 mAudioSession->mErrorStatusCode = 0;
6617 }
6618 else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
6619 {
6620 switch(mAudioSession->mErrorStatusCode)
6621 {
6622 case 404: // NOT_FOUND
6623 case 480: // TEMPORARILY_UNAVAILABLE
6624 case 408: // REQUEST_TIMEOUT
6625 // call failed because other user was not available
6626 // treat this as an error case
6627 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
6628
6629 // Reset the error code to make sure it won't be reused later by accident.
6630 mAudioSession->mErrorStatusCode = 0;
6631 break;
6632 }
6633 }
4090 } 6634 }
4091 6635
4092 LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) << ", session URI " << mSessionURI << LL_ENDL; 6636 LL_DEBUGS("Voice")
6637 << " " << LLVoiceClientStatusObserver::status2string(status)
6638 << ", session URI " << getAudioSessionURI()
6639 << (inSpatialChannel()?", proximal is true":", proximal is false")
6640 << LL_ENDL;
4093 6641
4094 for (status_observer_set_t::iterator it = mStatusObservers.begin(); 6642 for (status_observer_set_t::iterator it = mStatusObservers.begin();
4095 it != mStatusObservers.end(); 6643 it != mStatusObservers.end();
4096 ) 6644 )
4097 { 6645 {
4098 LLVoiceClientStatusObserver* observer = *it; 6646 LLVoiceClientStatusObserver* observer = *it;
4099 observer->onChange(status, mSessionURI, !mNonSpatialChannel); 6647 observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
4100 // In case onError() deleted an entry. 6648 // In case onError() deleted an entry.
4101 it = mStatusObservers.upper_bound(observer); 6649 it = mStatusObservers.upper_bound(observer);
4102 } 6650 }
4103 6651
4104} 6652}
4105 6653
6654void LLVoiceClient::addObserver(LLFriendObserver* observer)
6655{
6656 mFriendObservers.insert(observer);
6657}
6658
6659void LLVoiceClient::removeObserver(LLFriendObserver* observer)
6660{
6661 mFriendObservers.erase(observer);
6662}
6663
6664void LLVoiceClient::notifyFriendObservers()
6665{
6666 for (friend_observer_set_t::iterator it = mFriendObservers.begin();
6667 it != mFriendObservers.end();
6668 )
6669 {
6670 LLFriendObserver* observer = *it;
6671 it++;
6672 // The only friend-related thing we notify on is online/offline transitions.
6673 observer->changed(LLFriendObserver::ONLINE);
6674 }
6675}
6676
6677void LLVoiceClient::lookupName(const LLUUID &id)
6678{
6679 gCacheName->getName(id, onAvatarNameLookup);
6680}
6681
4106//static 6682//static
4107// void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data) 6683void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data)
4108// { 6684{
4109// participantState* statep = gVoiceClient->findParticipantByID(id); 6685 if(gVoiceClient)
6686 {
6687 std::string name = llformat("%s %s", first.c_str(), last.c_str());
6688 gVoiceClient->avatarNameResolved(id, name);
6689 }
6690}
4110 6691
4111// if (statep) 6692void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
4112// { 6693{
4113// statep->mDisplayName = first + " " + last; 6694 // If the avatar whose name just resolved is on our friends list, resync the friends list.
4114// } 6695 if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL)
6696 {
6697 mFriendsListDirty = true;
6698 }
4115 6699
4116// gVoiceClient->notifyObservers(); 6700 // Iterate over all sessions.
4117// } 6701 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6702 {
6703 sessionState *session = *iter;
6704
6705 // Check for this user as a participant in this session
6706 participantState *participant = session->findParticipantByID(id);
6707 if(participant)
6708 {
6709 // Found -- fill in the name
6710 participant->mAccountName = name;
6711 // and post a "participants updated" message to listeners later.
6712 session->mParticipantsChanged = true;
6713 }
6714
6715 // Check whether this is a p2p session whose caller name just resolved
6716 if(session->mCallerID == id)
6717 {
6718 // this session's "caller ID" just resolved. Fill in the name.
6719 session->mName = name;
6720 if(session->mTextInvitePending)
6721 {
6722 session->mTextInvitePending = false;
6723
6724 // We don't need to call gIMMgr->addP2PSession() here. The first incoming message will create the panel.
6725 }
6726 if(session->mVoiceInvitePending)
6727 {
6728 session->mVoiceInvitePending = false;
6729
6730 gIMMgr->inviteToSession(
6731 session->mIMSessionID,
6732 session->mName,
6733 session->mCallerID,
6734 session->mName,
6735 IM_SESSION_P2P_INVITE,
6736 LLIMMgr::INVITATION_TYPE_VOICE,
6737 session->mHandle,
6738 session->mSIPURI);
6739 }
6740
6741 }
6742 }
6743}
4118 6744
4119class LLViewerParcelVoiceInfo : public LLHTTPNode 6745class LLViewerParcelVoiceInfo : public LLHTTPNode
4120{ 6746{