summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoxanne Skelly <roxie@lindenlab.com>2024-03-13 11:33:13 -0700
committerGitHub <noreply@github.com>2024-03-13 11:33:13 -0700
commitb32364ba49ab58656fc6b146e5db499022dc9512 (patch)
tree20b774948f8592e0834b9b55af79180463b06e58
parentb242d696ba03f66494dfca1f05a74ed1b15cd6e4 (diff)
parent76460e8c2c33af185ae8641775d5673fcdab0759 (diff)
Merge pull request #116 from secondlife/roxie/webrtc-voice
WebRTC-based voice for Second Life
-rw-r--r--autobuild.xml56
-rwxr-xr-xetc/message.xml10
-rw-r--r--indra/CMakeLists.txt1
-rw-r--r--indra/cmake/00-Common.cmake4
-rw-r--r--indra/cmake/CMakeLists.txt1
-rw-r--r--indra/cmake/WebRTC.cmake32
-rw-r--r--indra/llmath/llquaternion.h1
-rw-r--r--indra/llwebrtc/CMakeLists.txt60
-rw-r--r--indra/llwebrtc/llwebrtc.cpp1213
-rw-r--r--indra/llwebrtc/llwebrtc.h235
-rw-r--r--indra/llwebrtc/llwebrtc_impl.h364
-rw-r--r--indra/newview/CMakeLists.txt8
-rw-r--r--indra/newview/app_settings/settings.xml2
-rw-r--r--indra/newview/llagent.cpp6
-rw-r--r--indra/newview/llappviewer.cpp10
-rw-r--r--indra/newview/llappviewer.h1
-rw-r--r--indra/newview/llavataractions.cpp7
-rw-r--r--indra/newview/llconversationview.cpp2
-rw-r--r--indra/newview/llfloaterimsession.cpp2
-rw-r--r--indra/newview/llfloaterimsession.h3
-rw-r--r--indra/newview/llgroupactions.cpp2
-rw-r--r--indra/newview/llimview.cpp321
-rw-r--r--indra/newview/llimview.h27
-rw-r--r--indra/newview/llpanelgroup.cpp2
-rw-r--r--indra/newview/llpanelgroup.h2
-rw-r--r--indra/newview/llpanelpeople.cpp2
-rw-r--r--indra/newview/llpanelpeople.h2
-rw-r--r--indra/newview/llpanelprofile.cpp2
-rw-r--r--indra/newview/llpanelprofile.h2
-rw-r--r--indra/newview/llpanelvoicedevicesettings.cpp67
-rw-r--r--indra/newview/llstartup.cpp2
-rw-r--r--indra/newview/llviewerhelp.cpp2
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--indra/newview/llviewerregion.h3
-rw-r--r--indra/newview/llvoavatar.cpp12
-rw-r--r--indra/newview/llvoavatar.h4
-rw-r--r--indra/newview/llvoicechannel.cpp317
-rw-r--r--indra/newview/llvoicechannel.h99
-rw-r--r--indra/newview/llvoiceclient.cpp719
-rw-r--r--indra/newview/llvoiceclient.h253
-rw-r--r--indra/newview/llvoicevivox.cpp1430
-rw-r--r--indra/newview/llvoicevivox.h455
-rw-r--r--indra/newview/llvoicewebrtc.cpp2885
-rw-r--r--indra/newview/llvoicewebrtc.h745
-rwxr-xr-xindra/newview/viewer_manifest.py20
-rwxr-xr-xscripts/messages/message_template.msg.sha12
46 files changed, 7426 insertions, 1970 deletions
diff --git a/autobuild.xml b/autobuild.xml
index effafa31d3..985a40695d 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2946,6 +2946,62 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>version</key>
<string>1</string>
</map>
+ <key>webrtc-shim</key>
+ <map>
+ <key>platforms</key>
+ <map>
+ <key>darwin64</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>creds</key>
+ <string>github</string>
+ <key>hash</key>
+ <string>a23ffe29c49f8fabb8c5f2de9879bed9d7e0e0ca</string>
+ <key>hash_algorithm</key>
+ <string>sha1</string>
+ <key>url</key>
+ <string>https://api.github.com/repos/secondlife/3p-webrtc-shim/releases/assets/155507516</string>
+ </map>
+ <key>name</key>
+ <string>darwin64</string>
+ </map>
+ <key>windows64</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>creds</key>
+ <string>github</string>
+ <key>hash</key>
+ <string>b264c6ed008bd45c4687c5dc4d7532727c74624a</string>
+ <key>hash_algorithm</key>
+ <string>sha1</string>
+ <key>url</key>
+ <string>https://api.github.com/repos/secondlife/3p-webrtc-shim/releases/assets/155507520</string>
+ </map>
+ <key>name</key>
+ <string>windows64</string>
+ </map>
+ </map>
+ <key>license</key>
+ <string>MIT</string>
+ <key>license_file</key>
+ <string>LICENSES/webrtc-license.txt</string>
+ <key>copyright</key>
+ <string>Copyright (c) 2011, The WebRTC project authors. All rights reserved.</string>
+ <key>version</key>
+ <string>m114.0.26.d4b8b92</string>
+ <key>name</key>
+ <string>webrtc-shim</string>
+ <key>vcs_branch</key>
+ <string>main</string>
+ <key>vcs_revision</key>
+ <string>d4b8b921825ae4344d87fdb0c9179c358c6e3698</string>
+ <key>vcs_url</key>
+ <string>https://github.com/secondlife/3p-webrtc-shim</string>
+ <key>canonical_repo</key>
+ <string>https://github.com/secondlife/3p-webrtc-shim</string>
+ </map>
<key>xxhash</key>
<map>
<key>platforms</key>
diff --git a/etc/message.xml b/etc/message.xml
index b444fe6c11..dee3fd72dd 100755
--- a/etc/message.xml
+++ b/etc/message.xml
@@ -506,6 +506,13 @@
<boolean>false</boolean>
</map>
+ <key>VoiceSignalingRequest</key>
+ <map>
+ <key>flavor</key>
+ <string>llsd</string>
+ <key>trusted-sender</key>
+ <boolean>false</boolean>
+ </map>
<!-- Server to client -->
<key>RequiredVoiceVersion</key>
<map>
@@ -688,6 +695,9 @@
<key>ProvisionVoiceAccountRequest</key>
<boolean>false</boolean>
+
+ <key>VoiceSignalingRequest</key>
+ <boolean>false</boolean>
<key>RemoteParcelRequest</key>
<boolean>false</boolean>
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 500ffa3e8b..422927704a 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -54,6 +54,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llmessage)
add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive)
add_subdirectory(${LIBS_OPEN_PREFIX}llrender)
add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem)
+add_subdirectory(${LIBS_OPEN_PREFIX}llwebrtc)
add_subdirectory(${LIBS_OPEN_PREFIX}llwindow)
add_subdirectory(${LIBS_OPEN_PREFIX}llxml)
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 24534c98d9..26a4162e42 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -182,6 +182,10 @@ if (LINUX OR DARWIN)
list(APPEND GCC_WARNINGS -Wno-reorder -Wno-non-virtual-dtor )
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13)
+ list(APPEND GCC_WARNINGS -Wno-unused-but-set-variable -Wno-unused-variable )
+ endif()
+
add_compile_options(${GCC_WARNINGS})
add_compile_options(-m${ADDRESS_SIZE})
endif (LINUX OR DARWIN)
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 05c51c018d..cce1c042bd 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -64,6 +64,7 @@ set(cmake_SOURCE_FILES
ViewerMiscLibs.cmake
VisualLeakDetector.cmake
LibVLCPlugin.cmake
+ WebRTC.cmake
XmlRpcEpi.cmake
xxHash.cmake
ZLIBNG.cmake
diff --git a/indra/cmake/WebRTC.cmake b/indra/cmake/WebRTC.cmake
new file mode 100644
index 0000000000..909a1345ed
--- /dev/null
+++ b/indra/cmake/WebRTC.cmake
@@ -0,0 +1,32 @@
+# -*- cmake -*-
+include(Linking)
+include(Prebuilt)
+
+include_guard()
+
+add_library( ll::webrtc INTERFACE IMPORTED )
+target_include_directories( ll::webrtc SYSTEM INTERFACE "${LIBS_PREBUILT_DIR}/include/webrtc" "${LIBS_PREBUILT_DIR}/include/webrtc/third_party/abseil-cpp")
+use_prebuilt_binary(webrtc-shim)
+
+if (WINDOWS)
+ target_link_libraries( ll::webrtc INTERFACE webrtc.lib )
+elseif (DARWIN)
+ FIND_LIBRARY(COREAUDIO_LIBRARY CoreAudio)
+ FIND_LIBRARY(COREGRAPHICS_LIBRARY CoreGraphics)
+ FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY AudioToolbox)
+ FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation)
+ FIND_LIBRARY(COCOA_LIBRARY Cocoa)
+
+ target_link_libraries( ll::webrtc INTERFACE
+ libwebrtc.a
+ ${COREAUDIO_LIBRARY}
+ ${AUDIOTOOLBOX_LIBRARY}
+ ${COREGRAPHICS_LIBRARY}
+ ${COREFOUNDATION_LIBRARY}
+ ${COCOA_LIBRARY}
+ )
+elseif (LINUX)
+ target_link_libraries( ll::webrtc INTERFACE libwebrtc )
+endif (WINDOWS)
+
+
diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h
index 51ce163b4e..aaa868352a 100644
--- a/indra/llmath/llquaternion.h
+++ b/indra/llmath/llquaternion.h
@@ -132,6 +132,7 @@ public:
friend LLQuaternion operator~(const LLQuaternion &a); // Returns a* (Conjugate of a)
bool operator==(const LLQuaternion &b) const; // Returns a == b
bool operator!=(const LLQuaternion &b) const; // Returns a != b
+ F64 operator[](int idx) const { return mQ[idx]; }
friend const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b); // Returns a * b
diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt
new file mode 100644
index 0000000000..29dc1df8d6
--- /dev/null
+++ b/indra/llwebrtc/CMakeLists.txt
@@ -0,0 +1,60 @@
+# -*- cmake -*-
+
+# some webrtc headers require C++ 20
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+include(00-Common)
+include(Linking)
+include(WebRTC)
+
+project(llwebrtc)
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
+
+
+set(llwebrtc_SOURCE_FILES
+ llwebrtc.cpp
+ )
+
+set(llwebrtc_HEADER_FILES
+ CMakeLists.txt
+ llwebrtc.h
+ llwebrtc_impl.h
+ )
+
+list(APPEND llwebrtc_SOURCE_FILES ${llwebrtc_HEADER_FILES})
+
+add_library (llwebrtc SHARED ${llwebrtc_SOURCE_FILES})
+
+set_target_properties(llwebrtc PROPERTIES PUBLIC_HEADER llwebrtc.h)
+
+if (WINDOWS)
+ target_link_libraries(llwebrtc PRIVATE ll::webrtc
+ secur32
+ winmm
+ dmoguids
+ wmcodecdspuuid
+ msdmo
+ strmiids
+ iphlpapi)
+elseif (DARWIN)
+ target_link_libraries(llwebrtc PRIVATE ll::webrtc)
+elseif (LINUX)
+ target_link_libraries(llwebrtc PRIVATE ll::webrtc)
+endif (WINDOWS)
+
+target_include_directories( llwebrtc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+
+if (WINDOWS)
+ set_property(TARGET llwebrtc PROPERTY
+ MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
+endif (WINDOWS)
+
+ADD_CUSTOM_COMMAND(TARGET llwebrtc POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ $<TARGET_FILE:llwebrtc>
+ ${SHARED_LIB_STAGING_DIR})
+# Add tests
+if (LL_TESTS)
+endif (LL_TESTS)
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
new file mode 100644
index 0000000000..875f233e65
--- /dev/null
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -0,0 +1,1213 @@
+/**
+ * @file llwebrtc.cpp
+ * @brief WebRTC interface implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llwebrtc_impl.h"
+#include <algorithm>
+#include <format>
+#include <string.h>
+
+#include "api/audio_codecs/audio_decoder_factory.h"
+#include "api/audio_codecs/audio_encoder_factory.h"
+#include "api/audio_codecs/builtin_audio_decoder_factory.h"
+#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/media_stream_interface.h"
+#include "api/media_stream_track.h"
+#include "modules/audio_processing/audio_buffer.h"
+
+namespace llwebrtc
+{
+
+LLAudioDeviceObserver::LLAudioDeviceObserver() : mSumVector {0}, mMicrophoneEnergy(0.0) {}
+
+float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; }
+
+// TODO: Pull smoothing/filtering code into a common helper function
+// for LLAudioDeviceObserver and LLCustomProcessor
+
+void LLAudioDeviceObserver::OnCaptureData(const void *audio_samples,
+ const size_t num_samples,
+ const size_t bytes_per_sample,
+ const size_t num_channels,
+ const uint32_t samples_per_sec)
+{
+ // calculate the energy
+ float energy = 0;
+ const short *samples = (const short *) audio_samples;
+ for (size_t index = 0; index < num_samples * num_channels; index++)
+ {
+ float sample = (static_cast<float>(samples[index]) / (float) 32767);
+ energy += sample * sample;
+ }
+
+ // smooth it.
+ size_t buffer_size = sizeof(mSumVector) / sizeof(mSumVector[0]);
+ float totalSum = 0;
+ int i;
+ for (i = 0; i < (buffer_size - 1); i++)
+ {
+ mSumVector[i] = mSumVector[i + 1];
+ totalSum += mSumVector[i];
+ }
+ mSumVector[i] = energy;
+ totalSum += energy;
+ mMicrophoneEnergy = std::sqrt(totalSum / (num_samples * buffer_size));
+}
+
+void LLAudioDeviceObserver::OnRenderData(const void *audio_samples,
+ const size_t num_samples,
+ const size_t bytes_per_sample,
+ const size_t num_channels,
+ const uint32_t samples_per_sec)
+{
+}
+
+LLCustomProcessor::LLCustomProcessor() :
+ mSampleRateHz(0),
+ mNumChannels(0),
+ mMicrophoneEnergy(0.0)
+{
+ memset(mSumVector, 0, sizeof(mSumVector));
+}
+
+void LLCustomProcessor::Initialize(int sample_rate_hz, int num_channels)
+{
+ mSampleRateHz = sample_rate_hz;
+ mNumChannels = num_channels;
+ memset(mSumVector, 0, sizeof(mSumVector));
+}
+
+void LLCustomProcessor::Process(webrtc::AudioBuffer *audio_in)
+{
+ webrtc::StreamConfig stream_config;
+ stream_config.set_sample_rate_hz(mSampleRateHz);
+ stream_config.set_num_channels(mNumChannels);
+ std::vector<float *> frame;
+ std::vector<float> frame_samples;
+
+ if (audio_in->num_channels() < 1 || audio_in->num_frames() < 480)
+ {
+ return;
+ }
+
+ // grab the input audio
+ frame_samples.resize(stream_config.num_samples());
+ frame.resize(stream_config.num_channels());
+ for (size_t ch = 0; ch < stream_config.num_channels(); ++ch)
+ {
+ frame[ch] = &(frame_samples)[ch * stream_config.num_frames()];
+ }
+
+ audio_in->CopyTo(stream_config, &frame[0]);
+
+ // calculate the energy
+ float energy = 0;
+ for (size_t index = 0; index < stream_config.num_samples(); index++)
+ {
+ float sample = frame_samples[index];
+ energy += sample * sample;
+ }
+
+ // smooth it.
+ size_t buffer_size = sizeof(mSumVector) / sizeof(mSumVector[0]);
+ float totalSum = 0;
+ int i;
+ for (i = 0; i < (buffer_size - 1); i++)
+ {
+ mSumVector[i] = mSumVector[i + 1];
+ totalSum += mSumVector[i];
+ }
+ mSumVector[i] = energy;
+ totalSum += energy;
+ mMicrophoneEnergy = std::sqrt(totalSum / (stream_config.num_samples() * buffer_size));
+}
+
+//
+// LLWebRTCImpl implementation
+//
+
+LLWebRTCImpl::LLWebRTCImpl() :
+ mPeerCustomProcessor(nullptr),
+ mMute(true),
+ mPlayoutDevice(0),
+ mRecordingDevice(0),
+ mTuningAudioDeviceObserver(nullptr)
+{
+}
+
+void LLWebRTCImpl::init()
+{
+ RTC_DCHECK(mPeerConnectionFactory);
+ mPlayoutDevice = 0;
+ mRecordingDevice = 0;
+ rtc::InitializeSSL();
+
+ // Normal logging is rather spammy, so turn it off.
+ rtc::LogMessage::LogToDebug(rtc::LS_NONE);
+ rtc::LogMessage::SetLogToStderr(true);
+
+ mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory();
+
+ // Create the native threads.
+ mNetworkThread = rtc::Thread::CreateWithSocketServer();
+ mNetworkThread->SetName("WebRTCNetworkThread", nullptr);
+ mNetworkThread->Start();
+ mWorkerThread = rtc::Thread::Create();
+ mWorkerThread->SetName("WebRTCWorkerThread", nullptr);
+ mWorkerThread->Start();
+ mSignalingThread = rtc::Thread::Create();
+ mSignalingThread->SetName("WebRTCSignalingThread", nullptr);
+ mSignalingThread->Start();
+
+ mTuningAudioDeviceObserver = new LLAudioDeviceObserver;
+ mWorkerThread->PostTask(
+ [this]()
+ {
+ // Initialize the audio devices on the Worker Thread
+ mTuningDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(
+ webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio,
+ mTaskQueueFactory.get(),
+ std::unique_ptr<webrtc::AudioDeviceDataObserver>(mTuningAudioDeviceObserver));
+
+ mTuningDeviceModule->Init();
+ mTuningDeviceModule->SetStereoRecording(true);
+ mTuningDeviceModule->SetStereoPlayout(true);
+ mTuningDeviceModule->EnableBuiltInAEC(false);
+ mTuningDeviceModule->SetAudioDeviceSink(this);
+ updateDevices();
+ });
+
+ mWorkerThread->BlockingCall(
+ [this]()
+ {
+ // the peer device module doesn't need an observer
+ // as we pull peer data after audio processing.
+ mPeerDeviceModule =
+ webrtc::CreateAudioDeviceWithDataObserver(
+ webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio,
+ mTaskQueueFactory.get(),
+ nullptr);
+ mPeerDeviceModule->Init();
+ mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice);
+ mPeerDeviceModule->SetRecordingDevice(mRecordingDevice);
+ mPeerDeviceModule->SetStereoRecording(true);
+ mPeerDeviceModule->SetStereoPlayout(true);
+ mPeerDeviceModule->EnableBuiltInAEC(false);
+ mPeerDeviceModule->InitMicrophone();
+ mPeerDeviceModule->InitSpeaker();
+ mPeerDeviceModule->InitRecording();
+ mPeerDeviceModule->InitPlayout();
+ });
+
+ // The custom processor allows us to retrieve audio data (and levels)
+ // from after other audio processing such as AEC, AGC, etc.
+ mPeerCustomProcessor = new LLCustomProcessor;
+ webrtc::AudioProcessingBuilder apb;
+ apb.SetCapturePostProcessing(std::unique_ptr<webrtc::CustomProcessing>(mPeerCustomProcessor));
+ rtc::scoped_refptr<webrtc::AudioProcessing> apm = apb.Create();
+
+ // TODO: wire some of these to the primary interface and ultimately
+ // to the UI to allow user config.
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ apm_config.echo_canceller.mobile_mode = false;
+ apm_config.gain_controller1.enabled = true;
+ apm_config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog;
+ apm_config.gain_controller2.enabled = true;
+ apm_config.high_pass_filter.enabled = true;
+ apm_config.noise_suppression.enabled = true;
+ apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kVeryHigh;
+ apm_config.transient_suppression.enabled = true;
+ apm_config.pipeline.multi_channel_render = true;
+ apm_config.pipeline.multi_channel_capture = true;
+ apm_config.pipeline.multi_channel_capture = true;
+
+ webrtc::ProcessingConfig processing_config;
+ processing_config.input_stream().set_num_channels(2);
+ processing_config.input_stream().set_sample_rate_hz(8000);
+ processing_config.output_stream().set_num_channels(2);
+ processing_config.output_stream().set_sample_rate_hz(8000);
+ processing_config.reverse_input_stream().set_num_channels(2);
+ processing_config.reverse_input_stream().set_sample_rate_hz(48000);
+ processing_config.reverse_output_stream().set_num_channels(2);
+ processing_config.reverse_output_stream().set_sample_rate_hz(48000);
+
+ apm->Initialize(processing_config);
+ apm->ApplyConfig(apm_config);
+
+ mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(),
+ mWorkerThread.get(),
+ mSignalingThread.get(),
+ mPeerDeviceModule,
+ webrtc::CreateBuiltinAudioEncoderFactory(),
+ webrtc::CreateBuiltinAudioDecoderFactory(),
+ nullptr /* video_encoder_factory */,
+ nullptr /* video_decoder_factory */,
+ nullptr /* audio_mixer */,
+ apm);
+
+
+ mWorkerThread->BlockingCall(
+ [this]()
+ {
+ mPeerDeviceModule->StartPlayout();
+ });
+}
+
+void LLWebRTCImpl::terminate()
+{
+ for (auto &connection : mPeerConnections)
+ {
+ connection->terminate();
+ }
+ mPeerConnections.clear();
+
+ mSignalingThread->BlockingCall([this]() { mPeerConnectionFactory = nullptr; });
+ mWorkerThread->BlockingCall(
+ [this]()
+ {
+ if (mTuningDeviceModule)
+ {
+ mTuningDeviceModule->StopRecording();
+ mTuningDeviceModule->Terminate();
+ }
+ if (mPeerDeviceModule)
+ {
+ mPeerDeviceModule->StopRecording();
+ mPeerDeviceModule->Terminate();
+ }
+ mTuningDeviceModule = nullptr;
+ mPeerDeviceModule = nullptr;
+ mTaskQueueFactory = nullptr;
+ });
+}
+
+//
+// Devices functions
+//
+// Most device-related functionality needs to happen
+// on the worker thread (the audio thread,) so those calls will be
+// proxied over to that thread.
+//
+void LLWebRTCImpl::setRecording(bool recording)
+{
+ mWorkerThread->PostTask(
+ [this, recording]()
+ {
+ if (recording)
+ {
+ mPeerDeviceModule->StartRecording();
+ }
+ else
+ {
+ mPeerDeviceModule->StopRecording();
+ }
+ });
+}
+
+void LLWebRTCImpl::refreshDevices()
+{
+ mWorkerThread->PostTask([this]() { updateDevices(); });
+}
+
+void LLWebRTCImpl::setDevicesObserver(LLWebRTCDevicesObserver *observer) { mVoiceDevicesObserverList.emplace_back(observer); }
+
+void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer)
+{
+ std::vector<LLWebRTCDevicesObserver *>::iterator it =
+ std::find(mVoiceDevicesObserverList.begin(), mVoiceDevicesObserverList.end(), observer);
+ if (it != mVoiceDevicesObserverList.end())
+ {
+ mVoiceDevicesObserverList.erase(it);
+ }
+}
+
+// TODO: There's potential for shared code here as the patterns
+// are similar.
+void LLWebRTCImpl::setCaptureDevice(const std::string &id)
+{
+ mWorkerThread->PostTask(
+ [this, id]()
+ {
+ int16_t tuningRecordingDevice = 0;
+ int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices();
+ for (int16_t i = 0; i < captureDeviceCount; i++)
+ {
+ char name[webrtc::kAdmMaxDeviceNameSize];
+ char guid[webrtc::kAdmMaxGuidSize];
+ mTuningDeviceModule->RecordingDeviceName(i, name, guid);
+ if (id == guid || id == "Default") // first one in list is default
+ {
+ RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i;
+ tuningRecordingDevice = i;
+ break;
+ }
+ }
+ mTuningDeviceModule->StopRecording();
+ mTuningDeviceModule->SetRecordingDevice(tuningRecordingDevice);
+ mTuningDeviceModule->InitMicrophone();
+ mTuningDeviceModule->InitRecording();
+ mTuningDeviceModule->StartRecording();
+ if (mPeerDeviceModule)
+ {
+ int16_t captureDeviceCount = mPeerDeviceModule->RecordingDevices();
+ for (int16_t i = 0; i < captureDeviceCount; i++)
+ {
+ char name[webrtc::kAdmMaxDeviceNameSize];
+ char guid[webrtc::kAdmMaxGuidSize];
+ mPeerDeviceModule->RecordingDeviceName(i, name, guid);
+ if (id == guid || id == "Default") // first one in list is default
+ {
+ RTC_LOG(LS_INFO)
+ << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i;
+ mRecordingDevice = i;
+ break;
+ }
+ }
+ bool was_peer_recording = mPeerDeviceModule->Recording();
+ if (was_peer_recording)
+ {
+ mPeerDeviceModule->StopRecording();
+ }
+ mPeerDeviceModule->SetRecordingDevice(mRecordingDevice);
+ mPeerDeviceModule->InitMicrophone();
+ mPeerDeviceModule->InitRecording();
+ if (was_peer_recording)
+ {
+ mPeerDeviceModule->StartRecording();
+ }
+ }
+ });
+}
+
+void LLWebRTCImpl::setRenderDevice(const std::string &id)
+{
+ mWorkerThread->PostTask(
+ [this, id]()
+ {
+ int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices();
+ int16_t tuningPlayoutDevice = 0;
+ for (int16_t i = 0; i < renderDeviceCount; i++)
+ {
+ char name[webrtc::kAdmMaxDeviceNameSize];
+ char guid[webrtc::kAdmMaxGuidSize];
+ mTuningDeviceModule->PlayoutDeviceName(i, name, guid);
+ if (id == guid || id == "Default")
+ {
+ RTC_LOG(LS_INFO) << __FUNCTION__ << "Set playout device to " << name << " " << guid << " " << i;
+ tuningPlayoutDevice = i;
+ break;
+ }
+ }
+ bool was_tuning_playing = mTuningDeviceModule->Playing();
+ if (was_tuning_playing)
+ {
+ mTuningDeviceModule->StopPlayout();
+ }
+
+ mTuningDeviceModule->SetPlayoutDevice(tuningPlayoutDevice);
+ mTuningDeviceModule->InitSpeaker();
+ mTuningDeviceModule->InitPlayout();
+ if (was_tuning_playing)
+ {
+ mTuningDeviceModule->StartPlayout();
+ }
+
+ if (mPeerDeviceModule)
+ {
+ renderDeviceCount = mPeerDeviceModule->PlayoutDevices();
+ for (int16_t i = 0; i < renderDeviceCount; i++)
+ {
+ char name[webrtc::kAdmMaxDeviceNameSize];
+ char guid[webrtc::kAdmMaxGuidSize];
+ mPeerDeviceModule->PlayoutDeviceName(i, name, guid);
+ if (id == guid || id == "Default")
+ {
+ RTC_LOG(LS_INFO)
+ << __FUNCTION__ << "Set playout device to " << name << " " << guid << " " << i;
+ mPlayoutDevice = i;
+ break;
+ }
+ }
+ mPeerDeviceModule->StopPlayout();
+ mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice);
+ mPeerDeviceModule->InitSpeaker();
+ mPeerDeviceModule->InitPlayout();
+ mPeerDeviceModule->StartPlayout();
+
+ }
+ });
+}
+
+// updateDevices needs to happen on the worker thread.
+void LLWebRTCImpl::updateDevices()
+{
+ int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices();
+ int16_t currentRenderDeviceIndex = mTuningDeviceModule->GetPlayoutDevice();
+
+ LLWebRTCVoiceDeviceList renderDeviceList;
+ for (int16_t index = 0; index < renderDeviceCount; index++)
+ {
+ char name[webrtc::kAdmMaxDeviceNameSize];
+ char guid[webrtc::kAdmMaxGuidSize];
+ mTuningDeviceModule->PlayoutDeviceName(index, name, guid);
+ renderDeviceList.emplace_back(name, guid, index == currentRenderDeviceIndex);
+ }
+
+ int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices();
+ int16_t currentCaptureDeviceIndex = mTuningDeviceModule->GetRecordingDevice();
+
+ LLWebRTCVoiceDeviceList captureDeviceList;
+ for (int16_t index = 0; index < captureDeviceCount; index++)
+ {
+ char name[webrtc::kAdmMaxDeviceNameSize];
+ char guid[webrtc::kAdmMaxGuidSize];
+ mTuningDeviceModule->RecordingDeviceName(index, name, guid);
+ captureDeviceList.emplace_back(name, guid, index == currentCaptureDeviceIndex);
+ }
+ for (auto &observer : mVoiceDevicesObserverList)
+ {
+ observer->OnDevicesChanged(renderDeviceList,
+ captureDeviceList);
+ }
+}
+
+void LLWebRTCImpl::OnDevicesUpdated()
+{
+ updateDevices();
+}
+
+
+void LLWebRTCImpl::setTuningMode(bool enable)
+{
+ mSignalingThread->PostTask(
+ [this, enable]
+ {
+ for (auto &connection : mPeerConnections)
+ {
+ if (enable)
+ {
+ connection->enableSenderTracks(false);
+ }
+ else
+ {
+ connection->resetMute();
+ }
+ connection->enableReceiverTracks(!enable);
+ }
+ });
+}
+
+float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mTuningAudioDeviceObserver->getMicrophoneEnergy()); }
+
+float LLWebRTCImpl::getPeerConnectionAudioLevel() { return -20 * log10f(mPeerCustomProcessor->getMicrophoneEnergy()); }
+
+
+//
+// Peer Connection Helpers
+//
+
+LLWebRTCPeerConnectionInterface *LLWebRTCImpl::newPeerConnection()
+{
+ rtc::scoped_refptr<LLWebRTCPeerConnectionImpl> peerConnection = rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>(new rtc::RefCountedObject<LLWebRTCPeerConnectionImpl>());
+ peerConnection->init(this);
+
+ mPeerConnections.emplace_back(peerConnection);
+ peerConnection->enableSenderTracks(!mMute);
+ return peerConnection.get();
+}
+
+void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection)
+{
+ std::vector<rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>>::iterator it =
+ std::find(mPeerConnections.begin(), mPeerConnections.end(), peer_connection);
+ if (it != mPeerConnections.end())
+ {
+ (*it)->terminate();
+ mPeerConnections.erase(it);
+ }
+ if (mPeerConnections.empty())
+ {
+ setRecording(false);
+ }
+}
+
+
+//
+// LLWebRTCPeerConnectionImpl implementation.
+//
+// Most peer connection (signaling) happens on
+// the signaling thread.
+
+LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
+ mWebRTCImpl(nullptr),
+ mMute(false),
+ mAnswerReceived(false)
+{
+}
+
+//
+// LLWebRTCPeerConnection interface
+//
+
+void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl)
+{
+ mWebRTCImpl = webrtc_impl;
+ mPeerConnectionFactory = mWebRTCImpl->getPeerConnectionFactory();
+}
+void LLWebRTCPeerConnectionImpl::terminate()
+{
+ mWebRTCImpl->SignalingBlockingCall(
+ [this]()
+ {
+ if (mPeerConnection)
+ {
+ mPeerConnection->Close();
+ mPeerConnection = nullptr;
+ }
+ });
+}
+
+void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); }
+
+void LLWebRTCPeerConnectionImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer)
+{
+ std::vector<LLWebRTCSignalingObserver *>::iterator it =
+ std::find(mSignalingObserverList.begin(), mSignalingObserverList.end(), observer);
+ if (it != mSignalingObserverList.end())
+ {
+ mSignalingObserverList.erase(it);
+ }
+}
+
+// TODO: Add initialization structure through which
+// stun and turn servers may be passed in from
+// the sim or login.
+bool LLWebRTCPeerConnectionImpl::initializeConnection()
+{
+ RTC_DCHECK(!mPeerConnection);
+ mAnswerReceived = false;
+
+ mWebRTCImpl->PostSignalingTask(
+ [this]()
+ {
+ webrtc::PeerConnectionInterface::RTCConfiguration config;
+ config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
+ webrtc::PeerConnectionInterface::IceServer server;
+ server.uri = "stun:roxie-turn.staging.secondlife.io:3478";
+ config.servers.push_back(server);
+ server.uri = "stun:stun.l.google.com:19302";
+ config.servers.push_back(server);
+ server.uri = "stun:stun1.l.google.com:19302";
+ config.servers.push_back(server);
+ server.uri = "stun:stun2.l.google.com:19302";
+ config.servers.push_back(server);
+ server.uri = "stun:stun3.l.google.com:19302";
+ config.servers.push_back(server);
+ server.uri = "stun:stun4.l.google.com:19302";
+ config.servers.push_back(server);
+
+ config.set_min_port(60000);
+ config.set_max_port(60100);
+
+ webrtc::PeerConnectionDependencies pc_dependencies(this);
+ auto error_or_peer_connection = mPeerConnectionFactory->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
+ if (error_or_peer_connection.ok())
+ {
+ mPeerConnection = std::move(error_or_peer_connection.value());
+ }
+ else
+ {
+ RTC_LOG(LS_ERROR) << __FUNCTION__ << "Error creating peer connection: " << error_or_peer_connection.error().message();
+ return;
+ }
+
+ webrtc::DataChannelInit init;
+ init.ordered = true;
+
+ auto data_channel_or_error = mPeerConnection->CreateDataChannelOrError("SLData", &init);
+ if (data_channel_or_error.ok())
+ {
+ mDataChannel = std::move(data_channel_or_error.value());
+
+ mDataChannel->RegisterObserver(this);
+ }
+
+ cricket::AudioOptions audioOptions;
+ audioOptions.auto_gain_control = true;
+ audioOptions.echo_cancellation = true; // incompatible with opus stereo
+ audioOptions.noise_suppression = true;
+
+ mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream");
+
+ rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
+ mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get()));
+ audio_track->set_enabled(true);
+ mLocalStream->AddTrack(audio_track);
+
+ mPeerConnection->AddTrack(audio_track, {"SLStream"});
+
+ auto senders = mPeerConnection->GetSenders();
+
+ for (auto &sender : senders)
+ {
+ webrtc::RtpParameters params;
+ webrtc::RtpCodecParameters codecparam;
+ codecparam.name = "opus";
+ codecparam.kind = cricket::MEDIA_TYPE_AUDIO;
+ codecparam.clock_rate = 48000;
+ codecparam.num_channels = 2;
+ codecparam.parameters["stereo"] = "1";
+ codecparam.parameters["sprop-stereo"] = "1";
+ params.codecs.push_back(codecparam);
+ sender->SetParameters(params);
+ }
+
+ auto receivers = mPeerConnection->GetReceivers();
+ for (auto &receiver : receivers)
+ {
+ webrtc::RtpParameters params;
+ webrtc::RtpCodecParameters codecparam;
+ codecparam.name = "opus";
+ codecparam.kind = cricket::MEDIA_TYPE_AUDIO;
+ codecparam.clock_rate = 48000;
+ codecparam.num_channels = 2;
+ codecparam.parameters["stereo"] = "1";
+ codecparam.parameters["sprop-stereo"] = "1";
+ params.codecs.push_back(codecparam);
+ receiver->SetParameters(params);
+ }
+
+ webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions;
+ mPeerConnection->CreateOffer(this, offerOptions);
+ });
+
+ return true;
+}
+
+bool LLWebRTCPeerConnectionImpl::shutdownConnection()
+{
+ if (mPeerConnection)
+ {
+ mWebRTCImpl->PostSignalingTask(
+ [this]()
+ {
+ if (mPeerConnection)
+ {
+ mPeerConnection->Close();
+ mPeerConnection = nullptr;
+ }
+ for (auto &observer : mSignalingObserverList)
+ {
+ observer->OnPeerConnectionShutdown();
+ }
+ });
+ return true;
+ }
+ return false;
+}
+
+void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable)
+{
+ // set_enabled shouldn't be done on the worker thread.
+ if (mPeerConnection)
+ {
+ auto senders = mPeerConnection->GetSenders();
+ for (auto &sender : senders)
+ {
+ sender->track()->set_enabled(enable);
+ }
+ }
+}
+
+void LLWebRTCPeerConnectionImpl::enableReceiverTracks(bool enable)
+{
+ // set_enabled shouldn't be done on the worker thread
+ if (mPeerConnection)
+ {
+ auto receivers = mPeerConnection->GetReceivers();
+ for (auto &receiver : receivers)
+ {
+ receiver->track()->set_enabled(enable);
+ }
+ }
+}
+
+// Tell the peer connection that we've received a SDP answer from the sim.
+void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
+{
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp;
+
+ mWebRTCImpl->PostSignalingTask(
+ [this, sdp]()
+ {
+ if (mPeerConnection)
+ {
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->peer_connection_state();
+ mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp),
+ rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>(this));
+ }
+ });
+}
+
+
+//
+// LLWebRTCAudioInterface implementation
+//
+
+void LLWebRTCPeerConnectionImpl::setMute(bool mute)
+{
+ mMute = mute;
+ mWebRTCImpl->PostSignalingTask(
+ [this]()
+ {
+ if (mPeerConnection)
+ {
+ auto senders = mPeerConnection->GetSenders();
+
+ RTC_LOG(LS_INFO) << __FUNCTION__ << (mMute ? "disabling" : "enabling") << " streams count " << senders.size();
+ for (auto &sender : senders)
+ {
+ auto track = sender->track();
+ if (track)
+ {
+ track->set_enabled(!mMute);
+ }
+ }
+ }
+ });
+}
+
+void LLWebRTCPeerConnectionImpl::resetMute()
+{
+ setMute(mMute);
+}
+
+void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
+{
+ mWebRTCImpl->PostSignalingTask(
+ [this, volume]()
+ {
+ if (mPeerConnection)
+ {
+ auto receivers = mPeerConnection->GetReceivers();
+
+ for (auto &receiver : receivers)
+ {
+ for (auto &stream : receiver->streams())
+ {
+ for (auto &track : stream->GetAudioTracks())
+ {
+ track->GetSource()->SetVolume(volume);
+ }
+ }
+ }
+ }
+ });
+}
+
+void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
+{
+ mWebRTCImpl->PostSignalingTask(
+ [this, volume]()
+ {
+ if (mLocalStream)
+ {
+ for (auto &track : mLocalStream->GetAudioTracks())
+ {
+ track->GetSource()->SetVolume(volume);
+ }
+ }
+ });
+}
+
+//
+// PeerConnectionObserver implementation.
+//
+
+void LLWebRTCPeerConnectionImpl::OnAddTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
+ const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams)
+{
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id();
+ webrtc::RtpParameters params;
+ webrtc::RtpCodecParameters codecparam;
+ codecparam.name = "opus";
+ codecparam.kind = cricket::MEDIA_TYPE_AUDIO;
+ codecparam.clock_rate = 48000;
+ codecparam.num_channels = 2;
+ codecparam.parameters["stereo"] = "1";
+ codecparam.parameters["sprop-stereo"] = "1";
+ params.codecs.push_back(codecparam);
+ receiver->SetParameters(params);
+}
+
+void LLWebRTCPeerConnectionImpl::OnRemoveTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver)
+{
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id();
+}
+
+void LLWebRTCPeerConnectionImpl::OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> channel)
+{
+ mDataChannel = channel;
+ channel->RegisterObserver(this);
+}
+
+void LLWebRTCPeerConnectionImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state)
+{
+ LLWebRTCSignalingObserver::EIceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW;
+ switch (new_state)
+ {
+ case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew:
+ webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW;
+ break;
+ case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringGathering:
+ webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_GATHERING;
+ break;
+ case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringComplete:
+ webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_COMPLETE;
+ break;
+ default:
+ RTC_LOG(LS_ERROR) << __FUNCTION__ << " Bad Ice Gathering State" << new_state;
+ webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW;
+ return;
+ }
+
+ if (mAnswerReceived)
+ {
+ for (auto &observer : mSignalingObserverList)
+ {
+ observer->OnIceGatheringState(webrtc_new_state);
+ }
+ }
+}
+
+// Called any time the PeerConnectionState changes.
+void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState new_state)
+{
+ RTC_LOG(LS_ERROR) << __FUNCTION__ << " Peer Connection State Change " << new_state;
+
+ switch (new_state)
+ {
+ case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected:
+ {
+ mWebRTCImpl->setRecording(true);
+
+ mWebRTCImpl->PostWorkerTask([this]() {
+ for (auto &observer : mSignalingObserverList)
+ {
+ observer->OnAudioEstablished(this);
+ }
+ });
+ break;
+ }
+ case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed:
+ case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected:
+ {
+ for (auto &observer : mSignalingObserverList)
+ {
+ observer->OnRenegotiationNeeded();
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+// Convert an ICE candidate into a string appropriate for trickling
+// to the Secondlife WebRTC server via the sim.
+static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterface *candidate)
+{
+ std::ostringstream candidate_stream;
+
+ candidate_stream <<
+ candidate->candidate().foundation() << " " <<
+ std::to_string(candidate->candidate().component()) << " " <<
+ candidate->candidate().protocol() << " " <<
+ std::to_string(candidate->candidate().priority()) << " " <<
+ candidate->candidate().address().ipaddr().ToString() << " " <<
+ candidate->candidate().address().PortAsString() << " typ ";
+
+ if (candidate->candidate().type() == cricket::LOCAL_PORT_TYPE)
+ {
+ candidate_stream << "host";
+ }
+ else if (candidate->candidate().type() == cricket::STUN_PORT_TYPE)
+ {
+ candidate_stream << "srflx " <<
+ "raddr " << candidate->candidate().related_address().ipaddr().ToString() << " " <<
+ "rport " << candidate->candidate().related_address().PortAsString();
+ }
+ else if (candidate->candidate().type() == cricket::RELAY_PORT_TYPE)
+ {
+ candidate_stream << "relay " <<
+ "raddr " << candidate->candidate().related_address().ipaddr().ToString() << " " <<
+ "rport " << candidate->candidate().related_address().PortAsString();
+ }
+ else if (candidate->candidate().type() == cricket::PRFLX_PORT_TYPE)
+ {
+ candidate_stream << "prflx " <<
+ "raddr " << candidate->candidate().related_address().ipaddr().ToString() << " " <<
+ "rport " << candidate->candidate().related_address().PortAsString();
+ }
+ else {
+ RTC_LOG(LS_ERROR) << __FUNCTION__ << " Unknown candidate type " << candidate->candidate().type();
+ }
+ if (candidate->candidate().protocol() == "tcp")
+ {
+ candidate_stream << " tcptype " << candidate->candidate().tcptype();
+ }
+
+ return candidate_stream.str();
+}
+
+// The webrtc library has a new ice candidate.
+void LLWebRTCPeerConnectionImpl::OnIceCandidate(const webrtc::IceCandidateInterface *candidate)
+{
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index();
+
+ if (!candidate)
+ {
+ RTC_LOG(LS_ERROR) << __FUNCTION__ << " No Ice Candidate Given";
+ return;
+ }
+ if (mAnswerReceived)
+ {
+ // We've already received an answer SDP from the Secondlife WebRTC server
+ // so simply tell observers about our new ice candidate.
+ for (auto &observer : mSignalingObserverList)
+ {
+ LLWebRTCIceCandidate ice_candidate;
+ ice_candidate.mCandidate = iceCandidateToTrickleString(candidate);
+ ice_candidate.mMLineIndex = candidate->sdp_mline_index();
+ ice_candidate.mSdpMid = candidate->sdp_mid();
+ observer->OnIceCandidate(ice_candidate);
+ }
+ }
+ else
+ {
+ // As we've not yet received our answer, cache the candidate.
+ mCachedIceCandidates.push_back(
+ webrtc::CreateIceCandidate(candidate->sdp_mid(),
+ candidate->sdp_mline_index(),
+ candidate->candidate()));
+ }
+}
+
+//
+// CreateSessionDescriptionObserver implementation.
+//
+void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface *desc)
+{
+ std::string sdp;
+ desc->ToString(&sdp);
+ RTC_LOG(LS_INFO) << sdp;
+ ;
+ // mangle the sdp as this is the only way currently to bump up
+ // the send audio rate to 48k
+ std::istringstream sdp_stream(sdp);
+ std::ostringstream sdp_mangled_stream;
+ std::string sdp_line;
+ std::string opus_payload;
+ while (std::getline(sdp_stream, sdp_line))
+ {
+ int bandwidth = 0;
+ int payload_id = 0;
+ // force mono down, stereo up
+ if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2)
+ {
+ opus_payload = std::to_string(payload_id);
+ sdp_mangled_stream << "a=rtpmap:" << opus_payload << " opus/48000/2" << "\n";
+ }
+ else if (sdp_line.find("a=fmtp:" + opus_payload) == 0)
+ {
+ sdp_mangled_stream << sdp_line << "a=fmtp:" << opus_payload
+ << " minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxplaybackrate=48000;sprop-maxplaybackrate=48000;sprop-maxcapturerate=48000\n";
+ }
+ else
+ {
+ sdp_mangled_stream << sdp_line << "\n";
+ }
+ }
+
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_mangled_stream.str();
+
+ for (auto &observer : mSignalingObserverList)
+ {
+ observer->OnOfferAvailable(sdp_mangled_stream.str());
+ }
+
+ mPeerConnection->SetLocalDescription(std::unique_ptr<webrtc::SessionDescriptionInterface>(webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str())),
+ rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface>(this));
+}
+
+void LLWebRTCPeerConnectionImpl::OnFailure(webrtc::RTCError error)
+{
+ RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message();
+}
+
+//
+// SetRemoteDescriptionObserverInterface implementation.
+//
+void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error)
+{
+ // we've received an answer SDP from the sim.
+
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state();
+ if (!error.ok())
+ {
+ RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message();
+ return;
+ }
+ mAnswerReceived = true;
+
+ // tell the observers about any cached ICE candidates.
+ for (auto &observer : mSignalingObserverList)
+ {
+ for (auto &candidate : mCachedIceCandidates)
+ {
+ LLWebRTCIceCandidate ice_candidate;
+ ice_candidate.mCandidate = iceCandidateToTrickleString(candidate.get());
+ ice_candidate.mMLineIndex = candidate->sdp_mline_index();
+ ice_candidate.mSdpMid = candidate->sdp_mid();
+ observer->OnIceCandidate(ice_candidate);
+ }
+ }
+ mCachedIceCandidates.clear();
+ OnIceGatheringChange(mPeerConnection->ice_gathering_state());
+
+}
+
+//
+// SetLocalDescriptionObserverInterface implementation.
+//
+void LLWebRTCPeerConnectionImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error)
+{
+}
+
+//
+// DataChannelObserver implementation
+//
+
+void LLWebRTCPeerConnectionImpl::OnStateChange()
+{
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State: " << webrtc::DataChannelInterface::DataStateString(mDataChannel->state());
+ switch (mDataChannel->state())
+ {
+ case webrtc::DataChannelInterface::kOpen:
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State Open";
+ for (auto &observer : mSignalingObserverList)
+ {
+ observer->OnDataChannelReady(this);
+ }
+ break;
+ case webrtc::DataChannelInterface::kConnecting:
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State Connecting";
+ break;
+ case webrtc::DataChannelInterface::kClosing:
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State closing";
+ break;
+ case webrtc::DataChannelInterface::kClosed:
+ RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State closed";
+ break;
+ default:
+ break;
+ }
+}
+
+void LLWebRTCPeerConnectionImpl::OnMessage(const webrtc::DataBuffer& buffer)
+{
+ std::string data((const char*)buffer.data.cdata(), buffer.size());
+ for (auto &observer : mDataObserverList)
+ {
+ observer->OnDataReceived(data, buffer.binary);
+ }
+}
+
+//
+// LLWebRTCDataInterface
+//
+
+void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary)
+{
+ if (mDataChannel)
+ {
+ rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length());
+ webrtc::DataBuffer buffer(cowBuffer, binary);
+ mDataChannel->Send(buffer);
+ }
+}
+
+void LLWebRTCPeerConnectionImpl::setDataObserver(LLWebRTCDataObserver* observer)
+{
+ mDataObserverList.emplace_back(observer);
+}
+
+void LLWebRTCPeerConnectionImpl::unsetDataObserver(LLWebRTCDataObserver* observer)
+{
+ std::vector<LLWebRTCDataObserver *>::iterator it =
+ std::find(mDataObserverList.begin(), mDataObserverList.end(), observer);
+ if (it != mDataObserverList.end())
+ {
+ mDataObserverList.erase(it);
+ }
+}
+
+LLWebRTCImpl * gWebRTCImpl = nullptr;
+LLWebRTCDeviceInterface * getDeviceInterface()
+{
+ return gWebRTCImpl;
+}
+
+LLWebRTCPeerConnectionInterface* newPeerConnection()
+{
+ return gWebRTCImpl->newPeerConnection();
+}
+
+void freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection)
+{
+ gWebRTCImpl->freePeerConnection(peer_connection);
+}
+
+
+void init()
+{
+ gWebRTCImpl = new LLWebRTCImpl();
+ gWebRTCImpl->init();
+}
+
+void terminate()
+{
+ if (gWebRTCImpl)
+ {
+ gWebRTCImpl->terminate();
+ gWebRTCImpl = nullptr;
+ }
+}
+
+} // namespace llwebrtc
diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h
new file mode 100644
index 0000000000..43b48e79ab
--- /dev/null
+++ b/indra/llwebrtc/llwebrtc.h
@@ -0,0 +1,235 @@
+/**
+ * @file llwebrtc.h
+ * @brief WebRTC interface
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free tSoftware
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+/*
+ * llwebrtc wraps the native webrtc c++ library in a dynamic library with a simlified interface
+ * so that the viewer can use it. This is done because native webrtc has a different
+ * overall threading model than the viewer.
+ * The native webrtc library is also compiled with clang, and has memory management
+ * functions that conflict namespace-wise with those in the viewer.
+ *
+ * Due to these differences, code from the viewer cannot be pulled in to this
+ * dynamic library, so it remains very simple.
+ */
+
+#ifndef LLWEBRTC_H
+#define LLWEBRTC_H
+
+#include <string>
+#include <vector>
+
+#ifdef LL_MAKEDLL
+#ifdef WEBRTC_WIN
+#define LLSYMEXPORT __declspec(dllexport)
+#elif WEBRTC_LINUX
+#define LLSYMEXPORT __attribute__((visibility("default")))
+#else
+#define LLSYMEXPORT /**/
+#endif
+#else
+#define LLSYMEXPORT /**/
+#endif // LL_MAKEDLL
+
+namespace llwebrtc
+{
+
+// LLWebRTCVoiceDevice is a simple representation of the
+// components of a device, used to communicate this
+// information to the viewer.
+
+
+// A note on threading.
+// Native WebRTC has it's own threading model. Some discussion
+// can be found here (https://webrtc.github.io/webrtc-org/native-code/native-apis/)
+//
+// Note that all callbacks to observers will occurr on one of the WebRTC native threads
+// (signaling, worker, etc.) Care should be taken to assure there are not
+// bad interactions with the viewer threads.
+
+class LLWebRTCVoiceDevice
+{
+ public:
+ std::string mDisplayName; // friendly name for user interface purposes
+ std::string mID; // internal value for selection
+ bool mCurrent; // current device
+
+ LLWebRTCVoiceDevice(const std::string &display_name, const std::string &id, bool current) :
+ mDisplayName(display_name),
+ mID(id),
+ mCurrent(current)
+ {};
+};
+
+typedef std::vector<LLWebRTCVoiceDevice> LLWebRTCVoiceDeviceList;
+
+
+// The LLWebRTCDeviceObserver should be implemented by the viewer
+// webrtc module, which will receive notifications when devices
+// change (are unplugged, etc.)
+class LLWebRTCDevicesObserver
+{
+ public:
+ virtual void OnDevicesChanged(const LLWebRTCVoiceDeviceList &render_devices,
+ const LLWebRTCVoiceDeviceList &capture_devices) = 0;
+};
+
+
+// The LLWebRTCDeviceInterface provides a way for the viewer
+// to enumerate, set, and get notifications of changes
+// for both capture (microphone) and render (speaker)
+// devices.
+class LLWebRTCDeviceInterface
+{
+ public:
+
+ // instructs webrtc to refresh the device list.
+ virtual void refreshDevices() = 0;
+
+ // set the capture and render devices using the unique identifier for the device
+ virtual void setCaptureDevice(const std::string& id) = 0;
+ virtual void setRenderDevice(const std::string& id) = 0;
+
+ // Device observers for device change callbacks.
+ virtual void setDevicesObserver(LLWebRTCDevicesObserver *observer) = 0;
+ virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0;
+
+ // tuning and audio levels
+ virtual void setTuningMode(bool enable) = 0;
+ virtual float getTuningAudioLevel() = 0; // for use during tuning
+ virtual float getPeerConnectionAudioLevel() = 0; // for use when not tuning
+};
+
+// LLWebRTCAudioInterface provides the viewer with a way
+// to set audio characteristics (mute, send and receive volume)
+class LLWebRTCAudioInterface
+{
+ public:
+ virtual void setMute(bool mute) = 0;
+ virtual void setReceiveVolume(float volume) = 0; // volume between 0.0 and 1.0
+ virtual void setSendVolume(float volume) = 0; // volume between 0.0 and 1.0
+};
+
+// LLWebRTCDataObserver allows the viewer voice module to be notified when
+// data is received over the data channel.
+class LLWebRTCDataObserver
+{
+public:
+ virtual void OnDataReceived(const std::string& data, bool binary) = 0;
+};
+
+// LLWebRTCDataInterface allows the viewer to send data over the data channel.
+class LLWebRTCDataInterface
+{
+public:
+
+ virtual void sendData(const std::string& data, bool binary=false) = 0;
+
+ virtual void setDataObserver(LLWebRTCDataObserver *observer) = 0;
+ virtual void unsetDataObserver(LLWebRTCDataObserver *observer) = 0;
+};
+
+// LLWebRTCIceCandidate is a basic structure containing
+// information needed for ICE trickling.
+struct LLWebRTCIceCandidate
+{
+ std::string mCandidate;
+ std::string mSdpMid;
+ int mMLineIndex;
+};
+
+// LLWebRTCSignalingObserver provides a way for the native
+// webrtc library to notify the viewer voice module of
+// various state changes.
+class LLWebRTCSignalingObserver
+{
+ public:
+
+ typedef enum e_ice_gathering_state {
+ ICE_GATHERING_NEW,
+ ICE_GATHERING_GATHERING,
+ ICE_GATHERING_COMPLETE
+ } EIceGatheringState;
+
+ // Called when ICE gathering states have changed.
+ // This may be called at any time, as ICE gathering
+ // can be redone while a connection is up.
+ virtual void OnIceGatheringState(EIceGatheringState state) = 0;
+
+ // Called when a new ice candidate is available.
+ virtual void OnIceCandidate(const LLWebRTCIceCandidate& candidate) = 0;
+
+ // Called when an offer is available after a connection is requested.
+ virtual void OnOfferAvailable(const std::string& sdp) = 0;
+
+ // Called when a connection enters a failure state and renegotiation is needed.
+ virtual void OnRenegotiationNeeded() = 0;
+
+ // Called when the audio channel has been established and audio
+ // can begin.
+ virtual void OnAudioEstablished(LLWebRTCAudioInterface *audio_interface) = 0;
+
+ // Called when the data channel has been established and data
+ // transfer can begin.
+ virtual void OnDataChannelReady(LLWebRTCDataInterface *data_interface) = 0;
+
+ // Called when a peer connection has finished shutting down.
+ virtual void OnPeerConnectionShutdown() = 0;
+};
+
+
+// LLWebRTCPeerConnectionInterface representsd a connection to a peer,
+// in most cases a Secondlife WebRTC server. This interface
+// allows for management of this peer connection.
+class LLWebRTCPeerConnectionInterface
+{
+ public:
+
+ virtual void setSignalingObserver(LLWebRTCSignalingObserver* observer) = 0;
+ virtual void unsetSignalingObserver(LLWebRTCSignalingObserver* observer) = 0;
+
+ virtual bool initializeConnection() = 0;
+ virtual bool shutdownConnection() = 0;
+ virtual void AnswerAvailable(const std::string &sdp) = 0;
+};
+
+// The following define the dynamic linked library
+// exports.
+
+// This library must be initialized before use.
+LLSYMEXPORT void init();
+
+// And should be terminated as part of shutdown.
+LLSYMEXPORT void terminate();
+
+// Return an interface for device management.
+LLSYMEXPORT LLWebRTCDeviceInterface* getDeviceInterface();
+
+// Allocate and free peer connections.
+LLSYMEXPORT LLWebRTCPeerConnectionInterface* newPeerConnection();
+LLSYMEXPORT void freePeerConnection(LLWebRTCPeerConnectionInterface *connection);
+}
+
+#endif // LLWEBRTC_H
diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h
new file mode 100644
index 0000000000..16afec061e
--- /dev/null
+++ b/indra/llwebrtc/llwebrtc_impl.h
@@ -0,0 +1,364 @@
+/**
+ * @file llwebrtc_impl.h
+ * @brief WebRTC dynamic library implementation header
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLWEBRTC_IMPL_H
+#define LLWEBRTC_IMPL_H
+
+#define LL_MAKEDLL
+#if defined(_WIN32) || defined(_WIN64)
+#define WEBRTC_WIN 1
+#elif defined(__APPLE__)
+#define WEBRTC_MAC 1
+#define WEBRTC_POSIX 1
+#elif __linux__
+#define WEBRTC_LINUX 1
+#endif
+
+#include "llwebrtc.h"
+// WebRTC Includes
+#ifdef WEBRTC_WIN
+#pragma warning(disable : 4996)
+#pragma warning(disable : 4068)
+#endif // WEBRTC_WIN
+
+#include "api/scoped_refptr.h"
+#include "rtc_base/ref_count.h"
+#include "rtc_base/ref_counted_object.h"
+#include "rtc_base/ssl_adapter.h"
+#include "rtc_base/thread.h"
+#include "api/peer_connection_interface.h"
+#include "api/media_stream_interface.h"
+#include "api/create_peerconnection_factory.h"
+#include "modules/audio_device/include/audio_device.h"
+#include "modules/audio_device/include/audio_device_data_observer.h"
+#include "rtc_base/task_queue.h"
+#include "api/task_queue/task_queue_factory.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "modules/audio_device/include/audio_device_defines.h"
+
+
+namespace llwebrtc
+{
+
+class LLWebRTCPeerConnectionImpl;
+
+
+// Implements a class allowing capture of audio data
+// to determine audio level of the microphone.
+class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver
+{
+ public:
+ LLAudioDeviceObserver();
+
+ // Retrieve the RMS audio loudness
+ float getMicrophoneEnergy();
+
+ // Data retrieved from the caputure device is
+ // passed in here for processing.
+ void OnCaptureData(const void *audio_samples,
+ const size_t num_samples,
+ const size_t bytes_per_sample,
+ const size_t num_channels,
+ const uint32_t samples_per_sec) override;
+
+ // This is for data destined for the render device.
+ // not currently used.
+ void OnRenderData(const void *audio_samples,
+ const size_t num_samples,
+ const size_t bytes_per_sample,
+ const size_t num_channels,
+ const uint32_t samples_per_sec) override;
+
+ protected:
+ static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing (30 frames)
+ float mSumVector[NUM_PACKETS_TO_FILTER];
+ float mMicrophoneEnergy;
+};
+
+// Used to process/retrieve audio levels after
+// all of the processing (AGC, AEC, etc.) for display in-world to the user.
+class LLCustomProcessor : public webrtc::CustomProcessing
+{
+ public:
+ LLCustomProcessor();
+ ~LLCustomProcessor() override {}
+
+ // (Re-) Initializes the submodule.
+ void Initialize(int sample_rate_hz, int num_channels) override;
+
+ // Analyzes the given capture or render signal.
+ void Process(webrtc::AudioBuffer *audio) override;
+
+ // Returns a string representation of the module state.
+ std::string ToString() const override { return ""; }
+
+ float getMicrophoneEnergy() { return mMicrophoneEnergy; }
+
+ protected:
+ static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing
+ int mSampleRateHz;
+ int mNumChannels;
+
+ float mSumVector[NUM_PACKETS_TO_FILTER];
+ float mMicrophoneEnergy;
+};
+
+
+// Primary singleton implementation for interfacing
+// with the native webrtc library.
+class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceSink
+{
+ public:
+ LLWebRTCImpl();
+ ~LLWebRTCImpl() {}
+
+ void init();
+ void terminate();
+
+ //
+ // LLWebRTCDeviceInterface
+ //
+
+ void refreshDevices() override;
+
+ void setDevicesObserver(LLWebRTCDevicesObserver *observer) override;
+ void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) override;
+
+ void setCaptureDevice(const std::string& id) override;
+ void setRenderDevice(const std::string& id) override;
+
+ void setTuningMode(bool enable) override;
+ float getTuningAudioLevel() override;
+ float getPeerConnectionAudioLevel() override;
+
+ //
+ // AudioDeviceSink
+ //
+ void OnDevicesUpdated() override;
+
+ //
+ // Helpers
+ //
+
+ // The following thread helpers allow the
+ // LLWebRTCPeerConnectionImpl class to post
+ // tasks to the native webrtc threads.
+ void PostWorkerTask(absl::AnyInvocable<void() &&> task,
+ const webrtc::Location& location = webrtc::Location::Current())
+ {
+ mWorkerThread->PostTask(std::move(task), location);
+ }
+
+ void PostSignalingTask(absl::AnyInvocable<void() &&> task,
+ const webrtc::Location& location = webrtc::Location::Current())
+ {
+ mSignalingThread->PostTask(std::move(task), location);
+ }
+
+ void PostNetworkTask(absl::AnyInvocable<void() &&> task,
+ const webrtc::Location& location = webrtc::Location::Current())
+ {
+ mNetworkThread->PostTask(std::move(task), location);
+ }
+
+ void WorkerBlockingCall(rtc::FunctionView<void()> functor,
+ const webrtc::Location& location = webrtc::Location::Current())
+ {
+ mWorkerThread->BlockingCall(std::move(functor), location);
+ }
+
+ void SignalingBlockingCall(rtc::FunctionView<void()> functor,
+ const webrtc::Location& location = webrtc::Location::Current())
+ {
+ mSignalingThread->BlockingCall(std::move(functor), location);
+ }
+
+ void NetworkBlockingCall(rtc::FunctionView<void()> functor,
+ const webrtc::Location& location = webrtc::Location::Current())
+ {
+ mNetworkThread->BlockingCall(std::move(functor), location);
+ }
+
+ // Allows the LLWebRTCPeerConnectionImpl class to retrieve the
+ // native webrtc PeerConnectionFactory.
+ rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> getPeerConnectionFactory()
+ {
+ return mPeerConnectionFactory;
+ }
+
+ // create or destroy a peer connection.
+ LLWebRTCPeerConnectionInterface* newPeerConnection();
+ void freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection);
+
+ // enables/disables capture via the capture device
+ void setRecording(bool recording);
+
+ protected:
+ // The native webrtc threads
+ std::unique_ptr<rtc::Thread> mNetworkThread;
+ std::unique_ptr<rtc::Thread> mWorkerThread;
+ std::unique_ptr<rtc::Thread> mSignalingThread;
+
+ // The factory that allows creation of native webrtc PeerConnections.
+ rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
+
+ // more native webrtc stuff
+ std::unique_ptr<webrtc::TaskQueueFactory> mTaskQueueFactory;
+
+
+ // Devices
+ void updateDevices();
+ rtc::scoped_refptr<webrtc::AudioDeviceModule> mTuningDeviceModule;
+ rtc::scoped_refptr<webrtc::AudioDeviceModule> mPeerDeviceModule;
+ std::vector<LLWebRTCDevicesObserver *> mVoiceDevicesObserverList;
+
+ // accessors in native webrtc for devices aren't apparently implemented yet.
+ int32_t mPlayoutDevice;
+ int32_t mRecordingDevice;
+ bool mMute;
+
+ LLAudioDeviceObserver * mTuningAudioDeviceObserver;
+ LLCustomProcessor * mPeerCustomProcessor;
+
+ // peer connections
+ std::vector<rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>> mPeerConnections;
+};
+
+
+// The implementation of a peer connection, which contains
+// the various interfaces used by the viewer to interact with
+// the webrtc connection.
+class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
+ public LLWebRTCAudioInterface,
+ public LLWebRTCDataInterface,
+ public webrtc::PeerConnectionObserver,
+ public webrtc::CreateSessionDescriptionObserver,
+ public webrtc::SetRemoteDescriptionObserverInterface,
+ public webrtc::SetLocalDescriptionObserverInterface,
+ public webrtc::DataChannelObserver
+
+{
+ public:
+ LLWebRTCPeerConnectionImpl();
+ ~LLWebRTCPeerConnectionImpl() {}
+
+ void init(LLWebRTCImpl * webrtc_impl);
+ void terminate();
+
+ virtual void AddRef() const override = 0;
+ virtual rtc::RefCountReleaseStatus Release() const override = 0;
+
+ //
+ // LLWebRTCPeerConnection
+ //
+
+ void setSignalingObserver(LLWebRTCSignalingObserver *observer) override;
+ void unsetSignalingObserver(LLWebRTCSignalingObserver *observer) override;
+ bool initializeConnection() override;
+ bool shutdownConnection() override;
+ void AnswerAvailable(const std::string &sdp) override;
+
+ //
+ // LLWebRTCAudioInterface
+ //
+ void setMute(bool mute) override;
+ void setReceiveVolume(float volume) override; // volume between 0.0 and 1.0
+ void setSendVolume(float volume) override; // volume between 0.0 and 1.0
+
+ //
+ // LLWebRTCDataInterface
+ //
+ void sendData(const std::string& data, bool binary=false) override;
+ void setDataObserver(LLWebRTCDataObserver *observer) override;
+ void unsetDataObserver(LLWebRTCDataObserver *observer) override;
+
+ //
+ // PeerConnectionObserver implementation.
+ //
+
+ void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {}
+ void OnAddTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
+ const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams) override;
+ void OnRemoveTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) override;
+ void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> channel) override;
+ void OnRenegotiationNeeded() override {}
+ void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {};
+ void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override;
+ void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override;
+ void OnIceConnectionReceivingChange(bool receiving) override {}
+ void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState new_state) override;
+
+ //
+ // CreateSessionDescriptionObserver implementation.
+ //
+ void OnSuccess(webrtc::SessionDescriptionInterface *desc) override;
+ void OnFailure(webrtc::RTCError error) override;
+
+ //
+ // SetRemoteDescriptionObserverInterface implementation.
+ //
+ void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override;
+
+ //
+ // SetLocalDescriptionObserverInterface implementation.
+ //
+ void OnSetLocalDescriptionComplete(webrtc::RTCError error) override;
+
+ //
+ // DataChannelObserver implementation.
+ //
+ void OnStateChange() override;
+ void OnMessage(const webrtc::DataBuffer& buffer) override;
+
+ // Helpers
+ void resetMute();
+ void enableSenderTracks(bool enable);
+ void enableReceiverTracks(bool enable);
+
+ protected:
+
+ LLWebRTCImpl * mWebRTCImpl;
+
+ rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
+
+ bool mMute;
+
+ // signaling
+ std::vector<LLWebRTCSignalingObserver *> mSignalingObserverList;
+ std::vector<std::unique_ptr<webrtc::IceCandidateInterface>> mCachedIceCandidates;
+ bool mAnswerReceived;
+
+ rtc::scoped_refptr<webrtc::PeerConnectionInterface> mPeerConnection;
+ rtc::scoped_refptr<webrtc::MediaStreamInterface> mLocalStream;
+
+ // data
+ std::vector<LLWebRTCDataObserver *> mDataObserverList;
+ rtc::scoped_refptr<webrtc::DataChannelInterface> mDataChannel;
+};
+
+}
+
+#endif // LLWEBRTC_IMPL_H
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 355f35c558..dc0c92bd19 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -692,6 +692,7 @@ set(viewer_SOURCE_FILES
llvoiceclient.cpp
llvoicevisualizer.cpp
llvoicevivox.cpp
+ llvoicewebrtc.cpp
llvoinventorylistener.cpp
llvopartgroup.cpp
llvosky.cpp
@@ -1338,6 +1339,7 @@ set(viewer_HEADER_FILES
llvoiceclient.h
llvoicevisualizer.h
llvoicevivox.h
+ llvoicewebrtc.h
llvoinventorylistener.h
llvopartgroup.h
llvosky.h
@@ -1438,6 +1440,7 @@ if (LINUX)
endif (LINUX)
if (WINDOWS)
+
list(APPEND viewer_SOURCE_FILES
llappviewerwin32.cpp
llwindebug.cpp
@@ -1729,6 +1732,7 @@ if (WINDOWS)
${SHARED_LIB_STAGING_DIR}/openjp2.dll
${SHARED_LIB_STAGING_DIR}/libhunspell.dll
${SHARED_LIB_STAGING_DIR}/uriparser.dll
+ ${SHARED_LIB_STAGING_DIR}/llwebrtc.dll
#${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/SLVoice.exe
#${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/libsndfile-1.dll
#${SHARED_LIB_STAGING_DIR}/${LL_INTDIR}/vivoxoal.dll
@@ -1795,13 +1799,14 @@ if (WINDOWS)
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
stage_third_party_libs
+ llwebrtc
${COPY_INPUT_DEPENDENCIES}
COMMENT "Performing viewer_manifest copy"
)
add_custom_target(copy_w_viewer_manifest ALL DEPENDS ${CMAKE_CFG_INTDIR}/copy_touched.bat)
- add_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon copy_w_viewer_manifest)
+ add_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon llwebrtc copy_w_viewer_manifest)
if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts)
add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts)
@@ -1925,6 +1930,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
llcorehttp
llcommon
llmeshoptimizer
+ llwebrtc
ll::ndof
lllogin
llprimitive
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index bd38527462..5f7d1d8a21 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -15128,7 +15128,7 @@
<key>Type</key>
<string>String</string>
<key>Value</key>
- <string>vivox</string>
+ <string>webrtc</string>
</map>
<key>WLSkyDetail</key>
<map>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 3853aaa8fd..ca27dbd818 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -311,7 +311,7 @@ bool LLAgent::isActionAllowed(const LLSD& sdname)
}
else
{
- allow_agent_voice = channel->isActive() && channel->callStarted();
+ allow_agent_voice = channel->isActive();
}
}
@@ -4078,10 +4078,6 @@ bool LLAgent::teleportCore(bool is_local)
}
make_ui_sound("UISndTeleportOut");
- // MBW -- Let the voice client know a teleport has begun so it can leave the existing channel.
- // This was breaking the case of teleporting within a single sim. Backing it out for now.
-// LLVoiceClient::getInstance()->leaveChannel();
-
return true;
}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 4a43133ff6..c079b3d1a1 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -3344,15 +3344,15 @@ LLSD LLAppViewer::getViewerInfo() const
{
LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion();
const std::string build_version = version.mBuildVersion;
- std::ostringstream version_string;
- if (std::equal(build_version.begin(), build_version.begin() + version.serverVersion.size(),
+ std::ostringstream version_string;
+ if (std::equal(version.mBuildVersion.begin(), version.mBuildVersion.begin() + version.serverVersion.size(),
version.serverVersion.begin()))
{ // Normal case: Show type and build version.
- version_string << version.serverType << " " << build_version << std::endl;
+ version_string << version.voiceServerType << " " << version.mBuildVersion << std::endl;
}
else
{ // Mismatch: Show both versions.
- version_string << version.serverVersion << "/" << build_version << std::endl;
+ version_string << version.voiceServerType << " " << version.serverVersion << "/" << version.mBuildVersion << std::endl;
}
info["VOICE_VERSION"] = version_string.str();
}
@@ -5121,7 +5121,7 @@ void LLAppViewer::sendLogoutRequest()
if(LLVoiceClient::instanceExists())
{
- LLVoiceClient::getInstance()->leaveChannel();
+ LLVoiceClient::getInstance()->setVoiceEnabled(false);
}
}
}
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 6d1496d517..bfa1dea324 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -43,6 +43,7 @@
#ifndef LL_LLAPPVIEWER_H
#define LL_LLAPPVIEWER_H
+#include "llapp.h"
#include "llallocator.h"
#include "llapr.h"
#include "llcontrol.h"
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index 313339f131..156b5b4047 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -241,7 +241,7 @@ static void on_avatar_name_cache_start_call(const LLUUID& agent_id,
const LLAvatarName& av_name)
{
std::string name = av_name.getDisplayName();
- LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id, true);
+ LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id, LLSD());
if (session_id != LLUUID::null)
{
gIMMgr->startCall(session_id);
@@ -277,8 +277,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate
// create the new ad hoc voice session
const std::string title = LLTrans::getString("conference-title");
- LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START,
- ids[0], id_array, true, floater_id);
+ LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, LLSD(), floater_id);
if (session_id == LLUUID::null)
{
return;
@@ -322,7 +321,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float
id_array.push_back(*it);
}
const std::string title = LLTrans::getString("conference-title");
- LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, false, floater_id);
+ LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, LLSD(), floater_id);
if (session_id == LLUUID::null)
{
diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp
index 48c7df40df..42194c9c16 100644
--- a/indra/newview/llconversationview.cpp
+++ b/indra/newview/llconversationview.cpp
@@ -57,7 +57,7 @@ public:
: conversation(conv)
{}
- virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal)
+ virtual void onChange(EStatusType status, const LLSD& channelInfo, bool proximal)
{
conversation->showVoiceIndicator(conversation
&& status != STATUS_JOINING
diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp
index ed2a2807b5..c6868ffeda 100644
--- a/indra/newview/llfloaterimsession.cpp
+++ b/indra/newview/llfloaterimsession.cpp
@@ -552,7 +552,7 @@ void LLFloaterIMSession::onCallButtonClicked()
}
}
-void LLFloaterIMSession::onChange(EStatusType status, const std::string &channelURI, bool proximal)
+void LLFloaterIMSession::onChange(EStatusType status, const LLSD& channelInfo, bool proximal)
{
if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL)
{
diff --git a/indra/newview/llfloaterimsession.h b/indra/newview/llfloaterimsession.h
index 28464fc14b..fc431f3ced 100644
--- a/indra/newview/llfloaterimsession.h
+++ b/indra/newview/llfloaterimsession.h
@@ -114,8 +114,7 @@ public:
// Implements LLVoiceClientStatusObserver::onChange() to enable the call
// button when voice is available
- void onChange(EStatusType status, const std::string &channelURI,
- bool proximal);
+ void onChange(EStatusType status, const LLSD& channelInfo, bool proximal);
virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; }
virtual void onVoiceChannelStateChanged(
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index 380e49c320..9d39da148c 100644
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -254,7 +254,7 @@ void LLGroupActions::startCall(const LLUUID& group_id)
return;
}
- LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, true);
+ LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, LLSD());
if (session_id == LLUUID::null)
{
LL_WARNS() << "Error adding session" << LL_ENDL;
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 61a01d7418..921e757b58 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -87,7 +87,20 @@ const S32 XL8_PADDING = 3; // XL8_START_TAG.size() + XL8_END_TAG.size()
/** Timeout of outgoing session initialization (in seconds) */
const static U32 SESSION_INITIALIZATION_TIMEOUT = 30;
-void startConfrenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents);
+
+enum EMultiAgentChatSessionType
+{
+ GROUP_CHAT_SESSION = 0,
+ CONFERENCE_SESSION = 1,
+ P2P_CHAT_SESSION = 2,
+ SESSION_TYPE_COUNT
+};
+
+
+void startConferenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents);
+
+void startP2PCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId);
+
void chatterBoxInvitationCoro(std::string url, LLUUID sessionId, LLIMMgr::EInvitationType invitationType);
void chatterBoxHistoryCoro(std::string url, LLUUID sessionId, std::string from, std::string message, U32 timestamp);
void start_deprecated_conference_chat(const LLUUID& temp_session_id, const LLUUID& creator_id, const LLUUID& other_participant_id, const LLSD& agents_to_invite);
@@ -108,7 +121,7 @@ BOOL LLSessionTimeoutTimer::tick()
{
gIMMgr->showSessionStartError("session_initialization_timed_out_error", mSessionId);
}
- return TRUE;
+ return TRUE;
}
@@ -137,7 +150,7 @@ void process_dnd_im(const LLSD& notification)
name,
IM_NOTHING_SPECIAL,
fromID,
- false,
+ LLSD(),
false); //will need slight refactor to retrieve whether offline message or not (assume online for now)
}
@@ -396,7 +409,7 @@ void on_new_message(const LLSD& msg)
notify_of_message(msg, false);
}
-void startConfrenceCoro(std::string url,
+void startConferenceCoro(std::string url,
LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
@@ -408,6 +421,9 @@ void startConfrenceCoro(std::string url,
postData["method"] = "start conference";
postData["session-id"] = tempSessionId;
postData["params"] = agents;
+ LLSD altParams;
+ altParams["voice_server_type"] = gSavedSettings.getString("VoiceServerType");
+ postData["alt_params"] = altParams;
LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData);
@@ -437,6 +453,38 @@ void startConfrenceCoro(std::string url,
}
}
+void startP2PCoro(std::string url, LLUUID sessionID, LLUUID creatorId, LLUUID otherParticipantId)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("ConferenceChatStart", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+
+ LLSD postData;
+ postData["method"] = "start p2p";
+ postData["session-id"] = sessionID;
+ postData["params"] = otherParticipantId;
+ LLSD altParams;
+ altParams["voice_server_type"] = gSavedSettings.getString("VoiceServerType");
+ postData["alt_params"] = altParams;
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (!status)
+ {
+ LL_WARNS("LLIMModel") << "Failed to start p2p session:" << postData << "->" << result << LL_ENDL;
+ // try an "old school" way.
+ // *TODO: What about other error status codes? 4xx 5xx?
+ if (status == LLCore::HttpStatus(HTTP_BAD_REQUEST))
+ {
+ static const std::string error_string("session_does_not_exist_error");
+ gIMMgr->showSessionStartError(error_string, sessionID);
+ }
+ }
+}
+
void chatterBoxInvitationCoro(std::string url, LLUUID sessionId, LLIMMgr::EInvitationType invitationType)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
@@ -643,7 +691,13 @@ LLIMModel::LLIMModel()
LLCallDialogManager::instance();
}
-LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg)
+LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id,
+ const std::string& name,
+ const EInstantMessage& type,
+ const LLUUID& other_participant_id,
+ const uuid_vec_t& ids,
+ const LLSD& voiceChannelInfo,
+ bool has_offline_msg)
: mSessionID(session_id),
mName(name),
mType(type),
@@ -658,36 +712,49 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&
mCallBackEnabled(true),
mTextIMPossible(true),
mStartCallOnInitialize(false),
- mStartedAsIMCall(voice),
+ mStartedAsIMCall(!voiceChannelInfo.isUndefined()),
mIsDNDsend(false),
mAvatarNameCacheConnection()
{
// set P2P type by default
- mSessionType = P2P_SESSION;
+ mSessionType = P2P_SESSION;
+ bool p2pAsAdhocCall = false;
if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType)
- {
- mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id);
+ {
+ LLVoiceP2POutgoingCallInterface *outgoingInterface =
+ LLVoiceClient::getInstance()->getOutgoingCallInterface(voiceChannelInfo);
+
+ if (outgoingInterface)
+ {
+ // only use LLVoiceChannelP2P if the provider can handle the special P2P interface,
+ // which uses the voice server to relay calls and invites. Otherwise,
+ // we use the group voice provider.
+ mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id, outgoingInterface);
+ }
+ else
+ {
+ p2pAsAdhocCall = true;
+ mVoiceChannel = new LLVoiceChannelGroup(session_id, name, true);
+ }
}
else
{
- mVoiceChannel = new LLVoiceChannelGroup(session_id, name);
-
// determine whether it is group or conference session
if (gAgent.isInGroup(mSessionID))
{
mSessionType = GROUP_SESSION;
+ mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false);
}
- else
+ else
{
mSessionType = ADHOC_SESSION;
+ mVoiceChannel = new LLVoiceChannelGroup(session_id, name, false);
}
}
- if(mVoiceChannel)
- {
- mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3));
- }
+ mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3));
+ mVoiceChannel->setChannelInfo(voiceChannelInfo);
mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
@@ -699,8 +766,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&
//we need to wait for session initialization for outgoing ad-hoc and group chat session
//correct session id for initiated ad-hoc chat will be received from the server
- if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID,
- mInitialTargetIDs, mType))
+ if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType, p2pAsAdhocCall))
{
//we don't need to wait for any responses
//so we're already initialized
@@ -849,22 +915,6 @@ LLIMModel::LLIMSession::~LLIMSession()
delete mSpeakers;
mSpeakers = NULL;
- // End the text IM session if necessary
- if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull())
- {
- switch(mType)
- {
- case IM_NOTHING_SPECIAL:
- case IM_SESSION_P2P_INVITE:
- LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID);
- break;
-
- default:
- // Appease the linux compiler
- break;
- }
- }
-
mVoiceChannelStateChangeConnection.disconnect();
// HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point).
@@ -1439,7 +1489,7 @@ void LLIMModel::testMessages()
//session name should not be empty
bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type,
- const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg)
+ const LLUUID& other_participant_id, const uuid_vec_t& ids, const LLSD& voiceChannelInfo, bool has_offline_msg)
{
if (name.empty())
{
@@ -1453,7 +1503,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co
return false;
}
- LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg);
+ LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voiceChannelInfo, has_offline_msg);
mId2SessionMap[session_id] = session;
// When notifying observer, name of session is used instead of "name", because they may not be the
@@ -1465,11 +1515,11 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co
}
-bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg)
+bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const LLSD& voiceChannelInfo, bool has_offline_msg)
{
uuid_vec_t ids;
ids.push_back(other_participant_id);
- return newSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg);
+ return newSession(session_id, name, type, other_participant_id, ids, voiceChannelInfo, has_offline_msg);
}
bool LLIMModel::clearSession(const LLUUID& session_id)
@@ -2018,7 +2068,8 @@ bool LLIMModel::sendStartSession(
const LLUUID& temp_session_id,
const LLUUID& other_participant_id,
const uuid_vec_t& ids,
- EInstantMessage dialog)
+ EInstantMessage dialog,
+ bool p2p_as_adhoc_call)
{
if ( dialog == IM_SESSION_GROUP_START )
{
@@ -2034,7 +2085,7 @@ bool LLIMModel::sendStartSession(
return true;
}
- else if ( dialog == IM_SESSION_CONFERENCE_START )
+ else if (dialog == IM_SESSION_CONFERENCE_START )
{
LLSD agents;
for (int i = 0; i < (S32) ids.size(); i++)
@@ -2049,8 +2100,8 @@ bool LLIMModel::sendStartSession(
std::string url = region->getCapability(
"ChatSessionRequest");
- LLCoros::instance().launch("startConfrenceCoro",
- boost::bind(&startConfrenceCoro, url,
+ LLCoros::instance().launch("startConferenceCoro",
+ boost::bind(&startConferenceCoro, url,
temp_session_id, gAgent.getID(), other_participant_id, agents));
}
else
@@ -2065,7 +2116,16 @@ bool LLIMModel::sendStartSession(
//we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id)
return true;
}
-
+ else if ((dialog == IM_SESSION_P2P_INVITE) || (dialog == IM_NOTHING_SPECIAL))
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region)
+ {
+ std::string url = region->getCapability("ChatSessionRequest");
+ LLCoros::instance().launch("startP2P", boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id));
+ }
+ return true;
+ }
return false;
}
@@ -2309,6 +2369,12 @@ void LLCallDialogManager::onVoiceChannelStateChangedInt(const LLVoiceChannel::ES
}
break;
+ case LLVoiceChannel::STATE_NO_CHANNEL_INFO :
+ // This will happen in p2p calls using the adhoc
+ // infrastructure, which marks the channel as no channel info
+ // after the call is closed, which forces a dialogue.
+ return;
+
case LLVoiceChannel::STATE_HUNG_UP:
// this state is coming before session is changed
break;
@@ -2651,15 +2717,15 @@ bool is_voice_call_type(const std::string &value)
}
LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) :
-LLCallDialog(payload),
-mAvatarNameCacheConnection()
+ LLCallDialog(payload),
+ mAvatarNameCacheConnection()
{
}
void LLIncomingCallDialog::onLifetimeExpired()
{
- std::string session_handle = mPayload["session_handle"].asString();
- if (LLVoiceClient::getInstance()->isValidChannel(session_handle))
+ LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(mPayload["voice_session_info"]);
+ if (call)
{
// restart notification's timer if call is still valid
mLifetimeTimer.start();
@@ -2836,6 +2902,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
LLUUID session_id = payload["session_id"].asUUID();
LLUUID caller_id = payload["caller_id"].asUUID();
std::string session_name = payload["session_name"].asString();
+ if (session_name.empty())
+ {
+ session_name = payload["caller_name"].asString();
+ }
EInstantMessage type = (EInstantMessage)payload["type"].asInteger();
LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger();
bool voice = true;
@@ -2852,10 +2922,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
{
// create a normal IM session
session_id = gIMMgr->addP2PSession(
- session_name,
- caller_id,
- payload["session_handle"].asString(),
- payload["session_uri"].asString());
+ session_name, caller_id, payload["voice_channel_info"]);
if (voice)
{
@@ -2905,7 +2972,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
}
}
- gIMMgr->addSession(correct_session_name, type, session_id, true);
+ gIMMgr->addSession(correct_session_name, type, session_id, payload["voice_channel_info"]);
std::string url = gAgent.getRegion()->getCapability(
"ChatSessionRequest");
@@ -2935,10 +3002,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
{
if (type == IM_SESSION_P2P_INVITE)
{
- if(LLVoiceClient::getInstance())
+ LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(payload["voice_session_info"]);
+ if (call)
{
- std::string s = payload["session_handle"].asString();
- LLVoiceClient::getInstance()->declineInvite(s);
+ call->declineInvite();
}
}
else
@@ -2961,90 +3028,6 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
}
}
-bool inviteUserResponse(const LLSD& notification, const LLSD& response)
-{
- if (!gIMMgr)
- return false;
-
- const LLSD& payload = notification["payload"];
- LLUUID session_id = payload["session_id"].asUUID();
- EInstantMessage type = (EInstantMessage)payload["type"].asInteger();
- LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger();
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- switch(option)
- {
- case 0: // accept
- {
- if (type == IM_SESSION_P2P_INVITE)
- {
- // create a normal IM session
- session_id = gIMMgr->addP2PSession(
- payload["session_name"].asString(),
- payload["caller_id"].asUUID(),
- payload["session_handle"].asString(),
- payload["session_uri"].asString());
-
- gIMMgr->startCall(session_id);
-
- gIMMgr->clearPendingAgentListUpdates(session_id);
- gIMMgr->clearPendingInvitation(session_id);
- }
- else
- {
- gIMMgr->addSession(
- payload["session_name"].asString(),
- type,
- session_id, true);
-
- std::string url = gAgent.getRegion()->getCapability(
- "ChatSessionRequest");
-
- LLCoros::instance().launch("chatterBoxInvitationCoro",
- boost::bind(&chatterBoxInvitationCoro, url,
- session_id, inv_type));
- }
- }
- break;
- case 2: // mute (also implies ignore, so this falls through to the "ignore" case below)
- {
- // mute the sender of this invite
- if (!LLMuteList::getInstance()->isMuted(payload["caller_id"].asUUID()))
- {
- LLMute mute(payload["caller_id"].asUUID(), payload["caller_name"].asString(), LLMute::AGENT);
- LLMuteList::getInstance()->add(mute);
- }
- }
- /* FALLTHROUGH */
-
- case 1: // decline
- {
- if (type == IM_SESSION_P2P_INVITE)
- {
- std::string s = payload["session_handle"].asString();
- LLVoiceClient::getInstance()->declineInvite(s);
- }
- else
- {
- std::string url = gAgent.getRegion()->getCapability(
- "ChatSessionRequest");
-
- LLSD data;
- data["method"] = "decline invitation";
- data["session-id"] = session_id;
- LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data,
- "Invitation declined.",
- "Invitation decline failed.");
- }
- }
-
- gIMMgr->clearPendingAgentListUpdates(session_id);
- gIMMgr->clearPendingInvitation(session_id);
- break;
- }
-
- return false;
-}
-
//
// Member Functions
//
@@ -3116,7 +3099,7 @@ void LLIMMgr::addMessage(
{
fixed_session_name = av_name.getDisplayName();
}
- LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg);
+ LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, LLSD(), is_offline_msg);
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id);
if (session)
@@ -3276,22 +3259,11 @@ void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id)
}
LLUUID LLIMMgr::addP2PSession(const std::string& name,
- const LLUUID& other_participant_id,
- const std::string& voice_session_handle,
- const std::string& caller_uri)
+ const LLUUID& other_participant_id,
+ const LLSD& voice_channel_info)
{
- LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id, true);
-
- LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
- if (speaker_mgr)
- {
- LLVoiceChannelP2P* voice_channel = dynamic_cast<LLVoiceChannelP2P*>(speaker_mgr->getVoiceChannel());
- if (voice_channel)
- {
- voice_channel->setSessionHandle(voice_session_handle, caller_uri);
- }
- }
- return session_id;
+ LL_DEBUGS("Voice") << "Add p2p voice channel info: " << voice_channel_info << LL_ENDL;
+ return addSession(name, IM_NOTHING_SPECIAL, other_participant_id, voice_channel_info);
}
// This adds a session to the talk view. The name is the local name of
@@ -3301,11 +3273,12 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name,
LLUUID LLIMMgr::addSession(
const std::string& name,
EInstantMessage dialog,
- const LLUUID& other_participant_id, bool voice)
+ const LLUUID& other_participant_id,
+ const LLSD& voiceChannelInfo)
{
std::vector<LLUUID> ids;
ids.push_back(other_participant_id);
- LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voice);
+ LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voiceChannelInfo);
return session_id;
}
@@ -3315,7 +3288,8 @@ LLUUID LLIMMgr::addSession(
const std::string& name,
EInstantMessage dialog,
const LLUUID& other_participant_id,
- const std::vector<LLUUID>& ids, bool voice,
+ const std::vector<LLUUID>& ids,
+ const LLSD& voiceChannelInfo,
const LLUUID& floater_id)
{
if (ids.empty())
@@ -3348,7 +3322,9 @@ LLUUID LLIMMgr::addSession(
bool new_session = (LLIMModel::getInstance()->findIMSession(session_id) == NULL);
//works only for outgoing ad-hoc sessions
- if (new_session && IM_SESSION_CONFERENCE_START == dialog && ids.size())
+ if (new_session &&
+ ((IM_NOTHING_SPECIAL == dialog) || (IM_SESSION_P2P_INVITE == dialog) || (IM_SESSION_CONFERENCE_START == dialog)) &&
+ ids.size())
{
LLIMModel::LLIMSession* ad_hoc_found = LLIMModel::getInstance()->findAdHocIMSession(ids);
if (ad_hoc_found)
@@ -3361,7 +3337,7 @@ LLUUID LLIMMgr::addSession(
//Notify observers that a session was added
if (new_session)
{
- LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice);
+ LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voiceChannelInfo);
}
//Notifies observers that the session was already added
else
@@ -3422,9 +3398,15 @@ void LLIMMgr::inviteToSession(
const std::string& caller_name,
EInstantMessage type,
EInvitationType inv_type,
- const std::string& session_handle,
- const std::string& session_uri)
+ const LLSD& voice_channel_info)
{
+
+ if (caller_id == gAgentID)
+ {
+ // ignore invites from ourself.
+ return;
+ }
+
std::string notify_box_type;
// voice invite question is different from default only for group call (EXT-7118)
std::string question_type = "VoiceInviteQuestionDefault";
@@ -3465,11 +3447,12 @@ void LLIMMgr::inviteToSession(
payload["caller_name"] = caller_name;
payload["type"] = type;
payload["inv_type"] = inv_type;
- payload["session_handle"] = session_handle;
- payload["session_uri"] = session_uri;
+ payload["voice_channel_info"] = voice_channel_info;
payload["notify_box_type"] = notify_box_type;
payload["question_type"] = question_type;
+ LL_WARNS("Voice") << "INVITE PAYLOAD: " << payload << LL_ENDL;
+
//ignore invites from muted residents
if (!is_linden)
{
@@ -3518,7 +3501,7 @@ void LLIMMgr::inviteToSession(
fixed_session_name = av_name.getDisplayName();
}
}
- LLIMModel::getInstance()->newSession(session_id, fixed_session_name, IM_NOTHING_SPECIAL, caller_id, false, false);
+ LLIMModel::getInstance()->newSession(session_id, fixed_session_name, IM_NOTHING_SPECIAL, caller_id, LLSD(), false);
}
LLSD args;
@@ -3747,7 +3730,6 @@ bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection dir
{
LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id);
if (!voice_channel) return false;
-
voice_channel->setCallDirection(direction);
voice_channel->activate();
return true;
@@ -4144,13 +4126,16 @@ public:
return;
}
+ BOOL session_type_p2p = input["body"]["voice"].get("invitation_type").asInteger() == EMultiAgentChatSessionType::P2P_CHAT_SESSION;
+ LL_DEBUGS("Voice") << "Received P2P voice information from the server: " << input["body"]<< LL_ENDL;
gIMMgr->inviteToSession(
input["body"]["session_id"].asUUID(),
input["body"]["session_name"].asString(),
input["body"]["from_id"].asUUID(),
input["body"]["from_name"].asString(),
- IM_SESSION_INVITE,
- LLIMMgr::INVITATION_TYPE_VOICE);
+ session_type_p2p ? IM_SESSION_P2P_INVITE : IM_SESSION_INVITE,
+ LLIMMgr::INVITATION_TYPE_VOICE,
+ input["body"]["voice"]);
}
else if ( input["body"].has("immediate") )
{
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index bace97d37a..93a1a95b23 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -80,7 +80,7 @@ public:
} SType;
LLIMSession(const LLUUID& session_id, const std::string& name,
- const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg);
+ const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, const LLSD& voiceChannelInfo, bool has_offline_msg);
virtual ~LLIMSession();
void sessionInitReplyReceived(const LLUUID& new_session_id);
@@ -199,10 +199,10 @@ public:
* @param name session name should not be empty, will return false if empty
*/
bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id,
- const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false);
+ const uuid_vec_t& ids, const LLSD& voiceChannelInfo = LLSD(), bool has_offline_msg = false);
- bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type,
- const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false);
+ bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID &other_participant_id,
+ const LLSD &voiceChannelInfo = LLSD(), bool has_offline_msg = false);
/**
* Remove all session data associated with a session specified by session_id
@@ -296,7 +296,7 @@ public:
static void sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id);
static bool sendStartSession(const LLUUID& temp_session_id, const LLUUID& other_participant_id,
- const uuid_vec_t& ids, EInstantMessage dialog);
+ const uuid_vec_t& ids, EInstantMessage dialog, bool p2p_as_adhoc_call);
static void sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing);
static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id,
const LLUUID& other_participant_id, EInstantMessage dialog);
@@ -379,7 +379,8 @@ public:
// session.
LLUUID addSession(const std::string& name,
EInstantMessage dialog,
- const LLUUID& other_participant_id, bool voice = false);
+ const LLUUID& other_participant_id,
+ const LLSD& voiceChannelInfo = LLSD());
// Adds a session using a specific group of starting agents
// the dialog type is assumed correct. Returns the uuid of the session.
@@ -387,7 +388,8 @@ public:
LLUUID addSession(const std::string& name,
EInstantMessage dialog,
const LLUUID& other_participant_id,
- const std::vector<LLUUID>& ids, bool voice = false,
+ const std::vector<LLUUID> &ids,
+ const LLSD& voiceChannelInfo = LLSD(),
const LLUUID& floater_id = LLUUID::null);
/**
@@ -397,10 +399,7 @@ public:
* @param caller_uri - sip URI of caller. It should be always be passed into the method to avoid
* incorrect working of LLVoiceChannel instances. See EXT-2985.
*/
- LLUUID addP2PSession(const std::string& name,
- const LLUUID& other_participant_id,
- const std::string& voice_session_handle,
- const std::string& caller_uri);
+ LLUUID addP2PSession(const std::string &name, const LLUUID &other_participant_id, const LLSD &voice_call_info);
/**
* Leave the session with session id. Send leave session notification
@@ -415,9 +414,9 @@ public:
const LLUUID& caller,
const std::string& caller_name,
EInstantMessage type,
- EInvitationType inv_type,
- const std::string& session_handle = LLStringUtil::null,
- const std::string& session_uri = LLStringUtil::null);
+ EInvitationType inv_type,
+ const LLSD &voice_channel_info = LLSD()
+ );
void processIMTypingStart(const LLUUID& from_id, const EInstantMessage im_type);
void processIMTypingStop(const LLUUID& from_id, const EInstantMessage im_type);
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
index ab255d5215..a06b50390f 100644
--- a/indra/newview/llpanelgroup.cpp
+++ b/indra/newview/llpanelgroup.cpp
@@ -276,7 +276,7 @@ void LLPanelGroup::changed(LLGroupChange gc)
}
// virtual
-void LLPanelGroup::onChange(EStatusType status, const std::string &channelURI, bool proximal)
+void LLPanelGroup::onChange(EStatusType status, const LLSD& channelInfo, bool proximal)
{
if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
{
diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h
index be40b08a6d..3ca6426887 100644
--- a/indra/newview/llpanelgroup.h
+++ b/indra/newview/llpanelgroup.h
@@ -62,7 +62,7 @@ public:
// Implements LLVoiceClientStatusObserver::onChange() to enable the call
// button when voice is available
- /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ /*virtual*/ void onChange(EStatusType status, const LLSD& channelInfo, bool proximal);
void showNotice(const std::string& subject,
const std::string& message,
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 13b52e97c5..27019badd2 100644
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -733,7 +733,7 @@ BOOL LLPanelPeople::postBuild()
}
// virtual
-void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI, bool proximal)
+void LLPanelPeople::onChange(EStatusType status, const LLSD& channelInfo, bool proximal)
{
if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
{
diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h
index 14205cebe2..e4484b66c6 100644
--- a/indra/newview/llpanelpeople.h
+++ b/indra/newview/llpanelpeople.h
@@ -55,7 +55,7 @@ public:
/*virtual*/ bool notifyChildren(const LLSD& info);
// Implements LLVoiceClientStatusObserver::onChange() to enable call buttons
// when voice is available
- /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ /*virtual*/ void onChange(EStatusType status, const LLSD& channelInfo, bool proximal);
// internals
class Updater;
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index ffbed778c1..132b710620 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1446,7 +1446,7 @@ void LLPanelProfileSecondLife::changed(U32 mask)
}
// virtual, called by LLVoiceClient
-void LLPanelProfileSecondLife::onChange(EStatusType status, const std::string &channelURI, bool proximal)
+void LLPanelProfileSecondLife::onChange(EStatusType status, const LLSD& channelInfo, bool proximal)
{
if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
{
diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h
index 11632a10ae..ea2bb25ac2 100644
--- a/indra/newview/llpanelprofile.h
+++ b/indra/newview/llpanelprofile.h
@@ -85,7 +85,7 @@ public:
// Implements LLVoiceClientStatusObserver::onChange() to enable the call
// button when voice is available
- void onChange(EStatusType status, const std::string &channelURI, bool proximal) override;
+ void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override;
void setAvatarId(const LLUUID& avatar_id) override;
diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp
index 28631e2b7b..1ecf670179 100644
--- a/indra/newview/llpanelvoicedevicesettings.cpp
+++ b/indra/newview/llpanelvoicedevicesettings.cpp
@@ -236,43 +236,46 @@ void LLPanelVoiceDeviceSettings::refresh()
if(mCtrlInputDevices)
{
- mCtrlInputDevices->removeall();
- mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
-
- for(device=LLVoiceClient::getInstance()->getCaptureDevices().begin();
- device != LLVoiceClient::getInstance()->getCaptureDevices().end();
- device++)
+ LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices();
+ if (devices.size() > 0) // if zero, we've not received our devices yet
{
- mCtrlInputDevices->add(getLocalizedDeviceName(device->display_name), device->full_name, ADD_BOTTOM);
- }
+ mCtrlInputDevices->removeall();
+ mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
+ for (auto& device : devices)
+ {
+ mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM);
+ }
- // Fix invalid input audio device preference.
- if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE))
- {
- mCtrlInputDevices->setValue(DEFAULT_DEVICE);
- gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE);
- mInputDevice = DEFAULT_DEVICE;
+ // Fix invalid input audio device preference.
+ if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE))
+ {
+ mCtrlInputDevices->setValue(DEFAULT_DEVICE);
+ gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE);
+ mInputDevice = DEFAULT_DEVICE;
+ }
}
}
if(mCtrlOutputDevices)
{
- mCtrlOutputDevices->removeall();
- mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
-
- for(device = LLVoiceClient::getInstance()->getRenderDevices().begin();
- device != LLVoiceClient::getInstance()->getRenderDevices().end();
- device++)
+ LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices();
+ if (devices.size() > 0) // if zero, we've not received our devices yet
{
- mCtrlOutputDevices->add(getLocalizedDeviceName(device->display_name), device->full_name, ADD_BOTTOM);
- }
+ mCtrlOutputDevices->removeall();
+ mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
- // Fix invalid output audio device preference.
- if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE))
- {
- mCtrlOutputDevices->setValue(DEFAULT_DEVICE);
- gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE);
- mOutputDevice = DEFAULT_DEVICE;
+ for (auto& device : devices)
+ {
+ mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM);
+ }
+
+ // Fix invalid output audio device preference.
+ if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE))
+ {
+ mCtrlOutputDevices->setValue(DEFAULT_DEVICE);
+ gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE);
+ mOutputDevice = DEFAULT_DEVICE;
+ }
}
}
}
@@ -316,8 +319,11 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice()
if(LLVoiceClient::getInstance())
{
mInputDevice = mCtrlInputDevices->getValue().asString();
- LLVoiceClient::getInstance()->setRenderDevice(mInputDevice);
+ LLVoiceClient::getInstance()->setCaptureDevice(mInputDevice);
}
+ // the preferences floater stuff is a mess, hence apply will never
+ // be called when 'ok' is pressed, so just force it for now.
+ apply();
}
void LLPanelVoiceDeviceSettings::onCommitOutputDevice()
@@ -328,6 +334,9 @@ void LLPanelVoiceDeviceSettings::onCommitOutputDevice()
mOutputDevice = mCtrlOutputDevices->getValue().asString();
LLVoiceClient::getInstance()->setRenderDevice(mOutputDevice);
}
+ // the preferences floater stuff is a mess, hence apply will never
+ // be called when 'ok' is pressed, so just force it for now.
+ apply();
}
void LLPanelVoiceDeviceSettings::onOutputDevicesClicked()
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index d0b76848f7..e86ea5f2e3 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -3337,7 +3337,7 @@ LLSD transform_cert_args(LLPointer<LLCertificate> cert)
// are actually arrays, and we want to format them as comma separated
// strings, so special case those.
LLSDSerialize::toXML(cert_info[iter->first], std::cout);
- if((iter->first == std::string(CERT_KEY_USAGE)) ||
+ if((iter->first== std::string(CERT_KEY_USAGE)) ||
(iter->first == std::string(CERT_EXTENDED_KEY_USAGE)))
{
value = "";
diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp
index 3181ae6283..6374d68988 100644
--- a/indra/newview/llviewerhelp.cpp
+++ b/indra/newview/llviewerhelp.cpp
@@ -45,7 +45,7 @@ public:
// requests will be throttled from a non-trusted browser
LLHelpHandler() : LLCommandHandler("help", UNTRUSTED_CLICK_ONLY) {}
- bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web)
+ bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) override
{
LLViewerHelp* vhelp = LLViewerHelp::getInstance();
if (! vhelp)
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 452dcdd8fd..39aaac57c1 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3136,6 +3136,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("ParcelVoiceInfoRequest");
capabilityNames.append("ProductInfoRequest");
capabilityNames.append("ProvisionVoiceAccountRequest");
+ capabilityNames.append("VoiceSignalingRequest");
capabilityNames.append("ReadOfflineMsgs"); // Requires to respond reliably: AcceptFriendship, AcceptGroupInvite, DeclineFriendship, DeclineGroupInvite
capabilityNames.append("RegionObjects");
capabilityNames.append("RemoteParcelRequest");
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index a409d837a4..5817bd7d8d 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -43,6 +43,7 @@
#include "m4math.h" // LLMatrix4
#include "llframetimer.h"
#include "llreflectionmap.h"
+#include "llpointer.h"
// Surface id's
#define LAND 1
@@ -545,7 +546,7 @@ public:
U8 mCentralBakeVersion;
LLVOCacheEntry* mLastVisitedEntry;
- U32 mInvisibilityCheckHistory;
+ U32 mInvisibilityCheckHistory;
// Information for Homestead / CR-53
S32 mClassID;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index fee00eb6f4..a600482d5b 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -614,7 +614,8 @@ BOOL LLVOAvatar::sShowAnimationDebug = FALSE;
BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE;
F32 LLVOAvatar::sLODFactor = 1.f;
F32 LLVOAvatar::sPhysicsLODFactor = 1.f;
-BOOL LLVOAvatar::sJointDebug = FALSE;
+BOOL LLVOAvatar::sJointDebug = FALSE;
+BOOL LLVOAvatar::sLipSyncEnabled = FALSE;
F32 LLVOAvatar::sUnbakedTime = 0.f;
F32 LLVOAvatar::sUnbakedUpdateTime = 0.f;
F32 LLVOAvatar::sGreyTime = 0.f;
@@ -1155,6 +1156,7 @@ void LLVOAvatar::initClass()
LLControlAvatar::sRegionChangedSlot = gAgent.addRegionChangedCallback(&LLControlAvatar::onRegionChanged);
sCloudTexture = LLViewerTextureManager::getFetchedTextureFromFile("cloud-particle.j2c");
+ gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&LLVOAvatar::handleVOAvatarPrefsChanged, _2));
}
@@ -1162,6 +1164,12 @@ void LLVOAvatar::cleanupClass()
{
}
+bool LLVOAvatar::handleVOAvatarPrefsChanged(const LLSD &newvalue)
+{
+ sLipSyncEnabled = gSavedSettings.getBOOL("LipSyncEnabled");
+ return true;
+}
+
// virtual
void LLVOAvatar::initInstance()
{
@@ -3072,7 +3080,7 @@ void LLVOAvatar::idleUpdateLipSync(bool voice_enabled)
// Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync
if ( voice_enabled
&& mLastRezzedStatus > 0 // no point updating lip-sync for clouds
- && (LLVoiceClient::getInstance()->lipSyncEnabled())
+ && sLipSyncEnabled
&& LLVoiceClient::getInstance()->getIsSpeaking( mID ) )
{
F32 ooh_morph_amount = 0.0f;
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 48bfd5293a..47526707dd 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -106,9 +106,10 @@ public:
virtual void markDead();
static void initClass(); // Initialize data that's only init'd once per class.
static void cleanupClass(); // Cleanup data that's only init'd once per class.
- virtual void initInstance(); // Called after construction to initialize the class.
+ virtual void initInstance(); // Called after construction to initialize the class.
protected:
virtual ~LLVOAvatar();
+ static bool handleVOAvatarPrefsChanged(const LLSD &newvalue);
/** Initialization
** **
@@ -365,6 +366,7 @@ public:
static F32 sLODFactor; // user-settable LOD factor
static F32 sPhysicsLODFactor; // user-settable physics LOD factor
static BOOL sJointDebug; // output total number of joints being touched for each avatar
+ static BOOL sLipSyncEnabled;
static LLPointer<LLViewerTexture> sCloudTexture;
diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp
index b0eb8d962c..28e895584b 100644
--- a/indra/newview/llvoicechannel.cpp
+++ b/indra/newview/llvoicechannel.cpp
@@ -39,7 +39,6 @@
#include "llcorehttputil.h"
LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap;
-LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap;
LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL;
LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL;
LLVoiceChannel::channel_changed_signal_t LLVoiceChannel::sCurrentVoiceChannelChangedSignal;
@@ -89,29 +88,18 @@ LLVoiceChannel::~LLVoiceChannel()
}
sVoiceChannelMap.erase(mSessionID);
- sVoiceChannelURIMap.erase(mURI);
}
-void LLVoiceChannel::setChannelInfo(
- const std::string& uri,
- const std::string& credentials)
+void LLVoiceChannel::setChannelInfo(const LLSD &channelInfo)
{
- setURI(uri);
-
- mCredentials = credentials;
+ mChannelInfo = channelInfo;
if (mState == STATE_NO_CHANNEL_INFO)
{
- if (mURI.empty())
- {
- LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
- LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL;
- deactivate();
- }
- else if (mCredentials.empty())
+ if (mChannelInfo.isUndefined())
{
LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
- LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL;
+ LL_WARNS("Voice") << "Received empty channel info for channel " << mSessionName << LL_ENDL;
deactivate();
}
else
@@ -130,9 +118,15 @@ void LLVoiceChannel::setChannelInfo(
}
}
-void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal)
+void LLVoiceChannel::onChange(EStatusType type, const LLSD& channelInfo, bool proximal)
{
- if (channelURI != mURI)
+ LL_DEBUGS("Voice") << "Incoming channel info: " << channelInfo << LL_ENDL;
+ LL_DEBUGS("Voice") << "Current channel info: " << mChannelInfo << LL_ENDL;
+ if (mChannelInfo.isUndefined())
+ {
+ mChannelInfo = channelInfo;
+ }
+ if (!LLVoiceClient::getInstance()->compareChannels(mChannelInfo, channelInfo))
{
return;
}
@@ -153,7 +147,7 @@ void LLVoiceChannel::handleStatusChange(EStatusType type)
switch(type)
{
case STATUS_LOGIN_RETRY:
- // no user notice
+ // no user notice
break;
case STATUS_LOGGED_IN:
break;
@@ -193,7 +187,7 @@ void LLVoiceChannel::handleError(EStatusType type)
BOOL LLVoiceChannel::isActive()
{
// only considered active when currently bound channel matches what our channel
- return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI;
+ return callStarted() && LLVoiceClient::getInstance()->isCurrentChannel(mChannelInfo);
}
BOOL LLVoiceChannel::callStarted()
@@ -246,10 +240,8 @@ void LLVoiceChannel::activate()
// activating the proximal channel between IM calls
LLVoiceChannel* old_channel = sCurrentVoiceChannel;
sCurrentVoiceChannel = this;
- mCallDialogPayload["old_channel_name"] = "";
if (old_channel)
{
- mCallDialogPayload["old_channel_name"] = old_channel->getSessionName();
old_channel->deactivate();
}
}
@@ -257,7 +249,7 @@ void LLVoiceChannel::activate()
if (mState == STATE_NO_CHANNEL_INFO)
{
// responsible for setting status to active
- getChannelInfo();
+ requestChannelInfo();
}
else
{
@@ -270,7 +262,7 @@ void LLVoiceChannel::activate()
sCurrentVoiceChannelChangedSignal(this->mSessionID);
}
-void LLVoiceChannel::getChannelInfo()
+void LLVoiceChannel::requestChannelInfo()
{
// pretend we have everything we need
if (sCurrentVoiceChannel == this)
@@ -293,20 +285,6 @@ LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id)
}
}
-//static
-LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri)
-{
- voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri);
- if (found_it == sVoiceChannelURIMap.end())
- {
- return NULL;
- }
- else
- {
- return found_it->second;
- }
-}
-
LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel()
{
return sCurrentVoiceChannel;
@@ -319,13 +297,6 @@ void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)
sVoiceChannelMap.insert(std::make_pair(mSessionID, this));
}
-void LLVoiceChannel::setURI(std::string uri)
-{
- sVoiceChannelURIMap.erase(mURI);
- mURI = uri;
- sVoiceChannelURIMap.insert(std::make_pair(mURI, this));
-}
-
void LLVoiceChannel::setState(EState state)
{
switch(state)
@@ -410,8 +381,11 @@ boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallbac
// LLVoiceChannelGroup
//
-LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) :
- LLVoiceChannel(session_id, session_name)
+LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID &session_id,
+ const std::string &session_name,
+ bool is_p2p) :
+ LLVoiceChannel(session_id, session_name),
+ mIsP2P(is_p2p)
{
mRetries = DEFAULT_RETRIES_COUNT;
mIsRetrying = FALSE;
@@ -424,7 +398,15 @@ void LLVoiceChannelGroup::deactivate()
LLVoiceClient::getInstance()->leaveNonSpatialChannel();
}
LLVoiceChannel::deactivate();
-}
+
+ if (mIsP2P)
+ {
+ // void the channel info for p2p adhoc channels
+ // so we request it again, hence throwing up the
+ // connect dialogue on the other side.
+ setState(STATE_NO_CHANNEL_INFO);
+ }
+ }
void LLVoiceChannelGroup::activate()
{
@@ -435,65 +417,64 @@ void LLVoiceChannelGroup::activate()
if (callStarted())
{
// we have the channel info, just need to use it now
- LLVoiceClient::getInstance()->setNonSpatialChannel(
- mURI,
- mCredentials);
+ LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo,
+ mCallDirection == OUTGOING_CALL,
+ mIsP2P);
- if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel
+ if (mIsP2P)
{
- LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionID);
- // Adding ad-hoc call participants to Recent People List.
- // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we
- // called(both online and offline) as source to get people for recent (STORM-210).
- if (session->isOutgoingAdHoc())
+ LLIMModel::addSpeakersToRecent(mSessionID);
+ }
+ else
+ {
+ if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel
{
- for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();
- it!=session->mInitialTargetIDs.end();++it)
+ LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(mSessionID);
+ // Adding ad-hoc call participants to Recent People List.
+ // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we
+ // called(both online and offline) as source to get people for recent (STORM-210).
+ if (session->isOutgoingAdHoc())
{
- const LLUUID id = *it;
- LLRecentPeople::instance().add(id);
+ for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); it != session->mInitialTargetIDs.end(); ++it)
+ {
+ const LLUUID id = *it;
+ LLRecentPeople::instance().add(id);
+ }
+ }
+ // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs
+ // would lead to EXT-8246. So in this case we get them from speakers list.
+ else
+ {
+ LLIMModel::addSpeakersToRecent(mSessionID);
}
- }
- // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs
- // would lead to EXT-8246. So in this case we get them from speakers list.
- else
- {
- LLIMModel::addSpeakersToRecent(mSessionID);
}
}
- //Mic default state is OFF on initiating/joining Ad-Hoc/Group calls
- if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())
- {
- LLVoiceClient::getInstance()->inputUserControlState(true);
- }
-
+ // Mic default state is OFF on initiating/joining Ad-Hoc/Group calls. It's on for P2P using the AdHoc infra.
+
+ LLVoiceClient::getInstance()->setUserPTTState(mIsP2P);
}
}
-void LLVoiceChannelGroup::getChannelInfo()
+void LLVoiceChannelGroup::requestChannelInfo()
{
LLViewerRegion* region = gAgent.getRegion();
if (region)
{
std::string url = region->getCapability("ChatSessionRequest");
- LLCoros::instance().launch("LLVoiceChannelGroup::voiceCallCapCoro",
- boost::bind(&LLVoiceChannelGroup::voiceCallCapCoro, this, url));
+ LLCoros::instance().launch("LLVoiceChannelGroup::voiceCallCapCoro",
+ boost::bind(&LLVoiceChannelGroup::voiceCallCapCoro, this, url));
}
}
-void LLVoiceChannelGroup::setChannelInfo(
- const std::string& uri,
- const std::string& credentials)
+void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo)
{
- setURI(uri);
-
- mCredentials = credentials;
+ mChannelInfo = channelInfo;
if (mState == STATE_NO_CHANNEL_INFO)
{
- if(!mURI.empty() && !mCredentials.empty())
+ if(!mChannelInfo.isUndefined())
{
setState(STATE_READY);
@@ -516,9 +497,9 @@ void LLVoiceChannelGroup::setChannelInfo(
else if ( mIsRetrying )
{
// we have the channel info, just need to use it now
- LLVoiceClient::getInstance()->setNonSpatialChannel(
- mURI,
- mCredentials);
+ LLVoiceClient::getInstance()->setNonSpatialChannel(channelInfo,
+ mCallDirection == OUTGOING_CALL,
+ mIsP2P);
}
}
@@ -556,7 +537,7 @@ void LLVoiceChannelGroup::handleError(EStatusType status)
mIsRetrying = TRUE;
mIgnoreNextSessionLeave = TRUE;
- getChannelInfo();
+ requestChannelInfo();
return;
}
else
@@ -604,61 +585,62 @@ void LLVoiceChannelGroup::setState(EState state)
void LLVoiceChannelGroup::voiceCallCapCoro(std::string url)
{
- LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
- LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceCallCapCoro", httpPolicy));
- LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceCallCapCoro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
- LLSD postData;
- postData["method"] = "call";
- postData["session-id"] = mSessionID;
+ LLSD postData;
+ postData["method"] = "call";
+ postData["session-id"] = mSessionID;
+ LLSD altParams;
+ altParams["preferred_voice_server_type"] = gSavedSettings.getString("VoiceServerType");
+ postData["alt_params"] = altParams;
- LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL;
+ LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL;
- LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData);
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData);
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
- LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
- if (!channelp)
- {
- LL_WARNS("Voice") << "Unable to retrieve channel with Id = " << mSessionID << LL_ENDL;
- return;
- }
-
- if (!status)
- {
- if (status == LLCore::HttpStatus(HTTP_FORBIDDEN))
- {
- //403 == no ability
- LLNotificationsUtil::add(
- "VoiceNotAllowed",
- channelp->getNotifyArgs());
- }
- else
- {
- LLNotificationsUtil::add(
- "VoiceCallGenericError",
- channelp->getNotifyArgs());
- }
- channelp->deactivate();
- return;
- }
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
+ if (!channelp)
+ {
+ LL_WARNS("Voice") << "Unable to retrieve channel with Id = " << mSessionID << LL_ENDL;
+ return;
+ }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
+ if (!status)
+ {
+ if (status == LLCore::HttpStatus(HTTP_FORBIDDEN))
+ {
+ //403 == no ability
+ LLNotificationsUtil::add(
+ "VoiceNotAllowed",
+ channelp->getNotifyArgs());
+ }
+ else
+ {
+ LLNotificationsUtil::add(
+ "VoiceCallGenericError",
+ channelp->getNotifyArgs());
+ }
+ channelp->deactivate();
+ return;
+ }
- LLSD::map_const_iterator iter;
- for (iter = result.beginMap(); iter != result.endMap(); ++iter)
- {
- LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got "
- << iter->first << LL_ENDL;
- }
+ result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- channelp->setChannelInfo(
- result["voice_credentials"]["channel_uri"].asString(),
- result["voice_credentials"]["channel_credentials"].asString());
+ LLSD::map_const_iterator iter;
+ for (iter = result.beginMap(); iter != result.endMap(); ++iter)
+ {
+ LL_DEBUGS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got "
+ << iter->first << LL_ENDL;
+ }
+ LL_INFOS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " << result << LL_ENDL;
+ channelp->setChannelInfo(result["voice_credentials"]);
}
@@ -682,13 +664,14 @@ void LLVoiceChannelProximal::activate()
if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED))
{
// we're connected to a non-spatial channel, so disconnect.
- LLVoiceClient::getInstance()->leaveNonSpatialChannel();
+ LLVoiceClient::getInstance()->leaveNonSpatialChannel();
}
+ LLVoiceClient::getInstance()->activateSpatialChannel(true);
LLVoiceChannel::activate();
}
-void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal)
+void LLVoiceChannelProximal::onChange(EStatusType type, const LLSD& channelInfo, bool proximal)
{
if (!proximal)
{
@@ -758,19 +741,22 @@ void LLVoiceChannelProximal::deactivate()
{
setState(STATE_HUNG_UP);
}
+ LLVoiceClient::getInstance()->activateSpatialChannel(false);
}
//
// LLVoiceChannelP2P
//
-LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) :
- LLVoiceChannelGroup(session_id, session_name),
- mOtherUserID(other_user_id),
- mReceivedCall(FALSE)
+LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID &session_id,
+ const std::string &session_name,
+ const LLUUID &other_user_id,
+ LLVoiceP2POutgoingCallInterface* outgoing_call_interface) :
+ LLVoiceChannelGroup(session_id, session_name, true),
+ mOtherUserID(other_user_id),
+ mReceivedCall(FALSE),
+ mOutgoingCallInterface(outgoing_call_interface)
{
- // make sure URI reflects encoded version of other user's agent id
- setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));
}
void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
@@ -837,23 +823,23 @@ void LLVoiceChannelP2P::activate()
if (callStarted())
{
// no session handle yet, we're starting the call
- if (mSessionHandle.empty())
+ if (mIncomingCallInterface == nullptr)
{
mReceivedCall = FALSE;
- LLVoiceClient::getInstance()->callUser(mOtherUserID);
+ mOutgoingCallInterface->callUser(mOtherUserID);
}
// otherwise answering the call
else
{
- if (!LLVoiceClient::getInstance()->answerInvite(mSessionHandle))
+ if (!mIncomingCallInterface->answerInvite())
{
mCallEndedByAgent = false;
- mSessionHandle.clear();
+ mIncomingCallInterface.reset();
handleError(ERROR_UNKNOWN);
return;
}
- // using the session handle invalidates it. Clear it out here so we can't reuse it by accident.
- mSessionHandle.clear();
+ // using the incoming call interface invalidates it. Clear it out here so we can't reuse it by accident.
+ mIncomingCallInterface.reset();
}
// Add the party to the list of people with which we've recently interacted.
@@ -867,7 +853,17 @@ void LLVoiceChannelP2P::activate()
}
}
-void LLVoiceChannelP2P::getChannelInfo()
+void LLVoiceChannelP2P::deactivate()
+{
+ if (callStarted())
+ {
+ mOutgoingCallInterface->hangup();
+ }
+ LLVoiceChannel::deactivate();
+}
+
+
+void LLVoiceChannelP2P::requestChannelInfo()
{
// pretend we have everything we need, since P2P doesn't use channel info
if (sCurrentVoiceChannel == this)
@@ -877,8 +873,9 @@ void LLVoiceChannelP2P::getChannelInfo()
}
// receiving session from other user who initiated call
-void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI)
+void LLVoiceChannelP2P::setChannelInfo(const LLSD& channel_info)
{
+ mChannelInfo = channel_info;
BOOL needs_activate = FALSE;
if (callStarted())
{
@@ -893,28 +890,16 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s
{
// we are active and have priority, invite the other user again
// under the assumption they will join this new session
- mSessionHandle.clear();
- LLVoiceClient::getInstance()->callUser(mOtherUserID);
+ mOutgoingCallInterface->callUser(mOtherUserID);
return;
}
}
- mSessionHandle = handle;
-
- // The URI of a p2p session should always be the other end's SIP URI.
- if(!inURI.empty())
- {
- setURI(inURI);
- }
- else
+ mReceivedCall = TRUE;
+ if (!channel_info.isUndefined())
{
- LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL;
- // See LLVoiceClient::sessionAddedEvent()
- setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
+ mIncomingCallInterface = LLVoiceClient::getInstance()->getIncomingCallInterface(channel_info);
}
-
- mReceivedCall = TRUE;
-
if (needs_activate)
{
activate();
@@ -932,7 +917,7 @@ void LLVoiceChannelP2P::setState(EState state)
if (mReceivedCall && state == STATE_RINGING)
{
//TODO: remove or redirect this call status notification
-// LLCallInfoDialog::show("answering", mNotifyArgs);
+ // LLCallInfoDialog::show("answering", mNotifyArgs);
doSetState(state);
return;
}
diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h
index e68bfbe1ff..bc22bf0df6 100644
--- a/indra/newview/llvoicechannel.h
+++ b/indra/newview/llvoicechannel.h
@@ -66,16 +66,14 @@ public:
LLVoiceChannel(const LLUUID& session_id, const std::string& session_name);
virtual ~LLVoiceChannel();
- /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ virtual void onChange(EStatusType status, const LLSD& channelInfo, bool proximal);
virtual void handleStatusChange(EStatusType status);
virtual void handleError(EStatusType status);
virtual void deactivate();
virtual void activate();
- virtual void setChannelInfo(
- const std::string& uri,
- const std::string& credentials);
- virtual void getChannelInfo();
+ virtual void setChannelInfo(const LLSD &channelInfo);
+ virtual void requestChannelInfo();
virtual BOOL isActive();
virtual BOOL callStarted();
@@ -96,43 +94,37 @@ public:
EDirection getCallDirection() {return mCallDirection;}
static LLVoiceChannel* getChannelByID(const LLUUID& session_id);
- static LLVoiceChannel* getChannelByURI(std::string uri);
static LLVoiceChannel* getCurrentVoiceChannel();
-
+
static void initClass();
-
+
static void suspend();
static void resume();
-protected:
+ protected:
virtual void setState(EState state);
/**
* Use this method if you want mStateChangedCallback to be executed while state is changed
*/
void doSetState(const EState& state);
- void setURI(std::string uri);
// there can be two directions INCOMING and OUTGOING
EDirection mCallDirection;
- std::string mURI;
- std::string mCredentials;
LLUUID mSessionID;
EState mState;
std::string mSessionName;
- LLSD mNotifyArgs;
- LLSD mCallDialogPayload;
+ LLSD mNotifyArgs;
+ LLSD mChannelInfo;
// true if call was ended by agent
bool mCallEndedByAgent;
- BOOL mIgnoreNextSessionLeave;
+ bool mIgnoreNextSessionLeave;
LLHandle<LLPanel> mLoginNotificationHandle;
typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t;
static voice_channel_map_t sVoiceChannelMap;
- typedef std::map<std::string, LLVoiceChannel*> voice_channel_map_uri_t;
- static voice_channel_map_uri_t sVoiceChannelURIMap;
-
+ static LLVoiceChannel* sProximalVoiceChannel;
static LLVoiceChannel* sCurrentVoiceChannel;
static LLVoiceChannel* sSuspendedVoiceChannel;
static BOOL sSuspended;
@@ -144,55 +136,58 @@ private:
class LLVoiceChannelGroup : public LLVoiceChannel
{
public:
- LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name);
+ LLVoiceChannelGroup(const LLUUID& session_id,
+ const std::string& session_name,
+ bool is_p2p);
- /*virtual*/ void handleStatusChange(EStatusType status);
- /*virtual*/ void handleError(EStatusType status);
- /*virtual*/ void activate();
- /*virtual*/ void deactivate();
- /*vritual*/ void setChannelInfo(
- const std::string& uri,
- const std::string& credentials);
- /*virtual*/ void getChannelInfo();
+ void handleStatusChange(EStatusType status) override;
+ void handleError(EStatusType status) override;
+ void activate() override;
+ void deactivate() override;
+ void setChannelInfo(const LLSD &channelInfo) override;
+ void requestChannelInfo() override;
protected:
- virtual void setState(EState state);
+ void setState(EState state) override;
private:
void voiceCallCapCoro(std::string url);
U32 mRetries;
BOOL mIsRetrying;
+ bool mIsP2P;
};
class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal>
{
- LLSINGLETON(LLVoiceChannelProximal);
-public:
-
- /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal) override;
- /*virtual*/ void handleStatusChange(EStatusType status) override;
- /*virtual*/ void handleError(EStatusType status) override;
- /*virtual*/ BOOL isActive() override;
- /*virtual*/ void activate() override;
- /*virtual*/ void deactivate() override;
-
+ LLSINGLETON_C11(LLVoiceChannelProximal);
+ public:
+
+ void onChange(EStatusType status, const LLSD &channelInfo, bool proximal) override;
+ void handleStatusChange(EStatusType status) override;
+ void handleError(EStatusType status) override;
+ BOOL isActive() override;
+ void activate() override;
+ void deactivate() override;
};
class LLVoiceChannelP2P : public LLVoiceChannelGroup
{
-public:
- LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id);
-
- /*virtual*/ void handleStatusChange(EStatusType status) override;
- /*virtual*/ void handleError(EStatusType status) override;
- /*virtual*/ void activate() override;
- /*virtual*/ void getChannelInfo() override;
-
- void setSessionHandle(const std::string& handle, const std::string &inURI);
-
-protected:
- virtual void setState(EState state) override;
+ public:
+ LLVoiceChannelP2P(const LLUUID &session_id,
+ const std::string &session_name,
+ const LLUUID &other_user_id,
+ LLVoiceP2POutgoingCallInterface * outgoing_call_interface);
+
+ void handleStatusChange(EStatusType status) override;
+ void handleError(EStatusType status) override;
+ void activate() override;
+ void requestChannelInfo() override;
+ void deactivate() override;
+ void setChannelInfo(const LLSD& channel_info) override;
+
+ protected:
+ void setState(EState state) override;
private:
@@ -201,10 +196,10 @@ private:
*
**/
void addToTheRecentPeopleList();
-
- std::string mSessionHandle;
LLUUID mOtherUserID;
BOOL mReceivedCall;
+ LLVoiceP2POutgoingCallInterface *mOutgoingCallInterface;
+ LLVoiceP2PIncomingCallInterfacePtr mIncomingCallInterface;
};
#endif // LL_VOICECHANNEL_H
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 68d9f4ffab..b9b8742c41 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -1,36 +1,36 @@
- /**
+ /**
* @file llvoiceclient.cpp
* @brief Voice client delegation class implementation.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#include "llviewerprecompiledheaders.h"
#include "llvoiceclient.h"
-#include "llviewercontrol.h"
-#include "llviewerwindow.h"
#include "llvoicevivox.h"
+#include "llvoicewebrtc.h"
#include "llviewernetwork.h"
+#include "llviewercontrol.h"
#include "llcommandhandler.h"
+#include "lldir.h"
#include "llhttpnode.h"
#include "llnotificationsutil.h"
#include "llsdserialize.h"
@@ -81,10 +81,10 @@ LLVoiceHandler gVoiceHandler;
std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
{
std::string result = "UNTRANSLATED";
-
+
// Prevent copy-paste errors when updating this list...
#define CASE(x) case x: result = #x; break
-
+
switch(inStatus)
{
CASE(STATUS_LOGIN_RETRY);
@@ -107,19 +107,35 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv
}
break;
}
-
+
#undef CASE
-
+
return result;
}
-
+LLVoiceModuleInterface *getVoiceModule(const std::string &voice_server_type)
+{
+ if (voice_server_type == VIVOX_VOICE_SERVER_TYPE || voice_server_type.empty())
+ {
+ return (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance();
+ }
+ else if (voice_server_type == WEBRTC_VOICE_SERVER_TYPE)
+ {
+ return (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance();
+ }
+ else
+ {
+ LLNotificationsUtil::add("VoiceVersionMismatch");
+ return nullptr;
+ }
+}
///////////////////////////////////////////////////////////////////////////////////////////////
LLVoiceClient::LLVoiceClient(LLPumpIO *pump)
:
- mVoiceModule(NULL),
+ mSpatialVoiceModule(NULL),
+ mNonSpatialVoiceModule(NULL),
m_servicePump(NULL),
mVoiceEffectEnabled(LLCachedControl<bool>(gSavedSettings, "VoiceMorphingEnabled", true)),
mVoiceEffectDefault(LLCachedControl<std::string>(gSavedPerAccountSettings, "VoiceEffectDefault", "00000000-0000-0000-0000-000000000000")),
@@ -133,7 +149,6 @@ LLVoiceClient::LLVoiceClient(LLPumpIO *pump)
mMuteMic(false),
mDisableMic(false)
{
- updateSettings();
init(pump);
}
@@ -142,46 +157,139 @@ LLVoiceClient::LLVoiceClient(LLPumpIO *pump)
LLVoiceClient::~LLVoiceClient()
{
- llassert(!mVoiceModule);
+ llassert(!mSpatialVoiceModule);
}
void LLVoiceClient::init(LLPumpIO *pump)
{
// Initialize all of the voice modules
m_servicePump = pump;
+ LLWebRTCVoiceClient::getInstance()->init(pump);
+ LLVivoxVoiceClient::getInstance()->init(pump);
}
void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID)
{
- // In the future, we should change this to allow voice module registration
- // with a table lookup of sorts.
- std::string voice_server = gSavedSettings.getString("VoiceServerType");
- LL_DEBUGS("Voice") << "voice server type " << voice_server << LL_ENDL;
- if(voice_server == "vivox")
+ gAgent.addRegionChangedCallback(boost::bind(&LLVoiceClient::onRegionChanged, this));
+ LLWebRTCVoiceClient::getInstance()->userAuthorized(user_id, agentID);
+ LLVivoxVoiceClient::getInstance()->userAuthorized(user_id, agentID);
+}
+
+void LLVoiceClient::handleSimulatorFeaturesReceived(const LLSD &simulatorFeatures)
+{
+ std::string voiceServerType = simulatorFeatures["VoiceServerType"].asString();
+ if (voiceServerType.empty())
{
- mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance();
+ voiceServerType = VIVOX_VOICE_SERVER_TYPE;
}
- else
+
+ if (mSpatialVoiceModule && !mNonSpatialVoiceModule)
+ {
+ // stop processing if we're going to change voice modules
+ // and we're not currently in non-spatial.
+ LLVoiceVersionInfo version = mSpatialVoiceModule->getVersion();
+ if (version.internalVoiceServerType != voiceServerType)
+ {
+ mSpatialVoiceModule->processChannels(false);
+ }
+ }
+ setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString());
+
+ // if we should be in spatial voice, switch to it and set the creds
+ if (mSpatialVoiceModule && !mNonSpatialVoiceModule)
+ {
+ if (!mSpatialCredentials.isUndefined())
+ {
+ mSpatialVoiceModule->setSpatialChannel(mSpatialCredentials);
+ }
+ mSpatialVoiceModule->processChannels(true);
+ }
+}
+
+static void simulator_features_received_callback(const LLUUID& region_id)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region && (region->getRegionID() == region_id))
{
- mVoiceModule = NULL;
- return;
+ LLSD simulatorFeatures;
+ region->getSimulatorFeatures(simulatorFeatures);
+ if (LLVoiceClient::getInstance())
+ {
+ LLVoiceClient::getInstance()->handleSimulatorFeaturesReceived(simulatorFeatures);
+ }
}
- mVoiceModule->init(m_servicePump);
- mVoiceModule->userAuthorized(user_id, agentID);
+}
+
+void LLVoiceClient::onRegionChanged()
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region && region->simulatorFeaturesReceived())
+ {
+ LLSD simulatorFeatures;
+ region->getSimulatorFeatures(simulatorFeatures);
+ if (LLVoiceClient::getInstance())
+ {
+ LLVoiceClient::getInstance()->handleSimulatorFeaturesReceived(simulatorFeatures);
+ }
+ }
+ else if (region)
+ {
+ if (mSimulatorFeaturesReceivedSlot.connected())
+ {
+ mSimulatorFeaturesReceivedSlot.disconnect();
+ }
+ mSimulatorFeaturesReceivedSlot =
+ region->setSimulatorFeaturesReceivedCallback(boost::bind(&simulator_features_received_callback, _1));
+ }
+}
+
+void LLVoiceClient::setSpatialVoiceModule(const std::string &voice_server_type)
+{
+ LLVoiceModuleInterface *module = getVoiceModule(voice_server_type);
+ if (!module)
+ {
+ return;
+ }
+ if (module != mSpatialVoiceModule)
+ {
+ if (inProximalChannel())
+ {
+ mSpatialVoiceModule->processChannels(false);
+ }
+ module->processChannels(true);
+ mSpatialVoiceModule = module;
+ mSpatialVoiceModule->updateSettings();
+ }
+}
+
+void LLVoiceClient::setNonSpatialVoiceModule(const std::string &voice_server_type)
+{
+ mNonSpatialVoiceModule = getVoiceModule(voice_server_type);
+ if (!mNonSpatialVoiceModule)
+ {
+ // we don't have a non-spatial voice module,
+ // so revert to spatial.
+ if (mSpatialVoiceModule)
+ {
+ mSpatialVoiceModule->processChannels(true);
+ }
+ return;
+ }
+ mNonSpatialVoiceModule->updateSettings();
}
void LLVoiceClient::setHidden(bool hidden)
{
- if (mVoiceModule)
+ if (mSpatialVoiceModule)
{
- mVoiceModule->setHidden(hidden);
+ mSpatialVoiceModule->setHidden(hidden);
}
}
void LLVoiceClient::terminate()
{
- if (mVoiceModule) mVoiceModule->terminate();
- mVoiceModule = NULL;
+ if (mSpatialVoiceModule) mSpatialVoiceModule->terminate();
+ mSpatialVoiceModule = NULL;
m_servicePump = NULL;
// Shutdown speaker volume storage before LLSingletonBase::deleteAll() does it
@@ -193,15 +301,15 @@ void LLVoiceClient::terminate()
const LLVoiceVersionInfo LLVoiceClient::getVersion()
{
- if (mVoiceModule)
+ if (mSpatialVoiceModule)
{
- return mVoiceModule->getVersion();
+ return mSpatialVoiceModule->getVersion();
}
else
{
LLVoiceVersionInfo result;
result.serverVersion = std::string();
- result.serverType = std::string();
+ result.voiceServerType = std::string();
result.mBuildVersion = std::string();
return result;
}
@@ -215,10 +323,8 @@ void LLVoiceClient::updateSettings()
updateMicMuteLogic();
- if (mVoiceModule)
- {
- mVoiceModule->updateSettings();
- }
+ LLWebRTCVoiceClient::getInstance()->updateSettings();
+ LLVivoxVoiceClient::getInstance()->updateSettings();
}
//--------------------------------------------------
@@ -226,117 +332,75 @@ void LLVoiceClient::updateSettings()
void LLVoiceClient::tuningStart()
{
- if (mVoiceModule) mVoiceModule->tuningStart();
+ LLWebRTCVoiceClient::getInstance()->tuningStart();
+ LLVivoxVoiceClient::getInstance()->tuningStart();
}
void LLVoiceClient::tuningStop()
{
- if (mVoiceModule) mVoiceModule->tuningStop();
+ LLWebRTCVoiceClient::getInstance()->tuningStop();
+ LLVivoxVoiceClient::getInstance()->tuningStop();
}
bool LLVoiceClient::inTuningMode()
{
- if (mVoiceModule)
- {
- return mVoiceModule->inTuningMode();
- }
- else
- {
- return false;
- }
+ return LLWebRTCVoiceClient::getInstance()->inTuningMode();
}
void LLVoiceClient::tuningSetMicVolume(float volume)
{
- if (mVoiceModule) mVoiceModule->tuningSetMicVolume(volume);
+ LLWebRTCVoiceClient::getInstance()->tuningSetMicVolume(volume);
}
void LLVoiceClient::tuningSetSpeakerVolume(float volume)
{
- if (mVoiceModule) mVoiceModule->tuningSetSpeakerVolume(volume);
+ LLWebRTCVoiceClient::getInstance()->tuningSetSpeakerVolume(volume);
}
float LLVoiceClient::tuningGetEnergy(void)
{
- if (mVoiceModule)
- {
- return mVoiceModule->tuningGetEnergy();
- }
- else
- {
- return 0.0;
- }
+ return LLWebRTCVoiceClient::getInstance()->tuningGetEnergy();
}
-
//------------------------------------------------
// devices
bool LLVoiceClient::deviceSettingsAvailable()
{
- if (mVoiceModule)
- {
- return mVoiceModule->deviceSettingsAvailable();
- }
- else
- {
- return false;
- }
+ return LLWebRTCVoiceClient::getInstance()->deviceSettingsAvailable();
}
bool LLVoiceClient::deviceSettingsUpdated()
{
- if (mVoiceModule)
- {
- return mVoiceModule->deviceSettingsUpdated();
- }
- else
- {
- return false;
- }
+ return LLWebRTCVoiceClient::getInstance()->deviceSettingsUpdated();
}
void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
{
- if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList);
+ LLWebRTCVoiceClient::getInstance()->refreshDeviceLists(clearCurrentList);
}
void LLVoiceClient::setCaptureDevice(const std::string& name)
{
- if (mVoiceModule) mVoiceModule->setCaptureDevice(name);
-
+ LLWebRTCVoiceClient::getInstance()->setCaptureDevice(name);
+ LLVivoxVoiceClient::getInstance()->setCaptureDevice(name);
}
void LLVoiceClient::setRenderDevice(const std::string& name)
{
- if (mVoiceModule) mVoiceModule->setRenderDevice(name);
+ LLWebRTCVoiceClient::getInstance()->setRenderDevice(name);
+ LLVivoxVoiceClient::getInstance()->setRenderDevice(name);
}
const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices()
{
- static LLVoiceDeviceList nullCaptureDevices;
- if (mVoiceModule)
- {
- return mVoiceModule->getCaptureDevices();
- }
- else
- {
- return nullCaptureDevices;
- }
+ return LLWebRTCVoiceClient::getInstance()->getCaptureDevices();
}
const LLVoiceDeviceList& LLVoiceClient::getRenderDevices()
{
- static LLVoiceDeviceList nullRenderDevices;
- if (mVoiceModule)
- {
- return mVoiceModule->getRenderDevices();
- }
- else
- {
- return nullRenderDevices;
- }
+ return LLWebRTCVoiceClient::getInstance()->getRenderDevices();
}
@@ -345,190 +409,158 @@ const LLVoiceDeviceList& LLVoiceClient::getRenderDevices()
void LLVoiceClient::getParticipantList(std::set<LLUUID> &participants)
{
- if (mVoiceModule)
- {
- mVoiceModule->getParticipantList(participants);
- }
- else
- {
- participants = std::set<LLUUID>();
- }
+ LLWebRTCVoiceClient::getInstance()->getParticipantList(participants);
+ LLVivoxVoiceClient::getInstance()->getParticipantList(participants);
}
bool LLVoiceClient::isParticipant(const LLUUID &speaker_id)
{
- if(mVoiceModule)
- {
- return mVoiceModule->isParticipant(speaker_id);
- }
- return false;
+ return LLWebRTCVoiceClient::getInstance()->isParticipant(speaker_id) ||
+ LLVivoxVoiceClient::getInstance()->isParticipant(speaker_id);
}
//--------------------------------------------------
// text chat
-
BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->isSessionTextIMPossible(id);
- }
- else
- {
- return FALSE;
- }
+ // all sessions can do TextIM, as we no longer support PSTN
+ return TRUE;
}
BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->isSessionCallBackPossible(id);
- }
- else
- {
- return FALSE;
- }
+ // we don't support PSTN calls anymore. (did we ever?)
+ return TRUE;
}
-/* obsolete
-BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
+//----------------------------------------------
+// channels
+
+bool LLVoiceClient::inProximalChannel()
{
- if (mVoiceModule)
+ if (mSpatialVoiceModule)
{
- return mVoiceModule->sendTextMessage(participant_id, message);
+ return mSpatialVoiceModule->inProximalChannel();
}
else
{
- return FALSE;
- }
+ return false;
+ }
}
-*/
-void LLVoiceClient::endUserIMSession(const LLUUID& participant_id)
+void LLVoiceClient::setNonSpatialChannel(
+ const LLSD& channelInfo,
+ bool notify_on_first_join,
+ bool hangup_on_last_leave)
{
- if (mVoiceModule)
+ setNonSpatialVoiceModule(channelInfo["voice_server_type"].asString());
+ if (mSpatialVoiceModule && mSpatialVoiceModule != mNonSpatialVoiceModule)
{
- // mVoiceModule->endUserIMSession(participant_id); // A SLim leftover
+ mSpatialVoiceModule->processChannels(false);
+ }
+ if (mNonSpatialVoiceModule)
+ {
+ mNonSpatialVoiceModule->processChannels(true);
+ mNonSpatialVoiceModule->setNonSpatialChannel(channelInfo, notify_on_first_join, hangup_on_last_leave);
}
}
-//----------------------------------------------
-// channels
-
-bool LLVoiceClient::inProximalChannel()
+void LLVoiceClient::setSpatialChannel(const LLSD &channelInfo)
{
- if (mVoiceModule)
+ mSpatialCredentials = channelInfo;
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region && region->simulatorFeaturesReceived())
{
- return mVoiceModule->inProximalChannel();
+ LLSD simulatorFeatures;
+ region->getSimulatorFeatures(simulatorFeatures);
+ setSpatialVoiceModule(simulatorFeatures["VoiceServerType"].asString());
}
else
{
- return false;
+ return;
}
-}
-void LLVoiceClient::setNonSpatialChannel(
- const std::string &uri,
- const std::string &credentials)
-{
- if (mVoiceModule)
- {
- mVoiceModule->setNonSpatialChannel(uri, credentials);
- }
-}
-
-void LLVoiceClient::setSpatialChannel(
- const std::string &uri,
- const std::string &credentials)
-{
- if (mVoiceModule)
- {
- mVoiceModule->setSpatialChannel(uri, credentials);
- }
+ if (mSpatialVoiceModule)
+ {
+ mSpatialVoiceModule->setSpatialChannel(channelInfo);
+ }
}
void LLVoiceClient::leaveNonSpatialChannel()
{
- if (mVoiceModule)
+ if (mNonSpatialVoiceModule)
{
- mVoiceModule->leaveNonSpatialChannel();
+ mNonSpatialVoiceModule->leaveNonSpatialChannel();
+ mNonSpatialVoiceModule->processChannels(false);
+ mNonSpatialVoiceModule = nullptr;
}
+ if (mSpatialVoiceModule)
+ {
+ mSpatialVoiceModule->processChannels(true);
+ ;
+ }
}
-void LLVoiceClient::leaveChannel(void)
+void LLVoiceClient::activateSpatialChannel(bool activate)
{
- if (mVoiceModule)
+ if (mSpatialVoiceModule)
{
- mVoiceModule->leaveChannel();
+ mSpatialVoiceModule->processChannels(activate);
}
}
-std::string LLVoiceClient::getCurrentChannel()
+bool LLVoiceClient::isCurrentChannel(const LLSD& channelInfo)
{
- if (mVoiceModule)
- {
- return mVoiceModule->getCurrentChannel();
- }
- else
- {
- return std::string();
- }
+ return LLWebRTCVoiceClient::getInstance()->isCurrentChannel(channelInfo) ||
+ LLVivoxVoiceClient::getInstance()->isCurrentChannel(channelInfo);
}
-
-//---------------------------------------
-// invitations
-
-void LLVoiceClient::callUser(const LLUUID &uuid)
+bool LLVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2)
{
- if (mVoiceModule) mVoiceModule->callUser(uuid);
+ return LLWebRTCVoiceClient::getInstance()->compareChannels(channelInfo1, channelInfo2) ||
+ LLVivoxVoiceClient::getInstance()->compareChannels(channelInfo1, channelInfo2);
}
-bool LLVoiceClient::isValidChannel(std::string &session_handle)
+LLVoiceP2PIncomingCallInterfacePtr LLVoiceClient::getIncomingCallInterface(const LLSD& voice_call_info)
{
- if (mVoiceModule)
- {
- return mVoiceModule->isValidChannel(session_handle);
- }
- else
+ LLVoiceModuleInterface *module = getVoiceModule(voice_call_info["voice_server_type"]);
+ if (module)
{
- return false;
+ return module->getIncomingCallInterface(voice_call_info);
}
-}
+ return nullptr;
-bool LLVoiceClient::answerInvite(std::string &channelHandle)
-{
- if (mVoiceModule)
- {
- return mVoiceModule->answerInvite(channelHandle);
- }
- else
- {
- return false;
- }
}
-void LLVoiceClient::declineInvite(std::string &channelHandle)
+//---------------------------------------
+// outgoing calls
+LLVoiceP2POutgoingCallInterface *LLVoiceClient::getOutgoingCallInterface(const LLSD& voiceChannelInfo)
{
- if (mVoiceModule) mVoiceModule->declineInvite(channelHandle);
+ std::string voiceServerType = gSavedSettings.getString("VoiceServerType");
+ if (voiceChannelInfo.has("voice_server_type"))
+ {
+ voiceServerType = voiceChannelInfo["voice_server_type"].asString();
+ }
+ LLVoiceModuleInterface *module = getVoiceModule(voiceServerType);
+ return dynamic_cast<LLVoiceP2POutgoingCallInterface *>(module);
}
-
//------------------------------------------
// Volume/gain
void LLVoiceClient::setVoiceVolume(F32 volume)
{
- if (mVoiceModule) mVoiceModule->setVoiceVolume(volume);
+ LLWebRTCVoiceClient::getInstance()->setVoiceVolume(volume);
+ LLVivoxVoiceClient::getInstance()->setVoiceVolume(volume);
}
-void LLVoiceClient::setMicGain(F32 volume)
+void LLVoiceClient::setMicGain(F32 gain)
{
- if (mVoiceModule) mVoiceModule->setMicGain(volume);
+ LLWebRTCVoiceClient::getInstance()->setMicGain(gain);
+ LLVivoxVoiceClient::getInstance()->setMicGain(gain);
}
@@ -537,29 +569,22 @@ void LLVoiceClient::setMicGain(F32 volume)
bool LLVoiceClient::voiceEnabled()
{
- if (mVoiceModule)
- {
- return mVoiceModule->voiceEnabled();
- }
- else
- {
- return false;
- }
+ static LLCachedControl<bool> enable_voice_chat(gSavedSettings, "EnableVoiceChat");
+ static LLCachedControl<bool> cmd_line_disable_voice(gSavedSettings, "CmdLineDisableVoice");
+ return enable_voice_chat && !cmd_line_disable_voice && !gNonInteractive;
}
void LLVoiceClient::setVoiceEnabled(bool enabled)
{
- if (mVoiceModule)
- {
- mVoiceModule->setVoiceEnabled(enabled);
- }
+ LLWebRTCVoiceClient::getInstance()->setVoiceEnabled(enabled);
+ LLVivoxVoiceClient::getInstance()->setVoiceEnabled(enabled);
}
void LLVoiceClient::updateMicMuteLogic()
{
// If not configured to use PTT, the mic should be open (otherwise the user will be unable to speak).
bool new_mic_mute = false;
-
+
if(mUsePTT)
{
// If configured to use PTT, track the user state.
@@ -571,34 +596,20 @@ void LLVoiceClient::updateMicMuteLogic()
// Either of these always overrides any other PTT setting.
new_mic_mute = true;
}
-
- if (mVoiceModule) mVoiceModule->setMuteMic(new_mic_mute);
-}
-
-void LLVoiceClient::setLipSyncEnabled(BOOL enabled)
-{
- if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled);
+ LLWebRTCVoiceClient::getInstance()->setMuteMic(new_mic_mute);
+ LLVivoxVoiceClient::getInstance()->setMuteMic(new_mic_mute);
}
-BOOL LLVoiceClient::lipSyncEnabled()
+void LLVoiceClient::setMuteMic(bool muted)
{
- if (mVoiceModule)
- {
- return mVoiceModule->lipSyncEnabled();
- }
- else
+ if (mMuteMic != muted)
{
- return false;
+ mMuteMic = muted;
+ updateMicMuteLogic();
+ mMicroChangedSignal();
}
}
-void LLVoiceClient::setMuteMic(bool muted)
-{
- mMuteMic = muted;
- updateMicMuteLogic();
- mMicroChangedSignal();
-}
-
// ----------------------------------------------
// PTT
@@ -627,7 +638,7 @@ void LLVoiceClient::setUsePTT(bool usePTT)
mUserPTTState = false;
}
mUsePTT = usePTT;
-
+
updateMicMuteLogic();
}
@@ -638,7 +649,7 @@ void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
// When the user turns off toggle, reset the current state.
mUserPTTState = false;
}
-
+
mPTTIsToggle = PTTIsToggle;
updateMicMuteLogic();
@@ -653,12 +664,12 @@ void LLVoiceClient::inputUserControlState(bool down)
{
if(mPTTIsToggle)
{
- if(down) // toggle open-mic state on 'down'
+ if(down) // toggle open-mic state on 'down'
{
toggleUserPTTState();
}
}
- else // set open-mic state as an absolute
+ else // set open-mic state as an absolute
{
setUserPTTState(down);
}
@@ -675,117 +686,71 @@ void LLVoiceClient::toggleUserPTTState(void)
BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->getVoiceEnabled(id);
- }
- else
- {
- return FALSE;
- }
+ return isParticipant(id) ? TRUE : FALSE;
}
std::string LLVoiceClient::getDisplayName(const LLUUID& id)
{
- if (mVoiceModule)
+ std::string result = LLWebRTCVoiceClient::getInstance()->getDisplayName(id);
+ if (result.empty())
{
- return mVoiceModule->getDisplayName(id);
- }
- else
- {
- return std::string();
+ result = LLVivoxVoiceClient::getInstance()->getDisplayName(id);
}
+ return result;
}
bool LLVoiceClient::isVoiceWorking() const
{
- if (mVoiceModule)
- {
- return mVoiceModule->isVoiceWorking();
- }
- return false;
+ return LLVivoxVoiceClient::getInstance()->isVoiceWorking() ||
+ LLWebRTCVoiceClient::getInstance()->isVoiceWorking();
}
BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->isParticipantAvatar(id);
- }
- else
- {
- return FALSE;
- }
+ return TRUE;
}
BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id)
{
- return FALSE;
+ return FALSE;
}
BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->getIsSpeaking(id);
- }
- else
- {
- return FALSE;
- }
+ return LLWebRTCVoiceClient::getInstance()->getIsSpeaking(id) ||
+ LLVivoxVoiceClient::getInstance()->getIsSpeaking(id);
}
BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->getIsModeratorMuted(id);
- }
- else
- {
- return FALSE;
- }
+ // don't bother worrying about p2p calls, as
+ // p2p calls don't have mute.
+ return LLWebRTCVoiceClient::getInstance()->getIsModeratorMuted(id) ||
+ LLVivoxVoiceClient::getInstance()->getIsModeratorMuted(id);
}
F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
-{
- if (mVoiceModule)
- {
- return mVoiceModule->getCurrentPower(id);
- }
- else
- {
- return 0.0;
- }
+{
+ return std::fmax(LLVivoxVoiceClient::getInstance()->getCurrentPower(id),
+ LLWebRTCVoiceClient::getInstance()->getCurrentPower(id));
}
BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->getOnMuteList(id);
- }
- else
- {
- return FALSE;
- }
+ // don't bother worrying about p2p calls, as
+ // p2p calls don't have mute.
+ return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat);
}
F32 LLVoiceClient::getUserVolume(const LLUUID& id)
{
- if (mVoiceModule)
- {
- return mVoiceModule->getUserVolume(id);
- }
- else
- {
- return 0.0;
- }
+ return std::fmax(LLVivoxVoiceClient::getInstance()->getUserVolume(id), LLWebRTCVoiceClient::getInstance()->getUserVolume(id));
}
void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
{
- if (mVoiceModule) mVoiceModule->setUserVolume(id, volume);
+ LLWebRTCVoiceClient::getInstance()->setUserVolume(id, volume);
+ LLVivoxVoiceClient::getInstance()->setUserVolume(id, volume);
}
//--------------------------------------------------
@@ -793,48 +758,49 @@ void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
{
- if (mVoiceModule) mVoiceModule->addObserver(observer);
+ LLVivoxVoiceClient::getInstance()->addObserver(observer);
+ LLWebRTCVoiceClient::getInstance()->addObserver(observer);
}
void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
{
- if (mVoiceModule)
- {
- mVoiceModule->removeObserver(observer);
- }
+ LLVivoxVoiceClient::getInstance()->removeObserver(observer);
+ LLWebRTCVoiceClient::getInstance()->removeObserver(observer);
}
void LLVoiceClient::addObserver(LLFriendObserver* observer)
{
- if (mVoiceModule) mVoiceModule->addObserver(observer);
+ LLVivoxVoiceClient::getInstance()->addObserver(observer);
+ LLWebRTCVoiceClient::getInstance()->addObserver(observer);
}
void LLVoiceClient::removeObserver(LLFriendObserver* observer)
{
- if (mVoiceModule)
- {
- mVoiceModule->removeObserver(observer);
- }
+ LLVivoxVoiceClient::getInstance()->removeObserver(observer);
+ LLWebRTCVoiceClient::getInstance()->removeObserver(observer);
}
void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
{
- if (mVoiceModule) mVoiceModule->addObserver(observer);
+ LLVivoxVoiceClient::getInstance()->addObserver(observer);
+ LLWebRTCVoiceClient::getInstance()->addObserver(observer);
}
void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
{
- if (mVoiceModule)
- {
- mVoiceModule->removeObserver(observer);
- }
+ LLVivoxVoiceClient::getInstance()->removeObserver(observer);
+ LLWebRTCVoiceClient::getInstance()->removeObserver(observer);
}
std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
{
- if (mVoiceModule)
+ if (mNonSpatialVoiceModule)
+ {
+ return mNonSpatialVoiceModule->sipURIFromID(id);
+ }
+ else if (mSpatialVoiceModule)
{
- return mVoiceModule->sipURIFromID(id);
+ return mSpatialVoiceModule->sipURIFromID(id);
}
else
{
@@ -844,7 +810,7 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const
{
- return getVoiceEffectEnabled() ? dynamic_cast<LLVoiceEffectInterface*>(mVoiceModule) : NULL;
+ return getVoiceEffectEnabled() ? dynamic_cast<LLVoiceEffectInterface*>(mSpatialVoiceModule) : NULL;
}
///////////////////
@@ -852,31 +818,52 @@ LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const
class LLViewerRequiredVoiceVersion : public LLHTTPNode
{
- static BOOL sAlertedUser;
+ static bool sAlertedUser;
virtual void post(
LLHTTPNode::ResponsePtr response,
const LLSD& context,
const LLSD& input) const
{
- //You received this messsage (most likely on region cross or
- //teleport)
- if ( input.has("body") && input["body"].has("major_version") )
+ std::string voice_server_type = "vivox";
+ if (input.has("body") && input["body"].has("voice_server_type"))
+ {
+ voice_server_type = input["body"]["voice_server_type"].asString();
+ }
+
+ LLVoiceModuleInterface *voiceModule = NULL;
+
+ if (voice_server_type == "vivox" || voice_server_type.empty())
{
- int major_voice_version =
- input["body"]["major_version"].asInteger();
- // int minor_voice_version =
- // input["body"]["minor_version"].asInteger();
- LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion();
-
- if (major_voice_version > 1)
+ voiceModule = (LLVoiceModuleInterface *) LLVivoxVoiceClient::getInstance();
+ }
+ else if (voice_server_type == "webrtc")
+ {
+ voiceModule = (LLVoiceModuleInterface *) LLWebRTCVoiceClient::getInstance();
+ }
+ else
+ {
+ LL_WARNS("Voice") << "Unknown voice server type " << voice_server_type << LL_ENDL;
+ if (!sAlertedUser)
+ {
+ // sAlertedUser = true;
+ LLNotificationsUtil::add("VoiceVersionMismatch");
+ }
+ return;
+ }
+
+ LLVoiceVersionInfo versionInfo = voiceModule->getVersion();
+ if (input.has("body") && input["body"].has("major_version") &&
+ input["body"]["major_version"].asInteger() > versionInfo.majorVersion)
+ {
+ if (!sAlertedUser)
{
- if (!sAlertedUser)
- {
- //sAlertedUser = TRUE;
- LLNotificationsUtil::add("VoiceVersionMismatch");
- gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
- }
+ // sAlertedUser = true;
+ LLNotificationsUtil::add("VoiceVersionMismatch");
+ LL_WARNS("Voice") << "Voice server version mismatch " << input["body"]["major_version"].asInteger() << "/"
+ << versionInfo.majorVersion
+ << LL_ENDL;
}
+ return;
}
}
};
@@ -890,41 +877,27 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode
{
//the parcel you are in has changed something about its
//voice information
-
+
//this is a misnomer, as it can also be when you are not in
//a parcel at all. Should really be something like
//LLViewerVoiceInfoChanged.....
if ( input.has("body") )
{
LLSD body = input["body"];
-
+
//body has "region_name" (str), "parcel_local_id"(int),
//"voice_credentials" (map).
-
+
//body["voice_credentials"] has "channel_uri" (str),
//body["voice_credentials"] has "channel_credentials" (str)
-
+
//if we really wanted to be extra careful,
//we'd check the supplied
//local parcel id to make sure it's for the same parcel
//we believe we're in
if ( body.has("voice_credentials") )
{
- LLSD voice_credentials = body["voice_credentials"];
- std::string uri;
- std::string credentials;
-
- if ( voice_credentials.has("channel_uri") )
- {
- uri = voice_credentials["channel_uri"].asString();
- }
- if ( voice_credentials.has("channel_credentials") )
- {
- credentials =
- voice_credentials["channel_credentials"].asString();
- }
-
- LLVoiceClient::getInstance()->setSpatialChannel(uri, credentials);
+ LLVoiceClient::getInstance()->setSpatialChannel(body["voice_credentials"]);
}
}
}
@@ -966,7 +939,7 @@ void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 vo
bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume)
{
speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id);
-
+
if (it != mSpeakersData.end())
{
volume = it->second;
@@ -1045,9 +1018,9 @@ void LLSpeakerVolumeStorage::load()
if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(settings_llsd, file))
{
LL_WARNS("Voice") << "failed to parse " << filename << LL_ENDL;
-
+
}
-
+
}
for (LLSD::map_const_iterator iter = settings_llsd.beginMap();
@@ -1087,7 +1060,7 @@ void LLSpeakerVolumeStorage::save()
}
}
-BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;
+bool LLViewerRequiredVoiceVersion::sAlertedUser = false;
LLHTTPRegistration<LLViewerParcelVoiceInfo>
gHTTPRegistrationMessageParcelVoiceInfo(
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index aa67502908..3b1b3bd0c4 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llvoiceclient.h
* @brief Declaration of LLVoiceClient class which is the interface to the voice client process.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,9 +34,11 @@ class LLVOAvatar;
#include "lliosocket.h"
#include "v3math.h"
#include "llframetimer.h"
+#include "llsingleton.h"
#include "llcallingcard.h" // for LLFriendObserver
#include "llsecapi.h"
#include "llcontrol.h"
+#include <boost/shared_ptr.hpp>
// devices
@@ -86,19 +88,54 @@ public:
} EStatusType;
virtual ~LLVoiceClientStatusObserver() { }
- virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) = 0;
+ virtual void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) = 0;
static std::string status2string(EStatusType inStatus);
};
struct LLVoiceVersionInfo
{
- std::string serverType;
+ std::string voiceServerType;
+ std::string internalVoiceServerType;
+ int majorVersion;
+ int minorVersion;
std::string serverVersion;
std::string mBuildVersion;
};
//////////////////////////////////
+/// @class LLVoiceP2POutgoingCallInterface
+/// @brief Outgoing call interface
+///
+/// For providers that support P2P signaling (vivox)
+/////////////////////////////////
+
+class LLVoiceP2POutgoingCallInterface
+{
+ public:
+ // initiate an outgoing call to a user
+ virtual void callUser(const LLUUID &agentID) = 0;
+ virtual void hangup() = 0;
+};
+
+//////////////////////////////////
+/// @class LLVoiceP2PIncomingCallInterface
+/// @brief Incoming call interface
+///
+/// For providers that support P2P signaling (vivox)
+/////////////////////////////////
+class LLVoiceP2PIncomingCallInterface
+{
+ public:
+ virtual ~LLVoiceP2PIncomingCallInterface() {}
+
+ virtual bool answerInvite() = 0;
+ virtual void declineInvite() = 0;
+};
+
+typedef boost::shared_ptr<LLVoiceP2PIncomingCallInterface> LLVoiceP2PIncomingCallInterfacePtr;
+
+//////////////////////////////////
/// @class LLVoiceModuleInterface
/// @brief Voice module interface
///
@@ -110,30 +147,32 @@ class LLVoiceModuleInterface
public:
LLVoiceModuleInterface() {}
virtual ~LLVoiceModuleInterface() {}
-
+
virtual void init(LLPumpIO *pump)=0; // Call this once at application startup (creates connector)
virtual void terminate()=0; // Call this to clean up during shutdown
-
+
virtual void updateSettings()=0; // call after loading settings and whenever they change
-
+
virtual bool isVoiceWorking() const = 0; // connected to a voice server and voice channel
-
+
virtual void setHidden(bool hidden)=0; // Hides the user from voice.
virtual const LLVoiceVersionInfo& getVersion()=0;
-
+
+
+
/////////////////////
/// @name Tuning
//@{
virtual void tuningStart()=0;
virtual void tuningStop()=0;
virtual bool inTuningMode()=0;
-
+
virtual void tuningSetMicVolume(float volume)=0;
virtual void tuningSetSpeakerVolume(float volume)=0;
virtual float tuningGetEnergy(void)=0;
//@}
-
+
/////////////////////
/// @name Devices
//@{
@@ -141,114 +180,110 @@ public:
// i.e. when the daemon is running and connected, and the device lists are populated.
virtual bool deviceSettingsAvailable()=0;
virtual bool deviceSettingsUpdated() = 0;
-
+
// Requery the vivox daemon for the current list of input/output devices.
// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
// (use this if you want to know when it's done).
// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
virtual void refreshDeviceLists(bool clearCurrentList = true)=0;
-
+
virtual void setCaptureDevice(const std::string& name)=0;
virtual void setRenderDevice(const std::string& name)=0;
-
+
virtual LLVoiceDeviceList& getCaptureDevices()=0;
virtual LLVoiceDeviceList& getRenderDevices()=0;
-
+
virtual void getParticipantList(std::set<LLUUID> &participants)=0;
virtual bool isParticipant(const LLUUID& speaker_id)=0;
//@}
-
+
////////////////////////////
/// @ name Channel stuff
//@{
// returns true iff the user is currently in a proximal (local spatial) channel.
// Note that gestures should only fire if this returns true.
virtual bool inProximalChannel()=0;
-
- virtual void setNonSpatialChannel(const std::string &uri,
- const std::string &credentials)=0;
-
- virtual bool setSpatialChannel(const std::string &uri,
- const std::string &credentials)=0;
-
- virtual void leaveNonSpatialChannel()=0;
-
- virtual void leaveChannel(void)=0;
-
- // Returns the URI of the current channel, or an empty string if not currently in a channel.
- // NOTE that it will return an empty string if it's in the process of joining a channel.
- virtual std::string getCurrentChannel()=0;
+
+ virtual void setNonSpatialChannel(const LLSD& channelInfo,
+ bool notify_on_first_join,
+ bool hangup_on_last_leave)=0;
+
+ virtual bool setSpatialChannel(const LLSD& channelInfo)=0;
+
+ virtual void leaveNonSpatialChannel() = 0;
+ virtual void processChannels(bool process) = 0;
+
+ virtual bool isCurrentChannel(const LLSD &channelInfo) = 0;
+ virtual bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) = 0;
+
//@}
-
-
+
+
//////////////////////////
- /// @name invitations
+ /// @name p2p
//@{
- // start a voice channel with the specified user
- virtual void callUser(const LLUUID &uuid)=0;
- virtual bool isValidChannel(std::string& channelHandle)=0;
- virtual bool answerInvite(std::string &channelHandle)=0;
- virtual void declineInvite(std::string &channelHandle)=0;
+
+ // initiate a call with a peer using the P2P interface, which only applies to some
+ // voice server types. Otherwise, a group call should be used for P2P
+ virtual LLVoiceP2POutgoingCallInterface* getOutgoingCallInterface() = 0;
+
+ // an incoming call was received, and the incoming call dialogue is asking for an interface to
+ // answer or decline.
+ virtual LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) = 0;
//@}
-
+
/////////////////////////
/// @name Volume/gain
//@{
virtual void setVoiceVolume(F32 volume)=0;
virtual void setMicGain(F32 volume)=0;
//@}
-
+
/////////////////////////
/// @name enable disable voice and features
//@{
- virtual bool voiceEnabled()=0;
virtual void setVoiceEnabled(bool enabled)=0;
- virtual void setLipSyncEnabled(BOOL enabled)=0;
- virtual BOOL lipSyncEnabled()=0;
virtual void setMuteMic(bool muted)=0; // Set the mute state of the local mic.
//@}
-
+
//////////////////////////
/// @name nearby speaker accessors
//@{
- virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar
virtual std::string getDisplayName(const LLUUID& id)=0;
virtual BOOL isParticipantAvatar(const LLUUID &id)=0;
virtual BOOL getIsSpeaking(const LLUUID& id)=0;
virtual BOOL getIsModeratorMuted(const LLUUID& id)=0;
virtual F32 getCurrentPower(const LLUUID& id)=0; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is...
- virtual BOOL getOnMuteList(const LLUUID& id)=0;
virtual F32 getUserVolume(const LLUUID& id)=0;
- virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal)
+ virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal)
//@}
-
+
//////////////////////////
/// @name text chat
//@{
virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0;
virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0;
//virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0;
- virtual void endUserIMSession(const LLUUID &uuid)=0;
//@}
-
+
// authorize the user
virtual void userAuthorized(const std::string& user_id,
const LLUUID &agentID)=0;
-
+
//////////////////////////////
/// @name Status notification
//@{
virtual void addObserver(LLVoiceClientStatusObserver* observer)=0;
virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0;
virtual void addObserver(LLFriendObserver* observer)=0;
- virtual void removeObserver(LLFriendObserver* observer)=0;
+ virtual void removeObserver(LLFriendObserver* observer)=0;
virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0;
- virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0;
+ virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0;
//@}
-
+
virtual std::string sipURIFromID(const LLUUID &id)=0;
//@}
-
+
};
@@ -320,9 +355,9 @@ public:
micro_changed_signal_t mMicroChangedSignal;
void terminate(); // Call this to clean up during shutdown
-
+
const LLVoiceVersionInfo getVersion();
-
+
static const F32 OVERDRIVEN_POWER_LEVEL;
static const F32 VOLUME_MIN;
@@ -337,18 +372,18 @@ public:
void tuningStart();
void tuningStop();
bool inTuningMode();
-
+
void tuningSetMicVolume(float volume);
void tuningSetSpeakerVolume(float volume);
float tuningGetEnergy(void);
-
+
// devices
-
+
// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
// i.e. when the daemon is running and connected, and the device lists are populated.
bool deviceSettingsAvailable();
bool deviceSettingsUpdated(); // returns true when the device list has been updated recently.
-
+
// Requery the vivox daemon for the current list of input/output devices.
// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
// (use this if you want to know when it's done).
@@ -365,60 +400,59 @@ public:
////////////////////////////
// Channel stuff
//
-
+
// returns true iff the user is currently in a proximal (local spatial) channel.
// Note that gestures should only fire if this returns true.
bool inProximalChannel();
- void setNonSpatialChannel(
- const std::string &uri,
- const std::string &credentials);
- void setSpatialChannel(
- const std::string &uri,
- const std::string &credentials);
+
+ void setNonSpatialChannel(const LLSD& channelInfo,
+ bool notify_on_first_join,
+ bool hangup_on_last_leave);
+
+ void setSpatialChannel(const LLSD &channelInfo);
+
+ void activateSpatialChannel(bool activate);
+
void leaveNonSpatialChannel();
-
- // Returns the URI of the current channel, or an empty string if not currently in a channel.
- // NOTE that it will return an empty string if it's in the process of joining a channel.
- std::string getCurrentChannel();
- // start a voice channel with the specified user
- void callUser(const LLUUID &uuid);
- bool isValidChannel(std::string& channelHandle);
- bool answerInvite(std::string &channelHandle);
- void declineInvite(std::string &channelHandle);
- void leaveChannel(void); // call this on logout or teleport begin
-
-
+
+ bool isCurrentChannel(const LLSD& channelInfo);
+
+ bool compareChannels(const LLSD& channelInfo1, const LLSD& channelInfo2);
+
+ // initiate a call with a peer using the P2P interface, which only applies to some
+ // voice server types. Otherwise, a group call should be used for P2P
+ LLVoiceP2POutgoingCallInterface* getOutgoingCallInterface(const LLSD& voiceChannelInfo);
+
+ LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voiceCallInfo);
+
/////////////////////////////
// Sending updates of current state
-
+
void setVoiceVolume(F32 volume);
void setMicGain(F32 volume);
- void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)
+ void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)
bool voiceEnabled();
- void setLipSyncEnabled(BOOL enabled);
void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
void setUserPTTState(bool ptt);
bool getUserPTTState();
void toggleUserPTTState(void);
- void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs
+ void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs
void setVoiceEnabled(bool enabled);
void setUsePTT(bool usePTT);
void setPTTIsToggle(bool PTTIsToggle);
- bool getPTTIsToggle();
+ bool getPTTIsToggle();
void updateMicMuteLogic();
- BOOL lipSyncEnabled();
-
boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); }
-
+
/////////////////////////////
// Accessors for data related to nearby speakers
BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar
- std::string getDisplayName(const LLUUID& id);
+ std::string getDisplayName(const LLUUID& id);
BOOL isOnlineSIP(const LLUUID &id);
BOOL isParticipantAvatar(const LLUUID &id);
BOOL getIsSpeaking(const LLUUID& id);
@@ -428,32 +462,33 @@ public:
F32 getUserVolume(const LLUUID& id);
/////////////////////////////
- BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled.
- // Use this to determine whether to show a "no speech" icon in the menu bar.
void getParticipantList(std::set<LLUUID> &participants);
bool isParticipant(const LLUUID& speaker_id);
-
+
//////////////////////////
/// @name text chat
//@{
BOOL isSessionTextIMPossible(const LLUUID& id);
BOOL isSessionCallBackPossible(const LLUUID& id);
//BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return true;} ;
- void endUserIMSession(const LLUUID &uuid);
//@}
-
+
+ void setSpatialVoiceModule(const std::string& voice_server_type);
+ void setNonSpatialVoiceModule(const std::string &voice_server_type);
void userAuthorized(const std::string& user_id,
- const LLUUID &agentID);
-
+ const LLUUID &agentID);
+
+ void onRegionChanged();
+
void addObserver(LLVoiceClientStatusObserver* observer);
void removeObserver(LLVoiceClientStatusObserver* observer);
void addObserver(LLFriendObserver* observer);
void removeObserver(LLFriendObserver* observer);
void addObserver(LLVoiceClientParticipantObserver* observer);
void removeObserver(LLVoiceClientParticipantObserver* observer);
-
- std::string sipURIFromID(const LLUUID &id);
+
+ std::string sipURIFromID(const LLUUID &id);
//////////////////////////
/// @name Voice effects
@@ -464,20 +499,30 @@ public:
// Returns NULL if voice effects are not supported, or not enabled.
LLVoiceEffectInterface* getVoiceEffectInterface() const;
//@}
-private:
+
+ void handleSimulatorFeaturesReceived(const LLSD &simulatorFeatures);
+
+ private:
+
void init(LLPumpIO *pump);
protected:
- LLVoiceModuleInterface* mVoiceModule;
+
+ LLVoiceModuleInterface* mSpatialVoiceModule;
+ LLVoiceModuleInterface* mNonSpatialVoiceModule;
+ LLSD mSpatialCredentials; // used to store spatial credentials for vivox
+ // so they're available when the region voice
+ // server is retrieved.
LLPumpIO *m_servicePump;
+ boost::signals2::connection mSimulatorFeaturesReceivedSlot;
LLCachedControl<bool> mVoiceEffectEnabled;
LLCachedControl<std::string> mVoiceEffectDefault;
bool mPTTDirty;
bool mPTT;
-
+
bool mUsePTT;
S32 mPTTMouseButton;
KEY mPTTKey;
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 3725510b6a..29aba8ecba 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -1,25 +1,25 @@
- /**
+ /**
* @file LLVivoxVoiceClient.cpp
* @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -80,12 +80,14 @@
extern LLMenuBarGL* gMenuBarView;
extern void handle_voice_morphing_subscribe();
+const std::string VIVOX_VOICE_SERVER_TYPE = "vivox";
+
namespace {
const F32 VOLUME_SCALE_VIVOX = 0.01f;
const F32 SPEAKING_TIMEOUT = 1.f;
- static const std::string VOICE_SERVER_TYPE = "Vivox";
+ static const std::string VISIBLE_VOICE_SERVER_TYPE = "Vivox";
// Don't retry connecting to the daemon more frequently than this:
const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f;
@@ -94,7 +96,7 @@ namespace {
// Don't send positional updates more frequently than this:
const F32 UPDATE_THROTTLE_SECONDS = 0.5f;
- // Timeout for connection to Vivox
+ // Timeout for connection to Vivox
const F32 CONNECT_ATTEMPT_TIMEOUT = 300.0f;
const F32 CONNECT_DNS_TIMEOUT = 5.0f;
const int CONNECT_RETRY_MAX = 3;
@@ -113,8 +115,8 @@ namespace {
const F32 SESSION_JOIN_TIMEOUT = 30.0f;
// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine()
- // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed
- // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process
+ // which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed
+ // from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process
// backs up processing join requests. There is a log statement that records when channel joins take longer than 100 frames.
const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500;
@@ -133,17 +135,17 @@ namespace {
static int scale_mic_volume(float volume)
{
- // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
- // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70
+ // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
+ // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70
return 30 + (int)(volume * 20.0f);
}
static int scale_speaker_volume(float volume)
{
- // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
- // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70
+ // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
+ // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70
return 30 + (int)(volume * 40.0f);
-
+
}
@@ -293,7 +295,6 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mTuningSpeakerVolumeDirty(true),
mDevicesListUpdated(false),
- mAreaVoiceDisabled(false),
mAudioSession(), // TBD - should be NULL
mAudioSessionChanged(false),
mNextAudioSession(),
@@ -325,10 +326,9 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mMicVolumeDirty(true),
mVoiceEnabled(false),
+ mProcessChannels(false),
mWriteInProgress(false),
- mLipSyncEnabled(false),
-
mVoiceFontsReceived(false),
mVoiceFontsNew(false),
mVoiceFontListDirty(false),
@@ -358,19 +358,24 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mSpeakerVolume = scale_speaker_volume(0);
mVoiceVersion.serverVersion = "";
- mVoiceVersion.serverType = VOICE_SERVER_TYPE;
-
+ mVoiceVersion.voiceServerType = VISIBLE_VOICE_SERVER_TYPE;
+ mVoiceVersion.internalVoiceServerType = VIVOX_VOICE_SERVER_TYPE;
+ mVoiceVersion.majorVersion = 1;
+ mVoiceVersion.minorVersion = 0;
+ mVoiceVersion.mBuildVersion = "";
+ mVoiceVersion.serverVersion = "";
+
// gMuteListp isn't set up at this point, so we defer this until later.
// gMuteListp->addObserver(&mutelist_listener);
-
+
#if LL_DARWIN || LL_LINUX
// HACK: THIS DOES NOT BELONG HERE
// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
// This should cause us to ignore SIGPIPE and handle the error through proper channels.
// This should really be set up elsewhere. Where should it go?
signal(SIGPIPE, SIG_IGN);
-
+
// Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
// Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that.
signal(SIGCHLD, SIG_IGN);
@@ -410,13 +415,13 @@ void LLVivoxVoiceClient::terminate()
return;
}
- // needs to be done manually here since we will not get another pass in
+ // needs to be done manually here since we will not get another pass in
// coroutines... that mechanism is long since gone.
if (mIsLoggedIn)
{
logoutOfVivox(false);
}
-
+
if(sConnected)
{
breakVoiceConnection(false);
@@ -437,7 +442,7 @@ void LLVivoxVoiceClient::terminate()
void LLVivoxVoiceClient::cleanUp()
{
LL_DEBUGS("Voice") << LL_ENDL;
-
+
deleteAllSessions();
deleteAllVoiceFonts();
deleteVoiceFontTemplates();
@@ -455,7 +460,7 @@ const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion()
void LLVivoxVoiceClient::updateSettings()
{
- setVoiceEnabled(voiceEnabled());
+ setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled());
setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
@@ -464,7 +469,6 @@ void LLVivoxVoiceClient::updateSettings()
setRenderDevice(outputDevice);
F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
setMicGain(mic_level);
- setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled"));
}
/////////////////////////////
@@ -480,7 +484,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str)
apr_status_t err;
apr_size_t size = (apr_size_t)str.size();
apr_size_t written = size;
-
+
//MARK: Turn this on to log outgoing XML
// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
@@ -489,7 +493,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str)
mSocket->getSocket(),
(const char*)str.data(),
&written);
-
+
if(err == 0 && written == size)
{
// Success.
@@ -512,7 +516,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str)
daemonDied();
}
}
-
+
return result;
}
@@ -523,7 +527,7 @@ void LLVivoxVoiceClient::connectorCreate()
{
std::ostringstream stream;
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
-
+
// Transition to stateConnectorStarted when the connector handle comes back.
std::string vivoxLogLevel = gSavedSettings.getString("VivoxDebugLevel");
if ( vivoxLogLevel.empty() )
@@ -531,8 +535,8 @@ void LLVivoxVoiceClient::connectorCreate()
vivoxLogLevel = "0";
}
LL_DEBUGS("Voice") << "creating connector with log level " << vivoxLogLevel << LL_ENDL;
-
- stream
+
+ stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
<< "<ClientName>V2 SDK</ClientName>"
<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
@@ -548,7 +552,7 @@ void LLVivoxVoiceClient::connectorCreate()
//<< "<Application></Application>" //Name can cause problems per vivox.
<< "<MaxCalls>12</MaxCalls>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
@@ -562,10 +566,10 @@ void LLVivoxVoiceClient::connectorShutdown()
<< "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>"
<< "</Request>"
<< "\n\n\n";
-
+
mShutdownComplete = false;
mConnectorEstablished = false;
-
+
writeString(stream.str());
}
else
@@ -597,7 +601,7 @@ void LLVivoxVoiceClient::setLoginInfo(
{
// Already logged in.
LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL;
-
+
// Don't process another login.
return;
}
@@ -612,14 +616,14 @@ void LLVivoxVoiceClient::setLoginInfo(
}
std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
-
+
if( !debugSIPURIHostName.empty() )
{
LL_INFOS("Voice") << "Overriding account server based on VivoxDebugSIPURIHostName: "
<< debugSIPURIHostName << LL_ENDL;
mVoiceSIPURIHostName = debugSIPURIHostName;
}
-
+
if( mVoiceSIPURIHostName.empty() )
{
// we have an empty account server name
@@ -639,7 +643,7 @@ void LLVivoxVoiceClient::setLoginInfo(
<< mVoiceSIPURIHostName << LL_ENDL;
}
-
+
std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
if( !debugAccountServerURI.empty() )
@@ -648,11 +652,11 @@ void LLVivoxVoiceClient::setLoginInfo(
<< debugAccountServerURI << LL_ENDL;
mVoiceAccountServerURI = debugAccountServerURI;
}
-
+
if( mVoiceAccountServerURI.empty() )
{
// If the account server URI isn't specified, construct it from the SIP URI hostname
- mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";
+ mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";
LL_INFOS("Voice") << "Inferring account server based on SIP URI Host name: "
<< mVoiceAccountServerURI << LL_ENDL;
}
@@ -663,11 +667,11 @@ void LLVivoxVoiceClient::idle(void* user_data)
}
//=========================================================================
-// the following are methods to support the coroutine implementation of the
-// voice connection and processing. They should only be called in the context
+// the following are methods to support the coroutine implementation of the
+// voice connection and processing. They should only be called in the context
// of a coroutine.
-//
-//
+//
+//
typedef enum e_voice_control_coro_state
{
@@ -816,7 +820,7 @@ void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state)
case VOICE_STATE_SESSION_ESTABLISHED:
{
- // enable/disable the automatic VAD and explicitly set the initial values of
+ // enable/disable the automatic VAD and explicitly set the initial values of
// the VAD variables ourselves when it is off - see SL-15072 for more details
// note: we set the other parameters too even if the auto VAD is on which is ok
unsigned int vad_auto = gSavedSettings.getU32("VivoxVadAuto");
@@ -914,7 +918,7 @@ bool LLVivoxVoiceClient::callbackEndDaemon(const LLSD& data)
bool LLVivoxVoiceClient::startAndLaunchDaemon()
{
//---------------------------------------------------------------------
- if (!voiceEnabled())
+ if (!LLVoiceClient::getInstance()->voiceEnabled())
{
// Voice is locked out, we must not launch the vivox daemon.
LL_WARNS("Voice") << "voice disabled; not starting daemon" << LL_ENDL;
@@ -991,7 +995,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
{
LLFile::rename(new_log, old_log);
}
-
+
std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout");
if (!shutdown_timeout.empty())
{
@@ -1072,7 +1076,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
llcoro::suspendUntilTimeout(DAEMON_CONNECT_THROTTLE_SECONDS);
}
}
-
+
//---------------------------------------------------------------------
if (sShuttingDown && !sConnected)
{
@@ -1090,7 +1094,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
{
return false;
}
-
+
// MBW -- Note to self: pumps and pipes examples in
// indra/test/io.cpp
// indra/test/llpipeutil.{cpp|h}
@@ -1128,7 +1132,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
// *TODO* Pump a message for wake up.
llcoro::suspend();
}
-
+
if (sShuttingDown)
{
return false;
@@ -1180,7 +1184,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
else
{
provisioned = true;
- }
+ }
} while (!provisioned && ++retryCount <= PROVISION_RETRY_MAX && !sShuttingDown);
if (sShuttingDown && !provisioned)
@@ -1194,7 +1198,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
LL_WARNS("Voice") << "Could not access voice provision cap after " << retryCount << " attempts." << LL_ENDL;
return false;
}
-
+ LL_WARNS("Voice") << "Voice Provision Result." << result << LL_ENDL;
std::string voiceSipUriHostname;
std::string voiceAccountServerUri;
std::string voiceUserName = result["username"].asString();
@@ -1204,7 +1208,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
{
voiceSipUriHostname = result["voice_sip_uri_hostname"].asString();
}
-
+
// this key is actually misnamed -- it will be an entire URI, not just a hostname.
if (result.has("voice_account_server_name"))
{
@@ -1235,7 +1239,7 @@ bool LLVivoxVoiceClient::establishVoiceConnection()
{
return false;
}
-
+
LLSD result;
bool connected(false);
bool giving_up(false);
@@ -1354,7 +1358,7 @@ bool LLVivoxVoiceClient::loginToVivox()
bool account_login(false);
bool send_login(true);
- do
+ do
{
mIsLoggingIn = true;
if (send_login)
@@ -1362,7 +1366,7 @@ bool LLVivoxVoiceClient::loginToVivox()
loginSendMessage();
send_login = false;
}
-
+
LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult);
if (sShuttingDown)
@@ -1402,7 +1406,7 @@ bool LLVivoxVoiceClient::loginToVivox()
// tell the user there is a problem
LL_WARNS("Voice") << "login " << loginresp << " will retry login in " << timeout << " seconds." << LL_ENDL;
-
+
if (!sShuttingDown)
{
// Todo: this is way to long, viewer can get stuck waiting during shutdown
@@ -1507,7 +1511,7 @@ bool LLVivoxVoiceClient::retrieveVoiceFonts()
mIsWaitingForFonts = true;
LLSD result;
- do
+ do
{
result = llcoro::suspendUntilEventOn(mVivoxPump);
@@ -1542,7 +1546,7 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL;
return false;
}
-
+
// update the parcel
checkParcelChanged(true);
@@ -1590,48 +1594,7 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
return false;
}
- std::string uri;
- std::string credentials;
-
- if (result.has("voice_credentials"))
- {
- LLSD voice_credentials = result["voice_credentials"];
- if (voice_credentials.has("channel_uri"))
- {
- LL_DEBUGS("Voice") << "got voice channel uri" << LL_ENDL;
- uri = voice_credentials["channel_uri"].asString();
- }
- else
- {
- LL_WARNS("Voice") << "No voice channel uri" << LL_ENDL;
- }
-
- if (voice_credentials.has("channel_credentials"))
- {
- LL_DEBUGS("Voice") << "got voice channel credentials" << LL_ENDL;
- credentials =
- voice_credentials["channel_credentials"].asString();
- }
- else
- {
- LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
- if (channel != NULL)
- {
- if (channel->getSessionName().empty() && channel->getSessionID().isNull())
- {
- if (LLViewerParcelMgr::getInstance()->allowAgentVoice())
- {
- LL_WARNS("Voice") << "No channel credentials for default channel" << LL_ENDL;
- }
- }
- else
- {
- LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL;
- }
- }
- }
- }
- else
+ if (!result.has("voice_credentials"))
{
if (LLViewerParcelMgr::getInstance()->allowAgentVoice())
{
@@ -1643,9 +1606,9 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
}
}
- // set the spatial channel. If no voice credentials or uri are
+ // set the spatial channel. If no voice credentials or uri are
// available, then we simply drop out of voice spatially.
- return !setSpatialChannel(uri, credentials);
+ return setSpatialChannel(result["voice_credentials"]);
}
bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
@@ -1698,7 +1661,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL;
}
-
+
// Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
// example for p2p many times while waiting for response, so it can't be used to detect errors
if (mAudioSession && mAudioSession->mIsSpatial)
@@ -1740,8 +1703,8 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
LLSD timeoutResult(LLSDMap("session", "timeout"));
- // We are about to start a whole new session. Anything that MIGHT still be in our
- // maildrop is going to be stale and cause us much wailing and gnashing of teeth.
+ // We are about to start a whole new session. Anything that MIGHT still be in our
+ // maildrop is going to be stale and cause us much wailing and gnashing of teeth.
// Just flush it all out and start new.
mVivoxPump.discard();
@@ -1784,25 +1747,25 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
}
else if ((message == "failed") || (message == "removed") || (message == "timeout"))
{ // we will get a removed message if a voice call is declined.
-
- if (message == "failed")
+
+ if (message == "failed")
{
int reason = result["reason"].asInteger();
LL_WARNS("Voice") << "Add and join failed for reason " << reason << LL_ENDL;
-
+
if ( (reason == ERROR_VIVOX_NOT_LOGGED_IN)
|| (reason == ERROR_VIVOX_OBJECT_NOT_FOUND))
{
LL_DEBUGS("Voice") << "Requesting reprovision and login." << LL_ENDL;
requestRelog();
- }
+ }
}
else
{
LL_WARNS("Voice") << "session '" << message << "' "
<< LL_ENDL;
}
-
+
notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
mIsJoiningSession = false;
return false;
@@ -1821,8 +1784,8 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
// Events that need to happen when a session is joined could go here.
// send an initial positional information immediately upon joining.
- //
- // do an initial update for position and the camera position, then send a
+ //
+ // do an initial update for position and the camera position, then send a
// positional update.
updatePosition();
enforceTether();
@@ -1929,7 +1892,7 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait)
notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
// Always reset the terminate request flag when we get here.
- // Some slower PCs have a race condition where they can switch to an incoming P2P call faster than the state machine leaves
+ // Some slower PCs have a race condition where they can switch to an incoming P2P call faster than the state machine leaves
// the region chat.
mSessionTerminateRequested = false;
@@ -1964,7 +1927,7 @@ bool LLVivoxVoiceClient::waitForChannel()
EVoiceWaitForChannelState state = VOICE_CHANNEL_STATE_LOGIN;
- do
+ do
{
if (sShuttingDown)
{
@@ -2008,7 +1971,6 @@ bool LLVivoxVoiceClient::waitForChannel()
break;
case VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING:
- mIsProcessingChannels = true;
llcoro::suspend();
state = VOICE_CHANNEL_STATE_PROCESS_CHANNEL;
break;
@@ -2022,7 +1984,7 @@ bool LLVivoxVoiceClient::waitForChannel()
{
recordingAndPlaybackMode();
}
- else if (checkParcelChanged() || (mNextAudioSession == NULL))
+ else if (mProcessChannels && (mNextAudioSession == NULL) && checkParcelChanged())
{
// the parcel is changed, or we have no pending audio sessions,
// so try to request the parcel voice info
@@ -2037,15 +1999,23 @@ bool LLVivoxVoiceClient::waitForChannel()
}
else if (mNextAudioSession)
{
+ if (!mNextAudioSession->mIsP2P && !mProcessChannels)
+ {
+ llcoro::suspend();
+ break;
+ }
sessionStatePtr_t joinSession = mNextAudioSession;
mNextAudioSession.reset();
+ mIsProcessingChannels = true;
if (!runSession(joinSession)) //suspends
{
+ mIsProcessingChannels = false;
LL_DEBUGS("Voice") << "runSession returned false; leaving inner loop" << LL_ENDL;
break;
}
else
{
+ mIsProcessingChannels = false;
LL_DEBUGS("Voice")
<< "runSession returned true to inner loop"
<< " RelogRequested=" << mRelogRequested
@@ -2131,13 +2101,13 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
{
LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL;
- bool joined_session = addAndJoinSession(session);
-
- if (sShuttingDown)
+ if (sShuttingDown || !mProcessChannels)
{
return false;
}
+ bool joined_session = addAndJoinSession(session);
+
if (!joined_session)
{
notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
@@ -2147,10 +2117,10 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
LL_DEBUGS("Voice") << "runSession terminate requested " << LL_ENDL;
terminateAudioSession(true);
}
- // if a relog has been requested then addAndJoineSession
+ // if a relog has been requested then addAndJoineSession
// failed in a spectacular way and we need to back out.
// If this is not the case then we were simply trying to
- // make a call and the other party rejected it.
+ // make a call and the other party rejected it.
return !mRelogRequested;
}
@@ -2161,12 +2131,14 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
mIsInChannel = true;
mMuteMicDirty = true;
+ mSessionTerminateRequested = false;
while (!sShuttingDown
&& mVoiceEnabled
&& isGatewayRunning()
&& !mSessionTerminateRequested
- && !mTuningMode)
+ && !mTuningMode
+ && mProcessChannels)
{
sendCaptureAndRenderDevices(); // suspends
@@ -2185,7 +2157,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
mAudioSession->mParticipantsChanged = false;
notifyParticipantObservers();
}
-
+
if (!inSpatialChannel())
{
// When in a non-spatial channel, never send positional updates.
@@ -2197,9 +2169,9 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
if (checkParcelChanged())
{
- // *RIDER: I think I can just return here if the parcel has changed
+ // *RIDER: I think I can just return here if the parcel has changed
// and grab the new voice channel from the outside loop.
- //
+ //
// if the parcel has changed, attempted to request the
// cap for the parcel voice info. If we can't request it
// then we don't have the cap URL so we do nothing and will
@@ -2242,7 +2214,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
}
if (result.has("session"))
- {
+ {
if (result.has("handle"))
{
if (!mAudioSession)
@@ -2347,7 +2319,7 @@ void LLVivoxVoiceClient::recordingAndPlaybackMode()
int LLVivoxVoiceClient::voiceRecordBuffer()
{
- LLSD timeoutResult(LLSDMap("recplay", "stop"));
+ LLSD timeoutResult(LLSDMap("recplay", "stop"));
LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL;
@@ -2418,95 +2390,11 @@ int LLVivoxVoiceClient::voicePlaybackBuffer()
bool LLVivoxVoiceClient::performMicTuning()
{
LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL;
-
mIsInTuningMode = true;
- llcoro::suspend();
while (mTuningMode && !sShuttingDown)
{
-
- if (mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // These can't be changed while in tuning mode. Set them before starting.
- std::ostringstream stream;
-
- buildSetCaptureDevice(stream);
- buildSetRenderDevice(stream);
-
- if (!stream.str().empty())
- {
- writeString(stream.str());
- }
-
- llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
- }
-
- // loop mic back to render device.
- //setMuteMic(0); // make sure the mic is not muted
- std::ostringstream stream;
-
- stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
- << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>"
- << "<Value>false</Value>"
- << "</Request>\n\n\n";
-
- // Dirty the mute mic state so that it will get reset when we finishing previewing
- mMuteMicDirty = true;
- mTuningSpeakerVolumeDirty = true;
-
- writeString(stream.str());
- tuningCaptureStartSendMessage(1); // 1-loop, zero, don't loop
-
- //---------------------------------------------------------------------
- if (!sShuttingDown)
- {
- llcoro::suspend();
- }
-
- while (mTuningMode && !mCaptureDeviceDirty && !mRenderDeviceDirty && !sShuttingDown)
- {
- // process mic/speaker volume changes
- if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
- {
- std::ostringstream stream;
-
- if (mTuningMicVolumeDirty)
- {
- LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
- << "<Level>" << mTuningMicVolume << "</Level>"
- << "</Request>\n\n\n";
- }
-
- if (mTuningSpeakerVolumeDirty)
- {
- LL_INFOS("Voice") << "setting tuning speaker level to " << mTuningSpeakerVolume << LL_ENDL;
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
- << "<Level>" << mTuningSpeakerVolume << "</Level>"
- << "</Request>\n\n\n";
- }
-
- mTuningMicVolumeDirty = false;
- mTuningSpeakerVolumeDirty = false;
-
- if (!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- llcoro::suspend();
- }
-
- //---------------------------------------------------------------------
-
- // transition out of mic tuning
- tuningCaptureStopSendMessage();
- if ((mCaptureDeviceDirty || mRenderDeviceDirty) && !sShuttingDown)
- {
- llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
- }
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
}
mIsInTuningMode = false;
@@ -2554,7 +2442,7 @@ void LLVivoxVoiceClient::logout()
// Ensure that we'll re-request provisioning before logging in again
mAccountPassword.clear();
mVoiceAccountServerURI.clear();
-
+
logoutSendMessage();
}
@@ -2579,7 +2467,7 @@ void LLVivoxVoiceClient::logoutSendMessage()
void LLVivoxVoiceClient::sessionGroupCreateSendMessage()
{
if(mAccountLoggedIn)
- {
+ {
std::ostringstream stream;
LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
@@ -2632,6 +2520,7 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &sessi
<< "<VoiceFontID>" << font_index << "</VoiceFontID>"
<< "<Name>" << mChannelName << "</Name>"
<< "</Request>\n\n\n";
+ LL_WARNS("Voice") << "Session.Create: " << stream.str() << LL_ENDL;
writeString(stream.str());
}
@@ -2647,7 +2536,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr
{
session->mMediaConnectInProgress = true;
}
-
+
std::string password;
if(!session->mHash.empty())
{
@@ -2672,7 +2561,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr
<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
<< "</Request>\n\n\n"
;
-
+
writeString(stream.str());
}
@@ -2684,7 +2573,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t
<< LL_ENDL;
session->mMediaConnectInProgress = true;
-
+
std::ostringstream stream;
stream
@@ -2701,7 +2590,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t
void LLVivoxVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session)
{
LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
-
+
std::ostringstream stream;
stream
@@ -2742,7 +2631,7 @@ void LLVivoxVoiceClient::leaveAudioSession()
time_t now = time(NULL);
const size_t BUF_SIZE = 64;
char time_str[BUF_SIZE]; /* Flawfinder: ignore */
-
+
strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
savepath += time_str;
}
@@ -2753,7 +2642,7 @@ void LLVivoxVoiceClient::leaveAudioSession()
}
else
{
- LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
+ LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
}
}
else
@@ -2770,12 +2659,12 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &se
sessionGroupTerminateSendMessage(session);
return;
/*
- LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
+ LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
*/
}
@@ -2783,13 +2672,13 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &se
void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session)
{
std::ostringstream stream;
-
- LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;
+
+ LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Terminate.1\">"
<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
@@ -2799,17 +2688,17 @@ void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr
sessionGroupTerminateSendMessage(session);
return;
/*
- LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
+ LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
<< "<Media>Audio</Media>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
*/
-
+
}
@@ -2819,7 +2708,7 @@ void LLVivoxVoiceClient::getCaptureDevicesSendMessage()
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
@@ -2829,7 +2718,7 @@ void LLVivoxVoiceClient::getRenderDevicesSendMessage()
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
@@ -2857,7 +2746,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
if(!mCaptureDevice.empty())
{
mCaptureDevice.clear();
- mCaptureDeviceDirty = true;
+ mCaptureDeviceDirty = true;
}
}
else
@@ -2865,7 +2754,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
if(mCaptureDevice != name)
{
mCaptureDevice = name;
- mCaptureDeviceDirty = true;
+ mCaptureDeviceDirty = true;
}
}
}
@@ -2875,7 +2764,7 @@ void LLVivoxVoiceClient::setDevicesListUpdated(bool state)
}
void LLVivoxVoiceClient::clearRenderDevices()
-{
+{
LL_DEBUGS("Voice") << "called" << LL_ENDL;
mRenderDevices.clear();
}
@@ -2898,7 +2787,7 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name)
if(!mRenderDevice.empty())
{
mRenderDevice.clear();
- mRenderDeviceDirty = true;
+ mRenderDeviceDirty = true;
}
}
else
@@ -2906,10 +2795,10 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name)
if(mRenderDevice != name)
{
mRenderDevice = name;
- mRenderDeviceDirty = true;
+ mRenderDeviceDirty = true;
}
}
-
+
}
void LLVivoxVoiceClient::tuningStart()
@@ -2931,6 +2820,9 @@ void LLVivoxVoiceClient::tuningStart()
void LLVivoxVoiceClient::tuningStop()
{
mTuningMode = false;
+ // force a renegotiation.
+ mCurrentParcelLocalID = 0;
+ mCurrentRegionName = "";
}
bool LLVivoxVoiceClient::inTuningMode()
@@ -2939,7 +2831,7 @@ bool LLVivoxVoiceClient::inTuningMode()
}
void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
-{
+{
mTuningAudioFile = name;
std::ostringstream stream;
stream
@@ -2947,7 +2839,7 @@ void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, b
<< "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
<< "<Loop>" << (loop?"1":"0") << "</Loop>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
@@ -2958,33 +2850,33 @@ void LLVivoxVoiceClient::tuningRenderStopSendMessage()
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
<< "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int loop)
{
LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
-
+
std::ostringstream stream;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
<< "<Duration>-1</Duration>"
<< "<LoopToRenderDevice>" << loop << "</LoopToRenderDevice>"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
}
void LLVivoxVoiceClient::tuningCaptureStopSendMessage()
{
LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL;
-
+
std::ostringstream stream;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
<< "</Request>\n\n\n";
-
+
writeString(stream.str());
mTuningEnergy = 0.0f;
@@ -3003,7 +2895,7 @@ void LLVivoxVoiceClient::tuningSetMicVolume(float volume)
void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume)
{
- int scaled_volume = scale_speaker_volume(volume);
+ int scaled_volume = scale_speaker_volume(volume);
if(scaled_volume != mTuningSpeakerVolume)
{
@@ -3011,7 +2903,7 @@ void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume)
mTuningSpeakerVolumeDirty = true;
}
}
-
+
float LLVivoxVoiceClient::tuningGetEnergy(void)
{
return mTuningEnergy;
@@ -3020,13 +2912,13 @@ float LLVivoxVoiceClient::tuningGetEnergy(void)
bool LLVivoxVoiceClient::deviceSettingsAvailable()
{
bool result = true;
-
+
if(!sConnected)
result = false;
-
+
if(mRenderDevices.empty())
result = false;
-
+
return result;
}
bool LLVivoxVoiceClient::deviceSettingsUpdated()
@@ -3037,7 +2929,7 @@ bool LLVivoxVoiceClient::deviceSettingsUpdated()
// a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel.
mDevicesListUpdated = false; // toggle the setting
}
- return updated;
+ return updated;
}
void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList)
@@ -3070,9 +2962,9 @@ void LLVivoxVoiceClient::giveUp()
static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
{
F32 nat[3], nup[3], nl[3]; // the new at, up, left vectors and the new position and velocity
-// F32 nvel[3];
+// F32 nvel[3];
F64 npos[3];
-
+
// The original XML command was sent like this:
/*
<< "<Position>"
@@ -3130,7 +3022,7 @@ static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVe
left.mV[i] = nl[i];
pos.mdV[i] = npos[i];
}
-
+
// This was the original transform done in the SDK
nat[0] = at.mV[2];
nat[1] = 0; // y component of at vector is always 0, this was up[2]
@@ -3184,7 +3076,7 @@ static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVe
left.mV[i] = nl[i];
pos.mdV[i] = npos[i];
}
-
+
#endif
}
@@ -3194,7 +3086,7 @@ void LLVivoxVoiceClient::setHidden(bool hidden)
if (mHidden && inSpatialChannel())
{
- // get out of the channel entirely
+ // get out of the channel entirely
leaveAudioSession();
}
else
@@ -3204,20 +3096,20 @@ void LLVivoxVoiceClient::setHidden(bool hidden)
}
void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
-{
+{
std::ostringstream stream;
-
+
if (mSpatialCoordsDirty && inSpatialChannel())
{
LLVector3 l, u, a, vel;
LLVector3d pos;
mSpatialCoordsDirty = false;
-
+
// Always send both speaker and listener positions together.
- stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"
<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
-
+
stream << "<SpeakerPosition>";
LLMatrix3 avatarRot = mAvatarRot.getMatrix3();
@@ -3233,7 +3125,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
// SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
// The old transform is replicated by this function.
oldSDKTransform(l, u, a, pos, vel);
-
+
if (mHidden)
{
for (int i=0;i<3;++i)
@@ -3241,7 +3133,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
pos.mdV[i] = VX_NULL_POSITION;
}
}
-
+
stream
<< "<Position>"
<< "<X>" << pos.mdV[VX] << "</X>"
@@ -3269,7 +3161,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
<< "<Z>" << l.mV [VZ] << "</Z>"
<< "</LeftOrientation>"
;
-
+
stream << "</SpeakerPosition>";
stream << "<ListenerPosition>";
@@ -3277,7 +3169,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
LLVector3d earPosition;
LLVector3 earVelocity;
LLMatrix3 earRot;
-
+
switch(mEarLocation)
{
case earLocCamera:
@@ -3286,13 +3178,13 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
earVelocity = mCameraVelocity;
earRot = mCameraRot;
break;
-
+
case earLocAvatar:
earPosition = mAvatarPosition;
earVelocity = mAvatarVelocity;
earRot = avatarRot;
break;
-
+
case earLocMixed:
earPosition = mAvatarPosition;
earVelocity = mAvatarVelocity;
@@ -3307,9 +3199,9 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
pos = earPosition;
vel = earVelocity;
-
+
oldSDKTransform(l, u, a, pos, vel);
-
+
if (mHidden)
{
for (int i=0;i<3;++i)
@@ -3317,7 +3209,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
pos.mdV[i] = VX_NULL_POSITION;
}
}
-
+
stream
<< "<Position>"
<< "<X>" << pos.mdV[VX] << "</X>"
@@ -3350,19 +3242,19 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
stream << "<ReqDispositionType>1</ReqDispositionType>"; //do not generate responses for update requests
stream << "</Request>\n\n\n";
- }
-
+ }
+
if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty))
{
participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
mAudioSession->mVolumeDirty = false;
mAudioSession->mMuteDirty = false;
-
+
for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
{
participantStatePtr_t p(iter->second);
-
+
if(p->mVolumeDirty)
{
// Can't set volume/mute for yourself
@@ -3371,7 +3263,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
// scale from the range 0.0-1.0 to vivox volume in the range 0-100
S32 volume = ll_round(p->mVolume / VOLUME_SCALE_VIVOX);
bool mute = p->mOnMuteList;
-
+
if(mute)
{
// SetParticipantMuteForMe doesn't work in p2p sessions.
@@ -3382,16 +3274,16 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
// changing it to 0, so that we can return to it when unmuting.
p->mVolumeSet = true;
}
-
+
if(volume == 0)
{
mute = true;
}
LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
-
+
// SLIM SDK: Send both volume and mute commands.
-
+
// Send a "volume for me" command for the user.
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
@@ -3411,7 +3303,7 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void)
<< "</Request>\n\n\n";
}
}
-
+
p->mVolumeDirty = false;
}
}
@@ -3431,13 +3323,13 @@ void LLVivoxVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
if(mCaptureDeviceDirty)
{
LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
-
- stream
+
+ stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
<< "</Request>"
<< "\n\n\n";
-
+
mCaptureDeviceDirty = false;
}
}
@@ -3529,7 +3421,7 @@ void LLVivoxVoiceClient::sendLocalAudioUpdates()
* Because of the recurring voice cutout issues (SL-15072) we are going to try
* to disable the automatic VAD (Voice Activity Detection) and set the associated
* parameters directly. We will expose them via Debug Settings and that should
- * let us iterate on a collection of values that work for us. Hopefully!
+ * let us iterate on a collection of values that work for us. Hopefully!
*
* From the VIVOX Docs:
*
@@ -3539,16 +3431,16 @@ void LLVivoxVoiceClient::sendLocalAudioUpdates()
* for the VAD to switch back to silence from speech mode after the last speech
* frame has been detected.
*
- * VadNoiseFloor: A dimensionless value between 0 and
+ * VadNoiseFloor: A dimensionless value between 0 and
* 20000 (default 576) that controls the maximum level at which the noise floor
* may be set at by the VAD's noise tracking. Too low of a value will make noise
- * tracking ineffective (A value of 0 disables noise tracking and the VAD then
- * relies purely on the sensitivity property). Too high of a value will make
+ * tracking ineffective (A value of 0 disables noise tracking and the VAD then
+ * relies purely on the sensitivity property). Too high of a value will make
* long speech classifiable as noise.
*
- * VadSensitivity: A dimensionless value between 0 and
+ * VadSensitivity: A dimensionless value between 0 and
* 100, indicating the 'sensitivity of the VAD'. Increasing this value corresponds
- * to decreasing the sensitivity of the VAD (i.e. '0' is most sensitive,
+ * to decreasing the sensitivity of the VAD (i.e. '0' is most sensitive,
* while 100 is 'least sensitive')
*/
void LLVivoxVoiceClient::setupVADParams(unsigned int vad_auto,
@@ -3596,7 +3488,7 @@ void LLVivoxVoiceClient::onVADSettingsChange()
// Response/Event handlers
void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
-{
+{
LLSD result = LLSD::emptyMap();
if(statusCode == 0)
@@ -3630,7 +3522,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
// this is usually fatal, but a long timeout might work
result["connector"] = LLSD::Boolean(false);
result["retry"] = LLSD::Real(CONNECT_ATTEMPT_TIMEOUT);
-
+
LL_WARNS("Voice") << "Voice connection failed" << LL_ENDL;
}
else if (statusCode == 10006) // name resolution failure - a shorter retry may work
@@ -3638,7 +3530,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
// some networks have slower DNS, but a short timeout might let it catch up
result["connector"] = LLSD::Boolean(false);
result["retry"] = LLSD::Real(CONNECT_DNS_TIMEOUT);
-
+
LL_WARNS("Voice") << "Voice connection DNS lookup failed" << LL_ENDL;
}
else // unknown failure - give up
@@ -3652,13 +3544,13 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
}
void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
-{
+{
LLSD result = LLSD::emptyMap();
LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
-
+
// Status code of 20200 means "bad password". We may want to special-case that at some point.
-
+
if ( statusCode == HTTP_UNAUTHORIZED )
{
// Login failure which is probably caused by the delay after a user's password being updated.
@@ -3683,20 +3575,20 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString
}
void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
-{
+{
sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId));
-
+
if(session)
{
session->mCreateInProgress = false;
}
-
+
if(statusCode != 0)
{
LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
if(session)
{
- session->mErrorStatusCode = statusCode;
+ session->mErrorStatusCode = statusCode;
session->mErrorStatusString = statusString;
if(session == mAudioSession)
{
@@ -3727,20 +3619,20 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
}
void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
-{
+{
sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId));
-
+
if(session)
{
session->mCreateInProgress = false;
}
-
+
if(statusCode != 0)
{
LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
if(session)
{
- session->mErrorStatusCode = statusCode;
+ session->mErrorStatusCode = statusCode;
session->mErrorStatusString = statusString;
if(session == mAudioSession)
{
@@ -3801,7 +3693,7 @@ void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int stat
}
void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString)
-{
+{
if(statusCode != 0)
{
LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
@@ -3819,21 +3711,21 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &
LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
// Should this ever fail? do we care if it does?
}
-
+
sConnected = false;
mShutdownComplete = true;
-
+
LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false)));
mVivoxPump.post(vivoxevent);
}
void LLVivoxVoiceClient::sessionAddedEvent(
- std::string &uriString,
- std::string &alias,
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- bool isChannel,
+ std::string &uriString,
+ std::string &alias,
+ std::string &sessionHandle,
+ std::string &sessionGroupHandle,
+ bool isChannel,
bool incoming,
std::string &nameString,
std::string &applicationString)
@@ -3841,7 +3733,7 @@ void LLVivoxVoiceClient::sessionAddedEvent(
sessionStatePtr_t session;
LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
-
+
session = addSession(uriString, sessionHandle);
if(session)
{
@@ -3849,19 +3741,19 @@ void LLVivoxVoiceClient::sessionAddedEvent(
session->mIsChannel = isChannel;
session->mIncoming = incoming;
session->mAlias = alias;
-
+
// Generate a caller UUID -- don't need to do this for channels
if(!session->mIsChannel)
{
if(IDFromName(session->mSIPURI, session->mCallerID))
{
- // Normal URI(base64-encoded UUID)
+ // Normal URI(base64-encoded UUID)
}
else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
{
// Wrong URI, but an alias is available. Stash the incoming URI as an alternate
session->mAlternateSIPURI = session->mSIPURI;
-
+
// and generate a proper URI from the ID.
setSessionURI(session, sipURIFromID(session->mCallerID));
}
@@ -3870,7 +3762,7 @@ void LLVivoxVoiceClient::sessionAddedEvent(
LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
session->mCallerID.generate(session->mSIPURI);
session->mSynthesizedCallerID = true;
-
+
// Can't look up the name in this case -- we have to extract it from the URI.
std::string namePortion = nameFromsipURI(session->mSIPURI);
if(namePortion.empty())
@@ -3878,14 +3770,14 @@ void LLVivoxVoiceClient::sessionAddedEvent(
// Didn't seem to be a SIP URI, just use the whole provided name.
namePortion = nameString;
}
-
+
// Some incoming names may be separated with an underscore instead of a space. Fix this.
LLStringUtil::replaceChar(namePortion, '_', ' ');
-
+
// Act like we just finished resolving the name (this stores it in all the right places)
avatarNameResolved(session->mCallerID, namePortion);
}
-
+
LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
if(!session->mSynthesizedCallerID)
@@ -3900,7 +3792,7 @@ void LLVivoxVoiceClient::sessionAddedEvent(
void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
{
LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
-
+
#if USE_SESSION_GROUPS
if(mMainSessionGroupHandle.empty())
{
@@ -3927,7 +3819,7 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)
// The old session may now need to be deleted.
reapSession(oldSession);
}
-
+
// This is the session we're joining.
if(mIsJoiningSession)
{
@@ -3943,10 +3835,10 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)
participant->mIsSelf = true;
lookupName(participant->mAvatarID);
- LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName
+ LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName
<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
}
-
+
if(!session->mIsChannel)
{
// this is a p2p session. Make sure the other end is added as a participant.
@@ -3962,9 +3854,9 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)
participant->mDisplayName = session->mName;
avatarNameResolved(participant->mAvatarID, session->mName);
}
-
+
// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
- LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName
+ LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName
<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
}
}
@@ -3972,11 +3864,11 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session)
}
void LLVivoxVoiceClient::sessionRemovedEvent(
- std::string &sessionHandle,
+ std::string &sessionHandle,
std::string &sessionGroupHandle)
{
LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
-
+
sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
@@ -3984,15 +3876,15 @@ void LLVivoxVoiceClient::sessionRemovedEvent(
// This message invalidates the session's handle. Set it to empty.
clearSessionHandle(session);
-
+
// This also means that the session's session group is now empty.
// Terminate the session group so it doesn't leak.
sessionGroupTerminateSendMessage(session);
-
+
// Reset the media state (we now have no info)
session->mMediaStreamState = streamStateUnknown;
//session->mTextStreamState = streamStateUnknown;
-
+
// Conditionally delete the session
reapSession(session);
}
@@ -4008,7 +3900,7 @@ void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session)
{
if(session)
{
-
+
if(session->mCreateInProgress)
{
LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
@@ -4030,7 +3922,7 @@ void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session)
// We don't have a reason to keep tracking this session, so just delete it.
LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
deleteSession(session);
- }
+ }
}
else
{
@@ -4042,32 +3934,32 @@ void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session)
bool LLVivoxVoiceClient::sessionNeedsRelog(const sessionStatePtr_t &session)
{
bool result = false;
-
+
if(session)
{
// Only make this check for spatial channels (so it won't happen for group or p2p calls)
if(session->mIsSpatial)
- {
+ {
std::string::size_type atsign;
-
+
atsign = session->mSIPURI.find("@");
-
+
if(atsign != std::string::npos)
{
std::string urihost = session->mSIPURI.substr(atsign + 1);
if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
{
// The hostname in this URI is different from what we expect. This probably means we need to relog.
-
+
// We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
// mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
-
+
result = true;
}
}
}
}
-
+
return result;
}
@@ -4083,9 +3975,9 @@ void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session)
}
void LLVivoxVoiceClient::accountLoginStateChangeEvent(
- std::string &accountHandle,
- int statusCode,
- std::string &statusString,
+ std::string &accountHandle,
+ int statusCode,
+ std::string &statusString,
int state)
{
LLSD levent = LLSD::emptyMap();
@@ -4097,9 +3989,9 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
login_state_logging_in = 2,
login_state_logging_out = 3,
login_state_resetting = 4,
- login_state_error=100
+ login_state_error=100
*/
-
+
LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL;
switch(state)
{
@@ -4129,7 +4021,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
mVivoxPump.post(levent);
break;
-
+
default:
//Used to be a commented out warning
LL_WARNS("Voice") << "unknown account state event: " << state << LL_ENDL;
@@ -4166,24 +4058,24 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s
}
void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- int statusCode,
- std::string &statusString,
- int state,
+ std::string &sessionHandle,
+ std::string &sessionGroupHandle,
+ int statusCode,
+ std::string &statusString,
+ int state,
bool incoming)
{
sessionStatePtr_t session(findSession(sessionHandle));
-
+
LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
-
+
if(session)
{
// We know about this session
-
+
// Save the state for later use
session->mMediaStreamState = state;
-
+
switch(statusCode)
{
case 0:
@@ -4211,9 +4103,9 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
session->mVoiceActive = true;
session->mMediaConnectInProgress = false;
joinedAudioSession(session);
- case streamStateConnecting: // do nothing, but prevents a warning getting into the logs.
+ case streamStateConnecting: // do nothing, but prevents a warning getting into the logs.
break;
-
+
case streamStateRinging:
if(incoming)
{
@@ -4232,13 +4124,13 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
}
}
break;
-
+
default:
LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
break;
-
+
}
-
+
}
else
{
@@ -4248,12 +4140,12 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
}
void LLVivoxVoiceClient::participantAddedEvent(
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- std::string &uriString,
- std::string &alias,
- std::string &nameString,
- std::string &displayNameString,
+ std::string &sessionHandle,
+ std::string &sessionGroupHandle,
+ std::string &uriString,
+ std::string &alias,
+ std::string &nameString,
+ std::string &displayNameString,
int participantType)
{
sessionStatePtr_t session(findSession(sessionHandle));
@@ -4264,7 +4156,7 @@ void LLVivoxVoiceClient::participantAddedEvent(
{
participant->mAccountName = nameString;
- LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName
+ LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName
<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
if(participant->mAvatarIDValid)
@@ -4286,7 +4178,7 @@ void LLVivoxVoiceClient::participantAddedEvent(
// Problems with both of the above, fall back to the account name
namePortion = nameString;
}
-
+
// Set the display name (which is a hint to the active speakers window not to do its own lookup)
participant->mDisplayName = namePortion;
avatarNameResolved(participant->mAvatarID, namePortion);
@@ -4296,10 +4188,10 @@ void LLVivoxVoiceClient::participantAddedEvent(
}
void LLVivoxVoiceClient::participantRemovedEvent(
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- std::string &uriString,
- std::string &alias,
+ std::string &sessionHandle,
+ std::string &sessionGroupHandle,
+ std::string &uriString,
+ std::string &alias,
std::string &nameString)
{
sessionStatePtr_t session(findSession(sessionHandle));
@@ -4324,20 +4216,20 @@ void LLVivoxVoiceClient::participantRemovedEvent(
void LLVivoxVoiceClient::participantUpdatedEvent(
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- std::string &uriString,
- std::string &alias,
- bool isModeratorMuted,
- bool isSpeaking,
- int volume,
+ std::string &sessionHandle,
+ std::string &sessionGroupHandle,
+ std::string &uriString,
+ std::string &alias,
+ bool isModeratorMuted,
+ bool isSpeaking,
+ int volume,
F32 energy)
{
sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
participantStatePtr_t participant(session->findParticipant(uriString));
-
+
if(participant)
{
//LL_INFOS("Voice") << "Participant Update for " << participant->mDisplayName << LL_ENDL;
@@ -4362,25 +4254,25 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
{
participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX;
}
-
- // *HACK: mantipov: added while working on EXT-3544
- /*
- Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE
- LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.
-
- participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted
- Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.
- Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.
-
- But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()
- voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager
- and event is not fired.
-
- So, we have to call LLSpeakerMgr::update() here.
+
+ // *HACK: mantipov: added while working on EXT-3544
+ /*
+ Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE
+ LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.
+
+ participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted
+ Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.
+ Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.
+
+ But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()
+ voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager
+ and event is not fired.
+
+ So, we have to call LLSpeakerMgr::update() here.
*/
LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel();
-
- // ignore session ID of local chat
+
+ // ignore session ID of local chat
if (voice_cnl && voice_cnl->getSessionID().notNull())
{
LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID());
@@ -4409,10 +4301,10 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
}
void LLVivoxVoiceClient::messageEvent(
- std::string &sessionHandle,
- std::string &uriString,
- std::string &alias,
- std::string &messageHeader,
+ std::string &sessionHandle,
+ std::string &uriString,
+ std::string &alias,
+ std::string &messageHeader,
std::string &messageBody,
std::string &applicationString)
{
@@ -4433,7 +4325,7 @@ void LLVivoxVoiceClient::messageEvent(
const std::string endSpan = "</span>";
std::string::size_type start;
std::string::size_type end;
-
+
// Default to displaying the raw string, so the message gets through.
message = messageBody;
@@ -4445,38 +4337,38 @@ void LLVivoxVoiceClient::messageEvent(
if(start != std::string::npos)
{
start += startMarker2.size();
-
+
if(end != std::string::npos)
end -= start;
-
+
message.assign(messageBody, start, end);
}
- else
+ else
{
// Didn't find a <body>, try looking for a <span> instead.
start = messageBody.find(startSpan);
start = messageBody.find(startMarker2, start);
end = messageBody.find(endSpan);
-
+
if(start != std::string::npos)
{
start += startMarker2.size();
-
+
if(end != std::string::npos)
end -= start;
-
+
message.assign(messageBody, start, end);
- }
+ }
}
- }
-
+ }
+
// LL_DEBUGS("Voice") << " raw message = \n" << message << LL_ENDL;
// strip formatting tags
{
std::string::size_type start;
std::string::size_type end;
-
+
while((start = message.find('<')) != std::string::npos)
{
if((end = message.find('>', start + 1)) != std::string::npos)
@@ -4491,7 +4383,7 @@ void LLVivoxVoiceClient::messageEvent(
}
}
}
-
+
// Decode ampersand-escaped chars
{
std::string::size_type mark = 0;
@@ -4503,14 +4395,14 @@ void LLVivoxVoiceClient::messageEvent(
message.replace(mark, 4, "<");
mark += 1;
}
-
+
mark = 0;
while((mark = message.find("&gt;", mark)) != std::string::npos)
{
message.replace(mark, 4, ">");
mark += 1;
}
-
+
mark = 0;
while((mark = message.find("&amp;", mark)) != std::string::npos)
{
@@ -4518,12 +4410,12 @@ void LLVivoxVoiceClient::messageEvent(
mark += 1;
}
}
-
+
// strip leading/trailing whitespace (since we always seem to get a couple newlines)
LLStringUtil::trim(message);
-
+
// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL;
-
+
sessionStatePtr_t session(findSession(sessionHandle));
if(session)
{
@@ -4533,7 +4425,7 @@ void LLVivoxVoiceClient::messageEvent(
LLChat chat;
chat.mMuted = is_muted && !is_linden;
-
+
if(!chat.mMuted)
{
chat.mFromID = session->mCallerID;
@@ -4544,7 +4436,7 @@ void LLVivoxVoiceClient::messageEvent(
{
// TODO: Question: Return do not disturb mode response here? Or maybe when session is started instead?
}
-
+
LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
LLIMMgr::getInstance()->addMessage(session->mIMSessionID,
session->mCallerID,
@@ -4557,14 +4449,14 @@ void LLVivoxVoiceClient::messageEvent(
LLUUID::null, // default arg
LLVector3::zero); // default arg
}
- }
+ }
}
}
void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
{
sessionStatePtr_t session(findSession(sessionHandle));
-
+
if(session)
{
participantStatePtr_t participant(session->findParticipant(uriString));
@@ -4627,11 +4519,11 @@ void LLVivoxVoiceClient::muteListChanged()
if(mAudioSession)
{
participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
-
+
for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
{
participantStatePtr_t p(iter->second);
-
+
// Check to see if this participant is on the mute list already
if(p->updateMuteState())
mAudioSession->mVolumeDirty = true;
@@ -4641,18 +4533,18 @@ void LLVivoxVoiceClient::muteListChanged()
/////////////////////////////
// Managing list of participants
-LLVivoxVoiceClient::participantState::participantState(const std::string &uri) :
- mURI(uri),
- mPTT(false),
- mIsSpeaking(false),
- mIsModeratorMuted(false),
- mLastSpokeTimestamp(0.f),
- mPower(0.f),
- mVolume(LLVoiceClient::VOLUME_DEFAULT),
+LLVivoxVoiceClient::participantState::participantState(const std::string &uri) :
+ mURI(uri),
+ mPTT(false),
+ mIsSpeaking(false),
+ mIsModeratorMuted(false),
+ mLastSpokeTimestamp(0.f),
+ mPower(0.f),
+ mVolume(LLVoiceClient::VOLUME_DEFAULT),
mUserVolume(0),
- mOnMuteList(false),
+ mOnMuteList(false),
mVolumeSet(false),
- mVolumeDirty(false),
+ mVolumeDirty(false),
mAvatarIDValid(false),
mIsSelf(false)
{
@@ -4662,7 +4554,7 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP
{
participantStatePtr_t result;
bool useAlternateURI = false;
-
+
// Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it
// matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
{
@@ -4684,14 +4576,14 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP
result = iter->second;
}
}
-
+
if(!result)
{
// participant isn't already in one list or the other.
result.reset(new participantState(useAlternateURI?mSIPURI:uri));
mParticipantsByURI.insert(participantMap::value_type(result->mURI, result));
mParticipantsChanged = true;
-
+
// Try to do a reverse transform on the URI to get the GUID back.
{
LLUUID id;
@@ -4707,12 +4599,12 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP
result->mAvatarID.generate(uri);
}
}
-
+
if(result->updateMuteState())
{
mMuteDirty = true;
}
-
+
mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result));
if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume))
@@ -4720,10 +4612,10 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addP
result->mVolumeDirty = true;
mVolumeDirty = true;
}
-
+
LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
}
-
+
return result;
}
@@ -4752,9 +4644,9 @@ void LLVivoxVoiceClient::sessionState::removeParticipant(const LLVivoxVoiceClien
{
participantMap::iterator iter = mParticipantsByURI.find(participant->mURI);
participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID);
-
+
LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
-
+
if(iter == mParticipantsByURI.end())
{
LL_WARNS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
@@ -4785,7 +4677,7 @@ void LLVivoxVoiceClient::sessionState::removeAllParticipants()
{
removeParticipant(mParticipantsByURI.begin()->second);
}
-
+
if(!mParticipantsByUUID.empty())
{
LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL;
@@ -4811,10 +4703,10 @@ void LLVivoxVoiceClient::sessionState::VerifySessions()
void LLVivoxVoiceClient::getParticipantList(std::set<LLUUID> &participants)
{
- if(mAudioSession)
+ if(mProcessChannels && mAudioSession)
{
for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin();
- iter != mAudioSession->mParticipantsByUUID.end();
+ iter != mAudioSession->mParticipantsByUUID.end();
iter++)
{
participants.insert(iter->first);
@@ -4824,18 +4716,18 @@ void LLVivoxVoiceClient::getParticipantList(std::set<LLUUID> &participants)
bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id)
{
- if(mAudioSession)
+ if(mProcessChannels && mAudioSession)
{
- return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end());
+ return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end());
}
- return false;
+ return false;
}
LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri)
{
participantStatePtr_t result;
-
+
participantMap::iterator iter = mParticipantsByURI.find(uri);
if(iter == mParticipantsByURI.end())
@@ -4852,7 +4744,7 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::find
{
result = iter->second;
}
-
+
return result;
}
@@ -4872,12 +4764,12 @@ LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::find
LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::findParticipantByID(const LLUUID& id)
{
participantStatePtr_t result;
-
+
if(mAudioSession)
{
result = mAudioSession->findParticipantByID(id);
}
-
+
return result;
}
@@ -4888,15 +4780,15 @@ bool LLVivoxVoiceClient::checkParcelChanged(bool update)
{
LLViewerRegion *region = gAgent.getRegion();
LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
-
+
if(region && parcel)
{
S32 parcelLocalID = parcel->getLocalID();
std::string regionName = region->getName();
-
+
// LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL;
-
- // The region name starts out empty and gets filled in later.
+
+ // The region name starts out empty and gets filled in later.
// Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
// If either is empty, wait for the next time around.
if(!regionName.empty())
@@ -4924,7 +4816,7 @@ bool LLVivoxVoiceClient::switchChannel(
std::string hash)
{
bool needsSwitch = !mIsInChannel;
-
+
if (mIsInChannel)
{
if (mSessionTerminateRequested)
@@ -4932,8 +4824,10 @@ bool LLVivoxVoiceClient::switchChannel(
// If a terminate has been requested, we need to compare against where the URI we're already headed to.
if(mNextAudioSession)
{
- if(mNextAudioSession->mSIPURI != uri)
+ if (mNextAudioSession->mSIPURI != uri)
+ {
needsSwitch = true;
+ }
}
else
{
@@ -4999,7 +4893,7 @@ bool LLVivoxVoiceClient::switchChannel(
mNextAudioSession->mReconnect = !no_reconnect;
mNextAudioSession->mIsP2P = is_p2p;
}
-
+
if (mIsInChannel)
{
// If we're already in a channel, or if we're joining one, terminate
@@ -5023,23 +4917,24 @@ void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session)
}
}
-void LLVivoxVoiceClient::setNonSpatialChannel(
- const std::string &uri,
- const std::string &credentials)
+void LLVivoxVoiceClient::setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave)
{
- switchChannel(uri, false, false, false, credentials);
+ switchChannel(channelInfo["channel_uri"].asString(), false, false, false, channelInfo["channel_credentials"].asString());
}
-bool LLVivoxVoiceClient::setSpatialChannel(
- const std::string &uri,
- const std::string &credentials)
+bool LLVivoxVoiceClient::setSpatialChannel(const LLSD& channelInfo)
{
- mSpatialSessionURI = uri;
- mSpatialSessionCredentials = credentials;
- mAreaVoiceDisabled = mSpatialSessionURI.empty();
+ mSpatialSessionURI = channelInfo["channel_uri"].asString();
+ mSpatialSessionCredentials = channelInfo["channel_credentials"].asString();
+ if (!mProcessChannels)
+ {
+ // we're not even processing channels (another provider is) so
+ // save the credentials aside and exit
+ return false;
+ }
+
+ LL_DEBUGS("Voice") << "got spatial channel uri: \"" << mSpatialSessionURI << "\"" << LL_ENDL;
- LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
-
if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
{
// User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
@@ -5059,85 +4954,39 @@ void LLVivoxVoiceClient::callUser(const LLUUID &uuid)
switchChannel(userURI, false, true, true);
}
-#if 0
-// Vivox text IMs are not in use.
-LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid)
-{
- // Figure out if a session with the user already exists
- sessionStatePtr_t session(findSession(uuid));
- if(!session)
- {
- // No session with user, need to start one.
- std::string uri = sipURIFromID(uuid);
- session = addSession(uri);
+void LLVivoxVoiceClient::hangup() { leaveChannel(); }
- llassert(session);
- if (!session)
- return session;
- session->mIsSpatial = false;
- session->mReconnect = false;
- session->mIsP2P = true;
- session->mCallerID = uuid;
- }
-
- if(session->mHandle.empty())
- {
- // Session isn't active -- start it up.
- sessionCreateSendMessage(session, false, false);
- }
- else
- {
- // Session is already active -- start up text.
- sessionTextConnectSendMessage(session);
- }
-
- return session;
-}
-#endif
-
-void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid)
+LLVoiceP2PIncomingCallInterfacePtr LLVivoxVoiceClient::getIncomingCallInterface(const LLSD &voice_call_info)
{
-#if 0
- // Vivox text IMs are not in use.
-
- // Figure out if a session with the user exists
- sessionStatePtr_t session(findSession(uuid));
- if(session)
- {
- // found the session
- if(!session->mHandle.empty())
- {
- // sessionTextDisconnectSendMessage(session); // a SLim leftover, not used any more.
- }
- }
- else
- {
- LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
- }
-#endif
+ return boost::make_shared<LLVivoxVoiceP2PIncomingCall>(voice_call_info);
}
-bool LLVivoxVoiceClient::isValidChannel(std::string &sessionHandle)
+
+bool LLVivoxVoiceClient::answerInvite(const std::string &sessionHandle)
{
- return(findSession(sessionHandle) != NULL);
-
+ // this is only ever used to answer incoming p2p call invites.
+
+ sessionStatePtr_t session(findSession(sessionHandle));
+ if (session)
+ {
+ session->mIsSpatial = false;
+ session->mReconnect = false;
+ session->mIsP2P = true;
+
+ joinSession(session);
+ return true;
+ }
+
+ return false;
}
-bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle)
+
+void LLVivoxVoiceClient::declineInvite(const std::string &sessionHandle)
{
- // this is only ever used to answer incoming p2p call invites.
-
sessionStatePtr_t session(findSession(sessionHandle));
- if(session)
- {
- session->mIsSpatial = false;
- session->mReconnect = false;
- session->mIsP2P = true;
-
- joinSession(session);
- return true;
- }
-
- return false;
+ if (session)
+ {
+ sessionMediaDisconnectSendMessage(session);
+ }
}
bool LLVivoxVoiceClient::isVoiceWorking() const
@@ -5154,9 +5003,9 @@ bool LLVivoxVoiceClient::isVoiceWorking() const
// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls.
BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id)
{
- BOOL result = TRUE;
+ BOOL result = TRUE;
sessionStatePtr_t session(findSession(id));
-
+
if(session)
{
// this is a p2p session with the indicated caller, or the session with the specified UUID.
@@ -5175,22 +5024,22 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id)
}
}
}
-
+
return result;
}
// Returns true if calling back the session URI after the session has closed is possible.
-// Currently this will be false only for PSTN P2P calls.
+// Currently this will be false only for PSTN P2P calls.
BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
{
- BOOL result = TRUE;
+ BOOL result = TRUE;
sessionStatePtr_t session(findSession(session_id));
-
+
if(session != NULL)
{
result = session->isCallBackPossible();
}
-
+
return result;
}
@@ -5200,62 +5049,69 @@ BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)
{
bool result = TRUE;
sessionStatePtr_t session(findSession(session_id));
-
+
if(session != NULL)
{
result = session->isTextIMPossible();
}
-
- return result;
-}
-
-void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle)
-{
- sessionStatePtr_t session(findSession(sessionHandle));
- if(session)
- {
- sessionMediaDisconnectSendMessage(session);
- }
+ return result;
}
void LLVivoxVoiceClient::leaveNonSpatialChannel()
{
LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL;
-
- // Make sure we don't rejoin the current session.
+
+ // Make sure we don't rejoin the current session.
sessionStatePtr_t oldNextSession(mNextAudioSession);
mNextAudioSession.reset();
-
+
// Most likely this will still be the current session at this point, but check it anyway.
reapSession(oldNextSession);
-
+
verifySessionState();
-
+
sessionTerminate();
}
-std::string LLVivoxVoiceClient::getCurrentChannel()
+void LLVivoxVoiceClient::processChannels(bool process)
{
- std::string result;
-
- if (mIsInChannel && !mSessionTerminateRequested)
- {
- result = getAudioSessionURI();
- }
-
- return result;
+ mProcessChannels = process;
+}
+
+bool LLVivoxVoiceClient::isCurrentChannel(const LLSD &channelInfo)
+{
+ if (!mProcessChannels || (channelInfo["voice_server_type"].asString() != VIVOX_VOICE_SERVER_TYPE))
+ {
+ return false;
+ }
+ if (mAudioSession)
+ {
+ if (!channelInfo["sessionHandle"].asString().empty())
+ {
+ return mAudioSession->mHandle == channelInfo["session_handle"].asString();
+ }
+ return channelInfo["channel_uri"].asString() == mAudioSession->mSIPURI;
+ }
+ return false;
+}
+
+bool LLVivoxVoiceClient::compareChannels(const LLSD& channelInfo1, const LLSD& channelInfo2)
+{
+ return (channelInfo1["voice_server_type"] == VIVOX_VOICE_SERVER_TYPE) &&
+ (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) &&
+ (channelInfo1["channel_uri"] == channelInfo2["channel_uri"]);
}
bool LLVivoxVoiceClient::inProximalChannel()
{
bool result = false;
-
+
if (mIsInChannel && !mSessionTerminateRequested)
{
result = inSpatialChannel();
}
-
+
return result;
}
@@ -5266,7 +5122,7 @@ std::string LLVivoxVoiceClient::sipURIFromID(const LLUUID &id)
result += nameFromID(id);
result += "@";
result += mVoiceSIPURIHostName;
-
+
return result;
}
@@ -5280,24 +5136,14 @@ std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
result += "@";
result += mVoiceSIPURIHostName;
}
-
- return result;
-}
-std::string LLVivoxVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
-{
- std::string result;
- if(avatar)
- {
- result = nameFromID(avatar->getID());
- }
return result;
}
std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid)
{
std::string result;
-
+
if (uuid.isNull()) {
//VIVOX, the uuid emtpy look for the mURIString and return that instead.
//result.assign(uuid.mURIStringName);
@@ -5306,31 +5152,31 @@ std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid)
}
// Prepending this apparently prevents conflicts with reserved names inside the vivox code.
result = "x";
-
- // Base64 encode and replace the pieces of base64 that are less compatible
+
+ // Base64 encode and replace the pieces of base64 that are less compatible
// with e-mail local-parts.
// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
result += LLBase64::encode(uuid.mData, UUID_BYTES);
LLStringUtil::replaceChar(result, '+', '-');
LLStringUtil::replaceChar(result, '/', '_');
-
+
// If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
-
+
// The reverse transform can be done with:
// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
-
+
return result;
}
bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
{
bool result = false;
-
+
// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
// If it is, convert to a bare name before doing the transform.
std::string name = nameFromsipURI(inName);
-
+
// Doesn't look like a SIP URI, assume it's an actual name.
if(name.empty())
name = inName;
@@ -5338,7 +5184,7 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
// This will only work if the name is of the proper form.
// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
// "xFnPP04IpREWNkuw1cOXlhw=="
-
+
if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
{
// The name appears to have the right form.
@@ -5348,7 +5194,7 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
LLStringUtil::replaceChar(temp, '-', '+');
LLStringUtil::replaceChar(temp, '_', '/');
- U8 rawuuid[UUID_BYTES + 1];
+ U8 rawuuid[UUID_BYTES + 1];
int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
if(len == UUID_BYTES)
{
@@ -5356,21 +5202,16 @@ bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
memcpy(uuid.mData, rawuuid, UUID_BYTES);
result = true;
}
- }
-
+ }
+
if(!result)
{
// VIVOX: not a standard account name, just copy the URI name mURIString field
// and hope for the best. bpj
uuid.setNull(); // VIVOX, set the uuid field to nulls
}
-
- return result;
-}
-std::string LLVivoxVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
-{
- return avatar->getFullname();
+ return result;
}
std::string LLVivoxVoiceClient::sipURIFromName(std::string &name)
@@ -5397,39 +5238,42 @@ std::string LLVivoxVoiceClient::nameFromsipURI(const std::string &uri)
{
result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
}
-
+
return result;
}
bool LLVivoxVoiceClient::inSpatialChannel(void)
{
bool result = false;
-
+
if(mAudioSession)
{
result = mAudioSession->mIsSpatial;
}
-
+
return result;
}
-std::string LLVivoxVoiceClient::getAudioSessionURI()
+
+LLSD LLVivoxVoiceClient::getAudioSessionChannelInfo()
{
- std::string result;
-
- if(mAudioSession)
- result = mAudioSession->mSIPURI;
-
+ LLSD result;
+
+ if (mAudioSession)
+ {
+ result = mAudioSession->getVoiceChannelInfo();
+ }
+
return result;
}
std::string LLVivoxVoiceClient::getAudioSessionHandle()
{
std::string result;
-
+
if(mAudioSession)
result = mAudioSession->mHandle;
-
+
return result;
}
@@ -5448,11 +5292,11 @@ void LLVivoxVoiceClient::enforceTether(void)
F32 camera_distance = (F32)camera_offset.magVec();
if(camera_distance > max_dist)
{
- tethered = mAvatarPosition +
+ tethered = mAvatarPosition +
(max_dist / camera_distance) * camera_offset;
}
}
-
+
if(dist_vec_squared(mCameraPosition, tethered) > 0.01)
{
mCameraPosition = tethered;
@@ -5469,19 +5313,19 @@ void LLVivoxVoiceClient::updatePosition(void)
LLMatrix3 rot;
LLVector3d pos;
LLQuaternion qrot;
-
+
// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
// They're currently always set to zero.
-
+
// Send the current camera position to the voice code
- rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis());
+ rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis());
pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin());
-
+
LLVivoxVoiceClient::getInstance()->setCameraPosition(
pos, // position
LLVector3::zero, // velocity
rot); // rotation matrix
-
+
// Send the current avatar position to the voice code
qrot = gAgentAvatarp->getRootJoint()->getWorldRotation();
pos = gAgentAvatarp->getPositionGlobal();
@@ -5489,7 +5333,7 @@ void LLVivoxVoiceClient::updatePosition(void)
// TODO: Can we get the head offset from outside the LLVOAvatar?
// pos += LLVector3d(mHeadOffset);
pos += LLVector3d(0.f, 0.f, 1.f);
-
+
LLVivoxVoiceClient::getInstance()->setAvatarPosition(
pos, // position
LLVector3::zero, // velocity
@@ -5500,13 +5344,13 @@ void LLVivoxVoiceClient::updatePosition(void)
void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
{
mCameraRequestedPosition = position;
-
+
if(mCameraVelocity != velocity)
{
mCameraVelocity = velocity;
mSpatialCoordsDirty = true;
}
-
+
if(mCameraRot != rot)
{
mCameraRot = rot;
@@ -5521,19 +5365,19 @@ void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLV
mAvatarPosition = position;
mSpatialCoordsDirty = true;
}
-
+
if(mAvatarVelocity != velocity)
{
mAvatarVelocity = velocity;
mSpatialCoordsDirty = true;
}
-
+
// If the two rotations are not exactly equal test their dot product
// to get the cos of the angle between them.
// If it is too small, don't update.
F32 rot_cos_diff = llabs(dot(mAvatarRot, rot));
if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS))
- {
+ {
mAvatarRot = rot;
mSpatialCoordsDirty = true;
}
@@ -5542,15 +5386,15 @@ void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLV
bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
{
bool result = false;
-
+
if(region)
{
name = region->getName();
}
-
+
if(!name.empty())
result = true;
-
+
return result;
}
@@ -5580,14 +5424,14 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled)
<< " was "<< (mVoiceEnabled ? "enabled" : "disabled")
<< " coro "<< (mIsCoroutineActive ? "active" : "inactive")
<< LL_ENDL;
-
+
if (enabled != mVoiceEnabled)
{
// TODO: Refactor this so we don't call into LLVoiceChannel, but simply
// use the status observer
mVoiceEnabled = enabled;
LLVoiceClientStatusObserver::EStatusType status;
-
+
if (enabled)
{
LL_DEBUGS("Voice") << "enabling" << LL_ENDL;
@@ -5620,38 +5464,13 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled)
}
}
-bool LLVivoxVoiceClient::voiceEnabled()
-{
- return gSavedSettings.getBOOL("EnableVoiceChat") &&
- !gSavedSettings.getBOOL("CmdLineDisableVoice") &&
- !gNonInteractive;
-}
-
-void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled)
-{
- mLipSyncEnabled = enabled;
-}
-
-BOOL LLVivoxVoiceClient::lipSyncEnabled()
-{
-
- if ( mVoiceEnabled )
- {
- return mLipSyncEnabled;
- }
- else
- {
- return FALSE;
- }
-}
-
void LLVivoxVoiceClient::setEarLocation(S32 loc)
{
if(mEarLocation != loc)
{
LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL;
-
+
mEarLocation = loc;
mSpatialCoordsDirty = true;
}
@@ -5659,7 +5478,7 @@ void LLVivoxVoiceClient::setEarLocation(S32 loc)
void LLVivoxVoiceClient::setVoiceVolume(F32 volume)
{
- int scaled_volume = scale_speaker_volume(volume);
+ int scaled_volume = scale_speaker_volume(volume);
if(scaled_volume != mSpeakerVolume)
{
@@ -5677,7 +5496,7 @@ void LLVivoxVoiceClient::setVoiceVolume(F32 volume)
void LLVivoxVoiceClient::setMicGain(F32 volume)
{
int scaled_volume = scale_mic_volume(volume);
-
+
if(scaled_volume != mMicVolume)
{
mMicVolume = scaled_volume;
@@ -5687,29 +5506,19 @@ void LLVivoxVoiceClient::setMicGain(F32 volume)
/////////////////////////////
// Accessors for data related to nearby speakers
-BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id)
-{
- BOOL result = FALSE;
- participantStatePtr_t participant(findParticipantByID(id));
- if(participant)
- {
- // I'm not sure what the semantics of this should be.
- // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
- result = TRUE;
- }
-
- return result;
-}
std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id)
{
std::string result;
- participantStatePtr_t participant(findParticipantByID(id));
- if(participant)
- {
- result = participant->mDisplayName;
- }
-
+ if (mProcessChannels)
+ {
+ participantStatePtr_t participant(findParticipantByID(id));
+ if (participant)
+ {
+ result = participant->mDisplayName;
+ }
+ }
+
return result;
}
@@ -5718,42 +5527,47 @@ std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id)
BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id)
{
BOOL result = FALSE;
+ if (mProcessChannels)
+ {
+ participantStatePtr_t participant(findParticipantByID(id));
+ if (participant)
+ {
+ if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
+ {
+ participant->mIsSpeaking = FALSE;
+ }
+ result = participant->mIsSpeaking;
+ }
+ }
- participantStatePtr_t participant(findParticipantByID(id));
- if(participant)
- {
- if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
- {
- participant->mIsSpeaking = FALSE;
- }
- result = participant->mIsSpeaking;
- }
-
return result;
}
BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id)
{
BOOL result = FALSE;
-
+ if (!mProcessChannels)
+ {
+ return FALSE;
+ }
participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mIsModeratorMuted;
}
-
+
return result;
}
F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id)
-{
+{
F32 result = 0;
participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
result = participant->mPower;
}
-
+
return result;
}
@@ -5770,19 +5584,6 @@ BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id)
// Does "using PTT" mean they're configured with a push-to-talk button?
// For now, we know there's no PTT mechanism in place, so nobody is using it.
}
-
- return result;
-}
-
-BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id)
-{
- BOOL result = FALSE;
-
- participantStatePtr_t participant(findParticipantByID(id));
- if(participant)
- {
- result = participant->mOnMuteList;
- }
return result;
}
@@ -5792,7 +5593,7 @@ F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id)
{
// Minimum volume will be returned for users with voice disabled
F32 result = LLVoiceClient::VOLUME_MIN;
-
+
participantStatePtr_t participant(findParticipantByID(id));
if(participant)
{
@@ -5840,26 +5641,21 @@ std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id)
{
result = participant->mGroupID;
}
-
- return result;
-}
-BOOL LLVivoxVoiceClient::getAreaVoiceDisabled()
-{
- return mAreaVoiceDisabled;
+ return result;
}
void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
{
// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
-
+
if(!mMainSessionGroupHandle.empty())
{
std::ostringstream stream;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
- << "<RecordingControlType>Start</RecordingControlType>"
+ << "<RecordingControlType>Start</RecordingControlType>"
<< "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
<< "<Filename>" << "" << "</Filename>"
<< "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
@@ -5881,7 +5677,7 @@ void LLVivoxVoiceClient::recordingLoopSave(const std::string& filename)
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
- << "<RecordingControlType>Flush</RecordingControlType>"
+ << "<RecordingControlType>Flush</RecordingControlType>"
<< "<Filename>" << filename << "</Filename>"
<< "</Request>\n\n\n";
@@ -5899,7 +5695,7 @@ void LLVivoxVoiceClient::recordingStop()
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
- << "<RecordingControlType>Stop</RecordingControlType>"
+ << "<RecordingControlType>Stop</RecordingControlType>"
<< "</Request>\n\n\n";
writeString(stream.str());
@@ -5916,7 +5712,7 @@ void LLVivoxVoiceClient::filePlaybackStart(const std::string& filename)
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
- << "<RecordingControlType>Start</RecordingControlType>"
+ << "<RecordingControlType>Start</RecordingControlType>"
<< "<Filename>" << filename << "</Filename>"
<< "</Request>\n\n\n";
@@ -5934,7 +5730,7 @@ void LLVivoxVoiceClient::filePlaybackStop()
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
- << "<RecordingControlType>Stop</RecordingControlType>"
+ << "<RecordingControlType>Stop</RecordingControlType>"
<< "</Request>\n\n\n";
writeString(stream.str());
@@ -5975,6 +5771,18 @@ LLVivoxVoiceClient::sessionState::sessionState() :
{
}
+LLSD LLVivoxVoiceClient::sessionState::getVoiceChannelInfo()
+{
+ LLSD result;
+
+ result["voice_server_type"] = VIVOX_VOICE_SERVER_TYPE;
+ result["channel_credentials"] = mHash;
+ result["channel_uri"] = mSIPURI;
+ result["session_handle"] = mHandle;
+
+ return result;
+}
+
/*static*/
LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::createSession()
{
@@ -6012,7 +5820,7 @@ bool LLVivoxVoiceClient::sessionState::isTextIMPossible()
}
-/*static*/
+/*static*/
LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByHandle(const std::string &handle)
{
sessionStatePtr_t result;
@@ -6026,7 +5834,7 @@ LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchS
return result;
}
-/*static*/
+/*static*/
LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri)
{
sessionStatePtr_t result;
@@ -6073,7 +5881,7 @@ void LLVivoxVoiceClient::sessionState::for_each(sessionFunc_t func)
std::for_each(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func));
}
-// simple test predicates.
+// simple test predicates.
// *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features.
bool LLVivoxVoiceClient::sessionState::testByHandle(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string handle)
{
@@ -6127,28 +5935,28 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const std:
{
result = iter->second;
}
-
+
return result;
}
LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
-{
+{
sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri);
-
+
return result;
}
LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const LLUUID &participant_id)
{
sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id);
-
+
return result;
}
LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle)
{
sessionStatePtr_t result;
-
+
if(handle.empty())
{
// No handle supplied.
@@ -6159,7 +5967,7 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::
{
// Check for an existing session with this handle
sessionMap::iterator iter = mSessionsByHandle.find(handle);
-
+
if(iter != mSessionsByHandle.end())
{
result = iter->second;
@@ -6169,7 +5977,7 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::
if(!result)
{
// No existing session found.
-
+
LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL;
result = sessionState::createSession();
result->mSIPURI = uri;
@@ -6182,8 +5990,8 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::
if(!result->mHandle.empty())
{
- // *TODO: Rider: This concerns me. There is a path (via switchChannel) where
- // we do not track the session. In theory this means that we could end up with
+ // *TODO: Rider: This concerns me. There is a path (via switchChannel) where
+ // we do not track the session. In theory this means that we could end up with
// a mAuidoSession that does not match the session tracked in mSessionsByHandle
mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result));
}
@@ -6191,7 +5999,7 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::
else
{
// Found an existing session
-
+
if(uri != result->mSIPURI)
{
// TODO: Should this be an internal error?
@@ -6213,12 +6021,12 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::
setSessionHandle(result, handle);
}
}
-
+
LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
}
verifySessionState();
-
+
return result;
}
@@ -6249,7 +6057,7 @@ void LLVivoxVoiceClient::clearSessionHandle(const sessionStatePtr_t &session)
void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle)
{
// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
-
+
if(!session->mHandle.empty())
{
// Remove session from the map if it should have been there.
@@ -6268,7 +6076,7 @@ void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, cons
LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL;
}
}
-
+
session->mHandle = handle;
if(!handle.empty())
@@ -6330,7 +6138,7 @@ void LLVivoxVoiceClient::deleteAllSessions()
const sessionStatePtr_t session = mSessionsByHandle.begin()->second;
deleteSession(session);
}
-
+
}
void LLVivoxVoiceClient::verifySessionState(void)
@@ -6418,19 +6226,25 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta
}
}
}
-
- LL_DEBUGS("Voice")
- << " " << LLVoiceClientStatusObserver::status2string(status)
- << ", session URI " << getAudioSessionURI()
+
+ LL_DEBUGS("Voice")
+ << " " << LLVoiceClientStatusObserver::status2string(status)
+ << ", session channelInfo " << getAudioSessionChannelInfo()
<< ", proximal is " << inSpatialChannel()
<< LL_ENDL;
+ if (!mProcessChannels)
+ {
+ // we're not processing...another voice module is.
+ // so nobody wants to hear from us.
+ return;
+ }
for (status_observer_set_t::iterator it = mStatusObservers.begin();
it != mStatusObservers.end();
)
{
LLVoiceClientStatusObserver* observer = *it;
- observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
+ observer->onChange(status, getAudioSessionChannelInfo(), inSpatialChannel());
// In case onError() deleted an entry.
it = mStatusObservers.upper_bound(observer);
}
@@ -6442,6 +6256,7 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta
{
bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
+ LL_WARNS("Voice") << "Setting voice connected " << (voice_status ? "True" : "False") << LL_ENDL;
gAgent.setVoiceConnected(voice_status);
if (voice_status)
@@ -6511,12 +6326,11 @@ void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sess
{
session->mTextInvitePending = false;
- // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel.
+ // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel.
}
if (session->mVoiceInvitePending)
{
session->mVoiceInvitePending = false;
-
LLIMMgr::getInstance()->inviteToSession(
session->mIMSessionID,
session->mName,
@@ -6524,10 +6338,8 @@ void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sess
session->mName,
IM_SESSION_P2P_INVITE,
LLIMMgr::INVITATION_TYPE_VOICE,
- session->mHandle,
- session->mSIPURI);
+ session->getVoiceChannelInfo());
}
-
}
}
@@ -7068,7 +6880,7 @@ void LLVivoxVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name
}
}
-// it updates VoiceMorphing menu items in accordance with purchased properties
+// it updates VoiceMorphing menu items in accordance with purchased properties
void LLVivoxVoiceClient::updateVoiceMorphingMenu()
{
if (mVoiceFontListDirty)
@@ -7309,7 +7121,7 @@ void LLVivoxVoiceClient::captureBufferPlayStopSendMessage()
LLVivoxProtocolParser::LLVivoxProtocolParser()
{
parser = XML_ParserCreate(NULL);
-
+
reset();
}
@@ -7342,7 +7154,7 @@ void LLVivoxProtocolParser::reset()
applicationString.clear();
}
-//virtual
+//virtual
LLVivoxProtocolParser::~LLVivoxProtocolParser()
{
if (parser)
@@ -7368,38 +7180,38 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
istr.read(buf, sizeof(buf));
mInput.append(buf, istr.gcount());
}
-
+
// Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser.
int start = 0;
int delim;
while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
- {
-
+ {
+
// Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
reset();
-
+
XML_ParserReset(parser, NULL);
XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
XML_SetCharacterDataHandler(parser, ExpatCharHandler);
- XML_SetUserData(parser, this);
+ XML_SetUserData(parser, this);
XML_Parse(parser, mInput.data() + start, delim - start, false);
-
+
LL_DEBUGS("VivoxProtocolParser") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
start = delim + 3;
}
-
+
if(start != 0)
mInput = mInput.substr(start);
-
+
LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
-
+
if(!LLVivoxVoiceClient::sConnected)
{
// If voice has been disabled, we just want to close the socket. This does so.
LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL;
return STATUS_STOP;
}
-
+
return STATUS_OK;
}
@@ -7443,11 +7255,11 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
textBuffer.clear();
// only accumulate text if we're not ignoring tags.
accumulateText = !ignoringTags;
-
+
if (responseDepth == 0)
- {
+ {
isEvent = !stricmp("Event", tag);
-
+
if (!stricmp("Response", tag) || isEvent)
{
// Grab the attributes
@@ -7455,7 +7267,7 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
{
const char *key = *attr++;
const char *value = *attr++;
-
+
if (!stricmp("requestId", key))
{
requestId = value;
@@ -7481,20 +7293,20 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
else
{
LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL;
-
+
// Ignore the InputXml stuff so we don't get confused
if (!stricmp("InputXml", tag))
{
ignoringTags = true;
ignoreDepth = responseDepth;
accumulateText = false;
-
+
LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
}
else if (!stricmp("CaptureDevices", tag))
{
LLVivoxVoiceClient::getInstance()->clearCaptureDevices();
- }
+ }
else if (!stricmp("RenderDevices", tag))
{
LLVivoxVoiceClient::getInstance()->clearRenderDevices();
@@ -7506,7 +7318,7 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
else if (!stricmp("RenderDevice", tag))
{
deviceString.clear();
- }
+ }
else if (!stricmp("SessionFont", tag))
{
id = 0;
@@ -7541,9 +7353,9 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
void LLVivoxProtocolParser::EndTag(const char *tag)
{
const std::string& string = textBuffer;
-
+
responseDepth--;
-
+
if (ignoringTags)
{
if (ignoreDepth == responseDepth)
@@ -7556,11 +7368,11 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
}
}
-
+
if (!ignoringTags)
{
LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
-
+
// Closing a tag. Finalize the text we've accumulated and reset
if (!stricmp("ReturnCode", tag))
returnCode = strtol(string.c_str(), NULL, 10);
@@ -7615,7 +7427,7 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
else if (!stricmp("DisplayName", tag))
displayNameString = string;
else if (!stricmp("Device", tag))
- deviceString = string;
+ deviceString = string;
else if (!stricmp("AccountName", tag))
nameString = string;
else if (!stricmp("ParticipantType", tag))
@@ -7713,7 +7525,7 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
textBuffer.clear();
accumulateText= false;
-
+
if (responseDepth == 0)
{
// We finished all of the XML, process the data
@@ -7730,7 +7542,7 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length)
This method is called for anything that isn't a tag, which can be text you
want that lies between tags, and a lot of stuff you don't want like file formatting
(tabs, spaces, CR/LF, etc).
-
+
Only copy text if we are in accumulate mode...
*/
if (accumulateText)
@@ -7757,14 +7569,14 @@ LLDate LLVivoxProtocolParser::expiryTimeStampToLLDate(const std::string& vivox_t
void LLVivoxProtocolParser::processResponse(std::string tag)
{
LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
-
+
// SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs.
// According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
// so I believe this will give correct behavior.
-
+
if(returnCode == 0)
statusCode = 0;
-
+
if (isEvent)
{
const char *eventTypeCstr = eventTypeString.c_str();
@@ -7832,7 +7644,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
}
else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
{
- /*
+ /*
<Event type="ParticipantAddedEvent">
<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
@@ -7864,12 +7676,12 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
// These are really spammy in tuning mode
LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy);
}
- else if (!stricmp(eventTypeCstr, "MessageEvent"))
+ else if (!stricmp(eventTypeCstr, "MessageEvent"))
{
//TODO: This probably is not received any more, it was used to support SLim clients
LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
}
- else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))
+ else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))
{
//TODO: This probably is not received any more, it was used to support SLim clients
LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType);
@@ -7939,23 +7751,23 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
}
else if (!stricmp(actionCstr, "Session.Create.1"))
{
- LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);
+ LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);
}
else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
{
- LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);
+ LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);
}
else if (!stricmp(actionCstr, "Session.Connect.1"))
{
- LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString);
+ LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString);
}
else if (!stricmp(actionCstr, "Account.Logout.1"))
{
- LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString);
+ LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString);
}
else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
{
- LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);
+ LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);
}
else if (!stricmp(actionCstr, "Account.GetSessionFonts.1"))
{
@@ -7984,75 +7796,75 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
}
else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
{
-
+
}
else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
{
-
+
}
*/
}
@@ -8070,7 +7882,7 @@ LLVivoxSecurity::LLVivoxSecurity()
random_value[b] = ll_rand() & 0xff;
}
mConnectorHandle = LLBase64::encode(random_value, VIVOX_TOKEN_BYTES);
-
+
for (int b = 0; b < VIVOX_TOKEN_BYTES; b++)
{
random_value[b] = ll_rand() & 0xff;
@@ -8081,3 +7893,7 @@ LLVivoxSecurity::LLVivoxSecurity()
LLVivoxSecurity::~LLVivoxSecurity()
{
}
+
+bool LLVivoxVoiceP2PIncomingCall::answerInvite() { return LLVivoxVoiceClient::getInstance()->answerInvite(mCallInfo["session_handle"]); }
+
+void LLVivoxVoiceP2PIncomingCall::declineInvite() { LLVivoxVoiceClient::getInstance()->declineInvite(mCallInfo["session_handle"]); }
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index ae2aec0e9c..6b40ad0cf6 100644
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llvoicevivox.h
* @brief Declaration of LLDiamondwareVoiceClient class which is the interface to the voice client process.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -51,12 +51,27 @@ class LLVivoxProtocolParser;
class LLAvatarName;
class LLVivoxVoiceClientMuteListObserver;
+extern const std::string VIVOX_VOICE_SERVER_TYPE;
+
+class LLVivoxVoiceP2PIncomingCall : public LLVoiceP2PIncomingCallInterface
+{
+ public:
+ LLVivoxVoiceP2PIncomingCall(const LLSD& call_info) : mCallInfo(call_info) {}
+ ~LLVivoxVoiceP2PIncomingCall() override {}
+
+ bool answerInvite() override;
+ void declineInvite() override;
+
+ protected:
+ LLSD mCallInfo;
+};
class LLVivoxVoiceClient : public LLSingleton<LLVivoxVoiceClient>,
virtual public LLVoiceModuleInterface,
- virtual public LLVoiceEffectInterface
+ virtual public LLVoiceEffectInterface,
+ virtual public LLVoiceP2POutgoingCallInterface
{
- LLSINGLETON(LLVivoxVoiceClient);
+ LLSINGLETON_C11(LLVivoxVoiceClient);
LOG_CLASS(LLVivoxVoiceClient);
virtual ~LLVivoxVoiceClient();
@@ -64,149 +79,149 @@ public:
/// @name LLVoiceModuleInterface virtual implementations
/// @see LLVoiceModuleInterface
//@{
- virtual void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector)
- virtual void terminate() override; // Call this to clean up during shutdown
-
- virtual const LLVoiceVersionInfo& getVersion() override;
-
- virtual void updateSettings() override; // call after loading settings and whenever they change
+ void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector)
+ void terminate() override; // Call this to clean up during shutdown
+
+ const LLVoiceVersionInfo& getVersion() override;
+
+ void updateSettings() override; // call after loading settings and whenever they change
- // Returns true if vivox has successfully logged in and is not in error state
- virtual bool isVoiceWorking() const override;
+ // Returns true if vivox has successfully logged in and is not in error state
+ bool isVoiceWorking() const override;
/////////////////////
/// @name Tuning
//@{
- virtual void tuningStart() override;
- virtual void tuningStop() override;
- virtual bool inTuningMode() override;
-
- virtual void tuningSetMicVolume(float volume) override;
- virtual void tuningSetSpeakerVolume(float volume) override;
- virtual float tuningGetEnergy(void) override;
+ void tuningStart() override;
+ void tuningStop() override;
+ bool inTuningMode() override;
+
+ void tuningSetMicVolume(float volume) override;
+ void tuningSetSpeakerVolume(float volume) override;
+ float tuningGetEnergy(void) override;
+
//@}
-
+
/////////////////////
/// @name Devices
//@{
// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
// i.e. when the daemon is running and connected, and the device lists are populated.
- virtual bool deviceSettingsAvailable() override;
- virtual bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel.
-
+ bool deviceSettingsAvailable() override;
+ bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel.
+
// Requery the vivox daemon for the current list of input/output devices.
// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
// (use this if you want to know when it's done).
// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
- virtual void refreshDeviceLists(bool clearCurrentList = true) override;
-
- virtual void setCaptureDevice(const std::string& name) override;
- virtual void setRenderDevice(const std::string& name) override;
-
- virtual LLVoiceDeviceList& getCaptureDevices() override;
- virtual LLVoiceDeviceList& getRenderDevices() override;
- //@}
-
- virtual void getParticipantList(std::set<LLUUID> &participants) override;
- virtual bool isParticipant(const LLUUID& speaker_id) override;
+ void refreshDeviceLists(bool clearCurrentList = true) override;
+
+ void setCaptureDevice(const std::string& name) override;
+ void setRenderDevice(const std::string& name) override;
+
+ LLVoiceDeviceList& getCaptureDevices() override;
+ LLVoiceDeviceList& getRenderDevices() override;
+ //@}
+
+ void getParticipantList(std::set<LLUUID> &participants) override;
+ bool isParticipant(const LLUUID& speaker_id) override;
// Send a text message to the specified user, initiating the session if necessary.
// virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;};
-
- // close any existing text IM session with the specified user
- virtual void endUserIMSession(const LLUUID &uuid) override;
// Returns true if calling back the session URI after the session has closed is possible.
- // Currently this will be false only for PSTN P2P calls.
- // NOTE: this will return true if the session can't be found.
- virtual BOOL isSessionCallBackPossible(const LLUUID &session_id) override;
-
+ // Currently this will be false only for PSTN P2P calls.
+ // NOTE: this will return true if the session can't be found.
+ BOOL isSessionCallBackPossible(const LLUUID &session_id) override;
+
// Returns true if the session can accepte text IM's.
// Currently this will be false only for PSTN P2P calls.
- // NOTE: this will return true if the session can't be found.
- virtual BOOL isSessionTextIMPossible(const LLUUID &session_id) override;
-
-
+ // NOTE: this will return true if the session can't be found.
+ BOOL isSessionTextIMPossible(const LLUUID &session_id) override;
+
////////////////////////////
/// @name Channel stuff
//@{
// returns true iff the user is currently in a proximal (local spatial) channel.
// Note that gestures should only fire if this returns true.
- virtual bool inProximalChannel() override;
-
- virtual void setNonSpatialChannel(const std::string &uri,
- const std::string &credentials) override;
-
- virtual bool setSpatialChannel(const std::string &uri,
- const std::string &credentials) override;
-
- virtual void leaveNonSpatialChannel() override;
-
- virtual void leaveChannel(void) override;
-
- // Returns the URI of the current channel, or an empty string if not currently in a channel.
- // NOTE that it will return an empty string if it's in the process of joining a channel.
- virtual std::string getCurrentChannel() override;
+ bool inProximalChannel() override;
+
+ void setNonSpatialChannel(const LLSD& channelInfo,
+ bool notify_on_first_join,
+ bool hangup_on_last_leave) override;
+
+ bool setSpatialChannel(const LLSD& channelInfo) override;
+
+ void leaveNonSpatialChannel() override;
+
+ void processChannels(bool process) override;
+
+ void leaveChannel(void);
+
+ bool isCurrentChannel(const LLSD &channelInfo) override;
+ bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override;
+
//@}
-
-
+
+
//////////////////////////
- /// @name invitations
+ /// @name LLVoiceP2POutgoingCallInterface
//@{
// start a voice channel with the specified user
- virtual void callUser(const LLUUID &uuid) override;
- virtual bool isValidChannel(std::string &channelHandle) override;
- virtual bool answerInvite(std::string &channelHandle) override;
- virtual void declineInvite(std::string &channelHandle) override;
+ void callUser(const LLUUID &uuid) override;
+ void hangup() override;
+
//@}
-
+
+ LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return this; }
+
+ LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override;
+
+ bool answerInvite(const std::string &sessionHandle);
+ void declineInvite(const std::string &sessionHandle);
+
/////////////////////////
/// @name Volume/gain
//@{
- virtual void setVoiceVolume(F32 volume) override;
- virtual void setMicGain(F32 volume) override;
+ void setVoiceVolume(F32 volume) override;
+ void setMicGain(F32 volume) override;
//@}
-
+
/////////////////////////
/// @name enable disable voice and features
//@{
- virtual bool voiceEnabled() override;
- virtual void setVoiceEnabled(bool enabled) override;
- virtual BOOL lipSyncEnabled() override;
- virtual void setLipSyncEnabled(BOOL enabled) override;
- virtual void setMuteMic(bool muted) override; // Set the mute state of the local mic.
+ void setVoiceEnabled(bool enabled) override;
+ void setMuteMic(bool muted) override; // Set the mute state of the local mic.
//@}
-
+
//////////////////////////
/// @name nearby speaker accessors
//@{
- virtual BOOL getVoiceEnabled(const LLUUID& id) override; // true if we've received data for this avatar
- virtual std::string getDisplayName(const LLUUID& id) override;
- virtual BOOL isParticipantAvatar(const LLUUID &id) override;
- virtual BOOL getIsSpeaking(const LLUUID& id) override;
- virtual BOOL getIsModeratorMuted(const LLUUID& id) override;
- virtual F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is...
- virtual BOOL getOnMuteList(const LLUUID& id) override;
- virtual F32 getUserVolume(const LLUUID& id) override;
- virtual void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal)
+ std::string getDisplayName(const LLUUID& id) override;
+ BOOL isParticipantAvatar(const LLUUID &id) override;
+ BOOL getIsSpeaking(const LLUUID& id) override;
+ BOOL getIsModeratorMuted(const LLUUID& id) override;
+ F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is...
+ F32 getUserVolume(const LLUUID& id) override;
+ void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal)
//@}
-
+
// authorize the user
- virtual void userAuthorized(const std::string& user_id,
- const LLUUID &agentID) override;
-
+ void userAuthorized(const std::string& user_id,
+ const LLUUID &agentID) override;
+
//////////////////////////////
/// @name Status notification
//@{
- virtual void addObserver(LLVoiceClientStatusObserver* observer) override;
- virtual void removeObserver(LLVoiceClientStatusObserver* observer) override;
- virtual void addObserver(LLFriendObserver* observer) override;
- virtual void removeObserver(LLFriendObserver* observer) override;
- virtual void addObserver(LLVoiceClientParticipantObserver* observer) override;
- virtual void removeObserver(LLVoiceClientParticipantObserver* observer) override;
+ void addObserver(LLVoiceClientStatusObserver* observer) override;
+ void removeObserver(LLVoiceClientStatusObserver* observer) override;
+ void addObserver(LLFriendObserver* observer) override;
+ void removeObserver(LLFriendObserver* observer) override;
+ void addObserver(LLVoiceClientParticipantObserver* observer) override;
+ void removeObserver(LLVoiceClientParticipantObserver* observer) override;
//@}
-
- virtual std::string sipURIFromID(const LLUUID &id) override;
+
+ std::string sipURIFromID(const LLUUID &id) override;
//@}
/// @name LLVoiceEffectInterface virtual implementations
@@ -216,32 +231,32 @@ public:
//////////////////////////
/// @name Accessors
//@{
- virtual bool setVoiceEffect(const LLUUID& id) override;
- virtual const LLUUID getVoiceEffect() override;
- virtual LLSD getVoiceEffectProperties(const LLUUID& id) override;
+ bool setVoiceEffect(const LLUUID& id) override;
+ const LLUUID getVoiceEffect() override;
+ LLSD getVoiceEffectProperties(const LLUUID& id) override;
- virtual void refreshVoiceEffectLists(bool clear_lists) override;
- virtual const voice_effect_list_t& getVoiceEffectList() const override;
- virtual const voice_effect_list_t& getVoiceEffectTemplateList() const override;
+ void refreshVoiceEffectLists(bool clear_lists) override;
+ const voice_effect_list_t& getVoiceEffectList() const override;
+ const voice_effect_list_t& getVoiceEffectTemplateList() const override;
//@}
//////////////////////////////
/// @name Status notification
//@{
- virtual void addObserver(LLVoiceEffectObserver* observer) override;
- virtual void removeObserver(LLVoiceEffectObserver* observer) override;
+ void addObserver(LLVoiceEffectObserver* observer) override;
+ void removeObserver(LLVoiceEffectObserver* observer) override;
//@}
//////////////////////////////
/// @name Effect preview buffer
//@{
- virtual void enablePreviewBuffer(bool enable) override;
- virtual void recordPreviewBuffer() override;
- virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override;
- virtual void stopPreviewBuffer() override;
+ void enablePreviewBuffer(bool enable) override;
+ void recordPreviewBuffer() override;
+ void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override;
+ void stopPreviewBuffer() override;
- virtual bool isPreviewRecording() override;
- virtual bool isPreviewPlaying() override;
+ bool isPreviewRecording() override;
+ bool isPreviewPlaying() override;
//@}
//@}
@@ -251,12 +266,12 @@ public:
protected:
//////////////////////
- // Vivox Specific definitions
-
+ // Vivox Specific definitions
+
friend class LLVivoxVoiceClientMuteListObserver;
- friend class LLVivoxVoiceClientFriendsObserver;
+ friend class LLVivoxVoiceClientFriendsObserver;
+
-
enum streamState
{
streamStateUnknown = 0,
@@ -265,16 +280,16 @@ protected:
streamStateRinging = 3,
streamStateConnecting = 6, // same as Vivox session_media_connecting enum
streamStateDisconnecting = 7, //Same as Vivox session_media_disconnecting enum
- };
+ };
struct participantState
{
public:
participantState(const std::string &uri);
-
+
bool updateMuteState(); // true if mute state has changed
bool isAvatar();
-
+
std::string mURI;
LLUUID mAvatarID;
std::string mAccountName;
@@ -299,7 +314,7 @@ protected:
typedef std::map<const std::string, participantStatePtr_t> participantMap;
typedef std::map<const LLUUID, participantStatePtr_t> participantUUIDMap;
-
+
struct sessionState
{
public:
@@ -310,7 +325,9 @@ protected:
static ptr_t createSession();
~sessionState();
-
+
+ LLSD getVoiceChannelInfo();
+
participantStatePtr_t addParticipant(const std::string &uri);
void removeParticipant(const participantStatePtr_t &participant);
void removeAllParticipants();
@@ -325,7 +342,8 @@ protected:
bool isCallBackPossible();
bool isTextIMPossible();
-
+ bool isSpatial() { return mIsSpatial; }
+
static void for_each(sessionFunc_t func);
std::string mHandle;
@@ -337,7 +355,7 @@ protected:
std::string mHash; // Channel password
std::string mErrorStatusString;
std::queue<std::string> mTextMsgQueue;
-
+
LLUUID mIMSessionID;
LLUUID mCallerID;
int mErrorStatusCode;
@@ -384,7 +402,7 @@ protected:
typedef boost::shared_ptr<sessionState> sessionStatePtr_t;
typedef std::map<std::string, sessionStatePtr_t> sessionMap;
-
+
///////////////////////////////////////////////////////
// Private Member Functions
//////////////////////////////////////////////////////
@@ -396,17 +414,17 @@ protected:
//@{
// Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch.
void daemonDied();
-
+
// Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away.
- void giveUp();
-
+ void giveUp();
+
// write to the tvc
bool writeString(const std::string &str);
-
+
void connectorCreate();
- void connectorShutdown();
- void closeSocket(void);
-
+ void connectorShutdown();
+ void closeSocket(void);
+
// void requestVoiceAccountProvision(S32 retries = 3);
void setLoginInfo(
const std::string& account_name,
@@ -415,14 +433,14 @@ protected:
const std::string& voice_account_server_uri);
void loginSendMessage();
void logout();
- void logoutSendMessage();
-
-
+ void logoutSendMessage();
+
+
//@}
-
+
//------------------------------------
// tuning
-
+
void tuningRenderStartSendMessage(const std::string& name, bool loop);
void tuningRenderStopSendMessage();
@@ -435,12 +453,12 @@ protected:
void addCaptureDevice(const LLVoiceDevice& device);
void clearRenderDevices();
void setDevicesListUpdated(bool state);
- void addRenderDevice(const LLVoiceDevice& device);
+ void addRenderDevice(const LLVoiceDevice& device);
void buildSetAudioDevices(std::ostringstream &stream);
-
+
void getCaptureDevicesSendMessage();
void getRenderDevicesSendMessage();
-
+
// local audio updates, mic mute, speaker mute, mic volume and speaker volumes
void sendLocalAudioUpdates();
@@ -467,9 +485,9 @@ protected:
void auxAudioPropertiesEvent(F32 energy);
void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString);
void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType);
-
+
void muteListChanged();
-
+
/////////////////////////////
// VAD changes
// disable auto-VAD and configure VAD parameters explicitly
@@ -485,7 +503,7 @@ protected:
void setEarLocation(S32 loc);
-
+
/////////////////////////////
// Accessors for data related to nearby speakers
@@ -494,30 +512,25 @@ protected:
std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable)
/////////////////////////////
- BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled.
- // Use this to determine whether to show a "no speech" icon in the menu bar.
-
-
- /////////////////////////////
// Recording controls
void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200);
void recordingLoopSave(const std::string& filename);
void recordingStop();
-
+
// Playback controls
void filePlaybackStart(const std::string& filename);
void filePlaybackStop();
void filePlaybackSetPaused(bool paused);
void filePlaybackSetMode(bool vox = false, float speed = 1.0f);
-
+
participantStatePtr_t findParticipantByID(const LLUUID& id);
-
+
#if 0
////////////////////////////////////////
// voice sessions.
typedef std::set<sessionStatePtr_t> sessionSet;
-
+
typedef sessionSet::iterator sessionIterator;
sessionIterator sessionsBegin(void);
sessionIterator sessionsEnd(void);
@@ -526,7 +539,7 @@ protected:
sessionStatePtr_t findSession(const std::string &handle);
sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri);
sessionStatePtr_t findSession(const LLUUID &participant_id);
-
+
sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string());
void clearSessionHandle(const sessionStatePtr_t &session);
void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle);
@@ -542,11 +555,11 @@ protected:
// This is called in several places where the session _may_ need to be deleted.
// It contains logic for whether to delete the session or keep it around.
void reapSession(const sessionStatePtr_t &session);
-
+
// Returns true if the session seems to indicate we've moved to a region on a different voice server
bool sessionNeedsRelog(const sessionStatePtr_t &session);
-
-
+
+
//////////////////////////////////////
// buddy list stuff, needed for SLIM later
struct buddyListEntry
@@ -566,13 +579,13 @@ protected:
};
typedef std::map<std::string, buddyListEntry*> buddyListMap;
-
+
/////////////////////////////
// session control messages
void accountListBlockRulesSendMessage();
void accountListAutoAcceptRulesSendMessage();
-
+
void sessionGroupCreateSendMessage();
void sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false);
void sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false);
@@ -583,20 +596,20 @@ protected:
void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session);
// void sessionTextDisconnectSendMessage(sessionState *session);
-
-
+
+
// Pokes the state machine to leave the audio session next time around.
- void sessionTerminate();
-
+ void sessionTerminate();
+
// Pokes the state machine to shut down the connector and restart it.
void requestRelog();
-
+
// Does the actual work to get out of the audio session
void leaveAudioSession();
-
+
friend class LLVivoxVoiceClientCapResponder;
-
-
+
+
void lookupName(const LLUUID &id);
void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
void avatarNameResolved(const LLUUID &id, const std::string &name);
@@ -616,10 +629,10 @@ protected:
const S32 font_status,
const bool template_font = false);
void accountGetSessionFontsResponse(int statusCode, const std::string &statusString);
- void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString);
+ void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString);
private:
-
+
LLVoiceVersionInfo mVoiceVersion;
// Coroutine support methods
@@ -661,21 +674,21 @@ private:
// The larger it is the greater is possibility there is a problem with connection to voice server.
// Introduced while fixing EXT-4313.
int mSpatialJoiningNum;
-
+
static void idle(void *user_data);
-
+
LLHost mDaemonHost;
LLSocket::ptr_t mSocket;
-
- // We should kill the voice daemon in case of connection alert
+
+ // We should kill the voice daemon in case of connection alert
bool mTerminateDaemon;
-
+
friend class LLVivoxProtocolParser;
-
+
std::string mAccountName;
std::string mAccountPassword;
std::string mAccountDisplayName;
-
+
bool mTuningMode;
float mTuningEnergy;
std::string mTuningAudioFile;
@@ -685,14 +698,13 @@ private:
bool mTuningSpeakerVolumeDirty;
bool mDevicesListUpdated; // set to true when the device list has been updated
// and false when the panelvoicedevicesettings has queried for an update status.
-
+
std::string mSpatialSessionURI;
std::string mSpatialSessionCredentials;
std::string mMainSessionGroupHandle; // handle of the "main" session group.
-
- std::string mChannelName; // Name of the channel to be looked up
- bool mAreaVoiceDisabled;
+
+ std::string mChannelName; // Name of the channel to be looked up
sessionStatePtr_t mAudioSession; // Session state for the current audio session
bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified.
@@ -700,27 +712,27 @@ private:
S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings
std::string mCurrentRegionName; // Used to detect parcel boundary crossings
-
+
bool mConnectorEstablished; // set by "Create Connector" response
- bool mAccountLoggedIn; // set by login message
+ bool mAccountLoggedIn; // set by login message
int mNumberOfAliases;
U32 mCommandCookie;
std::string mVoiceAccountServerURI;
std::string mVoiceSIPURIHostName;
-
+
int mLoginRetryCount;
-
+
sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map.
#if 0
sessionSet mSessions; // All sessions, not indexed. This is the canonical session list.
#endif
-
+
bool mBuddyListMapPopulated;
bool mBlockRulesListReceived;
bool mAutoAcceptRulesListReceived;
buddyListMap mBuddyListMap;
-
+
LLVoiceDeviceList mCaptureDevices;
LLVoiceDeviceList mRenderDevices;
@@ -731,32 +743,30 @@ private:
bool mIsInitialized;
bool mShutdownComplete;
-
+
bool checkParcelChanged(bool update = false);
bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = "");
void joinSession(const sessionStatePtr_t &session);
-
- std::string nameFromAvatar(LLVOAvatar *avatar);
+
std::string nameFromID(const LLUUID &id);
bool IDFromName(const std::string name, LLUUID &uuid);
- std::string displayNameFromAvatar(LLVOAvatar *avatar);
std::string sipURIFromAvatar(LLVOAvatar *avatar);
std::string sipURIFromName(std::string &name);
-
+
// Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not.
- std::string nameFromsipURI(const std::string &uri);
+ std::string nameFromsipURI(const std::string &uri);
bool inSpatialChannel(void);
- std::string getAudioSessionURI();
+ LLSD getAudioSessionChannelInfo();
std::string getAudioSessionHandle();
-
+
void setHidden(bool hidden) override; //virtual
void sendPositionAndVolumeUpdate(void);
-
+
void sendCaptureAndRenderDevices();
void buildSetCaptureDevice(std::ostringstream &stream);
void buildSetRenderDevice(std::ostringstream &stream);
-
+
void sendFriendsListUpdates();
@@ -767,9 +777,9 @@ private:
#endif
void enforceTether(void);
-
+
bool mSpatialCoordsDirty;
-
+
LLVector3d mCameraPosition;
LLVector3d mCameraRequestedPosition;
LLVector3 mCameraVelocity;
@@ -778,36 +788,35 @@ private:
LLVector3d mAvatarPosition;
LLVector3 mAvatarVelocity;
LLQuaternion mAvatarRot;
-
+
bool mMuteMic;
bool mMuteMicDirty;
bool mHidden; //Set to true during teleport to hide the agent's position.
-
+
// Set to true when the friends list is known to have changed.
bool mFriendsListDirty;
-
+
enum
{
earLocCamera = 0, // ear at camera
earLocAvatar, // ear at avatar
earLocMixed // ear at avatar location/camera direction
};
-
- S32 mEarLocation;
-
+
+ S32 mEarLocation;
+
bool mSpeakerVolumeDirty;
bool mSpeakerMuteDirty;
int mSpeakerVolume;
int mMicVolume;
bool mMicVolumeDirty;
-
+
bool mVoiceEnabled;
+ bool mProcessChannels;
bool mWriteInProgress;
std::string mWriteString;
size_t mWriteOffset;
-
- BOOL mLipSyncEnabled;
typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
observer_set_t mParticipantObservers;
@@ -816,7 +825,7 @@ private:
typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
status_observer_set_t mStatusObservers;
-
+
void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
typedef std::set<LLFriendObserver*> friend_observer_set_t;
@@ -923,7 +932,7 @@ private:
};
-/**
+/**
* @class LLVivoxProtocolParser
* @brief This class helps construct new LLIOPipe specializations
* @see LLIOPipe
@@ -936,12 +945,12 @@ class LLVivoxProtocolParser : public LLIOPipe
public:
LLVivoxProtocolParser();
virtual ~LLVivoxProtocolParser();
-
+
protected:
/* @name LLIOPipe virtual implementations
*/
//@{
- /**
+ /**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
@@ -951,16 +960,16 @@ protected:
LLSD& context,
LLPumpIO* pump);
//@}
-
+
std::string mInput;
-
+
// Expat control members
XML_Parser parser;
int responseDepth;
bool ignoringTags;
bool isEvent;
int ignoreDepth;
-
+
// Members for processing responses. The values are transient and only valid within a call to processResponse().
int returnCode;
int statusCode;
@@ -975,7 +984,7 @@ protected:
std::string sessionGroupHandle;
std::string alias;
std::string applicationString;
-
+
// Members for processing events. The values are transient and only valid within a call to processResponse().
std::string eventTypeString;
int state;
@@ -1014,19 +1023,19 @@ protected:
S32 fontType;
S32 fontStatus;
std::string mediaCompletionType;
-
+
// Members for processing text between tags
std::string textBuffer;
bool accumulateText;
-
+
void reset();
-
+
void processResponse(std::string tag);
-
+
static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
static void XMLCALL ExpatEndTag(void *data, const char *el);
static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
-
+
void StartTag(const char *tag, const char **attr);
void EndTag(const char *tag);
void CharData(const char *buffer, int length);
@@ -1053,7 +1062,7 @@ class LLVoiceVivoxStats : public LLSingleton<LLVoiceVivoxStats>
LLSINGLETON(LLVoiceVivoxStats);
LOG_CLASS(LLVoiceVivoxStats);
virtual ~LLVoiceVivoxStats();
-
+
private:
F64SecondsImplicit mStartTime;
@@ -1061,7 +1070,7 @@ class LLVoiceVivoxStats : public LLSingleton<LLVoiceVivoxStats>
F64 mConnectTime;
U32 mConnectAttempts;
-
+
F64 mProvisionTime;
U32 mProvisionAttempts;
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
new file mode 100644
index 0000000000..2bb16cb336
--- /dev/null
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -0,0 +1,2885 @@
+ /**
+ * @file LLWebRTCVoiceClient.cpp
+ * @brief Implementation of LLWebRTCVoiceClient class which is the interface to the voice client process.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include <algorithm>
+#include "llvoicewebrtc.h"
+
+#include "llsdutil.h"
+
+// Linden library includes
+#include "llavatarnamecache.h"
+#include "llvoavatarself.h"
+#include "llbufferstream.h"
+#include "llfile.h"
+#include "llmenugl.h"
+#ifdef LL_USESYSTEMLIBS
+# include "expat.h"
+#else
+# include "expat/expat.h"
+#endif
+#include "llcallbacklist.h"
+#include "llviewernetwork.h" // for gGridChoice
+#include "llbase64.h"
+#include "llviewercontrol.h"
+#include "llappviewer.h" // for gDisconnected, gDisableVoice
+#include "llprocess.h"
+
+// Viewer includes
+#include "llmutelist.h" // to check for muted avatars
+#include "llagent.h"
+#include "llcachename.h"
+#include "llimview.h" // for LLIMMgr
+#include "llworld.h"
+#include "llparcel.h"
+#include "llviewerparcelmgr.h"
+#include "llfirstuse.h"
+#include "llspeakers.h"
+#include "lltrans.h"
+#include "llrand.h"
+#include "llviewerwindow.h"
+#include "llviewercamera.h"
+#include "llversioninfo.h"
+
+#include "llviewernetwork.h"
+#include "llnotificationsutil.h"
+
+#include "llcorehttputil.h"
+#include "lleventfilter.h"
+
+#include "stringize.h"
+
+#include "llwebrtc.h"
+
+// for base64 decoding
+#include "apr_base64.h"
+
+#include "json/reader.h"
+#include "json/writer.h"
+
+const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc";
+
+namespace {
+
+ const F32 MAX_AUDIO_DIST = 50.0f;
+ const F32 VOLUME_SCALE_WEBRTC = 0.01f;
+ const F32 LEVEL_SCALE_WEBRTC = 0.008f;
+
+ const F32 SPEAKING_AUDIO_LEVEL = 0.40;
+
+ static const std::string REPORTED_VOICE_SERVER_TYPE = "Secondlife WebRTC Gateway";
+
+ // Don't send positional updates more frequently than this:
+ const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
+
+ // Cosine of a "trivially" small angle
+ const F32 FOUR_DEGREES = 4.0f * (F_PI / 180.0f);
+ const F32 MINUSCULE_ANGLE_COS = (F32) cos(0.5f * FOUR_DEGREES);
+
+} // namespace
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void LLVoiceWebRTCStats::reset()
+{
+ mStartTime = -1.0f;
+ mConnectCycles = 0;
+ mConnectTime = -1.0f;
+ mConnectAttempts = 0;
+ mProvisionTime = -1.0f;
+ mProvisionAttempts = 0;
+ mEstablishTime = -1.0f;
+ mEstablishAttempts = 0;
+}
+
+LLVoiceWebRTCStats::LLVoiceWebRTCStats()
+{
+ reset();
+}
+
+LLVoiceWebRTCStats::~LLVoiceWebRTCStats()
+{
+}
+
+void LLVoiceWebRTCStats::connectionAttemptStart()
+{
+ if (!mConnectAttempts)
+ {
+ mStartTime = LLTimer::getTotalTime();
+ mConnectCycles++;
+ }
+ mConnectAttempts++;
+}
+
+void LLVoiceWebRTCStats::connectionAttemptEnd(bool success)
+{
+ if ( success )
+ {
+ mConnectTime = (LLTimer::getTotalTime() - mStartTime) / USEC_PER_SEC;
+ }
+}
+
+void LLVoiceWebRTCStats::provisionAttemptStart()
+{
+ if (!mProvisionAttempts)
+ {
+ mStartTime = LLTimer::getTotalTime();
+ }
+ mProvisionAttempts++;
+}
+
+void LLVoiceWebRTCStats::provisionAttemptEnd(bool success)
+{
+ if ( success )
+ {
+ mProvisionTime = (LLTimer::getTotalTime() - mStartTime) / USEC_PER_SEC;
+ }
+}
+
+void LLVoiceWebRTCStats::establishAttemptStart()
+{
+ if (!mEstablishAttempts)
+ {
+ mStartTime = LLTimer::getTotalTime();
+ }
+ mEstablishAttempts++;
+}
+
+void LLVoiceWebRTCStats::establishAttemptEnd(bool success)
+{
+ if ( success )
+ {
+ mEstablishTime = (LLTimer::getTotalTime() - mStartTime) / USEC_PER_SEC;
+ }
+}
+
+LLSD LLVoiceWebRTCStats::read()
+{
+ LLSD stats(LLSD::emptyMap());
+
+ stats["connect_cycles"] = LLSD::Integer(mConnectCycles);
+ stats["connect_attempts"] = LLSD::Integer(mConnectAttempts);
+ stats["connect_time"] = LLSD::Real(mConnectTime);
+
+ stats["provision_attempts"] = LLSD::Integer(mProvisionAttempts);
+ stats["provision_time"] = LLSD::Real(mProvisionTime);
+
+ stats["establish_attempts"] = LLSD::Integer(mEstablishAttempts);
+ stats["establish_time"] = LLSD::Real(mEstablishTime);
+
+ return stats;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool LLWebRTCVoiceClient::sShuttingDown = false;
+
+LLWebRTCVoiceClient::LLWebRTCVoiceClient() :
+ mHidden(false),
+ mTuningMode(false),
+ mTuningMicGain(0.0),
+ mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume
+ mDevicesListUpdated(false),
+
+ mSpatialCoordsDirty(false),
+
+ mMuteMic(false),
+
+ mEarLocation(0),
+ mMicGain(0.0),
+
+ mVoiceEnabled(false),
+ mProcessChannels(false),
+
+ mAvatarNameCacheConnection(),
+ mIsInTuningMode(false),
+ mIsProcessingChannels(false),
+ mIsCoroutineActive(false),
+ mWebRTCPump("WebRTCClientPump"),
+ mWebRTCDeviceInterface(nullptr)
+{
+ sShuttingDown = false;
+
+ mSpeakerVolume = 0.0;
+
+ mVoiceVersion.serverVersion = "";
+ mVoiceVersion.voiceServerType = REPORTED_VOICE_SERVER_TYPE;
+ mVoiceVersion.internalVoiceServerType = WEBRTC_VOICE_SERVER_TYPE;
+ mVoiceVersion.minorVersion = 0;
+ mVoiceVersion.majorVersion = 2;
+ mVoiceVersion.mBuildVersion = "";
+}
+
+//---------------------------------------------------
+
+LLWebRTCVoiceClient::~LLWebRTCVoiceClient()
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ sShuttingDown = true;
+}
+
+//---------------------------------------------------
+
+void LLWebRTCVoiceClient::init(LLPumpIO* pump)
+{
+ // constructor will set up LLVoiceClient::getInstance()
+ llwebrtc::init();
+
+ mWebRTCDeviceInterface = llwebrtc::getDeviceInterface();
+ mWebRTCDeviceInterface->setDevicesObserver(this);
+}
+
+void LLWebRTCVoiceClient::terminate()
+{
+ if (sShuttingDown)
+ {
+ return;
+ }
+
+ mVoiceEnabled = false;
+ llwebrtc::terminate();
+
+ sShuttingDown = true;
+}
+
+//---------------------------------------------------
+
+void LLWebRTCVoiceClient::cleanUp()
+{
+ mNextSession.reset();
+ mSession.reset();
+ mNeighboringRegions.clear();
+ sessionState::for_each(boost::bind(predShutdownSession, _1));
+ LL_DEBUGS("Voice") << "Exiting" << LL_ENDL;
+}
+
+//---------------------------------------------------
+
+const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion()
+{
+ return mVoiceVersion;
+}
+
+//---------------------------------------------------
+
+void LLWebRTCVoiceClient::updateSettings()
+{
+ setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled());
+ setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
+
+ std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+ setCaptureDevice(inputDevice);
+ std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+ setRenderDevice(outputDevice);
+ F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
+ setMicGain(mic_level);
+}
+
+// Observers
+void LLWebRTCVoiceClient::addObserver(LLVoiceClientParticipantObserver *observer)
+{
+ mParticipantObservers.insert(observer);
+}
+
+void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver *observer)
+{
+ mParticipantObservers.erase(observer);
+}
+
+void LLWebRTCVoiceClient::notifyParticipantObservers()
+{
+ for (observer_set_t::iterator it = mParticipantObservers.begin(); it != mParticipantObservers.end();)
+ {
+ LLVoiceClientParticipantObserver *observer = *it;
+ observer->onParticipantsChanged();
+ // In case onParticipantsChanged() deleted an entry.
+ it = mParticipantObservers.upper_bound(observer);
+ }
+}
+
+void LLWebRTCVoiceClient::addObserver(LLVoiceClientStatusObserver *observer)
+{
+ mStatusObservers.insert(observer);
+}
+
+void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver *observer)
+{
+ mStatusObservers.erase(observer);
+}
+
+void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
+{
+ LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )"
+ << " mSession=" << mSession << LL_ENDL;
+
+ LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) << ", session channelInfo "
+ << getAudioSessionChannelInfo() << ", proximal is " << inSpatialChannel() << LL_ENDL;
+
+ mIsProcessingChannels = status == LLVoiceClientStatusObserver::STATUS_JOINED;
+
+ LLSD channelInfo = getAudioSessionChannelInfo();
+ for (status_observer_set_t::iterator it = mStatusObservers.begin(); it != mStatusObservers.end();)
+ {
+ LLVoiceClientStatusObserver *observer = *it;
+ observer->onChange(status, channelInfo, inSpatialChannel());
+ // In case onError() deleted an entry.
+ it = mStatusObservers.upper_bound(observer);
+ }
+
+ // skipped to avoid speak button blinking
+ if (status != LLVoiceClientStatusObserver::STATUS_JOINING &&
+ status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL &&
+ status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED)
+ {
+ bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
+
+ gAgent.setVoiceConnected(voice_status);
+
+ if (voice_status)
+ {
+ LLFirstUse::speak(true);
+ }
+ }
+}
+
+void LLWebRTCVoiceClient::addObserver(LLFriendObserver *observer)
+{
+}
+
+void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer)
+{
+}
+
+//---------------------------------------------------
+// Primary voice loop.
+// This voice loop is called every 100ms plus the time it
+// takes to process the various functions called in the loop
+// The loop does the following:
+// * gates whether we do channel processing depending on
+// whether we're running a WebRTC voice channel or
+// one from another voice provider.
+// * If in spatial voice, it determines whether we've changed
+// parcels, whether region/parcel voice settings have changed,
+// etc. and manages whether the voice channel needs to change.
+// * calls the state machines for the sessions to negotiate
+// connection to various voice channels.
+// * Sends updates to the voice server when this agent's
+// voice levels, or positions have changed.
+void LLWebRTCVoiceClient::voiceConnectionCoro()
+{
+ LL_DEBUGS("Voice") << "starting" << LL_ENDL;
+ mIsCoroutineActive = true;
+ LLCoros::set_consuming(true);
+ try
+ {
+ LLMuteList::getInstance()->addObserver(this);
+ while (!sShuttingDown)
+ {
+ // TODO: Doing some measurement and calculation here,
+ // we could reduce the timeout to take into account the
+ // time spent on the previous loop to have the loop
+ // cycle at exactly 100ms, instead of 100ms + loop
+ // execution time.
+ // Could help with voice updates making for smoother
+ // voice when we're busy.
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ bool voiceEnabled = mVoiceEnabled;
+
+ if (!isAgentAvatarValid())
+ {
+ continue;
+ }
+
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (!regionp)
+ {
+ continue;
+ }
+
+ if (!mProcessChannels)
+ {
+ // we've switched away from webrtc voice, so shut all channels down.
+ // leave channel can be called again and again without adverse effects.
+ // it merely tells channels to shut down if they're not already doing so.
+ leaveChannel(false);
+ }
+ else if (inSpatialChannel())
+ {
+ bool useEstateVoice = true;
+ // add session for region or parcel voice.
+ if (!regionp || regionp->getRegionID().isNull())
+ {
+ // no region, no voice.
+ continue;
+ }
+
+ voiceEnabled = voiceEnabled && regionp->isVoiceEnabled();
+
+ if (voiceEnabled)
+ {
+ LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+ // check to see if parcel changed.
+ if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID)
+ {
+ // parcel voice
+ if (!parcel->getParcelFlagAllowVoice())
+ {
+ voiceEnabled = false;
+ }
+ else if (!parcel->getParcelFlagUseEstateVoiceChannel())
+ {
+ // use the parcel-specific voice channel.
+ S32 parcel_local_id = parcel->getLocalID();
+ std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID());
+
+ useEstateVoice = false;
+ if (!inOrJoiningChannel(channelID))
+ {
+ startParcelSession(channelID, parcel_local_id);
+ }
+ }
+ }
+ if (useEstateVoice && !inEstateChannel())
+ {
+ // estate voice
+ startEstateSession();
+ }
+ }
+ if (!voiceEnabled)
+ {
+ // voice is disabled, so leave and disable PTT
+ leaveChannel(true);
+ }
+ else
+ {
+ // we're in spatial voice, and voice is enabled, so determine positions in order
+ // to send position updates.
+ updatePosition();
+ }
+ }
+
+ sessionState::processSessionStates();
+ if (mProcessChannels && voiceEnabled && !mHidden)
+ {
+ sendPositionUpdate(true);
+ updateOwnVolume();
+ }
+ }
+ }
+ catch (const LLCoros::Stop&)
+ {
+ LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL;
+ }
+ catch (const LLContinueError&)
+ {
+ LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient");
+ }
+ catch (...)
+ {
+ // Ideally for Windows need to log SEH exception instead or to set SEH
+ // handlers but bugsplat shows local variables for windows, which should
+ // be enough
+ LL_WARNS("Voice") << "voiceConnectionStateMachine crashed" << LL_ENDL;
+ throw;
+ }
+
+ cleanUp();
+}
+
+// For spatial, determine which neighboring regions to connect to
+// for cross-region voice.
+void LLWebRTCVoiceClient::updateNeighboringRegions()
+{
+ static const std::vector<LLVector3d> neighbors {LLVector3d(0.0f, 1.0f, 0.0f), LLVector3d(0.707f, 0.707f, 0.0f),
+ LLVector3d(1.0f, 0.0f, 0.0f), LLVector3d(0.707f, -0.707f, 0.0f),
+ LLVector3d(0.0f, -1.0f, 0.0f), LLVector3d(-0.707f, -0.707f, 0.0f),
+ LLVector3d(-1.0f, 0.0f, 0.0f), LLVector3d(-0.707f, 0.707f, 0.0f)};
+
+ // Estate voice requires connection to neighboring regions.
+ mNeighboringRegions.clear();
+
+ mNeighboringRegions.insert(gAgent.getRegion()->getRegionID());
+
+ // base off of speaker position as it'll move more slowly than camera position.
+ // Once we have hysteresis, we may be able to track off of speaker and camera position at 50m
+ // TODO: Add hysteresis so we don't flip-flop connections to neighbors
+ LLVector3d speaker_pos = LLWebRTCVoiceClient::getInstance()->getSpeakerPosition();
+ for (auto &neighbor_pos : neighbors)
+ {
+ // include every region within 100m (2*MAX_AUDIO_DIST) to deal witht he fact that the camera
+ // can stray 50m away from the avatar.
+ LLViewerRegion *neighbor = LLWorld::instance().getRegionFromPosGlobal(speaker_pos + 2 * MAX_AUDIO_DIST * neighbor_pos);
+ if (neighbor && !neighbor->getRegionID().isNull())
+ {
+ mNeighboringRegions.insert(neighbor->getRegionID());
+ }
+ }
+}
+
+//=========================================================================
+// shut down the current audio session to make room for the next one.
+void LLWebRTCVoiceClient::leaveAudioSession()
+{
+ if(mSession)
+ {
+ LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL;
+ mSession->shutdownAllConnections();
+ }
+ else
+ {
+ LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
+ }
+}
+
+//=========================================================================
+// Device Management
+void LLWebRTCVoiceClient::clearCaptureDevices()
+{
+ LL_DEBUGS("Voice") << "called" << LL_ENDL;
+ mCaptureDevices.clear();
+}
+
+void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device)
+{
+ LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL;
+ mCaptureDevices.push_back(device);
+}
+
+LLVoiceDeviceList& LLWebRTCVoiceClient::getCaptureDevices()
+{
+ return mCaptureDevices;
+}
+
+void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name)
+{
+ mWebRTCDeviceInterface->setCaptureDevice(name);
+}
+void LLWebRTCVoiceClient::setDevicesListUpdated(bool state)
+{
+ mDevicesListUpdated = state;
+}
+
+void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices,
+ const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices)
+{
+ std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+ std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+
+ clearRenderDevices();
+ bool renderDeviceSet = false;
+ for (auto &device : render_devices)
+ {
+ addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID));
+ if (device.mCurrent && outputDevice == device.mID)
+ {
+ setRenderDevice(outputDevice);
+ renderDeviceSet = true;
+ }
+ }
+ if (!renderDeviceSet)
+ {
+ setRenderDevice("Default");
+ }
+
+ clearCaptureDevices();
+ bool captureDeviceSet = false;
+ for (auto &device : capture_devices)
+ {
+ addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID));
+ if (device.mCurrent && inputDevice == device.mID)
+ {
+ setCaptureDevice(outputDevice);
+ captureDeviceSet = true;
+ }
+ }
+ if (!captureDeviceSet)
+ {
+ setCaptureDevice("Default");
+ }
+
+ setDevicesListUpdated(true);
+}
+
+void LLWebRTCVoiceClient::clearRenderDevices()
+{
+ LL_DEBUGS("Voice") << "called" << LL_ENDL;
+ mRenderDevices.clear();
+}
+
+void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device)
+{
+ LL_DEBUGS("Voice") << "display: '" << device.display_name << "' device: '" << device.full_name << "'" << LL_ENDL;
+ mRenderDevices.push_back(device);
+
+}
+
+LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices()
+{
+ return mRenderDevices;
+}
+
+void LLWebRTCVoiceClient::setRenderDevice(const std::string& name)
+{
+ mWebRTCDeviceInterface->setRenderDevice(name);
+}
+
+void LLWebRTCVoiceClient::tuningStart()
+{
+ if (!mIsInTuningMode)
+ {
+ mWebRTCDeviceInterface->setTuningMode(true);
+ mIsInTuningMode = true;
+ }
+}
+
+void LLWebRTCVoiceClient::tuningStop()
+{
+ if (mIsInTuningMode)
+ {
+ mWebRTCDeviceInterface->setTuningMode(false);
+ mIsInTuningMode = false;
+ }
+}
+
+bool LLWebRTCVoiceClient::inTuningMode()
+{
+ return mIsInTuningMode;
+}
+
+void LLWebRTCVoiceClient::tuningSetMicVolume(float volume)
+{
+ mTuningMicGain = volume;
+}
+
+void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
+{
+
+ if (volume != mTuningSpeakerVolume)
+ {
+ mTuningSpeakerVolume = volume;
+ }
+}
+
+float LLWebRTCVoiceClient::getAudioLevel()
+{
+ if (mIsInTuningMode)
+ {
+ return (1.0 - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1;
+ }
+ else
+ {
+ return (1.0 - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1;
+ }
+}
+
+float LLWebRTCVoiceClient::tuningGetEnergy(void)
+{
+ return getAudioLevel();
+}
+
+bool LLWebRTCVoiceClient::deviceSettingsAvailable()
+{
+ bool result = true;
+
+ if(mRenderDevices.empty() || mCaptureDevices.empty())
+ result = false;
+
+ return result;
+}
+bool LLWebRTCVoiceClient::deviceSettingsUpdated()
+{
+ bool updated = mDevicesListUpdated;
+ mDevicesListUpdated = false;
+ return updated;
+}
+
+void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList)
+{
+ if(clearCurrentList)
+ {
+ clearCaptureDevices();
+ clearRenderDevices();
+ }
+ mWebRTCDeviceInterface->refreshDevices();
+}
+
+
+void LLWebRTCVoiceClient::setHidden(bool hidden)
+{
+ mHidden = hidden;
+
+ if (inSpatialChannel())
+ {
+ if (mHidden)
+ {
+ // get out of the channel entirely
+ // mute the microphone.
+ sessionState::for_each(boost::bind(predSetMuteMic, _1, true));
+ }
+ else
+ {
+ // and put it back
+ sessionState::for_each(boost::bind(predSetMuteMic, _1, mMuteMic));
+ updatePosition();
+ sendPositionUpdate(true);
+ }
+ }
+}
+
+/////////////////////////////
+// session control messages.
+//
+// these are called by the sessions to report
+// status for a given channel. By filtering
+// on channel and region, these functions
+// can send various notifications to
+// other parts of the viewer, as well as
+// managing housekeeping
+
+// A connection to a channel was successfully established,
+// so shut down the current session and move on to the next
+// if one is available.
+// if the current session is the one that was established,
+// notify the observers.
+void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID, const LLUUID &regionID)
+{
+ if (gAgent.getRegion()->getRegionID() == regionID)
+ {
+ if (mNextSession && mNextSession->mChannelID == channelID)
+ {
+ if (mSession)
+ {
+ mSession->shutdownAllConnections();
+ }
+ mSession = mNextSession;
+ mNextSession.reset();
+
+ // Add ourselves as a participant.
+ mSession->addParticipant(gAgentID);
+ }
+
+ // The current session was established.
+ if (mSession && mSession->mChannelID == channelID)
+ {
+ LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+ // only set status to joined if asked to. This will happen in the case where we're not
+ // doing an ad-hoc based p2p session. Those sessions expect a STATUS_JOINED when the peer
+ // has, in fact, joined, which we detect elsewhere.
+ if (!mSession->mNotifyOnFirstJoin)
+ {
+ LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+ }
+ }
+ }
+}
+
+void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, const LLUUID &regionID)
+{
+ if (gAgent.getRegion()->getRegionID() == regionID)
+ {
+ if (mSession && mSession->mChannelID == channelID)
+ {
+ LL_DEBUGS("Voice") << "Main WebRTC Connection Shut Down." << LL_ENDL;
+ }
+ }
+}
+void LLWebRTCVoiceClient::OnConnectionFailure(const std::string &channelID, const LLUUID &regionID)
+{
+ LL_DEBUGS("Voice") << "A connection failed. channel:" << channelID << LL_ENDL;
+ if (gAgent.getRegion()->getRegionID() == regionID)
+ {
+ if (mNextSession && mNextSession->mChannelID == channelID)
+ {
+ LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
+ }
+ else if (mSession && mSession->mChannelID == channelID)
+ {
+ LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
+ }
+ }
+}
+
+// -----------------------------------------------------------
+// positional functionality.
+void LLWebRTCVoiceClient::setEarLocation(S32 loc)
+{
+ if (mEarLocation != loc)
+ {
+ LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL;
+
+ mEarLocation = loc;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLWebRTCVoiceClient::updatePosition(void)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region && isAgentAvatarValid())
+ {
+ // get the avatar position.
+ LLVector3d avatar_pos = gAgentAvatarp->getPositionGlobal();
+ LLQuaternion avatar_qrot = gAgentAvatarp->getRootJoint()->getWorldRotation();
+
+ avatar_pos += LLVector3d(0.f, 0.f, 1.f); // bump it up to head height
+
+ LLVector3d earPosition;
+ LLQuaternion earRot;
+ switch (mEarLocation)
+ {
+ case earLocCamera:
+ default:
+ earPosition = region->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin());
+ earRot = LLViewerCamera::getInstance()->getQuaternion();
+ break;
+
+ case earLocAvatar:
+ earPosition = mAvatarPosition;
+ earRot = mAvatarRot;
+ break;
+
+ case earLocMixed:
+ earPosition = mAvatarPosition;
+ earRot = LLViewerCamera::getInstance()->getQuaternion();
+ break;
+ }
+ setListenerPosition(earPosition, // position
+ LLVector3::zero, // velocity
+ earRot); // rotation matrix
+
+ setAvatarPosition(avatar_pos, // position
+ LLVector3::zero, // velocity
+ avatar_qrot); // rotation matrix
+
+ enforceTether();
+
+ updateNeighboringRegions();
+ }
+}
+
+void LLWebRTCVoiceClient::setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot)
+{
+ mListenerRequestedPosition = position;
+
+ if (mListenerVelocity != velocity)
+ {
+ mListenerVelocity = velocity;
+ mSpatialCoordsDirty = true;
+ }
+
+ if (mListenerRot != rot)
+ {
+ mListenerRot = rot;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLWebRTCVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot)
+{
+ if (dist_vec_squared(mAvatarPosition, position) > 0.01)
+ {
+ mAvatarPosition = position;
+ mSpatialCoordsDirty = true;
+ }
+
+ if (mAvatarVelocity != velocity)
+ {
+ mAvatarVelocity = velocity;
+ mSpatialCoordsDirty = true;
+ }
+
+ // If the two rotations are not exactly equal test their dot product
+ // to get the cos of the angle between them.
+ // If it is too small, don't update.
+ F32 rot_cos_diff = llabs(dot(mAvatarRot, rot));
+ if ((mAvatarRot != rot) && (rot_cos_diff < MINUSCULE_ANGLE_COS))
+ {
+ mAvatarRot = rot;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+// The listener (camera) must be within 50m of the
+// avatar. Enforce it on the client.
+// This will also be enforced on the voice server
+// based on position sent from the simulator to the
+// voice server.
+void LLWebRTCVoiceClient::enforceTether()
+{
+ LLVector3d tethered = mListenerRequestedPosition;
+
+ // constrain 'tethered' to within 50m of mAvatarPosition.
+ {
+ LLVector3d camera_offset = mListenerRequestedPosition - mAvatarPosition;
+ F32 camera_distance = (F32) camera_offset.magVec();
+ if (camera_distance > MAX_AUDIO_DIST)
+ {
+ tethered = mAvatarPosition + (MAX_AUDIO_DIST / camera_distance) * camera_offset;
+ }
+ }
+
+ if (dist_vec_squared(mListenerPosition, tethered) > 0.01)
+ {
+ mListenerPosition = tethered;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+// We send our position via a WebRTC data channel to the WebRTC
+// server for fine-grained, low latency updates. On the server,
+// these updates will be 'tethered' to the actual position of the avatar.
+// Those updates are higher latency, however.
+// This mechanism gives low latency spatial updates and server-enforced
+// prevention of 'evesdropping' by sending camera updates beyond the
+// standard 50m
+void LLWebRTCVoiceClient::sendPositionUpdate(bool force)
+{
+ Json::FastWriter writer;
+ std::string spatial_data;
+
+ if (mSpatialCoordsDirty || force)
+ {
+ Json::Value spatial = Json::objectValue;
+
+ spatial["sp"] = Json::objectValue;
+ spatial["sp"]["x"] = (int) (mAvatarPosition[0] * 100);
+ spatial["sp"]["y"] = (int) (mAvatarPosition[1] * 100);
+ spatial["sp"]["z"] = (int) (mAvatarPosition[2] * 100);
+ spatial["sh"] = Json::objectValue;
+ spatial["sh"]["x"] = (int) (mAvatarRot[0] * 100);
+ spatial["sh"]["y"] = (int) (mAvatarRot[1] * 100);
+ spatial["sh"]["z"] = (int) (mAvatarRot[2] * 100);
+ spatial["sh"]["w"] = (int) (mAvatarRot[3] * 100);
+
+ spatial["lp"] = Json::objectValue;
+ spatial["lp"]["x"] = (int) (mListenerPosition[0] * 100);
+ spatial["lp"]["y"] = (int) (mListenerPosition[1] * 100);
+ spatial["lp"]["z"] = (int) (mListenerPosition[2] * 100);
+ spatial["lh"] = Json::objectValue;
+ spatial["lh"]["x"] = (int) (mListenerRot[0] * 100);
+ spatial["lh"]["y"] = (int) (mListenerRot[1] * 100);
+ spatial["lh"]["z"] = (int) (mListenerRot[2] * 100);
+ spatial["lh"]["w"] = (int) (mListenerRot[3] * 100);
+
+ mSpatialCoordsDirty = false;
+ spatial_data = writer.write(spatial);
+
+ sessionState::for_each(boost::bind(predSendData, _1, spatial_data));
+ }
+}
+
+// Update our own volume on our participant, so it'll show up
+// in the UI. This is done on all sessions, so switching
+// sessions retains consistent volume levels.
+void LLWebRTCVoiceClient::updateOwnVolume() {
+ F32 audio_level = 0.0;
+ if (!mMuteMic && !mTuningMode)
+ {
+ audio_level = getAudioLevel();
+ }
+
+ sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level));
+}
+
+////////////////////////////////////
+// Managing list of participants
+
+// Provider-level participant management
+
+BOOL LLWebRTCVoiceClient::isParticipantAvatar(const LLUUID &id)
+{
+ // WebRTC participants are always SL avatars.
+ return TRUE;
+}
+
+void LLWebRTCVoiceClient::getParticipantList(std::set<LLUUID> &participants)
+{
+ if (mProcessChannels && mSession)
+ {
+ for (participantUUIDMap::iterator iter = mSession->mParticipantsByUUID.begin();
+ iter != mSession->mParticipantsByUUID.end();
+ iter++)
+ {
+ participants.insert(iter->first);
+ }
+ }
+}
+
+bool LLWebRTCVoiceClient::isParticipant(const LLUUID &speaker_id)
+{
+ if (mProcessChannels && mSession)
+ {
+ return (mSession->mParticipantsByUUID.find(speaker_id) != mSession->mParticipantsByUUID.end());
+ }
+ return false;
+}
+
+// protected provider-level participant management.
+LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::findParticipantByID(const std::string &channelID, const LLUUID &id)
+{
+ participantStatePtr_t result;
+ LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID);
+
+ if (session)
+ {
+ result = session->findParticipantByID(id);
+ }
+
+ return result;
+}
+
+LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantByID(const std::string &channelID, const LLUUID &id)
+{
+ participantStatePtr_t result;
+ LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID);
+ if (session)
+ {
+ result = session->addParticipant(id);
+ if (session->mNotifyOnFirstJoin && (id != gAgentID))
+ {
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+ }
+ }
+ return result;
+}
+
+void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id)
+{
+ participantStatePtr_t result;
+ LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID);
+ if (session)
+ {
+ participantStatePtr_t participant = session->findParticipantByID(id);
+ if (participant)
+ {
+ session->removeParticipant(participant);
+ if (session->mHangupOnLastLeave && (id != gAgentID) && (session->mParticipantsByUUID.size() <= 1))
+ {
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+ }
+ }
+ }
+}
+
+
+// participantState level participant management
+LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) :
+ mURI(agent_id.asString()),
+ mAvatarID(agent_id),
+ mIsSpeaking(false),
+ mIsModeratorMuted(false),
+ mLevel(0.f),
+ mVolume(LLVoiceClient::VOLUME_DEFAULT)
+{
+}
+
+LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id)
+{
+ participantStatePtr_t result;
+
+ participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id);
+
+ if (iter != mParticipantsByUUID.end())
+ {
+ result = iter->second;
+ }
+
+ if(!result)
+ {
+ // participant isn't already in one list or the other.
+ result.reset(new participantState(agent_id));
+ mParticipantsByUUID.insert(participantUUIDMap::value_type(agent_id, result));
+ result->mAvatarID = agent_id;
+
+ LLWebRTCVoiceClient::getInstance()->lookupName(agent_id);
+
+ LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume);
+ if (!LLWebRTCVoiceClient::sShuttingDown)
+ {
+ LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers();
+ }
+
+ LL_DEBUGS("Voice") << "Participant \"" << result->mURI << "\" added." << LL_ENDL;
+ }
+
+ return result;
+}
+
+
+// session-level participant management
+
+LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
+{
+ participantStatePtr_t result;
+ participantUUIDMap::iterator iter = mParticipantsByUUID.find(id);
+
+ if(iter != mParticipantsByUUID.end())
+ {
+ result = iter->second;
+ }
+
+ return result;
+}
+
+void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant)
+{
+ if (participant)
+ {
+ participantUUIDMap::iterator iter = mParticipantsByUUID.find(participant->mAvatarID);
+
+ LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
+
+ if (iter == mParticipantsByUUID.end())
+ {
+ LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
+ }
+ else
+ {
+ mParticipantsByUUID.erase(iter);
+ if (!LLWebRTCVoiceClient::sShuttingDown)
+ {
+ LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers();
+ }
+ }
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::removeAllParticipants()
+{
+ LL_DEBUGS("Voice") << "called" << LL_ENDL;
+
+ while (!mParticipantsByUUID.empty())
+ {
+ removeParticipant(mParticipantsByUUID.begin()->second);
+ }
+}
+
+// Initiated the various types of sessions.
+bool LLWebRTCVoiceClient::startEstateSession()
+{
+ leaveChannel(false);
+ mNextSession = addSession("Estate", sessionState::ptr_t(new estateSessionState()));
+ return true;
+}
+
+bool LLWebRTCVoiceClient::startParcelSession(const std::string &channelID, S32 parcelID)
+{
+ leaveChannel(false);
+ mNextSession = addSession(channelID, sessionState::ptr_t(new parcelSessionState(channelID, parcelID)));
+ return true;
+}
+
+bool LLWebRTCVoiceClient::startAdHocSession(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave)
+{
+ leaveChannel(false);
+ LL_WARNS("Voice") << "Start AdHoc Session " << channelInfo << LL_ENDL;
+ std::string channelID = channelInfo["channel_uri"];
+ std::string credentials = channelInfo["channel_credentials"];
+ mNextSession = addSession(channelID,
+ sessionState::ptr_t(new adhocSessionState(channelID,
+ credentials,
+ notify_on_first_join,
+ hangup_on_last_leave)));
+ return true;
+}
+
+bool LLWebRTCVoiceClient::isVoiceWorking() const
+{
+ return mIsProcessingChannels;
+}
+
+// Returns true if calling back the session URI after the session has closed is possible.
+// Currently this will be false only for PSTN P2P calls.
+BOOL LLWebRTCVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
+{
+ sessionStatePtr_t session(findP2PSession(session_id));
+ return session && session->isCallbackPossible() ? TRUE : FALSE;
+}
+
+// Channel Management
+void LLWebRTCVoiceClient::leaveNonSpatialChannel()
+{
+ LL_DEBUGS("Voice") << "Request to leave non-spatial channel." << LL_ENDL;
+
+ // make sure we're not simply rejoining the current session
+ deleteSession(mNextSession);
+
+ leaveChannel(true);
+}
+
+// determine whether we're processing channels, or whether
+// another voice provider is.
+void LLWebRTCVoiceClient::processChannels(bool process)
+{
+ mProcessChannels = process;
+}
+
+bool LLWebRTCVoiceClient::inProximalChannel()
+{
+ return inSpatialChannel();
+}
+
+bool LLWebRTCVoiceClient::inOrJoiningChannel(const std::string& channelID)
+{
+ return (mSession && mSession->mChannelID == channelID) || (mNextSession && mNextSession->mChannelID == channelID);
+}
+
+bool LLWebRTCVoiceClient::inEstateChannel()
+{
+ return (mSession && mSession->isEstate()) || (mNextSession && mNextSession->isEstate());
+}
+
+bool LLWebRTCVoiceClient::inSpatialChannel()
+{
+ bool result = true;
+
+ if (mNextSession)
+ {
+ result = mNextSession->isSpatial();
+ }
+ else if(mSession)
+ {
+ result = mSession->isSpatial();
+ }
+
+ return result;
+}
+
+// retrieves information used to negotiate p2p, adhoc, and group
+// channels
+LLSD LLWebRTCVoiceClient::getAudioSessionChannelInfo()
+{
+ LLSD result;
+
+ if (mSession)
+ {
+ result["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE;
+ result["channel_uri"] = mSession->mChannelID;
+ }
+
+ return result;
+}
+
+void LLWebRTCVoiceClient::leaveChannel(bool stopTalking)
+{
+ if (mSession)
+ {
+ deleteSession(mSession);
+ }
+
+ if (mNextSession)
+ {
+ deleteSession(mNextSession);
+ }
+
+ // If voice was on, turn it off
+ if (stopTalking && LLVoiceClient::getInstance()->getUserPTTState())
+ {
+ LLVoiceClient::getInstance()->setUserPTTState(false);
+ }
+}
+
+bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo)
+{
+ if (!mProcessChannels || (channelInfo["voice_server_type"].asString() != WEBRTC_VOICE_SERVER_TYPE))
+ {
+ return false;
+ }
+
+ if (mSession)
+ {
+ if (!channelInfo["sessionHandle"].asString().empty())
+ {
+ return mSession->mHandle == channelInfo["session_handle"].asString();
+ }
+ return channelInfo["channel_uri"].asString() == mSession->mChannelID;
+ }
+ return false;
+}
+
+bool LLWebRTCVoiceClient::compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2)
+{
+ return (channelInfo1["voice_server_type"] == WEBRTC_VOICE_SERVER_TYPE) &&
+ (channelInfo1["voice_server_type"] == channelInfo2["voice_server_type"]) &&
+ (channelInfo1["sip_uri"] == channelInfo2["sip_uri"]);
+}
+
+
+//----------------------------------------------
+// Audio muting, volume, gain, etc.
+
+// we're muting the mic, so tell each session such
+void LLWebRTCVoiceClient::setMuteMic(bool muted)
+{
+ mMuteMic = muted;
+ // when you're hidden, your mic is always muted.
+ if (!mHidden)
+ {
+ sessionState::for_each(boost::bind(predSetMuteMic, _1, muted));
+ }
+}
+
+void LLWebRTCVoiceClient::predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool muted)
+{
+ participantStatePtr_t participant = session->findParticipantByID(gAgentID);
+ if (participant)
+ {
+ participant->mLevel = 0.0;
+ }
+ session->setMuteMic(muted);
+}
+
+void LLWebRTCVoiceClient::setVoiceVolume(F32 volume)
+{
+ if (volume != mSpeakerVolume)
+ {
+ {
+ mSpeakerVolume = volume;
+ }
+ sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume));
+ }
+}
+
+void LLWebRTCVoiceClient::predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume)
+{
+ session->setSpeakerVolume(volume);
+}
+
+void LLWebRTCVoiceClient::setMicGain(F32 gain)
+{
+ if (gain != mMicGain)
+ {
+ mMicGain = gain;
+ sessionState::for_each(boost::bind(predSetMicGain, _1, gain));
+ }
+}
+
+void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 gain)
+{
+ session->setMicGain(gain);
+}
+
+void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)
+{
+ LL_DEBUGS("Voice")
+ << "( " << (enabled ? "enabled" : "disabled") << " )"
+ << " was "<< (mVoiceEnabled ? "enabled" : "disabled")
+ << " coro "<< (mIsCoroutineActive ? "active" : "inactive")
+ << LL_ENDL;
+
+ if (enabled != mVoiceEnabled)
+ {
+ // TODO: Refactor this so we don't call into LLVoiceChannel, but simply
+ // use the status observer
+ mVoiceEnabled = enabled;
+ LLVoiceClientStatusObserver::EStatusType status;
+
+ if (enabled)
+ {
+ LL_DEBUGS("Voice") << "enabling" << LL_ENDL;
+ LLVoiceChannel::getCurrentVoiceChannel()->activate();
+ status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
+ mSpatialCoordsDirty = true;
+ updatePosition();
+ if (!mIsCoroutineActive)
+ {
+ LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro",
+ boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance()));
+ }
+ else
+ {
+ LL_DEBUGS("Voice") << "coro should be active.. not launching" << LL_ENDL;
+ }
+ }
+ else
+ {
+ // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it.
+ LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
+ gAgent.setVoiceConnected(false);
+ status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
+ cleanUp();
+ }
+
+ notifyStatusObservers(status);
+ }
+ else
+ {
+ LL_DEBUGS("Voice") << " no-op" << LL_ENDL;
+ }
+}
+
+
+/////////////////////////////
+// Accessors for data related to nearby speakers
+
+std::string LLWebRTCVoiceClient::getDisplayName(const LLUUID& id)
+{
+ std::string result;
+ if (mProcessChannels && mSession)
+ {
+ participantStatePtr_t participant(mSession->findParticipantByID(id));
+ if (participant)
+ {
+ result = participant->mDisplayName;
+ }
+ }
+ return result;
+}
+
+BOOL LLWebRTCVoiceClient::getIsSpeaking(const LLUUID& id)
+{
+ BOOL result = FALSE;
+ if (mProcessChannels && mSession)
+ {
+ participantStatePtr_t participant(mSession->findParticipantByID(id));
+ if (participant)
+ {
+ result = participant->mIsSpeaking;
+ }
+ }
+ return result;
+}
+
+// TODO: Need to pull muted status from the webrtc server
+BOOL LLWebRTCVoiceClient::getIsModeratorMuted(const LLUUID& id)
+{
+ BOOL result = FALSE;
+ if (mProcessChannels && mSession)
+ {
+ participantStatePtr_t participant(mSession->findParticipantByID(id));
+ if (participant)
+ {
+ result = participant->mIsModeratorMuted;
+ }
+ }
+ return result;
+}
+
+F32 LLWebRTCVoiceClient::getCurrentPower(const LLUUID &id)
+{
+ F32 result = 0.0;
+ if (!mProcessChannels || !mSession)
+ {
+ return result;
+ }
+ participantStatePtr_t participant(mSession->findParticipantByID(id));
+ if (participant)
+ {
+ if (participant->mIsSpeaking)
+ {
+ result = participant->mLevel;
+ }
+ }
+ return result;
+}
+
+// External accessors.
+F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id)
+{
+ // Minimum volume will be returned for users with voice disabled
+ F32 result = LLVoiceClient::VOLUME_MIN;
+
+ participantStatePtr_t participant(mSession->findParticipantByID(id));
+ if(participant)
+ {
+ result = participant->mVolume;
+ }
+
+ return result;
+}
+
+void LLWebRTCVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
+{
+ F32 clamped_volume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX);
+ if(mSession)
+ {
+ participantStatePtr_t participant(mSession->findParticipantByID(id));
+ if (participant && (participant->mAvatarID != gAgentID))
+ {
+ if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT))
+ {
+ // Store this volume setting for future sessions if it has been
+ // changed from the default
+ LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume);
+ }
+ else
+ {
+ // Remove stored volume setting if it is returned to the default
+ LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id);
+ }
+
+ participant->mVolume = clamped_volume;
+ }
+ }
+ sessionState::for_each(boost::bind(predSetUserVolume, _1, id, clamped_volume));
+}
+
+// set volume level (gain level) for another user.
+void LLWebRTCVoiceClient::predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID &id, F32 volume)
+{
+ session->setUserVolume(id, volume);
+}
+
+////////////////////////
+///LLMuteListObserver
+///
+
+void LLWebRTCVoiceClient::onChange()
+{
+}
+
+void LLWebRTCVoiceClient::onChangeDetailed(const LLMute& mute)
+{
+ if (mute.mType == LLMute::AGENT)
+ {
+ bool muted = ((mute.mFlags & LLMute::flagVoiceChat) == 0);
+ sessionState::for_each(boost::bind(predSetUserMute, _1, mute.mID, muted));
+ }
+}
+
+void LLWebRTCVoiceClient::predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID &id, bool mute)
+{
+ session->setUserMute(id, mute);
+}
+
+//------------------------------------------------------------------------
+// Sessions
+
+std::map<std::string, LLWebRTCVoiceClient::sessionState::ptr_t> LLWebRTCVoiceClient::sessionState::mSessions;
+
+
+LLWebRTCVoiceClient::sessionState::sessionState() :
+ mHangupOnLastLeave(false),
+ mNotifyOnFirstJoin(false),
+ mMicGain(1.0),
+ mMuted(false),
+ mSpeakerVolume(1.0),
+ mShuttingDown(false)
+{
+}
+// ------------------------------------------------------------------
+// Predicates, for calls to all sessions
+
+void LLWebRTCVoiceClient::predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level)
+{
+ participantStatePtr_t participant = session->findParticipantByID(gAgentID);
+ if (participant)
+ {
+ participant->mLevel = audio_level;
+ // TODO: Add VAD for our own voice.
+ participant->mIsSpeaking = audio_level > SPEAKING_AUDIO_LEVEL;
+ }
+}
+
+void LLWebRTCVoiceClient::predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string &spatial_data)
+{
+ if (session->isSpatial() && !spatial_data.empty())
+ {
+ session->sendData(spatial_data);
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::sendData(const std::string &data)
+{
+ for (auto &connection : mWebRTCConnections)
+ {
+ connection->sendData(data);
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::setMuteMic(bool muted)
+{
+ mMuted = muted;
+ for (auto &connection : mWebRTCConnections)
+ {
+ connection->setMuteMic(muted);
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::setMicGain(F32 gain)
+{
+ mMicGain = gain;
+ for (auto &connection : mWebRTCConnections)
+ {
+ connection->setMicGain(gain);
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::setSpeakerVolume(F32 volume)
+{
+ mSpeakerVolume = volume;
+ for (auto &connection : mWebRTCConnections)
+ {
+ connection->setSpeakerVolume(volume);
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::setUserVolume(const LLUUID &id, F32 volume)
+{
+ if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end())
+ {
+ return;
+ }
+ for (auto &connection : mWebRTCConnections)
+ {
+ connection->setUserVolume(id, volume);
+ }
+}
+
+void LLWebRTCVoiceClient::sessionState::setUserMute(const LLUUID &id, bool mute)
+{
+ if (mParticipantsByUUID.find(id) == mParticipantsByUUID.end())
+ {
+ return;
+ }
+ for (auto &connection : mWebRTCConnections)
+ {
+ connection->setUserMute(id, mute);
+ }
+}
+/*static*/
+void LLWebRTCVoiceClient::sessionState::addSession(
+ const std::string & channelID,
+ LLWebRTCVoiceClient::sessionState::ptr_t& session)
+{
+ mSessions[channelID] = session;
+}
+
+LLWebRTCVoiceClient::sessionState::~sessionState()
+{
+ LL_DEBUGS("Voice") << "Destroying session CHANNEL=" << mChannelID << LL_ENDL;
+
+ removeAllParticipants();
+}
+
+/*static*/
+LLWebRTCVoiceClient::sessionState::ptr_t LLWebRTCVoiceClient::sessionState::matchSessionByChannelID(const std::string& channel_id)
+{
+ sessionStatePtr_t result;
+
+ // *TODO: My kingdom for a lambda!
+ std::map<std::string, ptr_t>::iterator it = mSessions.find(channel_id);
+ if (it != mSessions.end())
+ {
+ result = (*it).second;
+ }
+ return result;
+}
+
+void LLWebRTCVoiceClient::sessionState::for_each(sessionFunc_t func)
+{
+ std::for_each(mSessions.begin(), mSessions.end(), boost::bind(for_eachPredicate, _1, func));
+}
+
+void LLWebRTCVoiceClient::sessionState::reapEmptySessions()
+{
+ std::map<std::string, ptr_t>::iterator iter;
+ for (iter = mSessions.begin(); iter != mSessions.end();)
+ {
+ if (iter->second->isEmpty())
+ {
+ iter = mSessions.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+}
+
+/*static*/
+void LLWebRTCVoiceClient::sessionState::for_eachPredicate(const std::pair<std::string, LLWebRTCVoiceClient::sessionState::wptr_t> &a, sessionFunc_t func)
+{
+ ptr_t aLock(a.second.lock());
+
+ if (aLock)
+ func(aLock);
+ else
+ {
+ LL_WARNS("Voice") << "Stale handle in session map!" << LL_ENDL;
+ }
+}
+
+LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::addSession(const std::string &channel_id, sessionState::ptr_t session)
+{
+ sessionStatePtr_t existingSession = sessionState::matchSessionByChannelID(channel_id);
+ if (!existingSession)
+ {
+ // No existing session found.
+
+ LL_DEBUGS("Voice") << "adding new session with channel: " << channel_id << LL_ENDL;
+ session->setMuteMic(mMuteMic);
+ session->setMicGain(mMicGain);
+ session->setSpeakerVolume(mSpeakerVolume);
+
+ sessionState::addSession(channel_id, session);
+ return session;
+ }
+ else
+ {
+ // Found an existing session
+ LL_DEBUGS("Voice") << "Attempting to add already-existing session " << channel_id << LL_ENDL;
+ existingSession->revive();
+
+ return existingSession;
+ }
+}
+
+LLWebRTCVoiceClient::sessionStatePtr_t LLWebRTCVoiceClient::findP2PSession(const LLUUID &agent_id)
+{
+ sessionStatePtr_t result = sessionState::matchSessionByChannelID(agent_id.asString());
+ if (result && !result->isSpatial())
+ {
+ return result;
+ }
+
+ result.reset();
+ return result;
+}
+
+void LLWebRTCVoiceClient::sessionState::shutdownAllConnections()
+{
+ mShuttingDown = true;
+ for (auto &&connection : mWebRTCConnections)
+ {
+ connection->shutDown();
+ }
+}
+
+// in case we drop into a session (spatial, etc.) right after
+// telling the session to shut down, revive it so it reconnects.
+void LLWebRTCVoiceClient::sessionState::revive()
+{
+ mShuttingDown = false;
+}
+
+//=========================================================================
+// the following are methods to support the coroutine implementation of the
+// voice connection and processing. They should only be called in the context
+// of a coroutine.
+//
+//
+
+void LLWebRTCVoiceClient::sessionState::processSessionStates()
+{
+ auto iter = mSessions.begin();
+ while (iter != mSessions.end())
+ {
+ if (!iter->second->processConnectionStates() && iter->second->mShuttingDown)
+ {
+ // if the connections associated with a session are gone,
+ // and this session is shutting down, remove it.
+ iter = mSessions.erase(iter);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+}
+
+// process the states on each connection associated with a session.
+bool LLWebRTCVoiceClient::sessionState::processConnectionStates()
+{
+ std::list<connectionPtr_t>::iterator iter = mWebRTCConnections.begin();
+ while (iter != mWebRTCConnections.end())
+ {
+ if (!iter->get()->connectionStateMachine())
+ {
+ // if the state machine returns false, the connection is shut down
+ // so delete it.
+ iter = mWebRTCConnections.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ return !mWebRTCConnections.empty();
+}
+
+// processing of spatial voice connection states requires special handling.
+// as neighboring regions need to be started up or shut down depending
+// on our location.
+bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates()
+{
+ if (!mShuttingDown)
+ {
+ // Estate voice requires connection to neighboring regions.
+ std::set<LLUUID> neighbor_ids = LLWebRTCVoiceClient::getInstance()->getNeighboringRegions();
+
+ for (auto &connection : mWebRTCConnections)
+ {
+ boost::shared_ptr<LLVoiceWebRTCSpatialConnection> spatialConnection =
+ boost::static_pointer_cast<LLVoiceWebRTCSpatialConnection>(connection);
+
+ LLUUID regionID = spatialConnection.get()->getRegionID();
+
+ if (neighbor_ids.find(regionID) == neighbor_ids.end())
+ {
+ // shut down connections to neighbors that are too far away.
+ spatialConnection.get()->shutDown();
+ }
+ neighbor_ids.erase(regionID);
+ }
+
+ // add new connections for new neighbors
+ for (auto &neighbor : neighbor_ids)
+ {
+ connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID));
+
+ mWebRTCConnections.push_back(connection);
+ connection->setMicGain(mMicGain);
+ connection->setMuteMic(mMuted);
+ connection->setSpeakerVolume(mSpeakerVolume);
+ }
+ }
+ return LLWebRTCVoiceClient::sessionState::processConnectionStates();
+}
+
+// Various session state constructors.
+
+LLWebRTCVoiceClient::estateSessionState::estateSessionState()
+{
+ mHangupOnLastLeave = false;
+ mNotifyOnFirstJoin = false;
+ mChannelID = "Estate";
+ LLUUID region_id = gAgent.getRegion()->getRegionID();
+
+ mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, INVALID_PARCEL_ID, "Estate"));
+}
+
+LLWebRTCVoiceClient::parcelSessionState::parcelSessionState(const std::string &channelID, S32 parcel_local_id)
+{
+ mHangupOnLastLeave = false;
+ mNotifyOnFirstJoin = false;
+ LLUUID region_id = gAgent.getRegion()->getRegionID();
+ mChannelID = channelID;
+ mWebRTCConnections.emplace_back(new LLVoiceWebRTCSpatialConnection(region_id, parcel_local_id, channelID));
+}
+
+LLWebRTCVoiceClient::adhocSessionState::adhocSessionState(const std::string &channelID,
+ const std::string &credentials,
+ bool notify_on_first_join,
+ bool hangup_on_last_leave) :
+ mCredentials(credentials)
+{
+ mHangupOnLastLeave = hangup_on_last_leave;
+ mNotifyOnFirstJoin = notify_on_first_join;
+ LLUUID region_id = gAgent.getRegion()->getRegionID();
+ mChannelID = channelID;
+ mWebRTCConnections.emplace_back(new LLVoiceWebRTCAdHocConnection(region_id, channelID, credentials));
+}
+
+void LLWebRTCVoiceClient::predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t& session)
+{
+ session->shutdownAllConnections();
+}
+
+void LLWebRTCVoiceClient::deleteSession(const sessionStatePtr_t &session)
+{
+ if (!session)
+ {
+ return;
+ }
+
+ // At this point, the session should be unhooked from all lists and all state should be consistent.
+ session->shutdownAllConnections();
+ // If this is the current audio session, clean up the pointer which will soon be dangling.
+ bool deleteAudioSession = mSession == session;
+ bool deleteNextAudioSession = mNextSession == session;
+ if (deleteAudioSession)
+ {
+ mSession.reset();
+ }
+
+ // ditto for the next audio session
+ if (deleteNextAudioSession)
+ {
+ mNextSession.reset();
+ }
+}
+
+
+// Name resolution
+void LLWebRTCVoiceClient::lookupName(const LLUUID &id)
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLWebRTCVoiceClient::onAvatarNameCache, this, _1, _2));
+}
+
+void LLWebRTCVoiceClient::onAvatarNameCache(const LLUUID& agent_id,
+ const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
+ std::string display_name = av_name.getDisplayName();
+ avatarNameResolved(agent_id, display_name);
+}
+
+void LLWebRTCVoiceClient::predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name)
+{
+ participantStatePtr_t participant(session->findParticipantByID(id));
+ if (participant)
+ {
+ // Found -- fill in the name
+ participant->mDisplayName = name;
+ // and post a "participants updated" message to listeners later.
+ LLWebRTCVoiceClient::getInstance()->notifyParticipantObservers();
+ }
+}
+
+void LLWebRTCVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
+{
+ sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name));
+}
+
+// Leftover from vivox PTSN
+std::string LLWebRTCVoiceClient::sipURIFromID(const LLUUID& id)
+{
+ return id.asString();
+}
+
+
+/////////////////////////////
+// LLVoiceWebRTCConnection
+// These connections manage state transitions, negotiating webrtc connections,
+// and other such things for a single connection to a Secondlife WebRTC server.
+// Multiple of these connections may be active at once, in the case of
+// cross-region voice, or when a new connection is being created before the old
+// has a chance to shut down.
+LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID &regionID, const std::string &channelID) :
+ mWebRTCAudioInterface(nullptr),
+ mWebRTCDataInterface(nullptr),
+ mVoiceConnectionState(VOICE_STATE_START_SESSION),
+ mMuted(true),
+ mShutDown(false),
+ mTrickling(false),
+ mIceCompleted(false),
+ mSpeakerVolume(0.0),
+ mMicGain(0.0),
+ mOutstandingRequests(0),
+ mChannelID(channelID),
+ mRegionID(regionID)
+{
+ mWebRTCPeerConnectionInterface = llwebrtc::newPeerConnection();
+ mWebRTCPeerConnectionInterface->setSignalingObserver(this);
+}
+
+LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection()
+{
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ // peer connection and observers will be cleaned up
+ // by llwebrtc::terminate() on shutdown.
+ return;
+ }
+ if (mWebRTCPeerConnectionInterface)
+ {
+ llwebrtc::freePeerConnection(mWebRTCPeerConnectionInterface);
+ mWebRTCPeerConnectionInterface = nullptr;
+ }
+}
+
+
+// ICE (Interactive Connectivity Establishment)
+// When WebRTC tries to negotiate a connection to the Secondlife WebRTC Server,
+// the negotiation will result in a few updates about the best path
+// to which to connect.
+// The Secondlife servers are configured for ICE trickling, where, after a session is partially
+// negotiated, updates about the best connectivity paths may trickle in. These need to be
+// sent to the Secondlife WebRTC server via the simulator so that both sides have a clear
+// view of the network environment.
+void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state)
+{
+ LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL;
+
+ switch (state)
+ {
+ case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_COMPLETE:
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+ mIceCompleted = true;
+ break;
+ }
+ case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW:
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+ mIceCompleted = false;
+ }
+ default:
+ break;
+ }
+}
+
+void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate)
+{
+ LLMutexLock lock(&mVoiceStateMutex);
+ mIceCandidates.push_back(candidate);
+}
+
+void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result)
+{
+ mOutstandingRequests--;
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ return;
+ }
+ mTrickling = false;
+}
+
+void LLVoiceWebRTCConnection::onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result)
+{
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ mOutstandingRequests--;
+ return;
+ }
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
+
+ if (retries >= 0)
+ {
+ LL_WARNS("Voice") << "Unable to complete ice trickling voice account, retrying. " << result << LL_ENDL;
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCConnection::onIceUpdateComplete, this, ice_completed, _1),
+ boost::bind(&LLVoiceWebRTCConnection::onIceUpdateError, this, retries - 1, url, body, ice_completed, _1));
+ return;
+ }
+
+ LL_WARNS("Voice") << "Unable to complete ice trickling voice account, restarting connection. " << result << LL_ENDL;
+ if (!mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);
+ }
+ mTrickling = false;
+
+ mOutstandingRequests--;
+}
+
+
+// Ice candidates may be streamed in before or after the SDP offer is available (see below)
+// This function determines whether candidates are available to send to the Secondlife WebRTC
+// server via the simulator. If so, and there are no more candidates, this code
+// will make the cap call to the server sending up the ICE candidates.
+void LLVoiceWebRTCConnection::processIceUpdates()
+{
+ if (mShutDown || LLWebRTCVoiceClient::isShuttingDown())
+ {
+ return;
+ }
+
+ bool iceCompleted = false;
+ LLSD body;
+ {
+ if (!mTrickling)
+ {
+ if (!mIceCandidates.empty() || mIceCompleted)
+ {
+ LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID);
+ if (!regionp || !regionp->capabilitiesReceived())
+ {
+ LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL;
+ return;
+ }
+
+ std::string url = regionp->getCapability("VoiceSignalingRequest");
+ if (url.empty())
+ {
+ return;
+ }
+
+ LL_DEBUGS("Voice") << "region ready to complete voice signaling; url=" << url << LL_ENDL;
+ if (!mIceCandidates.empty())
+ {
+ LLSD candidates = LLSD::emptyArray();
+ for (auto &ice_candidate : mIceCandidates)
+ {
+ LLSD body_candidate;
+ body_candidate["sdpMid"] = ice_candidate.mSdpMid;
+ body_candidate["sdpMLineIndex"] = ice_candidate.mMLineIndex;
+ body_candidate["candidate"] = ice_candidate.mCandidate;
+ candidates.append(body_candidate);
+ }
+ body["candidates"] = candidates;
+ mIceCandidates.clear();
+ }
+ else if (mIceCompleted)
+ {
+ LLSD body_candidate;
+ body_candidate["completed"] = true;
+ body["candidate"] = body_candidate;
+ iceCompleted = mIceCompleted;
+ mIceCompleted = false;
+ }
+
+ body["viewer_session"] = mViewerSession;
+ body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE;
+
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(
+ new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateComplete, this, iceCompleted, _1),
+ boost::bind(&LLVoiceWebRTCSpatialConnection::onIceUpdateError, this, 3, url, body, iceCompleted, _1));
+ mOutstandingRequests++;
+ mTrickling = true;
+ }
+ }
+ }
+}
+
+
+// An 'Offer' comes in the form of a SDP (Session Description Protocol)
+// which contains all sorts of info about the session, from network paths
+// to the type of session (audio, video) to characteristics (the encoder type.)
+// This SDP also serves as the 'ticket' to the server, security-wise.
+// The Offer is retrieved from the WebRTC library on the client,
+// and is passed to the simulator via a CAP, which then passes
+// it on to the Secondlife WebRTC server.
+
+void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp)
+{
+ LL_DEBUGS("Voice") << "On Offer Available." << LL_ENDL;
+ LLMutexLock lock(&mVoiceStateMutex);
+ mChannelSDP = sdp;
+ if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_SESSION_START)
+ {
+ mVoiceConnectionState = VOICE_STATE_REQUEST_CONNECTION;
+ }
+}
+
+// Notifications from the webrtc library.
+void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface)
+{
+ LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL;
+ mWebRTCAudioInterface = audio_interface;
+ setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED);
+}
+
+void LLVoiceWebRTCConnection::OnRenegotiationNeeded()
+{
+ LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL;
+ if (!mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);
+ }
+}
+
+void LLVoiceWebRTCConnection::OnPeerConnectionShutdown()
+{
+ setVoiceConnectionState(VOICE_STATE_SESSION_EXIT);
+ mOutstandingRequests--; // shut down is an async call which is handled on a webrtc thread.
+}
+
+void LLVoiceWebRTCConnection::setMuteMic(bool muted)
+{
+ mMuted = muted;
+ if (mWebRTCAudioInterface)
+ {
+ mWebRTCAudioInterface->setMute(muted);
+ }
+}
+
+void LLVoiceWebRTCConnection::setMicGain(F32 gain)
+{
+ mMicGain = gain;
+ if (mWebRTCAudioInterface)
+ {
+ mWebRTCAudioInterface->setSendVolume(gain);
+ }
+}
+
+void LLVoiceWebRTCConnection::setSpeakerVolume(F32 volume)
+{
+ mSpeakerVolume = volume;
+ if (mWebRTCAudioInterface)
+ {
+ mWebRTCAudioInterface->setReceiveVolume(volume);
+ }
+}
+
+void LLVoiceWebRTCConnection::setUserVolume(const LLUUID& id, F32 volume)
+{
+ Json::Value root = Json::objectValue;
+ Json::Value user_gain = Json::objectValue;
+ user_gain[id.asString()] = (uint32_t)(volume*200); // give it two decimal places with a range from 0-200, where 100 is normal
+ root["ug"] = user_gain;
+ Json::FastWriter writer;
+ std::string json_data = writer.write(root);
+ mWebRTCDataInterface->sendData(json_data, false);
+}
+
+void LLVoiceWebRTCConnection::setUserMute(const LLUUID& id, bool mute)
+{
+ Json::Value root = Json::objectValue;
+ Json::Value muted = Json::objectValue;
+ muted[id.asString()] = mute;
+ root["m"] = muted;
+ Json::FastWriter writer;
+ std::string json_data = writer.write(root);
+ mWebRTCDataInterface->sendData(json_data, false);
+}
+
+
+// Send data to the Secondlife WebRTC server via the webrtc
+// data channel.
+void LLVoiceWebRTCConnection::sendData(const std::string &data)
+{
+ if (getVoiceConnectionState() == VOICE_STATE_SESSION_UP && mWebRTCDataInterface)
+ {
+ mWebRTCDataInterface->sendData(data, false);
+ }
+}
+
+// Tell the simulator that we're shutting down a voice connection.
+// The simulator will pass this on to the Secondlife WebRTC server.
+bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait)
+{
+ LL_DEBUGS("Voice") << "Disconnecting voice." << LL_ENDL;
+ if (mWebRTCDataInterface)
+ {
+ mWebRTCDataInterface->unsetDataObserver(this);
+ mWebRTCDataInterface = nullptr;
+ }
+ mWebRTCAudioInterface = nullptr;
+ LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID);
+ if (!regionp || !regionp->capabilitiesReceived())
+ {
+ LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL;
+ setVoiceConnectionState(VOICE_STATE_SESSION_EXIT);
+ return false;
+ }
+
+ std::string url = regionp->getCapability("ProvisionVoiceAccountRequest");
+ if (url.empty())
+ {
+ return false;
+ }
+
+ LL_DEBUGS("Voice") << "region ready for voice break; url=" << url << LL_ENDL;
+
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+
+ LLVoiceWebRTCStats::getInstance()->provisionAttemptStart();
+ LLSD body;
+ body["logout"] = TRUE;
+ body["viewer_session"] = mViewerSession;
+ body["voice_server_type"] = REPORTED_VOICE_SERVER_TYPE;
+
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1),
+ boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, 3, body, _1));
+ setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT);
+ mOutstandingRequests++;
+ return true;
+}
+
+void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result)
+{
+ mOutstandingRequests--;
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ return;
+ }
+
+ if (mWebRTCPeerConnectionInterface)
+ {
+ if (mWebRTCPeerConnectionInterface->shutdownConnection())
+ {
+ mOutstandingRequests++;
+ }
+ }
+ else
+ {
+ setVoiceConnectionState(VOICE_STATE_SESSION_EXIT);
+ }
+}
+
+void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result)
+{
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ mOutstandingRequests--;
+ return;
+ }
+ if (retries >= 0)
+ {
+ // retry a few times.
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestSuccess, this, _1),
+ boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1));
+ return;
+ }
+ if (mWebRTCPeerConnectionInterface)
+ {
+ mOutstandingRequests++;
+ mWebRTCPeerConnectionInterface->shutdownConnection();
+ }
+ else
+ {
+ setVoiceConnectionState(VOICE_STATE_SESSION_EXIT);
+ }
+ mOutstandingRequests--;
+}
+
+
+// Tell the simulator to tell the Secondlife WebRTC server that we want a voice
+// connection. The SDP is sent up as part of this, and the simulator will respond
+// with an 'answer' which is in the form of another SDP. The webrtc library
+// will use the offer and answer to negotiate the session.
+bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection()
+{
+ LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID);
+
+ LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL;
+ if (!regionp || !regionp->capabilitiesReceived())
+ {
+ LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL;
+ return false;
+ }
+
+ std::string url = regionp->getCapability("ProvisionVoiceAccountRequest");
+ if (url.empty())
+ {
+ return false;
+ }
+
+ LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL;
+
+ LLVoiceWebRTCStats::getInstance()->provisionAttemptStart();
+ LLSD body;
+ LLSD jsep;
+ jsep["type"] = "offer";
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+ jsep["sdp"] = mChannelSDP;
+ }
+ body["jsep"] = jsep;
+ if (mParcelLocalID != INVALID_PARCEL_ID)
+ {
+ body["parcel_local_id"] = mParcelLocalID;
+ }
+
+ body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE;
+
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestSuccess, this, _1),
+ boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1));
+ mOutstandingRequests++;
+ return true;
+}
+
+void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result)
+{
+ mOutstandingRequests--;
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ return;
+ }
+ LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true);
+
+ if (result.has("viewer_session") && result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" &&
+ result["jsep"].has("sdp"))
+ {
+ mRemoteChannelSDP = result["jsep"]["sdp"].asString();
+ mViewerSession = result["viewer_session"];
+ }
+ else
+ {
+ LL_WARNS("Voice") << "Invalid voice provision request result:" << result << LL_ENDL;
+ setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);
+ return;
+ }
+
+ LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response"
+ << " channel sdp " << mRemoteChannelSDP << LL_ENDL;
+
+ mWebRTCPeerConnectionInterface->AnswerAvailable(mRemoteChannelSDP);
+}
+
+
+void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result)
+{
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ mOutstandingRequests--;
+ return;
+ }
+ if (retries >= 0)
+ {
+ LL_WARNS("Voice") << "Failure connecting to voice, retrying." << body << " RESULT: " << result << LL_ENDL;
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess, this, _1),
+ boost::bind(&LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure, this, url, retries - 1, body, _1));
+ return;
+ }
+ LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL;
+ setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);
+ mOutstandingRequests--;
+}
+
+
+// Primary state machine for negotiating a single voice connection to the
+// Secondlife WebRTC server.
+bool LLVoiceWebRTCConnection::connectionStateMachine()
+{
+ processIceUpdates();
+
+ switch (getVoiceConnectionState())
+ {
+ case VOICE_STATE_START_SESSION:
+ {
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ break;
+ }
+ mTrickling = false;
+ mIceCompleted = false;
+ setVoiceConnectionState(VOICE_STATE_WAIT_FOR_SESSION_START);
+ // tell the webrtc library that we want a connection. The library will
+ // respond with an offer on a separate thread, which will cause
+ // the session state to change.
+ if (!mWebRTCPeerConnectionInterface->initializeConnection())
+ {
+ setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);
+ }
+ break;
+ }
+
+ case VOICE_STATE_WAIT_FOR_SESSION_START:
+ {
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ }
+ break;
+ }
+
+ case VOICE_STATE_REQUEST_CONNECTION:
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ break;
+ }
+ // Ask the sim to ask the Secondlife WebRTC server for a connection to
+ // a given voice channel. On completion, we'll move on to the
+ // VOICE_STATE_SESSION_ESTABLISHED via a callback on a webrtc thread.
+ if (!requestVoiceConnection())
+ {
+ setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);
+ }
+ else
+ {
+ setVoiceConnectionState(VOICE_STATE_CONNECTION_WAIT);
+ }
+ break;
+
+ case VOICE_STATE_CONNECTION_WAIT:
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ }
+ break;
+
+ case VOICE_STATE_SESSION_ESTABLISHED:
+ {
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ break;
+ }
+ // update the peer connection with the various characteristics of
+ // this connection.
+ mWebRTCAudioInterface->setMute(mMuted);
+ mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume);
+ mWebRTCAudioInterface->setSendVolume(mMicGain);
+ LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID);
+ setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL);
+ break;
+ }
+
+ case VOICE_STATE_WAIT_FOR_DATA_CHANNEL:
+ {
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ break;
+ }
+ if (mWebRTCDataInterface) // the interface will be set when the session is negotiated.
+ {
+ sendJoin(); // tell the Secondlife WebRTC server that we're here via the data channel.
+ setVoiceConnectionState(VOICE_STATE_SESSION_UP);
+ }
+ break;
+ }
+
+ case VOICE_STATE_SESSION_UP:
+ {
+ // we'll stay here as long as the session remains up.
+ if (mShutDown)
+ {
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ }
+ break;
+ }
+
+ case VOICE_STATE_SESSION_RETRY:
+ // something went wrong, so notify that the connection has failed.
+ LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID);
+ setVoiceConnectionState(VOICE_STATE_DISCONNECT);
+ break;
+
+ case VOICE_STATE_DISCONNECT:
+ breakVoiceConnection(true);
+ break;
+
+ case VOICE_STATE_WAIT_FOR_EXIT:
+ break;
+
+ case VOICE_STATE_SESSION_EXIT:
+ {
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+ if (!mShutDown)
+ {
+ mVoiceConnectionState = VOICE_STATE_START_SESSION;
+ }
+ else
+ {
+ // if we still have outstanding http or webrtc calls, wait for them to
+ // complete so we don't delete objects while they still may be used.
+ if (mOutstandingRequests <= 0)
+ {
+ LLWebRTCVoiceClient::getInstance()->OnConnectionShutDown(mChannelID, mRegionID);
+ return false;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ LL_WARNS("Voice") << "Unknown voice control state " << getVoiceConnectionState() << LL_ENDL;
+ return false;
+ }
+ }
+ return true;
+}
+
+// Data has been received on the webrtc data channel
+void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binary)
+{
+ // incoming data will be a json structure (if it's not binary.) We may pack
+ // binary for size reasons. Most of the keys in the json objects are
+ // single or double characters for size reasons.
+ // The primary element is:
+ // An object where each key is an agent id. (in the future, we may allow
+ // integer indices into an agentid list, populated on join commands. For size.
+ // Each key will point to a json object with keys identifying what's updated.
+ // 'p' - audio source power (level/volume) (int8 as int)
+ // 'j' - object of join data (currently only a boolean 'p' marking a primary participant)
+ // 'l' - boolean, always true if exists.
+ // 'v' - boolean - voice activity has been detected.
+
+ if (binary)
+ {
+ LL_WARNS("Voice") << "Binary data received from data channel." << LL_ENDL;
+ return;
+ }
+
+ Json::Reader reader;
+ Json::Value voice_data;
+ if (reader.parse(data, voice_data, false)) // don't collect comments
+ {
+ if (!voice_data.isObject())
+ {
+ LL_WARNS("Voice") << "Expected object from data channel:" << data << LL_ENDL;
+ return;
+ }
+ bool new_participant = false;
+ Json::Value mute = Json::objectValue;
+ Json::Value user_gain = Json::objectValue;
+ for (auto &participant_id : voice_data.getMemberNames())
+ {
+ LLUUID agent_id(participant_id);
+ if (agent_id.isNull())
+ {
+ LL_WARNS("Voice") << "Bad participant ID from data channel (" << participant_id << "):" << data << LL_ENDL;
+ continue;
+ }
+
+ LLWebRTCVoiceClient::participantStatePtr_t participant =
+ LLWebRTCVoiceClient::getInstance()->findParticipantByID(mChannelID, agent_id);
+ bool joined = false;
+ bool primary = false; // we ignore any 'joins' reported about participants
+ // that come from voice servers that aren't their primary
+ // voice server. This will happen with cross-region voice
+ // where a participant on a neighboring region may be
+ // connected to multiple servers. We don't want to
+ // add new identical participants from all of those servers.
+ if (voice_data[participant_id].isMember("j"))
+ {
+ // a new participant has announced that they're joining.
+ joined = true;
+ primary = voice_data[participant_id]["j"].get("p", Json::Value(false)).asBool();
+
+ // track incoming participants that are muted so we can mute their connections (or set their volume)
+ bool isMuted = LLMuteList::getInstance()->isMuted(agent_id, LLMute::flagVoiceChat);
+ if (isMuted)
+ {
+ mute[participant_id] = true;
+ }
+ F32 volume;
+ if(LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(agent_id, volume))
+ {
+ user_gain[participant_id] = (uint32_t)(volume * 200);
+ }
+ }
+
+ new_participant |= joined;
+ if (!participant && joined && (primary || !isSpatial()))
+ {
+ participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id);
+ }
+
+ if (participant)
+ {
+ if (voice_data[participant_id].get("l", Json::Value(false)).asBool())
+ {
+ // an existing participant is leaving.
+ if (agent_id != gAgentID)
+ {
+ LLWebRTCVoiceClient::getInstance()->removeParticipantByID(mChannelID, agent_id);
+ }
+ }
+ else
+ {
+ // we got a 'power' update.
+ F32 level = (F32) (voice_data[participant_id].get("p", Json::Value(participant->mLevel)).asInt()) / 128;
+ // convert to decibles
+ participant->mLevel = level;
+
+ if (voice_data[participant_id].isMember("v"))
+ {
+ participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool();
+ }
+ }
+ }
+ }
+
+ // tell the simulator to set the mute and volume data for this
+ // participant, if there are any updates.
+ Json::FastWriter writer;
+ Json::Value root = Json::objectValue;
+ if (mute.size() > 0)
+ {
+ root["m"] = mute;
+ }
+ if (user_gain.size() > 0)
+ {
+ root["ug"] = user_gain;
+ }
+ if (root.size() > 0)
+ {
+ std::string json_data = writer.write(root);
+ mWebRTCDataInterface->sendData(json_data, false);
+ }
+ }
+}
+
+void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface)
+{
+ if (data_interface)
+ {
+ mWebRTCDataInterface = data_interface;
+ mWebRTCDataInterface->setDataObserver(this);
+ }
+}
+
+// tell the Secondlife WebRTC server that
+// we're joining and whether we're
+// joining a server associated with the
+// the region we currently occupy or not (primary)
+// The WebRTC voice server will pass this info
+// to peers.
+void LLVoiceWebRTCConnection::sendJoin()
+{
+ Json::FastWriter writer;
+ Json::Value root = Json::objectValue;
+ Json::Value join_obj = Json::objectValue;
+ LLUUID regionID = gAgent.getRegion()->getRegionID();
+ if ((regionID == mRegionID) || !isSpatial())
+ {
+ join_obj["p"] = true;
+ }
+ root["j"] = join_obj;
+ std::string json_data = writer.write(root);
+ mWebRTCDataInterface->sendData(json_data, false);
+}
+
+/////////////////////////////
+// WebRTC Spatial Connection
+
+LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID &regionID,
+ S32 parcelLocalID,
+ const std::string &channelID) :
+ LLVoiceWebRTCConnection(regionID, channelID),
+ mParcelLocalID(parcelLocalID)
+{
+}
+
+LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection()
+{
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ // peer connection and observers will be cleaned up
+ // by llwebrtc::terminate() on shutdown.
+ return;
+ }
+ assert(mOutstandingRequests == 0);
+ mWebRTCPeerConnectionInterface->unsetSignalingObserver(this);
+}
+
+void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted)
+{
+ if (mMuted != muted)
+ {
+ mMuted = muted;
+ if (mWebRTCAudioInterface)
+ {
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp && mRegionID == regionp->getRegionID())
+ {
+ mWebRTCAudioInterface->setMute(muted);
+ }
+ else
+ {
+ // Always mute this agent with respect to neighboring regions.
+ // Peers don't want to hear this agent from multiple regions
+ // as that'll echo.
+ mWebRTCAudioInterface->setMute(true);
+ }
+ }
+ }
+}
+
+/////////////////////////////
+// WebRTC Spatial Connection
+
+LLVoiceWebRTCAdHocConnection::LLVoiceWebRTCAdHocConnection(const LLUUID &regionID,
+ const std::string& channelID,
+ const std::string& credentials) :
+ LLVoiceWebRTCConnection(regionID, channelID),
+ mCredentials(credentials)
+{
+}
+
+LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection()
+{
+ if (LLWebRTCVoiceClient::isShuttingDown())
+ {
+ // peer connection and observers will be cleaned up
+ // by llwebrtc::terminate() on shutdown.
+ return;
+ }
+ assert(mOutstandingRequests == 0);
+ mWebRTCPeerConnectionInterface->unsetSignalingObserver(this);
+}
+
+// Add-hoc connections require a different channel type
+// as they go to a different set of Secondlife WebRTC servers.
+// They also require credentials for the given channels.
+// So, we have a separate requestVoiceConnection call.
+bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection()
+{
+ LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID);
+
+ LL_DEBUGS("Voice") << "Requesting voice connection." << LL_ENDL;
+ if (!regionp || !regionp->capabilitiesReceived())
+ {
+ LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL;
+ return false;
+ }
+
+ std::string url = regionp->getCapability("ProvisionVoiceAccountRequest");
+ if (url.empty())
+ {
+ return false;
+ }
+
+ LLVoiceWebRTCStats::getInstance()->provisionAttemptStart();
+ LLSD body;
+ LLSD jsep;
+ jsep["type"] = "offer";
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+ jsep["sdp"] = mChannelSDP;
+ }
+ body["jsep"] = jsep;
+ body["credentials"] = mCredentials;
+ body["channel"] = mChannelID;
+ body["channel_type"] = "multiagent";
+ body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE;
+
+ LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(
+ url,
+ LLCore::HttpRequest::DEFAULT_POLICY_ID,
+ body,
+ boost::bind(&LLVoiceWebRTCAdHocConnection::OnVoiceConnectionRequestSuccess, this, _1),
+ boost::bind(&LLVoiceWebRTCAdHocConnection::OnVoiceConnectionRequestFailure, this, url, 3, body, _1));
+ mOutstandingRequests++;
+ return true;
+}
diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h
new file mode 100644
index 0000000000..03bbe00162
--- /dev/null
+++ b/indra/newview/llvoicewebrtc.h
@@ -0,0 +1,745 @@
+/**
+ * @file llvoicewebrtc.h
+ * @brief Declaration of LLWebRTCVoiceClient class which is the interface to the voice client process.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#ifndef LL_VOICE_WEBRTC_H
+#define LL_VOICE_WEBRTC_H
+
+class LLWebRTCProtocolParser;
+
+#include "lliopipe.h"
+#include "llpumpio.h"
+#include "llchainio.h"
+#include "lliosocket.h"
+#include "v3math.h"
+#include "llframetimer.h"
+#include "llviewerregion.h"
+#include "llcallingcard.h" // for LLFriendObserver
+#include "lleventcoro.h"
+#include "llcoros.h"
+#include "llparcel.h"
+#include "llmutelist.h"
+#include <queue>
+#include "json/reader.h"
+
+#ifdef LL_USESYSTEMLIBS
+# include "expat.h"
+#else
+# include "expat/expat.h"
+#endif
+#include "llvoiceclient.h"
+
+// WebRTC Includes
+#include <llwebrtc.h>
+
+class LLAvatarName;
+class LLVoiceWebRTCConnection;
+typedef boost::shared_ptr<LLVoiceWebRTCConnection> connectionPtr_t;
+
+extern const std::string WEBRTC_VOICE_SERVER_TYPE;
+
+class LLWebRTCVoiceClient : public LLSingleton<LLWebRTCVoiceClient>,
+ virtual public LLVoiceModuleInterface,
+ public llwebrtc::LLWebRTCDevicesObserver,
+ public LLMuteListObserver
+{
+ LLSINGLETON_C11(LLWebRTCVoiceClient);
+ LOG_CLASS(LLWebRTCVoiceClient);
+ virtual ~LLWebRTCVoiceClient();
+
+public:
+ /// @name LLVoiceModuleInterface virtual implementations
+ /// @see LLVoiceModuleInterface
+ //@{
+ void init(LLPumpIO *pump) override; // Call this once at application startup (creates connector)
+ void terminate() override; // Call this to clean up during shutdown
+
+ static bool isShuttingDown() { return sShuttingDown; }
+
+ const LLVoiceVersionInfo& getVersion() override;
+
+ void updateSettings() override; // call after loading settings and whenever they change
+
+ // Returns true if WebRTC has successfully logged in and is not in error state
+ bool isVoiceWorking() const override;
+
+ std::string sipURIFromID(const LLUUID &id) override;
+
+ /////////////////////
+ /// @name Tuning
+ //@{
+ void tuningStart() override;
+ void tuningStop() override;
+ bool inTuningMode() override;
+
+ void tuningSetMicVolume(float volume) override;
+ void tuningSetSpeakerVolume(float volume) override;
+ float tuningGetEnergy(void) override;
+ //@}
+
+ /////////////////////
+ /// @name Devices
+ //@{
+ // This returns true when it's safe to bring up the "device settings" dialog in the prefs.
+ bool deviceSettingsAvailable() override;
+ bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel.
+
+ // Requery the WebRTC daemon for the current list of input/output devices.
+ // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
+ // (use this if you want to know when it's done).
+ // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
+ void refreshDeviceLists(bool clearCurrentList = true) override;
+
+ void setCaptureDevice(const std::string& name) override;
+ void setRenderDevice(const std::string& name) override;
+
+ LLVoiceDeviceList& getCaptureDevices() override;
+ LLVoiceDeviceList& getRenderDevices() override;
+ //@}
+
+ void getParticipantList(std::set<LLUUID> &participants) override;
+ bool isParticipant(const LLUUID& speaker_id) override;
+
+ // Send a text message to the specified user, initiating the session if necessary.
+ // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;};
+
+ // Returns true if calling back the session URI after the session has closed is possible.
+ // Currently this will be false only for PSTN P2P calls.
+ // NOTE: this will return true if the session can't be found.
+ BOOL isSessionCallBackPossible(const LLUUID &session_id) override;
+
+ // WebRTC doesn't preclude text im
+ BOOL isSessionTextIMPossible(const LLUUID &session_id) override { return TRUE; }
+
+ ////////////////////////////
+ /// @name Channel stuff
+ //@{
+ // returns true iff the user is currently in a proximal (local spatial) channel.
+ // Note that gestures should only fire if this returns true.
+ bool inProximalChannel() override;
+
+ void setNonSpatialChannel(const LLSD& channelInfo, bool notify_on_first_join, bool hangup_on_last_leave) override
+ {
+ startAdHocSession(channelInfo, notify_on_first_join, hangup_on_last_leave);
+ }
+
+ bool setSpatialChannel(const LLSD &channelInfo) override
+ {
+ // we don't really have credentials for a spatial channel in webrtc,
+ // it's all handled by the sim.
+ return true;
+ }
+
+ void leaveNonSpatialChannel() override;
+
+ void processChannels(bool process) override;
+
+ void leaveChannel(bool stopTalking);
+
+ bool isCurrentChannel(const LLSD &channelInfo) override;
+ bool compareChannels(const LLSD &channelInfo1, const LLSD &channelInfo2) override;
+ //@}
+
+ LLVoiceP2POutgoingCallInterface *getOutgoingCallInterface() override { return nullptr; }
+
+ LLVoiceP2PIncomingCallInterfacePtr getIncomingCallInterface(const LLSD &voice_call_info) override { return nullptr; }
+
+ /////////////////////////
+ /// @name Volume/gain
+ //@{
+ void setVoiceVolume(F32 volume) override;
+ void setMicGain(F32 volume) override;
+ //@}
+
+ /////////////////////////
+ /// @name enable disable voice and features
+ //@{
+ void setVoiceEnabled(bool enabled) override;
+ void setMuteMic(bool muted) override; // Set the mute state of the local mic.
+ //@}
+
+ //////////////////////////
+ /// @name nearby speaker accessors
+ std::string getDisplayName(const LLUUID& id) override;
+ BOOL isParticipantAvatar(const LLUUID &id) override;
+ BOOL getIsSpeaking(const LLUUID& id) override;
+ BOOL getIsModeratorMuted(const LLUUID& id) override;
+ F32 getCurrentPower(const LLUUID& id) override; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is...
+ F32 getUserVolume(const LLUUID& id) override;
+ void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal)
+ //@}
+
+ //////////////////
+ /// @name LLMuteListObserver
+ //@{
+ void onChange() override;
+ void onChangeDetailed(const LLMute& ) override;
+ //@}
+
+ // authorize the user
+ void userAuthorized(const std::string &user_id, const LLUUID &agentID) override {};
+
+
+ void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID);
+ void OnConnectionShutDown(const std::string &channelID, const LLUUID &regionID);
+ void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID);
+ void sendPositionUpdate(bool force);
+ void updateOwnVolume();
+
+ //////////////////////////////
+ /// @name Status notification
+ //@{
+ void addObserver(LLVoiceClientStatusObserver* observer) override;
+ void removeObserver(LLVoiceClientStatusObserver* observer) override;
+ void addObserver(LLFriendObserver* observer) override;
+ void removeObserver(LLFriendObserver* observer) override;
+ void addObserver(LLVoiceClientParticipantObserver* observer) override;
+ void removeObserver(LLVoiceClientParticipantObserver* observer) override;
+ //@}
+
+ //////////////////////////////
+ /// @name Devices change notification
+ // LLWebRTCDevicesObserver
+ //@{
+ void OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices,
+ const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override;
+ //@}
+
+
+ struct participantState
+ {
+ public:
+ participantState(const LLUUID& agent_id);
+
+ bool isAvatar();
+
+ std::string mURI;
+ LLUUID mAvatarID;
+ std::string mDisplayName;
+ LLFrameTimer mSpeakingTimeout;
+ F32 mLevel; // the current audio level of the participant
+ F32 mVolume; // the gain applied to the participant
+ bool mIsSpeaking;
+ bool mIsModeratorMuted;
+ };
+ typedef boost::shared_ptr<participantState> participantStatePtr_t;
+
+ participantStatePtr_t findParticipantByID(const std::string &channelID, const LLUUID &id);
+ participantStatePtr_t addParticipantByID(const std::string& channelID, const LLUUID &id);
+ void removeParticipantByID(const std::string& channelID, const LLUUID &id);
+
+ protected:
+
+ typedef std::map<const LLUUID, participantStatePtr_t> participantUUIDMap;
+
+ class sessionState
+ {
+ public:
+ typedef boost::shared_ptr<sessionState> ptr_t;
+ typedef boost::weak_ptr<sessionState> wptr_t;
+
+ typedef boost::function<void(const ptr_t &)> sessionFunc_t;
+
+ static void addSession(const std::string &channelID, ptr_t& session);
+ virtual ~sessionState();
+
+ participantStatePtr_t addParticipant(const LLUUID& agent_id);
+ void removeParticipant(const participantStatePtr_t &participant);
+ void removeAllParticipants();
+
+ participantStatePtr_t findParticipantByID(const LLUUID& id);
+
+ static ptr_t matchSessionByChannelID(const std::string& channel_id);
+
+ void shutdownAllConnections();
+ void revive();
+
+ static void processSessionStates();
+
+ virtual bool processConnectionStates();
+
+ virtual void sendData(const std::string &data);
+
+ void setMuteMic(bool muted);
+ void setMicGain(F32 volume);
+ void setSpeakerVolume(F32 volume);
+ void setUserVolume(const LLUUID& id, F32 volume);
+
+ void setUserMute(const LLUUID& id, bool mute);
+
+ static void for_each(sessionFunc_t func);
+
+ static void reapEmptySessions();
+
+ bool isEmpty() { return mWebRTCConnections.empty(); }
+
+ virtual bool isSpatial() = 0;
+ virtual bool isEstate() = 0;
+ virtual bool isCallbackPossible() = 0;
+
+ std::string mHandle;
+ std::string mChannelID;
+ std::string mName;
+
+ bool mMuted; // this session is muted.
+ F32 mMicGain; // gain for this session.
+ F32 mSpeakerVolume; // volume for this session.
+
+ bool mShuttingDown;
+
+ participantUUIDMap mParticipantsByUUID;
+
+ static bool hasSession(const std::string &sessionID)
+ { return mSessions.find(sessionID) != mSessions.end(); }
+
+ bool mHangupOnLastLeave; // notify observers after the session becomes empty.
+ bool mNotifyOnFirstJoin; // notify observers when the first peer joins.
+
+ protected:
+ sessionState();
+ std::list<connectionPtr_t> mWebRTCConnections;
+
+ private:
+
+ static std::map<std::string, ptr_t> mSessions; // canonical list of outstanding sessions.
+
+ static void for_eachPredicate(const std::pair<std::string,
+ LLWebRTCVoiceClient::sessionState::wptr_t> &a,
+ sessionFunc_t func);
+ };
+
+ typedef boost::shared_ptr<sessionState> sessionStatePtr_t;
+ typedef std::map<std::string, sessionStatePtr_t> sessionMap;
+
+ class estateSessionState : public sessionState
+ {
+ public:
+ estateSessionState();
+ bool processConnectionStates() override;
+
+ bool isSpatial() override { return true; }
+ bool isEstate() override { return true; }
+ bool isCallbackPossible() override { return false; }
+ };
+
+ class parcelSessionState : public sessionState
+ {
+ public:
+ parcelSessionState(const std::string& channelID, S32 parcel_local_id);
+
+ bool isSpatial() override { return true; }
+ bool isEstate() override { return false; }
+ bool isCallbackPossible() override { return false; }
+ };
+
+ class adhocSessionState : public sessionState
+ {
+ public:
+ adhocSessionState(const std::string &channelID,
+ const std::string& credentials,
+ bool notify_on_first_join,
+ bool hangup_on_last_leave);
+
+ bool isSpatial() override { return false; }
+ bool isEstate() override { return false; }
+
+ // only p2p-type adhoc sessions allow callback
+ bool isCallbackPossible() override { return mNotifyOnFirstJoin && mHangupOnLastLeave; }
+
+ // don't send spatial data to adhoc sessions.
+ void sendData(const std::string &data) override { }
+
+ protected:
+ std::string mCredentials;
+ };
+
+
+ ///////////////////////////////////////////////////////
+ // Private Member Functions
+ //////////////////////////////////////////////////////
+
+ static void predSendData(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const std::string& spatial_data);
+ static void predUpdateOwnVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 audio_level);
+ static void predSetMuteMic(const LLWebRTCVoiceClient::sessionStatePtr_t &session, bool mute);
+ static void predSetMicGain(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume);
+ static void predSetSpeakerVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, F32 volume);
+ static void predShutdownSession(const LLWebRTCVoiceClient::sessionStatePtr_t &session);
+ static void predSetUserMute(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, bool mute);
+ static void predSetUserVolume(const LLWebRTCVoiceClient::sessionStatePtr_t &session, const LLUUID& id, F32 volume);
+
+ //----------------------------------
+ // devices
+ void clearCaptureDevices();
+ void addCaptureDevice(const LLVoiceDevice& device);
+
+ void clearRenderDevices();
+ void addRenderDevice(const LLVoiceDevice& device);
+ void setDevicesListUpdated(bool state);
+
+ /////////////////////////////
+ // Sending updates of current state
+ void updatePosition(void);
+ void setListenerPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot);
+ void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot);
+
+ LLVector3d getListenerPosition() { return mListenerPosition; }
+ LLVector3d getSpeakerPosition() { return mAvatarPosition; }
+
+ void setEarLocation(S32 loc);
+
+
+ /////////////////////////////
+ // Accessors for data related to nearby speakers
+
+ /////////////////////////////
+ sessionStatePtr_t findP2PSession(const LLUUID &agent_id);
+
+ sessionStatePtr_t addSession(const std::string &channel_id, sessionState::ptr_t session);
+ void deleteSession(const sessionStatePtr_t &session);
+
+ // Does the actual work to get out of the audio session
+ void leaveAudioSession();
+
+ friend class LLWebRTCVoiceClientCapResponder;
+
+
+ void lookupName(const LLUUID &id);
+ void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
+ void avatarNameResolved(const LLUUID &id, const std::string &name);
+ static void predAvatarNameResolution(const LLWebRTCVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name);
+
+ boost::signals2::connection mAvatarNameCacheConnection;
+
+private:
+
+ // helper function to retrieve the audio level
+ // Used in multiple places.
+ float getAudioLevel();
+
+ // Coroutine support methods
+ //---
+ void voiceConnectionCoro();
+
+ //---
+ /// Clean up objects created during a voice session.
+ void cleanUp();
+
+ bool mTuningMode;
+ F32 mTuningMicGain;
+ int mTuningSpeakerVolume;
+ bool mDevicesListUpdated; // set to true when the device list has been updated
+ // and false when the panelvoicedevicesettings has queried for an update status.
+ std::string mSpatialSessionCredentials;
+
+ std::string mMainSessionGroupHandle; // handle of the "main" session group.
+
+ sessionStatePtr_t mSession; // Session state for the current session
+
+ sessionStatePtr_t mNextSession; // Session state for the session we're trying to join
+
+ llwebrtc::LLWebRTCDeviceInterface *mWebRTCDeviceInterface;
+
+ LLVoiceDeviceList mCaptureDevices;
+ LLVoiceDeviceList mRenderDevices;
+
+ bool startEstateSession();
+ bool startParcelSession(const std::string& channelID, S32 parcelID);
+ bool startAdHocSession(const LLSD &channelInfo, bool notify_on_first_join, bool hangup_on_last_leave);
+
+ bool inSpatialChannel();
+ bool inOrJoiningChannel(const std::string &channelID);
+ bool inEstateChannel();
+
+ LLSD getAudioSessionChannelInfo();
+
+ void setHidden(bool hidden) override; //virtual
+
+ void enforceTether();
+
+ void updateNeighboringRegions();
+ std::set<LLUUID> getNeighboringRegions() { return mNeighboringRegions; }
+
+ LLVoiceVersionInfo mVoiceVersion;
+
+ bool mSpatialCoordsDirty;
+
+ LLVector3d mListenerPosition;
+ LLVector3d mListenerRequestedPosition;
+ LLVector3 mListenerVelocity;
+ LLQuaternion mListenerRot;
+
+ LLVector3d mAvatarPosition;
+ LLVector3 mAvatarVelocity;
+ LLQuaternion mAvatarRot;
+
+ std::set<LLUUID> mNeighboringRegions; // includes current region
+
+ bool mMuteMic;
+ bool mHidden; //Set to true during teleport to hide the agent's position.
+
+ enum
+ {
+ earLocCamera = 0, // ear at camera
+ earLocAvatar, // ear at avatar
+ earLocMixed // ear at avatar location/camera direction
+ };
+
+ S32 mEarLocation;
+
+ float mSpeakerVolume;
+
+ F32 mMicGain;
+
+ bool mVoiceEnabled;
+ bool mProcessChannels;
+
+ typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
+ observer_set_t mParticipantObservers;
+
+ void notifyParticipantObservers();
+
+ typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
+ status_observer_set_t mStatusObservers;
+
+ void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
+
+ bool mIsInTuningMode;
+ bool mIsProcessingChannels;
+ bool mIsCoroutineActive;
+
+ // These variables can last longer than WebRTC in coroutines so we need them as static
+ static bool sShuttingDown;
+
+ LLEventMailDrop mWebRTCPump;
+};
+
+
+class LLVoiceWebRTCStats : public LLSingleton<LLVoiceWebRTCStats>
+{
+ LLSINGLETON(LLVoiceWebRTCStats);
+ LOG_CLASS(LLVoiceWebRTCStats);
+ virtual ~LLVoiceWebRTCStats();
+
+ private:
+ F64SecondsImplicit mStartTime;
+
+ U32 mConnectCycles;
+
+ F64 mConnectTime;
+ U32 mConnectAttempts;
+
+ F64 mProvisionTime;
+ U32 mProvisionAttempts;
+
+ F64 mEstablishTime;
+ U32 mEstablishAttempts;
+
+ public:
+
+ void reset();
+ void connectionAttemptStart();
+ void connectionAttemptEnd(bool success);
+ void provisionAttemptStart();
+ void provisionAttemptEnd(bool success);
+ void establishAttemptStart();
+ void establishAttemptEnd(bool success);
+ LLSD read();
+};
+
+class LLVoiceWebRTCConnection :
+ public llwebrtc::LLWebRTCSignalingObserver,
+ public llwebrtc::LLWebRTCDataObserver
+{
+ public:
+ LLVoiceWebRTCConnection(const LLUUID &regionID, const std::string &channelID);
+
+ virtual ~LLVoiceWebRTCConnection() = 0;
+
+ //////////////////////////////
+ /// @name Signaling notification
+ // LLWebRTCSignalingObserver
+ //@{
+ void OnIceGatheringState(EIceGatheringState state) override;
+ void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override;
+ void OnOfferAvailable(const std::string &sdp) override;
+ void OnRenegotiationNeeded() override;
+ void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override;
+ void OnPeerConnectionShutdown() override;
+ //@}
+
+ /////////////////////////
+ /// @name Data Notification
+ /// LLWebRTCDataObserver
+ //@{
+ void OnDataReceived(const std::string &data, bool binary) override;
+ void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override;
+ //@}
+
+ void sendJoin();
+ void sendData(const std::string &data);
+
+ virtual void processIceUpdates();
+ virtual void onIceUpdateComplete(bool ice_completed, const LLSD &result);
+ virtual void onIceUpdateError(int retries, std::string url, LLSD body, bool ice_completed, const LLSD &result);
+
+ virtual void setMuteMic(bool muted);
+ virtual void setMicGain(F32 volume);
+ virtual void setSpeakerVolume(F32 volume);
+
+ void setUserVolume(const LLUUID& id, F32 volume);
+ void setUserMute(const LLUUID& id, bool mute);
+
+ bool connectionStateMachine();
+
+ virtual bool isSpatial() = 0;
+
+ LLUUID getRegionID() { return mRegionID; }
+
+ void shutDown()
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+ mShutDown = true;
+ }
+
+ void OnVoiceConnectionRequestSuccess(const LLSD &body);
+ void OnVoiceConnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result);
+
+ protected:
+ typedef enum e_voice_connection_state
+ {
+ VOICE_STATE_ERROR = 0x0,
+ VOICE_STATE_START_SESSION = 0x1,
+ VOICE_STATE_WAIT_FOR_SESSION_START = 0x2,
+ VOICE_STATE_REQUEST_CONNECTION = 0x4,
+ VOICE_STATE_CONNECTION_WAIT = 0x8,
+ VOICE_STATE_SESSION_ESTABLISHED = 0x10,
+ VOICE_STATE_WAIT_FOR_DATA_CHANNEL = 0x20,
+ VOICE_STATE_SESSION_UP = 0x40,
+ VOICE_STATE_SESSION_RETRY = 0x80,
+ VOICE_STATE_DISCONNECT = 0x100,
+ VOICE_STATE_WAIT_FOR_EXIT = 0x200,
+ VOICE_STATE_SESSION_EXIT = 0x400,
+ VOICE_STATE_SESSION_STOPPING = 0x780
+ } EVoiceConnectionState;
+
+ EVoiceConnectionState mVoiceConnectionState;
+ LLMutex mVoiceStateMutex;
+ void setVoiceConnectionState(EVoiceConnectionState new_voice_connection_state)
+ {
+ LLMutexLock lock(&mVoiceStateMutex);
+
+ if (new_voice_connection_state & VOICE_STATE_SESSION_STOPPING)
+ {
+ // the new state is shutdown or restart.
+ mVoiceConnectionState = new_voice_connection_state;
+ return;
+ }
+ if (mVoiceConnectionState & VOICE_STATE_SESSION_STOPPING)
+ {
+ // we're currently shutting down or restarting, so ignore any
+ // state changes.
+ return;
+ }
+
+ mVoiceConnectionState = new_voice_connection_state;
+ }
+ EVoiceConnectionState getVoiceConnectionState()
+ {
+ if (mVoiceStateMutex.isLocked())
+ {
+ LL_WARNS("Voice") << "LOCKED." << LL_ENDL;
+ }
+ LLMutexLock lock(&mVoiceStateMutex);
+ return mVoiceConnectionState;
+ }
+
+ virtual bool requestVoiceConnection() = 0;
+
+ bool breakVoiceConnection(bool wait);
+ void OnVoiceDisconnectionRequestSuccess(const LLSD &body);
+ void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result);
+
+ LLUUID mRegionID;
+ LLUUID mViewerSession;
+ std::string mChannelID;
+
+ std::string mChannelSDP;
+ std::string mRemoteChannelSDP;
+
+ bool mMuted;
+ F32 mMicGain;
+ F32 mSpeakerVolume;
+
+ bool mShutDown;
+ S32 mOutstandingRequests;
+
+ std::vector<llwebrtc::LLWebRTCIceCandidate> mIceCandidates;
+ bool mIceCompleted;
+ bool mTrickling;
+
+ llwebrtc::LLWebRTCPeerConnectionInterface *mWebRTCPeerConnectionInterface;
+ llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface;
+ llwebrtc::LLWebRTCDataInterface *mWebRTCDataInterface;
+};
+
+
+class LLVoiceWebRTCSpatialConnection :
+ public LLVoiceWebRTCConnection
+{
+ public:
+ LLVoiceWebRTCSpatialConnection(const LLUUID &regionID, S32 parcelLocalID, const std::string &channelID);
+
+ virtual ~LLVoiceWebRTCSpatialConnection();
+
+ void setMuteMic(bool muted) override;
+
+ bool isSpatial() override { return true; }
+
+
+protected:
+
+ bool requestVoiceConnection() override;
+
+ S32 mParcelLocalID;
+};
+
+class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection
+{
+ public:
+ LLVoiceWebRTCAdHocConnection(const LLUUID &regionID, const std::string &channelID, const std::string& credentials);
+
+ virtual ~LLVoiceWebRTCAdHocConnection();
+
+ bool isSpatial() override { return false; }
+
+ protected:
+ bool requestVoiceConnection() override;
+
+ std::string mCredentials;
+};
+
+#define VOICE_ELAPSED LLVoiceTimer(__FUNCTION__);
+
+#endif //LL_WebRTC_VOICE_CLIENT_H
+
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index c7f32d0da9..bcae75425d 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -547,6 +547,12 @@ class Windows_x86_64_Manifest(ViewerManifest):
# Get shared libs from the shared libs staging directory
with self.prefix(src=os.path.join(self.args['build'], os.pardir,
'sharedlibs', self.args['buildtype'])):
+ # WebRTC libraries
+ for libfile in (
+ 'llwebrtc.dll',
+ ):
+ self.path(libfile)
+
# Get fmodstudio dll if needed
if self.args['fmodstudio'] == 'ON':
if(self.args['buildtype'].lower() == 'debug'):
@@ -994,6 +1000,20 @@ class Darwin_x86_64_Manifest(ViewerManifest):
print("Skipping %s" % dst)
return added
+ # WebRTC libraries
+ with self.prefix(src=os.path.join(self.args['build'], os.pardir,
+ 'sharedlibs', self.args['buildtype'], 'Resources')):
+ for libfile in (
+ 'libllwebrtc.dylib',
+ ):
+ self.path(libfile)
+
+ oldpath = os.path.join("@rpath", libfile)
+ self.run_command(
+ ['install_name_tool', '-change', oldpath,
+ '@executable_path/../Resources/%s' % libfile,
+ executable])
+
# dylibs is a list of all the .dylib files we expect to need
# in our bundled sub-apps. For each of these we'll create a
# symlink from sub-app/Contents/Resources to the real .dylib.
diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1
index 5ad85458e9..47291dcd66 100755
--- a/scripts/messages/message_template.msg.sha1
+++ b/scripts/messages/message_template.msg.sha1
@@ -1 +1 @@
-e3bd0529a647d938ab6d48f26d21dd52c07ebc6e \ No newline at end of file
+dd15c52581b3fe99e072b26872deba2560893fc4