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