diff --git a/autobuild.xml b/autobuild.xml index e318ce0cf1..8c9e4ad723 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3256,11 +3256,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 8b0191fae0860782a3e79b886364129c433cfd6b + a49fb3bb8aaf8325e7c6c4b6036db3da16afa2c9 hash_algorithm md5 url - https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.52/webrtc-m114.5735.08.52.8319849783-darwin64-8319849783.tar.zst + https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.53/webrtc-m114.5735.08.53.8337236647-darwin64-8337236647.tar.zst name darwin64 @@ -3270,11 +3270,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 699b4fb87e20bdc05e76a32c84688fa21a16785d + 598baa054f63624a8e16883541c1f3dc7aa15a8a hash_algorithm sha1 url - https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.55/webrtc-m114.5735.08.55.8404951738-linux64-8404951738.tar.zst + https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.53/webrtc-m114.5735.08.53.8337236647-linux64-8337236647.tar.zst name linux64 @@ -3284,11 +3284,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 64eccac933cee532dc065d9f9729a21d8347aae4 + 59d5f2e40612ab7b0b1a5da8ba288f48d5979216 hash_algorithm sha1 url - https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.52/webrtc-m114.5735.08.52.8319849783-windows64-8319849783.tar.zst + https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.53/webrtc-m114.5735.08.53.8337236647-windows64-8337236647.tar.zst name windows64 @@ -3301,7 +3301,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors vcs_url https://github.com/secondlife/3p-webrtc-build version - m114.5735.08.52.8319849783 + m114.5735.08.53.8337236647 xmlrpc-epi diff --git a/indra/llcommon/llprofilercategories.h b/indra/llcommon/llprofilercategories.h index 30715505b2..8d7fe60bc9 100644 --- a/indra/llcommon/llprofilercategories.h +++ b/indra/llcommon/llprofilercategories.h @@ -1,5 +1,5 @@ /** - * @file llprofiler_ategories.h + * @file llprofilercategories.h * @brief Profiling categories to minimize Tracy memory usage when viewing captures. * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ @@ -33,7 +33,7 @@ // LL_PROFILER_CATEGORY_ENABLE_DRAWPOOL // LL_PROFILER_CATEGORY_ENABLE_LLSD // LL_PROFILER_CATEGORY_ENABLE_MEMORY -// LL_PROFILER_CATEGORY_ENABLE_SHADERS +// LL_PROFILER_CATEGORY_ENABLE_SHADER // // NOTE: You can still manually use: // LL_PROFILE_ZONE_SCOPED(); @@ -68,6 +68,7 @@ #define LL_PROFILER_CATEGORY_ENABLE_VERTEX 1 #define LL_PROFILER_CATEGORY_ENABLE_VOLUME 1 #define LL_PROFILER_CATEGORY_ENABLE_WIN32 0 // Rationalise this silliness +#define LL_PROFILER_CATEGORY_ENABLE_VOICE 1 #if LL_PROFILER_CATEGORY_ENABLE_APP #define LL_PROFILE_ZONE_NAMED_CATEGORY_APP LL_PROFILE_ZONE_NAMED @@ -287,5 +288,13 @@ #define LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32 #endif +#if LL_PROFILER_CATEGORY_ENABLE_VOICE +#define LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE LL_PROFILE_ZONE_NAMED +#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE LL_PROFILE_ZONE_SCOPED +#else +#define LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE(name) +#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE +#endif + #endif // LL_PROFILER_CATEGORIES_H diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 9fb278049e..eb9bb65e67 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -26,14 +26,6 @@ #include "llwebrtc_impl.h" #include - -/* Only available for much newer compilers on linux (fine with GCC 13). -This causes grief with some older compilers (like GCC 10). Since it doesnt appear -to be needed (at least when building the viewer on linux, dont try and include it*/ -#ifndef __linux__ -#include -#endif - #include #include "api/audio_codecs/audio_decoder_factory.h" @@ -47,6 +39,11 @@ to be needed (at least when building the viewer on linux, dont try and include i namespace llwebrtc { +static int16_t PLAYOUT_DEVICE_DEFAULT = -1; +static int16_t PLAYOUT_DEVICE_BAD = -2; +static int16_t RECORD_DEVICE_DEFAULT = -1; +static int16_t RECORD_DEVICE_BAD = -2; + LLAudioDeviceObserver::LLAudioDeviceObserver() : mSumVector {0}, mMicrophoneEnergy(0.0) {} float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } @@ -164,7 +161,6 @@ LLWebRTCImpl::LLWebRTCImpl() : void LLWebRTCImpl::init() { - RTC_DCHECK(mPeerConnectionFactory); mPlayoutDevice = 0; mRecordingDevice = 0; rtc::InitializeSSL(); @@ -197,10 +193,16 @@ void LLWebRTCImpl::init() std::unique_ptr(mTuningAudioDeviceObserver)); mTuningDeviceModule->Init(); - mTuningDeviceModule->SetStereoRecording(true); - mTuningDeviceModule->SetStereoPlayout(true); + mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); mTuningDeviceModule->EnableBuiltInAEC(false); mTuningDeviceModule->SetAudioDeviceSink(this); + mTuningDeviceModule->InitMicrophone(); + mTuningDeviceModule->InitSpeaker(); + mTuningDeviceModule->InitRecording(); + mTuningDeviceModule->InitPlayout(); + mTuningDeviceModule->SetStereoRecording(true); + mTuningDeviceModule->SetStereoPlayout(true); updateDevices(); }); @@ -215,13 +217,13 @@ void LLWebRTCImpl::init() 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(); + mPeerDeviceModule->SetStereoRecording(true); + mPeerDeviceModule->SetStereoPlayout(true); }); // The custom processor allows us to retrieve audio data (and levels) @@ -229,12 +231,10 @@ void LLWebRTCImpl::init() mPeerCustomProcessor = new LLCustomProcessor; webrtc::AudioProcessingBuilder apb; apb.SetCapturePostProcessing(std::unique_ptr(mPeerCustomProcessor)); - rtc::scoped_refptr apm = apb.Create(); + mAudioProcessingModule = 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.enabled = false; apm_config.echo_canceller.mobile_mode = false; apm_config.gain_controller1.enabled = true; apm_config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog; @@ -249,16 +249,16 @@ void LLWebRTCImpl::init() webrtc::ProcessingConfig processing_config; processing_config.input_stream().set_num_channels(2); - processing_config.input_stream().set_sample_rate_hz(8000); + processing_config.input_stream().set_sample_rate_hz(48000); processing_config.output_stream().set_num_channels(2); - processing_config.output_stream().set_sample_rate_hz(8000); + processing_config.output_stream().set_sample_rate_hz(48000); 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); + mAudioProcessingModule->ApplyConfig(apm_config); + mAudioProcessingModule->Initialize(processing_config); mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(), mWorkerThread.get(), @@ -269,7 +269,7 @@ void LLWebRTCImpl::init() nullptr /* video_encoder_factory */, nullptr /* video_decoder_factory */, nullptr /* audio_mixer */, - apm); + mAudioProcessingModule); mWorkerThread->BlockingCall([this]() { mPeerDeviceModule->StartPlayout(); }); } @@ -325,6 +325,49 @@ void LLWebRTCImpl::setRecording(bool recording) }); } +void LLWebRTCImpl::setAudioConfig(LLWebRTCDeviceInterface::AudioConfig config) +{ + webrtc::AudioProcessing::Config apm_config; + apm_config.echo_canceller.enabled = config.mEchoCancellation; + 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.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; + + switch (config.mNoiseSuppressionLevel) + { + case LLWebRTCDeviceInterface::AudioConfig::NOISE_SUPPRESSION_LEVEL_NONE: + apm_config.noise_suppression.enabled = false; + apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kLow; + break; + case LLWebRTCDeviceInterface::AudioConfig::NOISE_SUPPRESSION_LEVEL_LOW: + apm_config.noise_suppression.enabled = true; + apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kLow; + break; + case LLWebRTCDeviceInterface::AudioConfig::NOISE_SUPPRESSION_LEVEL_MODERATE: + apm_config.noise_suppression.enabled = true; + apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kModerate; + break; + case LLWebRTCDeviceInterface::AudioConfig::NOISE_SUPPRESSION_LEVEL_HIGH: + apm_config.noise_suppression.enabled = true; + apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kHigh; + break; + case LLWebRTCDeviceInterface::AudioConfig::NOISE_SUPPRESSION_LEVEL_VERY_HIGH: + apm_config.noise_suppression.enabled = true; + apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kVeryHigh; + break; + default: + apm_config.noise_suppression.enabled = false; + apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kLow; + } + mAudioProcessingModule->ApplyConfig(apm_config); +} + void LLWebRTCImpl::refreshDevices() { mWorkerThread->PostTask([this]() { updateDevices(); }); @@ -342,145 +385,169 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer) } } -static int16_t ll_get_device_module_capture_device(rtc::scoped_refptr device_module, const std::string &id) -{ - int16_t recordingDevice = 0; - int16_t captureDeviceCount = device_module->RecordingDevices(); - for (int16_t i = 0; i < captureDeviceCount; i++) - { - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - device_module->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; - recordingDevice = i; - break; - } - } - return recordingDevice; -} - void ll_set_device_module_capture_device(rtc::scoped_refptr device_module, int16_t device) { device_module->StopRecording(); - device_module->SetRecordingDevice(device); +#if WEBRTC_WIN + if (device < 0) + { + device_module->SetRecordingDevice(webrtc::AudioDeviceModule::kDefaultDevice); + } + else + { + device_module->SetRecordingDevice(device); + } +#else + // passed in default is -1, but the device list + // has it at 0 + device_module->SetRecordingDevice(device + 1); +#endif device_module->InitMicrophone(); - device_module->SetStereoRecording(false); device_module->InitRecording(); + device_module->SetStereoRecording(false); device_module->StartRecording(); } void LLWebRTCImpl::setCaptureDevice(const std::string &id) { - - mWorkerThread->PostTask( - [this, id]() - { - int16_t recordingDevice = ll_get_device_module_capture_device(mTuningDeviceModule, id); - if (recordingDevice != mRecordingDevice) - { - mRecordingDevice = recordingDevice; - if (mTuningMode) - { - ll_set_device_module_capture_device(mTuningDeviceModule, recordingDevice); - } - else - { - ll_set_device_module_capture_device(mPeerDeviceModule, recordingDevice); - } - } - }); -} - -static int16_t ll_get_device_module_render_device( - rtc::scoped_refptr device_module, - const std::string &id) -{ - int16_t playoutDevice = 0; - int16_t playoutDeviceCount = device_module->PlayoutDevices(); - for (int16_t i = 0; i < playoutDeviceCount; i++) + int16_t recordingDevice = RECORD_DEVICE_DEFAULT; + if (id != "Default") { - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - device_module->PlayoutDeviceName(i, name, guid); - if (id == guid || id == "Default") // first one in list is default + for (int16_t i = 0; i < mRecordingDeviceList.size(); i++) { - RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; - playoutDevice = i; - break; + if (mRecordingDeviceList[i].mID == id) + { + recordingDevice = i; + break; + } } } - return playoutDevice; + if (recordingDevice == mRecordingDevice) + { + return; + } + mRecordingDevice = recordingDevice; + if (mTuningMode) + { + mWorkerThread->PostTask([this, recordingDevice]() { ll_set_device_module_capture_device(mTuningDeviceModule, recordingDevice); }); + } + else + { + mWorkerThread->PostTask([this, recordingDevice]() { ll_set_device_module_capture_device(mPeerDeviceModule, recordingDevice); }); + } } void ll_set_device_module_render_device(rtc::scoped_refptr device_module, int16_t device) { device_module->StopPlayout(); - device_module->SetPlayoutDevice(device); +#if WEBRTC_WIN + if (device < 0) + { + device_module->SetPlayoutDevice(webrtc::AudioDeviceModule::kDefaultDevice); + } + else + { + device_module->SetPlayoutDevice(device); + } +#else + device_module->SetPlayoutDevice(device + 1); +#endif device_module->InitSpeaker(); - device_module->SetStereoPlayout(false); device_module->InitPlayout(); - device_module->StartPlayout(); + device_module->SetStereoPlayout(true); } void LLWebRTCImpl::setRenderDevice(const std::string &id) { - mWorkerThread->PostTask( - [this, id]() - { - int16_t playoutDevice = ll_get_device_module_render_device(mTuningDeviceModule, id); - if (playoutDevice != mPlayoutDevice) + int16_t playoutDevice = PLAYOUT_DEVICE_DEFAULT; + if (id != "Default") + { + for (int16_t i = 0; i < mPlayoutDeviceList.size(); i++) + { + if (mPlayoutDeviceList[i].mID == id) { - mPlayoutDevice = playoutDevice; - if (mTuningMode) - { - ll_set_device_module_render_device(mTuningDeviceModule, playoutDevice); - } - else - { - ll_set_device_module_render_device(mPeerDeviceModule, playoutDevice); - } + playoutDevice = i; + break; } - }); + } + } + if (playoutDevice == mPlayoutDevice) + { + return; + } + mPlayoutDevice = playoutDevice; + + if (mTuningMode) + { + mWorkerThread->PostTask( + [this, playoutDevice]() + { + ll_set_device_module_render_device(mTuningDeviceModule, playoutDevice); + }); + } + else + { + mWorkerThread->PostTask( + [this, playoutDevice]() + { + ll_set_device_module_render_device(mPeerDeviceModule, playoutDevice); + mPeerDeviceModule->StartPlayout(); + }); + } } // updateDevices needs to happen on the worker thread. void LLWebRTCImpl::updateDevices() { - int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); - int16_t currentRenderDeviceIndex = mTuningDeviceModule->GetPlayoutDevice(); + int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); - LLWebRTCVoiceDeviceList renderDeviceList; - for (int16_t index = 0; index < renderDeviceCount; index++) + mPlayoutDeviceList.clear(); +#if WEBRTC_WIN + int16_t index = 0; +#else + // index zero is always "Default" for darwin/linux, + // which is a special case, so skip it. + int16_t index = 1; +#endif + for (; index < renderDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mTuningDeviceModule->PlayoutDeviceName(index, name, guid); - renderDeviceList.emplace_back(name, guid); + mPlayoutDeviceList.emplace_back(name, guid); } - int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); - int16_t currentCaptureDeviceIndex = mTuningDeviceModule->GetRecordingDevice(); + int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); - LLWebRTCVoiceDeviceList captureDeviceList; - for (int16_t index = 0; index < captureDeviceCount; index++) + mRecordingDeviceList.clear(); +#if WEBRTC_WIN + index = 0; +#else + // index zero is always "Default" for darwin/linux, + // which is a special case, so skip it. + index = 1; +#endif + for (; index < captureDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mTuningDeviceModule->RecordingDeviceName(index, name, guid); - captureDeviceList.emplace_back(name, guid); + mRecordingDeviceList.emplace_back(name, guid); } + for (auto &observer : mVoiceDevicesObserverList) { - observer->OnDevicesChanged(renderDeviceList, - captureDeviceList); + observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList); } } void LLWebRTCImpl::OnDevicesUpdated() { + // reset these to a bad value so an update is forced + mRecordingDevice = RECORD_DEVICE_BAD; + mPlayoutDevice = PLAYOUT_DEVICE_BAD; + updateDevices(); } @@ -496,17 +563,24 @@ void LLWebRTCImpl::setTuningMode(bool enable) mPeerDeviceModule->StopPlayout(); ll_set_device_module_render_device(mTuningDeviceModule, mPlayoutDevice); ll_set_device_module_capture_device(mTuningDeviceModule, mRecordingDevice); + mTuningDeviceModule->InitPlayout(); + mTuningDeviceModule->InitRecording(); mTuningDeviceModule->StartRecording(); - mTuningDeviceModule->StartPlayout(); + // TODO: Starting Playout on the TDM appears to create an audio artifact (click) + // in this case, so disabling it for now. We may have to do something different + // if we enable 'echo playback' via the TDM when tuning. + //mTuningDeviceModule->StartPlayout(); } else { mTuningDeviceModule->StopRecording(); - mTuningDeviceModule->StopPlayout(); + //mTuningDeviceModule->StopPlayout(); ll_set_device_module_render_device(mPeerDeviceModule, mPlayoutDevice); ll_set_device_module_capture_device(mPeerDeviceModule, mRecordingDevice); - mPeerDeviceModule->StartRecording(); + mPeerDeviceModule->InitPlayout(); + mPeerDeviceModule->InitRecording(); mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->StartRecording(); } } ); @@ -569,8 +643,10 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn // Most peer connection (signaling) happens on // the signaling thread. -LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() : +LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() : mWebRTCImpl(nullptr), + mClosing(false), + mPeerConnection(nullptr), mMute(false), mAnswerReceived(false) { @@ -587,13 +663,27 @@ void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl) } void LLWebRTCPeerConnectionImpl::terminate() { - mWebRTCImpl->SignalingBlockingCall( - [this]() + rtc::scoped_refptr connection; + mPeerConnection.swap(connection); + rtc::scoped_refptr dataChannel; + mDataChannel.swap(dataChannel); + rtc::scoped_refptr localStream; + mLocalStream.swap(localStream); + + mSignalingObserverList.clear(); + mDataObserverList.clear(); + + mWebRTCImpl->PostSignalingTask( + [=]() { - if (mPeerConnection) + if (connection) { - mPeerConnection->Close(); - mPeerConnection = nullptr; + connection->Close(); + } + if (dataChannel) + { + dataChannel->UnregisterObserver(); + dataChannel->Close(); } }); } @@ -610,32 +700,28 @@ void LLWebRTCPeerConnectionImpl::unsetSignalingObserver(LLWebRTCSignalingObserve } } -// TODO: Add initialization structure through which -// stun and turn servers may be passed in from -// the sim or login. -bool LLWebRTCPeerConnectionImpl::initializeConnection() + +bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnectionInterface::InitOptions& options) { RTC_DCHECK(!mPeerConnection); mAnswerReceived = false; mWebRTCImpl->PostSignalingTask( - [this]() + [this,options]() { webrtc::PeerConnectionInterface::RTCConfiguration config; + for (auto server : options.mServers) + { + webrtc::PeerConnectionInterface::IceServer ice_server; + for (auto url : server.mUrls) + { + ice_server.urls.push_back(url); + } + ice_server.username = server.mUserName; + ice_server.password = server.mPassword; + config.servers.push_back(ice_server); + } 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); @@ -665,7 +751,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() cricket::AudioOptions audioOptions; audioOptions.auto_gain_control = true; - audioOptions.echo_cancellation = true; // incompatible with opus stereo + audioOptions.echo_cancellation = true; audioOptions.noise_suppression = true; mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream"); @@ -717,24 +803,9 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection() bool LLWebRTCPeerConnectionImpl::shutdownConnection() { - if (mPeerConnection) - { - mWebRTCImpl->PostSignalingTask( - [this]() - { - if (mPeerConnection) - { - mPeerConnection->Close(); - mPeerConnection = nullptr; - } - for (auto &observer : mSignalingObserverList) - { - observer->OnPeerConnectionShutdown(); - } - }); - return true; - } - return false; + mClosing = true; + terminate(); + return true; } void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable) @@ -1064,19 +1135,25 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * } RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_mangled_stream.str(); - + std::string mangled_sdp = sdp_mangled_stream.str(); for (auto &observer : mSignalingObserverList) { - observer->OnOfferAvailable(sdp_mangled_stream.str()); + observer->OnOfferAvailable(mangled_sdp); } + + mPeerConnection->SetLocalDescription(std::unique_ptr( + webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, mangled_sdp)), + rtc::scoped_refptr(this)); - mPeerConnection->SetLocalDescription(std::unique_ptr(webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp_mangled_stream.str())), - rtc::scoped_refptr(this)); } void LLWebRTCPeerConnectionImpl::OnFailure(webrtc::RTCError error) { RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); + for (auto &observer : mSignalingObserverList) + { + observer->OnRenegotiationNeeded(); + } } // @@ -1090,6 +1167,10 @@ void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError if (!error.ok()) { RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); + for (auto &observer : mSignalingObserverList) + { + observer->OnRenegotiationNeeded(); + } return; } mAnswerReceived = true; @@ -1107,7 +1188,10 @@ void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError } } mCachedIceCandidates.clear(); - OnIceGatheringChange(mPeerConnection->ice_gathering_state()); + if (mPeerConnection) + { + OnIceGatheringChange(mPeerConnection->ice_gathering_state()); + } } @@ -1124,6 +1208,10 @@ void LLWebRTCPeerConnectionImpl::OnSetLocalDescriptionComplete(webrtc::RTCError void LLWebRTCPeerConnectionImpl::OnStateChange() { + if (!mDataChannel) + { + return; + } RTC_LOG(LS_INFO) << __FUNCTION__ << " Data Channel State: " << webrtc::DataChannelInterface::DataStateString(mDataChannel->state()); switch (mDataChannel->state()) { @@ -1167,7 +1255,12 @@ void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary) { rtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length()); webrtc::DataBuffer buffer(cowBuffer, binary); - mDataChannel->Send(buffer); + mWebRTCImpl->PostNetworkTask([this, buffer]() { + if (mDataChannel) + { + mDataChannel->Send(buffer); + } + }); } } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index dab7774499..ac71e0c744 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -78,7 +78,12 @@ class LLWebRTCVoiceDevice LLWebRTCVoiceDevice(const std::string &display_name, const std::string &id) : mDisplayName(display_name), mID(id) - {}; + { + if (mID.empty()) + { + mID = display_name; + } + }; }; typedef std::vector LLWebRTCVoiceDeviceList; @@ -99,10 +104,32 @@ class LLWebRTCDevicesObserver // to enumerate, set, and get notifications of changes // for both capture (microphone) and render (speaker) // devices. + class LLWebRTCDeviceInterface { public: + struct AudioConfig { + bool mAGC { true }; + + bool mEchoCancellation { true }; + + // TODO: The various levels of noise suppression are configured + // on the APM which would require setting config on the APM. + // We should pipe the various values through + // later. + typedef enum { + NOISE_SUPPRESSION_LEVEL_NONE = 0, + NOISE_SUPPRESSION_LEVEL_LOW, + NOISE_SUPPRESSION_LEVEL_MODERATE, + NOISE_SUPPRESSION_LEVEL_HIGH, + NOISE_SUPPRESSION_LEVEL_VERY_HIGH + } ENoiseSuppressionLevel; + ENoiseSuppressionLevel mNoiseSuppressionLevel { NOISE_SUPPRESSION_LEVEL_VERY_HIGH }; + }; + + virtual void setAudioConfig(AudioConfig config) = 0; + // instructs webrtc to refresh the device list. virtual void refreshDevices() = 0; @@ -192,24 +219,38 @@ class LLWebRTCSignalingObserver // 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: + + struct InitOptions + { + // equivalent of PeerConnectionInterface::IceServer + struct IceServers { + + // Valid formats are described in RFC7064 and RFC7065. + // Urls should containe dns hostnames (not IP addresses) + // as the TLS certificate policy is 'secure.' + // and we do not currentply support TLS extensions. + std::vector mUrls; + std::string mUserName; + std::string mPassword; + }; + + std::vector mServers; + }; + + virtual bool initializeConnection(const InitOptions& options) = 0; + virtual bool shutdownConnection() = 0; 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; }; diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 1f6b9846f8..e1031099c7 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -146,6 +146,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // LLWebRTCDeviceInterface // + void setAudioConfig(LLWebRTCDeviceInterface::AudioConfig config = LLWebRTCDeviceInterface::AudioConfig()) override; + void refreshDevices() override; void setDevicesObserver(LLWebRTCDevicesObserver *observer) override; @@ -228,6 +230,8 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // The factory that allows creation of native webrtc PeerConnections. rtc::scoped_refptr mPeerConnectionFactory; + + rtc::scoped_refptr mAudioProcessingModule; // more native webrtc stuff std::unique_ptr mTaskQueueFactory; @@ -241,8 +245,12 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // accessors in native webrtc for devices aren't apparently implemented yet. bool mTuningMode; - int32_t mPlayoutDevice; int32_t mRecordingDevice; + LLWebRTCVoiceDeviceList mRecordingDeviceList; + + int32_t mPlayoutDevice; + LLWebRTCVoiceDeviceList mPlayoutDeviceList; + bool mMute; LLAudioDeviceObserver * mTuningAudioDeviceObserver; @@ -279,11 +287,11 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface, // // LLWebRTCPeerConnection // + bool initializeConnection(const InitOptions& options) override; + bool shutdownConnection() override; void setSignalingObserver(LLWebRTCSignalingObserver *observer) override; void unsetSignalingObserver(LLWebRTCSignalingObserver *observer) override; - bool initializeConnection() override; - bool shutdownConnection() override; void AnswerAvailable(const std::string &sdp) override; // @@ -347,21 +355,23 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface, LLWebRTCImpl * mWebRTCImpl; + bool mClosing; + rtc::scoped_refptr mPeerConnectionFactory; - bool mMute; + bool mMute; // signaling - std::vector mSignalingObserverList; + std::vector mSignalingObserverList; std::vector> mCachedIceCandidates; - bool mAnswerReceived; + bool mAnswerReceived; - rtc::scoped_refptr mPeerConnection; - rtc::scoped_refptr mLocalStream; + rtc::scoped_refptr mPeerConnection; + rtc::scoped_refptr mLocalStream; // data - std::vector mDataObserverList; - rtc::scoped_refptr mDataChannel; + std::vector mDataObserverList; + rtc::scoped_refptr mDataChannel; }; } diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 85634bc3ec..fa3b937cfe 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -18612,6 +18612,39 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 + VoiceEchoCancellation + + Comment + Voice Echo Cancellation + Persist + 1 + Type + Boolean + Value + 1 + + VoiceAutomaticGainControl + + Comment + Voice Automatic Gain Control + Persist + 1 + Type + Boolean + Value + 1 + + VoiceNoiseSuppressionLevel + + Comment + Voice Noise Suppression Level + Persist + 1 + Type + U32 + Value + 4 + WarningsAsChat Comment diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 602e0dc9dd..39dc15b704 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -123,7 +123,7 @@ enum EMultiAgentChatSessionType 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 startP2PVoiceCoro(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); @@ -591,14 +591,14 @@ void startConferenceCoro(std::string url, } } -void startP2PCoro(std::string url, LLUUID sessionID, LLUUID creatorId, LLUUID otherParticipantId) +void startP2PVoiceCoro(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)); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("StartP2PVoiceCoro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLSD postData; - postData["method"] = "start p2p"; + postData["method"] = "start p2p voice"; postData["session-id"] = sessionID; postData["params"] = otherParticipantId; LLSD altParams; @@ -2422,7 +2422,7 @@ bool LLIMModel::sendStartSession( if (region) { std::string url = region->getCapability("ChatSessionRequest"); - LLCoros::instance().launch("startP2P", boost::bind(&startP2PCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); + LLCoros::instance().launch("startP2PVoiceCoro", boost::bind(&startP2PVoiceCoro, url, temp_session_id, gAgent.getID(), other_participant_id)); } return true; } @@ -3027,7 +3027,7 @@ LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : void LLIncomingCallDialog::onLifetimeExpired() { - LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(mPayload["voice_session_info"]); + LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(mPayload["voice_channel_info"]); if (call) { // restart notification's timer if call is still valid @@ -3308,24 +3308,42 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload { if (type == IM_SESSION_P2P_INVITE) { - LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(payload["voice_session_info"]); + // decline p2p voice, either via the vivox-style call mechanism + // or via the webrtc-style "decline p2p" mechanism. + LLVoiceP2PIncomingCallInterfacePtr call = LLVoiceClient::getInstance()->getIncomingCallInterface(payload["voice_channel_info"]); if (call) { call->declineInvite(); } + else + { + // webrtc-style decline. + LLViewerRegion *region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); + + LLSD data; + data["method"] = "decline p2p voice"; + data["session-id"] = session_id; + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, "P2P declined", "P2P decline failed."); + } + } } else { - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); + LLViewerRegion *region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "decline invitation"; - data["session-id"] = session_id; + LLSD data; + data["method"] = "decline invitation"; + data["session-id"] = session_id; - LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, - "Invitation declined", - "Invitation decline failed."); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, "Invitation declined", "Invitation decline failed."); + } } } @@ -4134,6 +4152,40 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) { + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) + { + LLSD::map_const_iterator update_it; + for (update_it = body["agent_updates"].beginMap(); update_it != body["agent_updates"].endMap(); ++update_it) + { + LLUUID agent_id = LLUUID(update_it->first); + LLSD agent_data = update_it->second; + if (agent_data.has("transition") && agent_data["transition"].asString() == "LEAVE") + { + // ignore actual leaves as those will be handled separately. + continue; + } + + if (agent_id != gAgentID && agent_data.isMap() && agent_data.has("info") && agent_data["info"].isMap()) + { + // Is one of the participants leaving a P2P Chat? + if (agent_data["info"].has("can_voice_chat") && !agent_data["info"]["can_voice_chat"].asBoolean()) + { + LLVoiceChannelGroup *channelp = dynamic_cast < LLVoiceChannelGroup*>(LLVoiceChannel::getChannelByID(session_id)); + if (channelp && channelp->isP2P()) + { + // it's an adhoc-style P2P channel, and the peer has declined voice. notify the user + // and shut down the voice channel. + LLSD notifyArgs = LLSD::emptyMap(); + notifyArgs["VOICE_CHANNEL_NAME"] = channelp->getSessionName(); + LLNotificationsUtil::add("P2PCallDeclined", notifyArgs); + endCall(session_id); + break; + } + } + } + } + } + // [FS communication UI] //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 97dfdfb9ab..d884307756 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -1269,6 +1269,9 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "PushToTalkButton", handleVoiceClientPrefsChanged); setting_setup_signal_listener(gSavedSettings, "PushToTalkToggle", handleVoiceClientPrefsChanged); setting_setup_signal_listener(gSavedSettings, "VoiceEarLocation", handleVoiceClientPrefsChanged); + setting_setup_signal_listener(gSavedSettings, "VoiceEchoCancellation", handleVoiceClientPrefsChanged); + setting_setup_signal_listener(gSavedSettings, "VoiceAutomaticGainControl", handleVoiceClientPrefsChanged); + setting_setup_signal_listener(gSavedSettings, "VoiceNoiseSuppressionLevel", handleVoiceClientPrefsChanged); setting_setup_signal_listener(gSavedSettings, "VoiceInputAudioDevice", handleVoiceClientPrefsChanged); setting_setup_signal_listener(gSavedSettings, "VoiceOutputAudioDevice", handleVoiceClientPrefsChanged); setting_setup_signal_listener(gSavedSettings, "AudioLevelMic", handleVoiceClientPrefsChanged); diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index bc22bf0df6..adc387e22d 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -147,6 +147,8 @@ public: void setChannelInfo(const LLSD &channelInfo) override; void requestChannelInfo() override; + bool isP2P() { return mIsP2P; } + protected: void setState(EState state) override; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 2198977028..d6500123a4 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1273,7 +1273,9 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() do { LLVoiceVivoxStats::getInstance()->provisionAttemptStart(); - result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts); + LLSD body; + body["voice_server_type"] = "vivox"; + result = httpAdapter->postAndSuspend(httpRequest, url, body, httpOpts); if (sShuttingDown) { @@ -5248,7 +5250,7 @@ bool LLVivoxVoiceClient::isCurrentChannel(const LLSD &channelInfo) } if (mAudioSession) { - if (!channelInfo["sessionHandle"].asString().empty()) + if (!channelInfo["session_handle"].asString().empty()) { return mAudioSession->mHandle == channelInfo["session_handle"].asString(); } diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 85e1bbdf6d..563b1ed9d9 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -1,5 +1,5 @@ /** - * @file LLWebRTCVoiceClient.cpp + * @file llvoicewebrtc.cpp * @brief Implementation of LLWebRTCVoiceClient class which is the interface to the voice client process. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ @@ -24,6 +24,7 @@ * $/LicenseInfo$ */ #include +#include #include "llvoicewebrtc.h" #include "llsdutil.h" @@ -279,7 +280,7 @@ void LLWebRTCVoiceClient::cleanUp() LL_DEBUGS("Voice") << "Exiting" << LL_ENDL; } -//--------------------------------------------------- +// -------------------------------------------------- const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() { @@ -290,15 +291,36 @@ const LLVoiceVersionInfo& LLWebRTCVoiceClient::getVersion() void LLWebRTCVoiceClient::updateSettings() { - setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled()); - setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE - 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); + setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled()); + static LLCachedControl sVoiceEarLocation(gSavedSettings, "VoiceEarLocation"); + setEarLocation(sVoiceEarLocation); + + static LLCachedControl sInputDevice(gSavedSettings, "VoiceInputAudioDevice"); + setCaptureDevice(sInputDevice); + + static LLCachedControl sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice"); + setRenderDevice(sOutputDevice); + + static LLCachedControl sMicLevel(gSavedSettings, "AudioLevelMic"); + setMicGain(sMicLevel); + + llwebrtc::LLWebRTCDeviceInterface::AudioConfig config; + + static LLCachedControl sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true); + config.mEchoCancellation = sEchoCancellation; + + static LLCachedControl sAGC(gSavedSettings, "VoiceAutomaticGainControl", true); + config.mAGC = sAGC; + + static LLCachedControl sNoiseSuppressionLevel(gSavedSettings, + "VoiceNoiseSuppressionLevel", + llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH); + config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel) (U32)sNoiseSuppressionLevel; + + mWebRTCDeviceInterface->setAudioConfig(config); + } // Observers @@ -314,6 +336,7 @@ void LLWebRTCVoiceClient::removeObserver(LLVoiceClientParticipantObserver *obser void LLWebRTCVoiceClient::notifyParticipantObservers() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE for (observer_set_t::iterator it = mParticipantObservers.begin(); it != mParticipantObservers.end();) { LLVoiceClientParticipantObserver *observer = *it; @@ -335,6 +358,8 @@ void LLWebRTCVoiceClient::removeObserver(LLVoiceClientStatusObserver *observer) void LLWebRTCVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + LL_DEBUGS("Voice") << "( " << LLVoiceClientStatusObserver::status2string(status) << " )" << " mSession=" << mSession << LL_ENDL; @@ -401,6 +426,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() LLMuteList::getInstance()->addObserver(this); while (!sShuttingDown) { + LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop") // 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 @@ -487,7 +513,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() sessionState::processSessionStates(); if (mProcessChannels && voiceEnabled && !mHidden) { - sendPositionUpdate(true); + sendPositionUpdate(false); updateOwnVolume(); } } @@ -516,6 +542,8 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() // for cross-region voice. void LLWebRTCVoiceClient::updateNeighboringRegions() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + static const std::vector 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), @@ -546,6 +574,8 @@ void LLWebRTCVoiceClient::updateNeighboringRegions() // shut down the current audio session to make room for the next one. void LLWebRTCVoiceClient::leaveAudioSession() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + if(mSession) { LL_DEBUGS("Voice") << "leaving session: " << mSession->mChannelID << LL_ENDL; @@ -585,45 +615,41 @@ void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) mDevicesListUpdated = state; } -void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, - const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) +// the singleton 'this' pointer will outlive the work queue. +void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList& render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList& capture_devices) { + + LL::WorkQueue::postMaybe(mMainQueue, + [=] + { + OnDevicesChangedImpl(render_devices, capture_devices); + }); +} + +void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); LL_DEBUGS("Voice") << "Setting devices to-input: '" << inputDevice << "' output: '" << outputDevice << "'" << LL_ENDL; clearRenderDevices(); - bool renderDeviceSet = false; for (auto &device : render_devices) { addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID)); - LL_DEBUGS("Voice") << "Checking render device" << "'" << device.mID << "'" << LL_ENDL; - if (outputDevice == device.mID) - { - renderDeviceSet = true; - } - } - if (!renderDeviceSet) - { - setRenderDevice("Default"); } + setRenderDevice(outputDevice); clearCaptureDevices(); - bool captureDeviceSet = false; for (auto &device : capture_devices) { LL_DEBUGS("Voice") << "Checking capture device:'" << device.mID << "'" << LL_ENDL; addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID)); - if (inputDevice == device.mID) - { - captureDeviceSet = true; - } - } - if (!captureDeviceSet) - { - setCaptureDevice("Default"); } + setCaptureDevice(inputDevice); setDevicesListUpdated(true); } @@ -771,6 +797,8 @@ void LLWebRTCVoiceClient::setHidden(bool hidden) // notify the observers. void LLWebRTCVoiceClient::OnConnectionEstablished(const std::string &channelID, const LLUUID ®ionID) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + if (gAgent.getRegion()->getRegionID() == regionID) { if (mNextSession && mNextSession->mChannelID == channelID) @@ -812,18 +840,20 @@ void LLWebRTCVoiceClient::OnConnectionShutDown(const std::string &channelID, con } } } -void LLWebRTCVoiceClient::OnConnectionFailure(const std::string &channelID, const LLUUID ®ionID) +void LLWebRTCVoiceClient::OnConnectionFailure(const std::string &channelID, + const LLUUID ®ionID, + LLVoiceClientStatusObserver::EStatusType status_type) { 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); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(status_type); } else if (mSession && mSession->mChannelID == channelID) { - LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + LLWebRTCVoiceClient::getInstance()->notifyStatusObservers(status_type); } } } @@ -843,6 +873,8 @@ void LLWebRTCVoiceClient::setEarLocation(S32 loc) void LLWebRTCVoiceClient::updatePosition(void) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + LLViewerRegion *region = gAgent.getRegion(); if (region && isAgentAvatarValid()) { @@ -963,6 +995,8 @@ void LLWebRTCVoiceClient::enforceTether() // standard 50m void LLWebRTCVoiceClient::sendPositionUpdate(bool force) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + Json::FastWriter writer; std::string spatial_data; @@ -1074,6 +1108,8 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::addParticipantBy void LLWebRTCVoiceClient::removeParticipantByID(const std::string &channelID, const LLUUID &id) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + participantStatePtr_t result; LLWebRTCVoiceClient::sessionState::ptr_t session = sessionState::matchSessionByChannelID(channelID); if (session) @@ -1104,6 +1140,9 @@ LLWebRTCVoiceClient::participantState::participantState(const LLUUID& agent_id) LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::addParticipant(const LLUUID& agent_id) { + + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + participantStatePtr_t result; participantUUIDMap::iterator iter = mParticipantsByUUID.find(agent_id); @@ -1139,6 +1178,8 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::ad LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::findParticipantByID(const LLUUID& id) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + participantStatePtr_t result; participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); @@ -1152,6 +1193,8 @@ LLWebRTCVoiceClient::participantStatePtr_t LLWebRTCVoiceClient::sessionState::fi void LLWebRTCVoiceClient::sessionState::removeParticipant(const LLWebRTCVoiceClient::participantStatePtr_t &participant) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + if (participant) { participantUUIDMap::iterator iter = mParticipantsByUUID.find(participant->mAvatarID); @@ -1317,7 +1360,7 @@ bool LLWebRTCVoiceClient::isCurrentChannel(const LLSD &channelInfo) if (mSession) { - if (!channelInfo["sessionHandle"].asString().empty()) + if (!channelInfo["session_handle"].asString().empty()) { return mSession->mHandle == channelInfo["session_handle"].asString(); } @@ -1390,6 +1433,8 @@ void LLWebRTCVoiceClient::predSetMicGain(const LLWebRTCVoiceClient::sessionState void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + LL_DEBUGS("Voice") << "( " << (enabled ? "enabled" : "disabled") << " )" << " was "<< (mVoiceEnabled ? "enabled" : "disabled") @@ -1412,6 +1457,7 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) updatePosition(); if (!mIsCoroutineActive) { + mMainQueue = LL::WorkQueue::getInstance("mainloop"); LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); } @@ -1508,10 +1554,13 @@ 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) + if (mSession) { - result = participant->mVolume; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant) + { + result = participant->mVolume; + } } return result; @@ -1791,6 +1840,8 @@ void LLWebRTCVoiceClient::sessionState::revive() void LLWebRTCVoiceClient::sessionState::processSessionStates() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + auto iter = mSessions.begin(); while (iter != mSessions.end()) { @@ -1810,6 +1861,8 @@ void LLWebRTCVoiceClient::sessionState::processSessionStates() // process the states on each connection associated with a session. bool LLWebRTCVoiceClient::sessionState::processConnectionStates() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + std::list::iterator iter = mWebRTCConnections.begin(); while (iter != mWebRTCConnections.end()) { @@ -1832,6 +1885,8 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() // on our location. bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + if (!mShuttingDown) { // Estate voice requires connection to neighboring regions. @@ -1983,9 +2038,9 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const s mWebRTCAudioInterface(nullptr), mWebRTCDataInterface(nullptr), mVoiceConnectionState(VOICE_STATE_START_SESSION), + mCurrentStatus(LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED), mMuted(true), mShutDown(false), - mTrickling(false), mIceCompleted(false), mSpeakerVolume(0.0), mMicGain(0.0), @@ -2000,6 +2055,7 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const s mWebRTCPeerConnectionInterface = llwebrtc::newPeerConnection(); mWebRTCPeerConnectionInterface->setSignalingObserver(this); + mMainQueue = LL::WorkQueue::getInstance("mainloop"); } LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() @@ -2026,151 +2082,129 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() // 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. + +// callback from llwebrtc void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { - LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; + LL::WorkQueue::postMaybe(mMainQueue, + [=] { + 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; - } + switch (state) + { + case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_COMPLETE: + { + mIceCompleted = true; + break; + } + case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW: + { + mIceCompleted = false; + } + default: + break; + } + }); } -void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) +// callback from llwebrtc +void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate) { - LLMutexLock lock(&mVoiceStateMutex); - mIceCandidates.push_back(candidate); + LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); }); } -void LLVoiceWebRTCConnection::onIceUpdateComplete(bool ice_completed, const LLSD &result) +void LLVoiceWebRTCConnection::processIceUpdates() { - mOutstandingRequests--; - if (LLWebRTCVoiceClient::isShuttingDown()) - { - return; - } - mTrickling = false; + mOutstandingRequests++; + LLCoros::getInstance()->launch("LLVoiceWebRTCConnection::requestVoiceConnectionCoro", + boost::bind(&LLVoiceWebRTCConnection::processIceUpdatesCoro, this)); } -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() +void LLVoiceWebRTCConnection::processIceUpdatesCoro() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + if (mShutDown || LLWebRTCVoiceClient::isShuttingDown()) { + mOutstandingRequests--; return; } bool iceCompleted = false; LLSD body; + if (!mIceCandidates.empty() || mIceCompleted) { - if (!mTrickling) + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(mRegionID); + if (!regionp || !regionp->capabilitiesReceived()) { - if (!mIceCandidates.empty() || mIceCompleted) + LL_DEBUGS("Voice") << "no capabilities for ice gathering; waiting " << LL_ENDL; + mOutstandingRequests--; + return; + } + + std::string url = regionp->getCapability("VoiceSignalingRequest"); + if (url.empty()) + { + mOutstandingRequests--; + 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) { - 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; + 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; + + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( + new LLCoreHttpUtil::HttpCoroutineAdapter("LLVoiceWebRTCAdHocConnection::processIceUpdatesCoro", + LLCore::HttpRequest::DEFAULT_POLICY_ID)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, body, httpOpts); + + if (LLWebRTCVoiceClient::isShuttingDown()) + { + return; + } + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + // couldn't trickle the candidates, so restart the session. + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } } + mOutstandingRequests--; } @@ -2182,39 +2216,74 @@ void LLVoiceWebRTCConnection::processIceUpdates() // and is passed to the simulator via a CAP, which then passes // it on to the Secondlife WebRTC server. +// +// The LLWebRTCVoiceConnection object will not be deleted +// before the webrtc connection itself is shut down, so +// we shouldn't be getting this callback on a nonexistant +// this pointer. + +// callback from llwebrtc 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; - } + LL::WorkQueue::postMaybe(mMainQueue, + [=] { + if (mShutDown) + { + return; + } + LL_DEBUGS("Voice") << "On Offer Available." << LL_ENDL; + 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) + +// +// The LLWebRTCVoiceConnection object will not be deleted +// before the webrtc connection itself is shut down, so +// we shouldn't be getting this callback on a nonexistant +// this pointer. +// nor should audio_interface be invalid if the LLWebRTCVoiceConnection +// is shut down. + +// callback from llwebrtc +void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface) { - LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL; - mWebRTCAudioInterface = audio_interface; - setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); + LL::WorkQueue::postMaybe(mMainQueue, + [=] { + if (mShutDown) + { + return; + } + LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL; + mWebRTCAudioInterface = audio_interface; + setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); + }); } + +// +// The LLWebRTCVoiceConnection object will not be deleted +// before the webrtc connection itself is shut down, so +// we shouldn't be getting this callback on a nonexistant +// this pointer. + +// callback from llwebrtc void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { - LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; - if (!mShutDown) - { - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - } + LL::WorkQueue::postMaybe(mMainQueue, + [=] { + 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) { @@ -2286,8 +2355,10 @@ void LLVoiceWebRTCConnection::sendData(const std::string &data) // 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) +void LLVoiceWebRTCConnection::breakVoiceConnectionCoro() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + LL_DEBUGS("Voice") << "Disconnecting voice." << LL_ENDL; if (mWebRTCDataInterface) { @@ -2299,42 +2370,41 @@ bool LLVoiceWebRTCConnection::breakVoiceConnection(bool corowait) if (!regionp || !regionp->capabilitiesReceived()) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_EXIT); - return false; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); if (url.empty()) { - return false; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } 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; + body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( + new LLCoreHttpUtil::HttpCoroutineAdapter("LLVoiceWebRTCAdHocConnection::breakVoiceConnection", + LLCore::HttpRequest::DEFAULT_POLICY_ID)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); - 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; -} + setVoiceConnectionState(VOICE_STATE_WAIT_FOR_EXIT); + + // tell the server to shut down the connection as a courtesy. + // shutdownConnection will drop the WebRTC connection which will + // also shut things down. + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, body, httpOpts); -void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &result) -{ - mOutstandingRequests--; if (LLWebRTCVoiceClient::isShuttingDown()) { return; @@ -2342,67 +2412,36 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res 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); - } + 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() +void LLVoiceWebRTCSpatialConnection::requestVoiceConnection() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + 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; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); if (url.empty()) { - return false; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } LL_DEBUGS("Voice") << "region ready for voice provisioning; url=" << url << LL_ENDL; @@ -2411,10 +2450,7 @@ bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() LLSD body; LLSD jsep; jsep["type"] = "offer"; - { - LLMutexLock lock(&mVoiceStateMutex); - jsep["sdp"] = mChannelSDP; - } + jsep["sdp"] = mChannelSDP; body["jsep"] = jsep; if (mParcelLocalID != INVALID_PARCEL_ID) { @@ -2422,27 +2458,56 @@ bool LLVoiceWebRTCSpatialConnection::requestVoiceConnection() } body["channel_type"] = "local"; body["voice_server_type"] = WEBRTC_VOICE_SERVER_TYPE; + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( + new LLCoreHttpUtil::HttpCoroutineAdapter("LLVoiceWebRTCAdHocConnection::requestVoiceConnection", + LLCore::HttpRequest::DEFAULT_POLICY_ID)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - 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)); + httpOpts->setWantHeaders(true); mOutstandingRequests++; - return true; + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, body, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status) + { + OnVoiceConnectionRequestSuccess(result); + } + else + { + switch (status.getType()) + { + case HTTP_CONFLICT: + mCurrentStatus = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; + break; + case HTTP_UNAUTHORIZED: + mCurrentStatus = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; + break; + default: + mCurrentStatus = LLVoiceClientStatusObserver::ERROR_UNKNOWN; + break; + } + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } + mOutstandingRequests--; } void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result) { - mOutstandingRequests--; + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + if (LLWebRTCVoiceClient::isShuttingDown()) { return; } LLVoiceWebRTCStats::getInstance()->provisionAttemptEnd(true); - if (result.has("viewer_session") && result.has("jsep") && result["jsep"].has("type") && result["jsep"]["type"] == "answer" && + 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(); @@ -2457,32 +2522,28 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result 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) +static llwebrtc::LLWebRTCPeerConnectionInterface::InitOptions getConnectionOptions() { - if (LLWebRTCVoiceClient::isShuttingDown()) + llwebrtc::LLWebRTCPeerConnectionInterface::InitOptions options; + llwebrtc::LLWebRTCPeerConnectionInterface::InitOptions::IceServers servers; + + // TODO: Pull these from login + std::string grid = LLGridManager::getInstance()->getGridLoginID(); + std::transform(grid.begin(), grid.end(), grid.begin(), [](unsigned char c){ return std::tolower(c); }); + int num_servers = 2; + if (grid == "agni") { - mOutstandingRequests--; - return; + num_servers = 3; } - if (retries >= 0) + for (int i=1; i <= num_servers; i++) { - 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; + servers.mUrls.push_back(llformat("stun:stun%d.%s.secondlife.io:3478", i, grid.c_str())); } - LL_WARNS("Voice") << "Unable to connect voice." << body << " RESULT: " << result << LL_ENDL; - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - mOutstandingRequests--; + options.mServers.push_back(servers); + return options; } @@ -2490,24 +2551,27 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestFailure(std::string url, i // Secondlife WebRTC server. bool LLVoiceWebRTCConnection::connectionStateMachine() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + processIceUpdates(); switch (getVoiceConnectionState()) { case VOICE_STATE_START_SESSION: { + LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("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()) + if (!mWebRTCPeerConnectionInterface->initializeConnection(getConnectionOptions())) { setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); } @@ -2532,14 +2596,9 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // 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); - } + setVoiceConnectionState(VOICE_STATE_CONNECTION_WAIT); + LLCoros::getInstance()->launch("LLVoiceWebRTCConnection::requestVoiceConnectionCoro", + boost::bind(&LLVoiceWebRTCConnection::requestVoiceConnectionCoro, this)); break; case VOICE_STATE_CONNECTION_WAIT: @@ -2577,6 +2636,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() { sendJoin(); // tell the Secondlife WebRTC server that we're here via the data channel. setVoiceConnectionState(VOICE_STATE_SESSION_UP); + LLWebRTCVoiceClient::getInstance()->sendPositionUpdate(true); } break; } @@ -2599,7 +2659,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() if (mRetryWaitPeriod++ * UPDATE_THROTTLE_SECONDS > mRetryWaitSecs) { // something went wrong, so notify that the connection has failed. - LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID); + LLWebRTCVoiceClient::getInstance()->OnConnectionFailure(mChannelID, mRegionID, mCurrentStatus); setVoiceConnectionState(VOICE_STATE_DISCONNECT); mRetryWaitPeriod = 0; if (mRetryWaitSecs < MAX_RETRY_WAIT_SECONDS) @@ -2613,7 +2673,8 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() break; case VOICE_STATE_DISCONNECT: - breakVoiceConnection(true); + LLCoros::instance().launch("LLVoiceWebRTCConnection::breakVoiceConnectionCoro", + boost::bind(&LLVoiceWebRTCConnection::breakVoiceConnectionCoro, this)); break; case VOICE_STATE_WAIT_FOR_EXIT: @@ -2622,7 +2683,6 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() case VOICE_STATE_SESSION_EXIT: { { - LLMutexLock lock(&mVoiceStateMutex); if (!mShutDown) { mVoiceConnectionState = VOICE_STATE_START_SESSION; @@ -2651,19 +2711,37 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } // 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. + +// llwebrtc callback +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. + LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); +} + +// +// The LLWebRTCVoiceConnection object will not be deleted +// before the webrtc connection itself is shut down, so +// we shouldn't be getting this callback on a nonexistant +// this pointer. +void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool binary) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + + if (mShutDown) + { + return; + } if (binary) { @@ -2688,8 +2766,8 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar LLUUID agent_id(participant_id); if (agent_id.isNull()) { - LL_WARNS("Voice") << "Bad participant ID from data channel (" << participant_id << "):" << data << LL_ENDL; - continue; + // probably a test client. + continue; } LLWebRTCVoiceClient::participantStatePtr_t participant = @@ -2747,6 +2825,11 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar { participant->mIsSpeaking = voice_data[participant_id].get("v", Json::Value(false)).asBool(); } + + if (voice_data[participant_id].isMember("m")) + { + participant->mIsModeratorMuted = voice_data[participant_id].get("m", Json::Value(false)).asBool(); + } } } } @@ -2771,13 +2854,30 @@ void LLVoiceWebRTCConnection::OnDataReceived(const std::string &data, bool binar } } +// +// The LLWebRTCVoiceConnection object will not be deleted +// before the webrtc connection itself is shut down, so +// we shouldn't be getting this callback on a nonexistant +// this pointer. +// nor should data_interface be invalid if the LLWebRTCVoiceConnection +// is shut down. + +// llwebrtc callback void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { - if (data_interface) - { - mWebRTCDataInterface = data_interface; - mWebRTCDataInterface->setDataObserver(this); - } + LL::WorkQueue::postMaybe(mMainQueue, + [=] { + if (mShutDown) + { + return; + } + + if (data_interface) + { + mWebRTCDataInterface = data_interface; + mWebRTCDataInterface->setDataObserver(this); + } + }); } // tell the Secondlife WebRTC server that @@ -2788,6 +2888,8 @@ void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface // to peers. void LLVoiceWebRTCConnection::sendJoin() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + Json::FastWriter writer; Json::Value root = Json::objectValue; Json::Value join_obj = Json::objectValue; @@ -2874,21 +2976,25 @@ LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection() // 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() +void LLVoiceWebRTCAdHocConnection::requestVoiceConnection() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + 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; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } std::string url = regionp->getCapability("ProvisionVoiceAccountRequest"); if (url.empty()) { - return false; + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + return; } LLVoiceWebRTCStats::getInstance()->provisionAttemptStart(); @@ -2896,7 +3002,6 @@ bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() LLSD jsep; jsep["type"] = "offer"; { - LLMutexLock lock(&mVoiceStateMutex); jsep["sdp"] = mChannelSDP; } body["jsep"] = jsep; @@ -2905,12 +3010,26 @@ bool LLVoiceWebRTCAdHocConnection::requestVoiceConnection() 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)); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( + new LLCoreHttpUtil::HttpCoroutineAdapter("LLVoiceWebRTCAdHocConnection::requestVoiceConnection", + LLCore::HttpRequest::DEFAULT_POLICY_ID)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); mOutstandingRequests++; - return true; + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, body, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); + } + else + { + OnVoiceConnectionRequestSuccess(result); + } + mOutstandingRequests--; } diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index b26bea27ce..aa3298ec1b 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -203,7 +203,9 @@ public: void OnConnectionEstablished(const std::string& channelID, const LLUUID& regionID); void OnConnectionShutDown(const std::string &channelID, const LLUUID ®ionID); - void OnConnectionFailure(const std::string &channelID, const LLUUID& regionID); + void OnConnectionFailure(const std::string &channelID, + const LLUUID ®ionID, + LLVoiceClientStatusObserver::EStatusType status_type = LLVoiceClientStatusObserver::ERROR_UNKNOWN); void sendPositionUpdate(bool force); void updateOwnVolume(); @@ -225,7 +227,8 @@ public: void OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) override; //@} - + void OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, + const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices); struct participantState { @@ -271,7 +274,7 @@ public: participantStatePtr_t findParticipantByID(const LLUUID& id); static ptr_t matchSessionByChannelID(const std::string& channel_id); - + void shutdownAllConnections(); void revive(); @@ -445,6 +448,8 @@ private: /// Clean up objects created during a voice session. void cleanUp(); + LL::WorkQueue::weak_t mMainQueue; + bool mTuningMode; F32 mTuningMicGain; int mTuningSpeakerVolume; @@ -585,7 +590,6 @@ class LLVoiceWebRTCConnection : void OnOfferAvailable(const std::string &sdp) override; void OnRenegotiationNeeded() override; void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; - void OnPeerConnectionShutdown() override; //@} ///////////////////////// @@ -596,13 +600,15 @@ class LLVoiceWebRTCConnection : void OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) override; //@} + void OnDataReceivedImpl(const std::string &data, bool binary); + 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); + void processIceUpdates(); + void processIceUpdatesCoro(); + virtual void setMuteMic(bool muted); virtual void setMicGain(F32 volume); virtual void setSpeakerVolume(F32 volume); @@ -618,12 +624,10 @@ class LLVoiceWebRTCConnection : 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 @@ -644,11 +648,10 @@ class LLVoiceWebRTCConnection : } EVoiceConnectionState; EVoiceConnectionState mVoiceConnectionState; - LLMutex mVoiceStateMutex; + LL::WorkQueue::weak_t mMainQueue; + 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. @@ -666,19 +669,15 @@ class LLVoiceWebRTCConnection : } EVoiceConnectionState getVoiceConnectionState() { - if (mVoiceStateMutex.isLocked()) - { - LL_WARNS("Voice") << "LOCKED." << LL_ENDL; - } - LLMutexLock lock(&mVoiceStateMutex); return mVoiceConnectionState; } - virtual bool requestVoiceConnection() = 0; + virtual void requestVoiceConnection() = 0; + void requestVoiceConnectionCoro() { requestVoiceConnection(); } - bool breakVoiceConnection(bool wait); - void OnVoiceDisconnectionRequestSuccess(const LLSD &body); - void OnVoiceDisconnectionRequestFailure(std::string url, int retries, LLSD body, const LLSD &result); + void breakVoiceConnectionCoro(); + + LLVoiceClientStatusObserver::EStatusType mCurrentStatus; LLUUID mRegionID; LLUUID mViewerSession; @@ -702,7 +701,6 @@ class LLVoiceWebRTCConnection : std::vector mIceCandidates; bool mIceCompleted; - bool mTrickling; llwebrtc::LLWebRTCPeerConnectionInterface *mWebRTCPeerConnectionInterface; llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface; @@ -725,7 +723,7 @@ class LLVoiceWebRTCSpatialConnection : protected: - bool requestVoiceConnection() override; + void requestVoiceConnection() override; S32 mParcelLocalID; }; @@ -740,7 +738,7 @@ class LLVoiceWebRTCAdHocConnection : public LLVoiceWebRTCConnection bool isSpatial() override { return false; } protected: - bool requestVoiceConnection() override; + void requestVoiceConnection() override; std::string mCredentials; }; diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index d3938a7bf8..ff4f9e865a 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -971,6 +971,17 @@ name="enable_lip_sync" top_pad="5" width="237"/> + + + + Noise Suppression + + + + + + + +